https://wiki.freepascal.org/api.php?action=feedcontributions&user=Zoran&feedformat=atomLazarus wiki - User contributions [en]2024-03-28T09:52:45ZUser contributionsMediaWiki 1.35.6https://wiki.freepascal.org/index.php?title=FindAllFiles&diff=158322FindAllFiles2024-02-29T07:52:09Z<p>Zoran: Fixed links</p>
<hr />
<div>{{FindAllFiles}}<br />
<br />
[[Unit]]: Lazarus [[fileutil]].<br />
<br />
To enable FileUtil in your project, please add LazUtils into required packages. Follow these steps:<br />
<br />
* Go to ''Lazarus IDE Menu'' > ''Project'' > ''Project Inspector''<br />
* In the ''Project Inspector'' dialog window, click ''Add'' > ''New Requirement''<br />
* In the ''New Requirement'' dialog window, find ''LazUtils'' package then click OK.<br />
<br />
See also:<br />
<br />
* https://lazarus-ccr.sourceforge.io/docs/lazutils/fileutil/findallfiles.html<br />
* https://lazarus-ccr.sourceforge.io/docs/lazutils/fileutil/tfilesearcher.html<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure FindAllFiles(AList: TStrings; const SearchPath: String;<br />
SearchMask: String = ''; SearchSubDirs: Boolean = True; DirAttr: Word = faDirectory); <br />
<br />
function FindAllFiles(const SearchPath: String; SearchMask: String = '';<br />
SearchSubDirs: Boolean = True): TStringList;<br />
</syntaxhighlight><br />
<br />
'''FindAllFiles''' looks for files matching the SearchMask in the SearchPath directory and, if specified, its subdirectories, and populates a [[TStrings|stringlist]] with the resulting filenames.<br />
<br />
The mask can be a single mask like you can use with the FindFirst/FindNext functions,<br />
or it can consist of a list of masks, separated by a [[Semicolon|semicolon (;)]].<br><br />
Spaces in the mask are treated as literals.<br />
<br />
Parameter DirAttr is int file attribute: if file-system item has this attribute(s), it is considered as a directory. It can be faDirectory, faSymLink, (faDirectory+faSymLink) or maybe another bits can be used.<br />
<br />
There are two overloaded versions of this routine. The first one is a '''[[Procedure|procedure]]''' and assumes that the receiving stringlist already has been created.<br />
The second one is a '''[[Function|function]]''' which creates the stringlist internally and returns it as a function result. In both cases the stringlist must be destroyed by the calling procedure.<br />
<br />
== Example ==<br />
<br />
<syntaxhighlight lang="pascal"><br />
uses <br />
..., FileUtil, ...<br />
var<br />
PascalFiles: TStringList;<br />
begin<br />
PascalFiles := TStringList.Create;<br />
try<br />
FindAllFiles(PascalFiles, LazarusDirectory, '*.pas;*.pp;*.p;*.inc', true); //find e.g. all pascal sourcefiles<br />
ShowMessage(Format('Found %d Pascal source files', [PascalFiles.Count]));<br />
finally<br />
PascalFiles.Free;<br />
end;<br />
<br />
// or<br />
<br />
begin<br />
//No need to create the stringlist; the function does that for you<br />
PascalFiles := FindAllFiles(LazarusDirectory, '*.pas;*.pp;*.p;*.inc', true); //find e.g. all pascal sourcefiles<br />
try<br />
ShowMessage(Format('Found %d Pascal source files', [PascalFiles.Count]));<br />
finally<br />
PascalFiles.Free;<br />
end;<br />
</syntaxhighlight><br />
<br />
'''IMPORTANT NOTE:'''<br />
The ''function'' "FindAllFiles" creates the stringlist internally. This may look very convenient at first sight, but it is very easy to create '''memory leaks''' that way:<br />
<br />
<syntaxhighlight lang="pascal"><br />
// DON'T EVER DO THIS !!!! - There is no way to destroy the stringlist created by FindAllFiles.<br />
Listbox1.Items.Assign(FindAllFiles(LazarusDirectory, '*.pas;*.pp;*.p;*.inc', true);<br />
</syntaxhighlight><br />
<br />
{{Note|If you want to use this function in command line programs, add a project requirement for ''LCLBase'', which will not pull in the entire LCL}}</div>Zoranhttps://wiki.freepascal.org/index.php?title=Multiplatform_Programming_Guide&diff=149701Multiplatform Programming Guide2022-01-27T10:24:11Z<p>Zoran: /* Gtk2 and masking FPU exceptions */ -- updated link to bug report</p>
<hr />
<div>{{Multiplatform Programming Guide}}<br />
<br />
This is a tutorial on writing cross-platform applications with Lazarus and Free Pascal. It will cover the necessary precautions to aid in creating a cross-platform ready program that is ready to [[Deploying Your Application|deploy]].<br />
<br />
Most [[LCL]] applications work in a cross-platform way without any extra effort.<br />
This principle is called [[Write once compile anywhere|„write once, compile anywhere“]].<br />
<br />
== Introduction to Multiplatform (Cross-platform) Programming ==<br />
<br />
=== How many platforms do you need? ===<br />
<br />
To answer this question, you should first determine who your potential users are and how your program will be used. This question depends on where you are deploying your application.<br />
<br />
If you are developing generic desktop software in 2014, Microsoft Windows may be the most important platform. Note that macOS and/or Linux are gaining in popularity, and may be a significant target for your application.<br />
<br />
The popularity of the various desktop operating systems differs by country, by the type of software used, and with the target audience; there's no general rule. For example, macOS is quite popular in North America and western Europe, while in South America macOS is mostly restricted to video and sound work.<br />
<br />
On many contract projects, only one platform is relevant. Free Pascal and Lazarus are quite capable of writing software targeted at a specific platform. You can, for example, access the full Windows API to write a well integrated Windows program.<br />
<br />
If you're developing software that will run on a web server, a Unix platform in one of its various flavors is commonly used. In this case, perhaps only Linux, Solaris, *BSD and other Unixes make sense as your target platforms, although you may want to add support for Windows for completeness.<br />
<br />
Once you've addressed any cross-platform issues in your design, you can largely ignore the other platforms, much as you would when developing for a single platform. However, at some point you'll need to test deploying and running your program on the other platforms. For that, it will be helpful to have unrestricted access to machines running the target operating systems. If you don't want multiple physical computers, investigate dual-booting or Virtual Machine (VM) solutions like:<br />
<br />
* [https://www.vmware.com/products/fusion.html VMware Fusion for Mac] - Host system must be running Intel macOS (Commercial/Free for non-commercial use).<br />
* [https://www.vmware.com/products/workstation-player.html VMware Workstation Player] - Host system running Windows 64 bit or Linux 64 bit (Commercial/Free for personal use).<br />
* [https://www.vmware.com/products/workstation-pro.html VMware Workstation Pro] - Host system running Windows 64 bit or Linux 64 bit (Commercial/Free trial).<br />
* [https://www.parallels.com/ Parallels Desktop for Mac] - Host system must be running Intel macOS (Commercial/Free trial). Note: There is a [https://b2b.parallels.com/Apple-Silicon Technical Preview] that will run on Apple M1 ARM64 processors which will run the ARM versions of Linux and [https://www.microsoft.com/en-us/software-download/windowsinsiderpreviewARM64 Windows 10 Insider Preview].<br />
* [https://www.virtualbox.org/ VirtualBox] - Host system can be running Intel macOS, Windows, Linux or Solaris (Open Source).<br />
<br />
Some, eg Parallels on the Mac, make it trivial to install a desktop Linux operating system with a single click.<br />
<br />
'''See also:''' [[Small Virtual Machines]] and [[Qemu and other emulators]].<br />
<br />
== Cross-platform Programming ==<br />
<br />
=== Working with files and folders ===<br />
<br />
When working with files and folders, it is important to use non-platform specific path delimiters and [[End_of_Line|line ending]] sequences. Here is a list of declared [[Constant|constants]] in Lazarus to be used when working with files and folders.<br />
<br />
* '''PathSep''', '''PathSeparator''': path separator when adding many paths together (';', ...)<br />
* '''PathDelim''', '''DirectorySeparator''': directory separator for each platform ('/', '\', ...)<br />
* '''LineEnding''': proper line ending character sequence (#13#10 - CRLF, #10 - [[Line_feed|LF]], ...)<br />
<br />
Another important thing to be noted is the case sensitiveness of the file system.<br />
On Windows filenames are usually not case sensitive, while they usually are case sensitive on Unix platforms (eg Linux, FreeBSD) but not macOS despite its Unix heritage. But note that if an EXT2, EXT3, etc file system is mounted on Windows, it would be case sensitive. Likewise, a FAT file system mounted on Linux should not be case sensitive.<br />
<br />
Special attention should be paid to NTFS which is not case sensitive when used in Windows, but is case sensitive when mounted by POSIX OSes. This could cause '''various problems, including loss of files''' if files with the same filenames in different cases exist on an NTFS partition mounted in Windows. Using custom functions for checking and preventing creation of several files with the same names on NTFS should be considered by developers.<br />
<br />
macOS uses case insensitive filenames by default. This can be the cause of annoying bugs, so any portable application should use filenames consistently.<br />
<br />
The RTL file functions use the system encoding for file names. Under Windows this is one of the Windows code pages, while Linux, BSD and macOS usually use UTF-8. The unit '''FileUtil''' of the LCL provides file functions which takes UTF-8 strings like the rest of the LCL.<br />
<br />
<syntaxhighlight lang="pascal"><br />
// AnsiToUTF8 and UTF8ToAnsi need a widestring manager under Linux, BSD, macOS<br />
// but normally these OS use UTF-8 as system encoding so the widestringmanager<br />
// is not needed.<br />
function NeedRTLAnsi: boolean;// true if system encoding is not UTF-8<br />
procedure SetNeedRTLAnsi(NewValue: boolean);<br />
function UTF8ToSys(const s: string): string; // as UTF8ToAnsi but more independent of widestringmanager<br />
function SysToUTF8(const s: string): string; // as AnsiToUTF8 but more independent of widestringmanager<br />
function UTF8ToConsole(const s: string): string; // converts UTF8 string to console encoding (used by Write, WriteLn)<br />
<br />
// file operations<br />
function FileExistsUTF8(const Filename: string): boolean;<br />
function FileAgeUTF8(const FileName: string): Longint;<br />
function DirectoryExistsUTF8(const Directory: string): Boolean;<br />
function ExpandFileNameUTF8(const FileName: string): string;<br />
function ExpandUNCFileNameUTF8(const FileName: string): string;<br />
function ExtractShortPathNameUTF8(Const FileName : String) : String;<br />
function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec): Longint;<br />
function FindNextUTF8(var Rslt: TSearchRec): Longint;<br />
procedure FindCloseUTF8(var F: TSearchrec);<br />
function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;<br />
function FileGetAttrUTF8(const FileName: String): Longint;<br />
function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint;<br />
function DeleteFileUTF8(const FileName: String): Boolean;<br />
function RenameFileUTF8(const OldName, NewName: String): Boolean;<br />
function FileSearchUTF8(const Name, DirList : String): String;<br />
function FileIsReadOnlyUTF8(const FileName: String): Boolean;<br />
function GetCurrentDirUTF8: String;<br />
function SetCurrentDirUTF8(const NewDir: String): Boolean;<br />
function CreateDirUTF8(const NewDir: String): Boolean;<br />
function RemoveDirUTF8(const Dir: String): Boolean;<br />
function ForceDirectoriesUTF8(const Dir: string): Boolean;<br />
<br />
// environment<br />
function ParamStrUTF8(Param: Integer): string;<br />
function GetEnvironmentStringUTF8(Index: Integer): string;<br />
function GetEnvironmentVariableUTF8(const EnvVar: string): String;<br />
function GetAppConfigDirUTF8(Global: Boolean): string;<br />
<br />
// other<br />
function SysErrorMessageUTF8(ErrorCode: Integer): String;</syntaxhighlight><br />
<br />
===Empty file names and double path delimiters===<br />
<br />
There are differences in file/directory name handling in Windows versus Linux, Unix and Unix like systems.<br />
<br />
* Windows allows empty file names. That's why FileExistsUTF8('..\') checks under Windows in the parent directory for a file without name.<br />
* On Linux/Unix/Unix-like systems, an empty file is mapped to the directory and directories are treated as files. This means that FileExistsUTF8('../') under Unix checks for the existence of the parent directory, which normally results true.<br />
<br />
Double path delimiters in file names are also treated differently: <br />
* Windows: 'C:\' is not the same as 'C:\\'<br />
* Unix like OS: the path '/usr//' is the same as '/usr/'. If '/usr' is a directory then even all three are the same. <br />
<br />
This is important when concatenating file names. For example:<br />
<br />
<syntaxhighlight lang="pascal"><br />
FullFilename:=FilePath+PathDelim+ShortFilename; // can result in two PathDelims which gives different results under Windows and Linux<br />
FullFilename:=AppendPathDelim(FilePath) + ShortFilename; // creates only one PathDelim<br />
FullFilename:=TrimFilename(FilePath+PathDelim+ShortFilename); // creates only one PathDelim and do some more clean up</syntaxhighlight><br />
<br />
The function TrimFilename replaces double path delimiters with single ones and shorten '..' paths. For example /usr//lib/../src is trimmed to /usr/src.<br />
<br />
If you want to know if a directory exists use '''DirectoryExistsUTF8'''.<br />
<br />
Another common task is to check if the path part of a file name exists. You can get the path with ExtractFilePath, but this will contain the path delimiter. <br />
* Under Unix like system you can simply use FileExistsUTF8 on the path. For example FileExistsUTF8('/home/user/') will return true if the directory /home/user exists. <br />
* Under Windows you must use the DirectoryExistsUTF8 function, but before that you must delete the path delimiter, for example with the ChompPathDelim function. <br />
<br />
Under Unix like systems the root directory is '/' and using the ChompPathDelim function will create an empty string. The function DirPathExists works like the DirectoryExistsUTF8 function, but trims the given path.<br />
<br />
Note that Unix/Linux uses the '~' (tilde) symbol to stand for the home directory, typically '/home/jim/' for a user called jim. So '~/myapp/myfile' and '/home/jim/myapp/myfile' are identical on the command line and in scripts. However, the tilde is not automatically expanded by Lazarus. It is necessary to use ExpandFileNameUTF8('~/myapp/myfile') to get the full path.<br />
<br />
=== Text encoding ===<br />
<br />
Text files are often encoded in the current system encoding. Under Windows this is usually one of the windows code pages, while Linux, BSD, and macOS usually use UTF-8. <br />
There is no 100% rule to find out which encoding a text file uses. The LCL unit '''lconvencoding''' has a function to guess the encoding:<br />
<br />
<syntaxhighlight lang="pascal"><br />
function GuessEncoding(const s: string): string;<br />
function GetDefaultTextEncoding: string;</syntaxhighlight><br />
<br />
And it contains functions to convert from one encoding to another:<br />
<br />
<syntaxhighlight lang="pascal">function ConvertEncoding(const s, FromEncoding, ToEncoding: string): string;<br />
<br />
function UTF8BOMToUTF8(const s: string): string; // UTF8 with BOM<br />
function ISO_8859_1ToUTF8(const s: string): string; // central europe<br />
function CP1250ToUTF8(const s: string): string; // central europe<br />
function CP1251ToUTF8(const s: string): string; // cyrillic<br />
function CP1252ToUTF8(const s: string): string; // latin 1<br />
...<br />
function UTF8ToUTF8BOM(const s: string): string; // UTF8 with BOM<br />
function UTF8ToISO_8859_1(const s: string): string; // central europe<br />
function UTF8ToCP1250(const s: string): string; // central europe<br />
function UTF8ToCP1251(const s: string): string; // cyrillic<br />
function UTF8ToCP1252(const s: string): string; // latin 1<br />
...</syntaxhighlight><br />
<br />
For example to load a text file and convert it to UTF-8 you can use:<br />
<br />
<syntaxhighlight lang="pascal"><br />
var<br />
sl: TStringList;<br />
OriginalText: String;<br />
TextAsUTF8: String;<br />
begin<br />
sl:=TStringList.Create;<br />
try<br />
sl.LoadFromFile('sometext.txt'); // beware: this changes line endings to system line endings<br />
OriginalText:=sl.Text;<br />
TextAsUTF8:=ConvertEncoding(OriginalText,GuessEncoding(OriginalText),EncodingUTF8);<br />
...<br />
finally<br />
sl.Free;<br />
end;<br />
end;<br />
</syntaxhighlight><br />
<br />
And to save a text file in the system encoding you can use:<br />
<syntaxhighlight lang="pascal"><br />
sl.Text:=ConvertEncoding(TextAsUTF8,EncodingUTF8,GetDefaultTextEncoding);<br />
sl.SaveToFile('sometext.txt');<br />
</syntaxhighlight><br />
<br />
=== Configuration files ===<br />
<br />
You can use the [[doc:rtl/sysutils/getappconfigdir.html|GetAppConfigDir]] function from SysUtils unit to get a suitable place to store configuration files on different system. The function has one parameter, called Global. If it is True then the directory returned is a global directory, i.e. valid for all users on the system. If the parameter Global is false, then the directory is specific for the user who is executing the program. On systems that do not support multi-user environments, these two directories may be the same.<br />
<br />
There is also the [[doc:rtl/sysutils/getappconfigfile.html|GetAppConfigFile]] which will return an appropriate name for an application configuration file. You can use it like this:<br />
<syntaxhighlight Lang=pascal><br />
ConfigFilePath := GetAppConfigFile(False);<br />
</syntaxhighlight><br />
<br />
Below are examples of the output of default path functions on different systems:<br />
<br />
<syntaxhighlight lang="pascal"><br />
program project1;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
uses<br />
SysUtils;<br />
<br />
begin<br />
WriteLn(GetAppConfigDir(True));<br />
WriteLn(GetAppConfigDir(False));<br />
WriteLn(GetAppConfigFile(True));<br />
WriteLn(GetAppConfigFile(False));<br />
end.<br />
</syntaxhighlight><br />
<br />
You can notice that global configuration files are stored on the /etc directory and local configurations are stored in a hidden directory in the user's home directory. Directories whose name begin with a dot (.) are hidden on UNIX and UNIX-like operating systems. You can create a directory in the location returned by GetAppConfigDir and then store configuration files there.<br />
<br />
{{Note| Normal users are not allowed to write to the /etc directory. Only users with administration rights can do this.}}<br />
<br />
Notice that before FPC 2.2.4 the function was using the directory where the application was to store global configurations on Windows.<br />
<br />
The output on Windows 98 with FPC 2.2.0:<br />
<br />
<pre><br />
C:\Program Files\PROJECT1<br />
C:\Windows\Local Settings\Application Data\PROJECT1<br />
C:\Program Files\PROJECT1\PROJECT1.cfg<br />
C:\Windows\Local Settings\Application Data\PROJECT1\PROJECT1.cfg<br />
</pre><br />
<br />
The output on Windows XP with FPC 3.0.4:<br />
<br />
<pre><br />
C:\Documents and Settings\All Users\Application Data\project1\<br />
C:\Documents and Settings\user\Local Settings\Application Data\project1\<br />
C:\Documents and Settings\All Users\Application Data\project1\project1.cfg<br />
C:\Documents and Settings\user\Local Settings\Application Data\project1\project1.cfg<br />
</pre><br />
<br />
The output on Windows 7 and Windows 10 and FPC 3.0.4:<br />
<br />
<pre><br />
C:\ProgramData\project1\<br />
C:\Users\user\AppData\Local\project1\<br />
C:\ProgramData\project1\project1.cfg<br />
C:\Users\user\AppData\Local\project1\project1.cfg<br />
</pre><br />
<br />
The output on macOS 10.14.5 with FPC 3.0.4 (violates Apple Guidelines - see [[#macOS|below]] for the correct macOS file locations):<br />
<br />
<pre><br />
/etc/project1/<br />
/Users/user/.config/project1/<br />
/etc/project1.cfg<br />
/Users/user/.config/project1.cfg<br />
</pre><br />
<br />
The output on FreeBSD 12.1 with FPC 3.0.4:<br />
<br />
<pre><br />
/etc/project1/<br />
/home/user/.config/project1/<br />
/etc/project1.cfg<br />
/home/user/.config/project1.cfg<br />
</pre><br />
<br />
The observant will have noticed a difference between the Windows and non-Windows operating systems output - the Windows output for the <syntaxhighlight lang="pascal" inline>WriteLn(GetAppConfigFile(True));</syntaxhighlight> global configuration code includes a subdirectory but the other operating systems do not. To obtain the same results for non-Windows operating systems, you need to include an additional [[Boolean]] parameter: <syntaxhighlight lang="pascal" inline>WriteLn(GetAppConfigFile(True,True));</syntaxhighlight>.<br />
<br />
{{Note| The use of UPX interferes with the use of the GetAppConfigDir and GetAppConfigFile functions.}}<br />
<br />
==== macOS ====<br />
<br />
In most cases configuration files are preference files, which in macOS should be XML files ending with the ".plist" extension and be stored in /Library/Preferences or ~/Library/Preferences with filenames taken from the "Bundle identifier" field in the [[macOS property list files|Info.plist]] file of the [[Application Bundle|application bundle]]. Using .config files in the user directory is a violation of the Apple programming guidelines. See the [[Locating macOS app support, preferences folders]] article for code which handles this in an Apple-compliant manner.<br />
<br />
=== Data and resource files ===<br />
<br />
A very common question is where to store data files an application might need, such as Images, Music, XML files, database files, help files, etc. Unfortunately there is no cross-platform function to get the best location to look for data files. The solution is to implement differently on each platform using IFDEFs.<br />
<br />
==== Windows ====<br />
<br />
On Windows, application data that the program modifies should not be put in the application's directory (e.g. C:\Program Files\) but in a specific location (see e.g. [http://support.microsoft.com/kb/310294 "Classify Application Data"] (''link is broken''). Windows Vista and newer actively enforce this (users only have write access to these directories when using elevation or disabling UAC) but uses a folder redirection mechanism to accommodate older, wrongly-programmed applications. Just reading, not writing, data from application directories would still work but is not recommended.<br />
<br />
In short: use such folder:<br />
<syntaxhighlight lang="pascal"><br />
OpDirLocal:= GetEnvironmentVariableUTF8('appdata')+'\MyAppName';<br />
</syntaxhighlight><br />
<br />
See [[Windows_Programming_Tips#Getting_special_folders_.28My_documents.2C_Desktop.2C_local_application_data.2C_etc.29|Windows Programming Tips - Getting Special Folders]]<br />
<br />
==== Unix/Linux ====<br />
On most Unixes (like Linux, FreeBSD, OpenBSD, Solaris but '''not macOS'''), application data files are located in a fixed location, which can be something like: /usr/local/share/app_name or /opt/app_name.<br />
<br />
Application data that needs to be written to by the application often gets stored in places like /usr/local/var/app_name, /usr/local/share/app_name or /usr/local/app_name, with appropriate permissions set.<br />
<br />
Help files (aka man pages) should be stored in /usr/local/man/man[appropriate manual section number]/app_name>, with appropriate permissions set. Note that some FreeBSD and Linux manual sections differ or do not exist.<br />
<br />
User-specific read/write config/data will normally be stored somewhere under the user's home directory (eg in ~/.config/<programname>). Refer to [[Multiplatform_Programming_Guide#Configuration_files|Configuration Files]] above.<br />
<br />
==== macOS ====<br />
<br />
macOS is an exception among UNIX operating systems. An application is published in a bundle (a directory with ".app" extension) which is treated by the Finder file manager as a file (in a Terminal you can "cd path/myapp.app" or in Finder right click on the app and choose "Show Package Contents"). Your resource files should be located inside the bundle. If the bundle is "path/MyApp.app", then the:<br />
<br />
* executable file is "path/MyApp.app/Contents/MacOS/myapp"<br />
* resources directory is "path/MyApp.app/Contents/Resources"<br />
<br />
Application supplied data and resource files should generally be stored in the Resources directory of the application bundle. For code which handles this in an Apple compliant manner, see the [[Locating the macOS application resources directory]] article.<br />
<br />
Application data files generated by the user should be stored in the user's '~/Library/Application Support' directory. For code which handles this in an Apple compliant manner, see the [[Locating macOS app support, preferences folders]] article.<br />
<br />
{{Warning|'''Never''' use <syntaxhighlight lang="pascal" inline>paramStr(0)</syntaxhighlight>, or any function which uses it, on any UNIX platform to ''determine'' the location of the executable, as this is a DOS-Windows-OS/2 convention and has several conceptual problems, which cannot be solved using emulation on other platforms. The only thing <syntaxhighlight lang="pascal" inline>paramStr(0)</syntaxhighlight> is guaranteed to return on UNIX platforms is the name with which the program was started. The directory in which it is located and the name of the actual binary (in case it was started using a symbolic link) are not guaranteed to be available via <syntaxhighlight lang="pascal" inline>paramStr(0)</syntaxhighlight>. For macOS, you can '''reliably''' locate the application bundle directory using the native code in [[Locating the macOS application resources directory|this article]].}}<br />
<br />
=== 32/64 bit ===<br />
<br />
====Detecting bitness at runtime====<br />
While you can control whether you compile for 32 or 64 bit with compiler defines, sometimes you want to know what bitness the operating system runs.<br />
For example, if you are running a 32 bit Lazarus program on 64 bit Windows, you might want to run an external program in a 32 bit program files directory, or you might want to give different information to users: I need this in my LazUpdater Lazarus installer to offer the user a choice of 32 and 64 bit compilers. Code: [[Detect Windows x32-x64 example]].<br />
<br />
====Detecting bitness of external library before loading it====<br />
When you want to load functions from dynamic library into your program, it has to have same bitness as your application. On 64 bit Windows, your application might be 32-bit or 64-bit, and there can be 32-bit and 64-bit libraries on your system.<br />
So you might want to check whether dll's bitness is same as your application's bitness before loading the dll dynamically. Here is a function which tests dll's bitness ([http://forum.lazarus.freepascal.org/index.php/topic,36834.msg245859.html#msg245859 contributed in forum by GetMem]):<br />
<syntaxhighlight lang="pascal"><br />
<br />
uses {..., } JwaWindows;<br />
<br />
function GetPEType(const APath: WideString): Byte;<br />
const<br />
PE_UNKNOWN = 0; //if the file is not a valid dll, 0 is returned<br />
// PE_16BIT = 1; // not supported by this function<br />
PE_32BIT = 2;<br />
PE_64BIT = 3;<br />
var<br />
hFile, hFileMap: THandle;<br />
PMapView: Pointer;<br />
PIDH: PImageDosHeader;<br />
PINTH: PImageNtHeaders;<br />
Base: Pointer;<br />
begin<br />
Result := PE_UNKNOWN;<br />
<br />
hFile := CreateFileW(PWideChar(APath), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);<br />
if hFile = INVALID_HANDLE_VALUE then<br />
begin<br />
CloseHandle(hFile);<br />
Exit;<br />
end;<br />
<br />
hFileMap := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);<br />
if hFileMap = 0 then<br />
begin<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
Exit;<br />
end;<br />
<br />
PMapView := MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);<br />
if PMapView = nil then<br />
begin<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
Exit;<br />
end;<br />
<br />
PIDH := PImageDosHeader(PMapView);<br />
if PIDH^.e_magic <> IMAGE_DOS_SIGNATURE then<br />
begin<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
UnmapViewOfFile(PMapView);<br />
Exit;<br />
end;<br />
<br />
Base := PIDH;<br />
PINTH := PIMAGENTHEADERS(Base + LongWord(PIDH^.e_lfanew));<br />
if PINTH^.Signature = IMAGE_NT_SIGNATURE then<br />
begin<br />
case PINTH^.OptionalHeader.Magic of<br />
$10b: Result := PE_32BIT;<br />
$20b: Result := PE_64BIT<br />
end;<br />
end;<br />
<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
UnmapViewOfFile(PMapView);<br />
end;<br />
<br />
//Now, if you compile your application for 32-bit and 64-bit windows, you can check if dll's bitness is same as your application's:<br />
function IsCorrectBitness(const APath: WideString): Boolean;<br />
begin <br />
{$ifdef CPU32}<br />
Result := GetPEType(APath) = 2; //the application is compiled as 32-bit, we ask if GetPeType returns 2<br />
{$endif}<br />
{$ifdef CPU64}<br />
Result := GetPEType(APath) = 3; //the application is compiled as 64-bit, we ask if GetPeType returns 3<br />
{$endif}<br />
end;<br />
</syntaxhighlight><br />
<br />
==== Pointer / Integer Typecasts ====<br />
<br />
Pointers under 64bit need 8 bytes instead of 4 on 32bit. The 'Integer' type remains 32bit on all platforms for compatibility. This means you can not typecast pointers into integers and back. <br />
<br />
FPC defines two types for this: PtrInt and PtrUInt. PtrInt is a 32bit signed integer on 32 bit platforms and a 64bit signed integer on 64bit platforms. The same for PtrUInt, but ''unsigned'' integer instead.<br />
<br />
Use for code that should work with Delphi and FPC:<br />
<syntaxhighlight lang="pascal"><br />
{$IFNDEF FPC}<br />
type<br />
PtrInt = integer;<br />
PtrUInt = cardinal;<br />
{$ENDIF}<br />
</syntaxhighlight><br />
<br />
Replace all '''integer(SomePointerOrObject)''' with '''PtrInt(SomePointerOrObject)'''.<br />
<br />
=== Endianess ===<br />
<br />
Intel platforms are little endian, that means the least significant byte comes first. For example the two bytes of a word $1234 is stored as $34 $12 on little endian systems.<br />
On big endian systems like the powerpc the two bytes of a word $1234 are stored as $12 $34. The difference is important when reading files created on other systems.<br />
<br />
Use for code that should work on both:<br />
<syntaxhighlight lang="pascal"><br />
{$IFDEF ENDIAN_BIG}<br />
...<br />
{$ELSE}<br />
...<br />
{$ENDIF}<br />
</syntaxhighlight><br />
<br />
The opposite is ENDIAN_LITTLE.<br />
<br />
The system unit provides plenty of endian converting functions, like SwapEndian, BEtoN (big endian to current endian), LEtoN (little endian to current endian), NtoBE (current endian to big endian) and NtoLE (current endian to little endian).<br />
<br />
<br />
==== Libc and other special units ====<br />
<br />
Avoid legacy units like "oldlinux" and "libc" that are not supported outside of linux/i386.<br />
<br />
==== Assembler ====<br />
<br />
Avoid [[Assembly language|assembler]].<br />
<br />
==== Compiler defines ====<br />
<br />
<syntaxhighlight lang="pascal"><br />
{$ifdef CPU32}<br />
...write here code for 32 bit processors<br />
{$ENDIF}<br />
{$ifdef CPU64}<br />
...write here code for 64 bit processors<br />
{$ENDIF}<br />
</syntaxhighlight><br />
<br />
=== Projects, packages and search paths ===<br />
<br />
Lazarus projects and packages are designed for multi platforms. Normally you can simply copy the project and the required packages to another machine and compile them there. You don't need to create one project per platform.<br />
<br />
Some advice to achieve this<br />
<br />
The compiler creates for every unit a ppu with the same name. This ppu can be used by other projects and packages. The unit source files (e.g. unit1.pas) should not be shared. Simply give the compiler a unit output directory where to create the ppu files. The IDE does that by default, so nothing to do for you here.<br />
<br />
Every unit file must be part of '''one''' project or package. If a unit file is only used by a single project, add it to this project. Otherwise add it to a package. If you have not yet created a package for your shared units, see here: [[Lazarus_Packages#Creating a package for your common units|Creating a package for your common units]]<br />
<br />
Every project and every package should have '''disjunct directories''' - they should not share directories. Otherwise you must be an expert in the art of compiler search paths. If you are not an expert or if others who may use your project/package are not experts: do not share directories between projects/packages.<br />
<br />
==== Platform specific units ====<br />
For example the unit wintricks.pas should only be used under Windows. In the uses section use:<br />
<br />
<syntaxhighlight lang="pascal"><br />
uses<br />
Classes, SysUtils<br />
{$IFDEF Windows}<br />
,WinTricks<br />
{$ENDIF}<br />
;<br />
</syntaxhighlight><br />
<br />
If the unit is part of a package, you must also select the unit in the package editor of the package and disable the ''Use unit'' checkbox.<br />
<br />
See also [[Lazarus_Packages#Platform_specific_units|Platform specific units]]<br />
<br />
==== Platform specific search paths ====<br />
<br />
When you target several platforms and access the operating system directly, then you will quickly get tired of endless IFDEF constructions. One solution that is used often in the FPC and Lazarus sources is to use include files. Create one sub directory per target. For example win32, linux, bsd, darwin. Put into each directory an include file with the same name. Then use a macro in the include path. The unit can use a normal include directive. <br />
<br />
An example for one include file for each LCL widget set:<br />
<br />
Create one file for each widget set you want to support:<br />
win32/example.inc<br />
gtk/example.inc<br />
gtk2/example.inc<br />
carbon/example.inc<br />
<br />
You do not need to add the files to the package or project.<br />
Add the include search path ''$(LCLWidgetType)'' to the compiler options of your package or project.<br />
<br />
In your unit use the directive:<br />
{$I example.inc}<br />
<br />
Here are some useful macros and common values:<br />
*LCLWidgetType: win32, gtk, gtk2, qt, carbon, fpgui, nogui<br />
*TargetOS: linux, win32, win64, wince, freebsd, netbsd, openbsd, darwin (many more)<br />
*TargetCPU: i386, x86_64, arm, powerpc, sparc<br />
*SrcOS: win, unix<br />
<br />
You can use the $Env() macro to use environment variables.<br />
<br />
And of course you can use combinations. For example the LCL uses: <br />
<br />
$(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS);$(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS)/$(LCLWidgetType)<br />
<br />
See here the complete list of macros: [[IDE Macros in paths and filenames]]<br />
<br />
==== Machine / User specific search paths ====<br />
<br />
For example you have two windows machines stan and oliver. On stan your units are in ''C:\units'' and on oliver your units are in ''D:\path''. The units belong to the package ''SharedStuff'' which is ''C:\units\sharedstuff.lpk'' on stan and ''D:\path\sharedstuff.lpk'' on oliver.<br />
Once you opened the lpk in the IDE or by lazbuild, the path is automatically stored in its configuration files (packagefiles.xml).<br />
When compiling a project that requires the package ''SharedStuff'', the IDE and lazbuild knows where it is. So no configuration is needed.<br />
<br />
If you have want to deploy a package over many machine or for all users of a machine (e.g. a pool for students), then you can add a lpl file in the lazarus source directory. See packager/globallinks for examples.<br />
<br />
=== Locale differences ===<br />
<br />
Some functions from Free Pascal, like StrToFloat behave differently depending on the current [[locale]]. For example, in the USA the [[DecimalSeparator|decimal separator]] is usually ".", but in many European and South American countries it is ",". This can be a problem as sometimes it is desired to have these functions behave in a fixed way, independently from the locale. <br />
An example is a file format with decimal points that always needs to be interpreted the same way.<br />
<br />
The next sections explain how to do that.<br />
<br />
==== macOS ====<br />
<br />
Refer to the [[Locale settings for macOS]] article for details of setting the locale on macOS.<br />
<br />
====StrToFloat====<br />
<br />
A new set of format settings which set a fixed decimal separator can be created with the following code:<br />
<br />
<syntaxhighlight lang="pascal"><br />
// in your .lpr project file<br />
uses<br />
...<br />
{$IFDEF UNIX}<br />
clocale <br />
{ required on Linux/Unix for formatsettings support. Should be one of the first (probably after cthreads?}<br />
{$ENDIF}<br />
</syntaxhighlight><br />
<br />
and:<br />
<br />
<syntaxhighlight lang="pascal"><br />
// in your code:<br />
var<br />
FPointSeparator, FCommaSeparator: TFormatSettings;<br />
begin<br />
// Format settings to convert a string to a float<br />
FPointSeparator := DefaultFormatSettings;<br />
FPointSeparator.DecimalSeparator := '.';<br />
FPointSeparator.ThousandSeparator := '#';// disable the thousand separator<br />
FCommaSeparator := DefaultFormatSettings;<br />
FCommaSeparator.DecimalSeparator := ',';<br />
FCommaSeparator.ThousandSeparator := '#';// disable the thousand separator<br />
</syntaxhighlight><br />
<br />
Later on you can use this format settings when calling StrToFloat, like this:<br />
<br />
<syntaxhighlight lang="pascal"><br />
// This function works like StrToFloat, but simply tries two possible decimal separator<br />
// This will avoid an exception when the string format doesn't match the locale<br />
function AnSemantico.StringToFloat(AStr: string): Double;<br />
begin<br />
if Pos('.', AStr) > 0 then Result := StrToFloat(AStr, FPointSeparator)<br />
else Result := StrToFloat(AStr, FCommaSeparator);<br />
end;<br />
</syntaxhighlight><br />
<br />
=== Gtk2 and masking FPU exceptions ===<br />
<br />
Gtk2 library changes the default value of FPU (floating point unit) exception mask. The consequence of this is that some floating point exceptions do not get raised if Gtk2 library is used by the application. That means that, if for example you develop a LCL application on Windows with win32/64 widgetset (which is Windows default) and plan to compile for Linux (where Gtk2 is default widgetset), you should keep this incompatibilities in mind.<br />
<br />
After [http://www.lazarus.freepascal.org/index.php/topic,13460.0.html this forum topic] and answers on [https://gitlab.com/freepascal.org/lazarus/lazarus/-/issues/19674 this bug report] it became clear that nothing can be done about this, so we must know what actually these differences are.<br />
<br />
Therefore, let's do a test:<br />
<br />
<syntaxhighlight lang="pascal"><br />
uses<br />
..., math,...<br />
<br />
{...}<br />
<br />
var<br />
FPUException: TFPUException;<br />
FPUExceptionMask: TFPUExceptionMask;<br />
begin<br />
FPUExceptionMask := GetExceptionMask;<br />
for FPUException := Low(TFPUException) to High(TFPUException) do begin<br />
write(FPUException, ' - ');<br />
if not (FPUException in FPUExceptionMask) then<br />
write('not ');<br />
<br />
writeln('masked!');<br />
end;<br />
readln;<br />
end.<br />
</syntaxhighlight><br />
<br />
Our simple program will get what FPC default is:<br />
<br />
<pre><br />
exInvalidOp - not masked!<br />
exDenormalized - masked!<br />
exZeroDivide - not masked!<br />
exOverflow - not masked!<br />
exUnderflow - masked!<br />
exPrecision - masked!<br />
</pre><br />
<br />
However, with Gtk2, only exOverflow is not masked.<br />
<br />
The consequence is that EInvalidOp and EZeroDivide exceptions do not get raised if the application links to Gtk2 library! Normally, dividing non-zero value by zero raises EZeroDivide exception and dividing zero by zero raises EInvalidOp. For example the code like this:<br />
<br />
<syntaxhighlight lang="pascal"><br />
var<br />
X, A, B: Double;<br />
// ...<br />
<br />
try<br />
X := A / B;<br />
// code block 1<br />
except <br />
// code block 2<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
will take different direction when compiled in application with Gtk2 widgetset. On win widgetset, when B equals zero, an exception will get raised (EZeroDivide or EInvalidOp, depending on whether A is zero) and "code block 2" will be executed. On Gtk2 X becomes [http://www.freepascal.org/docs-html/rtl/math/infinity.html Infinity], [http://www.freepascal.org/docs-html/rtl/math/neginfinity.html NegInfinity], or [http://www.freepascal.org/docs-html/rtl/math/nan.html NaN] and "code block 1" will be executed.<br />
<br />
We can think of different ways to overcome this inconsistency. Most of the time you can simply test if B equals zero and don't try the dividing in that case. However, sometimes you will need some different approach. So, take a look at the following examples:<br />
<br />
<syntaxhighlight lang="pascal"><br />
uses<br />
..., math,...<br />
<br />
//...<br />
var<br />
X, A, B: Double;<br />
Ind: Boolean;<br />
// ...<br />
try<br />
X := A / B;<br />
Ind := IsInfinite(X) or IsNan(X); // with gtk2, we fall here<br />
except <br />
Ind := True; // in windows, we fall here when B equals zero<br />
end;<br />
if Ind then begin<br />
// code block 2<br />
end else begin<br />
// code block 1<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
Or:<br />
<syntaxhighlight lang="pascal"><br />
uses<br />
..., math,...<br />
<br />
//...<br />
var<br />
X, A, B: Double;<br />
FPUExceptionMask: TFPUExceptionMask;<br />
// ...<br />
<br />
try<br />
FPUExceptionMask := GetExceptionMask;<br />
SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]); // unmask<br />
try<br />
X := A / B;<br />
finally<br />
SetExceptionMask(FPUExceptionMask); // return previous masking immediately, we must not let Gtk2 internals to be called without the mask<br />
end;<br />
// code block 1<br />
except <br />
// code block 2<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
Be cautious, do not do something like this (call LCL with still removed mask):<br />
<syntaxhighlight lang="pascal"><br />
try<br />
FPUExceptionMask := GetExceptionMask;<br />
SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]);<br />
try<br />
Edit1.Text := FloatToStr(A / B); // NO! Setting Edit's text goes down to widgetset internals and Gtk2 API must not be called without the mask!<br />
finally<br />
SetExceptionMask(FPUExceptionMask);<br />
end;<br />
// code block 1<br />
except <br />
// code block 2<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
But use an auxiliary variable:<br />
<syntaxhighlight lang="pascal"><br />
try<br />
FPUExceptionMask := GetExceptionMask;<br />
SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]);<br />
try<br />
X := A / B; // First, we set auxiliary variable X<br />
finally<br />
SetExceptionMask(FPUExceptionMask);<br />
end;<br />
Edit1.Text := FloatToStr(X); // Now we can set Edit's text.<br />
// code block 1<br />
except <br />
// code block 2<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
In all situations, when developing LCL applications, it is most important to know about this and to keep in mind that some floating point operations can go different way with different widgetsets. Then you can think of an appropriate way to workaround this, but this should not go unnoticed.<br />
<br />
==Issues when moving from Windows to *nix etc==<br />
Issues specific to Linux, macOS, Android and other Unixes are described here. Not all subjects may apply to all platforms<br />
<br />
=== On Unix there is no "application directory" ===<br />
<br />
Many programmers are used to calling ExtractFilePath(ParamStr(0)) or Application.ExeName to get the location of the executable, and then search for the necessary files for the program execution (Images, XML files, database files, etc) based on the location of the executable. This is wrong on unixes. The string on ParamStr(0) may contain a directory other than the one of the executable, and it also varies between different shell programs (sh, bash, etc).<br />
<br />
Even if Application.ExeName could in fact know the directory where the executable is, that file could be a symbolic link, so you could get the directory of the link instead (depending on the Linux kernel version, you either get the directory of the link or of the program binary itself).<br />
<br />
To avoid this read the sections about [[Multiplatform_Programming_Guide#Configuration_files|configuration files]] and [[Multiplatform_Programming_Guide#Data_and_resource_files|data files]].<br />
<br />
=== Making do without Windows COM Automation ===<br />
<br />
With Windows, COM Automation is a powerful way not only of manipulating other programs remotely but also for allowing other programs to manipulate your program. With Delphi you can make your program both an COM Automation client and a COM Automation server, meaning it can both manipulate other programs and in turn be manipulated by other programs. For examples, <br />
see [http://wiki.lazarus.freepascal.org/Office_Automation#Using_COM_Automation_to_interact_with_OpenOffice_and_Microsoft_Office Using COM Automation to interact with OpenOffice and Microsoft Office].<br />
<br />
==== macOS alternative ====<br />
Unfortunately, COM Automation isn't available on macOS and Linux. However, you can simulate some of the functionality of COM Automation on macOS using AppleScript.<br />
<br />
AppleScript is similar to COM Automation in some ways. For example, you can write scripts that manipulate other programs. Here's a very simple example of AppleScript that starts NeoOffice (the Mac version of OpenOffice.org):<br />
<br />
<syntaxhighlight lang="applescript"><br />
tell application "NeoOffice"<br />
launch<br />
end tell<br />
</syntaxhighlight><br />
<br />
An app that is designed to be manipulated by AppleScript provides a "dictionary" of classes and commands that can be used with the app, similar to the classes of a Windows Automation server. However, even apps like NeoOffice that don't provide a dictionary will still respond to the commands "launch", "activate" and "quit". AppleScript can be run from the macOS Script Editor or Finder or even converted to an app that you can drop on the dock just like any app. You can also run AppleScript from your program, as in this example:<br />
<br />
<syntaxhighlight lang="pascal"><br />
fpsystem('myscript.applescript');<br />
</syntaxhighlight><br />
<br />
This assumes the script is in the indicated file. You can also run scripts on the fly from your app using the macOS OsaScript command:<br />
<br />
<syntaxhighlight lang="pascal"><br />
fpsystem('osascript -e '#39'tell application "NeoOffice"'#39 +<br />
' -e '#39'launch'#39' -e '#39'end tell'#39);<br />
{Note use of #39 to single-quote the parameters}<br />
</syntaxhighlight><br />
<br />
However, these examples are just the equivalent of the following Open command:<br />
<br />
<syntaxhighlight lang="pascal"><br />
fpsystem('open -a NeoOffice');<br />
</syntaxhighlight><br />
<br />
Similarly, in macOS you can emulate the Windows shell commands to launch a web browser and launch an email client with:<br />
<br />
<syntaxhighlight lang="pascal"><br />
fpsystem('open -a safari "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');<br />
</syntaxhighlight><br />
<br />
and <br />
<br />
<syntaxhighlight lang="pascal"><br />
fpsystem('open -a mail "mailto:ss4200@invalid.org"');<br />
</syntaxhighlight><br />
<br />
which assumes, fairly safely, that a macOS system will have the Safari and Mail applications installed. Of course, you should never make assumptions like this, and for the two previous examples, you can in fact just rely on macOS to do the right thing and pick the user's default web browser and email client if you instead use these variations:<br />
<br />
<syntaxhighlight lang="pascal"><br />
fpsystem('open "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');<br />
</syntaxhighlight><br />
<br />
and <br />
<br />
<syntaxhighlight lang="pascal"><br />
fpsystem('open "mailto:ss4200@invalid.org"');<br />
</syntaxhighlight><br />
<br />
Do not forget to include the Unix unit in your uses clause if you use <code>fpsystem</code> or <code>shell</code> (interchangeable).<br />
<br />
The real power of AppleScript is to manipulate programs remotely to create and open documents and automate other activities. How much you can do with a program depends on how extensive its AppleScript dictionary is (if it has one). For example, Microsoft's Office X programs are not very usable with AppleScript, whereas the newer Office 2004 programs have completely rewritten AppleScript dictionaries that compare in many ways with what's available via the Windows Office Automation servers.<br />
<br />
==== Linux alternatives ====<br />
While Linux shells support sophisticated command line scripting, the type of scripting is limited to what can be passed to a program on the command line. There is no single, unified way to access a program's internal classes and commands with Linux the way they are via Windows COM Automation and macOS AppleScript. However, individual desktop environments (GNOME/KDE) and application frameworks often provide such methods of interprocess communication. On GNOME see Bonobo Components. KDE has the KParts framework, DCOP. OpenOffice has a platform neutral API for controlling the office remotely (google OpenOffice SDK) - though you would probably have to write glue code in another language that has bindings (such as Python) to use it. In addition, some applications have "server modes" activated by special command-line options that allow them to be controlled from another process. It is also possible (Borland did it with Kylix document browser) to "embed" one top-level X application window into another using XReparentWindow (I think).<br />
<br />
As with Windows, many macOS and Linux programs are made up of multiple library files (.dylib and .so extensions). Sometimes these libraries are designed so you can also use them in programs you write. While this can be a way of adding some of the functionality of an external program to your program, it's not really the same as running and manipulating the external program itself. Instead, your program is just linking to and using the external program's library similar to the way it would use any programming library.<br />
<br />
=== Alternatives for Windows API functions ===<br />
<br />
Many Windows programs use the Windows API extensively. In cross-platform applications Win API functions in the Windows unit should not be used, or should be enclosed by a conditional compile (e.g. {$IFDEF MSWINDOWS} ).<br />
<br />
Fortunately many of the commonly used Windows API functions are implemented in a multiplatform way in the unit [[lclintf]]. This can be a solution for programs which rely heavily on the Windows API, although the best solution is to replace these calls with true cross-platform components from the LCL. You can replace calls to GDI painting functions with calls to a TCanvas object's methods, for example.<br />
<br />
=== Key codes ===<br />
Fortunately, detecting key codes (e.g. on KeyUp events) is portable: see [[LCL Key Handling]].<br />
<br />
===Installing your application===<br />
See [[Deploying Your Application]].<br />
<br />
== See also ==<br />
<br />
* [[Writing portable code regarding the processor architecture]]<br />
* [[Introduction to platform-sensitive development]]<br />
* [[Apple-specific UI elements]]<br />
<br />
== External links ==<br />
<br />
* http://www.midnightbeach.com/KylixForDelphiProgrammers.html A guide for Windows programmers starting with Kylix. Many of concepts / code snippets apply to Lazarus.<br />
* http://www.stack.nl/~marcov/porting.pdf A guide for writing portable source code, mainly between different compilers.</div>Zoranhttps://wiki.freepascal.org/index.php?title=SetExceptionMask&diff=149700SetExceptionMask2022-01-27T10:06:03Z<p>Zoran: </p>
<hr />
<div>Q: I'm trying to "merge" an existing MS Visual C++ project with Lazarus by converting it to a DLL and then calling some of its entry points from the Lazarus app. Everything is OK until some floating-point operation used and I get the exception "0xC00002B5 Multiple floating point traps".<br />
<br />
A (forum member ''PascalDragon''): C code doesn't usually expect the floating point exceptions that FPC enables. Use [https://www.freepascal.org/docs-html/rtl/math/setexceptionmask.html '''SetExceptionMask'''] to disable the corresponding exceptions (especially overflow and underflow).<br />
<br />
'''Set8087CW''' procedure only sets the control word for the x87 FPU, but in case of x86_64 Windows only SSE is used, thus the corresponding SSE exceptions wouldn't be disabled. And on ARM64 it doesn't exist at all. So the correct solution is SetExceptionMask (which handles both the x87 and SSE) and not Set8087CW directly.<br />
<br />
Example from GTK2 Lazarus widgetset:<br />
<br />
<syntaxhighlight lang="pascal"><br />
uses Math;<br />
//...<br />
begin<br />
SetExceptionMask(GetExceptionMask + [exOverflow,exZeroDivide,exInvalidOp]);<br />
</syntaxhighlight><br />
<br />
When creating a cross-platform LCL application, it is important to be aware of different ExceptionMask settings with gtk2 (and gtk3 perhaps?) -- more details here: [[Multiplatform_Programming_Guide#Gtk2_and_masking_FPU_exceptions]].</div>Zoranhttps://wiki.freepascal.org/index.php?title=DateTimeCtrls_Package&diff=149656DateTimeCtrls Package2022-01-26T08:47:12Z<p>Zoran: dtpoResetSelection -- remove "added in trunk", as it's in new release now.</p>
<hr />
<div>== About ==<br />
<br />
The DateTimeCtrls package contains two controls:<br />
<br />
[[Image:TDateTimePicker.png]] [[#TDateTimePicker|TDateTimePicker]]<br />
and<br />
[[Image:TDBDateTimePicker.png]][[#TDBDateTimePicker|TDBDateTimePicker]]<br />
<br />
Delphi's VCL has a control named [http://docwiki.embarcadero.com/Libraries/en/Vcl.ComCtrls.TDateTimePicker TDateTimePicker], which I find very useful for editing dates. LCL, however, does not have this control.<br />
<br />
Therefore, I tried to create a cross-platform Lazarus control which would resemble VCL's TDateTimePicker as much as possible.<br />
<br />
The TDateTimePicker does not use [http://msdn.microsoft.com/en-us/library/system.windows.forms.datetimepicker.aspx native Win control]. It descends from LCL-s TCustomControl to be cross-platform. It has been tested on Windows with Win32/64 and qt widgetsets and on Linux with gtk2 and qt widgetsets.<br />
<br />
Note that the TDateTimePicker control does not descend from TEdit, so it does not have unnecessary caret. The VCL's control doesn't have caret either.<br />
<br />
== Author ==<br />
:[[User:Zoran| Zoran Vučenović]]<br />
<br />
== License ==<br />
Modified LGPL (same as the FPC RTL and the Lazarus LCL).<br />
<br />
== Getting the package ==<br />
This package is part of Lazarus distribution (since Lazarus 1.4). You can find it in <''your_lazarus_folder''>/components/datetimectrls.<br />
<br />
The components are installed on Component palette by default (if you build the IDE yourself, they are installed with "make bigide"). The components TDateTimePicker and TDBDateTimePicker are located in [[Common_Controls_tab|Common Controls]] and [[Data_Controls_tab|Data Controls]] palette pages, respectively.<br />
<br />
Since Lazarus 1.8 the designtime code is separated to another package DateTimeCtrlsDsgn. Normally, you should not care about this, but only if you are installing the controls in the IDE manually, you should know that it is actually the designtime package DateTimeCtrlsDsgn which is installed in the IDE.<br />
<br />
This separation of designtime code to different package is done to prevent the code which is needed only in design time to be linked in final executable. [[Lazarus_1.8.0_release_notes#DateTimeCtrls|See the 1.8 release notes.]]<br />
<br />
== TDateTimePicker [[Image:TDateTimePicker.png]] ==<br />
<br />
=== Properties ===<br />
<br />
I'll explain some properties of TDateTimePicker control:<br />
<br />
==== DateTime: TDateTime (public) ====<br />
<br />
:The DateTime value displayed on the control. This property is not published in object inspector, but its value is actually the same as Date and Time properties composed in one value. This property is provided to allow setting or reading of both date and time value at once in program code. In design time, Date and Time can be set in object inspector. [[#DateTimePicker Editor|There is also component editor]] which provides easy way of setting this property in design time.<br />
<br />
==== Date: TDate ====<br />
<br />
:The date displayed on the control which the user can edit.<br />
<br />
==== Time: TTime ====<br />
<br />
:The time displayed on the control which the user can edit.<br />
<br />
==== MinDate: TDate ====<br />
<br />
:The minimal date user can enter.<br />
<br />
==== MaxDate: TDate ====<br />
<br />
:The maximal date user can enter.<br />
<br />
==== NullInputAllowed: Boolean ====<br />
<br />
:When True, the user can set the date to NullDate constant by pressing N key.<br />
<br />
==== CenturyFrom: Word ====<br />
<br />
:When user enters the year in two-digit format, then the CenturyFrom property is used to determine which century the year belongs to. The default is 1941, which means that when two digit year is entered, it falls in interval 1941 – 2040. Note that [[#MinDate: TDate|MinDate]] and [[#MaxDate: TDate|MaxDate]] properties can also have influence on the decision – for example, if the CenturyFrom is set to 1941 and MaxDate to 31. 12. 2010, if user enters year 23, it will be set to 1923, because it can’t be 2033, due to MaxDate limit.<br />
<br />
==== Kind: TDateTimeKind ====<br />
<br />
:'''type''' TDateTimeKind = (dtkDate, dtkTime, dtkDateTime);<br />
<br />
:The control displays only date, only time or both.<br />
:[[Image:DateControls03.PNG]]<br />
<br />
==== DateMode: TDTDateMode ====<br />
<br />
:'''type''' TDTDateMode = (dmComboBox, dmUpDown, dmNone)<br />
<br />
:When DateMode is set to dmComboBox, there is a button on the right side of the control. When user clicks the button, [http://lazarus-ccr.sourceforge.net/docs/lcl/calendar/tcalendar.html the calendar control] is shown, allowing the user to pick the date. When set to dmUpDown, then UpDown buttons are shown.<br />
<br />
:In my opinion the UpDown buttons aren't really useful in this control, they are provided for compatibility with Delphi's TDateTimePicker. Up and down keys can always serve for same purpose, so can mouse wheel.<br />
<br />
:In the picture the first control's DateMode is set to dmComboBox and the second control's to dmUpDown:<br />
:[[Image:DateControls01.PNG]]<br />
<br />
==== ShowCheckBox: Boolean ====<br />
<br />
:When set, there is a check box on the left side of the control.<br />
:By default, when unchecked, the display appears grayed and user interaction with the date is not possible. (The control is still enabled, though, only in sense that the check box remains enabled). This behaviour may be changed if dtpoEnabledIfUnchecked is added to [[#Options:_TDateTimePickerOptions|Options property]] -- then this check box is shown, but does nothing by itself (doesn't disable the control) and the programmer can decide how it will be used (by using OnCheckBoxChange event).<br />
:[[Image:DateControls02.PNG]]<br />
<br />
==== Checked: Boolean ====<br />
<br />
:If [[#ShowCheckBox: Boolean|ShowCheckBox]] is set to True, this property determines whether the check box is checked or not. If ShowCheckBox is False, this property has no purpose and is automatically set to True.<br />
<br />
==== DateDisplayOrder: TDateDisplayOrder ====<br />
<br />
:'''type''' TDateDisplayOrder = (ddoDMY, ddoMDY, ddoYMD, ddoTryDefault);<br />
<br />
:Defines the order for displaying day, month and year part of the date. When ddoTryDefault is set, then the controls tries to determine the order from [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/shortdateformat.html ShortDateFormat global variable].<br />
:This is similar to DateEdit's [http://lazarus-ccr.sourceforge.net/docs/lcl/editbtn/tdateedit.dateorder.html DateOrder] property.<br />
<br />
==== DateSeparator: String ====<br />
<br />
:Defines the string used to separate date, month and year date parts. Setting this property automatically sets the [[#UseDefaultSeparators: Boolean|UseDefaultSeparators]] property to False. To ensure that date and time separators are set to user's system defaults, set UseDefaultSeparators property to True.<br />
<br />
==== TimeSeparator: String ====<br />
<br />
:Defines the string used to separate hour, minute, second and millisecond time parts. Setting this property automatically sets the [[#UseDefaultSeparators: Boolean|UseDefaultSeparators]] property to False. To ensure that date and time separators are set to user's system defaults, set UseDefaultSeparators property to True.<br />
<br />
==== UseDefaultSeparators: Boolean ====<br />
<br />
:When this property is set to True, then the [[#DateSeparator: String|DateSeparator]] and [[#TimeSeparator: String|TimeSeparator]] properties will be set to [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/dateseparator.html DateSeparator] and [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/timeseparator.html TimeSeparator] global variables, which are set to user system defaults when application initializes.<br />
<br />
==== TrailingSeparator: Boolean ====<br />
<br />
:When set to True, then the [[#DateSeparator: String|DateSeparator]] is shown once more, after the last date part. This property exists because in some languages the correct format is 31. 1. 2010. including the last point, after the year.<br />
<br />
==== LeadingZeros: Boolean ====<br />
<br />
:Determines whether the date parts are displayed with or without leading zeros (this actually affects day, month and hour parts of date and time display).<br />
<br />
==== TimeDisplay: TTimeDisplay ====<br />
<br />
:'''type''' TTimeDisplay = (tdHM, tdHMS, tdHMSMs);<br />
<br />
:If Kind is dtkTime or dtkDateTime, then TimeDisplay value of tdHM means that only hours and minutes are displayed, tdHMS adds displaying of seconds and value of tdHMSMs means that milliseconds are displayed too.<br />
<br />
==== TimeFormat: TTimeFormat ====<br />
<br />
:'''type''' TTimeFormat = (tf12, tf24);<br />
<br />
:The value of tf12 sets the display of time to 12 hours format, with AM/PM string and tf24 sets to 24 hours format.<br />
<br />
==== TextForNullDate: String ====<br />
<br />
:Text which appears when the null date is set and control does not have focus. When control is focused, the text changes to defined format, but displaying zeros, which is appropriate to user input. User can set the date to NullDate by pressing N key, provided [[#NullInputAllowed: Boolean|NullInputAllowed]] property is True.<br />
:When TextForNullDate is set to empty string, zeros/nines format is displayed even when control does not have focus. If you want empty display, this can be achieved by setting TextForNullDate to one or more space characters.<br />
<br />
==== AutoAdvance: Boolean ====<br />
<br />
:When true, then when user is entering valid text, the selection automatically advances to next part of date/time. The default is True, because it makes user interaction easier and this behaviour is what a user should expect.<br />
<br />
==== Cascade: Boolean ====<br />
<br />
:When true, then when user is increasing or decreasing one date/time part (using up-down keys or mouse wheel), it can increase or decrease by one another date/time part. For example, when date is 31.08.2013. and user increases the day, the day becomes 1 and month increases by one and becomes 9, so the date becomes 01.09.2013. If Cascade were set to False, the month would not change and the date would become 01.08.2013.<br />
<br />
==== AutoButtonSize: Boolean ====<br />
<br />
:When true, the width of the arrow button (or up-down control, if it is shown instead) is automatically adjusted proportionally to the height.<br />
<br />
==== HideDateTimeParts: TDateTimeParts ====<br />
<br />
:'''type'''<br />
<br />
::TDateTimePart = (dtpDay, dtpMonth, dtpYear, dtpHour, dtpMinute, dtpSecond, dtpMiliSec, dtpAMPM);<br />
<br />
::TDateTimeParts = set of dtpDay..dtpMiliSec;<br />
<br />
:With HideDateTimeParts property, you can chose which date/time parts will not be shown. Most of the time you do not need to use this property and you can get the format you want by using other properties (see Kind, TimeDisplay). However, if you need more control (for example, you might want to let user edit only days, months and hours), you can additionally hide any date/time parts with this control. Keep in mind that, with this property, you cannot show any date/time part which is hidden by another property (for example, if TimeDisplay is tdHM, the second part is not shown, regardless of this property).<br />
<br />
==== CalendarWrapperClass: TCalendarControlWrapperClass (public) ====<br />
<br />
:When assigned, this property determines the type of the calendar control used for drop-down calendar. When set to nil, which is the default, the value of global variable DefaultCalendarWrapperClass is used. [[#Using some custom calendar control for drop-down|More details here]].<br />
<br />
==== ShowMonthNames: Boolean ====<br />
:When this property is set to True, month names, set in [[#MonthNames: String|MonthNames]] property, will be displayed instead of numbers.<br />
<br />
==== MonthNames: String ====<br />
:When [[#ShowMonthNames: Boolean|ShowMonthNames]] is set to True, this property determines which month names should appear.<br />
:If MonthNames is set to 'Short' or 'Long', then month names are set to [http://www.freepascal.org/docs-html/rtl/sysutils/shortmonthnames.html ShortMonthNames] or [http://www.freepascal.org/docs-html/rtl/sysutils/longmonthnames.html LongMonthNames] respectively.<br />
:To set the month names explicitly, this property should be set to string which starts with a character which will be used to separate months and then twelve names separated by this character. For example ',I,II,III,IV,V,VI,VII,VIII,IX,X,XI,XII' — roman numbers are used for months — the first character is comma, which means that comma is used to separate the months. Another valid example ';jan;feb;mar;apr;maj;jun;jul;avg;sep;okt;nov;dec'.<br />
:So, the separator should be the first character, before the first month name, and then only if there are twelve month names separated by that separator, the format is valid.<br />
:The default value of this property is 'Long'.<br />
<br />
==== Options: TDateTimePickerOptions ====<br />
<br />
* dtpoDoChangeOnSetDateTime: the OnChange handler will be called also when DateTime is programatically changed.<br />
* dtpoEnabledIfUnchecked: enable the date time picker if the checkbox is unchecked.<br />
* dtpoAutoCheck: auto-check an unchecked checkbox when DateTime is changed (makes sense only if dtpoEnabledIfUnchecked is set).<br />
* dtpoFlatButton: use flat button for calendar picker.<br />
* dtpoResetSelection: when the control receives focus, the selection is always in the first part (the control does not remember which part was selected).<br />
<br />
=== DateTimePicker Editor ===<br />
<br />
[[Image:datetimepickereditor.png]]<br />
<br />
:DateTimePicker Editor is a dialog which provides easy way to edit Date, Time, MinDate and MaxDate properties in design time. It is invoked when DateTimePicker control is double-clicked in form designer. It is also shown when the ellipsis (…) button shown in Date, Time, MinDate and MaxDate properties in Object inspector gets clicked.<br />
<br />
<br />
=== User interaction in runtime ===<br />
<br />
:The goal was to provide same user interaction as native DateTimePicker from Windows. The keyboard input is restricted to valid date/time values only. Up and down buttons can be used too. The controls behave just like VCL's control.<br />
<br />
:After this behaviour is achieved it is further improved with mouse wheel interaction. Date and time parts can be edited using mouse wheel, which is very fast and comes handy.<br />
<br />
<br />
=== Using some custom calendar control for drop-down ===<br />
<br />
Instead of LCL's TCalendar, some other calendar control can be used for drop-down. This calendar control has to provide a way to determine if the coordinates (within the calendar control) are on the date or not [[#AreCoordinatesOnDate|(see AreCoordinatesOnDate function)]]. DateTimePicker needs it to decide if it should close the drop-down calendar and take its date when the calendar gets clicked (see AreCoordinatesOnDate function). The calendar only needs to be derived from TControl. The mouse interaction with calendar will work and, if it is also TWinControl's descendant, the keyboard interaction will also work.<br />
<br />
Now, let's see how to use some calendar control for drop-down control. First, we need to define the "wrapper" class, then we will have to tell the DateTimePicker control to use this wrapper instead of default one.<br />
<br />
==== Defining the wrapper class ====<br />
<br />
:In unit calendarcontrolwrapper.pas, there is an abstract class TCalendarControlWrapper. We need to derive a new class from this abstract class. There are four abstract methods which new class has to override - GetCalendarControlClass, SetDate, GetDate and AreCoordinatesOnDate.<br />
:This should be pretty simple, please see the file lclcalendarwrapper.pas, where TLCLCalendarWrapper (the default wrapper) is defined.<br />
<br />
===== GetCalendarControlClass =====<br />
<br />
:<syntaxhighlight lang=pascal><br />
class function GetCalendarControlClass: TControlClass; virtual abstract;<br />
</syntaxhighlight><br />
:This class function should return the class of the wrapped calendar control.<br />
<br />
===== SetDate =====<br />
<br />
:<syntaxhighlight lang=pascal><br />
procedure SetDate(Date: TDate); virtual abstract;<br />
</syntaxhighlight><br />
:Should be overridden to set the date in the calendar control.<br />
<br />
===== GetDate =====<br />
<br />
:<syntaxhighlight lang=pascal><br />
function GetDate: TDate; virtual abstract;<br />
</syntaxhighlight><br />
:Should be overridden to get the date from the calendar control.<br />
<br />
===== AreCoordinatesOnDate =====<br />
<br />
:<syntaxhighlight lang=pascal><br />
function AreCoordinatesOnDate(X, Y: Integer): Boolean; virtual abstract;<br />
</syntaxhighlight><br />
:This function should return True if coordinates (X, Y) are on the date in the calendar control (DateTimePicker calls this function when the calendar is clicked, to determine whether the drop-down calendar should return the date or not). The calendar control must at least provide a way to determine whether the coordinates are on the date (when this control gets clicked, we must decide if the date has just been chosen — then we should respond by closing the drop-down form and setting the date from calendar to DateTimePicker — for example in LCL's TCalendar we will respond when the calendar is clicked on date, but not when the user clicks in title area changing months or years, then we let the user keep browsing the calendar).<br />
<br />
==== Telling to the DateTimePicker to use new wrapper ====<br />
<br />
:Now, when we have the wrapper class defined, the easiest way to make all DateTimePickers use the new control is setting global variable DefaultCalendarWrapperClass (declared in unit datetimepicker.pas) and all DateTimePickers in the project will use the new control.<br />
<br />
:There is public property CalendarWrapperClass in DateTimePicker. We can set it and DateTimePicker will use it.<br />
<br />
:This pseudo code shows how a DateTimePicker decides which calendar control to use:<br />
<br />
<syntaxhighlight lang="text"><br />
if property DateTimePicker.CalendarWrapperClass is not nil<br />
the property is used<br />
else if global variable DefaultCalendarWrapperClass is not nil<br />
the global variable is used<br />
else<br />
LCL's TCalendar (implemented through TLCLCalendarWrapper class) is used<br />
</syntaxhighlight><br />
<br />
== TDBDateTimePicker [[Image:TDBDateTimePicker.png]] ==<br />
<br />
:TDBDateTimePicker is a data-aware version of TDateTimePicker, with nice way of handling null database values.<br />
<br />
=== Handling of null values ===<br />
<br />
==== Displaying null values ====<br />
<br />
:When the underlying DB field has null value, then:<br />
<br />
::If the control is not focused, then it displays the text defined in [[#TextForNullDate: String|TextForNullDate]] property. The default is "NULL".<br />
<br />
::When the control gets focus, the text changes to defined format, but displaying zeros for date parts and nines for time parts (for example "00/00/0000 99:99:99"), which is appropriate to user input.<br />
<br />
==== Setting the field value to null ====<br />
<br />
::If [[#NullInputAllowed: Boolean|NullInputAllowed]] property is True, the user can set the date and time to null, by pressing N key.<br />
<br />
[[Category:Lazarus]]<br />
[[Category:Components]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=DateTimeCtrls_Package&diff=141273DateTimeCtrls Package2020-11-28T09:45:09Z<p>Zoran: Reverting previous revision</p>
<hr />
<div>== About ==<br />
<br />
The DateTimeCtrls package contains two controls:<br />
<br />
[[Image:TDateTimePicker.png]] [[#TDateTimePicker|TDateTimePicker]]<br />
and<br />
[[Image:TDBDateTimePicker.png]][[#TDBDateTimePicker|TDBDateTimePicker]]<br />
<br />
Delphi's VCL has a control named [http://docwiki.embarcadero.com/Libraries/en/Vcl.ComCtrls.TDateTimePicker TDateTimePicker], which I find very useful for editing dates. LCL, however, does not have this control.<br />
<br />
Therefore, I tried to create a cross-platform Lazarus control which would resemble VCL's TDateTimePicker as much as possible.<br />
<br />
The TDateTimePicker does not use [http://msdn.microsoft.com/en-us/library/system.windows.forms.datetimepicker.aspx native Win control]. It descends from LCL-s TCustomControl to be cross-platform. It has been tested on Windows with Win32/64 and qt widgetsets and on Linux with gtk2 and qt widgetsets.<br />
<br />
Note that the TDateTimePicker control does not descend from TEdit, so it does not have unnecessary caret. The VCL's control doesn't have caret either.<br />
<br />
== Author ==<br />
:[[User:Zoran| Zoran Vučenović]]<br />
<br />
== License ==<br />
Modified LGPL (same as the FPC RTL and the Lazarus LCL).<br />
<br />
== Getting the package ==<br />
This package is part of Lazarus distribution (since Lazarus 1.4). You can find it in <''your_lazarus_folder''>/components/datetimectrls.<br />
<br />
The components are installed on Component palette by default (if you build the IDE yourself, they are installed with "make bigide"). The components TDateTimePicker and TDBDateTimePicker are located in [[Common_Controls_tab|Common Controls]] and [[Data_Controls_tab|Data Controls]] palette pages, respectively.<br />
<br />
Since Lazarus 1.8 the designtime code is separated to another package DateTimeCtrlsDsgn. Normally, you should not care about this, but only if you are installing the controls in the IDE manually, you should know that it is actually the designtime package DateTimeCtrlsDsgn which is installed in the IDE.<br />
<br />
This separation of designtime code to different package is done to prevent the code which is needed only in design time to be linked in final executable. [[Lazarus_1.8.0_release_notes#DateTimeCtrls|See the 1.8 release notes.]]<br />
<br />
== TDateTimePicker [[Image:TDateTimePicker.png]] ==<br />
<br />
=== Properties ===<br />
<br />
I'll explain some properties of TDateTimePicker control:<br />
<br />
==== DateTime: TDateTime (public) ====<br />
<br />
:The DateTime value displayed on the control. This property is not published in object inspector, but its value is actually the same as Date and Time properties composed in one value. This property is provided to allow setting or reading of both date and time value at once in program code. In design time, Date and Time can be set in object inspector. [[#DateTimePicker Editor|There is also component editor]] which provides easy way of setting this property in design time.<br />
<br />
==== Date: TDate ====<br />
<br />
:The date displayed on the control which the user can edit.<br />
<br />
==== Time: TTime ====<br />
<br />
:The time displayed on the control which the user can edit.<br />
<br />
==== MinDate: TDate ====<br />
<br />
:The minimal date user can enter.<br />
<br />
==== MaxDate: TDate ====<br />
<br />
:The maximal date user can enter.<br />
<br />
==== NullInputAllowed: Boolean ====<br />
<br />
:When True, the user can set the date to NullDate constant by pressing N key.<br />
<br />
==== CenturyFrom: Word ====<br />
<br />
:When user enters the year in two-digit format, then the CenturyFrom property is used to determine which century the year belongs to. The default is 1941, which means that when two digit year is entered, it falls in interval 1941 – 2040. Note that [[#MinDate: TDate|MinDate]] and [[#MaxDate: TDate|MaxDate]] properties can also have influence on the decision – for example, if the CenturyFrom is set to 1941 and MaxDate to 31. 12. 2010, if user enters year 23, it will be set to 1923, because it can’t be 2033, due to MaxDate limit.<br />
<br />
==== Kind: TDateTimeKind ====<br />
<br />
:'''type''' TDateTimeKind = (dtkDate, dtkTime, dtkDateTime);<br />
<br />
:The control displays only date, only time or both.<br />
:[[Image:DateControls03.PNG]]<br />
<br />
==== DateMode: TDTDateMode ====<br />
<br />
:'''type''' TDTDateMode = (dmComboBox, dmUpDown, dmNone)<br />
<br />
:When DateMode is set to dmComboBox, there is a button on the right side of the control. When user clicks the button, [http://lazarus-ccr.sourceforge.net/docs/lcl/calendar/tcalendar.html the calendar control] is shown, allowing the user to pick the date. When set to dmUpDown, then UpDown buttons are shown.<br />
<br />
:In my opinion the UpDown buttons aren't really useful in this control, they are provided for compatibility with Delphi's TDateTimePicker. Up and down keys can always serve for same purpose, so can mouse wheel.<br />
<br />
:In the picture the first control's DateMode is set to dmComboBox and the second control's to dmUpDown:<br />
:[[Image:DateControls01.PNG]]<br />
<br />
==== ShowCheckBox: Boolean ====<br />
<br />
:When set, there is a check box on the left side of the control.<br />
:By default, when unchecked, the display appears grayed and user interaction with the date is not possible. (The control is still enabled, though, only in sense that the check box remains enabled). This behaviour may be changed if dtpoEnabledIfUnchecked is added to [[#Options:_TDateTimePickerOptions|Options property]] -- then this check box is shown, but does nothing by itself (doesn't disable the control) and the programmer can decide how it will be used (by using OnCheckBoxChange event).<br />
:[[Image:DateControls02.PNG]]<br />
<br />
==== Checked: Boolean ====<br />
<br />
:If [[#ShowCheckBox: Boolean|ShowCheckBox]] is set to True, this property determines whether the check box is checked or not. If ShowCheckBox is False, this property has no purpose and is automatically set to True.<br />
<br />
==== DateDisplayOrder: TDateDisplayOrder ====<br />
<br />
:'''type''' TDateDisplayOrder = (ddoDMY, ddoMDY, ddoYMD, ddoTryDefault);<br />
<br />
:Defines the order for displaying day, month and year part of the date. When ddoTryDefault is set, then the controls tries to determine the order from [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/shortdateformat.html ShortDateFormat global variable].<br />
:This is similar to DateEdit's [http://lazarus-ccr.sourceforge.net/docs/lcl/editbtn/tdateedit.dateorder.html DateOrder] property.<br />
<br />
==== DateSeparator: String ====<br />
<br />
:Defines the string used to separate date, month and year date parts. Setting this property automatically sets the [[#UseDefaultSeparators: Boolean|UseDefaultSeparators]] property to False. To ensure that date and time separators are set to user's system defaults, set UseDefaultSeparators property to True.<br />
<br />
==== TimeSeparator: String ====<br />
<br />
:Defines the string used to separate hour, minute, second and millisecond time parts. Setting this property automatically sets the [[#UseDefaultSeparators: Boolean|UseDefaultSeparators]] property to False. To ensure that date and time separators are set to user's system defaults, set UseDefaultSeparators property to True.<br />
<br />
==== UseDefaultSeparators: Boolean ====<br />
<br />
:When this property is set to True, then the [[#DateSeparator: String|DateSeparator]] and [[#TimeSeparator: String|TimeSeparator]] properties will be set to [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/dateseparator.html DateSeparator] and [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/timeseparator.html TimeSeparator] global variables, which are set to user system defaults when application initializes.<br />
<br />
==== TrailingSeparator: Boolean ====<br />
<br />
:When set to True, then the [[#DateSeparator: String|DateSeparator]] is shown once more, after the last date part. This property exists because in some languages the correct format is 31. 1. 2010. including the last point, after the year.<br />
<br />
==== LeadingZeros: Boolean ====<br />
<br />
:Determines whether the date parts are displayed with or without leading zeros (this actually affects day, month and hour parts of date and time display).<br />
<br />
==== TimeDisplay: TTimeDisplay ====<br />
<br />
:'''type''' TTimeDisplay = (tdHM, tdHMS, tdHMSMs);<br />
<br />
:If Kind is dtkTime or dtkDateTime, then TimeDisplay value of tdHM means that only hours and minutes are displayed, tdHMS adds displaying of seconds and value of tdHMSMs means that milliseconds are displayed too.<br />
<br />
==== TimeFormat: TTimeFormat ====<br />
<br />
:'''type''' TTimeFormat = (tf12, tf24);<br />
<br />
:The value of tf12 sets the display of time to 12 hours format, with AM/PM string and tf24 sets to 24 hours format.<br />
<br />
==== TextForNullDate: String ====<br />
<br />
:Text which appears when the null date is set and control does not have focus. When control is focused, the text changes to defined format, but displaying zeros, which is appropriate to user input. User can set the date to NullDate by pressing N key, provided [[#NullInputAllowed: Boolean|NullInputAllowed]] property is True.<br />
:When TextForNullDate is set to empty string, zeros/nines format is displayed even when control does not have focus. If you want empty display, this can be acchieved by setting TextForNullDate to one or more space characters.<br />
<br />
==== AutoAdvance: Boolean ====<br />
<br />
:When true, then when user is entering valid text, the selection automatically advances to next part of date/time. The default is True, because it makes user interaction easier and this behaviour is what a user should expect.<br />
<br />
==== Cascade: Boolean ====<br />
<br />
:When true, then when user is increasing or decreasing one date/time part (using up-down keys or mouse wheel), it can increase or decrease by one another date/time part. For example, when date is 31.08.2013. and user increases the day, the day becomes 1 and month increases by one and becomes 9, so the date becomes 01.09.2013. If Cascade were set to False, the month would not change and the date would become 01.08.2013.<br />
<br />
==== AutoButtonSize: Boolean ====<br />
<br />
:When true, the width of the arrow button (or up-down control, if it is shown instead) is automatically adjusted proportionally to the height.<br />
<br />
==== HideDateTimeParts: TDateTimeParts ====<br />
<br />
:'''type'''<br />
<br />
::TDateTimePart = (dtpDay, dtpMonth, dtpYear, dtpHour, dtpMinute, dtpSecond, dtpMiliSec, dtpAMPM);<br />
<br />
::TDateTimeParts = set of dtpDay..dtpMiliSec;<br />
<br />
:With HideDateTimeParts property, you can chose which date/time parts will not be shown. Most of the time you do not need to use this property and you can get the format you want by using other properties (see Kind, TimeDisplay). However, if you need more control (for example, you might want to let user edit only days, months and hours), you can additionally hide any date/time parts with this control. Keep in mind that, with this property, you cannot show any date/time part which is hidden by another property (for example, if TimeDisplay is tdHM, the second part is not shown, regardless of this property).<br />
<br />
==== CalendarWrapperClass: TCalendarControlWrapperClass (public) ====<br />
<br />
:When assigned, this property determines the type of the calendar control used for drop-down calendar. When set to nil, which is the default, the value of global variable DefaultCalendarWrapperClass is used. [[#Using some custom calendar control for drop-down|More details here]].<br />
<br />
==== ShowMonthNames: Boolean ====<br />
:When this property is set to True, month names, set in [[#MonthNames: String|MonthNames]] property, will be displayed instead of numbers.<br />
<br />
==== MonthNames: String ====<br />
:When [[#ShowMonthNames: Boolean|ShowMonthNames]] is set to True, this property determines which month names should appear.<br />
:If MonthNames is set to 'Short' or 'Long', then month names are set to [http://www.freepascal.org/docs-html/rtl/sysutils/shortmonthnames.html ShortMonthNames] or [http://www.freepascal.org/docs-html/rtl/sysutils/longmonthnames.html LongMonthNames] respectively.<br />
:To set the month names explicitly, this property should be set to string which starts with a character which will be used to separate months and then twelve names separated by this character. For example ',I,II,III,IV,V,VI,VII,VIII,IX,X,XI,XII' — roman numbers are used for months — the first character is comma, which means that comma is used to separate the months. Another valid example ';jan;feb;mar;apr;maj;jun;jul;avg;sep;okt;nov;dec'.<br />
:So, the separator should be the first character, before the first month name, and then only if there are twelve month names separated by that separator, the format is valid.<br />
:The default value of this property is 'Long'.<br />
<br />
==== Options: TDateTimePickerOptions ====<br />
<br />
* dtpoDoChangeOnSetDateTime: the OnChange handler will be called also when DateTime is programatically changed.<br />
* dtpoEnabledIfUnchecked: enable the date time picker if the checkbox is unchecked.<br />
* dtpoAutoCheck: auto-check an unchecked checkbox when DateTime is changed (makes sense only if dtpoEnabledIfUnchecked is set).<br />
* dtpoFlatButton: use flat button for calender picker.<br />
'''added in trunk 2.1:'''<br />
* dtpoResetSelection: when the control receives focus, the selection is always in the first part (the control does not remember which part was selected).<br />
<br />
=== DateTimePicker Editor ===<br />
<br />
[[Image:datetimepickereditor.png]]<br />
<br />
:DateTimePicker Editor is a dialog which provides easy way to edit Date, Time, MinDate and MaxDate properties in design time. It is invoked when DateTimePicker control is double-clicked in form designer. It is also shown when the ellipsis (…) button shown in Date, Time, MinDate and MaxDate properties in Object inspector gets clicked.<br />
<br />
<br />
=== User interaction in runtime ===<br />
<br />
:The goal was to provide same user interaction as native DateTimePicker from Windows. The keyboard input is restricted to valid date/time values only. Up and down buttons can be used too. The contros behaves just like VCL's control.<br />
<br />
:After this behaviour is achieved it is further improved with mouse wheel interaction. Date and time parts can be edited using mouse wheel, which is very fast and comes handy.<br />
<br />
<br />
=== Using some custom calendar control for drop-down ===<br />
<br />
Instead of LCL's TCalendar, some other calendar control can be used for drop-down. This calendar control has to provide a way to determine if the coordinates (within the calendar control) are on the date or not [[#AreCoordinatesOnDate|(see AreCoordinatesOnDate function)]]. DateTimePicker needs it to decide if it should close the drop-down calendar and take its date when the calendar gets clicked (see AreCoordinatesOnDate function). The calendar only needs to be derived from TControl. The mouse interaction with calendar will work and, if it is also TWinControl's descendent, the keyboard interaction will also work.<br />
<br />
Now, let's see how to use some calendar control for drop-down control. First, we need to define the "wrapper" class, then we will have to tell the DateTimePicker control to use this wrapper instead of default one.<br />
<br />
==== Definning the wrapper class ====<br />
<br />
:In unit calendarcontrolwrapper.pas, there is an abstract class TCalendarControlWrapper. We need to derive a new class from this abstract class. There are four abstract methods which new class has to override - GetCalendarControlClass, SetDate, GetDate and AreCoordinatesOnDate.<br />
:This should be pretty simple, please see the file lclcalendarwrapper.pas, where TLCLCalendarWrapper (the default wrapper) is defined.<br />
<br />
===== GetCalendarControlClass =====<br />
<br />
:<syntaxhighlight lang=pascal><br />
class function GetCalendarControlClass: TControlClass; virtual abstract;<br />
</syntaxhighlight><br />
:This class function should return the class of the wrapped calendar control.<br />
<br />
===== SetDate =====<br />
<br />
:<syntaxhighlight lang=pascal><br />
procedure SetDate(Date: TDate); virtual abstract;<br />
</syntaxhighlight><br />
:Should be overriden to set the date in the calendar control.<br />
<br />
===== GetDate =====<br />
<br />
:<syntaxhighlight lang=pascal><br />
function GetDate: TDate; virtual abstract;<br />
</syntaxhighlight><br />
:Should be overriden to get the date from the calendar control.<br />
<br />
===== AreCoordinatesOnDate =====<br />
<br />
:<syntaxhighlight lang=pascal><br />
function AreCoordinatesOnDate(X, Y: Integer): Boolean; virtual abstract; <br />
</syntaxhighlight><br />
:This function should return True if coordinates (X, Y) are on the date in the calendar control (DateTimePicker calls this function when the calendar is clicked, to determine whether the drop-down calendar should return the date or not). The calendar control must at least provide a way to determine whether the coordinates are on the date (when this control gets clicked, we must decide if the date has just been chosen — then we should respond by closing the drop-down form and setting the date from calendar to DateTimePicker — for example in LCL's TCalendar we will respond when the calendar is clicked on date, but not when the user clicks in title area changing months or years, then we let the user keep browsing the calendar).<br />
<br />
==== Telling to the DateTimePicker to use new wrapper ====<br />
<br />
:Now, when we have the wrapper class defined, the easiest way to make all DateTimePickers use the new control is setting global variable DefaultCalendarWrapperClass (declared in unit datetimepicker.pas) and all DateTimePickers in the project will use the new control.<br />
<br />
:There is public property CalendarWrapperClass in DateTimePicker. We can set it and DateTimePicker will use it.<br />
<br />
:This pseudo code shows how a DateTimePicker decides which calendar control to use:<br />
<br />
<syntaxhighlight lang="text"><br />
if property DateTimePicker.CalendarWrapperClass is not nil<br />
the property is used<br />
else if global variable DefaultCalendarWrapperClass is not nil<br />
the global variable is used<br />
else<br />
LCL's TCalendar (implemented through TLCLCalendarWrapper class) is used<br />
</syntaxhighlight><br />
<br />
== TDBDateTimePicker [[Image:TDBDateTimePicker.png]] ==<br />
<br />
:TDBDateTimePicker is a data-aware version of TDateTimePicker, with nice way of handling null database values.<br />
<br />
=== Handling of null values ===<br />
<br />
==== Displaying null values ====<br />
<br />
:When the underlying DB field has null value, then:<br />
<br />
::If the control is not focused, then it displays the text defined in [[#TextForNullDate: String|TextForNullDate]] property. The default is "NULL".<br />
<br />
::When the control gets focus, the text changes to defined format, but displaying zeros for date parts and nines for time parts (for example "00/00/0000 99:99:99"), which is appropriate to user input.<br />
<br />
==== Setting the field value to null ====<br />
<br />
::If [[#NullInputAllowed: Boolean|NullInputAllowed]] property is True, the user can set the date and time to null, by pressing N key.<br />
<br />
[[Category:Lazarus]]<br />
[[Category:Components]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=Talk:DateTimeCtrls_Package&diff=141272Talk:DateTimeCtrls Package2020-11-28T09:43:13Z<p>Zoran: Created page with "The revision of this page by DSiders, made on 2020-09-05, surely did correct some small language mistakes. However, in my opinion, it went too far. There certainly was no need..."</p>
<hr />
<div>The revision of this page by DSiders, made on 2020-09-05, surely did correct some small language mistakes. However, in my opinion, it went too far.<br />
There certainly was no need to correct "behaviour" (perfectly correct spelling) to "behavior".<br />
There were also some word reorderings which did not add any more clarity to the content, but broke text links.<br />
All in all, I am reverting it as a whole.</div>Zoranhttps://wiki.freepascal.org/index.php?title=SimpleIPC&diff=137777SimpleIPC2020-07-12T16:15:41Z<p>Zoran: </p>
<hr />
<div>Simple IPC is a Free Pascal unit (and Lazarus components) that allows interprocess communication (IPC) between Free Pascal programs.<br />
<br />
It allows two executables to communicate.<br />
<br />
==Advantages of SimpleIPC==<br />
<br />
You can create communication systems without requiring low level sockets programming of your own. SimpleIPC does the grunt work of IPC for you and makes a nice high level wrapper around lower level IPC mechanisms, so you don't have to program them yourself.<br />
<br />
SimpleIPC can be used to communicate between programs to make simple communication systems, plugin systems and much more,<br />
<br />
==Use Cases==<br />
<br />
The Lazarus help system itself, uses SimpleIPC.<br />
<br />
Some [[fpGUI]] tools and demos use IPC, for example, to detect if the program is already running (single instance)<br />
<br />
==Send Messages to Other Programming Languages==<br />
<br />
For IPC between Free Pascal programs and other programs written in any language (C++, Delphi, GoLang, etc.) see [[SimpleIPC Library]] which allows you to use SimpleIPC not just in FPC and Lazarus, but in multiple programming languages.<br />
<br />
==See also==<br />
[https://www.freepascal.org/docs-html/current/fcl/simpleipc/index.html Official FCL documentation for SimpleIPC unit]<br />
<br />
<br />
[[Category:FPC]]<br />
[[Category:Lazarus]]<br />
[[Category:Inter-process communication]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=Installing_Lazarus&diff=136244Installing Lazarus2020-05-13T07:08:27Z<p>Zoran: Replace .var links with .html</p>
<hr />
<div>{{Installing Lazarus}}<br />
<br />
= Overview =<br />
<br />
A real "in depth" build guide is [http://www.stack.nl/~marcov/buildfaq.pdf here].<br />
<br />
For binary downloads of Lazarus see [http://wiki.lazarus.freepascal.org/Getting_Lazarus#Download_and_install_Lazarus_release_version Download and install Lazarus release version]<br />
<br />
For people who simply want to install Lazarus and start using it for programming, the easiest approach is to download and install a recent, reasonably stable binary release (such as a Linux ".rpm" package, a Windows ".exe" installer, or a macOS ".dmg" package). You can read the sections under Linux or Windows entitled "fpc binaries" or the first paragraphs in the sections on installing Lazarus in Linux or Windows; most of the remaining information can be safely ignored.<br />
<br />
For those who want to participate in the development of the compiler or the IDE, or for those who want the most up-to-date tools, an installation from source files is necessary, and much of the rest of this information is relevant.<br />
<br />
Lazarus provides two main parts:<br />
* LCL - the Lazarus Component Library<br />
* IDE - the RAD tool<br />
<br />
These in turn are dependent on:<br />
* FPC - the Free Pascal compiler<br />
* FCL - the Free Pascal Component library, containing most of the non-graphic components used by Lazarus<br />
<br />
== Lazarus System Requirements ==<br />
<br />
# Free Pascal compiler, packages, and sources. (*important*: of the same version/date)<br />
# A supported widget set<br />
#:;Win32/Win64: The native Win32 API can be used, or Qt widgetset.<br />
#:;Linux/BSD: GTK+ 2.x or Qt : Most Linux distributions and *BSDs already install the GTK+ 2.x libraries. You can also find them at http://www.gtk.org. <br/>Qt is also supported with all distributions (auto installed if you prefer KDE). <br> <br />
#:;macOS: You need the Apple Xcode developer tools. See [[#macOS|Installing under macOS]] below. <br/> For macOS versions before 10.15 (Catalina), 32 bit Carbon or 64 bit Cocoa can be used. <br/> For macOS 10.15 onwards, 64 bit Cocoa must be used as all 32 bit support has been removed by Apple.<br/> Qt can be used too, but requires much more effort.<br />
<br />
The FAQ - Frequently Asked Questions file is available at http://www.lazarus.freepascal.org. Some of the questions can be found in the local file 'FAQ'.<br />
<br />
The Qt widget set is supported on Linux 32/64, Win 32/64, macOS 32/64, FreeBSD 32/64, Haiku and embedded Linux (qtopia) platforms.<br />
<br />
More about Qt installation, see the [[Qt Interface]] article.<br />
<br />
The following sections describe how to get Free Pascal and how to install Lazarus properly.<br />
<br />
= Quick Start Guides =<br />
<br />
While the remainder of this page has much valuable information, many users may need no more than the following "quick start guides" - perhaps ? <br />
<br />
== FreeBSD ==<br />
<br />
* See [[Installing Lazarus on FreeBSD]] <br />
<br />
==Linux==<br />
<br />
* See [[Installing Lazarus on Linux]]<br />
<br />
== macOS ==<br />
<br />
* See [[Installing Lazarus on macOS]]<br />
<br />
== Windows ==<br />
<br />
By far the easiest and most common way to install Lazarus on windows is to go to the Lazarus SourceForge download site, https://sourceforge.net/projects/lazarus/files/ select an appropriate combined FPC/Lazarus package, download and install.<br />
<br />
= Installing The Free Pascal Compiler =<br />
<br />
There is an extensive discussion of how to install and build Free Pascal compilers available here http://www.stack.nl/~marcov/buildfaq.pdf - it may be a little too detailed for some users, but is very comprehensive.<br />
<br />
== Linux ==<br />
<br />
'''FPC binaries'''<br />
<br />
The latest release of Free Pascal can be obtained from the Free Pascal website (https://www.freepascal.org/download.html, then choose a mirror) or from SourceForge (http://sourceforge.net/projects/lazarus). <br />
<br />
At the Lazarus downloads section (http://www.lazarus.freepascal.org) you can get the RPM or DEB of the Free Pascal compiler (compiler / Linux) and its packages. If you don't have an RPM-based or Debian-based distribution, you can download and install the tarballs from http://www.freepascal.org. If you want to compile the binaries for yourself, see the BSD section.<br />
<br />
Instructions: <br />
<br />
{{Warning| If you're '''not using RPMs or Debian packages''' (even if you plan to use alien) it's best to get latest stable FPC and install Lazarus from source.}}<br />
<br />
Start Linux and login as '''root'''.<br />
<br />
Download latest files from https://sourceforge.net/projects/lazarus/files/.<br />
<br />
As example:<br />
:* lazarus-2.0.4-0.x86_64.rpm<br />
:* fpc-3.0.4-1.x86_64.rpm<br />
:* fpc-src-3.0.4-1.x86_64.rpm<br />
<br />
and install them with:<br />
<br />
:* rpm -Uvh *.rpm<br />
<br />
Debian users are recommended to use the deb packages, but may use either alien (warning, it doesn't generate fpc.cfg) or the tarball install.<br />
<br />
'''FPC sources'''<br />
<br />
FPC source files are stored in a Subversion (SVN) repository that keeps track of all changes of the source tree.<br />
Once you have the sources, please see [[#From_source_on_Linux.2FBSD| installing Free Pascal from source under Linux/BSD]] for instructions on how to install them.<br />
<br />
'''Download daily source snapshot of development tree'''<br />
<br />
You can download today's development sources in the form of a packed snapshot from the SVN source repository: these snapshots are updated on a daily basis, and reflect the state of the source repository. They are not tested, might not work or even crash your system. The files are kept at the site which has the SVN archive. The version of FPC used can vary.<br />
Location: [http://www.hu.freepascal.org/lazarus/]<br />
<br />
'''Update source repository using SVN'''<br />
<br />
As an alternative to the daily zip files of the SVN sources, the SVN repository has been made accessible for everyone, with read-only access. This means that you can directly access the code, and you will have really the last-minute sources available. It is also a method which requires less bandwidth once you have done the first download (checkout in SVN lingo).<br />
<br />
'''Getting the source'''<br />
<br />
First, you need to have an SVN client installed. Use your package manager, install a tool like TortoiseSVN on Windows, or look at [http://subversion.tigris.org/ the web site] for more details.<br />
<br />
Using command line SVN: change directory (cd) to the parent directory of your development area, eg <br />
To retrieve the full source repository for the first time into an fpc subdirectory under your home directory, type<br />
<syntaxhighlight lang="bash"><br />
cd ~<br />
svn checkout http://svn.freepascal.org/svn/fpc/trunk fpc<br />
</syntaxhighlight><br />
<br />
To update the sources which were downloaded (checked out) above<br />
<syntaxhighlight lang="bash"><br />
cd ~<br />
svn update fpc<br />
</syntaxhighlight><br />
<br />
'''Getting a separate branch'''<br />
<br />
If the current trunk version is in a state of rapid change and unsuitable for much use unless you want to work on the compiler itself, you can stay on a version that is updated with fixes.<br />
To do this, you have to find out a stable '''branch''' that you want to track instead of the default '''trunk''' development version.<br />
<br />
The example below shows how you can track the fixes_2_6 version; of course replace as needed depending on what branches you want to track.<br />
<br />
This example keeps the fixes in another directory under your home directory - it wouldn't make sense to put two versions of the source in one directory...<br />
<br />
<syntaxhighlight lang="bash"><br />
cd ~<br />
svn checkout http://svn.freepascal.org/svn/fpc/branches/fixes_2_6 fpc_fixes_2_6<br />
</syntaxhighlight><br />
Update as usual:<br />
<syntaxhighlight lang="bash"><br />
cd ~<br />
svn update fpc_fixes_2_6<br />
</syntaxhighlight><br />
<br />
'''Documentation'''<br />
<br />
The documentation sources are in a separate repository called fpcdocs, so the command to get them is<br />
<syntaxhighlight lang="bash"><br />
cd ~<br />
svn checkout http://svn.freepascal.org/svn/fpcdocs/trunk fpcdocs<br />
</syntaxhighlight><br />
<br />
If you want to learn more about subversion, read this excellent [http://svnbook.red-bean.com/ Subversion book] which is also available online in different formats for free.<br />
<br />
For more information, see the [http://www.freepascal.org/develop.html Free Pascal] website.<br />
<br />
== FreeBSD ==<br />
<br />
{{Note|If you wish to also install Lazarus, you can omit installing FPC with the steps below as the Lazarus port will install it for you}}<br />
<br />
The FreeBSD ports collection has FPC v3.0.4 version in /usr/ports/lang/fpc. FPC is scattered over 38 (!) packages. The FPC source is now installed by default; it previously needed to be copied and uncompressed from /usr/ports/distfiles/freepascal.<br />
<br />
This must be done as root.<br />
<br />
<syntaxhighlight lang="bash"><br />
# cd /usr/ports/lang/fpc && make install && make clean<br />
</syntaxhighlight><br />
<br />
Once FPC is installed you can check if it's working by simply running as a normal user:<br />
<br />
<syntaxhighlight lang="bash"><br />
$ fpc test<br />
</syntaxhighlight><br />
<br />
which should produce output similar to this:<br />
<br />
Free Pascal Compiler version 3.0.4 [2019/07/21] for x86_64<br />
Copyright (c) 1993-2017 by Florian Klaempfl and others<br />
Target OS: FreeBSD for x86-64<br />
Compiling test<br />
Fatal: Cannot open file "test"<br />
Fatal: Compilation aborted<br />
Error: /usr/local/bin/ppcx64 returned an error exitcode<br />
<br />
== From source on Linux/BSD ==<br />
<br />
Effectively, you need:<br />
<br />
If you have a file with all FPC sources, or two (FPC and Lazarus):<br />
<br />
1. For fpc ftp://ftp.freepascal.org/fpc/dist/Linux/separate/sources.tar preferably an export (no SVN/dirs).<br />
<br />
2. Lazarus source snapshot.<br />
<br />
3. A starting (bootstrap) FPC compiler. An FPC release can always be built by the previously released FPC version, and FPC trunk can always be built by the current FPC release. You can download a bootstrap Free Pascal Compiler or use your distribution's package management/software system to install one.<br />
<br />
FPC build process:<br />
<br />
* Fetch necessary files (starting compiler), FPC source file or source svn directory<br />
* If using FPC source files: extract/de-tgz in work directory,<br />
* Build: enter work/fpc/ and run:<br />
<br />
<syntaxhighlight lang="bash"><br />
# Linux use: <br />
export MAKE=`which make` ; echo $MAKE <br />
# FreeBSD use (default csh, or tcsh):<br />
set MAKE=`which gmake` ; echo $MAKE<br />
# FreeBSD use (bash):<br />
export MAKE=`which gmake` ; echo $MAKE<br />
$MAKE all OPT='-gl' PP=/path/to/startingcompiler-name-ppc386<br />
# $MAKE is make on Linux and gmake on BSD <br />
# /path/to/ can be omitted when ppc386 is in the path<br />
</syntaxhighlight><br />
<br />
* Install fpc. Again in work/fpc, run<br />
<br />
<syntaxhighlight lang="bash"><br />
$MAKE install PP=compiler/ppc386 PREFIX=$THEPREFIX<br />
#replace the PP=compiler/ppc386 with the relevant compiler if not on Intel x86<br />
#THEPREFIX= usually is /usr/local or just /usr, but e.g. on NetBSD it is /usr/pkg for ports)<br />
</syntaxhighlight><br />
<br />
* Create a symlink:<br />
<br />
<syntaxhighlight lang="bash"><br />
ln -s $THEPREFIX/lib/fpc/3.0.0/ppc386 $THEPREFIX/bin/ppc386<br />
</syntaxhighlight><br />
<br />
* Install sources:<br />
<br />
<syntaxhighlight lang="bash"><br />
$MAKE install sourceinstall PREFIX=$THEPREFIX<br />
</syntaxhighlight><br />
<br />
* Create a symlink for default Lazarus source path:<br />
<br />
<syntaxhighlight lang=bash><br />
ln -sf $THEPREFIX/share/src/3.0.0/fpc /usr/share/fpcsrc<br />
</syntaxhighlight><br />
<br />
* Set up fpc.cfg configuration file:<br />
<br />
<syntaxhighlight lang="bash">$THEPREFIX/lib/fpc/3.0.0/samplecfg $THEPREFIX/lib/fpc/3.0.0 $ETCDIR</syntaxhighlight><br />
<br />
* Optionally test to see if ppc386 -i (or whatever compiler your architecture uses) gives output, else give a warning that user need to add $PREFIX/bin to his current path. Try to compiler a program with -viwn, and see if that gives errors.<br />
<br />
Notes:<br />
<br />
* If you need fpcmake package lists, you need to generate or supply them yourself, (in the port, or in an extra archive) either way, do a dummy install to /tmp/pack and determine the files installed with <syntaxhighlight lang="bash">find . >ll</syntaxhighlight><br />
<br />
* $THEPREFIX and $ETCDIR should be user configurable. Otherwise local installs aren't possible.<br />
<br />
* BSDHIER=1 on all make commands forces BSD hierarchy conventions.<br />
<br />
== Windows ==<br />
<br />
By far the easiest way to get a working installation of Free Pascal is to download the current binary Windows release of Lazarus from the [http://sourceforge.net/projects/lazarus/files/ SourceForge repository] - the release contains the current versions of the Free Pascal Compiler and the Free Pascal libraries as well as the Lazarus IDE.<br />
<br />
== From source on Windows ==<br />
<br />
You can get the installer zip for fpc at Free Pascal's download section (http://www.freepascal.org/download.html, then choose a mirror). <br />
<br />
Installing from the sources -- see the next section to know how to get them -- is not for novices, since you need a starting compiler as well.<br />
<br />
'''FPC Sources for Windows'''<br />
<br />
<<<< See section above under [[#FPC Sources|FPC Sources]] for Linux, where the use of SVN is described >>>><br />
<br />
The easiest way to get the Free Pascal sources is via SVN; see the next section for more on that. You can also download the package as a whole -- see http://www.freepascal.org/develop.html for the daily snapshot of the 2.5.x release tree.<br />
<br />
'''Windows FPC Sources via SVN'''<br />
<br />
You will need to have a SVN client such as TortoiseSVN installed in order to perform the steps below. The exact commands vary between SVN clients; the ones given below are to be used under SVN home's client, which is available for download here.<br />
<br />
First create a directory in which you'd like to put the sources. Any normal user can do this. Create a directory for fpc (e.g. C:\Source), then do the following at the command prompt:<br />
<br />
<syntaxhighlight lang="bash">C:\Source> svn co http://svn.freepascal.org/svn/fpc/trunk fpc</syntaxhighlight><br />
Hint: To download/update the latest changes you can simply do<br />
<syntaxhighlight lang="dos"><br />
C:\> cd Source\FPC<br />
C:\Source\FPC> svn up<br />
</syntaxhighlight><br />
<br />
See: http://www.freepascal.org/down/i386/win32.html . Download FPC as one big file, unzip it and run the install.exe. <br />
<br />
Extending your PATH variable to the fpc directory:<br />
<br />
* Win98: Edit autoexec.bat and add the line: PATH=C:\pp\bin\bin\win32;%PATH% NO trailing \ !<br />
* WinXP/2k: My Computer (Right Click) -> Properties -> Advanced (Page) -> Environment Variables -> System Variables -> Edit "PATH", Add "C:\pp\bin\bin\win32" there.<br />
<br />
Then restart windows.<br />
<br />
After you have FPC binaries installed you can build FPC source from subversion. <br />
<br />
Hints: <br />
<br />
* Windows (7+) requires that an elevated user status command prompt be used. From the start menu for "Command Prompt" right click and select "Run as Administrator".<br />
* YOUR-PREFIX is totally dependent on where you installed FPC to. At the time of this writing, the binaries are instructed to use a default location of "C:\FPC" and they were placed in "C:\FPC\2.6.4". Under Linux, the make install scripts were adjusted to create a new sub-folder IF the FPC version changed since last build. The Windows scripts do not. So if you know the sub-folder name ie. 3.1.1 you can specify that. However, since versions change frequently, it is recommended that you just select and maintain a single PREFIX with no respect for FPC versions. A good prefix is C:\FPC but you must also make sure that the C:\FPC\bin\i386-win32\ folder is added to your path environment variable (see above on how to set your path and change it from the binary version to the newly compiled one).<br />
<br />
Instructions:<br />
<br />
* In command Prompt navigate to the localized FPC source. ie.) type "cd c:\Developer\FPC"<br />
* To build FPC type "make all"<br />
* To overwrite existing FPC type "make install PREFIX=YOUR-PREFIX"<br />
* To install source type "make install sourceinstall PREFIX=YOUR-PREFIX"<br />
<br />
'''Compiling/installing FPC and Lazarus from Sources of SVN (Win32)'''<br />
<br />
Version FPC '''3.0.4''' or '''trunk''' - Version Lazarus '''1.9.x'''<br />
<br />
''STEP #1: Create directories and get the sources''<br />
<br />
Create the following directories:<br />
c:\freepascal\<br />
c:\freepascal\binutils\<br />
c:\freepascal\binutils\i386-win32\<br />
c:\freepascal\fpc\<br />
c:\freepascal\fpc\3.0.4\<br />
c:\freepascal\laz\<br />
<br />
or for fpc trunk:<br />
c:\freepascal\<br />
c:\freepascal\binutils\<br />
c:\freepascal\binutils\i386-win32\<br />
c:\freepascal\fpc\<br />
c:\freepascal\fpc\trunk\<br />
c:\freepascal\laz\<br />
<br />
You will need the latest '''released'' compiler to build a new compiler.<br />
Get the ppc386 (the compiler) in FTP (below) and unzip it in c:\freepascal\binutils\<br />
ftp://ftp.freepascal.org/pub/fpc/dist/3.0.4/bootstrap/i386-win32-ppc386.zip<br />
<br />
After installing [http://tortoisesvn.tigris.org/ TortoiseSVN], download the sources from SVN using a URL for each directory, see:<br />
Dir: c:\freepascal\binutils\i386-win32\<br />
URL: http://svn.freepascal.org/svn/fpcbuild/branches/fixes_3_0/install/binw32<br />
<br />
or for fpc trunk:<br />
Dir: c:\freepascal\binutils\i386-win32\<br />
URL: http://svn.freepascal.org/svn/fpcbuild/trunk/install/binw32<br />
<br />
<br />
Dir: c:\freepascal\fpc\3.0.4<br />
URL: http://svn.freepascal.org/svn/fpc/branches/fixes_3_0/<br />
<br />
or for fpc trunk:<br />
Dir: c:\freepascal\fpc\trunk<br />
URL: http://svn.freepascal.org/svn/fpc/trunk/<br />
<br />
<br />
Dir: c:\freepascal\laz<br />
URL: http://svn.freepascal.org/svn/lazarus/trunk<br />
<br />
''STEP #2: Create a BAT file to compile FPC''<br />
<br />
After everything is downloaded, we need a BAT file to compile the FPC sources.<br />
Create a new file c:\freepascal\makefpc.bat and copy/paste the following script:<br />
<br />
<syntaxhighlight lang="dos"><br />
@echo on<br />
set myroot=c:\freepascal<br />
set myFPC=%myroot%\fpc\3.0.4 <br />
set mybinutils=%myroot%\binutils<br />
set PATH=%PATH%;%mybinutils%\i386-win32;%myFPC%\bin\i386-win32<br />
cd %myFPC%<br />
rd /s /q %myfpc%\examples<br />
svn cleanup . --remove-unversioned --remove-ignored<br />
make distclean all install INSTALL_PREFIX=%myFPC% PP=%mybinutils%\ppc386.exe DATA2INC=%myFPC%\utils\data2inc.exe<br />
cd /d %myFPC%\bin\i386-win32<br />
fpcmkcfg -d basepath=%myFPC% -o .\fpc.cfg <br />
copy fpc.exe %mybinutils%\i386-win32<br />
</syntaxhighlight><br />
<br />
or for fpc trunk:<br />
<br />
<syntaxhighlight lang="dos"><br />
@echo on<br />
set myroot=c:\freepascal<br />
set myFPC=%myroot%\fpc\trunk <br />
set mybinutils=%myroot%\binutils<br />
set PATH=%PATH%;%mybinutils%\i386-win32;%myFPC%\bin\i386-win32<br />
cd %myFPC%<br />
rd /s /q %myfpc%\examples<br />
svn cleanup . --remove-unversioned --remove-ignored<br />
make distclean all install INSTALL_PREFIX=%myFPC% PP=%mybinutils%\ppc386.exe DATA2INC=%myFPC%\utils\data2inc.exe<br />
cd /d %myFPC%\bin\i386-win32<br />
del fpc.cfg<br />
fpcmkcfg -d basepath=%myFPC% -o .\fpc.cfg <br />
copy fpc.exe %mybinutils%\i386-win32<br />
</syntaxhighlight><br />
<br />
For crosscompiler to x86_64 add the following after the first make:<br />
<br />
<syntaxhighlight lang="dos"><br />
make all OS_TARGET=win64 CPU_TARGET=x86_64 INSTALL_PREFIX=%myFPC% PP=%mybinutils%\ppc386.exe DATA2INC=%myFPC%\utils\data2inc.exe<br />
make crossinstall OS_TARGET=win64 CPU_TARGET=x86_64 INSTALL_PREFIX=%myFPC% PP=%mybinutils%\ppc386.exe DATA2INC=%myFPC%\utils\data2inc.exe<br />
</syntaxhighlight><br />
<br />
''STEP #3: Make and install FPC''<br />
<br />
At the prompt (cmd.exe), navigate to the directory c:\freepascal and execute the script we just wrote: <br />
<br />
<syntaxhighlight lang="dos"><br />
cd /d c:\freepascal<br />
makefpc.bat<br />
</syntaxhighlight><br />
<br />
''STEP #4: Create a BAT file to compile Lazarus''<br />
<br />
To compile Lazarus for the first time, create a new file c:\freepascal\makelaz.bat and copy/paste the following script:<br />
<br />
<syntaxhighlight lang="dos"><br />
set myroot=c:\freepascal<br />
set myFPC=%myroot%\fpc\3.0.4<br />
set mybinutils=%myroot%\binutils<br />
set PATH=%PATH%;%mybinutils%\i386-win32;%myFPC%\bin\i386-win32<br />
cd %myroot%\laz<br />
make clean all OPT="-glw2 -Xg"<br />
</syntaxhighlight><br />
or for fpc trunk:<br />
<syntaxhighlight lang="dos"><br />
set myroot=c:\freepascal<br />
set myFPC=%myroot%\fpc\trunk<br />
set mybinutils=%myroot%\binutils<br />
set PATH=%mybinutils%\i386-win32;%myFPC%\bin\i386-win32;%PATH%<br />
cd %myroot%\laz<br />
make clean all OPT="-glw2 -Xg"<br />
</syntaxhighlight><br />
<br />
Tip: You only need to use this BAT at the first time. Then you can just build Lazarus using the menu Tools menu> Build Lazarus.<br />
<br />
''STEP #5: Make Lazarus''<br />
<br />
At the prompt, navigate to the directory c:\freepascal and type: makelaz.bat<br />
<br />
Done!<br />
<br />
== macOS ==<br />
<br />
See [[Installing Lazarus on macOS]]<br />
<br />
=Installing Lazarus=<br />
<br />
==Ubuntu/Debian Linux==<br />
<br />
It is recommended to use the [[fpcup|fpcUP]] updater-installer for first time users of Lazarus, which installs fpc & Lazarus in one go into a single subdirectory structure ( ~/development ). <br />
<br />
A way to get a current working installation of Lazarus is to download the .deb files for Free Pascal and Lazarus from the SourceForge repository. Here is how: [[Lazarus release version for Ubuntu|Getting Lazarus from our Ubuntu repository]].<br />
<br />
Note that installing from the default Ubuntu sources will not install the Free Pascal Source Libraries - use the method above.<br />
<br />
{{Note|on Linux Ubuntu at least, the command to start Lazarus from a console is [[startlazarus]]. Else, if you installed it from a Debian package, you should have a Lazarus menu entry under Application/Programming. <br />
(Issue: there is an ambiguity with a program also called "lazarus" from a "tct" package available for Ubuntu).}}<br />
<br />
'''Building debs the easy way'''<br />
<br />
The easiest way to get a current working installation of Lazarus is to download build your own .deb packages by following the instructions at:<br />
<br />
[[How to setup a FPC and Lazarus Ubuntu repository]]<br />
<br />
'''Installing using rpms'''<br />
<br />
The next easiest way is to the RPMs for Free Pascal and Lazarus from the SourceForge repository.<br />
<br />
You need to download the selected version of<br />
* the compiler (eg fpc-2.6.4-0.i686.rpm)<br />
* the pascal source library (eg fpc-src-2.6.4-0.i686.rpm)<br />
* the Lazarus package (eg lazarus-1.4.4-0.i686.rpm).<br />
<br />
Uninstall the old packages:<br />
<br />
<syntaxhighlight lang="bash"><br />
rpm -ev lazarus<br />
rpm -ev fpc<br />
rpm -ev fpc-src<br />
</syntaxhighlight><br />
Install the new packages:<br />
<syntaxhighlight lang="bash"><br />
rpm -ivh fpc-*<br />
rpm -ivh lazarus-*<br />
</syntaxhighlight><br />
<br />
==Raspbian Wheezy Linux==<br />
<br />
Raspbian is a custom version of Debian for the Raspberry Pi creditcard-size computer. See [[Lazarus on Raspberry Pi]] for details.<br />
<br />
==Mandriva Linux==<br />
<br />
'''Lazarus 0.9.30 on Mandriva 2010'''<br />
<br />
Install as given lower down however on compiling a program you may get two error messages telling you that you are missing pixbuf 2.0 and lgtk-x11-2.0. to fix this install from the the software installer libdgk_pixbuf2.0_0-devel and libgtk+2.0_0-devel.<br />
<br />
==Slackware Linux==<br />
<br />
'''Installing Lazarus on Slackware 13.0'''<br />
<br />
There is no real difference from the slackware 12.2 or 12.0 version, therefore the procedure described below should work just as well. <br />
<br />
'''Installing Lazarus 0.9.30, for Slackware 12.0'''<br />
<br />
This have worked in Slackware-12.0 on a Pentium-3 computer: <br />
<br />
* The Free Pascal Compiler (FPC) is installed in SUPERUSER mode <br />
* The lazarus in installed in USER mode<br />
* The FPC compiler will be recompiled<br />
* The lazarus Integrated Development Environment (IDE) source code is loaded from the SVN server <br />
<br />
-- Download "fpc-2.4.2.i386-linux.tar" in a user folder and install it. Go to this folder and type: <br />
<syntaxhighlight lang="bash"><br />
bash-3.1$ tar -xvf fpc-2.4.2.i386-linux.tar<br />
bash-3.1$ su <br />
bash-3.1$ password:xxxxxx<br />
bash-3.1# sh install.sh<br />
...<br />
(use the default answers for the next 6 questions: press "return" 6 times)<br />
...<br />
bash-3.1# exit<br />
bash-3.2$ fpc<br />
free pascal compiler version 2.4.2 [] for i386 <br />
Copyright (c) 2010 ...<br />
...<br />
...<br />
</syntaxhighlight><br />
<br />
You have installed the compiler. You may want to recompile it or not, but in any case you will need to download the source code as it is required by the lazarus IDE.<br />
<br />
-- Download "fpc-2.4.2.source.tar.gz" in a temporary folder. If you do not want to recompile the source then just skip this section. If you would like to run your own compiled version of FPC, then just type:<br />
<syntaxhighlight lang="bash"><br />
bash-3.1$ tar -xvzf fpc-2.4.2.source.tar.gz<br />
bash-3.1$ cd fpc-2.4.2<br />
bash-3.1$ make clean all<br />
bash-3.1$ su<br />
bash-3.1$ password: xxxxxxxx<br />
bash-3.1# make install<br />
bash-3.1# exit<br />
bash-3.1$ fpc<br />
free pascal compiler version 2.4.2 [...] for i386 <br />
Copyright (c) 2010 ...<br />
...<br />
...<br />
</syntaxhighlight><br />
<br />
Now you have your own compiled version working. <br />
<br />
-- The Lazarus IDE editor needs to look at the FPC source code. Even if you do not want to recompile FPC, you need its source code. However, in order to save some space, I only keep a clean (not compiled) copy of the source code. I start from the file "fpc-2.4.2.source.tar.gz" again and I copy it to the /usr/local/src/FPC folder:<br />
<br />
<syntaxhighlight lang="bash"><br />
bash-3.1$ su<br />
bash-3.1$ password: xxxxxxxx<br />
bash-3.1# cp fpc-2.4.2.source.tar.gz /usr/local/src/<br />
bash-3.1# cd /usr/local/src/<br />
bash-3.1# tar -xvzf fpc-2.4.2.source.tar.gz<br />
bash-3.1# rm fpc-2.4.2.source.tar.gz<br />
bash-3.1# exit<br />
</syntaxhighlight><br />
<br />
The version number is included in the folder name "fpc-2.4.2". This way I can keep many versions the the compiler source and eventually switch between them.<br />
<br />
-- The lazarus IDE is kept in my USER "~/lazarus" folder and always compiled in USER mode. I usually download the "lazarus-0.9.30-0.tar.gz" file, but here we can also get the latest development version from the SVN server:<br />
<syntaxhighlight lang="bash">bash-3.1$ svn co http://svn.freepascal.org/svn/lazarus/trunk lazarus</syntaxhighlight><br />
This is for the first time you load it. Next time you will only need to type:<br />
<syntaxhighlight lang="bash">bash-3.1$ svn update</syntaxhighlight><br />
If you do not have SVN installed on your computer, here is how to get it quickly:<br />
<br />
--Download the two files: "subversion-1.4.6.tar.gz" and "subversion-deps-1.4.5.tar.gz" (or later versions). Type:<br />
<br />
<syntaxhighlight lang="bash"><br />
bash-3.1$ tar -xvzf subversion-1.4.6.tar.gz<br />
bash-3.1$ tar -xvzf subversion-deps-1.4.6.tar.gz<br />
bash-3.1$ cd subversion-1.4.6<br />
bash-3.1$ ./configure<br />
bash-3.1$ make<br />
bash-3.1$ su<br />
bash-3.1$ password:xxxxxx<br />
bash-3.1# make install<br />
bash-3.1$ exit<br />
</syntaxhighlight><br />
<br />
-- At this point you have the folder "~/lazarus" containing the source code. You should compile it very simply:<br />
bash-3.1$ make clean all<br />
<br />
After a few minutes, the compiler stops:<br />
<br />
<syntaxhighlight lang="bash"><br />
...<br />
...<br />
Linking ../Lazbuild<br />
987 linescompiled ...<br />
make [2] leaving ...<br />
make [1] leaving ...<br />
bash-3.1$<br />
</syntaxhighlight><br />
<br />
-- Just type:<br />
<syntaxhighlight lang="bash">bash-3.1$ ./lazarus</syntaxhighlight><br />
<br />
WOW! You get a message: "Free Pascal sources not found". Just follow the instructions and indicate your Free Pascal Compiler source directory in the panel: "Environment->Options->Files". As explained earlier, on my computer this should point to "/usr/local/src/fpc-2.4.2". Note that when you change this folder, you should click on "Environment / Rescan_FPC_source_directory".<br />
<br />
Voila!<br />
<br />
==openSUSE Linux==<br />
<br />
''Installing Lazarus 0.9.30. For openSUSE 11.1''<br />
<br />
Free Pascal Compiler requires:<br><br />
- Gnu binutils (gnu as, gnu ld, gnu make)<br />
<br />
These utils can be installed by:<br />
<br />
<syntaxhighlight lang="bash">zypper in -t pattern devel_basis</syntaxhighlight><br />
<br />
Lazarus also requires these components:<br />
1) glib2 devel<br />
2) gtk2 devel<br />
<br />
'''Important''': The lazarus rpm requires the gtk 2 version, not the version 1.2.<br />
<br />
These libraries can be installed by:<br />
<br />
<syntaxhighlight lang="bash">zypper -n install gtk2 glib2</syntaxhighlight><br />
<br />
Download these binary files (RPM)<br />
:* fpc-2.4.2-0.i686.rpm (yes i686 and not i386)<br />
:* fpc-src-2.4.2-0.i686.rpm<br />
:* lazarus-0.9.30-0.i686.rpm<br />
<br />
install them opening a terminal session (mouse's right button -> Menu: Open in terminal)<br />
<syntaxhighlight lang="bash"><br />
rpm -Uvh fpc-2.4.2-0.i686.rpm<br />
rpm -Uvh fpc-src-2.4.2-0.i686.rpm<br />
rpm -Uvh lazarus-0.9.30-0.i686.rpm<br />
</syntaxhighlight><br />
<br />
==Fedora Linux==<br />
<br />
Recent packages of Lazarus and Free Pascal are included in Fedora by default. See [[Install_on_Fedora|Install on Fedora]] on how to install them.<br />
<br />
==Scientific Linux==<br />
<br />
Scientific Linux is an RPM-based distribution focussing on science and research. See [[Scientific Linux]] for details. <br />
<br />
<br />
==Debian GNU Linux==<br />
<br />
There are preliminary Debian packages for lazarus available for download. They are not the latest versions, however. Make sure you read /usr/share/doc/lazarus/README.Debian carefully before you start using it. Feedback is needed and appreciated; please send your comments to Carlos Laviola <claviola@debian.org>.<br />
<br />
Note that for a fully working Lazarus install, no older or incompatible versions of, for example, the fpc source or fpc compiler must be installed. Remove them by typing<br />
<br />
<syntaxhighlight lang="bash">dpkg -r <package name></syntaxhighlight><br />
<br />
without .deb extension. And then install the newest versions as described.<br />
<br />
==From source on Linux==<br />
<br />
If you prefer to install from source and compile the files yourself, follow these instructions. Because the whole Lazarus toolchain is installed into one directory, uninstall is very easy and you don't need to be root to install Lazarus. You can get tgz files for fpc, fpcsrc and lazarus from the downloads section or you can download it directly via svn.<br />
<br />
Here is an example of installing 0.9.28 to Ubuntu 6.06. If you understand Linux commands and bash script, you can get what steps are needed. Just copy the script (change the version number when new version has been released), paste it into a text editor, and save it as something like "install_lazarus.sh". Give it execute permission, and run it in <br />
a console.<br />
<br />
{{Note|In this example, fpc is installed in /opt. So when prompted ''''Install prefix'''', enter '/opt/fpc'. }}<br />
<br />
<syntaxhighlight lang="bash"><br />
#!/bin/sh<br />
<br />
#installing required packages<br />
sudo apt-get install build-essential<br />
sudo apt-get install libgtk2.0-dev<br />
sudo apt-get install libgdk-pixbuf-dev<br />
<br />
#installing Free Pascal source<br />
cd /opt<br />
sudo wget http://nchc.dl.sourceforge.net/lazarus/fpc-src-2.4.2.source.tgz<br />
sudo tar -xvf fpc-src-2.4.2.source.tgz<br />
sudo mv fpc fpcsrc<br />
<br />
#installing Free Pascal<br />
sudo mkdir fpc<br />
cd fpc<br />
sudo wget http://nchc.dl.sourceforge.net/lazarus/fpc-2.4.2.i686-linux.tar<br />
sudo tar -xvf fpc-2.4.2.i686-linux.tar<br />
echo "Enter '/opt/fpc' when prompted 'Install prefix'"<br />
sudo sh install.sh<br />
<br />
#adding fpc path to the PATH<br />
echo "#FPC PATH" >> ~/.bash_profile<br />
echo "if [ -d /opt/fpc/bin ] ; then" >> ~/.bash_profile <br />
echo PATH=/opt/fpc/bin:"${PATH}" >> ~/.bash_profile<br />
echo "fi" >> ~/.bash_profile<br />
<br />
#installing Lazarus<br />
cd ../<br />
sudo wget http://nchc.dl.sourceforge.net/lazarus/lazarus-0.9.30-0.tar.gz<br />
# sudo wget https://downloads.sourceforge.net/project/lazarus/Lazarus%20Zip%20_%20GZip/Lazarus%201.8.0RC5/lazarus-1.8.0-RC5.tar.gz<br />
sudo tar -zxvf lazarus-0.9.30-0.tar.gz<br />
PATH=/opt/fpc/bin:"${PATH}"<br />
sudo chmod -R 777 lazarus<br />
cd lazarus<br />
make clean all<br />
./lazarus<br />
</syntaxhighlight><br />
<br />
{{Note|You have to manually set fpc-src path in the Environmental Options.}}<br />
<br />
'''Downloading Lazarus Source Code'''<br />
<br />
Both the Lazarus and FPC source code reside in SVN/subversion repositories. SVN provides an easy way to update your sources by only downloading the changes. This is the recommended way and saves you a lot of time. A connection to the internet is needed for this, but you don't need to be root. <br />
<br />
Please note these instructions are for subversion, but there is also a Git mirror repository of Free Pascal Compiler and Lazarus: see [[git mirrors|Git mirror]]. You can also use git directly with the subversion server using git-svn link: see [[Lazarus git-svn|Lazarus git-svn]].<br />
<br />
Lazarus does not need any special permissions, neither during installation nor at runtime.<br />
<br />
<font color="red">If you decide to use TortoiseSVN, remember to check "command line client tools" during its installation if you want Lazarus to show the SVN revision number in the About dialog.</font><br />
<br />
;Now getting the sources:<br />
<br />
<syntaxhighlight lang="bash"> svn checkout http://svn.freepascal.org/svn/lazarus/trunk/ lazarus</syntaxhighlight><br />
(replace the last lazarus with any other dir where you want to place your sources)<br />
<br />
On subsequent occasions, to update simply type<br />
<br />
<syntaxhighlight lang="bash"> svn update lazarus</syntaxhighlight><br />
<br />
For more information on Subversion, see:<br />
http://subversion.tigris.org/<br />
<br />
'''Compiling and running'''<br />
<br />
Whether you checkout from cvs or svn, the next step is: <br />
<br />
;compile lazarus:<br />
<syntaxhighlight lang="bash"><br />
cd lazarus<br />
make (gmake on BSD)<br />
</syntaxhighlight><br />
<br />
If fpc is installed correctly, the compilation should work without problems. If not, see FAQ.<br />
<br />
;Start lazarus<br />
<syntaxhighlight lang="bash"> ./lazarus</syntaxhighlight><br />
<br />
The IDE should start. If you started lazarus in a terminal, you can see some notes about missing settings. This is normal at first start. The IDE automatically tries to find out where the Free Pascal compiler and its sources are installed by searching in the most common directories.<br />
<br />
;Check the paths:<br />
: Use the IDE menu to go to<br />
:: Environment -> Environment Options -> Files<br />
<br />
The 'FPC Source directory' should point to your fpc source directory. This directory normally ends with /fpc/ or /fpcsrc/ (e.g. /usr/src/fpcsrc or /home/username/freepascal/fpc) and contains directories like 'compiler', 'docs', 'fcl', 'rtl' and 'packages'.<br />
<br />
See here for the documentation about this dialog: [[IDE_Window:_Environment_Options|IDE Options]].<br />
<br />
;Hint:<br />
To update lazarus you can use<br />
<syntaxhighlight lang="bash"> svn update lazarus</syntaxhighlight><br />
then for either update pathway:<br />
<syntaxhighlight lang="bash"> make clean all (gmake on BSD)</syntaxhighlight><br />
This will rebuild lazarus and create an IDE without lazarus packages. To link your installed packages do '''after''' the above:<br />
<syntaxhighlight lang="bash"> ./lazbuild --build-ide=</syntaxhighlight><br />
<br />
You may have to append other options if for example you use a custom config directory (ie. add --pcp="C:\Documents and Settings\<USER>\Local Settings\Application Data\lazarus-tests"). See [[lazbuild]].<br />
<br />
==Windows==<br />
<br />
[[File:Lazarus-install-en.png|right]]<br />
<br />
The current releases of the Windows Lazarus binary packages install very easily, and should work 'out-of-the-box'. Upgrade is as simple as downloading the new installer and running. <br />
<br />
Except for Win98 and ME, which needs a special flag to compile. Use make OPT="-dWIN9XPLATFORM" otherwise the lazarus.exe will not be able to run on this platform.<br />
<br />
'''Installing Lazarus on Portable USB Drive'''<br />
<br />
It is even possible to install the whole Lazarus/FPC package on a portable USB drive (capacity at least 256 MB), for use in environments where you are not allowed to install software on your Windows workstation or where you haven't got administrator privileges. You do have to be a little careful about adjusting the paths in the compiler and environment options and the fpc.cfg file. It may also be necessary to keep the directory for test compilation on your portable drive. <br />
<br />
<< Q:DOES ANYONE KNOW HOW TO SET UP RELATIVE PATHS IN THESE TAGS AND FILES, SO THAT THE ADDRESSING WORKS WHEN YOU MOVE THE USB DEVICE TO ANOTHER MACHINE WHERE IT HAS A DIFFERENT DRIVE LETTER? <br />
<br />
A: This is what I do. It's relatively convoluted, but it's the best solution I've found. I have a "bin" directory on my USB drive, where I have several scripts and utilities installed. Inside that directory is a batch file called "setenv.bat" which sets an environment variable called THUMBDRIVE. It is set by using this command in the batch file:<br />
<syntaxhighlight lang="dos">set THUMBDRIVE=%CD:~0,2%</syntaxhighlight><br />
This is used in setenv.bat to set some paths to other things I have installed on the USB drive. I also have a link in the root directory of the thumb drive with this property:<br />
<syntaxhighlight lang="dos">%SystemRoot%\system32\cmd.exe /k bin\setenv</syntaxhighlight><br />
so that when I click on that link when the thumb drive folder is diplayed after inserting it, it will open a command prompt at the thumb drive with the environment variables set from setenv.bat.<br />
<br />
Also inside the bin directory is [http://sed.sf.net sed] (the actual binary is one I obtained from the mingw distribution). So I created another batch file called fixlaz.bat which takes one argument, the drive letter which is currently in the Lazarus/fpc settings files that you want to change (note that this is the previous drive letter the last time you ran fixlaz.bat, not the current one of your USB drive which fixlaz.bat already knows). You will need to create this batch file to fit where you installed Lazarus in the root directory structure of the drive if you didn't install it directly in the root folder, and then repeat these lines also for the editoroptions.xml and fpc.cfg files (fpc.cfg is the the fpc bin directory, which might be buried deep in the lazarus folder):<br />
<br />
<syntaxhighlight lang="dos"><br />
copy %THUMBDRIVE%\lazarus\environmentoptions.xml %THUMBDRIVE%\lazarus\environmentoptions.bak<br />
sed -e 's/%1/%THUMBDRIVE%/g' %THUMBDRIVE%\lazarus\environmentoptions.bak > %THUMBDRIVE%\lazarus\environmentoptions.xml<br />
</syntaxhighlight><br />
<br />
So to use it, I would type at the command prompt of the USB drive:<br />
<br />
<syntaxhighlight lang="dos">fixlaz G:</syntaxhighlight><br />
<br />
if "G:" was the previous drive letter used the last time I ran it. This will then scan the file(s) and replace "G:" with the current drive letter of the USB drive, which is in the %THUMBDRIVE% environment variable (after running setenv.bat). Note that you could write it to save the current drive letter in a separate file, so that you wouldn't have to remember it yourself the next time. But this works well enough for me right now.<br />
>><br />
<br />
The binary package is available for Linux and Windows from http://sourceforge.net/project/showfiles.php?group_id=89339<br />
<br />
Download the latest release and launch the application. You will be taken through a typical Windows installation, in which the FPC compiler and source libraries are installed within the same directory structure as Lazarus, and the IDE should launch and operate without significant problems, provided you have uninstalled(!!!) any previous version of Lazarus and/or FPC (often found in the C:\pp directory).<br />
<br />
You can also use a Lazarus Snapshot. For download locations see [[Lazarus Snapshots Downloads]].<br />
<br />
Tip:<br />
It's perhaps a good idea to reboot your Windows after you installed Lazarus and before you try to install additional lazarus components as zeoslib fore example.<br />
<br />
==From source on Windows==<br />
<br />
If you prefer to install from sources, then follow these instructions.<br />
<br />
Please note these instructions are for SubVersion, but there is also a Git mirror repository of Free Pascal Compiler and Lazarus. See [[git mirrors|Git mirror]] for details. You can also use git directly with SubVersion server using git-svn link. See [[Lazarus git-svn|Lazarus git-svn]] for details.<br />
<br />
Open a command prompt window. Start->Run...>CMD or choose MS-DOS icon. You will use this window to enter the commands below<br />
<br />
You have to download the lazarus source from one of the [[Lazarus Snapshots Downloads|snapshots servers]].<br />
Then unzip it to c:\lazarus for example [below called $(LazarusDir)].<br />
<br />
Or you use SVN (example for text mode SVN; adapt to GUI tools such as TortoiseSVN if you want to):<br />
<syntaxhighlight lang="bash"><br />
mkdir c:\lazarus<br />
cd /d c:\lazarus<br />
svn checkout http://svn.freepascal.org/svn/lazarus/trunk/ c:\lazarus<br />
</syntaxhighlight><br />
<br />
You have to install at least the latests stable FPC version (e.g. FPC 3.0.4, but an FPC 3.1.1 snapshot is also possible). <br />
<br />
Type (replace "YourLazarusDir" with the path you have unzipped/checked out Lazarus; replace <br />
<syntaxhighlight lang="dos"><br />
cd "YourLazarusDir"<br />
rem Of course change the first path variable to<br />
rem the path of your FPC compiler<br />
set path=c:\freepascal\bin\x86_64-win64;%PATH%<br />
make<br />
</syntaxhighlight><br />
<br />
*Win9x/WinME: use make OPT="-dWIN9XPLATFORM" otherwise the lazarus.exe will not be able to run on this platform.<br />
<br />
If this works, you can type: lazarus.exe. <br />
<br />
You can compile examples also:<br />
<br />
<syntaxhighlight lang="dos"><br />
cd "YourLazarusDir"\examples<br />
make<br />
</syntaxhighlight><br />
<br />
'''Installing from source starting with a stable release'''<br />
<br />
An alternative version of the instructions above.<br />
<br />
1> First of all install the latest stable Lazarus to obtain a good starting FPC, for example in C:\lazarus_1_4<br />
<br />
2> Now use TortoiseSVN to checkout http://svn.freepascal.org/svn/lazarus/trunk/ into c:\lazarus<br />
<br />
3> Make the following C:\lazarus\build.bat file:<br />
<br />
Replace $(LazarusDir) with your Lazarus did and make sure the FPC version number matches<br />
<br />
<syntaxhighlight lang="dos"><br />
SET PATH=$(LazarusDir)\fpc\2.6.4\bin\i386-win32\<br />
make bigide<br />
</syntaxhighlight><br />
<br />
Now create a shortcut in your desktop to start Lazarus and put the following command to start Lazarus which will make sure that it separates the config files from the stable and the SVN versions:<br />
<br />
<syntaxhighlight lang="dos">$(LazarusDir)\startlazarus.exe --pcp=$(LazarusDir)\configdir </syntaxhighlight><br />
<br />
Always start Lazarus from this shortcut, never directly from the executable. At the first time you start Lazarus configure you FPC dir, FPC sources dir and Lazarus dir.<br />
<br />
'''Building Lazarus on Win98 and WinME'''<br />
<br />
Because the Lazarus IDE by default links to a dll-call "CreateToolhelp32Snapshot", which does not exist on the Win9x platform, the IDE will not run on Win9x out of the box. In order to make it run you have to rebuild the IDE with make "-dWIN9XPLATFORM".<br />
<br />
== FreeBSD ==<br />
<br />
See [[Installing Lazarus on FreeBSD]].<br />
<br />
== PC-BSD 1.0rc1+ ==<br />
<br />
You can install Lazarus on PB-BSD by simply downloading the Lazarus PBI from [http://www.pbidir.com/ PBI Dir]<br />
<br />
Note that you must install glib* port from /usr/port/devel/glib* or glib packages by pkg_add -r glib12 glib20.<br />
I will fix this in new PBI releases.<br />
<br />
'''[other OpenBSD/NetBSD/DragonFlyBSD goes here]'''<br />
<br />
== macOS ==<br />
<br />
See [[Installing Lazarus on macOS]].<br />
<br />
== Haiku ==<br />
<br />
Lazarus requires Qt under Haiku. Qt is not installed by default under Haiku. You need to install package available from this site : http://qt-haiku.ru/<br />
<br />
Currently, there is no binary package to install Lazarus.<br />
<br />
You will have to compile Lazarus from sources.<br />
<br />
Detailed instructions to build Lazarus under Haiku are available here : [[Installing Lazarus on Haiku]]<br />
<br />
=Multiple Lazarus installs=<br />
Please see [[Multiple Lazarus]] for details on having more than one Lazarus version installed on one system. We cover issues that can arise due to multiple Lazarus installs here, because they can also happen when installing over a previous version.<br />
<br />
=Troubleshooting=<br />
<br />
Troubleshooting details that should (hopefully) be applicable across platforms may be found in the article [[Installation Troubleshooting]].<br />
<br />
=Installing cross compilers=<br />
<br />
A cross compiler allows you to create binaries (executables) for a platform different from the platform being used for compilation. For example, working under macOS and creating executables for Win32, FreeBSD or Linux. For details on how to do this, see [[Cross_compiling|Cross Compiling]].<br />
<br />
=Installing old versions=<br />
<br />
See [[Installation hints for old versions]]<br />
</div></div>Zoranhttps://wiki.freepascal.org/index.php?title=fpcupdeluxe&diff=135301fpcupdeluxe2020-04-13T12:07:43Z<p>Zoran: /* Caveats, Observations, Troubleshooting */</p>
<hr />
<div>{{LanguageBar}}<br />
<br />
= Overview =<br />
<br />
[[File:fpcupdeluxe.JPG|300px|thumb|fpupdeluxe on Windows]]<br />
* fpc'''up'''''deluxe'', a GUI installer for FPC and Lazarus. Based on [[fpcup]]<br />
* [https://github.com/newpascal/fpcupdeluxe fpcupdeluxe source]<br />
* [https://github.com/newpascal/fpcupdeluxe/releases/latest Latest release]<br />
<br />
= Installation of FPC and Lazarus =<br />
<br />
The main purpose of fpcdeluxe is to provide a means for installing and updating multiple versions of FPC and Lazarus in a self-contained manner. Self-contained meaning that an install by fpcupdeluxe will have no influence on (interference with) your system: Hence you can install multiple versions of FPC and Lazarus.<br />
<br />
In order to have the installation working '''you MUST use the generated link to start the install''' !<br />
<br />
That launcher link contains a line like: <br />
Exec=/P/s/lazarus/lazarus --pcp="/P/s/config_lazarus"<br />
<br />
directing Lazarus to use the [[pcp]] , i.e. '''primary configuration''' setting '''path''' of "/P/s/config_lazarus" or a similar subdir under /home/ depending on you choice made from the GUI. No fpc or anything is required in the searchpath. <br />
<br />
= Advanced settings =<br />
<br />
[[File:fpcupdeluxe_advanced.JPG|none|400px|thumb|left|Fpcupdeluxe advanced settings screen.]]<br />
<br />
The above screenshot (click Setup+ on mainscreen) shows the advanced settings of fpcupdeluxe.<br />
<br />
Some examples of advanced settings:<br />
<br />
--> for debugging FPC itself, add "-g -gl -O-" into the "FPC options" field.<br />
<br />
= Crosscompiling =<br />
<br />
Fpcupdeluxe makes cross-compiling easy: just select a CPU and an OS, and press install crosscompiler.<br />
If libraries and/or binary tools are needed, fpcupdeluxe will try to get them online.<br />
Use the link to see what is available at the moment !<br />
<br />
* [https://github.com/newpascal/fpcupdeluxe/releases/tag/linuxx64crossbins_v1.0 Linux 64bit binary toolchains]<br />
* [https://github.com/newpascal/fpcupdeluxe/releases/tag/linuxi386crossbins_v1.0 Linux 32bit binary toolchains]<br />
* [https://github.com/newpascal/fpcupdeluxe/releases/tag/wincrossbins_v1.0 Windows binary toolchains]<br />
* [https://github.com/newpascal/fpcupdeluxe/releases/tag/crosslibs_v1.0 System libraries]<br />
<br />
== Crosscompiling from Windows and Linux towards Darwin: the hard way ==<br />
<br />
With the help of fpcupdeluxe, NewPascal or FPC trunk, and a toolset called osxcross/cctools-port, you can crosscompile towards Darwin. This help will concentrate on manually setting up crossing from Windows/Linux towards Darwin (fpcupdeluxe can also do all the auto-magic for you for crossing towards Darwin; see above).<br />
<br />
First, you will need to get osxcross/cctools-port, and compile it yourself.<br />
<br />
* [https://github.com/tpoechtrager/osxcross osxcross original]<br />[https://github.com/LongDirtyAnimAlf/osxcross osxcross for FPC]<br />
* [https://github.com/tpoechtrager/cctools-port cctools-port original]<br />[https://github.com/LongDirtyAnimAlf/cctools-port cctools-port for FPC]<br />
<br />
For macOS, you will need the [https://github.com/LongDirtyAnimAlf/osxcross osxcross for FPC] that has been adapted for use by FPC.<br />
<br />
For iOS/iPhone, you will need the [https://github.com/LongDirtyAnimAlf/cctools-port cctools-port for FPC] that has been adapted for use by FPC.<br />
<br />
Get yourself a SDK, either from your own Mac, or from online sources: [https://github.com/phracker/MacOSX-SDKs/releases Various Mac SDK's]<br />
<br />
SDKs for iPhone can also be obtained from online sources: [https://github.com/theos/sdks Various iPhone SDK's]<br />
<br />
Build osxcross/cctools-port according to the instructions. For example, on Windows with Cygwin.<br />
<br />
{{Note|Please note: to be able to cross from Windows towards Darwin, you need to have NewPascal or FPC trunk installed !}}<br />
<br />
Use fpcupdeluxe to install NewPascal or FPC trunk.<br />
<br />
Before building the cross-compiler, you need to inform fpcupdeluxe where to find the Darwin libs (SDK) and the binary tools (build by osxcross/cctools-port). The two screenshot below show how to proceed:<br />
<br />
* Choose a CPU-type and an OS (Darwin). Select custom. You should now be able to use the buttons and point fpcupdeluxe to the right locations.</p><br />
[[File:fpcupdeluxe_darwinlibs.JPG|none|200px|thumb|left|Point fpcupdeluxe towards the library location]]<br />
[[File:fpcupdeluxe_darwinbins.JPG|none|200px|thumb|left|Point fpcupdeluxe towards the binary tools location]]<br />
* Now, on the mainscreen, you can select your CPU and OS (Darwin) and build the cross-compiler !</p><br />
* '''Again, fpcupdeluxe also has pre-built tools for crossing towards Darwin: using the fpcupdeluxe-auto-magic will be the easiest !!!'''<br />
<br />
== Caveats, Observations, [[Troubleshooting]] ==<br />
<br />
* It was observed, that '''f.''' often times works on first run on an empty install dir, but subsequents runs, i.e. in order to add controls to the pallette, add the .chm [[help]] system asf., often times fail. In case of failure to recompile / [[make]] the IDE, the Lazarus binary may be gone, so unless you made a file backup earlier, you cannot start Lazarus anymore. <br />
* copy and save the logfile for reference / troubleshoot.<br />
* adding the [[help]] system is easy: just tag it under "advanced settings", saving some manual labour.<br />
* it takes about 10 minutes to make a full new install of fpc + Laz.<br />
* try out new --pcp settings to get a feeling for it, you can always delete the config and start anew (save the pristine fresh content of the --pcp dir right after install).<br />
* Sometimes, fpcupdeluxe hangs when it cannot download OpenSSL library. It's chicken-egg problem - OpenSSL is needed to download from https, so fpcupdeluxe cannot download OpenSSL without OpenSSL. Fpcupdeluxe has its strategy to overcome this, but sometimes it fails. Then, try this workaround - in fpcupdeluxe go to "Extras" tab and click on "get OpenSSL by browser", then unpack the downloaded file to fpcupdeluxe folder (where fpcupdeluxe.exe is located). Now, try again.<br />
<br />
= Launching an installed Lazarus and fpc instance =<br />
<br />
For launching the Lazarus GUI, a script is created (in the users home directory) and a launcher (on the desktop) which opens Lazarus with the correct config path & environment. Example, on Linux if installing to a folder named "/home/user/trunk", fpcupdeluxe creates:<br />
<br />
/home/user/Desktop/Lazarus_trunk.desktop (launcher)<br />
/home/user/Lazarus_trunk (shell script)<br />
<br />
For Linux FPC/lazbuild command line work the following shell script can be used (assumes a bash shell and using example install dir of "/home/user/trunk"):<br />
<br />
<syntaxhighlight lang="bash"><br />
#!/bin/sh<br />
# fpcupdeluxe: FPC home startlink script<br />
export PATH="/home/user/trunk/fpc/bin/x86_64-linux:/home/user/trunk/lazarus:${PATH}"<br />
export PPC_CONFIG_PATH="/home/user/trunk/fpc/bin/x86_64-linux"<br />
exec /bin/bash<br />
</syntaxhighlight><br />
<br />
Windows equivalent batch file for setting up environment for working from command line (using example install dir of "f:\trunk"):<br />
<br />
<syntaxhighlight lang="batch"><br />
REM fpcupdeluxe: FPC home startlink script<br />
SET PATH=F:\trunk\fpc\bin\x86_64-win64;F:\trunk\lazarus;%PATH%<br />
SET PPC_CONFIG_PATH=F:\trunk\fpc\bin\x86_64-win64<br />
cmd.exe<br />
</syntaxhighlight><br />
<br />
{{Note|Setting the paths this way is not global and only effects the current terminal shell and child shells.}}<br />
<br />
= External links =<br />
<br />
* See in depth guide for ''BUILD / MAKE'': [http://www.stack.nl/~marcov/buildfaq.pdf Build FAQ]<br />
* [http://wiki.lazarus.freepascal.org/fpcup Fpcup by Reinier]<br />
* [https://github.com/newpascal/fpcupdeluxe Fpcupdeluxe source]<br />
* [https://github.com/newpascal/fpcupdeluxe/releases/latest Latest release]<br />
* [http://newpascal.org NewPascal]<br />
<br />
<br />
[[Category:Install]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=fpcupdeluxe&diff=135300fpcupdeluxe2020-04-13T12:04:00Z<p>Zoran: /* Caveats, Observations, Troubleshooting */</p>
<hr />
<div>{{LanguageBar}}<br />
<br />
= Overview =<br />
<br />
[[File:fpcupdeluxe.JPG|300px|thumb|fpupdeluxe on Windows]]<br />
* fpc'''up'''''deluxe'', a GUI installer for FPC and Lazarus. Based on [[fpcup]]<br />
* [https://github.com/newpascal/fpcupdeluxe fpcupdeluxe source]<br />
* [https://github.com/newpascal/fpcupdeluxe/releases/latest Latest release]<br />
<br />
= Installation of FPC and Lazarus =<br />
<br />
The main purpose of fpcdeluxe is to provide a means for installing and updating multiple versions of FPC and Lazarus in a self-contained manner. Self-contained meaning that an install by fpcupdeluxe will have no influence on (interference with) your system: Hence you can install multiple versions of FPC and Lazarus.<br />
<br />
In order to have the installation working '''you MUST use the generated link to start the install''' !<br />
<br />
That launcher link contains a line like: <br />
Exec=/P/s/lazarus/lazarus --pcp="/P/s/config_lazarus"<br />
<br />
directing Lazarus to use the [[pcp]] , i.e. '''primary configuration''' setting '''path''' of "/P/s/config_lazarus" or a similar subdir under /home/ depending on you choice made from the GUI. No fpc or anything is required in the searchpath. <br />
<br />
= Advanced settings =<br />
<br />
[[File:fpcupdeluxe_advanced.JPG|none|400px|thumb|left|Fpcupdeluxe advanced settings screen.]]<br />
<br />
The above screenshot (click Setup+ on mainscreen) shows the advanced settings of fpcupdeluxe.<br />
<br />
Some examples of advanced settings:<br />
<br />
--> for debugging FPC itself, add "-g -gl -O-" into the "FPC options" field.<br />
<br />
= Crosscompiling =<br />
<br />
Fpcupdeluxe makes cross-compiling easy: just select a CPU and an OS, and press install crosscompiler.<br />
If libraries and/or binary tools are needed, fpcupdeluxe will try to get them online.<br />
Use the link to see what is available at the moment !<br />
<br />
* [https://github.com/newpascal/fpcupdeluxe/releases/tag/linuxx64crossbins_v1.0 Linux 64bit binary toolchains]<br />
* [https://github.com/newpascal/fpcupdeluxe/releases/tag/linuxi386crossbins_v1.0 Linux 32bit binary toolchains]<br />
* [https://github.com/newpascal/fpcupdeluxe/releases/tag/wincrossbins_v1.0 Windows binary toolchains]<br />
* [https://github.com/newpascal/fpcupdeluxe/releases/tag/crosslibs_v1.0 System libraries]<br />
<br />
== Crosscompiling from Windows and Linux towards Darwin: the hard way ==<br />
<br />
With the help of fpcupdeluxe, NewPascal or FPC trunk, and a toolset called osxcross/cctools-port, you can crosscompile towards Darwin. This help will concentrate on manually setting up crossing from Windows/Linux towards Darwin (fpcupdeluxe can also do all the auto-magic for you for crossing towards Darwin; see above).<br />
<br />
First, you will need to get osxcross/cctools-port, and compile it yourself.<br />
<br />
* [https://github.com/tpoechtrager/osxcross osxcross original]<br />[https://github.com/LongDirtyAnimAlf/osxcross osxcross for FPC]<br />
* [https://github.com/tpoechtrager/cctools-port cctools-port original]<br />[https://github.com/LongDirtyAnimAlf/cctools-port cctools-port for FPC]<br />
<br />
For macOS, you will need the [https://github.com/LongDirtyAnimAlf/osxcross osxcross for FPC] that has been adapted for use by FPC.<br />
<br />
For iOS/iPhone, you will need the [https://github.com/LongDirtyAnimAlf/cctools-port cctools-port for FPC] that has been adapted for use by FPC.<br />
<br />
Get yourself a SDK, either from your own Mac, or from online sources: [https://github.com/phracker/MacOSX-SDKs/releases Various Mac SDK's]<br />
<br />
SDKs for iPhone can also be obtained from online sources: [https://github.com/theos/sdks Various iPhone SDK's]<br />
<br />
Build osxcross/cctools-port according to the instructions. For example, on Windows with Cygwin.<br />
<br />
{{Note|Please note: to be able to cross from Windows towards Darwin, you need to have NewPascal or FPC trunk installed !}}<br />
<br />
Use fpcupdeluxe to install NewPascal or FPC trunk.<br />
<br />
Before building the cross-compiler, you need to inform fpcupdeluxe where to find the Darwin libs (SDK) and the binary tools (build by osxcross/cctools-port). The two screenshot below show how to proceed:<br />
<br />
* Choose a CPU-type and an OS (Darwin). Select custom. You should now be able to use the buttons and point fpcupdeluxe to the right locations.</p><br />
[[File:fpcupdeluxe_darwinlibs.JPG|none|200px|thumb|left|Point fpcupdeluxe towards the library location]]<br />
[[File:fpcupdeluxe_darwinbins.JPG|none|200px|thumb|left|Point fpcupdeluxe towards the binary tools location]]<br />
* Now, on the mainscreen, you can select your CPU and OS (Darwin) and build the cross-compiler !</p><br />
* '''Again, fpcupdeluxe also has pre-built tools for crossing towards Darwin: using the fpcupdeluxe-auto-magic will be the easiest !!!'''<br />
<br />
== Caveats, Observations, [[Troubleshooting]] ==<br />
<br />
* It was observed, that '''f.''' often times works on first run on an empty install dir, but subsequents runs, i.e. in order to add controls to the pallette, add the .chm [[help]] system asf., often times fail. In case of failure to recompile / [[make]] the IDE, the Lazarus binary may be gone, so unless you made a file backup earlier, you cannot start Lazarus anymore. <br />
* copy and save the logfile for reference / troubleshoot.<br />
* adding the [[help]] system is easy: just tag it under "advanced settings", saving some manual labour.<br />
* it takes about 10 minutes to make a full new install of fpc + Laz.<br />
* try out new --pcp settings to get a feeling for it, you can always delete the config and start anew (save the pristine fresh content of the --pcp dir right after install).<br />
* Sometimes, fpcupdeluxe hangs when it cannot download OpenSSL library. Tt's chicken-egg problem - OpenSSL is needed to download from https, so fpcupdeluxe cannot download OpenSSL without OpenSSL. Fpcupdeluxe has its strategy to overcome this, but sometimes it fails. Then, try this workaround - in fpcupdeluxe go to "Extras" tab and click on "get OpenSSL by browser", then unpack the downloaded file to fpcupdeluxe folder (where fpcupdeluxe.exe is located). Now, try again.<br />
<br />
= Launching an installed Lazarus and fpc instance =<br />
<br />
For launching the Lazarus GUI, a script is created (in the users home directory) and a launcher (on the desktop) which opens Lazarus with the correct config path & environment. Example, on Linux if installing to a folder named "/home/user/trunk", fpcupdeluxe creates:<br />
<br />
/home/user/Desktop/Lazarus_trunk.desktop (launcher)<br />
/home/user/Lazarus_trunk (shell script)<br />
<br />
For Linux FPC/lazbuild command line work the following shell script can be used (assumes a bash shell and using example install dir of "/home/user/trunk"):<br />
<br />
<syntaxhighlight lang="bash"><br />
#!/bin/sh<br />
# fpcupdeluxe: FPC home startlink script<br />
export PATH="/home/user/trunk/fpc/bin/x86_64-linux:/home/user/trunk/lazarus:${PATH}"<br />
export PPC_CONFIG_PATH="/home/user/trunk/fpc/bin/x86_64-linux"<br />
exec /bin/bash<br />
</syntaxhighlight><br />
<br />
Windows equivalent batch file for setting up environment for working from command line (using example install dir of "f:\trunk"):<br />
<br />
<syntaxhighlight lang="batch"><br />
REM fpcupdeluxe: FPC home startlink script<br />
SET PATH=F:\trunk\fpc\bin\x86_64-win64;F:\trunk\lazarus;%PATH%<br />
SET PPC_CONFIG_PATH=F:\trunk\fpc\bin\x86_64-win64<br />
cmd.exe<br />
</syntaxhighlight><br />
<br />
{{Note|Setting the paths this way is not global and only effects the current terminal shell and child shells.}}<br />
<br />
= External links =<br />
<br />
* See in depth guide for ''BUILD / MAKE'': [http://www.stack.nl/~marcov/buildfaq.pdf Build FAQ]<br />
* [http://wiki.lazarus.freepascal.org/fpcup Fpcup by Reinier]<br />
* [https://github.com/newpascal/fpcupdeluxe Fpcupdeluxe source]<br />
* [https://github.com/newpascal/fpcupdeluxe/releases/latest Latest release]<br />
* [http://newpascal.org NewPascal]<br />
<br />
<br />
[[Category:Install]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=Lazarus_Resources&diff=134431Lazarus Resources2020-03-13T18:45:30Z<p>Zoran: /* Lazarus resources */</p>
<hr />
<div>{{Lazarus Resources}}<br />
<br />
==Introduction==<br />
Resource files contain data which should be compiled into the executable file. That data could consist of images, string tables, version info, ... even a Windows XP manifest and forms. This includes data that the programmer can retrieve in his code (accessing them as files). Using resources can be handy if you want to distribute self-contained executables.<br />
<br />
Before FPC 2.4 it was not possible to use "normal" resource files (*.res) in Lazarus because they were Win32 specific. Please see [[#Lazarus resources]] below.<br />
<br />
Normal resources are now recommended for current FPC '''(including all recent Lazarus versions)'''. Please see [[#FPC resources]] below.<br />
<br />
==Lazarus resources==<br />
{{Warning|As mentioned above, using "normal"/Windows resources. Lazarus has also switched to this format since ver. 1.4; see [[Lazarus_1.4.0_release_notes]] }}<br />
In order to use your files as Lazarus resources, they must be included as a resource file. <br />
<br />
You can create LRS files with [https://sourceforge.net/projects/lrsexplorer/ LRS explorer] or using ''lazres'' with the command line.<br />
<br />
Lazres can be found in the "Tools" directory (C:\Lazarus\Tools\ - you may need to compile it from lazres.lpi project) of your Lazarus installation folder.<br />
<br />
Then you can compile Lazarus resource files (*.lrs) via the command line. The syntax for lazres is:<br />
<br />
<pre><nowiki>lazres <filename of resource file> <files to include (file1 file2 file3 ...)></nowiki></pre><br />
<br />
Example:<br />
<syntaxhighlight lang="dos">lazres mylazarusresource.lrs image.jpg</syntaxhighlight><br />
<br />
Alternatively you can compile and execute glazres (C:\Lazarus\Tools\glazres) for a GUI version of lazres.<br />
<br />
To use a Lazarus resource file in your project, include the file with the $I compiler directive in the '''initialization''' section of your unit.<br />
<br />
You can access the data in the resources directly or with the LoadFromLazarusResource method of the variables which will hold the file contents afterwards. LoadFromLazarusResource requires a string parameter that indicates which object should be loaded from the resource file.<br />
<br />
Example:<br />
<br />
<syntaxhighlight lang="pascal"><br />
uses ...LResources...;<br />
<br />
...<br />
procedure exampleproc;<br />
var<br />
Image: TImage<br />
begin<br />
Image := TImage.Create;<br />
Image.Picture.LoadFromLazarusResource('image'); // note that there is no need for the extension<br />
end;<br />
<br />
initialization<br />
{$I mylazarusresource.lrs}<br />
</syntaxhighlight><br />
<br />
This code includes the file mylazarusresource.lrs into the project. In the procedure exampleproc an icon object is created and loaded from the object "image" out of the resource. The file which was compiled into the resource was probably named image.jpg.<br />
<br />
Every class that is derived from [[TGraphic]] contains the [[LoadFromLazarusResource]] procedure.<br />
<br />
=== Lazarus Resource Form File ===<br />
Lazarus generates .LRS files from .LFM form files. <br />
<br />
When a LRS form file is missing, FPC reports the following error:<br />
<code>ERROR: unit1.pas(193,4) Fatal: Can't open include file "unit1.lrs"</code><br />
<br />
To solve it you can either:<br />
* use lazres: c:\lazarus\tools\lazres.exe unit1.lrs unit1.lfm<br />
* (easier): make a trivial change in the form design, revert it and save the form; this will recreate the .lrs file without needing to run lazres.<br />
<br />
<br />
===Getting the raw data of an lrs resource===<br />
You can retrieve the data of the resource with:<br />
<br />
<syntaxhighlight lang="pascal"><br />
uses ...LResources...;<br />
<br />
...<br />
procedure TForm1.FormCreate(Sender: TObject);<br />
var<br />
r: TLResource;<br />
Data: String;<br />
begin<br />
r:=LazarusResources.Find('datafile1');<br />
if r=nil then raise Exception.Create('resource datafile1 is missing');<br />
Data:=r.Value;<br />
...do something with the data...<br />
end;<br />
</syntaxhighlight><br />
<br />
== FPC resources ==<br />
Starting from FPC 2.4 you can use standard .rc (resource script) and .res (compiled resource) files in your project to include resources. To turn a .rc script used in the sources into a binary resource (.res file), FPC runs the appropriate external resource compiler (windres or GoRC). Therefore that resource compiler needs to be installed and present in the PATH environment variable.<br />
For more details, see: [http://www.freepascal.org/docs-html/prog/progch13.html FPC Programmer's guide, chapter 13 "Using Windows resources"]<br />
<br />
To simplify the compiling process, it is possible to use only the compiled resources in the .res files. You can precompile the resources by any available resource compiler - windres (available both on unixes and windows), GoRC (windows only), Microsoft resource compiler (rc.exe included in Visual Studio), Borland resource compiler (brcc32.exe included in Delphi, C++ Builder or Rad Studio products) or any other.<br />
<br />
Use <br />
* {$R filename.rc} to compile a resource script and include the resulting resource file or <br />
* {$R filename.res} directive to include a compiled resource file <br />
into the executable. <br />
<br />
FPC RTL provides both low-level functions as well as high-level classes to access resources.<br />
<br />
The low-level functions are:<br />
* EnumResourceTypes<br />
* EnumResourceNames<br />
* EnumResourceLanguages<br />
* FindResource<br />
* FindResourceEx<br />
* LoadResource<br />
* SizeofResource<br />
* LockResource<br />
* UnlockResource<br />
* FreeResource<br />
They are compatible with the Windows API functions: [http://msdn.microsoft.com/en-us/library/ff468902(VS.85).aspx]<br />
<br />
The main class used to work with resources is TResourceStream. LCL uses it to load embedded bitmaps, icons and form streams. Look at TGraphic.LoadFromResourceID or TIcon.LoadFromResourceHandle to see how it is used inside LCL.<br />
<br />
=== Adding resources to your program ===<br />
Let's review the situation when you need to store some data inside your executable and during the program run you want to extract this data from it.<br />
<br />
First we need to tell the compiler what files to include in the resource. We do this in a .rc (resource script) file:<br />
mydata.rc file: <br />
<br />
<syntaxhighlight lang="pascal"><br />
MYDATA RCDATA "mydata.dat"<br />
</syntaxhighlight> <br />
<br />
Here MYDATA is the resource name, RCDATA is the type of resource (look here http://msdn.microsoft.com/en-us/library/ms648009(VS.85).aspx for explanation of possible types) and "mydata.dat" is your data file. <br />
<br />
Let's instruct the compiler to include the resource into your project:<br />
<br />
<syntaxhighlight lang="pascal"><br />
program mydata;<br />
<br />
{$R mydata.rc}<br />
begin<br />
end.<br />
</syntaxhighlight><br />
<br />
Behind the scenes, the FPC compiler actually instructs the resource compiler distributed with FPC to follow the .rc script to compile your data files into a binary .res resource file. The linker will then include this into the executable. Though this is transparent to the programmer, you can, if you want to, create your own .res file with e.g. the Borland resource compiler. Instead of using <syntaxhighlight lang="pascal">{$R mydata.rc}</syntaxhighlight> you'd use <syntaxhighlight lang="pascal">{$R mydata.res}</syntaxhighlight>.<br />
<br />
=== Checking you have windres ===<br />
<br />
On Linux/FreeBSD/macOS, you might need to make sure you have the ''windres'' resource compiler, which is part of the GNU binutils project.<br />
<br />
==== Linux ====<br />
<br />
On Debian systems you could run (FIXME : this work for a 64B version, for 32B systems you will must probably change w64 to w32) :<br />
<syntaxhighlight lang="bash"><br />
sudo apt install mingw-w64 mingw-w64-tools<br />
</syntaxhighlight><br />
Next 2 windres compilers will be installed (in /usr/bin) x86_64-w64-mingw32-windres and i386-w64-mingw32-windres.<br />
But FPC search "windres" (worst the path can be "Select manually"). You must edit /etc/fpc.cfg and add lines at the end (delete the old "-FC")<br />
<syntaxhighlight lang="c"><br />
# MS Windows .rc resource compiler<br />
#IFDEF CPUAMD64<br />
-FCx86_64-w64-mingw32-windres<br />
#ENDIF<br />
#IFDEF cpui386<br />
-FCi386-w64-mingw32-windres<br />
#ENDIF<br />
</syntaxhighlight><br />
<br />
==== FreeBSD ====<br />
<br />
Even if you install the ''binutils'' package via the ports collection, the ''windres'' binary is not installed - it is probably considered too Windows centric. So you have to manually add ''windres'' yourself.<br />
<br />
When you build the ''binutils'' package via ports, the ''windres'' binary is actually built, but it is not installed by default. So, after building and installing the package:<br />
<br />
1. From the /usr/ports/devel/binutils directory, cd work/binutils-2.xx/binutils<br />
<br />
2. Copy the ''windres'' binary into your /usr/local/bin/ directory, or into your $HOME/bin/ directory.<br />
<br />
'''Note:''' The ''windres'' utility is hardcoded to use ''gcc'' but [https://www.freebsd.org/releases/10.0R/relnotes.html#userland FreeBSD 10.0] (January 2014) replaced ''gcc'' with ''clang'', so you will also need to install ''gcc'' from ports to be able to use ''windres'' if you are using a version of FreeBSD that is not end-of-life.<br />
<br />
==== macOS ====<br />
<br />
You will need to install GNU Binary Utilities for <code>windres</code>. Eg using [https://brew.sh/ Homebrew]:<br />
<br />
<syntaxhighlight lang="bash"><br />
brew install binutils<br />
</syntaxhighlight><br />
<br />
This will place <code>windres</code> in <code>/usr/local/Cellar/binutils/2.32/bin/windres</code> (<code>2.32</code> will change as the binutils version get bumped). But if you execute it you will get a <code>windres: Can't detect architecture.</code> error. <br />
<br />
To fix this you will need to pass on the <code>--target=</code> argument but the <code>windres</code> call is hard coded in <code>fpc</code>. So a solution from the [https://forum.lazarus.freepascal.org/index.php/topic,20224.msg116563.html#msg116563 forum] is to create a shell script that you pass with the <code>-FC</code> flag. Eg <code>vim /usr/local/bin/coff-x86-64-windres</code>:<br />
<br />
<syntaxhighlight lang="bash"><br />
#!/bin/bash<br />
/usr/local/Cellar/binutils/2.32/bin/windres --target=coff-x86-64 "$@"<br />
</syntaxhighlight><br />
<br />
Then you can edit <code>/etc/fpc.cfg</code> to include:<br />
<br />
<syntaxhighlight lang="c"><br />
-FCcoff-x86-64-windres<br />
</syntaxhighlight><br />
<br />
==== Windows ====<br />
<br />
On Windows, windres and the gorc resource compiler should be provided by the FPC and Lazarus installers.<br />
<br />
=== Using resources in your program ===<br />
Now let's extract the stored resource into a file, e.g. mydata.dat:<br />
<br />
<syntaxhighlight lang="pascal"><br />
program mydata;<br />
uses<br />
SysUtils, Classes, Windows {needed for RT_RCDATA, could be omitted};<br />
{$R mydata.res}<br />
var<br />
S: TResourceStream;<br />
F: TFileStream;<br />
begin<br />
// create a resource stream which points to our resource<br />
S := TResourceStream.Create(HInstance, 'MYDATA', RT_RCDATA);<br />
// Replace RT_RCDATA with ??? with what?<br />
// Please ensure you write the enclosing apostrophes around MYDATA, <br />
// otherwise no data will be extracted.<br />
try<br />
// create a file mydata.dat in the application directory<br />
F := TFileStream.Create(ExtractFilePath(ParamStr(0)) + 'mydata.dat', fmCreate); <br />
try<br />
F.CopyFrom(S, S.Size); // copy data from the resource stream to file stream<br />
finally<br />
F.Free; // destroy the file stream<br />
end;<br />
finally<br />
S.Free; // destroy the resource stream<br />
end;<br />
end.</syntaxhighlight><br />
<br />
Many classes (at least in Lazarus trunk/development version) also support loading directly from FPC resources, e.g.<br />
<br />
<syntaxhighlight lang="pascal"><br />
procedure exampleproc;<br />
var<br />
Image: TImage<br />
begin<br />
Image := TImage.Create;<br />
Image.Picture.LoadFromResourceName(HInstance,'image'); // note that there is no need for the extension<br />
end;<br />
</syntaxhighlight><br />
<br />
[[Category:Tutorials]]<br />
[[Category:Lazarus]]<br />
[[Category:FPC]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=Autosize_/_Layout&diff=125498Autosize / Layout2019-07-03T11:38:12Z<p>Zoran: /* Differences to Delphi */</p>
<hr />
<div>{{Autosize / Layout}}<br />
<br />
=Overview=<br />
<br />
The [[LCL]] can automatically alter a control's size and position so it adapts to changes in font, theme and text or other content. If you want to run your program on several platforms, or if your captions are available in several languages, your controls need to adapt correctly to their environment. The [[LCL]] allows you not only to make a quick first design (moving controls around on a form using the mouse) but also later to set several key properties which will make the controls automatically adapt subsequently to changed content etc.<br />
<br />
*[[#Fixed_design|Fixed design]]: this is the default when placing a control in the Designer. The control's position is fixed relative to its parent. The control's size and position (Left, Top) is fully adjustable by the programmer. You can move the control around with the mouse, and resize it freely.<br />
*[[#Align|Aligned]]: aligned controls fill up the remaining parental space at the top, bottom, left, or right, or fill the entire remaining space.<br />
*[[#Anchor_Sides|Anchored]]: you can anchor a control's sides (left, top, right, bottom) to its parent or to another control. Anchoring means: the [[LCL]] will try to maintain the same distance from the anchor point.<br />
*[[#Layout|Layout]]: controls can be automatically aligned in rows and columns (e.g. TRadioGroup)<br />
*[[#Custom_layout_with_OnResize_.2F_OnChangeBounds|Custom via OnResize]]: you can align controls yourself in code using the OnResize and OnChangeBounds events.<br />
*[[#Custom_Controls|Custom controls]]: writing your own controls, you can override nearly every [[LCL]] behaviour you want.<br />
<br />
==Precedence rules==<br />
<br />
# Constraints<br />
# Align<br />
# Anchors<br />
# ChildSizing.Layout<br />
# AutoSize<br />
# OnResize, OnChangeBounds - however, if you set bounds that conflict with the rules above you will create an endless loop<br />
<br />
==Common Properties==<br />
<br />
Several important properties can be changed to configure autosizing:<br />
<br />
*Left, Top, Width, Height<br />
*[[#AutoSize|AutoSize]]: autosize instructs the [[LCL]] to automatically resize the Width and Height of a control<br />
*[[#Anchor_Sides|Anchors]]: lets you create dependencies, for example to anchor a ComboBox to the right side of a Label.<br />
*[[#Align|Align]]<br />
*Constraints: lets you set a minimum and maximum for the Width and Height<br />
*[[#BorderSpacing|BorderSpacing]]: lets you set the spacing between anchored controls<br />
*[[#Layout|ChildSizing]]: lets you set the layout and spacing of child controls<br />
<br />
The internals of the algorithms are explained here: [[LCL AutoSizing]].<br />
<br />
=Fixed design=<br />
<br />
Fixed design is the default. The Anchor's property is set to [akLeft,akTop], which means the values of Left and Top are not changed by the [[LCL]]. If AutoSize is false, the [[LCL]] does not alter Width or Height. If AutoSize is true, the Width and Height are changed to fit the content. For example, TLabel.AutoSize defaults to true and thus changing its Caption will resize the label to accommodate the changed wording. TButton.AutoSize defaults to false, so changing a button's Caption does not resize the button. When you set Button.AutoSize to true the Button will shrink or enlarge every time the button's Caption or its font (or theme) changes. Note, this change does not always happen immediately. For example during FormCreate all autosizing is suspended.<br />
At any time you can change the "Left", "Top", "Width", or the "Height" property.<br />
<br />
=AutoSize=<br />
<br />
<var>AutoSize</var> is a boolean property found in many classes; it permits the size of a control to be adjusted automatically to accommodate differences in the text or graphic contained therein, and allows most efficient use of available space. This is a crucial mechanism to create cross platform forms.<br />
<br />
Normally AutoSize=true does two things for a visible control:<br />
*If possible it resizes the control to the preferred size. For example a TButton's Width and Height is resized to fit the caption, while a TEdit is only resized in Height. A TEdit's Width is not autosized. You can change the Width of a TEdit. (see GetPreferredSize).<br />
*It moves all fixed positioned child controls by the same amount, so that the leftmost child control is moved to Left=0 (depends on BorderSpacing) and the topmost child control is moved to Top=0.<br />
<br />
==Lazarus vs Delphi==<br />
<br />
*Delphi's AutoSize happens only when certain properties change, for example when the Font of a TLabel is changed. The [[LCL]] AutoSize is always active. Delphi allows one to change the size of a Label which has AutoSize=true, the Lazarus control does not. <br />
*Delphi's hidden controls are not autosized.<br />
*With Delphi, changing the size of a control does not resize/move anchored child controls during loading, but a constructor is normally used to make the desired effect. When using akRight,akBottom anchors, set AnchorSides and BorderSpacing to keep the distance correct.<br />
<br />
==AutoSize and resizing the control==<br />
<br />
With AutoSize=false buttons are given a default fixed size.<br />
<br />
[[Image:Autosize1.png]]<br />
<br />
When setting AutoSize=true for each button, the buttons are enlarged (or shrunk) to fit the text and theme frame. <br />
<br />
[[Image:Autosize on.png]]<br />
<br />
AutoSize does not shrink to the smallest possible size as you can see in the OK Button. It uses the GetPreferredSize method of the control, which calls the CalculatePreferredSize method. The default TWinControl implementation queries the widgetset, which might have a preferred width or height. Each control can override the CalculatePreferredSize method. For example TImage overrides it and returns the size of the Picture. If no preferred width (height) is available the returned value is 0 and the [[LCL]] will keep the Width (Height) (unless the ControlStyle flag csAutoSize0x0 is set, which is currently only used by TPanel).<br />
<br />
A TWinControl computes the size of all its child controls and uses that to compute its preferred size.<br />
<br />
When a control is anchored to both left and right its ''width is fixed''. For example with ''Align=alTop'' the control is anchored to left and right and follows the width of the Parent. If ''Parent.AutoSize'' is true, then the ''Parent'' will use the preferred width of the control to compute its own preferred width, and thus the control will be resized to its preferred width. See [[Autosize_/_Layout#Align_and_AutoSize|Align and AutoSize]]. If no preferred width is available, then the last set bounds are used ('''BaseBounds''' or '''ReadBounds'''). If no bounds were set, the '''GetControlClassDefaultSize''' method is used. Same for '''Height''' and anchoring to top and bottom.<br />
<br />
Constraints are always applied and have precedence.<br />
<br />
==AutoSize and moving child controls==<br />
<br />
When AutoSize=false you can place and move controls freely:<br />
<br />
[[Image:Autosize on.png]]<br />
<br />
When AutoSize=true child controls with fixed positions are moved to fit.<br />
<br />
[[Image:Autosize panel on.png]]<br />
<br />
Both buttons on the panel were moved by the same amount to top and left, so that there is no space left to the left and top. If the buttons would have BorderSpacing>0 or the Panel.ChildSizing.LeftRightSpacing>0 the buttons would be moved so that the defined space is used.<br />
<br />
Only child controls with the following values are moved:<br />
* Anchors=[akLeft,akRight]<br />
* AnchorSide[akLeft].Control=nil<br />
* AnchorSide[akTop].Control=nil<br />
* Align=alNone<br />
<br />
The moving of the child controls depend on the propert [[Autosize_/_Layout#Layout|ChildSizing.Layout]]. The layout is applied in the method TWinControl.AlignControls which can be overridden completely or in parts. For example TToolBar overrides ControlsAligned to position all controls with Align=alCustom and defines a flow layout where controls that do not fit are put into subsequent lines.<br />
<br />
Controls can disable the moving of child controls by setting the ControlStyle flag csAutoSizeKeepChildLeft and csAutoSizeKeepChildTop (since 0.9.29).<br />
<br />
==AutoSize and Forms==<br />
<br />
Forms without Parent are controlled by the window manager and can therefore not be placed or resized freely. Setting the size is only a recommendation and might be ignored by the window manager. For example you might set the Width of a Form to 1000 and the widgetset might answer with a resize to 800. If you set the Width in OnResize you might create an endless loop. That's why the [[LCL]] TForm disables AutoSize when the widgetset resizes the form.<br />
<br />
That means AutoSize for forms stops when the user resizes the form or if the window manager does not like the bounds. For example some Linux window managers have features like magnetic sides which resizes windows in relationship to other windows.<br />
<br />
===Force an auto sizing of a form===<br />
You can start/execute a new AutoSize by doing:<br />
<br />
<syntaxhighlight>Form1.AutoSize := False;<br />
Form1.AutoSize := True;</syntaxhighlight><br />
<br />
===Compute the size of an autosized form in advance===<br />
<br />
When placing an autosized form you might need the size of the form before showing it. Autosizing requires the handle. You can compute the size of a form before showing it with:<br />
<br />
<syntaxhighlight>Form1.HandleNeeded;<br />
Form1.GetPreferredSize(PreferredWidth,PreferredHeight);</syntaxhighlight><br />
<br />
'''Note''': The window manager and form events might change the size. The preferred size does not include the form's window borders. This is a planned feature.<br />
<br />
=Anchor Sides=<br />
<br />
Controls have four sides: akLeft, akTop, akRight and akBottom. Each side can be anchored to the parent or the side of another sibling (a control with the same parent). Anchoring means '''keeping the distance'''. The default is Anchors=[akLeft,akTop]. Vertical anchors are independent of horizontal anchors. Some properties like Align and Parent.AutoSize have higher precedence and can change the behaviour. <br />
<br />
==Anchored to Parent or Nil==<br />
<br />
Anchoring to Nil (the default) has almost the same effect as anchoring to the Parent. Both try to keep the distance to a side of the Parent's client area. Anchoring to Nil uses the distance of the last SetBounds call, while Anchoring to Parent uses the BorderSpacing value.<br />
<br />
There are four combinations of akLeft,akRight (akTop,akBottom):<br />
<br />
*'''akLeft, no akRight''': the control's '''Left''' is fixed and not changed by the [[LCL]]. The right side is not anchored, so it follows the left side. That means the '''Width''' is kept too.<br />
*'''akLeft and akRight''': the control's '''Left''' is fixed and not changed by the [[LCL]]. The right side is anchored to the Parent's right side. That means if the Parent is enlarged by 100pixel then the control's '''Width''' is enlarged by 100pixel too.<br />
*'''akRight, no akLeft''': the control's left side is not anchored, so it will follow its right side. The right side is anchored. That means if the parent is enlarged by 100pixel then the control is moved to the right by 100pixel.<br />
*'''no akLeft and no akRight''': neither sides are anchored. The position of the center of the control scales with the parent. For example if the control is in the middle of the parent and the parent is enlarged by 100 pixel, then the control is moved 50pixel to the right.<br />
<br />
===Changing the size of the parent of an anchored control===<br />
<br />
When changing the size of a parent all anchored child controls are moved and/or resized immediately unless AutoSizing is disabled. For example AutoSizing is disabled during loading of a form and during creation of a form.<br />
<br />
A GroupBox with a Button: [[Image:Anchors1.png]]<br />
<br />
When the GroupBox is enlarged the distance of the anchored side is kept:<br />
<br />
With akLeft, no akRight: [[Image:Anchors akLeft.png]]<br />
<br />
With akLeft and akRight: [[Image:Anchors akLeft akRight.png]]<br />
<br />
With akRight, no akLeft: [[Image:Anchors akRight no akLeft.png]]<br />
<br />
Three buttons in a groupbox: [[Image:Anchors no akLeft no akRight small.png]]<br />
<br />
With no akLeft and no akRight their centers are scaled: [[Image:Anchors no akLeft no akRight big.png]]<br />
<br />
<br />
'''Notes:'''<br />
*Loading a form is like one big SetBounds. During loading properties are set using the values of the lfm. Keep in mind that there can be multiple lfm files because of ancestors and frames. After loading the [[LCL]] enables autosizing. Anchored controls use the bounds at the end of loading. Any step in between is ignored.<br />
*For custom controls it is often better to set AnchorSides instead of only Anchors.<br />
<br />
===Changing the size of an anchored control===<br />
<br />
When changing the Width of an anchored control, for example via the Object Inspector or via code ''Button1.Width:=3'', you can see the difference between anchoring to ''Parent'' and anchoring to ''Nil''. Anchoring to Parent will resize and move the Button1, while anchoring to Nil will only resize. For example:<br />
<br />
====Anchored to nil====<br />
<br />
[[Image:Anchored_right_nil_resize1.png]] A Button1 anchored [akTop,akRight], AnchorSide[akRight].Control=nil <br />
<br />
[[Image:Anchored_right_nil_resize2.png]] Setting the Width to a smaller value will shrink the button, keeping the button's ''Left'', increasing the distance of the right side.<br />
<br />
[[Image:Anchored_right_nil_resize3.png]] When the Groupbox is resized the button will keep the new distance.<br />
<br />
'''Explanation:''' setting the '''Width''' is equivalent to calling SetBounds('''Left''',Top,'''NewWidth''',Height). That's why the ''Left'' is kept. This is Delphi compatible.<br />
<br />
====Anchored to Parent====<br />
[[Image:Anchored_right_parent_resize1.png]] A Button1 anchored [akTop,akRight], AnchorSide[akRight].Control=Button1.Parent<br />
<br />
[[Image:Anchored_right_parent_resize2.png]] Setting the Width to a smaller value will shrink the button, keeping the right distance, changing the button's ''Left''.<br />
<br />
==Anchored to sibling==<br />
<br />
You can anchor to neighbor controls. The following example shows:<br />
<br />
*you can anchor a label's left to the left of a button<br />
*anchor a label's top to the bottom of a button<br />
*anchor the center of a label to the center of a button<br />
<br />
[[Image:Anchorsides example1.png]]<br />
<br />
[[Image:Anchorside example2.png]]<br />
<br />
For more details and how to setup anchors see: [[Anchor Sides]].<br />
<br />
[[Image:Anchoreditor.png]]<br />
<br />
==BorderSpacing==<br />
<br />
The BorderSpacing properties controls the minimum amount of space around a control. The properties are:<br />
<br />
*'''Around''': this amount in pixel is added to Left, Top, Right, Bottom.<br />
*'''Left''': space in pixel on the left side of the control<br />
*'''Top''': space in pixel above the control<br />
*'''Right''': space in pixel on the right side of the control<br />
*'''Bottom''': space in pixel below the control<br />
*'''InnerBorder''': this amount in pixel is added twice to the preferred width and height. Some controls override the calculation and ignore this property. An example where it works is TButton. With InnerBorder you can make a button bigger than needed.<br />
*'''CellAlignHorizontal''': This is used in table layouts like ChildSizing.Layout=cclLeftToRightThenTopToBottom. If the control is smaller than the table cell, this property defines where to align the control: to the left ccaLeftTop, to the right ccaRightBottom or in the middle ccaCenter.<br />
*'''CellAlignVertical''': same as CellAlignHorizontal but for vertical alignment.<br />
<br />
===BorderSpacing rules===<br />
<br />
*Around is added to Left,Top,Right,Bottom borderspacing<br />
*The space can be even bigger, if the controls have constraints that don't allow to expand.<br />
<br />
====Anchoring to the opposite side====<br />
<br />
For example right side of A to left side of B.<br />
<br />
Both borderspacings of A and B are used.<br />
<br />
*The horizontal space between two controls (LeftControl, RightControl on Parent) is the maximum of <br />
**LeftControl.BorderSpacing.Right + LeftControl.BorderSpacing.Around<br />
**RightControl.BorderSpacing.Left + RightControl.BorderSpacing.Around<br />
**Parent.ChildSizing.HorizontalSpacing<br />
*The vertical space works analog: between two controls (TopControl, BottomControl on Parent) is the maximum of <br />
**TopControl.BorderSpacing.Bottom + TopControl.BorderSpacing.Around<br />
**BottomControl.BorderSpacing.Top + BottomControl.BorderSpacing.Around<br />
**Parent.ChildSizing.VerticalSpacing<br />
<br />
For instance if LeftControl.BorderSpacing.Right = 3 and LeftControl.BorderSpacing.Around = 4, then there is at least 7 pixel space between the two controls. If RightControl.BorderSpacing.Left = 4 and RightControl.BorderSpacing.Around = 4 then the space will be at least 8. If Parent.ChildSizing.HorizontalSpacing = 10 then the space will be at least 10.<br />
<br />
====Anchoring to the same side====<br />
<br />
For example right side of A to right side of B.<br />
<br />
*Only borderspacings of A is used.<br />
*Parent.ChildSizing.Horizontal/VerticalSpacing are not used.<br />
<br />
====Anchoring to center====<br />
<br />
For example center of A to center of B.<br />
<br />
No borderspacing is used and no Parent.ChildSizing.Horizontal/VerticalSpacing is used.<br />
<br />
====Example====<br />
<br />
A common example is anchoring a Label to an Edit.<br />
<br />
[[Image:BorderSpacing Anchors.png]]<br />
<br />
The Label center is vertically anchored to the Edit. The Edit's left side is anchored to the right side of the Label. Both have BorderSpacing.Around=6. This results in 6 pixel space between the Label and the Edit and 6 pixel space left of the Label and 6 pixel space right of the Edit. There is 6 pixel space above and below the Edit as well.<br />
<br />
*The left space between a control and its parent (Label1 on GroupBox1) is the maximum of <br />
**Label1.BorderSpacing.Left + Label1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.LeftTopSpacing<br />
<br />
*The right space between a control and its parent (Edit1 on GroupBox1) is the maximum of <br />
**Edit1.BorderSpacing.Right + Edit1.BorderSpacing.Around<br />
**GroupBox1.ChildSizing.RightBottomSpacing<br />
<br />
*When a control's center is anchored to another control, for example the above Label is centered vertically to the Edit1, then all spacings are ignored.<br />
<br />
*When a control's left side is anchored to the left side of another control (i.e. they are aligned with the left side), then the distance between both left's side is the <br />
**Control1.BorderSpacing.Left + Control1.BorderSpacing.Around<br />
<br />
<br />
Here is a more complex example:<br />
<br />
[[Image:Borderspacing anchors2.png]]<br />
<br />
The important parts of the lfm code:<br />
<br />
<syntaxhighlight> object GroupBox1: TGroupBox<br />
AutoSize = True<br />
Caption = 'GroupBox1'<br />
TabOrder = 0<br />
object Label1: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label1'<br />
end<br />
object Edit1: TEdit<br />
AnchorSideLeft.Control = Label1<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = GroupBox1<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Around = 6<br />
TabOrder = 0<br />
Text = 'Edit1'<br />
end<br />
object Label2: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'Label2'<br />
end<br />
object ComboBox1: TComboBox<br />
AnchorSideLeft.Control = Label2<br />
AnchorSideTop.Control = Label2<br />
AnchorSideTop.Side = asrBottom<br />
AnchorSideRight.Control = GroupBox1<br />
AnchorSideRight.Side = asrBottom<br />
Anchors = [akTop, akLeft, akRight]<br />
BorderSpacing.Right = 6<br />
TabOrder = 1<br />
Text = 'ComboBox1'<br />
end<br />
object CheckBox1: TCheckBox<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = ComboBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
Caption = 'CheckBox1'<br />
TabOrder = 2<br />
end<br />
object Label3: TLabel<br />
AnchorSideLeft.Control = GroupBox1<br />
AnchorSideTop.Control = Edit2<br />
AnchorSideTop.Side = asrCenter<br />
BorderSpacing.Around = 6<br />
Caption = 'Label3'<br />
end<br />
object Edit2: TEdit<br />
AnchorSideLeft.Control = Label3<br />
AnchorSideLeft.Side = asrBottom<br />
AnchorSideTop.Control = CheckBox1<br />
AnchorSideTop.Side = asrBottom<br />
BorderSpacing.Around = 6<br />
TabOrder = 3<br />
Text = 'Edit2'<br />
end<br />
end</syntaxhighlight><br />
<br />
===BorderSpacing and Align===<br />
<br />
BorderSpacing works with Align. In the example below there is a Memo1 with Align=alTop and a Memo2 with Align=alCLient. <br />
*Normally the two Memos would fill the whole GroupBox. <br />
*But the Memo1 has BorderSpacing.Around=10, so there 10 pixel space around Memo1. <br />
*The Memo2 has BorderSpacing.Top=20. The space between Memo1 and Memo2 is the maximum, which is the 20 from Memo2. <br />
*Memo2 has also BorderSpacing.Right=50 so there is 50 Pixel space right of Memo2.<br />
*The GroupBox can define default space for all its child controls via ChildSizing.LeftRightSpacing/VerticalSpacing/HorizontalSpacing. In this example it does not (all 0).<br />
<br />
[[Image:Borderspacing align1.png]]<br />
<br />
=Align=<br />
<br />
The Align property works pretty much like in Delphi and can be used to quickly fill an area. For example to let a TListBox fill the entire area of its Parent, set ListBox1.Align=alClient.<br />
The align values alTop, alBottom, alLeft and alRight will place controls without overlapping if possible. That means all controls with Align in alLeft,alTop,alBottom,alRight will not overlap if there is enough space.<br />
<br />
The algorithm works as follows:<br />
*First all controls with alTop are put to the top of the client area. If there are several controls with alTop the last added/moved will be put topmost. The algorithm will try to avoid overlapping and keep the height of the control, while expanding the width to maximum. AnchorSides of the left, top and right sides are ignored. The bottom AnchorSide works normal. Borderspacing and Parent.ChildSizing spaces are considered, so you can define space around each aligned control.<br />
*Then all controls with alBottom are put to the bottom of the client area. Otherwise it works analog to alTop.<br />
*Then all controls with alLeft are put to the left of the client area between the alTop and alBottom controls. Otherwise it works analog to alTop.<br />
*Then all controls with alRight are put to the right of the client area between the alTop and alBottom controls. Otherwise it works analog to alTop.<br />
*If there is a control with alClient it will fill the remaining client.<br />
*If there is more than one control with alClient they will be placed at the same position, overlapping each other. Use the Visibility property to define which one is shown.<br />
<br />
[[Image:Autosize_align.png]]<br />
<br />
==Align and BorderSpacing==<br />
<br />
The space of BorderSpacing and of the parent's ChildSizing is applied to aligned controls. The memo below has Align=alClient.<br />
<br />
[[Image:Autosize align borderspacing.png]]<br />
<br />
==Align and Anchoring==<br />
<br />
The free side of an aligned control (e.g. the right side of a '''Align=alLeft''') follows the anchoring rules. If the anchor is not set, then the control will keep its '''Width'''. If the anchor is set, then the '''Width''' will change.<br />
<br />
==Align and AutoSize==<br />
<br />
A control aligned with alLeft or alRight expands vertically, and will use its designed width. If ''AutoSize'' is set to ''true'', then the ''Width'' will be the preferred width. The button below has ''Align=alRight'' and ''AutoSize=true''.<br />
<br />
[[Image:Autosize align autosize.png]]<br />
<br />
==Align and parent AutoSize==<br />
<br />
A control aligned with alClient fills the remaining space. A parent control with ''AutoSize=true'' will expand or shrink to enclose its children exactly. What happens if you combine these two properties? Say you put a button with Align=alClient into a groupbox with AutoSize=true? <br />
<br />
[[Image:Autosize nested1.png]]<br />
<br />
The [[LCL]] uses the ''preferred size'' of the buttons and expands or shrinks the groupbox accordingly:<br />
<br />
[[Image:Autosize nested2.png]]<br />
<br />
==alCustom==<br />
<br />
This Align value exists for custom AutoSize algorithms and is treated by the [[LCL]] almost like alNone. Controls with alCustom are not moved by the [[LCL]] but by your custom control. You have to override CalculatePreferredSize and DoAutoSize.<br />
<br />
==Order of controls with same Align==<br />
<br />
Controls with same Align are positioned in the following order. On alLeft the control with the lowest Left wins, for alTop the lowest Top, for alRight the biggest Left+Width, for alBottom the biggest Top+Height. If two controls have the same coordinate the one last moved (calls to SetBounds or SetParent) wins. Controls can override CreateControlAlignList to change the order or override DoAlignChildControls to handle the whole Align.<br />
<br />
Note: Before 0.9.29 controls at the same coordinate were put in Z order. This resulted in reordering in some cases and had to be fixed. There is one incompatibility: If you added several controls with alTop or alLeft without specifying bounds the controls are put at 0,0 and therefore the last added wins, where formerly the first won.<br />
<br />
Note for Delphians:<br />
Contrary to the VCL the LCL can and often does delay recomputing the layout. Delphi does the layout after every property change. Setting Align=alTop calls in the VCL a special AlignControls, where the current control has a special meaning. That's why in the VCL the order of setting properties is important. The LCL layout algorithm is independent of the chronological order of setting the properties.<br />
<br />
For example all changes in FormCreate are delayed and the layout is computed from the final property values. On the other hand ButtonClick has no delay (aka you have to add the<br />
Disable/EnableAutoSize yourself), so inserting alTop controls may have different results.<br />
<br />
If you want a specific order, use Disable/EnableAutoSize and set the "Top" properties. For example TForm.OnCreate is called with AutoSize disabled, so you can use:<br />
<br />
<syntaxhighlight><br />
procedure TForm1.FormCreate(Sender: TObject);<br />
begin<br />
// making sure Panel2 is above Panel1, even though Panel1 is set first:<br />
Panel1.Align:=alTop;<br />
Panel1.Top:=1;<br />
Panel2.Align:=alTop;<br />
Panel2.Top:=0;<br />
end;<br />
</syntaxhighlight><br />
<br />
=Layout=<br />
<br />
==Rows, columns and lines==<br />
<br />
You can align child controls in rows and columns using the ChildSizing properties. For example:<br />
<br />
*ChildSizing.Layout=cclLeftToRightThenTopToBottom<br />
*ChildSizing.ControlsPerLine=3<br />
*AutoSize=false (the groupbox does not resize to fit the child controls)<br />
<br />
[[Image:Autosize layout lefttoright.png]]<br />
<br />
The '''Layout''' property defaults to cclNone. If you set '''Layout''' to another value, every child, that has ''normal'' anchors, will be aligned. ''Normal'' anchors means:<br />
*Anchors=[akLeft,akTop]<br />
*AnchorSide[akLeft].Control=nil<br />
*AnchorSide[akTop].Control=nil<br />
*Align=alNone<br />
<br />
The value '''cclLeftToRightThenTopToBottom''' will put the first child at top, left, the second to the right, and so forth. This is a '''line'''. The property '''ControlsPerLine''' defines when a new line is started. In the above example each line (row) has 3 controls. There are 12 controls, so there are 4 lines (rows) each with 3 controls (columns).<br />
If ''ControlsPerLine'' is 0 it means unlimited controls per line - there would be only one row with all childs.<br />
<br />
You can see that the rows have different sizes, each row has the size of the biggest control and that the controls are resized to column width. There is no space between the rows. The space in the image comes from the used theme, not from the [[LCL]].<br />
<br />
==Fixed space between rows and columns==<br />
<br />
You can add space between with these properties:<br />
<br />
*ChildSizing.VerticalSpacing - Space between rows<br />
*ChildSizing.HorizontalSpacing - Space between columns<br />
*ChildSizing.LeftRightSpacing - Space on the left and right of ''all'' columns<br />
*ChildSizing.TopBottomSpacing - Space above and below of ''all'' columns<br />
<br />
The above example with<br />
ChildSizing.VerticalSpacing=6, ChildSizing.HorizontalSpacing=15, ChildSizing.LeftRightSpacing=30, ChildSizing.TopBottomSpacing=10, AutoSize=true<br />
<br />
[[Image:Autosize layout parentspacing.png]]<br />
<br />
Additionally you can add individual space for each control with its BorderSpacing properties.<br />
<br />
==Enlarge==<br />
<br />
The above example resized the GroupBox to the needed space. If your GroupBox has a fixed size or if it is not freely resizable, for instance if the GroupBox should fill the whole width of the form, then the childs should enlarge. There are several modes. The default mode ''ChildSizing.EnlargeHorizontal=crsAnchorAligning'' is to not enlarge anything. The space on the right side will be unused.<br />
<br />
*crsAnchorAligning - do not use the extra space<br />
*crsScaleChilds - multiply the width/height with the same factor<br />
*crsHomogeneousChildResize - add to each width/height the same amount<br />
*crsHomogeneousSpaceResize - add to each space between the childs the same amount<br />
<br />
===crsScaleChilds===<br />
<br />
ChildSizing.EnlargeHorizontal=crsScaleChilds, ChildSizing.EnlargeVertical=crsScaleChilds, AutoSize=false<br />
<br />
[[Image:Autosize layout scalechilds.png]]<br />
<br />
For example if the ClientWidth is twice as big as needed, then every child will be twice as big.<br />
<br />
===crsHomogeneousChildResize===<br />
<br />
ChildSizing.EnlargeHorizontal=crsHomogeneousChildResize, ChildSizing.EnlargeVertical=crsHomogeneousChildResize, AutoSize=false<br />
<br />
[[Image:Autosize layout homogeneouschildresize.png]]<br />
<br />
For example if the ClientWidth is 30 pixel bigger than needed, then every child will be 10 pixel broader.<br />
<br />
===crsHomogeneousSpaceResize===<br />
<br />
ChildSizing.EnlargeHorizontal=crsHomogeneousSpaceResize, ChildSizing.EnlargeVertical=crsHomogeneousSpaceResize, AutoSize=false<br />
<br />
[[Image:Autosize layout homogeneousspaceresize.png]]<br />
<br />
For example if the ClientWidth is 40 pixel bigger than needed, there will be 10 pixel space on the left, right and between each child.<br />
<br />
==Shrink==<br />
<br />
Shrinking works similarly to enlarging. You can set different modes if there is not enough space for controls. '''ShrinkHorizontal''', '''ShrinkVertical'''.<br />
<br />
==Individual cells==<br />
<br />
In the above examples all controls were resized the same, each filled the whole ''cell''. A ''cell'' is the space in a specific row and column. Normally a control fills the whole cell space. This can be changed with the properties '''BorderSpacing.CellAlignHorizontal''' and '''BorderSpacing.CellAlignVertical'''.<br />
<br />
For example set the ''BorderSpacing.CellAlignHorizontal'' of the fifth button to '''caCenter''' you will get this:<br />
<br />
[[Image:Autosize layout cellhorizontal cacenter.png]]<br />
<br />
There are four possible values for CellAlignHorizontal/CellAlignVertical:<br />
<br />
*caFill: the child control fills the whole width (height) of the cell<br />
*caCenter: the child control uses its preferred width (height) and will be centered in the cell<br />
*caLeftTop: the child control uses its preferred width (height) and will be leftaligned in the cell<br />
*caRightBottom: the child control uses its preferred width (height) and will be rightaligned in the cell<br />
<br />
[[Image:Autosize layout cellalign.png]]<br />
<br />
=Custom layout with OnResize / OnChangeBounds=<br />
<br />
Sometimes the [[LCL]] layout is not sufficient. The below example shows a GroupBox1 with a ListBox1 and a Memo1. The ListBox1 should fill one third of the space, the Memo1 takes the rest.<br />
<br />
[[Image:Autosize_onresize.png]]<br />
<br />
Whenever the GroupBox is resized the ListBox1.Width should be one third. To achieve this set the OnResize event of the GroupBox1 to:<br />
<br />
<syntaxhighlight>procedure TForm1.GroupBox1Resize(Sender: TObject);<br />
begin<br />
ListBox1.Width := GroupBox1.ClientWidth div 3;<br />
end;</syntaxhighlight><br />
<br />
===Common mistake: Wrong OnResize event===<br />
<br />
Do not put ''all'' your resizing code into the Form OnResize event. The Form OnResize event is only called when the Form was resized. The child controls (e.g. a GroupBox1) is resized later, so the GroupBox1.ClientWidth has still the old value during the FormResize event. You must use the GroupBox1.OnResize event to react to changes of GroupBox1.ClientWidth.<br />
<br />
===Common mistake: Width instead of ClientWidth, AdjustClientRect===<br />
<br />
The Width is the size including the frame. The ClientWidth is the inner Width without the frame. Some controls like the TPanel paints a further frame. Then you have to use AdjustClientRect. The same example, but instead of a GroupBox1 the ListBox1 is in a Panel1:<br />
<br />
<syntaxhighlight>procedure TForm1.Panel1Resize(Sender: TObject);<br />
var <br />
r: TRect;<br />
begin<br />
r := Panel1.ClientRect;<br />
Panel1.AdjustClientRect(r);<br />
ListBox1.Width := (r.Right - r.Left) div 3;<br />
end;</syntaxhighlight><br />
<br />
=Custom Controls=<br />
<br />
When you write your own control, you can override and fine tune many parts of the [[LCL]] autosizing. <br />
<br />
==SetBounds, ChangeBounds, DoSetBounds==<br />
<br />
'''SetBounds''' is called when the properties Left, Top, Width, Height, BoundsRect is set or the user calls it directly. SetBounds updates the BaseBounds and BaseParentClientSize, which are used by anchoring to keep the distance. For example loading a Form with TMemo and the lfm contains TMemo's Left and Width, then SetBounds is called two times for the memo. When the user maximizes a window, SetBounds is called for the form, but not for the Memo, keeping the BaseBounds of the Memo. If the Memo is anchored to the right, the Width of the Memo is changed based on the BaseBounds and BaseParentClientSize. Keep in mind that the given aLeft, aTop, aWidth, aHeight might not be valid and will be changed by the [[LCL]] before applied. Delphi calls SetBounds more often. SetBounds calls ChangeBounds with KeepBase=false.<br />
<br />
'''ChangeBounds''' is called whenever the position or size of the control is set, either via the properties or by the layouter of the [[LCL]]. SetBounds calls internally ChangeBounds with KeepBase=false, while the [[LCL]] layouter calls it with KeepBase=true. Override this for code that might change the preferred size or resizes other controls. Keep in mind that the given aLeft, aTop, aWidth, aHeight might not be valid and will be changed by the [[LCL]] before applied. You can call this function.<br />
<br />
'''DoSetBounds''' is a low level function to set the private variables FLeft, FTop, FWidth, FHeight. Do not call this function, only the [[LCL]] calls it. It also updates FClientWidth and FClientHeight accordingly. Override this to update the content layout of the control, for example scroll bars. As always: do not paint here, but call Invalidate and paint in OnPaint or override Paint.<br />
<br />
'''DoAdjustClientRectChange''' is called by the [[LCL]] and the [[LCL]] interface, when the ClientRect has changed and the Width and Height were kept.<br />
<br />
'''WMSize''' exists for Delphi/VCL compatibility. It is called by the [[LCL]] interface and on every change of bounds.<br />
<br />
==AdjustClientRect==<br />
<br />
The method AdjustClientRect can be overriden by your custom controls and affects Align, ChildSizing.Layout and AnchorSides. It does not affect the meaning of Left, Top and does not affect normal anchoring (for example setting).<br />
<br />
When you want to draw your own frame, then the child controls should be aligned within these frames. For example TPanel draws a frame and reduces the client area by overriding the method '''AdjustClientRect''':<br />
<br />
<syntaxhighlight> TCustomPanel = class(TCustomControl)<br />
...<br />
protected<br />
...<br />
procedure AdjustClientRect(var aRect: TRect); override;<br />
...<br />
<br />
procedure TCustomPanel.AdjustClientRect(var aRect: TRect);<br />
var<br />
BevelSize: Integer;<br />
begin<br />
inherited AdjustClientRect(aRect);<br />
<br />
BevelSize := BorderWidth;<br />
if (BevelOuter <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
if (BevelInner <> bvNone) then<br />
inc(BevelSize, BevelWidth);<br />
<br />
InflateRect(aRect, -BevelSize, -BevelSize);<br />
end;</syntaxhighlight><br />
<br />
===AdjustClientRect and Align===<br />
<br />
AdjustClientRect can be used to reduce the client area used by all autosize operations. For example TPanel uses AdjustClientRect to reduce the client area by the borderwidth:<br />
<br />
[[Image:Adjustclientrect_align.png]]<br />
<br />
The Button1 in the screenshot was created with Align=alClient. ChildSizing.Layout is affected too. <br />
<br />
When anchoring to a parent via AnchorSides the AdjustClientRect is used too:<br />
<syntaxhighlight><br />
Button1.AnchorParallel(akTop,0,Button1.Parent);<br />
</syntaxhighlight><br />
<br />
The Button1's top side is anchored to the top side of the parent's client area. If the AdjustClientRect adds 3 to the Top the Button1.Top will be 3 (3 plus 0).<br />
<br />
==Own AutoSize==<br />
<br />
When ''AutoSize'' is set to true the control should be resized to the preferred size if possible. <br />
<br />
===Preferred Size===<br />
<br />
The new size is fetched by the [[LCL]] via '''GetPreferredSize''' which calls '''CalculatePreferredSize''', which can be overridden. For example let's write a '''TQuadrat''', which is a TShape, but its height should equal its width:<br />
<br />
<syntaxhighlight> TQuadrat = class(TShape)<br />
protected<br />
procedure CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean); override;<br />
end;<br />
...<br />
procedure TQuadrat.CalculatePreferredSize(var PreferredWidth,<br />
PreferredHeight: integer; WithThemeSpace: Boolean);<br />
begin<br />
PreferredHeight:=Width;<br />
end;</syntaxhighlight><br />
<br />
The method CalculatePreferredSize gets two var parameters: ''PreferredWidth'' and ''PreferredHeight''. They default to 0 which means: there is no preferred size, so the [[LCL]] will not change the size. The above function sets ''PreferredHeight'' to the current ''Width''. The boolean parameter ''WithThemeSpace'' is deprecated and always false.<br />
<br />
'''Important''': CalculatePreferredSize must not change the bounds or any other value of the control that can trigger an autosize. Doing so will create a loop.<br />
<br />
Computing the ''PreferredWidth/Height'' can be expensive. Therefore the [[LCL]] caches the result until ''InvalidatePreferredSize'' is called for the control. In our example the ''PreferredHeight'' depends on the ''Width'', so we must invalidate when the ''Width'' changes:<br />
<br />
<syntaxhighlight> TQuadrat = class(TShape)<br />
protected<br />
...<br />
procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;<br />
end;<br />
...<br />
procedure TQuadrat.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);<br />
InvalidatePreferredSize;<br />
// Note: The AdjustSize can be omitted here, because the [[LCL]] does that after calling DoSetBounds.<br />
end;</syntaxhighlight><br />
<br />
The [[LCL]] will automatically trigger the autosizing when the bounds have changed, so the example is complete.<br />
<br />
The default TWinControl implementation of CalculatePreferredSize queries the widgetset, which might return a preferred width and/or height. Each control can override the CalculatePreferredSize method. For example TImage overrides it and returns the size of the Picture. If no preferred width (height) is available the returned value is 0 and the [[LCL]] will keep the current Width (Height). If 0 is a valid size for your control, you must set the ControlStyle flag csAutoSize0x0 (ControlStyle:=ControlStyle+[csAutoSize0x0];). An example is the [[LCL]] control TPanel.<br />
<br />
===AdjustSize===<br />
<br />
When the preferred size depends on a new property, then every time the property changes the auto sizing must be triggered. For example:<br />
<br />
<syntaxhighlight>procedure TQuadrat.SetSubTitle(const AValue: string);<br />
begin<br />
if FSubTitle = AValue then exit;<br />
FSubTitle := AValue;<br />
InvalidatePreferredSize;<br />
AdjustSize;<br />
end;</syntaxhighlight><br />
<br />
=Reduce overhead with DisableAutoSizing, EnableAutoSizing=<br />
<br />
Since Lazarus 0.9.29 there is a new autosizing algorithm, that reduces the overhead and allows deep nested dependencies. Up to 0.9.28 DisableAlign/EnableAlign and Disable/EnableAutoSizing worked only for one control and its direct child controls.<br />
<br />
Each time you change a property the [[LCL]] triggers a recomputation of the layout:<br />
<br />
<syntaxhighlight>Label1.Caption := 'A'; // first recompute<br />
Label2.Caption := 'B'; // second recompute</syntaxhighlight><br />
<br />
The recomputations will trigger events and send messages. To reduce the overhead you can suspend the autosizing:<br />
<br />
<syntaxhighlight>DisableAutoSizing;<br />
try<br />
Label1.Caption := 'A'; // no recompute<br />
Label2.Caption := 'B'; // no recompute<br />
finally<br />
EnableAutoSizing; // one recompute<br />
end;</syntaxhighlight><br />
<br />
You must balance calls to Disable/EnableAutoSizing. Autosizing starts only when EnableAutoSizing is called after a corresponding (earlier) DisableAutoSizing.<br />
<br />
Since 0.9.29 the Disable/EnableAutoSizing works for the entire form. This means every call to DisableAutoSizing suspends autosizing for '''all''' controls on that form. If you write your own control you can now use the following:<br />
<br />
<syntaxhighlight>procedure TMyRadioGroup.DoSomething;<br />
begin<br />
DisableAutoSizing; // disables not only TMyRadioGroup, but the whole form's autosizing<br />
try<br />
// delete items ...<br />
// add, reorder items ...<br />
// change item captions ...<br />
finally<br />
EnableAutoSizing; // recompute<br />
end;<br />
end;</syntaxhighlight><br />
<br />
Which basically means: you do not have to care. Just call Disable/EnableAutoSizing. <br />
<br />
Note: this is wrong:<br />
<br />
<syntaxhighlight>Button1.DisableAutoSizing;<br />
Label1.EnableAutoSizing; // wrong: every control has its own autosizing reference counter</syntaxhighlight><br />
<br />
==DisableAutoSizing and Form bounds==<br />
<br />
DisableAutoSizing has another useful effect under asynchronous Window Managers such as you find in Linux systems.<br />
Each time a form is resized or moved the bounds are sent to the widgetset (DoSendBoundsToInterface). Even if the form is not shown the Handle is resized. The window manager often treats these bounds only as a proposal. The window manager has its own logic and often only the first bounds sent are used. The second, third or further moves may be ignored. With DisableAutoSizing you can make sure that only the final bounds are sent to the widgetset making the form bounds more reliable.<br />
<br />
=Example: a button panel=<br />
<br />
This example combines several of the [[LCL]] layout mechanisms to create a panel with three buttons: a ''Help'' button to the left and ''Ok'' and ''Cancel'' buttons to the right. We want the panel to be at the bottom of the form filling the entire width. The buttons and the panel are autosized to fit all fonts and themes.<br />
<br />
Step 1: Create the panel and set its Align property to alBottom. Add three TBitBtns.<br />
<br />
[[Image:Autosize example buttonpanel1.png]]<br />
<br />
Set the ''Kind'' properties of the BitBtns to show the glyphs. You might need to set GlyphShowMode to gsmAlways to see them on your platform. Set the BitBtn's ''AutoSize'' property to ''True'', which will shrink/enlarge the buttons to fit perfectly around the glyphs and text. Depending on your theme and platform you might notice that the buttons have different heights.<br />
<br />
[[Image:Autosize example buttonpanel2.png]]<br />
<br />
Set the ''Align'' property of the help button to ''alLeft'' and set the other two buttons' ''Align'' property to ''alRight''. This will enlarge the buttons vertically and move them to the far left/right. alLeft/alRight has no effect on the width, so the buttons use their preferred width.<br />
<br />
[[Image:Autosize example buttonpanel3.png]]<br />
<br />
The Panel height is still fixed. Now set the panel ''AutoSize'' property to ''True''. The panel now shrinks vertically to fit the tallest button.<br />
<br />
[[Image:Autosize example buttonpanel4.png]]<br />
<br />
The ''Ok'' button has a short caption, so the button is very small. Set the button's ''Constraints.MinWidth'' to ''75''. The button will now enlarge somewhat.<br />
<br />
[[Image:Autosize example buttonpanel5.png]]<br />
<br />
Now add some space around the buttons. Set the panel's ''ChildSizing.LeftTopSpacing/RightBottomSpacing/HorizontalSpacing'' to ''6''.<br />
<br />
[[Image:Autosize example buttonpanel6.png]]<br />
<br />
Finally clear the ''Caption'' of the panel and set its ''BevelOuter'' to ''bvNone''.<br />
<br />
[[Image:Autosize example buttonpanel7.png]]<br />
<br />
=Scrolling=<br />
<br />
Some [[LCL]] controls like ''TScrollBox'', ''TForm'', and ''TFrame'' show scrollbars if the child controls are too big to fit the scrollbox, form or frame. They inherit this behaviour from their ancestor '''TScrollingWinControl'''.<br />
<br />
A scrolling control's '''logical client area''' can be bigger than the '''visible client area'''. The ''visible client area'' is '''ClientRect'''. It always starts at 0,0 and its width and height is the inner area. For example in a TGroupBox it is the size of the area inside the frame. So the following is always true:<br />
<br />
<syntaxhighlight>ClientWidth <= Width<br />
ClientHeight <= Height</syntaxhighlight><br />
<br />
The '''logical client area''' is defined by the method '''GetLogicalClientRect'''. By default it is the same as ''ClientRect''. When a child control is anchored to the right side, it uses the ''logical client area''. ''TScrollingWinControl'' overrides this method and returns the ''Range'' of the scrollbars if they are bigger than the ''ClientRect''. The '''Range''' can be set manually or automatically with ''AutoScroll=true''. An example for ''AutoScroll=true'':<br />
<br />
[[Image:Autoscroll all fit1.png]]<br />
<br />
The upper button has a fixed Width of 200. The lower button is anchored to the right side of the panel. Therefore the child controls have a preferred width of 200. Because the panel is bigger than 200 the logical client area is bigger too and the lower button expands. <br />
<br />
Now the panel is shrunk, so that the ClientWidth becomes lower than 200:<br />
<br />
[[Image:Autoscroll not fit1.png]]<br />
<br />
The preferred width is still 200, so the logical client area is now 200 and bigger than the visible client area. The lower button has now a Width of 200 and the panel shows a horizontal scrollbar.<br />
<br />
==Scroll Position==<br />
<br />
Changing the Position of a scrollbar does not change the Left or Top of any child control, nor does it change the logical client area, nor does it affect autosizing. The child controls are only virtually moved.<br />
<br />
==Scrolling and AutoSize==<br />
<br />
When AutoSize=true the [[LCL]] will expand the control to accommodate all its child controls, and no scrollbars are needed. If the control cannot be expanded, then (only) is the secondary action of AutoSize executed: moving the child controls.<br />
<br />
=Docking=<br />
<br />
Docking uses the described methods and properties of this page, see [[Docking]].<br />
<br />
=Splitter=<br />
<br />
See [[TSplitter]].<br />
<br />
=TLabel.WordWrap=<br />
<br />
'''TLabel.WordWrap''' changes the behavior of the preferred size of the label. '''WordWrap=true''' requires that the Width of the label is fixed, for example by anchoring the left and right size of the label. The preferred height of the label is then computed by breaking the Caption into multiple lines.<br />
<br />
=DPI auto-adjustment and absolute layout auto-adjustment=<br />
<br />
Historically the [[LCL]] has been utilized mostly to design absolute layouts, despite the huge amount of options which Lazarus offers for flexible layouts, like Align, Anchors, etc, as described on the rest of this article. On top of that, it has also historically ignored the DPI of the target and instead utilized values in pixels to measure the left, top, width and height properties of controls. For desktop platforms and Windows CE this has worked reasonably well, but with the advent of [[LCL]] support for Android this could no longer be ignored. Starting in Lazarus 0.9.31 the [[LCL]] can reinterprete the [[LCL]] absolute layout in pixels as a flexible grid. There are multiple modes to choose from and they will allow to reinterprete the pixel values as either really absolute, or as in being adjusted for the DPI or as in being considered simply a fraction of the form size.<br />
<br />
It is important to note that these new rules affect only controls which are positioned without Align and with the most standard Anchors only.<br />
<br />
In the case where the values will be adjusted for DPI, there is a new property: TCustomForm.DesignTimeDPI which should store the DPI value of the system where the form was designed. The positioning values will be expanded when the target DPI is larger than the design time DPI or reduced otherwise. The common value for desktop DPIs is 96 and is the default value given.<br />
<br />
property DesignTimeDPI: Integer read FDesignTimeDPI write FDesignTimeDPI;<br />
<br />
The way in which the layout is adjusted can be controlled with the property TApplication.LayoutAdjustmentPolicy<br />
<br />
TLayoutAdjustmentPolicy = (<br />
lapDefault, // widgetset dependent<br />
lapFixedLayout, // A fixed absolute layout in all platforms<br />
lapAutoAdjustWithoutHorizontalScrolling, // Smartphone platforms use this one,<br />
// the x axis is stretched to fill the screen and<br />
// the y is scaled to fit the DPI<br />
lapAutoAdjustForDPI // For desktops using High DPI, scale x and y to fit the DPI<br />
);<br />
<br />
And the following new methods in TControl allow to force a layout-autoadjustment in a particular control and all its children or to control how particular descendants of TControl react to this:<br />
<br />
<syntaxhighlight><br />
TControl = class<br />
public<br />
...<br />
procedure AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy;<br />
const AFromDPI, AToDPI, AOldFormWidth, ANewFormWidth: Integer); virtual;<br />
function ShouldAutoAdjustLeftAndTop: Boolean; virtual;<br />
function ShouldAutoAdjustWidthAndHeight: Boolean; virtual;<br />
</syntaxhighlight><br />
<br />
LCL-CustomDrawn-Android will call AutoAdjustLayout when the screen rotates, for example.<br />
<br />
=More details=<br />
<br />
<p>Many controls override ''TControl.DoAutoSize'' to perform the actual auto-sizing. </p><br />
<p>IMPORTANT: Many Delphi controls override this method and many call this method directly after setting some properties.</p><br />
<p>During handle creation not all interfaces can create complete Device Contexts which are needed to calculate things like text size.</p><br />
<p>That's why you should always call ''AdjustSize'' instead of <var>DoAutoSize</var>.</p><br />
<br />
<p><var>TControl.AdjustSize</var> calls <var>DoAutoSize</var> in a smart fashion.</p><br />
<p>During loading and handle creation the calls are delayed.</p><br />
<p>This method initially does the same as ''TWinControl.DoAutoSize''. But since <var>DoAutoSize</var> is commonly overriden by descendant components, it is not useful to perform all tests, which can result in too much overhead. To reduce this the [[LCL]] calls <var>AdjustSize</var> instead.</p><br />
<br />
When setting AutoSize = true the [[LCL]] autosizes the control in width and height. This is one of the most complex parts of the [[LCL]], because the result depends on nearly a hundred properties. Let's start simple:<br />
<br />
The [[LCL]] will only autosize the ''Width'' (Height) if it is free to resize. In other words - the width is not autosized if:<br />
*the left and right side is anchored. You can anchor the sides with the '''Anchors''' property or by setting the ''Align'' property to alTop, alBottom or alClient.<br />
*the ''Width'' is bound by the ''Constraints'' properties. The Contraints can also be overriden by the widgetset. For example the winapi does not allow resizing the height of a combobox. And the gtk widgetset does not allow resizing the width of a vertical scrollbar.<br />
<br />
Same for ''Height''.<br />
<br />
The new size is calculated by the protected method '''TControl.CalculatePreferredSize'''.<br />
This method asks the widgetset for an appropriate Width and Height. For example a TButton has preferred Width and Height. A TComboBox has only a preferred Height. The preferred Width is returned as 0 and so the [[LCL]] does not autosize the Width - it keeps the width unaltered. Finally a TMemo has no preferred Width or Height. Therefore AutoSize has no effect on a TMemo.<br />
<br />
Some controls override this method. For example the TGraphicControl descendants like TLabel have no window handle and so cannot query the widgetset. They must calculate their preferred Width and Height themselves.<br />
<br />
The widgetsets must override the GetPreferredSize method for each widget class that has a preferred size (Width or Height or both).<br />
<br />
==Parent.AutoSize==<br />
<br />
The above described the simple explanation. The real algorithm provides far more possibilities and is therefore far more complex.<br />
<br />
==Properties / Methods==<br />
<br />
*Left<br />
*Top<br />
<br />
If Parent<>nil then Left, Top are the pixel distance to the top, left pixel of the parent's client area (not scrolled). Remember the client area is always ''without'' the frame and scrollbars of the parent. For Delphi users: Some VCL controls like TGroupbox define the client area as the whole control including the frame and some not - the [[LCL]] is more consistent, and therefore Delphi incompatible. Left and Top can be negative or bigger than the client area. Some widgetsets define a minimum/maximum somewhere around 10.000 or more.<br />
<br />
When the client area is scrolled the Left and Top are kept unchanged. <br />
<br />
During resizing/moving Left and Top are not always in sync with the coordinates of the Handle object.<br />
<br />
If Parent=nil then Left, Top depend on the widgetset and the window manager. Till Lazarus 0.9.25 this is typically the screen coordinate of the left,top of the client area of the form. This is Delphi incompatible. It is planned to change this to the Left, Top of the window.<br />
<br />
<br />
Hint:<br />
Each time you change Left and Top the [[LCL]] moves instantly and recomputes the whole layout. If you want to change Left ''and'' Top use instead:<br />
<syntaxhighlight>with Button1 do<br />
SetBounds(NewLeft, NewTop, Width, Height);</syntaxhighlight><br />
<br />
*Width<br />
*Height<br />
<br />
The Size in pixels must not be negative, and most widgetsets do not allow Width=0 and/or Height=0. Some controls on some platforms define a bigger minimum constraint in Constraints.MinInterfaceWidth/Height. Instead of sizing a control to Width=0 and/or Height=0, set Visible=false or Parent=nil. During resizing/moving Width and Height are not always in sync with the size of the Handle object.<br />
<br />
<br />
*BoundsRect<br />
<br />
Same as Bounds(Left, Top, Width, Height).<br />
<br />
Common newbie mistake:<br />
<syntaxhighlight>BoundsRect.Left := 3; // WRONG: common newbie mistake</syntaxhighlight><br />
This has no effect, because reading BoundsRect is a function. It creates a temporary TRect on the stack. The above is the same as<br />
<syntaxhighlight>var<br />
r: TRect;<br />
begin<br />
r := BoundsRect; // fetch the bounds<br />
r.Left := 3; // change a value on the stack<br />
end; // no change</syntaxhighlight><br />
<br />
*ClientRect<br />
<br />
Left and Top are always 0,0. Width and Height are the visible size in pixels of the client area. Remember the client area is without the frame and without scrollbars. In a scrollable client area the logical client area can be bigger than the visible.<br />
<br />
*ClientOrigin<br />
<br />
Returns the screen coordinate of the topleft coordinate 0,0 of the client area. Note that this value is the position as stored in the interface and is not always in sync with the [[LCL]]. When a control is moved, the [[LCL]] sets the bounds to the desired position and sends a move message to the interface. It is up to the interface to handle moves instantly or queued.<br />
<br />
*LCLIntf.GetClientBounds<br />
<br />
Returns the client bounds of a control. Like ClientRect, but Left and Top are the pixel distances to the control's left, top. For example on a TGroupBox the Left, Top are the width and height of the left and top frame border. Scrolling has no effect on GetClientBounds.<br />
<br />
*LCLIntf.GetWindowRect<br />
<br />
After the call, ARect will be the control area in screen coordinates. That means, Left and Top will be the screen coordinate of the TopLeft pixel of the Handle object and Right and Bottom will be the screen coordinate of the BottomRight pixel.<br />
<br />
<br />
*FBaseBoundsLock: integer<br />
<br />
Increased/Decreased by LockBaseBounds/UnlockBaseBounds.<br />
Used to keep FBaseBounds during SetBounds calls.<br />
<br />
<br />
*FBaseParentClientSize: TPoint<br />
<br />
The Parent.ClientRect size valid for the FBaseBounds.<br />
FBaseBounds and FBaseParentClientSize are used to calculate the distance for<br />
akRight (akBottom). When the parent is resized, the [[LCL]] knows what distance to keep.<br />
<br />
<br />
*FBoundsRectForNewParent: TRect<br />
<br />
When changing the Parent of a control the Handle is recreated and many<br />
things can happen. Especially for docking forms the process is too<br />
unreliable. Therefore the BoundsRect is saved. The VCL uses a similar<br />
mechanism.<br />
<br />
<br />
*fLastAlignedBounds: TRect<br />
<br />
See TControl.SetAlignedBounds for an explanation.<br />
In short: It stops some circles between interface and [[LCL]] autosizing.<br />
<br />
<br />
*FLastChangebounds: TRect<br />
<br />
Used to stop calling ChangeBounds with the same coordinates. This happens<br />
very often.<br />
<br />
<br />
*FLastDoChangeBounds: TRect<br />
<br />
Used to avoid calling OnChangeBounds with the same coordinates. This reduces<br />
user defined autosizing.<br />
<br />
<br />
*FLastResizeClientHeight: integer<br />
*FLastResizeClientWidth: integer<br />
*FLastResizeHeight: integer<br />
*FLastResizeWidth: integer<br />
<br />
Used to avoid calling OnResize with the same coordinates. This reduces user<br />
defined autosizing.<br />
<br />
<br />
*FLoadedClientSize: TPoint<br />
<br />
During loading many things are delayed and many things are set and worse: in<br />
the wrong order. That's why SetClientWidth/SetClientHeight calls are stored<br />
and set at end of loading again.<br />
This way the [[LCL]] can restore the distances (e.g. akRight) used during<br />
designing.<br />
<br />
<br />
*FReadBounds: TRect<br />
<br />
Same as FLoadedClientSize, but for SetLeft, SetTop, SetWidth, SetHeight.<br />
<br />
<br />
*procedure SetBoundsRectForNewParent(const AValue: TRect);<br />
<br />
Used to set FBoundsRectForNewParent. See above.<br />
<br />
<br />
*procedure SetAlignedBounds(aLeft, aTop, aWidth, aHeight: integer); virtual; <br />
<br />
As SetBounds but without changing the default sizes.<br />
<br />
<br />
*procedure SetInitialBounds(aLeft, aTop, aWidth, aHeight: integer); virtual; <br />
<br />
A smart version of SetBounds, reducing overhead during creation and loading.<br />
<br />
<br />
*procedure UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: boolean); virtual;<br />
<br />
Commit current bounds to base bounds.<br />
<br />
*procedure SetClientHeight(Value: Integer);<br />
*procedure SetClientSize(Value: TPoint);<br />
*procedure SetClientWidth(Value: Integer); <br />
<br />
Exists for Delphi compatibility too. Resizes the control, to get the wanted ClientRect size.<br />
<br />
<br />
*procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;<br />
<br />
This is the internal SetBounds.<br />
Applies constraints, updates base bounds, calls OnChangeBound, OnResize,<br />
locks bounds.<br />
<br />
<br />
*procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); virtual;<br />
<br />
This really sets the FLeft, FTop, FWidth, FHeight private variables.<br />
<br />
<br />
*procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); virtual;<br />
<br />
This is the standard procedure overriden by many Delphi controls.<br />
TWinControl overrides it too. <br />
** ignores calls when bounds are locked<br />
** lock the FBoundsRealized to avoid overhead to the interface during auto sizing.<br />
ChangeBounds is not locked this way.<br />
<br />
<br />
*Function GetClientOrigin: TPoint; virtual;<br />
<br />
Screen coordinate of Left, Top of client area.<br />
<br />
*Function GetClientRect: TRect; virtual;<br />
<br />
Size of client area. (always Left=0, Top=0)<br />
<br />
*Function GetScrolledClientRect: TRect; virtual;<br />
<br />
Visible client area in ClientRect.<br />
<br />
<br />
*function GetChildsRect(Scrolled: boolean): TRect; virtual;<br />
<br />
Returns the Client rectangle relative to the control's Left, Top.<br />
If Scrolled is true, the rectangle is moved by the current scrolling values<br />
(for an example see TScrollingWincontrol).<br />
<br />
*function GetClientScrollOffset: TPoint; virtual;<br />
<br />
Returns the scrolling offset of the client area.<br />
<br />
<br />
*function GetControlOrigin: TPoint; virtual;<br />
<br />
Returns the screen coordinate of the topleft coordinate 0,0 of the control area. (The topleft pixel of the control on the screen)<br />
Note that this value is the position as stored in the interface and is not always in sync with the [[LCL]]. When a control is moved, the [[LCL]] sets the<br />
bounds to the wanted position and sends a move message to the interface. It is up to the interface to handle moves instantly or queued.<br />
<br />
=FAQ=<br />
<br />
==Why does AutoSize not work in the designer properly?==<br />
<br />
In the designer controls can be dragged around and properties can be set in almost any order. To allow this and avoid possible conflicts, the AutoSizing is not updated on every change at design time. <br />
<br />
==Why does TForm.AutoSize not work when something changes?==<br />
<br />
See [[Autosize_/_Layout#AutoSize_and_Forms|AutoSize and Forms]]<br />
<br />
==Do I need to call Application.ProcessMessages when creating lots of controls?==<br />
<br />
Application.ProcessMessages is called by the [[LCL]] automatically after every message (e.g. after every event like OnClick). Calling it yourself is only needed if the changes should become visible to the user immediately. For example:<br />
<br />
<source>procedure TFrom.Button1Click(Sender: TObject);<br />
begin<br />
// change width of a control<br />
Button1.Width := Button1.Width + 10;<br />
// apply any needed changes and repaint the button<br />
Application.ProcessMessages;<br />
// do a lot of things that takes a long time<br />
...<br />
// after leaving the OnClick the LCL automatically processes messages<br />
end;</source><br />
<br />
==When enabling Anchors at runtime the control resizes, does not use the current values. Why?==<br />
<br />
akBottom means: keep a distance to the bottom side of the parent.<br />
The distance to keep is defined by the base bounds. They are set at designtime or by runtime calls of SetBounds or UpdateBaseBounds.<br />
<br />
For example:<br />
A TListBox (Anchors=[akLeft,aTop]) at designtime has a bottom distance of 100 pixel.<br />
And a button to enable/disable the akBottom of the TListBox.<br />
Now start the application and press the button to enable akBottom. The 100 pixel distance will be activated, because this was the last time the programmer defined the base bounds of the TListBox. All other resizes were done by the [[LCL]] and are irrelevant. The programmers base bounds rules. You can resize the form and the 100 pixel will be kept.<br />
In order to use the current bounds as base bounds use:<br />
<syntaxhighlight>ListBox1.UpdateBaseBounds(true, true, false);<br />
ListBox1.Anchors := ListBox1.Anchors + [akBottom];</syntaxhighlight><br />
<br />
Setting Anchors does not automatically call UpdateBaseBounds, because this would destroy the ability to change properties independently.<br />
<br />
==Resizing stringgrid columns in form's OnResize event does not work==<br />
<br />
The Form's OnResize is triggered when the Form's Width, Height, ClientWidth or ClientHeight changes.<br />
This is per se independent of TStringGrid. Of course it often happens that both the Form and the TStringGrid are resized. This means using forms OnResize will often work, but not always. A prominent example where it always fails is when the theme is changed and you have a TStringGrid in a TGroupBox in a TForm. When the theme changed the form size is kept, so no Forms OnResize is triggered. But the TGroupBox changed, so the TStringGrid should be resized.<br />
<br />
Solution: Use the TStringGrid's OnResize.<br />
<br />
==Why is TForm.Width equal to TForm.ClientWidth?==<br />
<br />
Mattias' notes:<br />
<br />
"There are historical and technical reasons.<br />
<br />
For forms without parent the Clientwidth equals the Width, because the real Width including the frame was not available on Linux ten years ago (at least not reliable on various window managers). I didn't test, but I heard it is now possible with gtk2. The main problem is the autosizing, because this needs the frame sizes before the form is mapped to the screen. It might be, that this is only available after an event, which means that you have to wait for it, which means trouble for ShowModal.<br />
Changing this breaks compatibility with a lot of existing [[LCL]] code, but for this we added the LCLVersion in the lfm files.<br />
<br />
There is a new compiler define '''LCLRealFormBounds''' In Lazarus trunk 1.7 that enables real size for a form. To use it just compile your application with LCLRealFormBounds ON. For now, only win32 widgetset is supported.<br />
<br />
For all other controls the rules is ClientWidth<=Width. The Width is the ClientWidth plus the widget frame. The question is if the scrollbars belong to the frame. I would say yes and it was implemented that way some time ago. Apparently this has changed. See the current cursor problem on synedit."<br />
<br />
==I get an infinite loop / How to debug autosizing?==<br />
<br />
Here are some notes, what other users made wrong and how to find out:<br />
<br />
===Differences to Delphi===<br />
For Delphi users: Please read: [[Autosize_/_Layout#Lazarus_vs_Delphi|Lazarus_vs_Delphi]]<br />
<br />
===Overriding a [[LCL]] method and triggering a recompute===<br />
<br />
You override a method and tell the [[LCL]] to compute again, even if nothing changed. Check for AdjustSize, Realign, AlignControls, InvalidatePreferredSize. For example:<br />
<br />
<syntaxhighlight>procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
// This will create an endless loop<br />
InvalidatePreferredSize;<br />
<br />
// This will create an endless loop too:<br />
OtherComponent.Left:=10;<br />
OtherComponent.Left:=20;<br />
<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
Explanation: The SetBounds method is called often, even without changing anything. For example a "Left:=30;" will do so.<br />
<br />
Remedy: Check for changes:<br />
<br />
<syntaxhighlight>procedure TMainForm.SetBounds(ALeft, ATop, AWidth, AHeight: integer);<br />
begin<br />
if (Left <> ALeft) or (Top <> ATop) or (Width <> AWidth) or (Height <> AHeight) then<br />
InvalidatePreferredSize;<br />
inherited SetBounds(ALeft, ATop, AWidth, AHeight);<br />
end;</syntaxhighlight><br />
<br />
====TWinControl.AlignControls====<br />
<br />
<code><br />
procedure AlignControls(aControl: TControl; var aRect: TRect); override;<br />
</code><br />
<br />
AlignControls moves and sizes all child controls. The [[LCL]] implementation ignores controls with Align=alCustom.<br />
<br />
The parameter aControl: TControl is kept for VCL compatibility. The [[LCL]] always passes nil. It gives aControl precedence when applying the Align property.<br />
When you have for example two controls A,B with Align=alLeft then the one with the lower Left is put left most. If both have the same Left then creation order wins.<br />
Now imagine you want to switch both controls A,B in the designer. You drag B to the left. This results in setting B.Left to 0. Now AlignControls starts and find both A.Left=0 and B.Left=0. Normally A would win.<br />
To let B win the VCL calls AlignControls(B,r).<br />
So aControl is the last moved.<br />
Contrary to the VCL the [[LCL]] allows to combine multiple layout changes without recomputing on every step. The [[LCL]] keeps track of the last moved controls in TWinControl.fAlignControls and applies the order in TWinControl.CreateControlAlignList. The aControl parameter is always nil.<br />
<br />
See TWinControl.CreateControlAlignList.<br />
<br />
===OnResize/OnChangeBounds conflict with [[LCL]] properties===<br />
You set bounds that bite the [[LCL]] properties. For example a TLabel.AutoSize is true by default. If you set the Label1.Width in an OnResize event, the [[LCL]] will recompute, resize the Label1 and call the OnResize again. Start your application in the debugger and reproduce the bug. When it enters the loop, pause the application and see the call stack. If you see one of your events or your methods start searching there. For example:<br />
<br />
<syntaxhighlight>procedure TMainForm.FormResize(Sender: TObject);<br />
begin<br />
// if Button1 is anchored or AutoSize=true then the following might create an endless loop:<br />
Button1.Width:=100;<br />
end;</syntaxhighlight><br />
<br />
===[[LCL]] interface bug, custom widget===<br />
Sometimes the [[LCL]] interface or your custom control has a bug and undoes the [[LCL]] bounds. Compile the [[LCL]] with -dVerboseIntfSizing. This will write the communication between [[LCL]] and the [[LCL]] interface. If a widget does not allow free resizing, it must tell the [[LCL]] via the Constraints. Search for SetInterfaceConstraints in the various [[LCL]] interfaces and TWSControlClass.ConstraintWidth/Height.<br />
<br />
===alCustom===<br />
You use alCustom. This was only half implemented in earlier Lazarus versions, but some clever programmers used it to create some special effects. Now it is implemented and your program does not work anymore. Please see here what alCustom does: [[Autosize_/_Layout#alCustom|alCustom]].<br />
<br />
=See also=<br />
<br />
* [[Docking]]<br />
* [[Anchor Sides]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=How_do_I_create_a_bug_report&diff=124617How do I create a bug report2019-05-24T13:24:59Z<p>Zoran: </p>
<hr />
<div>{{How do I create a bug report}}<br />
<br />
This document contains some guidelines for using the FPC / Lazarus [http://bugs.freepascal.org bug tracker] as a reporter. This document is written for FPC / Lazarus users who identify bugs, have recommendations, want to submit patches or find other issues and want to report these to the Lazarus development team.<br />
<br />
== Code compilation errors ==<br />
If you have errors when compiling code from latest SVN revision, please contact a proper [http://freepascal.org/maillist.var FPC mailing list] or [http://lists.lazarus-ide.org/listinfo Lazarus mailing list] or better join <b>#fpc</b> or <b>#lazarus-ide</b> IRC channel on irc.freenode.net. Then the problem should be solved more promptly.<br />
<br />
== Logging in / Creating new account ==<br />
You need to be logged in to edit or submit bug reports. If you are logged in as guest, you need to log out first (Guests cannot make reports, only watch them). If you already have an account, go to the [http://bugs.freepascal.org/login_page.php login page], otherwise create a new account on the [http://bugs.freepascal.org/signup_page.php sign up page].<br />
<br />
== Check if the bug is not already reported ==<br />
Use the search field in [http://bugs.freepascal.org/view_all_bug_page.php View Issues]. Hint: The searching is not smart: e.g. if you have a problem using TEdit.SelStart, search for "SelStart".<br />
If the issue is already reported:<br />
* reopen it, if the bug report has been resolved or closed - use Reopen Issue button<br />
* add a note, if you have reproduced this bug in a different situation than reported<br />
* you can set the system to monitor changes in this bug report - use Monitor Issue button<br />
Note: you need to be logged in to perform these operations, see section [[#Logging in / Creating new account]].<br />
<br />
== What should be submitted via the Bug Tracker? ==<br />
* Bugs: If you identify errors, glitches, or other faults in [[FPC]] or [[Lazarus]]<br />
* Suggestions: If you have identified a better way to do something<br />
* Improvements: If you can make something work better<br />
<br />
Please note: The Bug Tracker is '''not''' designed to field questions. These should be directed toward the Forums [http://forum.lazarus.freepascal.org/].<br />
<br />
* For reporting, go to [http://bugs.freepascal.org/set_project.php?project_id=1 Lazarus bug tracker]. You must be logged in, see section [[#Logging in / Creating new account]].<br />
* Go to the [http://bugs.freepascal.org/bug_report_advanced_page.php Report Issue] page. Fill in as much as you can and know. The more specific, the better. <br />
<br />
=== Bug ===<br />
* Important fields are the OS and Product fields and the steps to reproduce this issue. If an issue cannot be reproduced by the developers, they cannot start to fix it! Do not forget to mention your specific architecture/configuration (32 or 64 bit, little or big endian if both are possible on your platform, version of your operating system).<br />
* If possible, please <b>upload a small test application that shows the bug</b>. This will likely speed up a fix.<br />
* If there is some graphical error, it is useful to upload a (partial) screenshot (in png or jpeg, not bmp format).<br />
* If it is a crash, try to create a backtrace. See [[Creating a Backtrace with GDB]] for more info.<br />
* You can try to reproduce the bug on as many different platforms as you can - it helps to determine if it is widget specific issue.<br />
* If you have a possible solution, you can add a patch - see [[Creating A Patch]], which will speed up the process.<br />
* You can boost fixing the bug by submitting a bounty, see [[Bounties]].<br />
<br />
=== Regression caused by a certain revision ===<br />
If you can find a revision in trunk that caused a bug, please include also its SVN revision number. The report will usually be assigned to the author of that revision.<br />
You can find a quilty revision by a "bisect" process which is a binary search over the revisions. There are tools to help with that:<br />
* A Git command [https://git-scm.com/docs/git-bisect git bisect]. For that you must use [[git_mirrors]] or a [[Lazarus_git-svn|git svn link]]. Git is fast in this operation because all the revision history is local and nothing needs to fetched from a server.<br />
* A Perl script [http://search.cpan.org/dist/App-SVN-Bisect/bin/svn-bisect svn-bisect], available in CPAN. It mimics the "git bisect" command but works on SVN data directly.<br />
<br />
=== Suggestion ===<br />
Explain your idea. A GUI mockup or an example of another tool using the feature can be helpful.<br />
<br />
=== Improvement ===<br />
* If you have implemented a new feature in the source code or improved documentation in the XML files, create a patch - see [[Creating A Patch]].<br />
* If you have improved translation in a language .po file, attach the whole .po file (not a diff).<br />
* If you have another resource file, for example an icon, attach it to the report.<br />
<br />
==Attachments==<br />
If you add source code or project sample attachments for the bug report ('''strongly recommended''', see [[Tips on writing bug reports]]), please compress them using preferably these formats:<br />
* zip (.zip)<br />
* gzip (.gz) <br />
* tar.gzip (.tgz/.tar.gz)<br />
<br />
Other formats like 7zip, Bzip and RAR are ok, too. Nowadays tools for them are easily available.<br />
<br />
==Understanding the Report Status==<br />
An issue can have the following states:<br />
* New: it has entered in the bug tracker, but hasn't been assigned, acknowledged, confirmed or resolved.<br />
* Acknowledged: the Lazarus team has seen the issue and has set its target, though they have not necessarily verified that the bug is valid<br />
* Confirmed: a member of the Lazarus team has duplicated the bug or agrees that the feature should be implemented<br />
* Assigned: the issue has been assigned to a Lazarus developer, who will try to fix/implement it<br />
* Resolved: the person to whom the issue was assigned thinks the issue can be closed. Then he also sets the resolution, for example '''fixed''' or '''not an issue'''.<br />
* Feedback: the reporter should provide feedback to answer any questions posed by the Lazarus team, or to confirm that the issue is fixed satisfactorily.<br />
* Closed: the reporter tested the fix and agrees with the fix. Periodically resolved issues that have not been closed by the reporter, will be a closed by the bug tracker administrator.<br />
<br />
==See also==<br />
* [[Creating A Patch]] If you have modified the source code to implement a solution, this article helps you to add it to your bug report in the most efficient way, so that developers can add it to the main code as fast as possible<br />
* [[Database bug reporting]] Specific info and sample programs for database bugs<br />
* [[Moderating the bug tracker]]<br />
* The following page contains good tips about [http://www.chiark.greenend.org.uk/~sgtatham/bugs.html How to Report Bugs Effectively].</div>Zoranhttps://wiki.freepascal.org/index.php?title=How_do_I_create_a_bug_report&diff=124616How do I create a bug report2019-05-24T13:22:50Z<p>Zoran: Swap order of sections Logging in and Check if bug reported already -- looks more natural to me</p>
<hr />
<div>{{How do I create a bug report}}<br />
<br />
This document contains some guidelines for using the FPC / Lazarus [http://bugs.freepascal.org bug tracker] as a reporter. This document is written for FPC / Lazarus users who identify bugs, have recommendations, want to submit patches or find other issues and want to report these to the Lazarus development team.<br />
<br />
== Code compilation errors ==<br />
If you have errors when compiling code from latest SVN revision, please contact a proper [http://freepascal.org/maillist.var FPC mailing list] or [http://lists.lazarus-ide.org/listinfo Lazarus mailing list] or better join <b>#fpc</b> or <b>#lazarus-ide</b> IRC channel on irc.freenode.net. Then the problem should be solved more promptly.<br />
<br />
== Logging in / Creating new account ==<br />
You need to be logged in to edit or submit bug reports. If you are logged in as guest, you need to log out first (Guests cannot make reports, only watch them). If you already have an account, go to the [http://bugs.freepascal.org/login_page.php login page], otherwise create a new account on the [http://bugs.freepascal.org/signup_page.php sign up page].<br />
<br />
== Check if the bug is not already reported ==<br />
Use the search field in [http://bugs.freepascal.org/view_all_bug_page.php View Issues]. Hint: The searching is not smart: e.g. if you have a problem using TEdit.SelStart, search for "SelStart".<br />
If the issue is already reported:<br />
* reopen it, if the bug report has been resolved or closed - use Reopen Issue button<br />
* add a note, if you have reproduced this bug in a different situation than reported<br />
* you can set the system to monitor changes in this bug report - use Monitor Issue button<br />
Note: you need to be logged in to perform these operations, see section [[#Logging/Creating new account]].<br />
<br />
== What should be submitted via the Bug Tracker? ==<br />
* Bugs: If you identify errors, glitches, or other faults in [[FPC]] or [[Lazarus]]<br />
* Suggestions: If you have identified a better way to do something<br />
* Improvements: If you can make something work better<br />
<br />
Please note: The Bug Tracker is '''not''' designed to field questions. These should be directed toward the Forums [http://forum.lazarus.freepascal.org/].<br />
<br />
* For reporting, go to [http://bugs.freepascal.org/set_project.php?project_id=1 Lazarus bug tracker]. You must be logged in, see section [[#Logging in / Creating new account]].<br />
* Go to the [http://bugs.freepascal.org/bug_report_advanced_page.php Report Issue] page. Fill in as much as you can and know. The more specific, the better. <br />
<br />
=== Bug ===<br />
* Important fields are the OS and Product fields and the steps to reproduce this issue. If an issue cannot be reproduced by the developers, they cannot start to fix it! Do not forget to mention your specific architecture/configuration (32 or 64 bit, little or big endian if both are possible on your platform, version of your operating system).<br />
* If possible, please <b>upload a small test application that shows the bug</b>. This will likely speed up a fix.<br />
* If there is some graphical error, it is useful to upload a (partial) screenshot (in png or jpeg, not bmp format).<br />
* If it is a crash, try to create a backtrace. See [[Creating a Backtrace with GDB]] for more info.<br />
* You can try to reproduce the bug on as many different platforms as you can - it helps to determine if it is widget specific issue.<br />
* If you have a possible solution, you can add a patch - see [[Creating A Patch]], which will speed up the process.<br />
* You can boost fixing the bug by submitting a bounty, see [[Bounties]].<br />
<br />
=== Regression caused by a certain revision ===<br />
If you can find a revision in trunk that caused a bug, please include also its SVN revision number. The report will usually be assigned to the author of that revision.<br />
You can find a quilty revision by a "bisect" process which is a binary search over the revisions. There are tools to help with that:<br />
* A Git command [https://git-scm.com/docs/git-bisect git bisect]. For that you must use [[git_mirrors]] or a [[Lazarus_git-svn|git svn link]]. Git is fast in this operation because all the revision history is local and nothing needs to fetched from a server.<br />
* A Perl script [http://search.cpan.org/dist/App-SVN-Bisect/bin/svn-bisect svn-bisect], available in CPAN. It mimics the "git bisect" command but works on SVN data directly.<br />
<br />
=== Suggestion ===<br />
Explain your idea. A GUI mockup or an example of another tool using the feature can be helpful.<br />
<br />
=== Improvement ===<br />
* If you have implemented a new feature in the source code or improved documentation in the XML files, create a patch - see [[Creating A Patch]].<br />
* If you have improved translation in a language .po file, attach the whole .po file (not a diff).<br />
* If you have another resource file, for example an icon, attach it to the report.<br />
<br />
==Attachments==<br />
If you add source code or project sample attachments for the bug report ('''strongly recommended''', see [[Tips on writing bug reports]]), please compress them using preferably these formats:<br />
* zip (.zip)<br />
* gzip (.gz) <br />
* tar.gzip (.tgz/.tar.gz)<br />
<br />
Other formats like 7zip, Bzip and RAR are ok, too. Nowadays tools for them are easily available.<br />
<br />
==Understanding the Report Status==<br />
An issue can have the following states:<br />
* New: it has entered in the bug tracker, but hasn't been assigned, acknowledged, confirmed or resolved.<br />
* Acknowledged: the Lazarus team has seen the issue and has set its target, though they have not necessarily verified that the bug is valid<br />
* Confirmed: a member of the Lazarus team has duplicated the bug or agrees that the feature should be implemented<br />
* Assigned: the issue has been assigned to a Lazarus developer, who will try to fix/implement it<br />
* Resolved: the person to whom the issue was assigned thinks the issue can be closed. Then he also sets the resolution, for example '''fixed''' or '''not an issue'''.<br />
* Feedback: the reporter should provide feedback to answer any questions posed by the Lazarus team, or to confirm that the issue is fixed satisfactorily.<br />
* Closed: the reporter tested the fix and agrees with the fix. Periodically resolved issues that have not been closed by the reporter, will be a closed by the bug tracker administrator.<br />
<br />
==See also==<br />
* [[Creating A Patch]] If you have modified the source code to implement a solution, this article helps you to add it to your bug report in the most efficient way, so that developers can add it to the main code as fast as possible<br />
* [[Database bug reporting]] Specific info and sample programs for database bugs<br />
* [[Moderating the bug tracker]]<br />
* The following page contains good tips about [http://www.chiark.greenend.org.uk/~sgtatham/bugs.html How to Report Bugs Effectively].</div>Zoranhttps://wiki.freepascal.org/index.php?title=Firebird&diff=124582Firebird2019-05-17T19:44:36Z<p>Zoran: /* Advanced transactions */</p>
<hr />
<div>{{Firebird}}<br />
{{Infobox databases}}<br />
This is a guide about using '''Firebird''' database in Lazarus/FPC, using Firebird with SQLDB (the FPC/Lazarus built-in database library). Other access methods are described in [[Other Firebird libraries]].<br />
<br />
__TOC__<br />
<br />
==Overview==<br />
Firebird is an open source, free database server that has been in use and developed for decades (it developed out of the Interbase 6 database that was open sourced by Borland). It includes rich support for SQL statements (e.g. INSERT...RETURNING), stored procedures, triggers, etc. You can write compiled UDFs (User-Defined Functions) libraries for the server in FreePascal if you want to extend the already extensive function list of Firebird.<br />
<br />
The database requires very little manual DBA work once it is set up, making it ideal for small business use or embedded use. It can grow to terabyte scale given proper tuning, although PostgreSQL may be a better choice for such large environments.<br />
<br />
Firebird offers both embedded (file-based) and client-server database - usable without having to change a single line of code in FPC/Lazarus. If used as an embedded database, it offers a richer SQL support than [[SQLite]] as well as seamless migration to a client-server database, although SQLite is a quite capable embedded database itself.<br />
<br />
The latest stable version, Firebird 2.5, runs on Windows (32- and 64-bit), various Linux versions (32- and 64-bit), Solaris (Sparc and Intel), HP-UX (PA-Risc) and OSX.<br />
<br />
It is being ported to Android at the moment; it is not available on Windows CE/Windows Mobile.<br />
<br />
Support for Firebird in FPC's SQLDB is quite good, comparable to the level of [[postgres|PostgreSQL]] support.<br />
<br />
== Documentation ==<br />
Official documentation is included in FPC 2.6.2+: [http://www.freepascal.org/docs-html/fcl/ibconnection/index.html SQLDB documentation for IBConnection]<br />
<br />
==Client/server and embedded installation==<br />
Firebird can run in client/server and embedded mode.<br />
<br />
''Client/Server'' means that you have a physical Firebird server somewhere: either on your local machine or another machine reachable over your network. Connections to the server go through TCP/IP; when specifying the connection, the hostname contains a name or IP address. The Firebird DLL you need for this is ''fbclient.dll'' (''along with its support files'').<br />
<br />
''Embedded Firebird'' means that your application loads the Firebird DLLs to access a Firebird database on the local machine. When specifying the connection, the hostname is always empty. The Firebird DLL you need for this is ''fbembed.dll'' (''along with its support files'').<br />
See [[Firebird embedded|the wiki page on Firebird embedded]] for more details.<br />
<br />
Note that ''fbembed.dll'' can be used both for client/server and embedded use, so installing only this dll may be a smart thing to do.<br />
<br />
===Windows===<br />
Win64: please see warning [[Windows Programming Tips#FPC 2.6.x/Lazarus warning|here]] on not using certain FPC/Lazarus Win64 versions.<br />
<br />
On Windows: (this applies to all SQLDB database drivers) you must have '''fbclient.dll''' (or '''fbembed.dll''') and its support dlls installed in:<br />
* the project directory '''and''' the executable output directory/the application directory (e.g. lib/something under your project directory)<br />
* or a directory in your PATH (not your system directory)<br />
* If you want to use the system directory, please use the official installer and tick "copy fbclient to system directory"<br />
<br />
As with all (database) DLLs, the bitness of the DLL must match your application: use a 32 bit library for a 32 bit compiled program and a 64 bit library for a 64 bit program.<br />
<br />
===Unix/Linux/OSX===<br />
On Linux/OSX/FreeBSD, the Firebird client library should be installed (e.g. by your package manager; install the regular package and the -dev package), or they should be placed in the library search path.<br />
<br />
FPC searches for the most common library names (e.g. libfbclient.so.2.5, libgds.so, and libfbembed.so.2.5; please check ibase60.inc if your version is different). If you want to, you can explicitly specify the library name. There are 2 ways for this:<br />
* use the [[TSQLDBLibraryLoader]] component from sqldblib (FPC 2.7.1). Works for all SQLDB connector components.<br />
* call <syntaxhighlight>function InitialiseIBase60(Const LibraryName : AnsiString) : integer;</syntaxhighlight> with the correct library name (you may need to use unit ibase60dyn for this).<br />
<br />
== Connection examples ==<br />
Example for client/server:<br />
Hostname: 192.168.1.1<br />
* The database is on the server with IP address 192.168.1.1. <br />
DatabaseName: /interdata/example.fdb <br />
* The name of the database file is "example.fdb" in the /interdata directory of the server (the machine with IP address 192.168.1.1).<br />
Username: SYSDBA<br />
Password: masterkey<br />
<br />
Another example for client/server:<br />
Hostname: dbhost<br />
* The database is on the server with the host name dbhost<br />
DatabaseName: F:\Program Files\firebird\examples\employee.fdb <br />
* The name of the database file is "employee.fdb" in the Program Files\firebird\examples directory on the F: drive of dbhost.<br />
Username: SYSDBA<br />
Password: masterkey<br />
<br />
An embedded example:<br />
Hostname: <empty string><br />
* Leaving the hostname empty selects embedded use.<br />
DatabaseName: test.fdb<br />
* The database file is "test.fdb" in the directory where the application runs (make sure fbembed.dll is in the application executable directory)<br />
Username: SYSDBA<br />
* On embedded, you do have to specify a username...<br />
Password: <empty string><br />
* ... but it doesn't matter what password you give.<br />
<br />
== Troubleshooting client/server access issues ==<br />
Make sure you started the Interbase/Firebird server on the server IP/hostname you specified. You can test connectivity by telnetting to the machine. Firebird usually listens on port 3050:<br />
<syntaxhighlight lang="bash"><br />
telnet 192.168.1.1 3050<br />
</syntaxhighlight><br />
You should see something, maybe just a blank screen, but you can type something. This means you can send data to the Firebird database.<br />
<br />
For further information, please see the Firebird documentation.<br />
<br />
== Monitoring Events ==<br />
FPC/Lazarus comes with a component to monitor events coming from Firebird databases; see [[TFBEventMonitor]].<br />
== Creating objects programmatically ==<br />
While you can use tools such as Flamerobin to create tables etc, you can also create these programmatically/dynamically, which could be handy when you want your programs to update existing database schemas to a new schema.<br />
<br />
You can use e.g. TSQLQuery.ExecSQL to perform this task:<br />
<syntaxhighlight><br />
Query.ExecSQL('CREATE TABLE TEST(ID INTEGER NOT NULL, TESTNAME VARCHAR(800))');<br />
// You need to commit the transaction after DDL before any DML - SELECT, INSERT etc statements.<br />
// Otherwise the SQL won't see the created objects<br />
</syntaxhighlight><br />
<br />
To do this, use the TSQLScript object - see [[TSQLScript]].<br />
<br />
== Database Administration ==<br />
FPC/Lazarus has a component for database adminstration; see [[TFBAdmin]]<br />
<br />
== Common problems and solutions ==<br />
Sometimes using Firebird in Lazarus seems to be tricky. Please find solutions below.<br />
<br />
=== Attempted update of read-only column / COMPUTED BY fields ===<br />
If you have COMPUTED BY fields (server-side calculated fields) in your Firebird table, SQLDB will not pick up that these are read only fields (for performance reasons).<br />
<br />
In this case, auto-generated INSERTSQL,UPDATESQL statements can lead to error messages like "attempted update of read-only column".<br />
The solution is to manually specify that the field in question may not be updated after setting the TSQLQuery's SQL property, something like:<br />
<syntaxhighlight><br />
// Disable updating this field or searching for changed values as user cannot change it<br />
sqlquery1.fieldbyname('full_name').ProviderFlags:=[];<br />
</syntaxhighlight><br />
<br />
=== Bigint: lost precision ===<br />
If you use the bigint datatype (64 bit signed integer) in Firebird, please use .AsLargeInt instead of .AsInteger for parameters:<br />
<syntaxhighlight><br />
// Assuming ID is bigint here<br />
sqlquery1.sql.text := 'insert into ADDRESS (ID) values (:ID)';<br />
// Use this:<br />
sqlquery1.params.parambyname('ID').aslargeint := <some qword or 64 bit integer variable>;<br />
// Do not use this:<br />
//sqlquery1.params.parambyname('ID').asinteger := <some qword or 64 bit integer variable>;<br />
...<br />
</syntaxhighlight><br />
... otherwise you might get errors like duplicate PK (primary key) - if using the bigint as a primary key.<br />
<br />
=== Boolean data types ===<br />
Firebird versions below 3.0 do not support boolean data types.<br />
<br />
At least on FPC trunk (2.7.1) this datatype can be emulated:<br />
<br />
Use a DOMAIN that uses a SMALLINT type (other integer types may work as well - please test and adjust text): <syntaxhighlight lang="SQL">CREATE DOMAIN "BOOLEAN"<br />
AS SMALLINT<br />
CHECK (VALUE IS NULL OR VALUE IN (-1,0,1))<br />
/* -1 used for compatibility with FPC SQLDB; 1 is used by many other data access layers */<br />
;<br />
</syntaxhighlight><br />
<br />
<br />
Let your field/column use this domain type e.g. <syntaxhighlight lang="SQL"><br />
CREATE TABLE MYTABLE<br />
(<br />
...<br />
MYBOOLEANCOLUMN "BOOLEAN",<br />
);<br />
</syntaxhighlight><br />
* Now you can use .AsBoolean for assigning field values etc<br />
<br />
'''To do: verify this works with Lazarus grids etc as well.'''<br />
<br />
=== INSERT INTO...RETURNING problems/Cursor is not open ====<br />
If you try to select SQL (e.g. Query.Open) with SQL like this:<br />
<syntaxhighlight lang="SQL"><br />
INSERT INTO PEOPLE (NICKNAME) VALUES ('Superman') RETURNING ID<br />
</syntaxhighlight><br />
and get something like this error:<br />
<syntaxhighlight lang="dos"><br />
Database error: : Fetch :<br />
-Dynamic SQL Error<br />
-SQL error code = -504<br />
-Invalid cursor reference<br />
-Cursor is not open(error code: 335544569)<br />
</syntaxhighlight><br />
while running FPC 2.6.0 (which is supplied with Lazarus 1.0) or lower, then you probably ran into an FPC SQLDB parser bug.<br />
<br />
SQLDB thinks the statement you're running is a normal INSERT statement, which doesn't return data. Obviously, it should return data.<br />
Newer FPC code has fixes for this.<br />
<br />
If you're using generators/sequences for your primary keys (like many do), a workaround is to first get the next sequence number:<br />
<syntaxhighlight lang="SQL"><br />
SELECT NEXT VALUE FOR GEN_PEOPLEID FROM RDB$DATABASE /* If your generator name is GEN_PEOPLEID */ <br />
</syntaxhighlight><br />
then use that to do a regular INSERT.<br />
see [http://www.firebirdfaq.org/faq111/|Firebird FAQ entry]<br />
<br />
=== Locate does not seem to work ===<br />
Source: {{MantisLink|#21988}}<br />
<br />
When running locate on UTF8 (or presumably other multibyte character sets) CHAR fields, locate may not find your record.<br />
<br />
The problem is related mostly to UTF8 charset used and how Firebird reports column length. In case of UTF8 Firebird reports the maximum column length (in bytes) as 4*"character length". So if you have a column defined as char(8), Firebird reports 4*8=32.<br />
<br />
Values are right-padded to this length 32.<br />
When locating say '57200001' there is no match because the field actually stores '57200001 ........................' (with trailing spaces represented by dots here).<br />
<br />
Workaround: rewrite your select query:<br />
<syntaxhighlight><br />
SELECT substring(THEFIELD from 1 for 8) AS THEFIELD ...<br />
</syntaxhighlight><br />
or<br />
<syntaxhighlight><br />
SELECT cast(THEFIELD as varchar(8)) as THEFIELD ...<br />
</syntaxhighlight><br />
or use VARCHAR fields.<br />
<br />
Note: this problem may occur for other databases as well depending on their reporting of field length.<br />
<br />
== Advanced transactions ==<br />
Sources for this information/further reading: <br />
* [http://www.ibphoenix.com/resources/documents/how_to/doc_400 Understanding Firebird Transactions] very detailed article<br />
* [https://ib-aid.com/en/transactions-in-firebird-acid-isolation-levels-deadlocks-and-update-conflicts-resolution/ Transactions in Firebird] another very detailed article<br />
* Interbase 6 API Guide (valid for Firebird+Interbase), page 63 and further <br />
* [http://conferences.embarcadero.com/article/32280 Overview of transaction settings in Interbase]<br />
* [http://tech.groups.yahoo.com/group/firebird-support/message/58653 Detailed explanation of parameters in Firebird]<br />
* [http://fhasovic.blogspot.com/2005/02/transaction-isolation-levels-in.html Overview of settings in Firebird]<br />
* [http://www.devrace.com/en/fibplus/articles/3292.php Transaction isolation levels in FIBPlus]<br />
* README.set_transaction.txt in Firebird 2.5 documentation folder.<br />
<br />
=== Transaction isolation levels ===<br />
If you want to, you can change the transaction isolation levels by adding a line in the transaction's Parameters property:<br />
* <code>isc_tpb_read_committed</code>: you see all changes committed by other transactions<br />
* <code>isc_tpb_concurrency</code>: also called Snapshot: you see database as it was when the transaction started. Has more overhead than isc_tpb_read_committed. Better than ANSI Serializable because it has no phantom reads.<br />
* <code>isc_tpb_consistency</code>: also called Table Stability: stable, serializable view of data, but locks tables. Unlikely you will need this<br />
<br />
Example:<br />
<syntaxhighlight><br />
SQLTransaction1.Params.text:='isc_tpb_read_committed';<br />
</syntaxhighlight><br />
<br />
You can also add additional parameters that have an effect on the transaction (taken from the ibconnection.pp source file and [http://conferences.embarcadero.com/article/32280]):<br />
<br />
=== Access mode ===<br />
This allow reads only or read/write<br />
* <code>isc_tpb_read</code>: read permission<br />
* <code>isc_tpb_write</code>: read+write permission<br />
<br />
=== Lock resolution ===<br />
* <code>isc_tpb_nowait</code>: if another transaction is editing the record then don't wait<br />
* <code>isc_tpb_wait</code>: if another transaction is editing the record then wait for it to finish. Can mitigate "live locks" in heavy contention ([http://tech.groups.yahoo.com/group/firebird-support/message/58653]). See below for timeout value.<br />
<br />
=== Table reservation ===<br />
Deals with locking entire tables.<br />
* <code>isc_tpb_shared</code>: first specify this, then either lock_read or lock_write for one or more tables. Shared read or write mode for tables.<br />
* <code>isc_tpb_protected</code>: first specify this, then either lock_read or lock_write for one or more tables. Lock on tables; can allow deadlock-free operation at the cost of delayed transactions<br />
* <code>isc_tpb_lock_read</code>: Set a read lock. Specify which table to lock, e.g. isc_tpb_lock_read=CUSTOMERS<br />
* <code>isc_tpb_lock_write</code>: Set a read/write lock. Specify which table to lock, e.g. isc_tpb_lock_read=CUSTOMERS<br />
Combinations:<br />
* Shared, lock_write: write transactions with concurrency or read committed isolation<br />
can update the table. All transactions can read the table<br />
* Shared, lock_read: any transaction can read or update<br />
* Protected, lock_write: Other transactions cannot update the table. Only concurrency and<br />
read committed transactions can read the table<br />
* Protected, lock_read: Other transactions cannot update the table. Any transaction can<br />
read the table<br />
<br />
=== Record versions ===<br />
This setting is apparently only relevant for isc_tpb_read_committed isolation mode for records being modified by other transactions:<br />
* <code>isc_tpb_no_rec_version</code>: only newest record version is read. Can be useful for batch/bulk insert operations together with isc_tpb_read_committed)<br />
* <code>isc_tpb_rec_version</code>: the latest committed version is read, even when the other transaction has other uncommitted changes '''note: verify this'''. More overhead than isc_tpb_no_rec_version<br />
<br />
=== Various options ===<br />
For completeness, some more options appearing in the Firebird/Interbase FPC code.<br />
You will likely only ever use isc_tpb_no_auto_undo to speed up batch inserts/edits.<br />
* isc_tpb_exclusive (apparently translates to protected in Firebird, see [http://tech.groups.yahoo.com/group/firebird-support/message/58653])<br />
* isc_tpb_verb_time (Related to deferred constraints, which could execute at verb time or commit time. Firebird: not implemented, always use verb time)<br />
* isc_tpb_commit_time (Related to deferred constraints, which could execute at verb time or commit time. Firebird: not implemented, always use verb time)<br />
* isc_tpb_ignore_limbo (ignores the records created by transactions in limbo. Limbo transactions are failing two-phase commits in multi-database transactions. Unlikely that you will need this feature)<br />
* isc_tpb_autocommit (autocommit this transaction: every statement is a separate transaction. Probably specifically for JayBird JDBC driver.)<br />
* isc_tpb_restart_requests (apparently looks for requests in the connection which had been active in another transaction, unwinds them, and restarts them under the new transaction.)<br />
* isc_tpb_no_auto_undo (disable transaction-level undo log, handy for getting max throughput when performing a batch update. Has no effect when only reading data.)<br />
* isc_tpb_lock_timeout (specify number of seconds to wait for lock release, if you use isc_tpb_wait. If this value is reached without lock release, an error is reported.)<br />
<br />
=== Firebird and ISO transactions ===<br />
Firebird transaction do not map 1 to 1 to ISO/ANSI transaction levels. An approximation is:<br />
* ISO Read Committed=READ COMMITTED+RECORD_VERSION<br />
* ISO Read Committed=READ COMMITTED+NO RECORD_VERSION<br />
* ISO Repeatable Read=SNAPSHOT (also known as CONCURRENCY)<br />
* ISO Serializable=SNAPSHOT TABLE STABILITY (also known as CONSISTENCY)<br />
<br />
=== Common combinations ===<br />
Default is (probably, will have to check) read committed.<br />
<br />
==== Batch/bulk insert ====<br />
isc_tpb_read_committed and isc_tpb_no_rec_version could be a good combination: it allows other transactions to function while the batch is going on.<br />
<br />
==== Read only transaction ====<br />
If you want to only have read access to the database, you can do so by setting these transaction parameters:<br />
* <code>isc_tpb_read</code><br />
* <code>isc_tpb_read_committed</code><br />
* <code>isc_tpb_rec_version</code><br />
* <code>nowait</code><br />
This combination will not block garbage collection, which is a good thing.<br />
Source: [http://tech.groups.yahoo.com/group/firebird-support/message/118748]<br />
<br />
Note: the [http://www.firebirdfaq.org/faq164/ Firebird FAQ] indicates you will need write access to the database file even if you only read from it, unless you set the database read-only flag (e.g. using gfix).<br />
<br />
== Links and more information ==<br />
<br />
The list below shows links to more information on Firebird and related tools.<br />
* [[Firebird_embedded|Using Firebird embedded with FPC/Lazarus]]<br />
<br />
=== Lazarus Firebird samples ===<br />
* Sample Lazarus/Firebird application source code [http://lazarus.freepascal.org/index.php/topic,13940.msg73617.html#msg73617]<br />
* Firebird/SQLDB tutorials: <br />
** [[SQLdb Tutorial0]]<br />
** [[SQLdb Tutorial1]]<br />
** [[SQLdb Tutorial2]]<br />
** [[SQLdb Tutorial3]]<br />
** [[SQLdb Tutorial4]]<br />
<br />
*PP4S tutorials; cover Firebird installation on Windows as well:<br />
** [http://www.pp4s.co.uk/main/tu-db-plan.html Planning a Database]<br />
** [http://www.pp4s.co.uk/main/tu-db-installingfirebird01.html Installing Firebird]<br />
** [http://www.pp4s.co.uk/main/tu-db-firebird-create.html Creating a Firebird database]<br />
** [http://www.pp4s.co.uk/main/tu-db-firebird-demo1.html Editing a Firebird database]<br />
** [http://www.pp4s.co.uk/main/tu-db-firebird-demo2.html Searching a Firebird database]<br />
** [http://www.pp4s.co.uk/main/tu-db-firebird-demo3.html Creating and printing a report]<br />
** [http://www.pp4s.co.uk/main/tu-db-firebird-demo4.html Creating and using stored procedures]<br />
<br />
=== Tools ===<br />
* FlameRobin [http://www.flamerobin.org/ Flamerobin site] Open source GUI tool to manage Firebird, available for Linux, Windows and Mac OSX. Highly recommended.<br />
* Turbobird [https://github.com/motaz/turbobird Turbobird site] Open source GUI tool to manage Firebird. Written with Lazarus using TIBConnection<br />
* ibconsole : Tool to manage Firebird an Interbase Databases with a GUI, available for Windows and Linux<br />
* Lazarus Data Desktop - included in the Lazarus repository (found in the 'tools/lazdatadesktop/' directory)<br />
* [http://lazsqlx.wordpress.com/ LazSQLX] Multi-database open source database management tool. Written with Lazarus using both SQLDB and Zeos components. Includes support for Firebird. <br />
* tiSQLEditor - included in the "Support Apps" directory of the [[tiOPF]] repository. It is a tool used to write SQL with some code completion support, can run scripts, execute upgrade scripts for applications from one build to a later build, has various handy copy and paste functions for Object Pascal applications, has many features that are useful to tiOPF (creates Object Pascal code templates from query results, for tiOPF's visitor classes), export query results to CSV etc.<br />
<br />
=== Firebird ===<br />
* The Firebird RDBMS [http://firebirdsql.org/ Firebird site]. This site also contains a lot of documentation on Firebird.<br />
* Firebird FAQ [http://firebirdfaq.org/]. Handy site that shows e.g. differences with other RDBMS.<br />
<br/></div>Zoranhttps://wiki.freepascal.org/index.php?title=DateTimeCtrls_Package&diff=123608DateTimeCtrls Package2019-03-07T11:32:08Z<p>Zoran: /* Options: TDateTimePickerOptions */</p>
<hr />
<div>== About ==<br />
The DateTimeCtrls package contains two controls:<br />
<br />
[[Image:TDateTimePicker.png]] [[#TDateTimePicker|TDateTimePicker]]<br />
and<br />
[[Image:TDBDateTimePicker.png]][[#TDBDateTimePicker|TDBDateTimePicker]]<br />
<br />
Delphi's VCL has a control named [http://docwiki.embarcadero.com/Libraries/en/Vcl.ComCtrls.TDateTimePicker TDateTimePicker], which I find very useful for editing dates. LCL, however, does not have this control.<br />
<br />
Therefore, I tried to create a cross-platform Lazarus control which would resemble VCL's TDateTimePicker as much as possible.<br />
<br />
The TDateTimePicker does not use [http://msdn.microsoft.com/en-us/library/system.windows.forms.datetimepicker.aspx native Win control]. It descends from LCL-s TCustomControl to be cross-platform. It has been tested on Windows with Win32/64 and qt widgetsets and on Linux with gtk2 and qt widgetsets.<br />
<br />
Note that the TDateTimePicker control does not descend from TEdit, so it does not have unnecessary caret. The VCL's control doesn't have caret either.<br />
<br />
== Author ==<br />
:[[User:Zoran| Zoran Vučenović]]<br />
<br />
== License ==<br />
Modified LGPL (same as the FPC RTL and the Lazarus LCL).<br />
<br />
== Getting the package ==<br />
This package is part of Lazarus distribution (since Lazarus 1.4). You can find it in <''your_lazarus_folder''>/components/datetimectrls.<br />
<br />
The components are installed on Component palette by default (if you build the IDE yourself, they are installed with "make bigide"). The components TDateTimePicker and TDBDateTimePicker are located in [[Common_Controls_tab|Common Controls]] and [[Data_Controls_tab|Data Controls]] palette pages, respectively.<br />
<br />
Since Lazarus 1.8 the designtime code is separated to another package DateTimeCtrlsDsgn. Normally, you should not care about this, but only if you are installing the controls in the IDE manually, you should know that it is actually the designtime package DateTimeCtrlsDsgn which is installed in the IDE.<br />
<br />
This separation of designtime code to different package is done to prevent the code which is needed only in design time to be linked in final executable. [[Lazarus_1.8.0_release_notes#DateTimeCtrls|See the 1.8 release notes.]]<br />
<br />
== TDateTimePicker [[Image:TDateTimePicker.png]] ==<br />
<br />
=== Properties ===<br />
<br />
I'll explain some properties of TDateTimePicker control:<br />
<br />
==== DateTime: TDateTime (public) ====<br />
<br />
:The DateTime value displayed on the control. This property is not published in object inspector, but its value is actually the same as Date and Time properties composed in one value. This property is provided to allow setting or reading of both date and time value at once in program code. In design time, Date and Time can be set in object inspector. [[#DateTimePicker Editor|There is also component editor]] which provides easy way of setting this property in design time.<br />
<br />
==== Date: TDate ====<br />
<br />
:The date displayed on the control which the user can edit.<br />
<br />
==== Time: TTime ====<br />
<br />
:The time displayed on the control which the user can edit.<br />
<br />
==== MinDate: TDate ====<br />
<br />
:The minimal date user can enter.<br />
<br />
==== MaxDate: TDate ====<br />
<br />
:The maximal date user can enter.<br />
<br />
==== NullInputAllowed: Boolean ====<br />
<br />
:When True, the user can set the date to NullDate constant by pressing N key.<br />
<br />
==== CenturyFrom: Word ====<br />
<br />
:When user enters the year in two-digit format, then the CenturyFrom property is used to determine which century the year belongs to. The default is 1941, which means that when two digit year is entered, it falls in interval 1941 – 2040. Note that [[#MinDate: TDate|MinDate]] and [[#MaxDate: TDate|MaxDate]] properties can also have influence on the decision – for example, if the CenturyFrom is set to 1941 and MaxDate to 31. 12. 2010, if user enters year 23, it will be set to 1923, because it can’t be 2033, due to MaxDate limit.<br />
<br />
==== Kind: TDateTimeKind ====<br />
<br />
:'''type''' TDateTimeKind = (dtkDate, dtkTime, dtkDateTime);<br />
<br />
:The control displays only date, only time or both.<br />
:[[Image:DateControls03.PNG]]<br />
<br />
==== DateMode: TDTDateMode ====<br />
<br />
:'''type''' TDTDateMode = (dmComboBox, dmUpDown, dmNone)<br />
<br />
:When DateMode is set to dmComboBox, there is a button on the right side of the control. When user clicks the button, [http://lazarus-ccr.sourceforge.net/docs/lcl/calendar/tcalendar.html the calendar control] is shown, allowing the user to pick the date. When set to dmUpDown, then UpDown buttons are shown.<br />
<br />
:In my opinion the UpDown buttons aren't really useful in this control, they are provided for compatibility with Delphi's TDateTimePicker. Up and down keys can always serve for same purpose, so can mouse wheel.<br />
<br />
:In the picture the first control's DateMode is set to dmComboBox and the second control's to dmUpDown:<br />
:[[Image:DateControls01.PNG]]<br />
<br />
==== ShowCheckBox: Boolean ====<br />
<br />
:When set, there is a check box on the left side of the control.<br />
:By default, when unchecked, the display appears grayed and user interaction with the date is not possible. (The control is still enabled, though, only in sense that the check box remains enabled). This behaviour may be changed if dtpoEnabledIfUnchecked is added to [[#Options:_TDateTimePickerOptions|Options property]] -- then this check box is shown, but does nothing by itself (doesn't disable the control) and the programmer can decide how it will be used (by using OnCheckBoxChange event).<br />
:[[Image:DateControls02.PNG]]<br />
<br />
==== Checked: Boolean ====<br />
<br />
:If [[#ShowCheckBox: Boolean|ShowCheckBox]] is set to True, this property determines whether the check box is checked or not. If ShowCheckBox is False, this property has no purpose and is automatically set to True.<br />
<br />
==== DateDisplayOrder: TDateDisplayOrder ====<br />
<br />
:'''type''' TDateDisplayOrder = (ddoDMY, ddoMDY, ddoYMD, ddoTryDefault);<br />
<br />
:Defines the order for displaying day, month and year part of the date. When ddoTryDefault is set, then the controls tries to determine the order from [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/shortdateformat.html ShortDateFormat global variable].<br />
:This is similar to DateEdit's [http://lazarus-ccr.sourceforge.net/docs/lcl/editbtn/tdateedit.dateorder.html DateOrder] property.<br />
<br />
==== DateSeparator: String ====<br />
<br />
:Defines the string used to separate date, month and year date parts. Setting this property automatically sets the [[#UseDefaultSeparators: Boolean|UseDefaultSeparators]] property to False. To ensure that date and time separators are set to user's system defaults, set UseDefaultSeparators property to True.<br />
<br />
==== TimeSeparator: String ====<br />
<br />
:Defines the string used to separate hour, minute, second and millisecond time parts. Setting this property automatically sets the [[#UseDefaultSeparators: Boolean|UseDefaultSeparators]] property to False. To ensure that date and time separators are set to user's system defaults, set UseDefaultSeparators property to True.<br />
<br />
==== UseDefaultSeparators: Boolean ====<br />
<br />
:When this property is set to True, then the [[#DateSeparator: String|DateSeparator]] and [[#TimeSeparator: String|TimeSeparator]] properties will be set to [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/dateseparator.html DateSeparator] and [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/timeseparator.html TimeSeparator] global variables, which are set to user system defaults when application initializes.<br />
<br />
==== TrailingSeparator: Boolean ====<br />
<br />
:When set to True, then the [[#DateSeparator: String|DateSeparator]] is shown once more, after the last date part. This property exists because in some languages the correct format is 31. 1. 2010. including the last point, after the year.<br />
<br />
==== LeadingZeros: Boolean ====<br />
<br />
:Determines whether the date parts are displayed with or without leading zeros (this actually affects day, month and hour parts of date and time display).<br />
<br />
==== TimeDisplay: TTimeDisplay ====<br />
<br />
:'''type''' TTimeDisplay = (tdHM, tdHMS, tdHMSMs);<br />
<br />
:If Kind is dtkTime or dtkDateTime, then TimeDisplay value of tdHM means that only hours and minutes are displayed, tdHMS adds displaying of seconds and value of tdHMSMs means that milliseconds are displayed too.<br />
<br />
==== TimeFormat: TTimeFormat ====<br />
<br />
:'''type''' TTimeFormat = (tf12, tf24);<br />
<br />
:The value of tf12 sets the display of time to 12 hours format, with AM/PM string and tf24 sets to 24 hours format.<br />
<br />
==== TextForNullDate: String ====<br />
<br />
:Text which appears when the null date is set and control does not have focus. When control is focused, the text changes to defined format, but displaying zeros, which is appropriate to user input. User can set the date to NullDate by pressing N key, provided [[#NullInputAllowed: Boolean|NullInputAllowed]] property is True.<br />
:When TextForNullDate is set to empty string, zeros/nines format is displayed even when control does not have focus. If you want empty display, this can be acchieved by setting TextForNullDate to one or more space characters.<br />
<br />
==== AutoAdvance: Boolean ====<br />
<br />
:When true, then when user is entering valid text, the selection automatically advances to next part of date/time. The default is True, because it makes user interaction easier and this behaviour is what a user should expect.<br />
<br />
==== Cascade: Boolean ====<br />
<br />
:When true, then when user is increasing or decreasing one date/time part (using up-down keys or mouse wheel), it can increase or decrease by one another date/time part. For example, when date is 31.08.2013. and user increases the day, the day becomes 1 and month increases by one and becomes 9, so the date becomes 01.09.2013. If Cascade were set to False, the month would not change and the date would become 01.08.2013.<br />
<br />
==== AutoButtonSize: Boolean ====<br />
<br />
:When true, the width of the arrow button (or up-down control, if it is shown instead) is automatically adjusted proportionally to the height.<br />
<br />
==== HideDateTimeParts: TDateTimeParts ====<br />
<br />
:'''type'''<br />
<br />
::TDateTimePart = (dtpDay, dtpMonth, dtpYear, dtpHour, dtpMinute, dtpSecond, dtpMiliSec, dtpAMPM);<br />
<br />
::TDateTimeParts = set of dtpDay..dtpMiliSec;<br />
<br />
:With HideDateTimeParts property, you can chose which date/time parts will not be shown. Most of the time you do not need to use this property and you can get the format you want by using other properties (see Kind, TimeDisplay). However, if you need more control (for example, you might want to let user edit only days, months and hours), you can additionally hide any date/time parts with this control. Keep in mind that, with this property, you cannot show any date/time part which is hidden by another property (for example, if TimeDisplay is tdHM, the second part is not shown, regardless of this property).<br />
<br />
==== CalendarWrapperClass: TCalendarControlWrapperClass (public) ====<br />
<br />
:When assigned, this property determines the type of the calendar control used for drop-down calendar. When set to nil, which is the default, the value of global variable DefaultCalendarWrapperClass is used. [[#Using some custom calendar control for drop-down|More details here]].<br />
<br />
==== ShowMonthNames: Boolean ====<br />
:When this property is set to True, month names, set in [[#MonthNames: String|MonthNames]] property, will be displayed instead of numbers.<br />
<br />
==== MonthNames: String ====<br />
:When [[#ShowMonthNames: Boolean|ShowMonthNames]] is set to True, this property determines which month names should appear.<br />
:If MonthNames is set to 'Short' or 'Long', then month names are set to [http://www.freepascal.org/docs-html/rtl/sysutils/shortmonthnames.html ShortMonthNames] or [http://www.freepascal.org/docs-html/rtl/sysutils/longmonthnames.html LongMonthNames] respectively.<br />
:To set the month names explicitly, this property should be set to string which starts with a character which will be used to separate months and then twelve names separated by this character. For example ',I,II,III,IV,V,VI,VII,VIII,IX,X,XI,XII' — roman numbers are used for months — the first character is comma, which means that comma is used to separate the months. Another valid example ';jan;feb;mar;apr;maj;jun;jul;avg;sep;okt;nov;dec'.<br />
:So, the separator should be the first character, before the first month name, and then only if there are twelve month names separated by that separator, the format is valid.<br />
:The default value of this property is 'Long'.<br />
<br />
==== Options: TDateTimePickerOptions ====<br />
<br />
* dtpoDoChangeOnSetDateTime: the OnChange handler will be called also when DateTime is programatically changed.<br />
* dtpoEnabledIfUnchecked: enable the date time picker if the checkbox is unchecked.<br />
* dtpoAutoCheck: auto-check an unchecked checkbox when DateTime is changed (makes sense only if dtpoEnabledIfUnchecked is set).<br />
* dtpoFlatButton: use flat button for calender picker.<br />
'''added in trunk 2.1:'''<br />
* dtpoResetSelection: when the control receives focus, the selection is always in the first part (the control does not remember which part was selected).<br />
<br />
=== DateTimePicker Editor ===<br />
<br />
[[Image:datetimepickereditor.png]]<br />
<br />
:DateTimePicker Editor is a dialog which provides easy way to edit Date, Time, MinDate and MaxDate properties in design time. It is invoked when DateTimePicker control is double-clicked in form designer. It is also shown when the ellipsis (…) button shown in Date, Time, MinDate and MaxDate properties in Object inspector gets clicked.<br />
<br />
<br />
=== User interaction in runtime ===<br />
<br />
:The goal was to provide same user interaction as native DateTimePicker from Windows. The keyboard input is restricted to valid date/time values only. Up and down buttons can be used too. The contros behaves just like VCL's control.<br />
<br />
:After this behaviour is achieved it is further improved with mouse wheel interaction. Date and time parts can be edited using mouse wheel, which is very fast and comes handy.<br />
<br />
<br />
=== Using some custom calendar control for drop-down ===<br />
<br />
Instead of LCL's TCalendar, some other calendar control can be used for drop-down. This calendar control has to provide a way to determine if the coordinates (within the calendar control) are on the date or not [[#AreCoordinatesOnDate|(see AreCoordinatesOnDate function)]]. DateTimePicker needs it to decide if it should close the drop-down calendar and take its date when the calendar gets clicked (see AreCoordinatesOnDate function). The calendar only needs to be derived from TControl. The mouse interaction with calendar will work and, if it is also TWinControl's descendent, the keyboard interaction will also work.<br />
<br />
Now, let's see how to use some calendar control for drop-down control. First, we need to define the "wrapper" class, then we will have to tell the DateTimePicker control to use this wrapper instead of default one.<br />
<br />
==== Definning the wrapper class ====<br />
<br />
:In unit calendarcontrolwrapper.pas, there is an abstract class TCalendarControlWrapper. We need to derive a new class from this abstract class. There are four abstract methods which new class has to override - GetCalendarControlClass, SetDate, GetDate and AreCoordinatesOnDate.<br />
:This should be pretty simple, please see the file lclcalendarwrapper.pas, where TLCLCalendarWrapper (the default wrapper) is defined.<br />
<br />
===== GetCalendarControlClass =====<br />
<br />
:<syntaxhighlight><br />
class function GetCalendarControlClass: TControlClass; virtual abstract;<br />
</syntaxhighlight><br />
:This class function should return the class of the wrapped calendar control.<br />
<br />
===== SetDate =====<br />
<br />
:<syntaxhighlight><br />
procedure SetDate(Date: TDate); virtual abstract;<br />
</syntaxhighlight><br />
:Should be overriden to set the date in the calendar control.<br />
<br />
===== GetDate =====<br />
<br />
:<syntaxhighlight><br />
function GetDate: TDate; virtual abstract;<br />
</syntaxhighlight><br />
:Should be overriden to get the date from the calendar control.<br />
<br />
===== AreCoordinatesOnDate =====<br />
<br />
:<syntaxhighlight><br />
function AreCoordinatesOnDate(X, Y: Integer): Boolean; virtual abstract; <br />
</syntaxhighlight><br />
:This function should return True if coordinates (X, Y) are on the date in the calendar control (DateTimePicker calls this function when the calendar is clicked, to determine whether the drop-down calendar should return the date or not). The calendar control must at least provide a way to determine whether the coordinates are on the date (when this control gets clicked, we must decide if the date has just been chosen — then we should respond by closing the drop-down form and setting the date from calendar to DateTimePicker — for example in LCL's TCalendar we will respond when the calendar is clicked on date, but not when the user clicks in title area changing months or years, then we let the user keep browsing the calendar).<br />
<br />
==== Telling to the DateTimePicker to use new wrapper ====<br />
<br />
:Now, when we have the wrapper class defined, the easiest way to make all DateTimePickers use the new control is setting global variable DefaultCalendarWrapperClass (declared in unit datetimepicker.pas) and all DateTimePickers in the project will use the new control.<br />
<br />
:There is public property CalendarWrapperClass in DateTimePicker. We can set it and DateTimePicker will use it.<br />
<br />
:This pseudo code shows how a DateTimePicker decides which calendar control to use:<br />
<br />
<syntaxhighlight lang="text"><br />
if property DateTimePicker.CalendarWrapperClass is not nil<br />
the property is used<br />
else if global variable DefaultCalendarWrapperClass is not nil<br />
the global variable is used<br />
else<br />
LCL's TCalendar (implemented through TLCLCalendarWrapper class) is used<br />
</syntaxhighlight><br />
<br />
== TDBDateTimePicker [[Image:TDBDateTimePicker.png]] ==<br />
<br />
:TDBDateTimePicker is a data-aware version of TDateTimePicker, with nice way of handling null database values.<br />
<br />
=== Handling of null values ===<br />
<br />
==== Displaying null values ====<br />
<br />
:When the underlying DB field has null value, then:<br />
<br />
::If the control is not focused, then it displays the text defined in [[#TextForNullDate: String|TextForNullDate]] property. The default is "NULL".<br />
<br />
::When the control gets focus, the text changes to defined format, but displaying zeros for date parts and nines for time parts (for example "00/00/0000 99:99:99"), which is appropriate to user input.<br />
<br />
==== Setting the field value to null ====<br />
<br />
::If [[#NullInputAllowed: Boolean|NullInputAllowed]] property is True, the user can set the date and time to null, by pressing N key.<br />
<br />
[[Category:Components]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=Lazarus_2.0.0_release_notes&diff=123253Lazarus 2.0.0 release notes2019-02-15T15:20:01Z<p>Zoran: /* TCustomGrid: The var parameter ABitmap in event OnUserCheckBoxBitmap used to be preassigned with default bitmap, but now it is nil. */</p>
<hr />
<div>'''Lazarus 2.0.0 was released on 2019-02-05''' [[http://forum.lazarus.freepascal.org/index.php/topic,44161.0.html release announcement]]<br />
<br />
= LCL Interfaces Changes =<br />
<br />
= LCL Changes =<br />
<br />
=== TScrollingWinControl ([[TForm]], [[TScrollBox]], [[TFrame]]) ===<br />
* ScreenToClient and ClientToScreen are now calculated without scrollbar offset. Done for Delphi compatibility.<br />
<br />
=== Support for mouse wheel horz scrolling ===<br />
Added TControl events for horizontal mouse wheel moving (special mice with horz scroll buttons for PC, and modern mice for Mac). OnMouseWheelHorz, OnMouseWheelLeft, OnMouseWheelRight. Events are used in some non native LCL components (TreeView, UpDown, SpinEdit). Native components should handle horz scrolling already.<br />
<br />
=== Compiler defines to exclude some graphics support ===<br />
Added flags to exclude support for some graphics formats to create smaller applications:<br />
* -dDisableLCLGIF<br />
* -dDisableLCLJPEG<br />
* -dDisableLCLPNM<br />
* -dDisableLCLTIFF<br />
<br />
=== TCustomImageList / TImageList ===<br />
* the image list now supports multiple resolutions of one image. See [[TImageList|Multiple-resolution TImageList in Lazarus 1.9 and newer]] for more details. As a result all LCL controls support High-DPI glyphs on Windows+Linux and Retina on Mac without any additional code.<br />
* every LCL control that supports ImageList has now a new ImagesWidth property to decide what custom width at 96 PPI (100% scale) is to be used. Example: TToolBar.Images/.ImagesWidth, TListView.LargeImages/.LargeImagesWidth, .StateImages/.StateImagesWidth.<br />
* set the TCustomImageList.Scaled=True property to let the image list automatically pick up the right resolution for your control.<br />
<br />
=== TSpeedButton, TBitBtn ===<br />
* New properties Images, ImageIndex and ImageWidth. With them full ImageList support was added.<br />
** no need to save the same Glyph in LFM all over the application<br />
** automatic high-DPI image handling<br />
<br />
=== TWinControl.DoubleBuffered, .ParentDoubleBuffered and TApplication.DoubleBuffered ===<br />
* Note: DoubleBuffered is a LCLWin32-only feature<br />
* Old behavior: DoubleBuffered wasn't properly implemented and it was forced True<br />
* New behavior:<br />
** A Delphi-compatible DoubleBuffered/ParentDoubleBuffered concept was created (it is equal to the Font/ParentFont concept).<br />
** The LCL has the TApplication.DoubleBuffered extension over Delphi that allows you to set form's default DoubleBuffered value globally for the whole application (set Application.DoubleBuffered before creating the first form). The value is then applied to all controls on the form with ParentDoubleBuffered:=True.<br />
** DoubleBuffered is True by default unless in remote session (this is different to Delphi where DoubleBuffered is False by default).<br />
** If you need one specific control to be DoubleBuffered:=False even for Application.DoubleBuffered:=adbDefault, make sure you set control.DoubleBuffered:=False and .ParentDoubleBuffered:=False.<br />
<br />
=== TListView ===<br />
<br />
Added CustomSort method like in Delphi.<br />
<br />
=== TTreeView ===<br />
<br />
* Implemented HotTrack property (show blue underline for item under cursor).<br />
* Implemented auto-scrolling up/down during drag-drop (when mouse reaches the edge of treeview).<br />
<br />
=== FilterOptions in filter controls ===<br />
<br />
Base class for filter controls TCustomControlFilterEdit has new property FilterOptions, which is set of such flags:<br />
<br />
TFilterStringOption = (fsoCaseSensitive, fsoMatchOnlyAtStart);<br />
TFilterStringOptions = set of TFilterStringOption;<br />
<br />
Two flags are implemented for 3 filter controls in LazControls: TListFilterEdit, TListViewFilterEdit, TTreeFilterEdit.<br />
<br />
=== Advanced menus Assign ===<br />
<br />
* Supported TMenuItem.Assign(TMenuItem): this copies all props of menuitem.<br />
* Supported TMenu.Assign(TMenu): this copies all items with all nested subitems from one menu to another (TMainMenu, TPopupMenu).<br />
<br />
=== TPageSetupDialog ===<br />
<br />
* TPageSetupDialog: added Margin* and Units properties.<br />
* Big rework of Unix TPageSetupDialog, it is now complete like it's on Windows (on Windows it was native, on Unix - emulation).<br />
<br />
= IDE Changes =<br />
<br />
* several '''High-DPI IDE improvements''' and retina support on Cocoa<br />
* '''Delphi Attributes''': Find declaration, parameter hints, $modeswitch prefixedattributes.<br />
* The IDE parses the custom compiler options for the '''fpc switch -FN<namespaces>''', to define the default namespaces, so you can omit the namespaces in the uses sections.<br />
* [[pas2js]] support:<br />
** New IDE package '''[[lazarus_pas2js_integration|pas2jsdsgn]]''' (not installed by default):<br />
*** You can create a browser or nodejs webapplication<br />
*** on '''Run'' starts the webapp in your webbrowser<br />
** pas2js settings are automatically fetched, same as fpc settings<br />
** quickfixes work with pas2js messages<br />
* added quickfix for fpc message "inherited method is hidden": add modifier ''override'', ''overload'', or ''reintroduce'' shortcuts.<br />
* added designer menu item to hide icons for components like TOpenDialog. Option: '''Show non visual components'''<br />
* extended filter for the identifier completion window - include identifiers containing prefix. (Settings: IDE Options -> Codetools -> Identifier Completion -> Sorting -> Include identifiers containing prefix.) See https://bugs.freepascal.org/view.php?id=32974.<br />
* the identifier completion window includes also text (words) from the currently opened units. It can be disabled or set-up in IDE Options -> Codetools -> Identifier Completion -> Include words<br />
* the Publish Project/Package feature and GUI window was revamped. Now all project/package member files are included automatically. More files can be added using a filter. The directory structure is maintained even if some files are in directories "above" the main directory.<br />
* the "New Component" window and user interface is improved. It also now supports 3 icons for different resolutions.<br />
* the Run -> Compile many Modes... feature now remembers its previously selected modes.<br />
* the Search -> Procedure List... window now remembers the state of its filter buttons.<br />
<br />
== Editor ==<br />
<br />
* Added new '''"smart move cursor"''' moving commands to editor mappings. They introduce word jumping positions both at word starts and ends. They are useful as alternative Ctrl+Left/+Right/+Shift+Left/+Shift+Right key <br />
* Added Goto/Toggle bookmark submenu to toolbar.<br />
: Ctrl-b pops up "goto bookmark" list, with location for each bookmark. Ctrl-Shift-b pops up "toggle bookmark" list.<br />
* Sublime like handling of selection with right/left navigation keys. Remove selection and keep caret at ex-boundary of selection. To activate check both: "Caret skips selection" and "Caret left/rigt clears selection (no move)". See [https://bugs.freepascal.org/view.php?id=26477 mantis 26477]<br />
* More options to fine tune outline colors<br />
<br />
== Debugger ==<br />
<br />
* Auto closing of the asm window, if it was opened by breaking at a none source line. https://bugs.freepascal.org/view.php?id=27800#c109463<br />
* Dragging selected identifier from source editor to watches window, to create a watch<br />
<br />
=== LLDB based Debugger (New) ===<br />
<br />
The main target for this debugger is the Mac environment, which comes with a pre-installed lldb. For other platforms the use of the GDB based debugger is highly recommended.<br />
<br />
The LLDB debugger depends on the availability and correct function of the lldb executable.<br />
<br />
* Alpha: LLDB based debugger for MacOs (no code-signing required)<br />
: This is a raw LLDB wrapper. All watches must be lldb compatible. This may require C-style expressions entered into the watch window.<br />
: This debugger is experimental and provided "as is".<br />
* Beta: LLDB + FpDebug based debugger for MacOs (no code-signing required)<br />
: This is the recommended LLDB based debugger.<br />
<br />
To use the lldb based debugger:<br />
<br />
;Open the IDE and install the package "LazDebuggerFpLLdb":<br />
* It should be in the list of available packages, but if not it is in components/lazdebuggers/lazdebuggerfplldb<br />
* Make sure to use the one with "Fp" in the name. (There also is LazDebuggerLLdb, but it is not as good.)<br />
* Restart the IDE.<br />
<br />
;Go to Tools > Options > Debugger:<br />
* In the "debugger type" dropdown, you should find and select "LLDB debugger (with fpdebug)"<br />
* The edit below this (where you normally have the path to gdb), should be changed to the path of lldb.<br />
: (lldb comes with the tools from Apple; and as such it is already codesigned by Apple).<br />
<br />
In case of problems please see: http://forum.lazarus-ide.org/index.php/topic,42869.0.html<br />
<br />
=== GDB based Debugger ===<br />
<br />
;GDB Debugger new options:<br />
<br />
* Added option "FixStackFrameForFpcAssert" to workaround fpc wrong frame pointer (display correct line after assert failed)<br />
* Added option "FixIncorrectStepOver" to workaround "step over" issues experienced on some platforms (step-over acts like step-in) http://forum.lazarus-ide.org/index.php/topic,44121<br />
* Added option "AssemblerStyle": ATT vs Intel<br />
* Added option "DisableStartupShell": Required on MacOs.<br />
* Added more size limits for data evaluation (avoid errors, timeouts and extremely slow responses)<br />
: Some users may experience a small speed gain, when setting both of the "...LengthForStaticArray" values to an equal value, and both of the "...ValueMemLimit" to an equal value (the mem and length may differ,this has no effect)<br />
:* MaxDisplayLengthForStaticArray: Similar to existing "MaxDisplayLengthForString". This should apply to static array, but it is up to gdb which types it applies it to.<br />
:* MaxLocalsLengthForStaticArray: The same, but applied while getting values for the locals window, and the function parameters show in the stack window. Should be more restrictive.<br />
:* GdbValueMemLimit: For all types. Do not evaluate values, if gdb would need more memory. Setting this to big may crash gdb. Big values may also lead to slow response times (several minutes during which the IDE would be blocked)<br />
:* GdbLocalsValueMemLimit: For locals and stack.<br />
* DebugServer: Added "target-download" if remote supports it.<br />
<br />
= IDE Interfaces Changes =<br />
<br />
* Added '''FormEditingHook.SaveComponentAsPascal''', which stores a designer form as Pascal statements using '''TCompWriterPas'''. You have various options to define the format and it tells you what units are needed for the Pascal code. There is an example adding a designer menu item to copy the Pascal statements to the clipboard ''examples/pascalstream/CopyAsPasPkg/copyaspasdemounit1.pas''.<br />
<br />
* Added interface class TLazCompilationToolOptions with a Command and CompileReasons. Used in CompilerOptions.ExecuteBefore and .ExecuteAfter.<br />
<br />
= Components =<br />
<br />
=== TOpenGLControl ===<br />
* New property Options of type set, currently with ocoMacRetinaMode as the only member. If set, ocoMacRetinaMode determines that the OpenGL controls will use retina support (high resolution mode).<br />
* Control can use Qt5 widgeset now, Qt4 is also fixed for modern OpenGL contexts.<br />
<br />
=== TAChart ===<br />
* The new TExpressionSeries and TExpressionColorMapSeries plot mathematical functions at design-time.<br />
* TLineSeries has a new property "ColorEach" which can be used to color the line segments individually.<br />
* TCubicSplineSeries has a new property "SplineType" which allows to select between the "natural" and the "monotone Hermite" splines (the latter avoiding overshoot of the interpolation).<br />
* TAreaSeris has a new property "Banded" which suppresses painting of the bottom layer of stacked areas and makes the series look like a band or a stack of bands.<br />
* The new property "MarkDistancePercent" of TPieSeries can be used, for example, to center a label within its pie for all chart sizes.<br />
* TLineSeries, TCubicSplineSeries, TBSplineSeries and TFitSeries now can display error bars.<br />
* The TFitSeries fitting engine was extended to be able to exclude specific parameters from fitting and to do a statistical analysis of the fit results (error estimates of the determined fit parameters, confidence limits, goodness of fit).<br />
* TBarSeries has new property "MarkPositionCentered" which allows to center series marks within each bar. Available also for TAreaSeries where it works accordingly. NOTE: In intermediate trunk versions and some of the release candidates, this effect could be achieved partly by using option lmpInsideCenter of TMarkPositions - this option has been dropped now.<br />
<br />
=== TCustomEdit ===<br />
* Qt5 widgetset native implementation of TextHint property, Qt4 uses emulated TextHint.<br />
<br />
= Changes affecting compatibility =<br />
<br />
==Installer==<br />
<br />
=== Debian package fpc was renamed to fpc-laz ===<br />
<br />
This about the packages provided by the Lazarus team on sourceforge.<br />
<br />
* Old: fpc_3.0.4-3_amd64.deb, fpc_3.0.4-3_i386.deb<br />
* New: fpc-laz_3.0.4-1_amd64.deb, fpc-laz_3.0.4-1_i386.deb<br />
* Reason: avoiding name clash with Ubuntu/Debian repository package "fpc"<br />
* Remedy: adapt your scripts<br />
<br />
Note, that the fpc-src package keeps its name, as the Ubuntu/Debian package is fpc-source, so there is no name clash here.<br />
<br />
==LazUtils==<br />
<br />
==LCL incompatibilities==<br />
<br />
=== TToolBar children ignore Align ===<br />
Controls placed within TToolBar cannot be aligned with the Align property.<br />
* Old behavior: The Align property of TToolBar children was taken into account.<br />
* New behavior: The Align property is ignored.<br />
* Reason: It caused endless alignment loops when the control's size was changed by code in Create.<br />
* Remedy: use a parent container for TToolBar and align the controls within this container.<br />
<br />
=== TCustomComboBox.ReadOnly was deprecated ===<br />
* Old behavior: When True, only items from the list are accepted, by direct selection from the list or AutoComplete.<br />
* New behavior: it does nothing and will be removed.<br />
* Reason: Delphi-compatibility, confusing naming, WS compatibility (different behavior on Win32/Qt/Gtk)<br />
* Remedy: Use extended styles for the same feature.<br />
<br />
=== Predefined clipboard format pcfDelphiBitmap was removed ===<br />
* Old behavior: The enumeration TPredefineClipboardFormat contained an element pcfDelphiBitmap which once had to be introduced due to streaming differences of bitmaps between Delphi and LCL. <br />
* New behavior: pcfDelphiBitmap has been removed. (Rare) code relying on exact count and position of the elements of this enumeration will fail.<br />
* Reason: No longer needed and causing trouble in clipboard access.<br />
* Remedy: None - revisit your code.<br />
<br />
=== TEdit.Action visibility lowered to public ===<br />
* Old behavior: TEdit.Action was published.<br />
* New behavior: TEdit.Action is now public.<br />
* Reason: Delphi compatibility.<br />
* Remedy: set Action in code.<br />
<br />
=== [[TControl]].ScaleFontsPPI, .DoScaleFontPPI parameter change ===<br />
<br />
* Old behavior: No AToPPI parameter<br />
* New behavior: AToPPI parameter was added<br />
* Reason: font scaling problem {{MantisLink|32882}}. This change will be merged to 1.8.1<br />
* Remedy: fix parameters<br />
<br />
=== MouseEntered deprecated/missing ===<br />
<br />
* Old behavior: No warning on using MouseEntered<br />
* New behavior: Warning: Symbol "MouseEntered" is deprecated: "use MouseInClient instead"<br />
* Reason: Delphi compatibility<br />
* Remedy: use property MouseInClient instead<br />
<br />
=== TCustomImageList.Add method ===<br />
* Old behavior: the image got sliced if too big or extended if too small.<br />
* New behavior: the image is scaled to all resolutions in the image list.<br />
* Reason: Image List now supports multiple resolutions.<br />
* Remedy: use AddSliced (if the image consists of several icons to be added) or AddSlice (if one image from a custom rect has to be added - also rect outside the image is supported).<br />
<br />
=== TBitBtn.Glyph in combination with TBitBtn.Kind ===<br />
* Old behavior: The Glyph property was filled with a valid bitmap when the Kind property was set to a different value from bkCustom.<br />
* New behavior: The Glyph property is empty.<br />
* Reason: TBitBtn now supports image list with multiple resolutions. The Glyph (TBitmap) doesn't support multiple resolutions and thus cannot be used anymore to define the TBitBtn image.<br />
* Remedy: if you need the image for a specific TBitBtn.Kind get it directly from the LCLGlyphs property defined in ImgList.<br />
<br />
=== TCustomTreeView.OnChanging event: Node parameter ===<br />
* Old behvior: The parameter Node passed to the OnChanging event points to the currently selected node. This is not compatible with Delphi which has the destination node here.<br />
* New behavior: The parameter Node passed to the OnChanging event is the node which will be the selected node after the node-changing operation has completed.<br />
* Reasons:<br />
** The operation can be aborted by setting "AllowChange" to false. For this decision, knowledge of the node going to be selected is helpfull, at least more helpful than knowledge of the node to be left which still can be accessed as TreeView.Selected at this point. The old behavior does not tell the new node.<br />
** The new behavior is compatible with Delphi<br />
* Remedy: If old OnChanging handlers refere to the parameter Node replace Node by TreeView.Selected.<br />
<br />
=== TCustomDialog properties Width and Height visibility lowered to public ===<br />
* Old behaviour: Width and Height were published.<br />
* New behaviour: Width and Height are public.<br />
* Reason: Delphi compatibility.<br />
* Remedy: set the properties in code.<br />
<br />
=== No LCL Application exception dump ===<br />
* Old behavior: In case of an exception a dump was automatically written with DebugLn (e.g. to a console).<br />
* New behavior: There is no automatic exception dump any more.<br />
* Reason: The LCL should not emit debugging info by default.<br />
* Remedy 1: Add the LCLExceptionStackTrace unit to the uses clause of any of your units.<br />
* Remedy 2: To enable LCLExceptionStacktrace globally for all projects, create a new "Additions and Overrides" entry in "Stored in IDE" for "#project" target with the contents "-FaLCLExceptionStacktrace" (see screenshot below). (The drawback of this approach is that this will now also be used for console applications, so they won't compile unless you uncheck the addition in Compiler Options.)<br />
<br />
=== No default LazLogger ===<br />
* Old behavior: LazLogger was registered by default by the LCL.<br />
* New behavior: LazLogger is not registered any more, so there is an empty logger (LazLoggerBase) used with no output.<br />
* Reason: The LCL should not register any logger by default - the user has to define if he wants to use the logger and what logger he wants to use.<br />
* Remedy 1: Add the LazLogger unit to the uses clause of any of your units.<br />
* Remedy 2: To enable LazLogger globally for all projects, create a new "Additions and Overrides" entry in "Stored in IDE" for "#project" target with the contents "-FaLazLogger" (see screenshot below). (The drawback of this approach is that this will now also be used for console applications, so they won't compile unless you uncheck the addition in Compiler Options.)<br />
<br />
=== Screenshot for LCLExceptionStackTrace and LazLogger Additions and Overrides ===<br />
[[File:AdditionsOverridesExUnits.png]]<br />
<br />
=== TCustomTabControl setting TabIndex or PageIndex by code ===<br />
* Old behavior: setting TabIndex or PageIndex by code would invoke a call to CanChangePageIndex/CanChange<br />
* New behavior: setting TabIndex or PageIndex by code will not invoke CanChangePageIndex/CanChange. As a result OnChanging is not called in this scenario.<br />
* Reason: Delphi compatibility.<br />
* Remedy: set nboDoChangeOnSetIndex in Options (this option is not available in Delphi), call OnChanging in you own code when changing TabIndex/PageIndex, or move the code in OnChanging (or an overridden CanChangePageIndex/CanChange) to another event (or method) if applicable.<br />
<br />
=== TCustomGrid: The var parameter ABitmap in event OnUserCheckBoxBitmap used to be preassigned with default bitmap, but now it is nil ===<br />
* Old behaviour: You could draw directly to ABitmap in OnUserCheckBoxBitmap event.<br />
* New behaviour: You have to create a bitmap yourself and assign it to ABitmap parameter in OnUserCheckBoxBitmap event.<br />
* reason: this is var parameter and its proper usage has always been this way.<br />
* remedy: change code to provide the bitmap and assign it to ABitmap parameter in code of OnUserCheckBoxBitmap.<br />
More information in [https://bugs.freepascal.org/view.php?id=35085 bugreport 35085].<br />
<br />
==Components incompatibilities==<br />
<br />
===LazControls: TSpinEditEx no longer inherits from TCustomFloatSpinEditEx===<br />
* Old behavior: TSpinEditEx inherited from TFloatSpinEditEx.<br />
* New behavior: Both TSpinEditEx and TFloatSpinEditEx now inherit from a common (generic) base class: TSpinEditExBase. As a result testing for "is TCustomFloatSpinEdit" now returns False.<br />
* Reason: support for Int64 values in TSpinEdit (Double does not have enough precision to cover the Int64 range). Also makes for cleaner code.<br />
* Remedy: test for the appropriate (base) class instead.<br />
<br />
=== TAChart: Reticule-related properties deprecated ===<br />
* Old behavior: The reticule could be used to display a crosshair cursor in the chart.<br />
* New behavior: The reticule will be removed because the crosshair cursor is included in the more versatile charttools.<br />
* Reason: Remove duplicate features.<br />
* Remedy: Attach a TChartToolset to the chart, add a DatapointCrosshairTool to the toolset and adjust its properties.<br />
<br />
=== TAChart events On[After|Before]Draw[Background|BackwallWall] deprecated ===<br />
* Old behavior: These events could be used for custom-painting of the chart background or the chart data rectangle background.<br />
* New behavior: These events will be removed.<br />
* Reason: The events have a parameter Canvas to paint on. TAChart, however, supports additional drawing backends which do not have a canvas.<br />
* Remedy: Use the new events On[After|Before]CustomDraw[Background|Backwall] which get the currenty used drawer as a parameter. For drawing use the corresponding drawer methods.<br />
<br />
=== TAChart: The TCubicSplineOption <code>csoDrawFewPoints</code> is removed. ===<br />
* Old behavior: The CubicSplineSeries required at least 4 data points for drawing. In case of a TCubicSplineSeries with less data points a polygon was drawn as a replacement using the <code>BadDataPen</code> if the option <code>csoDrawFewDataPoints</code> was active; if this option was not set the series was not drawn at all.<br />
* New behavior: After fixing the cubic spline interpolation in a local version if NumLib's ipf unit it is possible to draw splines also for less than 4 data points. Old code using the <code>csoDrawFewDatapoints</code> option will not compile any more.<br />
* Reason: The old behavior was a workaround for a NumLib bug.<br />
* Remedy: Don't use <code>csoDrawFewPoints</code> any more.<br />
<br />
=== TAChart: Other properties removed that had been marked as deprecated for a long time ===<br />
* The following table lists the removed properties/procedures/functions along with their replacements:<br />
{| class="wikitable"<br />
|-<br />
! removed<br />
! use instead<br />
|-<br />
| property <code>TChartTitle.Font</code> (unit TAChartAxisUtils)<br />
| <code>TChartTitle.LabelFont</code><br />
|-<br />
| property <code>TCustomChartSeries.ShowInLegend</code> (unit TACustomSeries)<br />
| <code>TCustomChartSeries.Legend.Visible</code><br />
|-<br />
| method <code>TFuncSeries.GetFitEquationText</code> (unit TAFuncSeries)<br />
| <code>TFuncSeries.EquationText</code><br />
|-<br />
| method <code>TChart.DrawOnCanvas</code> (unit TAGraph)<br />
| <code>TChart.PaintOnCanvas</code><br />
|-<br />
| property <code>TChartLegend.Margin</code> (unit TALegend)<br />
| <code>TChartLegend.MarginX</code><br />
|-<br />
| event <code>TLineSeries.OnDrawPointer</code> (unit TASeries)<br />
| <code>TLineSeries.OnCustomDrawPointer</code><br />
|- <br />
| class <code>TSerie</code> (unit TASeries)<br />
| <code>TLineSeries</code><br />
|-<br />
| class <code>TLine</code> (unit TASeries)<br />
| <code>TConstantLine</code><br />
|-<br />
| property <code>TZoomDragTool.Proportional</code> (unit TATools)<br />
| <code>TZoomDragTool.RatioLimit</code>, set to <code>zlrProportional</code><br />
|}<br />
<br />
==IDE incompatibilities==<br />
<br />
= Previous release notes =<br />
<br />
*[[Lazarus 1.8.0 release notes]]<br />
*[[Lazarus 1.6.0 release notes]]<br />
*[[Lazarus 1.4.0 release notes]]<br />
*[[Lazarus 1.2.0 release notes]]<br />
*[[Lazarus 1.0 release notes]]<br />
<br />
{{Navbar Lazarus Release Notes}}<br />
<br />
[[Category:Release Notes]]<br />
[[Category:Lazarus]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=Lazarus_2.0.0_release_notes&diff=123252Lazarus 2.0.0 release notes2019-02-15T15:09:23Z<p>Zoran: Move CustomGrid from "Components incompatibilities" to "LCL incompatibilities"</p>
<hr />
<div>'''Lazarus 2.0.0 was released on 2019-02-05''' [[http://forum.lazarus.freepascal.org/index.php/topic,44161.0.html release announcement]]<br />
<br />
= LCL Interfaces Changes =<br />
<br />
= LCL Changes =<br />
<br />
=== TScrollingWinControl ([[TForm]], [[TScrollBox]], [[TFrame]]) ===<br />
* ScreenToClient and ClientToScreen are now calculated without scrollbar offset. Done for Delphi compatibility.<br />
<br />
=== Support for mouse wheel horz scrolling ===<br />
Added TControl events for horizontal mouse wheel moving (special mice with horz scroll buttons for PC, and modern mice for Mac). OnMouseWheelHorz, OnMouseWheelLeft, OnMouseWheelRight. Events are used in some non native LCL components (TreeView, UpDown, SpinEdit). Native components should handle horz scrolling already.<br />
<br />
=== Compiler defines to exclude some graphics support ===<br />
Added flags to exclude support for some graphics formats to create smaller applications:<br />
* -dDisableLCLGIF<br />
* -dDisableLCLJPEG<br />
* -dDisableLCLPNM<br />
* -dDisableLCLTIFF<br />
<br />
=== TCustomImageList / TImageList ===<br />
* the image list now supports multiple resolutions of one image. See [[TImageList|Multiple-resolution TImageList in Lazarus 1.9 and newer]] for more details. As a result all LCL controls support High-DPI glyphs on Windows+Linux and Retina on Mac without any additional code.<br />
* every LCL control that supports ImageList has now a new ImagesWidth property to decide what custom width at 96 PPI (100% scale) is to be used. Example: TToolBar.Images/.ImagesWidth, TListView.LargeImages/.LargeImagesWidth, .StateImages/.StateImagesWidth.<br />
* set the TCustomImageList.Scaled=True property to let the image list automatically pick up the right resolution for your control.<br />
<br />
=== TSpeedButton, TBitBtn ===<br />
* New properties Images, ImageIndex and ImageWidth. With them full ImageList support was added.<br />
** no need to save the same Glyph in LFM all over the application<br />
** automatic high-DPI image handling<br />
<br />
=== TWinControl.DoubleBuffered, .ParentDoubleBuffered and TApplication.DoubleBuffered ===<br />
* Note: DoubleBuffered is a LCLWin32-only feature<br />
* Old behavior: DoubleBuffered wasn't properly implemented and it was forced True<br />
* New behavior:<br />
** A Delphi-compatible DoubleBuffered/ParentDoubleBuffered concept was created (it is equal to the Font/ParentFont concept).<br />
** The LCL has the TApplication.DoubleBuffered extension over Delphi that allows you to set form's default DoubleBuffered value globally for the whole application (set Application.DoubleBuffered before creating the first form). The value is then applied to all controls on the form with ParentDoubleBuffered:=True.<br />
** DoubleBuffered is True by default unless in remote session (this is different to Delphi where DoubleBuffered is False by default).<br />
** If you need one specific control to be DoubleBuffered:=False even for Application.DoubleBuffered:=adbDefault, make sure you set control.DoubleBuffered:=False and .ParentDoubleBuffered:=False.<br />
<br />
=== TListView ===<br />
<br />
Added CustomSort method like in Delphi.<br />
<br />
=== TTreeView ===<br />
<br />
* Implemented HotTrack property (show blue underline for item under cursor).<br />
* Implemented auto-scrolling up/down during drag-drop (when mouse reaches the edge of treeview).<br />
<br />
=== FilterOptions in filter controls ===<br />
<br />
Base class for filter controls TCustomControlFilterEdit has new property FilterOptions, which is set of such flags:<br />
<br />
TFilterStringOption = (fsoCaseSensitive, fsoMatchOnlyAtStart);<br />
TFilterStringOptions = set of TFilterStringOption;<br />
<br />
Two flags are implemented for 3 filter controls in LazControls: TListFilterEdit, TListViewFilterEdit, TTreeFilterEdit.<br />
<br />
=== Advanced menus Assign ===<br />
<br />
* Supported TMenuItem.Assign(TMenuItem): this copies all props of menuitem.<br />
* Supported TMenu.Assign(TMenu): this copies all items with all nested subitems from one menu to another (TMainMenu, TPopupMenu).<br />
<br />
=== TPageSetupDialog ===<br />
<br />
* TPageSetupDialog: added Margin* and Units properties.<br />
* Big rework of Unix TPageSetupDialog, it is now complete like it's on Windows (on Windows it was native, on Unix - emulation).<br />
<br />
= IDE Changes =<br />
<br />
* several '''High-DPI IDE improvements''' and retina support on Cocoa<br />
* '''Delphi Attributes''': Find declaration, parameter hints, $modeswitch prefixedattributes.<br />
* The IDE parses the custom compiler options for the '''fpc switch -FN<namespaces>''', to define the default namespaces, so you can omit the namespaces in the uses sections.<br />
* [[pas2js]] support:<br />
** New IDE package '''[[lazarus_pas2js_integration|pas2jsdsgn]]''' (not installed by default):<br />
*** You can create a browser or nodejs webapplication<br />
*** on '''Run'' starts the webapp in your webbrowser<br />
** pas2js settings are automatically fetched, same as fpc settings<br />
** quickfixes work with pas2js messages<br />
* added quickfix for fpc message "inherited method is hidden": add modifier ''override'', ''overload'', or ''reintroduce'' shortcuts.<br />
* added designer menu item to hide icons for components like TOpenDialog. Option: '''Show non visual components'''<br />
* extended filter for the identifier completion window - include identifiers containing prefix. (Settings: IDE Options -> Codetools -> Identifier Completion -> Sorting -> Include identifiers containing prefix.) See https://bugs.freepascal.org/view.php?id=32974.<br />
* the identifier completion window includes also text (words) from the currently opened units. It can be disabled or set-up in IDE Options -> Codetools -> Identifier Completion -> Include words<br />
* the Publish Project/Package feature and GUI window was revamped. Now all project/package member files are included automatically. More files can be added using a filter. The directory structure is maintained even if some files are in directories "above" the main directory.<br />
* the "New Component" window and user interface is improved. It also now supports 3 icons for different resolutions.<br />
* the Run -> Compile many Modes... feature now remembers its previously selected modes.<br />
* the Search -> Procedure List... window now remembers the state of its filter buttons.<br />
<br />
== Editor ==<br />
<br />
* Added new '''"smart move cursor"''' moving commands to editor mappings. They introduce word jumping positions both at word starts and ends. They are useful as alternative Ctrl+Left/+Right/+Shift+Left/+Shift+Right key <br />
* Added Goto/Toggle bookmark submenu to toolbar.<br />
: Ctrl-b pops up "goto bookmark" list, with location for each bookmark. Ctrl-Shift-b pops up "toggle bookmark" list.<br />
* Sublime like handling of selection with right/left navigation keys. Remove selection and keep caret at ex-boundary of selection. To activate check both: "Caret skips selection" and "Caret left/rigt clears selection (no move)". See [https://bugs.freepascal.org/view.php?id=26477 mantis 26477]<br />
* More options to fine tune outline colors<br />
<br />
== Debugger ==<br />
<br />
* Auto closing of the asm window, if it was opened by breaking at a none source line. https://bugs.freepascal.org/view.php?id=27800#c109463<br />
* Dragging selected identifier from source editor to watches window, to create a watch<br />
<br />
=== LLDB based Debugger (New) ===<br />
<br />
The main target for this debugger is the Mac environment, which comes with a pre-installed lldb. For other platforms the use of the GDB based debugger is highly recommended.<br />
<br />
The LLDB debugger depends on the availability and correct function of the lldb executable.<br />
<br />
* Alpha: LLDB based debugger for MacOs (no code-signing required)<br />
: This is a raw LLDB wrapper. All watches must be lldb compatible. This may require C-style expressions entered into the watch window.<br />
: This debugger is experimental and provided "as is".<br />
* Beta: LLDB + FpDebug based debugger for MacOs (no code-signing required)<br />
: This is the recommended LLDB based debugger.<br />
<br />
To use the lldb based debugger:<br />
<br />
;Open the IDE and install the package "LazDebuggerFpLLdb":<br />
* It should be in the list of available packages, but if not it is in components/lazdebuggers/lazdebuggerfplldb<br />
* Make sure to use the one with "Fp" in the name. (There also is LazDebuggerLLdb, but it is not as good.)<br />
* Restart the IDE.<br />
<br />
;Go to Tools > Options > Debugger:<br />
* In the "debugger type" dropdown, you should find and select "LLDB debugger (with fpdebug)"<br />
* The edit below this (where you normally have the path to gdb), should be changed to the path of lldb.<br />
: (lldb comes with the tools from Apple; and as such it is already codesigned by Apple).<br />
<br />
In case of problems please see: http://forum.lazarus-ide.org/index.php/topic,42869.0.html<br />
<br />
=== GDB based Debugger ===<br />
<br />
;GDB Debugger new options:<br />
<br />
* Added option "FixStackFrameForFpcAssert" to workaround fpc wrong frame pointer (display correct line after assert failed)<br />
* Added option "FixIncorrectStepOver" to workaround "step over" issues experienced on some platforms (step-over acts like step-in) http://forum.lazarus-ide.org/index.php/topic,44121<br />
* Added option "AssemblerStyle": ATT vs Intel<br />
* Added option "DisableStartupShell": Required on MacOs.<br />
* Added more size limits for data evaluation (avoid errors, timeouts and extremely slow responses)<br />
: Some users may experience a small speed gain, when setting both of the "...LengthForStaticArray" values to an equal value, and both of the "...ValueMemLimit" to an equal value (the mem and length may differ,this has no effect)<br />
:* MaxDisplayLengthForStaticArray: Similar to existing "MaxDisplayLengthForString". This should apply to static array, but it is up to gdb which types it applies it to.<br />
:* MaxLocalsLengthForStaticArray: The same, but applied while getting values for the locals window, and the function parameters show in the stack window. Should be more restrictive.<br />
:* GdbValueMemLimit: For all types. Do not evaluate values, if gdb would need more memory. Setting this to big may crash gdb. Big values may also lead to slow response times (several minutes during which the IDE would be blocked)<br />
:* GdbLocalsValueMemLimit: For locals and stack.<br />
* DebugServer: Added "target-download" if remote supports it.<br />
<br />
= IDE Interfaces Changes =<br />
<br />
* Added '''FormEditingHook.SaveComponentAsPascal''', which stores a designer form as Pascal statements using '''TCompWriterPas'''. You have various options to define the format and it tells you what units are needed for the Pascal code. There is an example adding a designer menu item to copy the Pascal statements to the clipboard ''examples/pascalstream/CopyAsPasPkg/copyaspasdemounit1.pas''.<br />
<br />
* Added interface class TLazCompilationToolOptions with a Command and CompileReasons. Used in CompilerOptions.ExecuteBefore and .ExecuteAfter.<br />
<br />
= Components =<br />
<br />
=== TOpenGLControl ===<br />
* New property Options of type set, currently with ocoMacRetinaMode as the only member. If set, ocoMacRetinaMode determines that the OpenGL controls will use retina support (high resolution mode).<br />
* Control can use Qt5 widgeset now, Qt4 is also fixed for modern OpenGL contexts.<br />
<br />
=== TAChart ===<br />
* The new TExpressionSeries and TExpressionColorMapSeries plot mathematical functions at design-time.<br />
* TLineSeries has a new property "ColorEach" which can be used to color the line segments individually.<br />
* TCubicSplineSeries has a new property "SplineType" which allows to select between the "natural" and the "monotone Hermite" splines (the latter avoiding overshoot of the interpolation).<br />
* TAreaSeris has a new property "Banded" which suppresses painting of the bottom layer of stacked areas and makes the series look like a band or a stack of bands.<br />
* The new property "MarkDistancePercent" of TPieSeries can be used, for example, to center a label within its pie for all chart sizes.<br />
* TLineSeries, TCubicSplineSeries, TBSplineSeries and TFitSeries now can display error bars.<br />
* The TFitSeries fitting engine was extended to be able to exclude specific parameters from fitting and to do a statistical analysis of the fit results (error estimates of the determined fit parameters, confidence limits, goodness of fit).<br />
* TBarSeries has new property "MarkPositionCentered" which allows to center series marks within each bar. Available also for TAreaSeries where it works accordingly. NOTE: In intermediate trunk versions and some of the release candidates, this effect could be achieved partly by using option lmpInsideCenter of TMarkPositions - this option has been dropped now.<br />
<br />
=== TCustomEdit ===<br />
* Qt5 widgetset native implementation of TextHint property, Qt4 uses emulated TextHint.<br />
<br />
= Changes affecting compatibility =<br />
<br />
==Installer==<br />
<br />
=== Debian package fpc was renamed to fpc-laz ===<br />
<br />
This about the packages provided by the Lazarus team on sourceforge.<br />
<br />
* Old: fpc_3.0.4-3_amd64.deb, fpc_3.0.4-3_i386.deb<br />
* New: fpc-laz_3.0.4-1_amd64.deb, fpc-laz_3.0.4-1_i386.deb<br />
* Reason: avoiding name clash with Ubuntu/Debian repository package "fpc"<br />
* Remedy: adapt your scripts<br />
<br />
Note, that the fpc-src package keeps its name, as the Ubuntu/Debian package is fpc-source, so there is no name clash here.<br />
<br />
==LazUtils==<br />
<br />
==LCL incompatibilities==<br />
<br />
=== TToolBar children ignore Align ===<br />
Controls placed within TToolBar cannot be aligned with the Align property.<br />
* Old behavior: The Align property of TToolBar children was taken into account.<br />
* New behavior: The Align property is ignored.<br />
* Reason: It caused endless alignment loops when the control's size was changed by code in Create.<br />
* Remedy: use a parent container for TToolBar and align the controls within this container.<br />
<br />
=== TCustomComboBox.ReadOnly was deprecated ===<br />
* Old behavior: When True, only items from the list are accepted, by direct selection from the list or AutoComplete.<br />
* New behavior: it does nothing and will be removed.<br />
* Reason: Delphi-compatibility, confusing naming, WS compatibility (different behavior on Win32/Qt/Gtk)<br />
* Remedy: Use extended styles for the same feature.<br />
<br />
=== Predefined clipboard format pcfDelphiBitmap was removed ===<br />
* Old behavior: The enumeration TPredefineClipboardFormat contained an element pcfDelphiBitmap which once had to be introduced due to streaming differences of bitmaps between Delphi and LCL. <br />
* New behavior: pcfDelphiBitmap has been removed. (Rare) code relying on exact count and position of the elements of this enumeration will fail.<br />
* Reason: No longer needed and causing trouble in clipboard access.<br />
* Remedy: None - revisit your code.<br />
<br />
=== TEdit.Action visibility lowered to public ===<br />
* Old behavior: TEdit.Action was published.<br />
* New behavior: TEdit.Action is now public.<br />
* Reason: Delphi compatibility.<br />
* Remedy: set Action in code.<br />
<br />
=== [[TControl]].ScaleFontsPPI, .DoScaleFontPPI parameter change ===<br />
<br />
* Old behavior: No AToPPI parameter<br />
* New behavior: AToPPI parameter was added<br />
* Reason: font scaling problem {{MantisLink|32882}}. This change will be merged to 1.8.1<br />
* Remedy: fix parameters<br />
<br />
=== MouseEntered deprecated/missing ===<br />
<br />
* Old behavior: No warning on using MouseEntered<br />
* New behavior: Warning: Symbol "MouseEntered" is deprecated: "use MouseInClient instead"<br />
* Reason: Delphi compatibility<br />
* Remedy: use property MouseInClient instead<br />
<br />
=== TCustomImageList.Add method ===<br />
* Old behavior: the image got sliced if too big or extended if too small.<br />
* New behavior: the image is scaled to all resolutions in the image list.<br />
* Reason: Image List now supports multiple resolutions.<br />
* Remedy: use AddSliced (if the image consists of several icons to be added) or AddSlice (if one image from a custom rect has to be added - also rect outside the image is supported).<br />
<br />
=== TBitBtn.Glyph in combination with TBitBtn.Kind ===<br />
* Old behavior: The Glyph property was filled with a valid bitmap when the Kind property was set to a different value from bkCustom.<br />
* New behavior: The Glyph property is empty.<br />
* Reason: TBitBtn now supports image list with multiple resolutions. The Glyph (TBitmap) doesn't support multiple resolutions and thus cannot be used anymore to define the TBitBtn image.<br />
* Remedy: if you need the image for a specific TBitBtn.Kind get it directly from the LCLGlyphs property defined in ImgList.<br />
<br />
=== TCustomTreeView.OnChanging event: Node parameter ===<br />
* Old behvior: The parameter Node passed to the OnChanging event points to the currently selected node. This is not compatible with Delphi which has the destination node here.<br />
* New behavior: The parameter Node passed to the OnChanging event is the node which will be the selected node after the node-changing operation has completed.<br />
* Reasons:<br />
** The operation can be aborted by setting "AllowChange" to false. For this decision, knowledge of the node going to be selected is helpfull, at least more helpful than knowledge of the node to be left which still can be accessed as TreeView.Selected at this point. The old behavior does not tell the new node.<br />
** The new behavior is compatible with Delphi<br />
* Remedy: If old OnChanging handlers refere to the parameter Node replace Node by TreeView.Selected.<br />
<br />
=== TCustomDialog properties Width and Height visibility lowered to public ===<br />
* Old behaviour: Width and Height were published.<br />
* New behaviour: Width and Height are public.<br />
* Reason: Delphi compatibility.<br />
* Remedy: set the properties in code.<br />
<br />
=== No LCL Application exception dump ===<br />
* Old behavior: In case of an exception a dump was automatically written with DebugLn (e.g. to a console).<br />
* New behavior: There is no automatic exception dump any more.<br />
* Reason: The LCL should not emit debugging info by default.<br />
* Remedy 1: Add the LCLExceptionStackTrace unit to the uses clause of any of your units.<br />
* Remedy 2: To enable LCLExceptionStacktrace globally for all projects, create a new "Additions and Overrides" entry in "Stored in IDE" for "#project" target with the contents "-FaLCLExceptionStacktrace" (see screenshot below). (The drawback of this approach is that this will now also be used for console applications, so they won't compile unless you uncheck the addition in Compiler Options.)<br />
<br />
=== No default LazLogger ===<br />
* Old behavior: LazLogger was registered by default by the LCL.<br />
* New behavior: LazLogger is not registered any more, so there is an empty logger (LazLoggerBase) used with no output.<br />
* Reason: The LCL should not register any logger by default - the user has to define if he wants to use the logger and what logger he wants to use.<br />
* Remedy 1: Add the LazLogger unit to the uses clause of any of your units.<br />
* Remedy 2: To enable LazLogger globally for all projects, create a new "Additions and Overrides" entry in "Stored in IDE" for "#project" target with the contents "-FaLazLogger" (see screenshot below). (The drawback of this approach is that this will now also be used for console applications, so they won't compile unless you uncheck the addition in Compiler Options.)<br />
<br />
=== Screenshot for LCLExceptionStackTrace and LazLogger Additions and Overrides ===<br />
[[File:AdditionsOverridesExUnits.png]]<br />
<br />
=== TCustomTabControl setting TabIndex or PageIndex by code ===<br />
* Old behavior: setting TabIndex or PageIndex by code would invoke a call to CanChangePageIndex/CanChange<br />
* New behavior: setting TabIndex or PageIndex by code will not invoke CanChangePageIndex/CanChange. As a result OnChanging is not called in this scenario.<br />
* Reason: Delphi compatibility.<br />
* Remedy: set nboDoChangeOnSetIndex in Options (this option is not available in Delphi), call OnChanging in you own code when changing TabIndex/PageIndex, or move the code in OnChanging (or an overridden CanChangePageIndex/CanChange) to another event (or method) if applicable.<br />
<br />
=== TCustomGrid: The var parameter ABitmap in event OnUserCheckBoxBitmap used to be preassigned with default bitmap, but now it is nil. ===<br />
* Old behaviour: You could draw directly to ABitmap in OnUserCheckBoxBitmap event.<br />
* New behaviour: You have to create a bitmap yourself and assign it to ABitmap parameter in OnUserCheckBoxBitmap event.<br />
* reason: this is var parameter and its proper usage has always been this way.<br />
* remedy: change code to provide the bitmap and assign it to ABitmap parameter in code of OnUserCheckBoxBitmap.<br />
More information in [https://bugs.freepascal.org/view.php?id=35085 bugreport 35085].<br />
<br />
<br />
==Components incompatibilities==<br />
<br />
===LazControls: TSpinEditEx no longer inherits from TCustomFloatSpinEditEx===<br />
* Old behavior: TSpinEditEx inherited from TFloatSpinEditEx.<br />
* New behavior: Both TSpinEditEx and TFloatSpinEditEx now inherit from a common (generic) base class: TSpinEditExBase. As a result testing for "is TCustomFloatSpinEdit" now returns False.<br />
* Reason: support for Int64 values in TSpinEdit (Double does not have enough precision to cover the Int64 range). Also makes for cleaner code.<br />
* Remedy: test for the appropriate (base) class instead.<br />
<br />
=== TAChart: Reticule-related properties deprecated ===<br />
* Old behavior: The reticule could be used to display a crosshair cursor in the chart.<br />
* New behavior: The reticule will be removed because the crosshair cursor is included in the more versatile charttools.<br />
* Reason: Remove duplicate features.<br />
* Remedy: Attach a TChartToolset to the chart, add a DatapointCrosshairTool to the toolset and adjust its properties.<br />
<br />
=== TAChart events On[After|Before]Draw[Background|BackwallWall] deprecated ===<br />
* Old behavior: These events could be used for custom-painting of the chart background or the chart data rectangle background.<br />
* New behavior: These events will be removed.<br />
* Reason: The events have a parameter Canvas to paint on. TAChart, however, supports additional drawing backends which do not have a canvas.<br />
* Remedy: Use the new events On[After|Before]CustomDraw[Background|Backwall] which get the currenty used drawer as a parameter. For drawing use the corresponding drawer methods.<br />
<br />
=== TAChart: The TCubicSplineOption <code>csoDrawFewPoints</code> is removed. ===<br />
* Old behavior: The CubicSplineSeries required at least 4 data points for drawing. In case of a TCubicSplineSeries with less data points a polygon was drawn as a replacement using the <code>BadDataPen</code> if the option <code>csoDrawFewDataPoints</code> was active; if this option was not set the series was not drawn at all.<br />
* New behavior: After fixing the cubic spline interpolation in a local version if NumLib's ipf unit it is possible to draw splines also for less than 4 data points. Old code using the <code>csoDrawFewDatapoints</code> option will not compile any more.<br />
* Reason: The old behavior was a workaround for a NumLib bug.<br />
* Remedy: Don't use <code>csoDrawFewPoints</code> any more.<br />
<br />
=== TAChart: Other properties removed that had been marked as deprecated for a long time ===<br />
* The following table lists the removed properties/procedures/functions along with their replacements:<br />
{| class="wikitable"<br />
|-<br />
! removed<br />
! use instead<br />
|-<br />
| property <code>TChartTitle.Font</code> (unit TAChartAxisUtils)<br />
| <code>TChartTitle.LabelFont</code><br />
|-<br />
| property <code>TCustomChartSeries.ShowInLegend</code> (unit TACustomSeries)<br />
| <code>TCustomChartSeries.Legend.Visible</code><br />
|-<br />
| method <code>TFuncSeries.GetFitEquationText</code> (unit TAFuncSeries)<br />
| <code>TFuncSeries.EquationText</code><br />
|-<br />
| method <code>TChart.DrawOnCanvas</code> (unit TAGraph)<br />
| <code>TChart.PaintOnCanvas</code><br />
|-<br />
| property <code>TChartLegend.Margin</code> (unit TALegend)<br />
| <code>TChartLegend.MarginX</code><br />
|-<br />
| event <code>TLineSeries.OnDrawPointer</code> (unit TASeries)<br />
| <code>TLineSeries.OnCustomDrawPointer</code><br />
|- <br />
| class <code>TSerie</code> (unit TASeries)<br />
| <code>TLineSeries</code><br />
|-<br />
| class <code>TLine</code> (unit TASeries)<br />
| <code>TConstantLine</code><br />
|-<br />
| property <code>TZoomDragTool.Proportional</code> (unit TATools)<br />
| <code>TZoomDragTool.RatioLimit</code>, set to <code>zlrProportional</code><br />
|}<br />
<br />
==IDE incompatibilities==<br />
<br />
= Previous release notes =<br />
<br />
*[[Lazarus 1.8.0 release notes]]<br />
*[[Lazarus 1.6.0 release notes]]<br />
*[[Lazarus 1.4.0 release notes]]<br />
*[[Lazarus 1.2.0 release notes]]<br />
*[[Lazarus 1.0 release notes]]<br />
<br />
{{Navbar Lazarus Release Notes}}<br />
<br />
[[Category:Release Notes]]<br />
[[Category:Lazarus]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=Lazarus_2.0.0_release_notes&diff=123251Lazarus 2.0.0 release notes2019-02-15T15:05:21Z<p>Zoran: /* Components incompatibilities */</p>
<hr />
<div>'''Lazarus 2.0.0 was released on 2019-02-05''' [[http://forum.lazarus.freepascal.org/index.php/topic,44161.0.html release announcement]]<br />
<br />
= LCL Interfaces Changes =<br />
<br />
= LCL Changes =<br />
<br />
=== TScrollingWinControl ([[TForm]], [[TScrollBox]], [[TFrame]]) ===<br />
* ScreenToClient and ClientToScreen are now calculated without scrollbar offset. Done for Delphi compatibility.<br />
<br />
=== Support for mouse wheel horz scrolling ===<br />
Added TControl events for horizontal mouse wheel moving (special mice with horz scroll buttons for PC, and modern mice for Mac). OnMouseWheelHorz, OnMouseWheelLeft, OnMouseWheelRight. Events are used in some non native LCL components (TreeView, UpDown, SpinEdit). Native components should handle horz scrolling already.<br />
<br />
=== Compiler defines to exclude some graphics support ===<br />
Added flags to exclude support for some graphics formats to create smaller applications:<br />
* -dDisableLCLGIF<br />
* -dDisableLCLJPEG<br />
* -dDisableLCLPNM<br />
* -dDisableLCLTIFF<br />
<br />
=== TCustomImageList / TImageList ===<br />
* the image list now supports multiple resolutions of one image. See [[TImageList|Multiple-resolution TImageList in Lazarus 1.9 and newer]] for more details. As a result all LCL controls support High-DPI glyphs on Windows+Linux and Retina on Mac without any additional code.<br />
* every LCL control that supports ImageList has now a new ImagesWidth property to decide what custom width at 96 PPI (100% scale) is to be used. Example: TToolBar.Images/.ImagesWidth, TListView.LargeImages/.LargeImagesWidth, .StateImages/.StateImagesWidth.<br />
* set the TCustomImageList.Scaled=True property to let the image list automatically pick up the right resolution for your control.<br />
<br />
=== TSpeedButton, TBitBtn ===<br />
* New properties Images, ImageIndex and ImageWidth. With them full ImageList support was added.<br />
** no need to save the same Glyph in LFM all over the application<br />
** automatic high-DPI image handling<br />
<br />
=== TWinControl.DoubleBuffered, .ParentDoubleBuffered and TApplication.DoubleBuffered ===<br />
* Note: DoubleBuffered is a LCLWin32-only feature<br />
* Old behavior: DoubleBuffered wasn't properly implemented and it was forced True<br />
* New behavior:<br />
** A Delphi-compatible DoubleBuffered/ParentDoubleBuffered concept was created (it is equal to the Font/ParentFont concept).<br />
** The LCL has the TApplication.DoubleBuffered extension over Delphi that allows you to set form's default DoubleBuffered value globally for the whole application (set Application.DoubleBuffered before creating the first form). The value is then applied to all controls on the form with ParentDoubleBuffered:=True.<br />
** DoubleBuffered is True by default unless in remote session (this is different to Delphi where DoubleBuffered is False by default).<br />
** If you need one specific control to be DoubleBuffered:=False even for Application.DoubleBuffered:=adbDefault, make sure you set control.DoubleBuffered:=False and .ParentDoubleBuffered:=False.<br />
<br />
=== TListView ===<br />
<br />
Added CustomSort method like in Delphi.<br />
<br />
=== TTreeView ===<br />
<br />
* Implemented HotTrack property (show blue underline for item under cursor).<br />
* Implemented auto-scrolling up/down during drag-drop (when mouse reaches the edge of treeview).<br />
<br />
=== FilterOptions in filter controls ===<br />
<br />
Base class for filter controls TCustomControlFilterEdit has new property FilterOptions, which is set of such flags:<br />
<br />
TFilterStringOption = (fsoCaseSensitive, fsoMatchOnlyAtStart);<br />
TFilterStringOptions = set of TFilterStringOption;<br />
<br />
Two flags are implemented for 3 filter controls in LazControls: TListFilterEdit, TListViewFilterEdit, TTreeFilterEdit.<br />
<br />
=== Advanced menus Assign ===<br />
<br />
* Supported TMenuItem.Assign(TMenuItem): this copies all props of menuitem.<br />
* Supported TMenu.Assign(TMenu): this copies all items with all nested subitems from one menu to another (TMainMenu, TPopupMenu).<br />
<br />
=== TPageSetupDialog ===<br />
<br />
* TPageSetupDialog: added Margin* and Units properties.<br />
* Big rework of Unix TPageSetupDialog, it is now complete like it's on Windows (on Windows it was native, on Unix - emulation).<br />
<br />
= IDE Changes =<br />
<br />
* several '''High-DPI IDE improvements''' and retina support on Cocoa<br />
* '''Delphi Attributes''': Find declaration, parameter hints, $modeswitch prefixedattributes.<br />
* The IDE parses the custom compiler options for the '''fpc switch -FN<namespaces>''', to define the default namespaces, so you can omit the namespaces in the uses sections.<br />
* [[pas2js]] support:<br />
** New IDE package '''[[lazarus_pas2js_integration|pas2jsdsgn]]''' (not installed by default):<br />
*** You can create a browser or nodejs webapplication<br />
*** on '''Run'' starts the webapp in your webbrowser<br />
** pas2js settings are automatically fetched, same as fpc settings<br />
** quickfixes work with pas2js messages<br />
* added quickfix for fpc message "inherited method is hidden": add modifier ''override'', ''overload'', or ''reintroduce'' shortcuts.<br />
* added designer menu item to hide icons for components like TOpenDialog. Option: '''Show non visual components'''<br />
* extended filter for the identifier completion window - include identifiers containing prefix. (Settings: IDE Options -> Codetools -> Identifier Completion -> Sorting -> Include identifiers containing prefix.) See https://bugs.freepascal.org/view.php?id=32974.<br />
* the identifier completion window includes also text (words) from the currently opened units. It can be disabled or set-up in IDE Options -> Codetools -> Identifier Completion -> Include words<br />
* the Publish Project/Package feature and GUI window was revamped. Now all project/package member files are included automatically. More files can be added using a filter. The directory structure is maintained even if some files are in directories "above" the main directory.<br />
* the "New Component" window and user interface is improved. It also now supports 3 icons for different resolutions.<br />
* the Run -> Compile many Modes... feature now remembers its previously selected modes.<br />
* the Search -> Procedure List... window now remembers the state of its filter buttons.<br />
<br />
== Editor ==<br />
<br />
* Added new '''"smart move cursor"''' moving commands to editor mappings. They introduce word jumping positions both at word starts and ends. They are useful as alternative Ctrl+Left/+Right/+Shift+Left/+Shift+Right key <br />
* Added Goto/Toggle bookmark submenu to toolbar.<br />
: Ctrl-b pops up "goto bookmark" list, with location for each bookmark. Ctrl-Shift-b pops up "toggle bookmark" list.<br />
* Sublime like handling of selection with right/left navigation keys. Remove selection and keep caret at ex-boundary of selection. To activate check both: "Caret skips selection" and "Caret left/rigt clears selection (no move)". See [https://bugs.freepascal.org/view.php?id=26477 mantis 26477]<br />
* More options to fine tune outline colors<br />
<br />
== Debugger ==<br />
<br />
* Auto closing of the asm window, if it was opened by breaking at a none source line. https://bugs.freepascal.org/view.php?id=27800#c109463<br />
* Dragging selected identifier from source editor to watches window, to create a watch<br />
<br />
=== LLDB based Debugger (New) ===<br />
<br />
The main target for this debugger is the Mac environment, which comes with a pre-installed lldb. For other platforms the use of the GDB based debugger is highly recommended.<br />
<br />
The LLDB debugger depends on the availability and correct function of the lldb executable.<br />
<br />
* Alpha: LLDB based debugger for MacOs (no code-signing required)<br />
: This is a raw LLDB wrapper. All watches must be lldb compatible. This may require C-style expressions entered into the watch window.<br />
: This debugger is experimental and provided "as is".<br />
* Beta: LLDB + FpDebug based debugger for MacOs (no code-signing required)<br />
: This is the recommended LLDB based debugger.<br />
<br />
To use the lldb based debugger:<br />
<br />
;Open the IDE and install the package "LazDebuggerFpLLdb":<br />
* It should be in the list of available packages, but if not it is in components/lazdebuggers/lazdebuggerfplldb<br />
* Make sure to use the one with "Fp" in the name. (There also is LazDebuggerLLdb, but it is not as good.)<br />
* Restart the IDE.<br />
<br />
;Go to Tools > Options > Debugger:<br />
* In the "debugger type" dropdown, you should find and select "LLDB debugger (with fpdebug)"<br />
* The edit below this (where you normally have the path to gdb), should be changed to the path of lldb.<br />
: (lldb comes with the tools from Apple; and as such it is already codesigned by Apple).<br />
<br />
In case of problems please see: http://forum.lazarus-ide.org/index.php/topic,42869.0.html<br />
<br />
=== GDB based Debugger ===<br />
<br />
;GDB Debugger new options:<br />
<br />
* Added option "FixStackFrameForFpcAssert" to workaround fpc wrong frame pointer (display correct line after assert failed)<br />
* Added option "FixIncorrectStepOver" to workaround "step over" issues experienced on some platforms (step-over acts like step-in) http://forum.lazarus-ide.org/index.php/topic,44121<br />
* Added option "AssemblerStyle": ATT vs Intel<br />
* Added option "DisableStartupShell": Required on MacOs.<br />
* Added more size limits for data evaluation (avoid errors, timeouts and extremely slow responses)<br />
: Some users may experience a small speed gain, when setting both of the "...LengthForStaticArray" values to an equal value, and both of the "...ValueMemLimit" to an equal value (the mem and length may differ,this has no effect)<br />
:* MaxDisplayLengthForStaticArray: Similar to existing "MaxDisplayLengthForString". This should apply to static array, but it is up to gdb which types it applies it to.<br />
:* MaxLocalsLengthForStaticArray: The same, but applied while getting values for the locals window, and the function parameters show in the stack window. Should be more restrictive.<br />
:* GdbValueMemLimit: For all types. Do not evaluate values, if gdb would need more memory. Setting this to big may crash gdb. Big values may also lead to slow response times (several minutes during which the IDE would be blocked)<br />
:* GdbLocalsValueMemLimit: For locals and stack.<br />
* DebugServer: Added "target-download" if remote supports it.<br />
<br />
= IDE Interfaces Changes =<br />
<br />
* Added '''FormEditingHook.SaveComponentAsPascal''', which stores a designer form as Pascal statements using '''TCompWriterPas'''. You have various options to define the format and it tells you what units are needed for the Pascal code. There is an example adding a designer menu item to copy the Pascal statements to the clipboard ''examples/pascalstream/CopyAsPasPkg/copyaspasdemounit1.pas''.<br />
<br />
* Added interface class TLazCompilationToolOptions with a Command and CompileReasons. Used in CompilerOptions.ExecuteBefore and .ExecuteAfter.<br />
<br />
= Components =<br />
<br />
=== TOpenGLControl ===<br />
* New property Options of type set, currently with ocoMacRetinaMode as the only member. If set, ocoMacRetinaMode determines that the OpenGL controls will use retina support (high resolution mode).<br />
* Control can use Qt5 widgeset now, Qt4 is also fixed for modern OpenGL contexts.<br />
<br />
=== TAChart ===<br />
* The new TExpressionSeries and TExpressionColorMapSeries plot mathematical functions at design-time.<br />
* TLineSeries has a new property "ColorEach" which can be used to color the line segments individually.<br />
* TCubicSplineSeries has a new property "SplineType" which allows to select between the "natural" and the "monotone Hermite" splines (the latter avoiding overshoot of the interpolation).<br />
* TAreaSeris has a new property "Banded" which suppresses painting of the bottom layer of stacked areas and makes the series look like a band or a stack of bands.<br />
* The new property "MarkDistancePercent" of TPieSeries can be used, for example, to center a label within its pie for all chart sizes.<br />
* TLineSeries, TCubicSplineSeries, TBSplineSeries and TFitSeries now can display error bars.<br />
* The TFitSeries fitting engine was extended to be able to exclude specific parameters from fitting and to do a statistical analysis of the fit results (error estimates of the determined fit parameters, confidence limits, goodness of fit).<br />
* TBarSeries has new property "MarkPositionCentered" which allows to center series marks within each bar. Available also for TAreaSeries where it works accordingly. NOTE: In intermediate trunk versions and some of the release candidates, this effect could be achieved partly by using option lmpInsideCenter of TMarkPositions - this option has been dropped now.<br />
<br />
=== TCustomEdit ===<br />
* Qt5 widgetset native implementation of TextHint property, Qt4 uses emulated TextHint.<br />
<br />
= Changes affecting compatibility =<br />
<br />
==Installer==<br />
<br />
=== Debian package fpc was renamed to fpc-laz ===<br />
<br />
This about the packages provided by the Lazarus team on sourceforge.<br />
<br />
* Old: fpc_3.0.4-3_amd64.deb, fpc_3.0.4-3_i386.deb<br />
* New: fpc-laz_3.0.4-1_amd64.deb, fpc-laz_3.0.4-1_i386.deb<br />
* Reason: avoiding name clash with Ubuntu/Debian repository package "fpc"<br />
* Remedy: adapt your scripts<br />
<br />
Note, that the fpc-src package keeps its name, as the Ubuntu/Debian package is fpc-source, so there is no name clash here.<br />
<br />
==LazUtils==<br />
<br />
==LCL incompatibilities==<br />
<br />
=== TToolBar children ignore Align ===<br />
Controls placed within TToolBar cannot be aligned with the Align property.<br />
* Old behavior: The Align property of TToolBar children was taken into account.<br />
* New behavior: The Align property is ignored.<br />
* Reason: It caused endless alignment loops when the control's size was changed by code in Create.<br />
* Remedy: use a parent container for TToolBar and align the controls within this container.<br />
<br />
=== TCustomComboBox.ReadOnly was deprecated ===<br />
* Old behavior: When True, only items from the list are accepted, by direct selection from the list or AutoComplete.<br />
* New behavior: it does nothing and will be removed.<br />
* Reason: Delphi-compatibility, confusing naming, WS compatibility (different behavior on Win32/Qt/Gtk)<br />
* Remedy: Use extended styles for the same feature.<br />
<br />
=== Predefined clipboard format pcfDelphiBitmap was removed ===<br />
* Old behavior: The enumeration TPredefineClipboardFormat contained an element pcfDelphiBitmap which once had to be introduced due to streaming differences of bitmaps between Delphi and LCL. <br />
* New behavior: pcfDelphiBitmap has been removed. (Rare) code relying on exact count and position of the elements of this enumeration will fail.<br />
* Reason: No longer needed and causing trouble in clipboard access.<br />
* Remedy: None - revisit your code.<br />
<br />
=== TEdit.Action visibility lowered to public ===<br />
* Old behavior: TEdit.Action was published.<br />
* New behavior: TEdit.Action is now public.<br />
* Reason: Delphi compatibility.<br />
* Remedy: set Action in code.<br />
<br />
=== [[TControl]].ScaleFontsPPI, .DoScaleFontPPI parameter change ===<br />
<br />
* Old behavior: No AToPPI parameter<br />
* New behavior: AToPPI parameter was added<br />
* Reason: font scaling problem {{MantisLink|32882}}. This change will be merged to 1.8.1<br />
* Remedy: fix parameters<br />
<br />
=== MouseEntered deprecated/missing ===<br />
<br />
* Old behavior: No warning on using MouseEntered<br />
* New behavior: Warning: Symbol "MouseEntered" is deprecated: "use MouseInClient instead"<br />
* Reason: Delphi compatibility<br />
* Remedy: use property MouseInClient instead<br />
<br />
=== TCustomImageList.Add method ===<br />
* Old behavior: the image got sliced if too big or extended if too small.<br />
* New behavior: the image is scaled to all resolutions in the image list.<br />
* Reason: Image List now supports multiple resolutions.<br />
* Remedy: use AddSliced (if the image consists of several icons to be added) or AddSlice (if one image from a custom rect has to be added - also rect outside the image is supported).<br />
<br />
=== TBitBtn.Glyph in combination with TBitBtn.Kind ===<br />
* Old behavior: The Glyph property was filled with a valid bitmap when the Kind property was set to a different value from bkCustom.<br />
* New behavior: The Glyph property is empty.<br />
* Reason: TBitBtn now supports image list with multiple resolutions. The Glyph (TBitmap) doesn't support multiple resolutions and thus cannot be used anymore to define the TBitBtn image.<br />
* Remedy: if you need the image for a specific TBitBtn.Kind get it directly from the LCLGlyphs property defined in ImgList.<br />
<br />
=== TCustomTreeView.OnChanging event: Node parameter ===<br />
* Old behvior: The parameter Node passed to the OnChanging event points to the currently selected node. This is not compatible with Delphi which has the destination node here.<br />
* New behavior: The parameter Node passed to the OnChanging event is the node which will be the selected node after the node-changing operation has completed.<br />
* Reasons:<br />
** The operation can be aborted by setting "AllowChange" to false. For this decision, knowledge of the node going to be selected is helpfull, at least more helpful than knowledge of the node to be left which still can be accessed as TreeView.Selected at this point. The old behavior does not tell the new node.<br />
** The new behavior is compatible with Delphi<br />
* Remedy: If old OnChanging handlers refere to the parameter Node replace Node by TreeView.Selected.<br />
<br />
=== TCustomDialog properties Width and Height visibility lowered to public ===<br />
* Old behaviour: Width and Height were published.<br />
* New behaviour: Width and Height are public.<br />
* Reason: Delphi compatibility.<br />
* Remedy: set the properties in code.<br />
<br />
=== No LCL Application exception dump ===<br />
* Old behavior: In case of an exception a dump was automatically written with DebugLn (e.g. to a console).<br />
* New behavior: There is no automatic exception dump any more.<br />
* Reason: The LCL should not emit debugging info by default.<br />
* Remedy 1: Add the LCLExceptionStackTrace unit to the uses clause of any of your units.<br />
* Remedy 2: To enable LCLExceptionStacktrace globally for all projects, create a new "Additions and Overrides" entry in "Stored in IDE" for "#project" target with the contents "-FaLCLExceptionStacktrace" (see screenshot below). (The drawback of this approach is that this will now also be used for console applications, so they won't compile unless you uncheck the addition in Compiler Options.)<br />
<br />
=== No default LazLogger ===<br />
* Old behavior: LazLogger was registered by default by the LCL.<br />
* New behavior: LazLogger is not registered any more, so there is an empty logger (LazLoggerBase) used with no output.<br />
* Reason: The LCL should not register any logger by default - the user has to define if he wants to use the logger and what logger he wants to use.<br />
* Remedy 1: Add the LazLogger unit to the uses clause of any of your units.<br />
* Remedy 2: To enable LazLogger globally for all projects, create a new "Additions and Overrides" entry in "Stored in IDE" for "#project" target with the contents "-FaLazLogger" (see screenshot below). (The drawback of this approach is that this will now also be used for console applications, so they won't compile unless you uncheck the addition in Compiler Options.)<br />
<br />
=== Screenshot for LCLExceptionStackTrace and LazLogger Additions and Overrides ===<br />
[[File:AdditionsOverridesExUnits.png]]<br />
<br />
=== TCustomTabControl setting TabIndex or PageIndex by code ===<br />
* Old behavior: setting TabIndex or PageIndex by code would invoke a call to CanChangePageIndex/CanChange<br />
* New behavior: setting TabIndex or PageIndex by code will not invoke CanChangePageIndex/CanChange. As a result OnChanging is not called in this scenario.<br />
* Reason: Delphi compatibility.<br />
* Remedy: set nboDoChangeOnSetIndex in Options (this option is not available in Delphi), call OnChanging in you own code when changing TabIndex/PageIndex, or move the code in OnChanging (or an overridden CanChangePageIndex/CanChange) to another event (or method) if applicable.<br />
<br />
==Components incompatibilities==<br />
<br />
===LazControls: TSpinEditEx no longer inherits from TCustomFloatSpinEditEx===<br />
* Old behavior: TSpinEditEx inherited from TFloatSpinEditEx.<br />
* New behavior: Both TSpinEditEx and TFloatSpinEditEx now inherit from a common (generic) base class: TSpinEditExBase. As a result testing for "is TCustomFloatSpinEdit" now returns False.<br />
* Reason: support for Int64 values in TSpinEdit (Double does not have enough precision to cover the Int64 range). Also makes for cleaner code.<br />
* Remedy: test for the appropriate (base) class instead.<br />
<br />
=== TAChart: Reticule-related properties deprecated ===<br />
* Old behavior: The reticule could be used to display a crosshair cursor in the chart.<br />
* New behavior: The reticule will be removed because the crosshair cursor is included in the more versatile charttools.<br />
* Reason: Remove duplicate features.<br />
* Remedy: Attach a TChartToolset to the chart, add a DatapointCrosshairTool to the toolset and adjust its properties.<br />
<br />
=== TAChart events On[After|Before]Draw[Background|BackwallWall] deprecated ===<br />
* Old behavior: These events could be used for custom-painting of the chart background or the chart data rectangle background.<br />
* New behavior: These events will be removed.<br />
* Reason: The events have a parameter Canvas to paint on. TAChart, however, supports additional drawing backends which do not have a canvas.<br />
* Remedy: Use the new events On[After|Before]CustomDraw[Background|Backwall] which get the currenty used drawer as a parameter. For drawing use the corresponding drawer methods.<br />
<br />
=== TAChart: The TCubicSplineOption <code>csoDrawFewPoints</code> is removed. ===<br />
* Old behavior: The CubicSplineSeries required at least 4 data points for drawing. In case of a TCubicSplineSeries with less data points a polygon was drawn as a replacement using the <code>BadDataPen</code> if the option <code>csoDrawFewDataPoints</code> was active; if this option was not set the series was not drawn at all.<br />
* New behavior: After fixing the cubic spline interpolation in a local version if NumLib's ipf unit it is possible to draw splines also for less than 4 data points. Old code using the <code>csoDrawFewDatapoints</code> option will not compile any more.<br />
* Reason: The old behavior was a workaround for a NumLib bug.<br />
* Remedy: Don't use <code>csoDrawFewPoints</code> any more.<br />
<br />
=== TAChart: Other properties removed that had been marked as deprecated for a long time ===<br />
* The following table lists the removed properties/procedures/functions along with their replacements:<br />
{| class="wikitable"<br />
|-<br />
! removed<br />
! use instead<br />
|-<br />
| property <code>TChartTitle.Font</code> (unit TAChartAxisUtils)<br />
| <code>TChartTitle.LabelFont</code><br />
|-<br />
| property <code>TCustomChartSeries.ShowInLegend</code> (unit TACustomSeries)<br />
| <code>TCustomChartSeries.Legend.Visible</code><br />
|-<br />
| method <code>TFuncSeries.GetFitEquationText</code> (unit TAFuncSeries)<br />
| <code>TFuncSeries.EquationText</code><br />
|-<br />
| method <code>TChart.DrawOnCanvas</code> (unit TAGraph)<br />
| <code>TChart.PaintOnCanvas</code><br />
|-<br />
| property <code>TChartLegend.Margin</code> (unit TALegend)<br />
| <code>TChartLegend.MarginX</code><br />
|-<br />
| event <code>TLineSeries.OnDrawPointer</code> (unit TASeries)<br />
| <code>TLineSeries.OnCustomDrawPointer</code><br />
|- <br />
| class <code>TSerie</code> (unit TASeries)<br />
| <code>TLineSeries</code><br />
|-<br />
| class <code>TLine</code> (unit TASeries)<br />
| <code>TConstantLine</code><br />
|-<br />
| property <code>TZoomDragTool.Proportional</code> (unit TATools)<br />
| <code>TZoomDragTool.RatioLimit</code>, set to <code>zlrProportional</code><br />
|}<br />
<br />
=== TCustomGrid: The var parameter ABitmap in event OnUserCheckBoxBitmap used to be preassigned with default bitmap, but now it is nil. ===<br />
* Old behaviour: You could draw directly to ABitmap in OnUserCheckBoxBitmap event.<br />
* New behaviour: You have to create a bitmap yourself and assign it to ABitmap parameter in OnUserCheckBoxBitmap event.<br />
* reason: this is var parameter and its proper usage has always been this way.<br />
* remedy: change code to provide the bitmap and assign it to ABitmap parameter in code of OnUserCheckBoxBitmap.<br />
More information in [https://bugs.freepascal.org/view.php?id=35085 bugreport 35085].<br />
<br />
==IDE incompatibilities==<br />
<br />
= Previous release notes =<br />
<br />
*[[Lazarus 1.8.0 release notes]]<br />
*[[Lazarus 1.6.0 release notes]]<br />
*[[Lazarus 1.4.0 release notes]]<br />
*[[Lazarus 1.2.0 release notes]]<br />
*[[Lazarus 1.0 release notes]]<br />
<br />
{{Navbar Lazarus Release Notes}}<br />
<br />
[[Category:Release Notes]]<br />
[[Category:Lazarus]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=Office_Automation&diff=115416Office Automation2018-01-27T20:37:51Z<p>Zoran: Fix broken link</p>
<hr />
<div>{{Platform only|Windows|Windows|Windows}}<br />
{{Office Automation}}<br />
<br />
The ability to interact with Office software and generate spreadsheets, text documents and presentations from code can be invaluable in the office, and save a lot of time otherwise spent on repetitive tasks. <br />
<br />
One example of this is the creation of applications that can read files in an arbitrary format and output an Excel file, a task that can be done much more efficiently with code than manually.<br />
<br />
== Using the OpenOffice UNO Bridge ==<br />
OpenOffice has language bindings for C++, Java, JavaScript and Python. On Windows, OpenOffice can also be manipulated in Pascal via COM Automation (see below), but there is currently no easy way of using OpenOffice's UNO (Universal Network Objects) from Pascal on OS X and Linux. If you're interested in developing an OO "bridge" for Pascal, please refer to these links for more information (caution: these links are quite techie in true Sun fashion):<br />
<br />
[http://api.openoffice.org/ api.openoffice.org]<br />
<br />
[http://wiki.services.openoffice.org/wiki/Uno/Article/About_Bridges About Bridges]<br />
<br />
== Using COM Automation to interact with OpenOffice and Microsoft Office==<br />
Automation is unique to Windows so the following two examples won't work on OS X or Linux. For those platforms, please refer to [http://wiki.lazarus.freepascal.org/Multiplatform_Programming_Guide#Making_do_without_Windows_COM_Automation Making do without Windows COM Automation]. If you only need to create and/or view a word processing document from your program, take a look at the [[XDev Toolkit]].<br />
<br />
=== OpenOffice/LibreOffice on Windows ===<br />
Here's a simple example of how to open a document with your program using the OpenOffice Automation server. Note that this works only on Windows.<br />
<br />
<syntaxhighlight>program TestOO;<br />
<br />
{$IFDEF FPC}<br />
{$MODE Delphi}<br />
{$ELSE}<br />
{$APPTYPE CONSOLE}<br />
{$ENDIF} <br />
<br />
uses<br />
SysUtils, Variants, ComObj;<br />
<br />
const<br />
ServerName = 'com.sun.star.ServiceManager';<br />
var <br />
Server : Variant;<br />
Desktop : Variant;<br />
LoadParams : Variant;<br />
Document : Variant;<br />
TextCursor : Variant;<br />
begin<br />
if Assigned(InitProc) then<br />
TProcedure(InitProc);<br />
<br />
try<br />
Server := CreateOleObject(ServerName);<br />
except<br />
WriteLn('Unable to start OO.');<br />
Exit;<br />
end;<br />
<br />
Desktop := Server.CreateInstance('com.sun.star.frame.Desktop');<br />
<br />
LoadParams := VarArrayCreate([0, -1], varVariant);<br />
<br />
{Create new document}<br />
Document := Desktop.LoadComponentFromURL('private:factory/swriter', '_blank', 0, LoadParams);<br />
<br />
{or Open existing} //you must use forward slashes, not backward!<br />
//Document := Desktop.LoadComponentFromURL('file:///C:/my/path/mydoc.doc', '_blank', 0, LoadParams); <br />
<br />
TextCursor := Document.Text.CreateTextCursor;<br />
<br />
{Insert existing document} //Substitute your path and doc<br />
TextCursor.InsertDocumentFromURL('file:///C:/my/path/mydoc.doc', LoadParams);<br />
end.</syntaxhighlight><br />
<br />
More comprehensive examples can found [https://bitbucket.org/blikblum/pascal-demos/src/04a4d10bf30b59215ff3ad4cd41dba71405ae5fc/fpc/LibreOfficeOLE/?at=master here]:<br />
* OLECreateTextDoc: Create and populate a text document with paragraph, table and frame<br />
* OLEEditTextDoc: Load an existing text document, search with regular expression, replace text and add rows to a table<br />
<br />
=== Office on Windows ===<br />
Here's a simple example of how to open a document with your program using the Word Automation server. Note that this example works only on Windows. This will work with both delphi and fpc.<br />
<br />
<syntaxhighlight>program TestMsOffice;<br />
<br />
{$IFDEF FPC}<br />
{$MODE Delphi}<br />
{$ELSE}<br />
{$APPTYPE CONSOLE}<br />
{$ENDIF} <br />
<br />
uses<br />
SysUtils, Variants, ComObj;<br />
<br />
const<br />
ServerName = 'Word.Application';<br />
var<br />
Server : Variant;<br />
w:widestring;<br />
begin<br />
if Assigned(InitProc) then<br />
TProcedure(InitProc);<br />
<br />
try<br />
Server := CreateOleObject(ServerName);<br />
except<br />
WriteLn('Unable to start Word.');<br />
Exit;<br />
end;<br />
<br />
{Open existing document} //Substitute your path and doc<br />
w:= UTF8Decode('c:\my\path\mydoc.doc');<br />
Server.Documents.Open(w); //OLE uses BSTR (http://msdn.microsoft.com/en-us/library/windows/desktop/ms221069(v=vs.85).aspx). Only widestring is compatible with BSTR in FPC, so conversion is needed for nonlatin chars.<br />
Server.Visible := True; {Make Word visible}<br />
<br />
end.</syntaxhighlight><br />
<br />
<br />
Here is a sample code how to work in an open Word document, using the Word Automation server.<br />
<syntaxhighlight><br />
var<br />
Server: Variant;<br />
begin<br />
try<br />
Server := GetActiveOleObject('Word.Application');<br />
except<br />
try<br />
ShowMessage('Word not already open create a Word Object');<br />
// If no instance of Word is running, try to Create a new Word Object<br />
Server := CreateOleObject('Word.Application');<br />
except<br />
ShowMessage('Cannot start Word/Word not installed ?');<br />
Exit;<br />
end;<br />
end;<br />
end; <br />
</syntaxhighlight><br />
<br />
Limitations:<br />
Since '''End''' is a reserved word in FPC it shall be used as a parameter after the ''&'' sign.<br />
<syntaxhighlight><br />
Server.ActiveDocument.Application.Selection.start:=Server.ActiveDocument.Application.Selection.&end+1;<br />
</syntaxhighlight><br />
<br />
<br />
A lot of examples for Excel are available on the German wiki page [[ExcelAutomation/de]].<br />
<br />
== Using the fpXMLXSDExport unit ==<br />
FPC 2.6 and newer contain the '''fpXMLXSDExport''' unit, part of the FCL-DB export components. With that, you can export datasets to various XML formats, including a Microsoft Access-compatible format and a Microsoft Excel-compatible format.<br />
<br />
The Access format can output XML with or without an embedded XSD data/table definition. Note that exporting binary/BLOB type data needs additional action at the Access import end, as Access does not support proper binary fields, only OLE fields.<br />
<br />
In the Excel format, multiline text fields are not supported at the moment: the line ends are removed during the export.<br />
<br />
Lazarus provides a visual component for this: after installing the '''lazdbexport''' package, you will see the '''TXMLXSDExporter''' component on the '''Data Export''' tab<br />
<br />
See [[fpXMLXSDExport]] for details.<br />
<br />
== Using the FPSpreadsheet Library ==<br />
<br />
Another way to automate repetitive work with spreadsheets is to use the [[FPSpreadsheet]] library. It can read and write spreadsheets in several formats and it doesn't require having any external application installed on the machine.<br />
<br />
The advantages are that fpspreadsheet is 100% [[Object Pascal]] code, and it requires no external libraries or programs.<br />
<br />
== Writing an Excel file using ADO ==<br />
please write me.<br />
<br />
== Reading/Writing an Excel file using OLE ==<br />
<br />
This method needs Excel to be installed on the user's machine because it uses OLE to access it.<br />
<br />
'''''Keep in mind that this method starts Excel in the background, which opens the file and works with it like a real user.''<br />
'''<br />
<br />
* Create a new form with button, stringgrid and edit.<br />
* Create a new Excel file and fill a few cells.<br />
<br />
<br />
[[File:excel123.png]]<br />
<br />
<br />
Example - Open/Read Excel file:<br />
<syntaxhighlight><br />
uses ..... comobj;<br />
<br />
procedure TForm1.Button1Click(Sender: TObject);<br />
<br />
Var XLApp: OLEVariant;<br />
x,y: byte;<br />
path: variant;<br />
<br />
begin<br />
XLApp := CreateOleObject('Excel.Application'); // requires comobj in uses<br />
try<br />
XLApp.Visible := False; // Hide Excel<br />
XLApp.DisplayAlerts := False;<br />
path := edit1.Text;<br />
XLApp.Workbooks.Open(Path); // Open the Workbook<br />
for x := 1 to 4 do<br />
begin<br />
for y := 1 to 6 do<br />
begin<br />
SG.Cells[x,y] := XLApp.Cells[y,x].Value; // fill stringgrid with values<br />
end;<br />
end;<br />
finally<br />
XLApp.Quit;<br />
XLAPP := Unassigned;<br />
end; <br />
</syntaxhighlight><br />
<br />
If you want to make some changes and you want them to write back into the Excel, file you can use:<br />
<br />
<syntaxhighlight><br />
XLApp.Cells[x,y].Value := SG.Cells[y,x];<br />
</syntaxhighlight><br />
<br />
If you want to save:<br />
<syntaxhighlight><br />
XLApp.ActiveWorkBook.Save;<br />
</syntaxhighlight><br />
<br />
== Read/Writing an Excel file using the SpreadSheet Interface Component ==<br />
<br />
The component provides a library interface, abstracting the Excel COM and the Calc Open Office UNO interfaces.<br />
The component is available here:<br />
http://tcoq.free.fr/composants.html (Link verified in May 2016)<br />
<br />
Since Automation is not yet available, but COM is available, the Excel interface component provides a set of Lazarus classes encapsulating calls to the Excel COM interface (the one below the Automation). It hides most of the drudgery of working with low-level code.<br />
Be careful, this is a work-in-progress. Use it at your own risk.<br />
<br />
'''Functionality:'''<br />
<br />
* creating and loading excel workbooks,<br />
* saving workbooks,<br />
* creating and accessing sheets,<br />
* getting values and setting values (and formulas) in cells,<br />
* getting and changing color of cells,<br />
* getting and changing column height and row width,<br />
* creating comments,<br />
* creating shapes,<br />
* creating charts.<br />
<br />
'''Inits first.'''<br />
<br />
<syntaxhighlight> IMPLEMENTATION<br />
USES<br />
ExcelUtilities,<br />
SpreadSheetInterfaces ;<br />
<br />
VAR<br />
aCell : IRange ;<br />
aValue : OleVariant ; // Not sure about this, but it works. ie( Edit.Text := STRING(aValue); )<br />
ExcelApp : TExcelApplication ;<br />
ExcelWbs : IWorkBooks ;<br />
<br />
ExcelBook : IWorkBook ;<br />
ExcelSheet : ISheet ;<br />
ExcelSheets : ISheets ;</syntaxhighlight><br />
<br />
Getting a sheet is simple:<br />
<syntaxhighlight> // Initializing the common excel workbook:<br />
ExcelApp := TExcelApplication.Create(nil) ;<br />
ExcelApp.Active := True ;<br />
ExcelApp.Visible := True ;<br />
<br />
ExcelWbs := ExcelApp.WorkBooks ;<br />
ExcelBook := ExcelWbs.Add ;<br />
ExcelSheets := ExcelBook.Sheets ;<br />
ExcelSheet := ExcelSheets.Sheet(1) ;</syntaxhighlight><br />
<br />
Playing around with cells is simple too:<br />
<syntaxhighlight> // adding a value<br />
aCell := ExcelSheet.Cells(1, 1) ;<br />
aCell.Value := 10;<br />
<br />
// adding a formula<br />
aCell := ExcelSheet.Cells(2,1) ;<br />
aCell.Formula := '=A1+10' ;<br />
<br />
// getting the value computed in Excel<br />
aValue := aCell.Value ;</syntaxhighlight><br />
<br />
<br />
The test case provided has many more examples.<br />
<br />
==Copy HTML to the clipboard==<br />
<br />
<p>You can copy HTML to the clipboard which is understood by many applications. This way you can copy formatted text. For those applications that only understand text put plain text too.</p><br />
<p>Microsoft Office applications require HTML to be pasted onto the clipboard in a more complex format than described here. [[Clipboard#Windows|See here for an example that works with Microsoft Office.]]</p><br />
<br />
<br />
<syntaxhighlight>uses<br />
ClipBrd;<br />
...<br />
// register the mime type for text/html. You can do this once at program start:<br />
ClipbrdFmtHTML:=RegisterClipboardFormat('text/html');<br />
...<br />
// Clear any previous formats off the clipboard before starting<br />
Clipboard.Clear;<br />
<br />
// put text and html on the clipboard. Other applications will choose the best format automatically.<br />
ThePlainUTF8Text:='Simple text';<br />
Clipboard.AsText:=ThePlainUTF8Text; <br />
<br />
AsHTML:='<b>Formatted</b> text'; // text with formattings<br />
Clipboard.AddFormat(ClipbrdFmtHTML,AsHTML[1],length(AsHTML));</syntaxhighlight><br />
<br />
==See also==<br />
<br />
* [[Clipboard]]<br />
<br />
== External links ==<br />
<br />
* [http://sc.openoffice.org/excelfileformat.pdf Excel file format] - description on OpenOffice website<br />
<br />
[[Category:Tutorials]]<br />
[[Category:Data import and export]]<br />
[[Category:FPC]] <!-- yes this is really relevant to FPC as it can be done without Lazarus --><br />
[[Category:Lazarus]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=FPSpreadsheet_tutorial:_Writing_a_mini_spreadsheet_application&diff=115101FPSpreadsheet tutorial: Writing a mini spreadsheet application2018-01-13T20:25:33Z<p>Zoran: /* TsWorksheetGrid */</p>
<hr />
<div>{{FPSpreadsheet tutorial: Writing a mini spreadsheet application}}<br />
<br />
== Introduction ==<br />
[[FPSpreadsheet]] is a powerful package for reading and writing spreadsheet files. The main intention is to provide a platform which is capable of native export/import of an application's data to/from the most important spreadsheet file formats without having these spreadsheet applications installed. <br />
<br />
Soon, however, the wish arises to use this package also for editing of file content or formatting. For this purpose, the library contains a dedicated grid control, the FPSpreadsheetGrid, which closely resembles the features of a worksheet of a spreadsheet application. The demo "spready" which comes along with FPSpreadsheet demonstrates usage of this grid. Along with a bunch of formatting options, this demo still comes up to more than 1400 lines of code in the main form unit. Therefore, a set of visual controls was developed which greatly simplify creation of spreadsheet applications. <br />
<br />
It is the intention of this tutorial to write a simple spreadsheet program on the basis of these controls.<br />
<br />
Although most of the internal structure of the FPSpreadsheet library is covered by the visual controls it is recommended that you have some knowledge of FPSpreadsheet. Of course, you should not have a basic understanding of Lazarus and FPC, and you must know how to work with the object inspector of Lazarus.<br />
<br />
== Visual FPSpreadsheet Controls ==<br />
FPSpreadsheet exposes non-visual classes, such as TsWorkbook, TsWorksheet etc. This keeps the library general enough for all kind of Pascal programs. For GUI programs, on the other hand, some infrastructure is needed which relates the spreadsheets to forms, grids, and other controls.<br />
<br />
=== TsWorkbookSource ===<br />
[[file:TSWORKBOOKSOURCE.png|left]] <br />
The heart of the visual FPSpreadsheet controls is the '''TsWorkbookSource''' class. This provides a link between the non-visual spreadsheet data and the visual controls on the form. Its purpose is similar to that of a TDataSource component in database applications which links database tables or queries to dedicated "data-aware" controls. <br />
<br />
All visual FPSpreadsheet controls have a property <tt>WorkbookSource</tt> which links them into the information chain provided by the TsWorkbookSource. The WorkbookSource keeps a list of all controls attached. Internally, these controls are called "listeners" because they listen to information distributed by the WorkbookSource.<br />
<br />
The workbook and worksheets use events to notify the WorkbookSource of all relevant changes: changes in cell content or formatting, selecting other cells, adding or deleting worksheet etc. Information on these changes is passed on to the listening controls, and they react in their own specialized way on these changes. If, for example, a new worksheet is added to a workbook the visual TsWorkbookTabControl creates a new tab for the new worksheet, and the TsWorksheetGrid loads the new worksheet into the grid.<br />
<br />
=== TsWorkbookTabControl ===<br />
[[file:TSWORKBOOKTABCONTROL.png|left]]<br />
This is a tabcontrol which provides a tab for each worksheet of the current workbook. The tab names are identical with the names of the worksheets. Selecting another tab is communicated to the other visual spreadsheet controls via the WorkbookSource.<br />
<br />
=== TsWorksheetGrid ===<br />
[[file:TSWORKSHEETGRID.png|left]]<br />
This is a customized DrawGrid descendant of the LCL and displays cells of the currently selected worksheet. The texts are not stored in the grid (like a StringGrid would do), but are taken from the TsWorksheet data structure. Similarly, the worksheet provides the information of how each cell is formatted. Like any LCL grid it has a bunch of properties and can be tuned for many applications by adapting its <tt>Options</tt>. The most important one will be described below. <br />
{{Note|The <tt>TsWorksheetGrid</tt> can also be operated without a <tt>TsWorkbookSource</tt>. For this purpose it provides its own set of methods for reading and writing files.}}<br />
<br />
=== TsCellEdit ===<br />
[[file:TSCELLEDIT.png|left]]<br />
The typical spreadsheet applications provide a line for editing formulas or cell content. This is the purpose of the '''TsCellEdit'''. It displays the content of the active cell of the worksheet which is the same as the active cell of the WorksheetGrid. If editing is finished (by pressing {{keypress|ENTER}}, or by selecting another cell in the WorksheetGrid) the new cell value is transferred to the worksheet. Internally, the TsCellEdit is a memo control, i.e. it is able to process multi-line text correctly. Use {{keypress|Ctrl}}+{{keypress|ENTER}} to insert a forced line-break.<br />
<br />
=== TsCellIndicator ===<br />
[[file:TSCELLINDICATOR.png|left]]<br />
This is a TEdit control which displays the address of the currently selected cell in Excel notation, e.g. 'A1' if the active cell is in the first row and first column (row = 0, column = 0). Conversely, if a valid cell address is entered into this control the corresponding cell becomes active. <br />
<br />
=== TsCellCombobox ===<br />
[[file:TSCELLCOMBOBOX.png|left]]This combobox can be used to modify various cell properties by selecting values from the dropdown list. The property affected is determined by the <tt>CellFormatItem</tt> of the combobox:<br />
* <tt>cfiFontName</tt>: the list contains he names of all fonts available on the current system. If an item is selected the corresponding font is used to format the cell of the currently selected cells.<br />
* <tt>cfiFontSize</tt>: the list contains the most typical font sizes used in spreadsheets. Selecting an item sets the font size of the currently selected cells accordingly.<br />
* <tt>cfiFontColor</tt>: the list contains all colors of the workbook's palette. The selected color is assigned to the font of the selected cells.<br />
* <tt>cfiBackgroundColor</tt>: like <tt>cfiFontColor</tt> - the selected color is used as background fill color of the selected cells.<br />
<br />
=== TsSpreadsheetInspector ===<br />
[[file:TSSPREADSHEETINSPECTOR.png|left]]<br />
Inherits from TValueListEditor and displays name-value pairs for properties of the workbook, the selected worksheet, and the content and formatting of the active cell. It's main purpose is to help with debugging.<br />
<br />
== Writing a spreadsheet application ==<br />
Enough of theory, let's get started. Let's write a small spreadsheet application. Sure - it cannot compete with the spreadsheets of the main Office applications like Excel or Open/LibreOffice, but it has all the main ingredients due to FPSpreadsheet. And using the FPSpreadsheet controls allows to achieve this with minimum lines of code.<br />
<br />
=== Preparations ===<br />
[[file:fpspreadsheetcontrols_preparations.png|right|400px]]<br />
Create a new project and store it in a folder of your liking. <br />
<br />
Since Office applications have a menu and a toolbar add a '''TMainMenu''' and a '''TToolbar''' component to the form. (You could even mimic the ribbon user interface of the new Microsoft applications by adding a '''TSpkToolbar''' from [http://sourceforge.net/p/lazarus-ccr/svn/HEAD/tree/components/spktoolbar/ Lazarus Code and Components Repository], but be aware that this component does not yet provide all the features of a standard toolbar).<br />
<br />
In fact, we will be needing '''another toolbar''' for the formula edit line. As you will see later, it will be resizable; as size control add a '''TSplitter''' to the form and top-align it such that it is positioned underneath the two toolbars. In order to keep a minimum size of the toolbar you should establish constraints: Look at the current height of the toolbar and enter this number into the <tt>MinHeight</tt> field of the <tt>Constraints</tt> property of the toolbar. To separate the formula toolbar from the rest of the main form, activate the option <tt>ebBottom</tt> of the <tt>EdgeBorders</tt> property of the second toolbar.<br />
<br />
Since menu and toolbars will have to handle same user actions it is advantageous to provide a '''TActionList''' to store all possible actions. If assigned to the menu items and toolbuttons both will react on user interaction in the same way without any additional coding. And: The FPSpreadsheet visual controls package contains a bunch of spreadsheet-related standard actions ready to use.<br />
<br />
The toolbar of the completed application will contain a lot of of icons. Therefore, we need a '''TImageList''' component which has to be linked to the <tt>Images</tt> property of the TMainMenu, the TToolbars, and the TActionList. Where to get icons? You can have a look in the folder <tt>images</tt> of your Lazarus installation where you'll find standard icons for loading and saving etc. This is a subset of the [http://www.famfamfam.com/lab/icons/silk/ famfamfam SILK icon library]. Another huge icon set is the [http://p.yusukekamiyamane.com/ Fugue icon collection]. Both collections are licensed as "Creative commons" and are free even for commercial use, provided that appropriate reference is given in the created programs. When selecting icons prefer the png image format, and make sure to use always the same size, usually 16x16 pixels.<br />
<br />
=== Setting up the visual workbook ===<br />
<br />
==== TsWorkbookSource ====<br />
As described in the introductory section the '''TsWorkbookSource''' component is the interface between workbook and controls on the user interface. Add this component to the form and give it a decent name (we'll keep the default name <tt>sWorkbookSource1</tt> here, though). As you will see shortly, this component will have to be assigned to the property <tt>WorkbookSource</tt> of all controls of the FPSpreadsheet_visual package.<br />
<br />
The WorkbookSource is responsible for loading and writing data from/to file and for communicating with the workbook. Therefore, it owns a set of options that are passed to the workbook and control these processes:<br />
[[file:sTabControl.png|right|400px]]<br />
<syntaxhighlight><br />
type<br />
TsWorkbookOption = (boVirtualMode, boBufStream, boAutoCalc, boCalcBeforeSaving, boReadFormulas);<br />
TsWorkbookOptions = set of TsWorkbookOption;<br />
</syntaxhighlight><br />
<br />
The most important ones are<br />
* <tt>boAutoCalc</tt>: activates automatic calculation of formulas whenever cell content changes.<br />
* <tt>boCalcBeforeSaving</tt>: calculated formulas before a workbook is written to file<br />
* <tt>boReadFormulas</tt>: if set full formulas are read from the file, otherwise only formula results.<br />
* <tt>boBufStream</tt> and <tt>boVirtualMode</tt>: In non-visual programs, these options can help if running out of memory in case of large workbooks. <tt>boVirtualMode</tt>, in particular, is not usable for visual applications, though, because it avoids keeping data in the worksheet cells. See also [[FPSpreadsheet#Virtual_mode]].<br />
<br />
In this tutorial, it is assumed that the options <tt>boAutoCalc</tt> and <tt>boReadFormulas</tt> are activated.<br />
<br />
==== TsWorkbookTabControl ====<br />
The first visual control used in the form is a '''TsWorkbookTabControl''' - click it onto the form (into the space not occupied by the toolbar). Client-align it within the form, this shows the TabControl as a bright rectangle only. Now link its <tt>WorkbookSource</tt> property to the TsWorkbookSource component that we have added just before. Now the TabControl shows a tab labelled "Sheet1". This is because the TsWorkbookSource has created a dummy workkbook containing a single worksheet "Sheet1". The WorkbookSource synchronizes this internal workbook with the TabControl (and the other visual controls to come) such that it displays this worksheet as a tab. <br />
<br />
In Excel the worksheet tabs are at the bottom of the form - to achieve this effect you can set the property <tt>TabPosition</tt> of the TabControl to <tt>tpBottom</tt>; there are some painting issues of the LCL with this TabPosition, though, therefore, I prefer the default setting, <tt>tpTop</tt>.<br />
<br />
The screenshot shows how far we've got. <br clear="all" /><br />
<br />
==== TsWorksheetGrid ====<br />
[[file:sWorksheetGrid.png|right|400px]] Now we add a '''TsWorksheetGrid''' control. Click it somewhere into the space occupied by the TabControl such that it becomes a child a of the TabControl. You see a standard stringgrid-like component. Link its <tt>WorkbookSource</tt> property to the source added at the beginning, and the grid looks more like a spreadsheet: there are the column headers labelled by letters "A", "B", etc, and the row headers labelled by numbers "1", "2", etc; the active cell, A1, is marked by a thick border. <br />
<br />
You may want to switch the grid's <tt>TitleStyle</tt> to <tt>tsNative</tt> in order to achieve themed painting of the row and column headers. And here is a good place to adapt the grid's <tt>Options</tt> in order to activate many features well-known to spreadsheets:<br />
* <tt>goEditing</tt> must be active, otherwise the grid contents cannot be modified. <br />
* <tt>goAlwaysShowEditor</tt> should be off because it interferes with the editing convention of spreadsheet applications.<br />
* <tt>goColSizing</tt> enables changing of the column width by dragging the dividing line between adjacent column headers. Dragging occurs with the left mouse button pressed.<br />
* <tt>goRowSizing</tt> does the same with the row heights.<br />
* <tt>goDblClickAutoResize</tt> activates the feature that optimum column width can be set by double-clicking in the header on its dividing line to the next column. The "optimum" column width is such that no cell content is truncated and no extra space is shown in the column.<br />
* <tt>goHeaderHotTrack</tt> gives visual feedback if the mouse is above a header cell.<br />
* <tt>goRangeSelect</tt> (which is on by default) enables selection of a rectangular range of cells by dragging the mouse between cells at opposite corners of the rectangle. You can even select multiple rectangles by holding the CTRL key down before the next rectangle is dragged (in older Lazarus releases - before 1.4 - only a single range could be selected).<br />
* <tt>goThumbTracking</tt> activates immediate scrolling of the worksheet if one of the scrollbars is dragged with the mouse. The Office applications usually scroll by lines; you can achieve this by turning off <tt>goSmoothScroll</tt>.<br />
<br />
In addition to these <tt>Options</tt> inherited from <tt>TCustomGrid</tt> there are some more properties specialized for spreadsheet operation:<br />
* <tt>ShowGridLines</tt>, if <tt>false</tt>, hides the row and column grid lines.<br />
* <tt>ShowHeaders</tt> can be set to <tt>false</tt> if the the column and row headers are to be hidden. (The same can be achieved also by the deprecated property <tt>DisplayFixedColRow</tt>).<br />
* The LCL grids normally truncate text at the cell border if it is longer than the cell width. If <tt>TextOverflow</tt> is set to <tt>true</tt> then text can overflow into adjacent empty cells.<br />
<br />
The properties <tt>AutoCalc</tt> and <tt>ReadFormulas</tt> are meant for stand-alone usage of the WorksheetGrid (i.e. without a TsWorkbookSource). Please use the corresponding options of the WorkbookSource instead. (<tt>AutoCalc</tt> enables automatic calculation of formulas whenever cell content changes. <tt>ReadFormulas</tt> activates reading of formulas from files, otherwise the grid would display only the formula results).<br />
<br />
=== Editing of values and formulas, Navigating ===<br />
<br />
When you compile and run the program you'll already be able to enter data into the grid. Just select the cell that you want to edit by clicking or using the arrow keys - the active cell is highlighted by a thick border. Then begin typing. If the grid property <tt>EditorLineMode</tt> has been switched to <tt>elmMultiLine</tt> then manual line breaks can be entered by the key combination {{keypress|Ctrl}}+{{keypress|ENTER}}. When finished select another cell or press the {{keypress|ENTER}} key. Using {{keypress|ENTER}} automatically selects the next cell in the grid. The grid's property <tt>AutoAdvance</tt> defines what is understood as being the "next cell": by default, {{keypress|ENTER}} moves the active cell down (<tt>aaDown</tt>), but you can also move it to the right (<tt>aaRight</tt>), or turn this feature off (<tt>aaNone</tt>) - see the type <tt>TAutoAdvance</tt> defined in the unit <tt>grids.pas</tt> for even more options.<br />
<br />
If - as assumed above - the WorkbookSource option <tt>boAutoCalc</tt> is enabled the worksheet automatically supports calculation of '''formulas'''. As an example, go to cell A1, and enter the number <tt>10</tt>. Then, go to cell A2 and enter the formula <tt>=A1+10</tt>. The formula is automatically evaluated, and its result, <tt>20</tt>, is displayed in cell A2. <br />
<br />
When you navigate in the grid you may notice that cell A2 only displays the formula result, it seems that there is no way to modify the formula once it has been entered. No need to worry - press the key {{keypress|F2}} or click into the cell a second time to enter '''enhanced edit mode''' in which formulas are visible in the cell.<br />
<br />
[[file:sCellIndicator_sCellEdit.png|right|400px]]<br />
In order to edit formulas the Office applications offer a dedicated formula editor bar. Of course, fpspreadsheet has this feature, too. It is built into the '''TsCellEdit''' component which is set up such as to always show the full content of a cell. You remember the second toolbar from the "Preparations" section? This will house the TsCellEdit. But wait a minute - there's more to consider: Since formulas occasionally may get rather long the control should be capable of managing serval lines. The same with multi-lined text. TsCellEdit can do this since it is inherited from TCustomMemo which is a multi-line control. You also remember that we added a splitter to the form of the second toolbar? This is for height adjustment for the case that we want to use the multi-line feature of the TsCellEdit: just drag the splitter down to show more lines, or drag it upwards to stop at the height of a single line due to the MinHeight constraints that we had assigned to the toolbar.<br />
<br />
The TsCellEdit will cover all available space in the second toolbar. Before we add the TsCellEdit we can make life easier if we think about what else will be in the second toolbar. In Excel, there is an indicator which displays the address of the currently active cell. This is the purpose of the '''TsCellIndicator'''. Since its height should not change when the toolbar is dragged down we first add a '''TPanel''' to the second toolbar; reduce its <tt>Width</tt> to about 100 pixels, remove its <tt>Caption</tt> and set its <tt>BevelOuter</tt> to <tt>bvNone</tt>.<br />
<br />
Add the TsCellIndicator to this panel and align it to the top of the panel. Connect its <tt>WorkbookSource</tt> to the TsWorkbookSource control on the form, and immediately you'll see the text "A1", the address of the currenly selected cell.<br />
<br />
Sometimes it is desirable to change the width of this box at runtime. So, why not add a splitter to the second toolbar? Set its <tt>Align</tt> property to <tt>alLeft</tt>. The result is a bit strange: the splitter is at the very left edge of the toolbar, but you'd expect to see it at the right of the panel. This is because the panel is not aligned by default. Set the <tt>Align</tt> property of the panel to <tt>alLeft</tt> as well, and drag the splitter to the right of the panel. Now the splitter is at the correct position.<br />
<br />
Almost done now... We finally add a TsCellEdit component to the empty space of the toolbar. Client-align it so that it fills the entire rest of the toolbar. As usual, set its <tt>WorkbookSource</tt> property to the instance of the TsWorkbookSource on the the form.<br />
<br />
Compile and run. Play with the program:<br />
<br />
* Enter some dummy data. Navigate in the worksheet. You'll see that the CellIndicator always shows the address of the active cell. The contents of the active cell is displayed in the CellEdit box. The CellIndicator is not just a passive display of the current cell, it can also be edited. Type in the address of a cell which you want to become active, press {{keypress|ENTER}}, and see what happens...<br />
* Enter a formula. Navigate back into the formula cell - the formula is displayed in the CellEdit and can be changed there readily.<br />
* Enter multi-lined text - you can enforce a lineending in the CellEdit by holding the {{keypress|Ctrl}} key down when you press {{keypress|ENTER}}. The cell displays only one line of the text. Drag the horizontal splitter underneath the second toolbar down - the CellEdit shows all lines. Another way to see all lines of the text, is to adjust the cell height. You must have activated the grid <tt>Option</tt> <tt>goRowSizing</tt>. Then you can drag the lower dividing line of the row with the multi-line cell down to increase the row height - the missing lines now appear in the cell!<br />
<br />
=== Formatting of cells ===<br />
<br />
In addition to entering data the user usually wants to apply some formatting to the cells in order to enhance or group them. The worksheet grid is set up in such a way that its cells display the formats taken from the workbook. In addition, the visual FPSpreadsheet controls are able to store formatting attributes into the cell. Because of the notification mechanism via the WorkbookSource these formats are returned to the WorksheetGrid for display.<br />
<br />
==== Adding comboboxes for font name, font size, and font color ====<br />
[[file:sCellFontCombobox.png|right|400px]]<br />
In this section, we want to provide the possibility to modify the font of the cell texts by selecting its name, size and/or color. The visual FPSpreadsheet provide the flexible '''TsCellCombobox''' for this purpose. It has the property <tt>CellFormatItem</tt> which defines which attribute it controls:<br />
* <tt>cfiFontName</tt>: This option populates the combobox with all fonts found in the current system. The selected item is used for the type face in the selected cells.<br />
* <tt>cfiFontSize</tt> fills the combobox with the mostly used font sizes (in points). Again, the selected item defines the font size of the selected cells.<br />
* <tt>cfiFontColor</tt> adds all pre-defined colors ("palette") of the workbook to the combobox to set the text color of the selected cells. The combobox items consist of a little color box along with the color name. If the <tt>ColorRectWidth</tt> is set to <tt>-1</tt> the color name is dropped.<br />
* <tt>cfiBackgroundColor</tt>, the same with the background color of the selected cells.<br />
* <tt>cfiCellBorderColor</tt>, the same with the border color of the selected cells - this feature is currently not yet supported.<br />
<br />
Add three TsCellComboboxes to the first toolbar and set their <tt>CellFormatItem</tt> to <tt>cfiFontname</tt>, <tt>cfiFontSize</tt>, and <tt>cfiFontColor</tt>, respectively. Link their <tt>WorkbookSource</tt> property to the TsWorkbookSource on the form. You may want to increase the width of the font name combobox such that the longest font names are not cut off; the other comboboxes may become narrower. You may also want to turn off the color names of the third combobox by setting its <tt>ColorRectWidth</tt> to <tt>-1</tt>.<br />
<br />
That's all to modify fonts. Compile and run. Enter some text and play with these new features of the program.<br />
<br />
==== Using standard actions ====<br />
[[file:sFontStyleAction_selected.png|right|300px]]<br />
FPSpreadsheet supports a lot of formats that can be applied to cells, such as text alignment, text rotation, text font, or cell borders or background colors. Typical gui applications contain menu commands and/or toolbar buttons which are assigned to each of these properties and allow to set them by a simple mouse click. In addition, the state of these controls often reflects the properties of the active cell. For example, if there is a button for using a bold type-face this button should be drawn as being pressed if the active cell is bold, but as released if it is not. To simplify the coding of these tasks a large number of standard actions has been added to the library. <br />
<br />
* '''TsWorksheetAddAction''': adds an empty worksheet to the workbook. Specify its name in the <tt>NameMask</tt> property. The <tt>NameMask</tt> must contain the format specifier <tt>%d</tt> which is replaced at runtime by a number such that the worksheet name is unique.<br />
* '''TsWorksheetDeleteAction''': deletes the active worksheet from the workbook after a confirmation dialog. The last worksheet cannot be deleted.<br />
* '''TsWorksheetRenameAction''': renames the active worksheet.<br />
* '''TsCopyAction''': Copies the currently selected cells to an internal list ("CellClipboard") from where they can be pasted back into the spreadsheet to another location. The process can occur in a clipboard-manner ("copy"/"cut", then "paste") or in the way of the "copy brush" of the Office applications. The property <tt>CopyItem</tt> determines whether the entire cell, or only cell values, cell formulas, or cell formats are transferred. <br />
* '''TsFontStyleAction''': Modifies the font style of the selected cells. The property <tt>FontStyle</tt> defines whether the action makes the font bold, italic, underlined or striked-out. Normally each font style is handles by its own action. See the example below.<br />
* '''TsHorAlignmentAction''': Can be used to modify the horizontal alignment of text in the selected cells. Select <tt>HorAlignment</tt> to define which kind of alignment (left, center, right) is covered by the action. Like with the TsFontStyleAction, several actions should be provided to offer all available alignments. They are grouped in a mutually exclusive way like radiobuttons.<br />
* '''TsVertAlignmentAction''': Changes the vertical alignment of text in the selected cells: the kind of alignment is defined by the <tt>VertAlignment</tt> property. Again, these actions work like radiobuttons.<br />
* '''TsTextRotationAction''': Allows to specify the text orientation in the selected cells as defined by the property <tt>TextRotation</tt> in a mutially exclusive way.<br />
* '''TsWordWrapAction''': Activates the word-wrapping feature for the selected cells: if text is longer than the width of the cell (or height, if the text is rotated) then it is wrapped into multiple lines.<br />
* '''TsNumberFormatAction''': Defines the number format to be used for the selected cells. The format to be used is defined by the properties <tt>NumberFormat</tt> (such as <tt>nfFixed</tt>) for built-in formats, and <tt>NumberFormatStr</tt> for specialized formatting.<br />
* '''TsDecimalsAction''': Allows to increase or decrease the number of decimal places shown in the selected cells. The property <tt>Delta</tt> controls whether an increase (+1) or decrease (-1) is wanted.<br />
* '''TsCellBorderAction''': Allows to specify if a border will be drawn around the selected cells. The subproperties <tt>East</tt>, <tt>West</tt>, <tt>North</tt>, <tt>South</tt>, <tt>InnerHor</tt>, <tt>InnerVert</tt> of <tt>Borders</tt> define what the border will look like at each side of the cell range. Note that each rectangular range of cells is considere as a single block; the properties <tt>East</tt>, <tt>West</tt>, <tt>North</tt> and <tt>South</tt> are responsible for the outer borders of the entire block, inner borders are defined by <tt>InnerHor</tt> and <tt>InnerVert</tt>. Using these properties, borders can be switched on and off (<tt>Visible</tt>), and in addition, the line style and line color can be changed.<br />
* '''TsMergeAction''': If checked, the cells of each selected rectangular range are merged to a single block. Unchecking the action separates the block to individual cells. Note that the block's content and formatting is defined by the top-left cell of each block; content and formats of other cells will be lost.<br />
* '''TsCellProtectionAction''': comes in two flavors, one for protecting cells from modifications, and one for hiding formulas, depending on the value of the property <tt>Protection</tt>. Note that this action is effective only if worksheet protection has been enabled by calling <tt>Workbooksource.Worksheet.Protect(true)</tt>.<br />
<br />
==== Adding buttons for "Bold", "Italic", and "Underline" ====<br />
[[file:sFontStyleAction_in_ActionListEditor.png|right|350px]]<br />
If you have never worked with '''standard actions''' before here are some detailed '''step-by-step instructions'''. Let us stick to above example and provide the possibility to switch the font style of the selected cells to '''bold'''. The standard action which is responsible for this feature is the <tt>TsFontStyleAction</tt>. <br />
<br />
* At first, we add this action to the form: Double-click on the '''TActionList''' to open the "ActionList Editor". <br />
* Click on the down-arrow next to the "+" button, and select the item "New standard action" from the drop-down menu. <br />
* This opens a dialog with the list of registered "Standard Action Classes". <br />
* Scroll down until you find a group named "FPSpreadsheet". <br />
* In this group, select the item "TsFontStyleAction" by double-clicking. <br />
* Now an item <tt>sFontStyleAction1</tt> appears in the ActionList Editor. <br />
* It should already be selected like in the screenshot at the right. If not, select <tt>sFontStyleAction1</tt> in the ActionList Editor to bring it up in the Object Inspector and to set up its properties:<br />
** Use the text "Bold" for the <tt>Caption</tt> - this is the text that will be assigned to the corresponding menu item.<br />
** Similarly, assign "Bold font" to the <tt>Hint</tt> property.<br />
** Set the <tt>ImageIndex</tt> to the index of the icon in the form's ImageList that you want to see in the toolbar.<br />
** Make sure that the item <tt>fssBold</tt> is highlighted in the dropdown list of the property <tt>FontStyle</tt>. If not, select it. Since <tt>TsFontStyleAction</tt> can handle several font styles (bold, italic, underline, strikeout) we have to tell the action which font style it should be responsible of. <br />
** Like with the visual controls, don't forget to assign the TsWorkbookSource to the corresponding property <tt>WorkbookSource</tt> of the action. This activates the communication between the worksheet/workbook on the one hand, and the action and the related controls on the other hand. <br />
<br />
Having set up the standard action we add a menu item to the form's '''MainMenu'''. Double-click on the TMainMenu of the form to bring up the "Menu Editor". Since the menu is empty so far there is only a dummy item, "New item1". This will become our "Format" menu. Select the item, and type "Format" into the <tt>Caption</tt> property field. Now the dummy item is re-labelled as "Format". Right-click on this "Format" item, and select "Create submenu" from the popup menu which brings up another new menu item, "New item2". Select it. In the dropdown list of the property <tt>Action</tt> of the object inspector, pick the <tt>sFontStyle1</tt> action - this is the action that we have just set up - and the menu item automatically shows the caption provided by the action component, "Bold". <br />
<br />
Finally we add a '''toolbar button''' for the "bold" action. Right-click onto the TToolbar, and add a new toolbutton by selecting item "New button" from the popup menu. Go to the property <tt>Action</tt> in the object inspector again, pick the <tt>sFontStyle1</tt> item, and this is enough to give the tool button the ability to set a cell font to bold!<br />
<br />
Repeat this procedure with two other buttons. Design them to set the font style to '''italic''' and '''underlined'''.<br />
<br />
Test the program by compiling. Type some text into cells. Select one of them and click the "Bold" toolbutton - voila, the cell is in bold font. Select another cell. Note that the toolbutton is automatically drawn in the down state if the cell has bold font. Repeat with the other buttons.<br />
<br />
=== Saving to file ===<br />
<br />
After having entered data into the grid you will certainly want to '''save the grid to a spreadsheet file'''. Lazarus provides all the necessary infrastructure for saving available in the standard action <tt>TFileSaveAs</tt>. This action automatically opens a '''FileDialog''' for entering the file name. <br />
<br />
Select the <tt>TFileSaveAs</tt> standard action from the list of standard action classes. Note that it cannot be found in the "FPSpreadsheet" category, but in the "File" group since it is a standard action of the LCL.<br />
<br />
[[file:sFileFormatsForSaving.png|right|400px]]<br />
<br />
At first, let us specify the properties of the FileDialog. Select the property <tt>Dialog</tt> of the <tt>TFileSaveAs</tt> action in the object inspector. It is convenient to be able to store the workbook in various file formats; this can be prepared by providing a file format list in the <tt>Filter</tt> property of the dialog. Paste the following text into this property:<br />
<br />
:<tt>Excel XML spreadsheet (*.xlsx)|*.xlsx|Excel 97-2003 spreadsheets (*.xls)|*.xls|Excel 5 spreadsheet (*.xls)|*.xls|Excel 2.1 spreadsheets (*.xls)|*.xls|LibreOffice/OpenOffice spreadsheet (*.ods)|*.ods|Comma-delimited files (*.csv)|*.csv|WikiTable (WikiMedia-Format, *.wikitable_wikimedia)|*.wikitable_wikimedia</tt><br />
<br />
When you click on the ellipsis button next to <tt>Filter</tt> the file list appears in a more clearly arranged dialog shown at the right.<br />
<br />
Make one of these file extensions, e.g. xlsx, the default of the file dialog by assigning its list index to the <tt>FilterIndex</tt> property. The xlsx file is the first format in the filter list. <tt>FilterIndex</tt>, therefore, must be set to 1. <br />
<br />
{{Note|The indexes in the filter list are 1-based, in contrast to the convention of Lazarus and FPC using 0-based indexes.}}<br />
<br />
Next, we define what happens after a file name has been selected in the file dialog. For this purpose, the <tt>TFileSaveAs</tt> action provides the event <tt>OnAccept</tt>. This is one of the few places where we have to write code in this project... But it is short: We check which file format has been selected in the format list and write the corresponding spreadsheet file by calling the method <tt>SaveToSpreadsheetFile</tt> of the TWorkbookSource:<br />
<br />
<syntaxhighlight><br />
uses<br />
..., fpstypes, ...; // for TsSpreadsheetFormat<br />
<br />
procedure TForm1.FileSaveAs1Accept(Sender: TObject);<br />
var<br />
fmt: TsSpreadsheetFormat;<br />
begin<br />
Screen.Cursor := crHourglass;<br />
try<br />
case FileSaveAs1.Dialog.FilterIndex of<br />
1: fmt := sfOOXML; // Note: Indexes are 1-based here!<br />
2: fmt := sfExcel8;<br />
3: fmt := sfExcel5;<br />
4: fmt := sfExcel2;<br />
5: fmt := sfOpenDocument;<br />
6: fmt := sfCSV;<br />
7: fmt := sfWikiTable_WikiMedia;<br />
end;<br />
sWorkbookSource1.SaveToSpreadsheetFile(FileSaveAs1.Dialog.FileName, fmt);<br />
finally<br />
Screen.Cursor := crDefault;<br />
end;<br />
end; <br />
</syntaxhighlight><br />
<br />
We will make the FileSaveAs action available in the toolbar and in the menu:<br />
* '''Toolbar''': Add a TToolButton to the first toolbar and drag it to its left edge. Assign the FileSaveAs action to its <tt>Action</tt> property.<br />
* '''Menu''': The "Save" command is usually in a submenu called "File". Therefore, double click on the TMainMenu, right-click on the "Format" item and insert a new item "before" the current one. Name it "File". Add a submenu to it. Click at the default menu item and assign the FileSaveAs action to its <tt>Action</tt> property.<br />
<br />
=== Reading from file ===<br />
<br />
What is left is '''reading of a spreadsheet file''' into our application. Of course, FPSpreadsheet is well-prepared for this task. The operations are very similar to saving. But instead of using a TFileSaveAs standard action, we use a '''TFileOpen''' standard action. Again, this standard action has a built-in file dialog where we have to set the <tt>DefaultExtension</tt> (".xls" or ".xlsx", most probably) and the format <tt>Filter</tt>:<br />
<br />
:<tt>All spreadsheet files|*.xls;*.xlsx;*.ods;*.csv|All Excel files (*.xls, *.xlsx)|*.xls;*.xlsx|Excel XML spreadsheet (*.xlsx)|*.xlsx|Excel 97-2003 spreadsheets (*.xls)|*.xls|Excel 5 spreadsheet (*.xls)|*.xls|Excel 2.1 spreadsheets (*.xls)|*.xls|LibreOffice/OpenOffice spreadsheet (*.ods)|*.ods|Comma-delimited files (*.csv)|*.csv</tt><br />
<br />
(Copy this string into the field <tt>Filter</tt> of the action's <tt>Dialog</tt>). As you may notice the <tt>Filter</tt> contains selections which cover various file formats, such as "All spreadsheet files", or "All Excel files". This is possible because the TsWorkbookSource has a property <tt>AutoDetectFormat</tt> for automatic detection of the spreadsheet file format. In the other cases, like "Libre/OpenOffice", we can specify the format, <tt>sfOpenDocument</tt>, explicitly. Evaluation of the correct file format and reading of the file is done in the <tt>OnAccept</tt> event handler of the action:<br />
<br />
<syntaxhighlight><br />
{ Loads the spreadsheet file selected by the FileOpen standard action }<br />
procedure TForm1.FileOpen1Accept(Sender: TObject);<br />
begin<br />
sWorkbookSource1.AutodetectFormat := false;<br />
case FileOpen1.Dialog.FilterIndex of<br />
1: sWorkbookSource1.AutoDetectFormat := true; // All spreadsheet files<br />
2: sWorkbookSource1.AutoDetectFormat := true; // All Excel files<br />
3: sWorkbookSource1.FileFormat := sfOOXML; // Excel 2007+<br />
4: sWorkbookSource1.FileFormat := sfExcel8; // Excel 97-2003<br />
5: sWorkbookSource1.FileFormat := sfExcel5; // Excel 5.0<br />
6: sWorkbookSource1.FileFormat := sfExcel2; // Excel 2.1<br />
7: sWorkbookSource1.FileFormat := sfOpenDocument; // Open/LibreOffice<br />
8: sWorkbookSource1.FileFormat := sfCSV; // Text files<br />
end;<br />
sWorkbookSource1.FileName :=FileOpen1.Dialog.FileName; // This loads the file<br />
end;<br />
</syntaxhighlight><br />
<br />
In order to see this action in the toolbar and menu, add a TToolButton to the '''toolbar''' and assign the TFileOpenAction to its <tt>Action</tt> property. In the '''menu''', add a new item before the "Save as" item, and assign its <tt>Action</tt> accordingly.<br />
<br />
{{Note|You can see a spreadsheet file even at designtime if you assign its name to the <tt>Filename</tt> property of the TsWorkbookSource. But be aware that the file probably cannot be found at runtime if it is specified by a relative path and if the application is to run on another computer with a different directory structure! }}<br />
<br />
== Summary ==<br />
If you followed us through the steps of this tutorial you have programmed a complex spreadsheet gui application almost without having written any line of code (with the exception of the loading and saving routines). If you did not, have a look at the demo "fps_ctrls" in the ''examples'' folder of the FPSpreadsheet installation; it shows the result of this tutorial with some add-ons. <br />
<br />
[[Category:Tutorials]]<br />
[[Category:Data import and export]]<br />
[[Category:FPSpreadsheet]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=FPSpreadsheet_tutorial:_Writing_a_mini_spreadsheet_application&diff=115100FPSpreadsheet tutorial: Writing a mini spreadsheet application2018-01-13T20:12:16Z<p>Zoran: /* TsWorksheetGrid */</p>
<hr />
<div>{{FPSpreadsheet tutorial: Writing a mini spreadsheet application}}<br />
<br />
== Introduction ==<br />
[[FPSpreadsheet]] is a powerful package for reading and writing spreadsheet files. The main intention is to provide a platform which is capable of native export/import of an application's data to/from the most important spreadsheet file formats without having these spreadsheet applications installed. <br />
<br />
Soon, however, the wish arises to use this package also for editing of file content or formatting. For this purpose, the library contains a dedicated grid control, the FPSpreadsheetGrid, which closely resembles the features of a worksheet of a spreadsheet application. The demo "spready" which comes along with FPSpreadsheet demonstrates usage of this grid. Along with a bunch of formatting options, this demo still comes up to more than 1400 lines of code in the main form unit. Therefore, a set of visual controls was developed which greatly simplify creation of spreadsheet applications. <br />
<br />
It is the intention of this tutorial to write a simple spreadsheet program on the basis of these controls.<br />
<br />
Although most of the internal structure of the FPSpreadsheet library is covered by the visual controls it is recommended that you have some knowledge of FPSpreadsheet. Of course, you should not have a basic understanding of Lazarus and FPC, and you must know how to work with the object inspector of Lazarus.<br />
<br />
== Visual FPSpreadsheet Controls ==<br />
FPSpreadsheet exposes non-visual classes, such as TsWorkbook, TsWorksheet etc. This keeps the library general enough for all kind of Pascal programs. For GUI programs, on the other hand, some infrastructure is needed which relates the spreadsheets to forms, grids, and other controls.<br />
<br />
=== TsWorkbookSource ===<br />
[[file:TSWORKBOOKSOURCE.png|left]] <br />
The heart of the visual FPSpreadsheet controls is the '''TsWorkbookSource''' class. This provides a link between the non-visual spreadsheet data and the visual controls on the form. Its purpose is similar to that of a TDataSource component in database applications which links database tables or queries to dedicated "data-aware" controls. <br />
<br />
All visual FPSpreadsheet controls have a property <tt>WorkbookSource</tt> which links them into the information chain provided by the TsWorkbookSource. The WorkbookSource keeps a list of all controls attached. Internally, these controls are called "listeners" because they listen to information distributed by the WorkbookSource.<br />
<br />
The workbook and worksheets use events to notify the WorkbookSource of all relevant changes: changes in cell content or formatting, selecting other cells, adding or deleting worksheet etc. Information on these changes is passed on to the listening controls, and they react in their own specialized way on these changes. If, for example, a new worksheet is added to a workbook the visual TsWorkbookTabControl creates a new tab for the new worksheet, and the TsWorksheetGrid loads the new worksheet into the grid.<br />
<br />
=== TsWorkbookTabControl ===<br />
[[file:TSWORKBOOKTABCONTROL.png|left]]<br />
This is a tabcontrol which provides a tab for each worksheet of the current workbook. The tab names are identical with the names of the worksheets. Selecting another tab is communicated to the other visual spreadsheet controls via the WorkbookSource.<br />
<br />
=== TsWorksheetGrid ===<br />
[[file:TSWORKSHEETGRID.png|left]]<br />
This is a customized DrawGrid descendant of the LCL and displays cells of the currently selected worksheet. The texts are not stored in the grid (like a StringGrid would do), but are taken from the TsWorksheet data structure. Similarly, the worksheet provides the information of how each cell is formatted. Like any LCL grid it has a bunch of properties and can be tuned for many applications by adapting its <tt>Options</tt>. The most important one will be described below. <br />
{{Note|The <tt>TsWorksheetGrid</tt> can also be operated without a <tt>TsWorkbookSource</tt>. For this purpose it provides its own set of methods for reading and writing files.}}<br />
<br />
=== TsCellEdit ===<br />
[[file:TSCELLEDIT.png|left]]<br />
The typical spreadsheet applications provide a line for editing formulas or cell content. This is the purpose of the '''TsCellEdit'''. It displays the content of the active cell of the worksheet which is the same as the active cell of the WorksheetGrid. If editing is finished (by pressing {{keypress|ENTER}}, or by selecting another cell in the WorksheetGrid) the new cell value is transferred to the worksheet. Internally, the TsCellEdit is a memo control, i.e. it is able to process multi-line text correctly. Use {{keypress|Ctrl}}+{{keypress|ENTER}} to insert a forced line-break.<br />
<br />
=== TsCellIndicator ===<br />
[[file:TSCELLINDICATOR.png|left]]<br />
This is a TEdit control which displays the address of the currently selected cell in Excel notation, e.g. 'A1' if the active cell is in the first row and first column (row = 0, column = 0). Conversely, if a valid cell address is entered into this control the corresponding cell becomes active. <br />
<br />
=== TsCellCombobox ===<br />
[[file:TSCELLCOMBOBOX.png|left]]This combobox can be used to modify various cell properties by selecting values from the dropdown list. The property affected is determined by the <tt>CellFormatItem</tt> of the combobox:<br />
* <tt>cfiFontName</tt>: the list contains he names of all fonts available on the current system. If an item is selected the corresponding font is used to format the cell of the currently selected cells.<br />
* <tt>cfiFontSize</tt>: the list contains the most typical font sizes used in spreadsheets. Selecting an item sets the font size of the currently selected cells accordingly.<br />
* <tt>cfiFontColor</tt>: the list contains all colors of the workbook's palette. The selected color is assigned to the font of the selected cells.<br />
* <tt>cfiBackgroundColor</tt>: like <tt>cfiFontColor</tt> - the selected color is used as background fill color of the selected cells.<br />
<br />
=== TsSpreadsheetInspector ===<br />
[[file:TSSPREADSHEETINSPECTOR.png|left]]<br />
Inherits from TValueListEditor and displays name-value pairs for properties of the workbook, the selected worksheet, and the content and formatting of the active cell. It's main purpose is to help with debugging.<br />
<br />
== Writing a spreadsheet application ==<br />
Enough of theory, let's get started. Let's write a small spreadsheet application. Sure - it cannot compete with the spreadsheets of the main Office applications like Excel or Open/LibreOffice, but it has all the main ingredients due to FPSpreadsheet. And using the FPSpreadsheet controls allows to achieve this with minimum lines of code.<br />
<br />
=== Preparations ===<br />
[[file:fpspreadsheetcontrols_preparations.png|right|400px]]<br />
Create a new project and store it in a folder of your liking. <br />
<br />
Since Office applications have a menu and a toolbar add a '''TMainMenu''' and a '''TToolbar''' component to the form. (You could even mimic the ribbon user interface of the new Microsoft applications by adding a '''TSpkToolbar''' from [http://sourceforge.net/p/lazarus-ccr/svn/HEAD/tree/components/spktoolbar/ Lazarus Code and Components Repository], but be aware that this component does not yet provide all the features of a standard toolbar).<br />
<br />
In fact, we will be needing '''another toolbar''' for the formula edit line. As you will see later, it will be resizable; as size control add a '''TSplitter''' to the form and top-align it such that it is positioned underneath the two toolbars. In order to keep a minimum size of the toolbar you should establish constraints: Look at the current height of the toolbar and enter this number into the <tt>MinHeight</tt> field of the <tt>Constraints</tt> property of the toolbar. To separate the formula toolbar from the rest of the main form, activate the option <tt>ebBottom</tt> of the <tt>EdgeBorders</tt> property of the second toolbar.<br />
<br />
Since menu and toolbars will have to handle same user actions it is advantageous to provide a '''TActionList''' to store all possible actions. If assigned to the menu items and toolbuttons both will react on user interaction in the same way without any additional coding. And: The FPSpreadsheet visual controls package contains a bunch of spreadsheet-related standard actions ready to use.<br />
<br />
The toolbar of the completed application will contain a lot of of icons. Therefore, we need a '''TImageList''' component which has to be linked to the <tt>Images</tt> property of the TMainMenu, the TToolbars, and the TActionList. Where to get icons? You can have a look in the folder <tt>images</tt> of your Lazarus installation where you'll find standard icons for loading and saving etc. This is a subset of the [http://www.famfamfam.com/lab/icons/silk/ famfamfam SILK icon library]. Another huge icon set is the [http://p.yusukekamiyamane.com/ Fugue icon collection]. Both collections are licensed as "Creative commons" and are free even for commercial use, provided that appropriate reference is given in the created programs. When selecting icons prefer the png image format, and make sure to use always the same size, usually 16x16 pixels.<br />
<br />
=== Setting up the visual workbook ===<br />
<br />
==== TsWorkbookSource ====<br />
As described in the introductory section the '''TsWorkbookSource''' component is the interface between workbook and controls on the user interface. Add this component to the form and give it a decent name (we'll keep the default name <tt>sWorkbookSource1</tt> here, though). As you will see shortly, this component will have to be assigned to the property <tt>WorkbookSource</tt> of all controls of the FPSpreadsheet_visual package.<br />
<br />
The WorkbookSource is responsible for loading and writing data from/to file and for communicating with the workbook. Therefore, it owns a set of options that are passed to the workbook and control these processes:<br />
[[file:sTabControl.png|right|400px]]<br />
<syntaxhighlight><br />
type<br />
TsWorkbookOption = (boVirtualMode, boBufStream, boAutoCalc, boCalcBeforeSaving, boReadFormulas);<br />
TsWorkbookOptions = set of TsWorkbookOption;<br />
</syntaxhighlight><br />
<br />
The most important ones are<br />
* <tt>boAutoCalc</tt>: activates automatic calculation of formulas whenever cell content changes.<br />
* <tt>boCalcBeforeSaving</tt>: calculated formulas before a workbook is written to file<br />
* <tt>boReadFormulas</tt>: if set full formulas are read from the file, otherwise only formula results.<br />
* <tt>boBufStream</tt> and <tt>boVirtualMode</tt>: In non-visual programs, these options can help if running out of memory in case of large workbooks. <tt>boVirtualMode</tt>, in particular, is not usable for visual applications, though, because it avoids keeping data in the worksheet cells. See also [[FPSpreadsheet#Virtual_mode]].<br />
<br />
In this tutorial, it is assumed that the options <tt>boAutoCalc</tt> and <tt>boReadFormulas</tt> are activated.<br />
<br />
==== TsWorkbookTabControl ====<br />
The first visual control used in the form is a '''TsWorkbookTabControl''' - click it onto the form (into the space not occupied by the toolbar). Client-align it within the form, this shows the TabControl as a bright rectangle only. Now link its <tt>WorkbookSource</tt> property to the TsWorkbookSource component that we have added just before. Now the TabControl shows a tab labelled "Sheet1". This is because the TsWorkbookSource has created a dummy workkbook containing a single worksheet "Sheet1". The WorkbookSource synchronizes this internal workbook with the TabControl (and the other visual controls to come) such that it displays this worksheet as a tab. <br />
<br />
In Excel the worksheet tabs are at the bottom of the form - to achieve this effect you can set the property <tt>TabPosition</tt> of the TabControl to <tt>tpBottom</tt>; there are some painting issues of the LCL with this TabPosition, though, therefore, I prefer the default setting, <tt>tpTop</tt>.<br />
<br />
The screenshot shows how far we've got. <br clear="all" /><br />
<br />
==== TsWorksheetGrid ====<br />
[[file:sWorksheetGrid.png|right|400px]] Now we add a '''TsWorksheetGrid''' control. Click it somewhere into the space occupied by the TabControl such that it becomes a child a of the TabControl. You see a standard stringgrid-like component. Link its <tt>WorkbookSource</tt> property to the source added at the beginning, and the grid looks more like a spreadsheet: there are the column headers labelled by letters "A", "B", etc, and the row headers labelled by numbers "1", "2", etc; the active cell, A1, is marked by a thick border. <br />
<br />
You may want to switch the grid's <tt>TitleStyle</tt> to <tt>tsNative</tt> in order to achieve themed painting of the row and column headers. And here is a good place to adapt the grid's <tt>Options</tt> in order to activate many features well-known to spreadsheets:<br />
* <tt>goEditing</tt> must be active, otherwise the grid contents cannot be modified. <br />
* <tt>goAlwaysShowEditor</tt> should be off because it interferes with the editing convention of spreadsheet applications.<br />
* <tt>goColSizing</tt> enables changing of the column width by dragging the dividing line between adjacent column headers. Dragging occurs with the left mouse button pressed.<br />
* <tt>goRowSizing</tt> does the same with the row heights.<br />
* <tt>goDblClickAutoResize</tt> activates the feature that optimum column width can be set by double-clicking in the header on its dividing line to the next column. The "optimum" column width is such that no cell content is truncated and no extra space is shown in the column.<br />
* <tt>goHeaderHotTrack</tt> gives visual feedback if the mouse is above a header cell.<br />
* <tt>goRangeSelect</tt> (which is on by default) enables selection of a rectangular range of cells by dragging the mouse between cells at opposite corners of the rectangle. If you have Lazarus trunk you can even select multiple rectangles by holding the CTRL key down before the next rectangle is dragged - in the release version of Lazarus (1.2.6 at the time of this writing) only a single range can be selected.<br />
* <tt>goThumbTracking</tt> activates immediate scrolling of the worksheet if one of the scrollbars is dragged with the mouse. The Office applications usually scroll by lines; you can achieve this by turning off <tt>goSmoothScroll</tt>.<br />
<br />
In addition to these <tt>Options</tt> inherited from <tt>TCustomGrid</tt> there are some more properties specialized for spreadsheet operation:<br />
* <tt>ShowGridLines</tt>, if <tt>false</tt>, hides the row and column grid lines.<br />
* <tt>ShowHeaders</tt> can be set to <tt>false</tt> if the the column and row headers are to be hidden. (The same can be achieved also by the deprecated property <tt>DisplayFixedColRow</tt>).<br />
* The LCL grids normally truncate text at the cell border if it is longer than the cell width. If <tt>TextOverflow</tt> is set to <tt>true</tt> then text can overflow into adjacent empty cells.<br />
<br />
The properties <tt>AutoCalc</tt> and <tt>ReadFormulas</tt> are meant for stand-alone usage of the WorksheetGrid (i.e. without a TsWorkbookSource). Please use the corresponding options of the WorkbookSource instead. (<tt>AutoCalc</tt> enables automatic calculation of formulas whenever cell content changes. <tt>ReadFormulas</tt> activates reading of formulas from files, otherwise the grid would display only the formula results).<br />
<br />
=== Editing of values and formulas, Navigating ===<br />
<br />
When you compile and run the program you'll already be able to enter data into the grid. Just select the cell that you want to edit by clicking or using the arrow keys - the active cell is highlighted by a thick border. Then begin typing. If the grid property <tt>EditorLineMode</tt> has been switched to <tt>elmMultiLine</tt> then manual line breaks can be entered by the key combination {{keypress|Ctrl}}+{{keypress|ENTER}}. When finished select another cell or press the {{keypress|ENTER}} key. Using {{keypress|ENTER}} automatically selects the next cell in the grid. The grid's property <tt>AutoAdvance</tt> defines what is understood as being the "next cell": by default, {{keypress|ENTER}} moves the active cell down (<tt>aaDown</tt>), but you can also move it to the right (<tt>aaRight</tt>), or turn this feature off (<tt>aaNone</tt>) - see the type <tt>TAutoAdvance</tt> defined in the unit <tt>grids.pas</tt> for even more options.<br />
<br />
If - as assumed above - the WorkbookSource option <tt>boAutoCalc</tt> is enabled the worksheet automatically supports calculation of '''formulas'''. As an example, go to cell A1, and enter the number <tt>10</tt>. Then, go to cell A2 and enter the formula <tt>=A1+10</tt>. The formula is automatically evaluated, and its result, <tt>20</tt>, is displayed in cell A2. <br />
<br />
When you navigate in the grid you may notice that cell A2 only displays the formula result, it seems that there is no way to modify the formula once it has been entered. No need to worry - press the key {{keypress|F2}} or click into the cell a second time to enter '''enhanced edit mode''' in which formulas are visible in the cell.<br />
<br />
[[file:sCellIndicator_sCellEdit.png|right|400px]]<br />
In order to edit formulas the Office applications offer a dedicated formula editor bar. Of course, fpspreadsheet has this feature, too. It is built into the '''TsCellEdit''' component which is set up such as to always show the full content of a cell. You remember the second toolbar from the "Preparations" section? This will house the TsCellEdit. But wait a minute - there's more to consider: Since formulas occasionally may get rather long the control should be capable of managing serval lines. The same with multi-lined text. TsCellEdit can do this since it is inherited from TCustomMemo which is a multi-line control. You also remember that we added a splitter to the form of the second toolbar? This is for height adjustment for the case that we want to use the multi-line feature of the TsCellEdit: just drag the splitter down to show more lines, or drag it upwards to stop at the height of a single line due to the MinHeight constraints that we had assigned to the toolbar.<br />
<br />
The TsCellEdit will cover all available space in the second toolbar. Before we add the TsCellEdit we can make life easier if we think about what else will be in the second toolbar. In Excel, there is an indicator which displays the address of the currently active cell. This is the purpose of the '''TsCellIndicator'''. Since its height should not change when the toolbar is dragged down we first add a '''TPanel''' to the second toolbar; reduce its <tt>Width</tt> to about 100 pixels, remove its <tt>Caption</tt> and set its <tt>BevelOuter</tt> to <tt>bvNone</tt>.<br />
<br />
Add the TsCellIndicator to this panel and align it to the top of the panel. Connect its <tt>WorkbookSource</tt> to the TsWorkbookSource control on the form, and immediately you'll see the text "A1", the address of the currenly selected cell.<br />
<br />
Sometimes it is desirable to change the width of this box at runtime. So, why not add a splitter to the second toolbar? Set its <tt>Align</tt> property to <tt>alLeft</tt>. The result is a bit strange: the splitter is at the very left edge of the toolbar, but you'd expect to see it at the right of the panel. This is because the panel is not aligned by default. Set the <tt>Align</tt> property of the panel to <tt>alLeft</tt> as well, and drag the splitter to the right of the panel. Now the splitter is at the correct position.<br />
<br />
Almost done now... We finally add a TsCellEdit component to the empty space of the toolbar. Client-align it so that it fills the entire rest of the toolbar. As usual, set its <tt>WorkbookSource</tt> property to the instance of the TsWorkbookSource on the the form.<br />
<br />
Compile and run. Play with the program:<br />
<br />
* Enter some dummy data. Navigate in the worksheet. You'll see that the CellIndicator always shows the address of the active cell. The contents of the active cell is displayed in the CellEdit box. The CellIndicator is not just a passive display of the current cell, it can also be edited. Type in the address of a cell which you want to become active, press {{keypress|ENTER}}, and see what happens...<br />
* Enter a formula. Navigate back into the formula cell - the formula is displayed in the CellEdit and can be changed there readily.<br />
* Enter multi-lined text - you can enforce a lineending in the CellEdit by holding the {{keypress|Ctrl}} key down when you press {{keypress|ENTER}}. The cell displays only one line of the text. Drag the horizontal splitter underneath the second toolbar down - the CellEdit shows all lines. Another way to see all lines of the text, is to adjust the cell height. You must have activated the grid <tt>Option</tt> <tt>goRowSizing</tt>. Then you can drag the lower dividing line of the row with the multi-line cell down to increase the row height - the missing lines now appear in the cell!<br />
<br />
=== Formatting of cells ===<br />
<br />
In addition to entering data the user usually wants to apply some formatting to the cells in order to enhance or group them. The worksheet grid is set up in such a way that its cells display the formats taken from the workbook. In addition, the visual FPSpreadsheet controls are able to store formatting attributes into the cell. Because of the notification mechanism via the WorkbookSource these formats are returned to the WorksheetGrid for display.<br />
<br />
==== Adding comboboxes for font name, font size, and font color ====<br />
[[file:sCellFontCombobox.png|right|400px]]<br />
In this section, we want to provide the possibility to modify the font of the cell texts by selecting its name, size and/or color. The visual FPSpreadsheet provide the flexible '''TsCellCombobox''' for this purpose. It has the property <tt>CellFormatItem</tt> which defines which attribute it controls:<br />
* <tt>cfiFontName</tt>: This option populates the combobox with all fonts found in the current system. The selected item is used for the type face in the selected cells.<br />
* <tt>cfiFontSize</tt> fills the combobox with the mostly used font sizes (in points). Again, the selected item defines the font size of the selected cells.<br />
* <tt>cfiFontColor</tt> adds all pre-defined colors ("palette") of the workbook to the combobox to set the text color of the selected cells. The combobox items consist of a little color box along with the color name. If the <tt>ColorRectWidth</tt> is set to <tt>-1</tt> the color name is dropped.<br />
* <tt>cfiBackgroundColor</tt>, the same with the background color of the selected cells.<br />
* <tt>cfiCellBorderColor</tt>, the same with the border color of the selected cells - this feature is currently not yet supported.<br />
<br />
Add three TsCellComboboxes to the first toolbar and set their <tt>CellFormatItem</tt> to <tt>cfiFontname</tt>, <tt>cfiFontSize</tt>, and <tt>cfiFontColor</tt>, respectively. Link their <tt>WorkbookSource</tt> property to the TsWorkbookSource on the form. You may want to increase the width of the font name combobox such that the longest font names are not cut off; the other comboboxes may become narrower. You may also want to turn off the color names of the third combobox by setting its <tt>ColorRectWidth</tt> to <tt>-1</tt>.<br />
<br />
That's all to modify fonts. Compile and run. Enter some text and play with these new features of the program.<br />
<br />
==== Using standard actions ====<br />
[[file:sFontStyleAction_selected.png|right|300px]]<br />
FPSpreadsheet supports a lot of formats that can be applied to cells, such as text alignment, text rotation, text font, or cell borders or background colors. Typical gui applications contain menu commands and/or toolbar buttons which are assigned to each of these properties and allow to set them by a simple mouse click. In addition, the state of these controls often reflects the properties of the active cell. For example, if there is a button for using a bold type-face this button should be drawn as being pressed if the active cell is bold, but as released if it is not. To simplify the coding of these tasks a large number of standard actions has been added to the library. <br />
<br />
* '''TsWorksheetAddAction''': adds an empty worksheet to the workbook. Specify its name in the <tt>NameMask</tt> property. The <tt>NameMask</tt> must contain the format specifier <tt>%d</tt> which is replaced at runtime by a number such that the worksheet name is unique.<br />
* '''TsWorksheetDeleteAction''': deletes the active worksheet from the workbook after a confirmation dialog. The last worksheet cannot be deleted.<br />
* '''TsWorksheetRenameAction''': renames the active worksheet.<br />
* '''TsCopyAction''': Copies the currently selected cells to an internal list ("CellClipboard") from where they can be pasted back into the spreadsheet to another location. The process can occur in a clipboard-manner ("copy"/"cut", then "paste") or in the way of the "copy brush" of the Office applications. The property <tt>CopyItem</tt> determines whether the entire cell, or only cell values, cell formulas, or cell formats are transferred. <br />
* '''TsFontStyleAction''': Modifies the font style of the selected cells. The property <tt>FontStyle</tt> defines whether the action makes the font bold, italic, underlined or striked-out. Normally each font style is handles by its own action. See the example below.<br />
* '''TsHorAlignmentAction''': Can be used to modify the horizontal alignment of text in the selected cells. Select <tt>HorAlignment</tt> to define which kind of alignment (left, center, right) is covered by the action. Like with the TsFontStyleAction, several actions should be provided to offer all available alignments. They are grouped in a mutually exclusive way like radiobuttons.<br />
* '''TsVertAlignmentAction''': Changes the vertical alignment of text in the selected cells: the kind of alignment is defined by the <tt>VertAlignment</tt> property. Again, these actions work like radiobuttons.<br />
* '''TsTextRotationAction''': Allows to specify the text orientation in the selected cells as defined by the property <tt>TextRotation</tt> in a mutially exclusive way.<br />
* '''TsWordWrapAction''': Activates the word-wrapping feature for the selected cells: if text is longer than the width of the cell (or height, if the text is rotated) then it is wrapped into multiple lines.<br />
* '''TsNumberFormatAction''': Defines the number format to be used for the selected cells. The format to be used is defined by the properties <tt>NumberFormat</tt> (such as <tt>nfFixed</tt>) for built-in formats, and <tt>NumberFormatStr</tt> for specialized formatting.<br />
* '''TsDecimalsAction''': Allows to increase or decrease the number of decimal places shown in the selected cells. The property <tt>Delta</tt> controls whether an increase (+1) or decrease (-1) is wanted.<br />
* '''TsCellBorderAction''': Allows to specify if a border will be drawn around the selected cells. The subproperties <tt>East</tt>, <tt>West</tt>, <tt>North</tt>, <tt>South</tt>, <tt>InnerHor</tt>, <tt>InnerVert</tt> of <tt>Borders</tt> define what the border will look like at each side of the cell range. Note that each rectangular range of cells is considere as a single block; the properties <tt>East</tt>, <tt>West</tt>, <tt>North</tt> and <tt>South</tt> are responsible for the outer borders of the entire block, inner borders are defined by <tt>InnerHor</tt> and <tt>InnerVert</tt>. Using these properties, borders can be switched on and off (<tt>Visible</tt>), and in addition, the line style and line color can be changed.<br />
* '''TsMergeAction''': If checked, the cells of each selected rectangular range are merged to a single block. Unchecking the action separates the block to individual cells. Note that the block's content and formatting is defined by the top-left cell of each block; content and formats of other cells will be lost.<br />
* '''TsCellProtectionAction''': comes in two flavors, one for protecting cells from modifications, and one for hiding formulas, depending on the value of the property <tt>Protection</tt>. Note that this action is effective only if worksheet protection has been enabled by calling <tt>Workbooksource.Worksheet.Protect(true)</tt>.<br />
<br />
==== Adding buttons for "Bold", "Italic", and "Underline" ====<br />
[[file:sFontStyleAction_in_ActionListEditor.png|right|350px]]<br />
If you have never worked with '''standard actions''' before here are some detailed '''step-by-step instructions'''. Let us stick to above example and provide the possibility to switch the font style of the selected cells to '''bold'''. The standard action which is responsible for this feature is the <tt>TsFontStyleAction</tt>. <br />
<br />
* At first, we add this action to the form: Double-click on the '''TActionList''' to open the "ActionList Editor". <br />
* Click on the down-arrow next to the "+" button, and select the item "New standard action" from the drop-down menu. <br />
* This opens a dialog with the list of registered "Standard Action Classes". <br />
* Scroll down until you find a group named "FPSpreadsheet". <br />
* In this group, select the item "TsFontStyleAction" by double-clicking. <br />
* Now an item <tt>sFontStyleAction1</tt> appears in the ActionList Editor. <br />
* It should already be selected like in the screenshot at the right. If not, select <tt>sFontStyleAction1</tt> in the ActionList Editor to bring it up in the Object Inspector and to set up its properties:<br />
** Use the text "Bold" for the <tt>Caption</tt> - this is the text that will be assigned to the corresponding menu item.<br />
** Similarly, assign "Bold font" to the <tt>Hint</tt> property.<br />
** Set the <tt>ImageIndex</tt> to the index of the icon in the form's ImageList that you want to see in the toolbar.<br />
** Make sure that the item <tt>fssBold</tt> is highlighted in the dropdown list of the property <tt>FontStyle</tt>. If not, select it. Since <tt>TsFontStyleAction</tt> can handle several font styles (bold, italic, underline, strikeout) we have to tell the action which font style it should be responsible of. <br />
** Like with the visual controls, don't forget to assign the TsWorkbookSource to the corresponding property <tt>WorkbookSource</tt> of the action. This activates the communication between the worksheet/workbook on the one hand, and the action and the related controls on the other hand. <br />
<br />
Having set up the standard action we add a menu item to the form's '''MainMenu'''. Double-click on the TMainMenu of the form to bring up the "Menu Editor". Since the menu is empty so far there is only a dummy item, "New item1". This will become our "Format" menu. Select the item, and type "Format" into the <tt>Caption</tt> property field. Now the dummy item is re-labelled as "Format". Right-click on this "Format" item, and select "Create submenu" from the popup menu which brings up another new menu item, "New item2". Select it. In the dropdown list of the property <tt>Action</tt> of the object inspector, pick the <tt>sFontStyle1</tt> action - this is the action that we have just set up - and the menu item automatically shows the caption provided by the action component, "Bold". <br />
<br />
Finally we add a '''toolbar button''' for the "bold" action. Right-click onto the TToolbar, and add a new toolbutton by selecting item "New button" from the popup menu. Go to the property <tt>Action</tt> in the object inspector again, pick the <tt>sFontStyle1</tt> item, and this is enough to give the tool button the ability to set a cell font to bold!<br />
<br />
Repeat this procedure with two other buttons. Design them to set the font style to '''italic''' and '''underlined'''.<br />
<br />
Test the program by compiling. Type some text into cells. Select one of them and click the "Bold" toolbutton - voila, the cell is in bold font. Select another cell. Note that the toolbutton is automatically drawn in the down state if the cell has bold font. Repeat with the other buttons.<br />
<br />
=== Saving to file ===<br />
<br />
After having entered data into the grid you will certainly want to '''save the grid to a spreadsheet file'''. Lazarus provides all the necessary infrastructure for saving available in the standard action <tt>TFileSaveAs</tt>. This action automatically opens a '''FileDialog''' for entering the file name. <br />
<br />
Select the <tt>TFileSaveAs</tt> standard action from the list of standard action classes. Note that it cannot be found in the "FPSpreadsheet" category, but in the "File" group since it is a standard action of the LCL.<br />
<br />
[[file:sFileFormatsForSaving.png|right|400px]]<br />
<br />
At first, let us specify the properties of the FileDialog. Select the property <tt>Dialog</tt> of the <tt>TFileSaveAs</tt> action in the object inspector. It is convenient to be able to store the workbook in various file formats; this can be prepared by providing a file format list in the <tt>Filter</tt> property of the dialog. Paste the following text into this property:<br />
<br />
:<tt>Excel XML spreadsheet (*.xlsx)|*.xlsx|Excel 97-2003 spreadsheets (*.xls)|*.xls|Excel 5 spreadsheet (*.xls)|*.xls|Excel 2.1 spreadsheets (*.xls)|*.xls|LibreOffice/OpenOffice spreadsheet (*.ods)|*.ods|Comma-delimited files (*.csv)|*.csv|WikiTable (WikiMedia-Format, *.wikitable_wikimedia)|*.wikitable_wikimedia</tt><br />
<br />
When you click on the ellipsis button next to <tt>Filter</tt> the file list appears in a more clearly arranged dialog shown at the right.<br />
<br />
Make one of these file extensions, e.g. xlsx, the default of the file dialog by assigning its list index to the <tt>FilterIndex</tt> property. The xlsx file is the first format in the filter list. <tt>FilterIndex</tt>, therefore, must be set to 1. <br />
<br />
{{Note|The indexes in the filter list are 1-based, in contrast to the convention of Lazarus and FPC using 0-based indexes.}}<br />
<br />
Next, we define what happens after a file name has been selected in the file dialog. For this purpose, the <tt>TFileSaveAs</tt> action provides the event <tt>OnAccept</tt>. This is one of the few places where we have to write code in this project... But it is short: We check which file format has been selected in the format list and write the corresponding spreadsheet file by calling the method <tt>SaveToSpreadsheetFile</tt> of the TWorkbookSource:<br />
<br />
<syntaxhighlight><br />
uses<br />
..., fpstypes, ...; // for TsSpreadsheetFormat<br />
<br />
procedure TForm1.FileSaveAs1Accept(Sender: TObject);<br />
var<br />
fmt: TsSpreadsheetFormat;<br />
begin<br />
Screen.Cursor := crHourglass;<br />
try<br />
case FileSaveAs1.Dialog.FilterIndex of<br />
1: fmt := sfOOXML; // Note: Indexes are 1-based here!<br />
2: fmt := sfExcel8;<br />
3: fmt := sfExcel5;<br />
4: fmt := sfExcel2;<br />
5: fmt := sfOpenDocument;<br />
6: fmt := sfCSV;<br />
7: fmt := sfWikiTable_WikiMedia;<br />
end;<br />
sWorkbookSource1.SaveToSpreadsheetFile(FileSaveAs1.Dialog.FileName, fmt);<br />
finally<br />
Screen.Cursor := crDefault;<br />
end;<br />
end; <br />
</syntaxhighlight><br />
<br />
We will make the FileSaveAs action available in the toolbar and in the menu:<br />
* '''Toolbar''': Add a TToolButton to the first toolbar and drag it to its left edge. Assign the FileSaveAs action to its <tt>Action</tt> property.<br />
* '''Menu''': The "Save" command is usually in a submenu called "File". Therefore, double click on the TMainMenu, right-click on the "Format" item and insert a new item "before" the current one. Name it "File". Add a submenu to it. Click at the default menu item and assign the FileSaveAs action to its <tt>Action</tt> property.<br />
<br />
=== Reading from file ===<br />
<br />
What is left is '''reading of a spreadsheet file''' into our application. Of course, FPSpreadsheet is well-prepared for this task. The operations are very similar to saving. But instead of using a TFileSaveAs standard action, we use a '''TFileOpen''' standard action. Again, this standard action has a built-in file dialog where we have to set the <tt>DefaultExtension</tt> (".xls" or ".xlsx", most probably) and the format <tt>Filter</tt>:<br />
<br />
:<tt>All spreadsheet files|*.xls;*.xlsx;*.ods;*.csv|All Excel files (*.xls, *.xlsx)|*.xls;*.xlsx|Excel XML spreadsheet (*.xlsx)|*.xlsx|Excel 97-2003 spreadsheets (*.xls)|*.xls|Excel 5 spreadsheet (*.xls)|*.xls|Excel 2.1 spreadsheets (*.xls)|*.xls|LibreOffice/OpenOffice spreadsheet (*.ods)|*.ods|Comma-delimited files (*.csv)|*.csv</tt><br />
<br />
(Copy this string into the field <tt>Filter</tt> of the action's <tt>Dialog</tt>). As you may notice the <tt>Filter</tt> contains selections which cover various file formats, such as "All spreadsheet files", or "All Excel files". This is possible because the TsWorkbookSource has a property <tt>AutoDetectFormat</tt> for automatic detection of the spreadsheet file format. In the other cases, like "Libre/OpenOffice", we can specify the format, <tt>sfOpenDocument</tt>, explicitly. Evaluation of the correct file format and reading of the file is done in the <tt>OnAccept</tt> event handler of the action:<br />
<br />
<syntaxhighlight><br />
{ Loads the spreadsheet file selected by the FileOpen standard action }<br />
procedure TForm1.FileOpen1Accept(Sender: TObject);<br />
begin<br />
sWorkbookSource1.AutodetectFormat := false;<br />
case FileOpen1.Dialog.FilterIndex of<br />
1: sWorkbookSource1.AutoDetectFormat := true; // All spreadsheet files<br />
2: sWorkbookSource1.AutoDetectFormat := true; // All Excel files<br />
3: sWorkbookSource1.FileFormat := sfOOXML; // Excel 2007+<br />
4: sWorkbookSource1.FileFormat := sfExcel8; // Excel 97-2003<br />
5: sWorkbookSource1.FileFormat := sfExcel5; // Excel 5.0<br />
6: sWorkbookSource1.FileFormat := sfExcel2; // Excel 2.1<br />
7: sWorkbookSource1.FileFormat := sfOpenDocument; // Open/LibreOffice<br />
8: sWorkbookSource1.FileFormat := sfCSV; // Text files<br />
end;<br />
sWorkbookSource1.FileName :=FileOpen1.Dialog.FileName; // This loads the file<br />
end;<br />
</syntaxhighlight><br />
<br />
In order to see this action in the toolbar and menu, add a TToolButton to the '''toolbar''' and assign the TFileOpenAction to its <tt>Action</tt> property. In the '''menu''', add a new item before the "Save as" item, and assign its <tt>Action</tt> accordingly.<br />
<br />
{{Note|You can see a spreadsheet file even at designtime if you assign its name to the <tt>Filename</tt> property of the TsWorkbookSource. But be aware that the file probably cannot be found at runtime if it is specified by a relative path and if the application is to run on another computer with a different directory structure! }}<br />
<br />
== Summary ==<br />
If you followed us through the steps of this tutorial you have programmed a complex spreadsheet gui application almost without having written any line of code (with the exception of the loading and saving routines). If you did not, have a look at the demo "fps_ctrls" in the ''examples'' folder of the FPSpreadsheet installation; it shows the result of this tutorial with some add-ons. <br />
<br />
[[Category:Tutorials]]<br />
[[Category:Data import and export]]<br />
[[Category:FPSpreadsheet]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=FPSpreadsheet_tutorial:_Writing_a_mini_spreadsheet_application&diff=115099FPSpreadsheet tutorial: Writing a mini spreadsheet application2018-01-13T20:10:07Z<p>Zoran: /* TsWorksheetGrid */</p>
<hr />
<div>{{FPSpreadsheet tutorial: Writing a mini spreadsheet application}}<br />
<br />
== Introduction ==<br />
[[FPSpreadsheet]] is a powerful package for reading and writing spreadsheet files. The main intention is to provide a platform which is capable of native export/import of an application's data to/from the most important spreadsheet file formats without having these spreadsheet applications installed. <br />
<br />
Soon, however, the wish arises to use this package also for editing of file content or formatting. For this purpose, the library contains a dedicated grid control, the FPSpreadsheetGrid, which closely resembles the features of a worksheet of a spreadsheet application. The demo "spready" which comes along with FPSpreadsheet demonstrates usage of this grid. Along with a bunch of formatting options, this demo still comes up to more than 1400 lines of code in the main form unit. Therefore, a set of visual controls was developed which greatly simplify creation of spreadsheet applications. <br />
<br />
It is the intention of this tutorial to write a simple spreadsheet program on the basis of these controls.<br />
<br />
Although most of the internal structure of the FPSpreadsheet library is covered by the visual controls it is recommended that you have some knowledge of FPSpreadsheet. Of course, you should not have a basic understanding of Lazarus and FPC, and you must know how to work with the object inspector of Lazarus.<br />
<br />
== Visual FPSpreadsheet Controls ==<br />
FPSpreadsheet exposes non-visual classes, such as TsWorkbook, TsWorksheet etc. This keeps the library general enough for all kind of Pascal programs. For GUI programs, on the other hand, some infrastructure is needed which relates the spreadsheets to forms, grids, and other controls.<br />
<br />
=== TsWorkbookSource ===<br />
[[file:TSWORKBOOKSOURCE.png|left]] <br />
The heart of the visual FPSpreadsheet controls is the '''TsWorkbookSource''' class. This provides a link between the non-visual spreadsheet data and the visual controls on the form. Its purpose is similar to that of a TDataSource component in database applications which links database tables or queries to dedicated "data-aware" controls. <br />
<br />
All visual FPSpreadsheet controls have a property <tt>WorkbookSource</tt> which links them into the information chain provided by the TsWorkbookSource. The WorkbookSource keeps a list of all controls attached. Internally, these controls are called "listeners" because they listen to information distributed by the WorkbookSource.<br />
<br />
The workbook and worksheets use events to notify the WorkbookSource of all relevant changes: changes in cell content or formatting, selecting other cells, adding or deleting worksheet etc. Information on these changes is passed on to the listening controls, and they react in their own specialized way on these changes. If, for example, a new worksheet is added to a workbook the visual TsWorkbookTabControl creates a new tab for the new worksheet, and the TsWorksheetGrid loads the new worksheet into the grid.<br />
<br />
=== TsWorkbookTabControl ===<br />
[[file:TSWORKBOOKTABCONTROL.png|left]]<br />
This is a tabcontrol which provides a tab for each worksheet of the current workbook. The tab names are identical with the names of the worksheets. Selecting another tab is communicated to the other visual spreadsheet controls via the WorkbookSource.<br />
<br />
=== TsWorksheetGrid ===<br />
[[file:TSWORKSHEETGRID.png|left]]<br />
This is a customized DrawGrid descendant of the LCL and displays cells of the currently selected worksheet. The texts are not stored in the grid (like a StringGrid would do), but are taken from the TsWorksheet data structure. Similarly, the worksheet provides the information of how each cell is formatted. Like any LCL grid it has a bunch of properties and can be tuned for many applications by adapting its <tt>Options</tt>. The most important one will be described below. <br />
{{Note|The <tt>TsWorksheetGrid</tt> can also be operated without a <tt>TsWorkbookSource</tt>. For this purpose it provides its own set of methods for reading and writing files.}}<br />
<br />
=== TsCellEdit ===<br />
[[file:TSCELLEDIT.png|left]]<br />
The typical spreadsheet applications provide a line for editing formulas or cell content. This is the purpose of the '''TsCellEdit'''. It displays the content of the active cell of the worksheet which is the same as the active cell of the WorksheetGrid. If editing is finished (by pressing {{keypress|ENTER}}, or by selecting another cell in the WorksheetGrid) the new cell value is transferred to the worksheet. Internally, the TsCellEdit is a memo control, i.e. it is able to process multi-line text correctly. Use {{keypress|Ctrl}}+{{keypress|ENTER}} to insert a forced line-break.<br />
<br />
=== TsCellIndicator ===<br />
[[file:TSCELLINDICATOR.png|left]]<br />
This is a TEdit control which displays the address of the currently selected cell in Excel notation, e.g. 'A1' if the active cell is in the first row and first column (row = 0, column = 0). Conversely, if a valid cell address is entered into this control the corresponding cell becomes active. <br />
<br />
=== TsCellCombobox ===<br />
[[file:TSCELLCOMBOBOX.png|left]]This combobox can be used to modify various cell properties by selecting values from the dropdown list. The property affected is determined by the <tt>CellFormatItem</tt> of the combobox:<br />
* <tt>cfiFontName</tt>: the list contains he names of all fonts available on the current system. If an item is selected the corresponding font is used to format the cell of the currently selected cells.<br />
* <tt>cfiFontSize</tt>: the list contains the most typical font sizes used in spreadsheets. Selecting an item sets the font size of the currently selected cells accordingly.<br />
* <tt>cfiFontColor</tt>: the list contains all colors of the workbook's palette. The selected color is assigned to the font of the selected cells.<br />
* <tt>cfiBackgroundColor</tt>: like <tt>cfiFontColor</tt> - the selected color is used as background fill color of the selected cells.<br />
<br />
=== TsSpreadsheetInspector ===<br />
[[file:TSSPREADSHEETINSPECTOR.png|left]]<br />
Inherits from TValueListEditor and displays name-value pairs for properties of the workbook, the selected worksheet, and the content and formatting of the active cell. It's main purpose is to help with debugging.<br />
<br />
== Writing a spreadsheet application ==<br />
Enough of theory, let's get started. Let's write a small spreadsheet application. Sure - it cannot compete with the spreadsheets of the main Office applications like Excel or Open/LibreOffice, but it has all the main ingredients due to FPSpreadsheet. And using the FPSpreadsheet controls allows to achieve this with minimum lines of code.<br />
<br />
=== Preparations ===<br />
[[file:fpspreadsheetcontrols_preparations.png|right|400px]]<br />
Create a new project and store it in a folder of your liking. <br />
<br />
Since Office applications have a menu and a toolbar add a '''TMainMenu''' and a '''TToolbar''' component to the form. (You could even mimic the ribbon user interface of the new Microsoft applications by adding a '''TSpkToolbar''' from [http://sourceforge.net/p/lazarus-ccr/svn/HEAD/tree/components/spktoolbar/ Lazarus Code and Components Repository], but be aware that this component does not yet provide all the features of a standard toolbar).<br />
<br />
In fact, we will be needing '''another toolbar''' for the formula edit line. As you will see later, it will be resizable; as size control add a '''TSplitter''' to the form and top-align it such that it is positioned underneath the two toolbars. In order to keep a minimum size of the toolbar you should establish constraints: Look at the current height of the toolbar and enter this number into the <tt>MinHeight</tt> field of the <tt>Constraints</tt> property of the toolbar. To separate the formula toolbar from the rest of the main form, activate the option <tt>ebBottom</tt> of the <tt>EdgeBorders</tt> property of the second toolbar.<br />
<br />
Since menu and toolbars will have to handle same user actions it is advantageous to provide a '''TActionList''' to store all possible actions. If assigned to the menu items and toolbuttons both will react on user interaction in the same way without any additional coding. And: The FPSpreadsheet visual controls package contains a bunch of spreadsheet-related standard actions ready to use.<br />
<br />
The toolbar of the completed application will contain a lot of of icons. Therefore, we need a '''TImageList''' component which has to be linked to the <tt>Images</tt> property of the TMainMenu, the TToolbars, and the TActionList. Where to get icons? You can have a look in the folder <tt>images</tt> of your Lazarus installation where you'll find standard icons for loading and saving etc. This is a subset of the [http://www.famfamfam.com/lab/icons/silk/ famfamfam SILK icon library]. Another huge icon set is the [http://p.yusukekamiyamane.com/ Fugue icon collection]. Both collections are licensed as "Creative commons" and are free even for commercial use, provided that appropriate reference is given in the created programs. When selecting icons prefer the png image format, and make sure to use always the same size, usually 16x16 pixels.<br />
<br />
=== Setting up the visual workbook ===<br />
<br />
==== TsWorkbookSource ====<br />
As described in the introductory section the '''TsWorkbookSource''' component is the interface between workbook and controls on the user interface. Add this component to the form and give it a decent name (we'll keep the default name <tt>sWorkbookSource1</tt> here, though). As you will see shortly, this component will have to be assigned to the property <tt>WorkbookSource</tt> of all controls of the FPSpreadsheet_visual package.<br />
<br />
The WorkbookSource is responsible for loading and writing data from/to file and for communicating with the workbook. Therefore, it owns a set of options that are passed to the workbook and control these processes:<br />
[[file:sTabControl.png|right|400px]]<br />
<syntaxhighlight><br />
type<br />
TsWorkbookOption = (boVirtualMode, boBufStream, boAutoCalc, boCalcBeforeSaving, boReadFormulas);<br />
TsWorkbookOptions = set of TsWorkbookOption;<br />
</syntaxhighlight><br />
<br />
The most important ones are<br />
* <tt>boAutoCalc</tt>: activates automatic calculation of formulas whenever cell content changes.<br />
* <tt>boCalcBeforeSaving</tt>: calculated formulas before a workbook is written to file<br />
* <tt>boReadFormulas</tt>: if set full formulas are read from the file, otherwise only formula results.<br />
* <tt>boBufStream</tt> and <tt>boVirtualMode</tt>: In non-visual programs, these options can help if running out of memory in case of large workbooks. <tt>boVirtualMode</tt>, in particular, is not usable for visual applications, though, because it avoids keeping data in the worksheet cells. See also [[FPSpreadsheet#Virtual_mode]].<br />
<br />
In this tutorial, it is assumed that the options <tt>boAutoCalc</tt> and <tt>boReadFormulas</tt> are activated.<br />
<br />
==== TsWorkbookTabControl ====<br />
The first visual control used in the form is a '''TsWorkbookTabControl''' - click it onto the form (into the space not occupied by the toolbar). Client-align it within the form, this shows the TabControl as a bright rectangle only. Now link its <tt>WorkbookSource</tt> property to the TsWorkbookSource component that we have added just before. Now the TabControl shows a tab labelled "Sheet1". This is because the TsWorkbookSource has created a dummy workkbook containing a single worksheet "Sheet1". The WorkbookSource synchronizes this internal workbook with the TabControl (and the other visual controls to come) such that it displays this worksheet as a tab. <br />
<br />
In Excel the worksheet tabs are at the bottom of the form - to achieve this effect you can set the property <tt>TabPosition</tt> of the TabControl to <tt>tpBottom</tt>; there are some painting issues of the LCL with this TabPosition, though, therefore, I prefer the default setting, <tt>tpTop</tt>.<br />
<br />
The screenshot shows how far we've got. <br clear="all" /><br />
<br />
==== TsWorksheetGrid ====<br />
[[file:sWorksheetGrid.png|right|400px]] Now we add a '''TsWorksheetGrid''' control. Click it somewhere into the space occupied by the TabControl such that it becomes a child a of the TabControl. You see a standard stringgrid-like component. Link its <tt>WorkbookSource</tt> property to the source added at the beginning, and the grid looks more like a spreadsheet: there are the column headers labelled by letters "A", "B", etc, and the row headers labelled by numbers "1", "2", etc; the active cell, A1, is marked by a thick border. <br />
<br />
You may want to switch the grid's <tt>TitleStyle</tt> to <tt>tsNative</tt> in order to achieve themed painting of the row and column headers. And here is a good place to adapt the grid's <tt>Options</tt> in order to activate many features well-known to spreadsheets:<br />
* <tt>goEditing</tt> must be active, otherwise the grid contents cannot be modified. <br />
* <tt>goAlwaysShowEditor</tt> should be off because it interferes with the editing convention of spreadsheet applications.<br />
* <tt>goColSizing</tt> enables changing of the column width by dragging the dividing line between adjacent column headers. Dragging occurs with the left mouse button pressed.<br />
* <tt>goRowSizing</tt> does the same with the row heights.<br />
* <tt>goDblClickAutoResize</tt> activates the feature that optimum column width can be set by double-clicking in the header on its dividing line to the next column. The "optimum" column width is such that no cell content is truncated and no extra space is shown in the column.<br />
* <tt>goHeaderHotTack</tt> gives visual feedback if the mouse is above a header cell.<br />
* <tt>goRangeSelect</tt> (which is on by default) enables selection of a rectangular range of cells by dragging the mouse between cells at opposite corners of the rectangle. If you have Lazarus trunk you can even select multiple rectangles by holding the CTRL key down before the next rectangle is dragged - in the release version of Lazarus (1.2.6 at the time of this writing) only a single range can be selected.<br />
* <tt>goThumbTracking</tt> activates immediate scrolling of the worksheet if one of the scrollbars is dragged with the mouse. The Office applications usually scroll by lines; you can achieve this by turning off <tt>goSmoothScroll</tt>.<br />
<br />
In addition to these <tt>Options</tt> inherited from <tt>TCustomGrid</tt> there are some more properties specialized for spreadsheet operation:<br />
* <tt>ShowGridLines</tt>, if <tt>false</tt>, hides the row and column grid lines.<br />
* <tt>ShowHeaders</tt> can be set to <tt>false</tt> if the the column and row headers are to be hidden. (The same can be achieved also by the deprecated property <tt>DisplayFixedColRow</tt>).<br />
* The LCL grids normally truncate text at the cell border if it is longer than the cell width. If <tt>TextOverflow</tt> is set to <tt>true</tt> then text can overflow into adjacent empty cells.<br />
<br />
The properties <tt>AutoCalc</tt> and <tt>ReadFormulas</tt> are meant for stand-alone usage of the WorksheetGrid (i.e. without a TsWorkbookSource). Please use the corresponding options of the WorkbookSource instead. (<tt>AutoCalc</tt> enables automatic calculation of formulas whenever cell content changes. <tt>ReadFormulas</tt> activates reading of formulas from files, otherwise the grid would display only the formula results).<br />
<br />
=== Editing of values and formulas, Navigating ===<br />
<br />
When you compile and run the program you'll already be able to enter data into the grid. Just select the cell that you want to edit by clicking or using the arrow keys - the active cell is highlighted by a thick border. Then begin typing. If the grid property <tt>EditorLineMode</tt> has been switched to <tt>elmMultiLine</tt> then manual line breaks can be entered by the key combination {{keypress|Ctrl}}+{{keypress|ENTER}}. When finished select another cell or press the {{keypress|ENTER}} key. Using {{keypress|ENTER}} automatically selects the next cell in the grid. The grid's property <tt>AutoAdvance</tt> defines what is understood as being the "next cell": by default, {{keypress|ENTER}} moves the active cell down (<tt>aaDown</tt>), but you can also move it to the right (<tt>aaRight</tt>), or turn this feature off (<tt>aaNone</tt>) - see the type <tt>TAutoAdvance</tt> defined in the unit <tt>grids.pas</tt> for even more options.<br />
<br />
If - as assumed above - the WorkbookSource option <tt>boAutoCalc</tt> is enabled the worksheet automatically supports calculation of '''formulas'''. As an example, go to cell A1, and enter the number <tt>10</tt>. Then, go to cell A2 and enter the formula <tt>=A1+10</tt>. The formula is automatically evaluated, and its result, <tt>20</tt>, is displayed in cell A2. <br />
<br />
When you navigate in the grid you may notice that cell A2 only displays the formula result, it seems that there is no way to modify the formula once it has been entered. No need to worry - press the key {{keypress|F2}} or click into the cell a second time to enter '''enhanced edit mode''' in which formulas are visible in the cell.<br />
<br />
[[file:sCellIndicator_sCellEdit.png|right|400px]]<br />
In order to edit formulas the Office applications offer a dedicated formula editor bar. Of course, fpspreadsheet has this feature, too. It is built into the '''TsCellEdit''' component which is set up such as to always show the full content of a cell. You remember the second toolbar from the "Preparations" section? This will house the TsCellEdit. But wait a minute - there's more to consider: Since formulas occasionally may get rather long the control should be capable of managing serval lines. The same with multi-lined text. TsCellEdit can do this since it is inherited from TCustomMemo which is a multi-line control. You also remember that we added a splitter to the form of the second toolbar? This is for height adjustment for the case that we want to use the multi-line feature of the TsCellEdit: just drag the splitter down to show more lines, or drag it upwards to stop at the height of a single line due to the MinHeight constraints that we had assigned to the toolbar.<br />
<br />
The TsCellEdit will cover all available space in the second toolbar. Before we add the TsCellEdit we can make life easier if we think about what else will be in the second toolbar. In Excel, there is an indicator which displays the address of the currently active cell. This is the purpose of the '''TsCellIndicator'''. Since its height should not change when the toolbar is dragged down we first add a '''TPanel''' to the second toolbar; reduce its <tt>Width</tt> to about 100 pixels, remove its <tt>Caption</tt> and set its <tt>BevelOuter</tt> to <tt>bvNone</tt>.<br />
<br />
Add the TsCellIndicator to this panel and align it to the top of the panel. Connect its <tt>WorkbookSource</tt> to the TsWorkbookSource control on the form, and immediately you'll see the text "A1", the address of the currenly selected cell.<br />
<br />
Sometimes it is desirable to change the width of this box at runtime. So, why not add a splitter to the second toolbar? Set its <tt>Align</tt> property to <tt>alLeft</tt>. The result is a bit strange: the splitter is at the very left edge of the toolbar, but you'd expect to see it at the right of the panel. This is because the panel is not aligned by default. Set the <tt>Align</tt> property of the panel to <tt>alLeft</tt> as well, and drag the splitter to the right of the panel. Now the splitter is at the correct position.<br />
<br />
Almost done now... We finally add a TsCellEdit component to the empty space of the toolbar. Client-align it so that it fills the entire rest of the toolbar. As usual, set its <tt>WorkbookSource</tt> property to the instance of the TsWorkbookSource on the the form.<br />
<br />
Compile and run. Play with the program:<br />
<br />
* Enter some dummy data. Navigate in the worksheet. You'll see that the CellIndicator always shows the address of the active cell. The contents of the active cell is displayed in the CellEdit box. The CellIndicator is not just a passive display of the current cell, it can also be edited. Type in the address of a cell which you want to become active, press {{keypress|ENTER}}, and see what happens...<br />
* Enter a formula. Navigate back into the formula cell - the formula is displayed in the CellEdit and can be changed there readily.<br />
* Enter multi-lined text - you can enforce a lineending in the CellEdit by holding the {{keypress|Ctrl}} key down when you press {{keypress|ENTER}}. The cell displays only one line of the text. Drag the horizontal splitter underneath the second toolbar down - the CellEdit shows all lines. Another way to see all lines of the text, is to adjust the cell height. You must have activated the grid <tt>Option</tt> <tt>goRowSizing</tt>. Then you can drag the lower dividing line of the row with the multi-line cell down to increase the row height - the missing lines now appear in the cell!<br />
<br />
=== Formatting of cells ===<br />
<br />
In addition to entering data the user usually wants to apply some formatting to the cells in order to enhance or group them. The worksheet grid is set up in such a way that its cells display the formats taken from the workbook. In addition, the visual FPSpreadsheet controls are able to store formatting attributes into the cell. Because of the notification mechanism via the WorkbookSource these formats are returned to the WorksheetGrid for display.<br />
<br />
==== Adding comboboxes for font name, font size, and font color ====<br />
[[file:sCellFontCombobox.png|right|400px]]<br />
In this section, we want to provide the possibility to modify the font of the cell texts by selecting its name, size and/or color. The visual FPSpreadsheet provide the flexible '''TsCellCombobox''' for this purpose. It has the property <tt>CellFormatItem</tt> which defines which attribute it controls:<br />
* <tt>cfiFontName</tt>: This option populates the combobox with all fonts found in the current system. The selected item is used for the type face in the selected cells.<br />
* <tt>cfiFontSize</tt> fills the combobox with the mostly used font sizes (in points). Again, the selected item defines the font size of the selected cells.<br />
* <tt>cfiFontColor</tt> adds all pre-defined colors ("palette") of the workbook to the combobox to set the text color of the selected cells. The combobox items consist of a little color box along with the color name. If the <tt>ColorRectWidth</tt> is set to <tt>-1</tt> the color name is dropped.<br />
* <tt>cfiBackgroundColor</tt>, the same with the background color of the selected cells.<br />
* <tt>cfiCellBorderColor</tt>, the same with the border color of the selected cells - this feature is currently not yet supported.<br />
<br />
Add three TsCellComboboxes to the first toolbar and set their <tt>CellFormatItem</tt> to <tt>cfiFontname</tt>, <tt>cfiFontSize</tt>, and <tt>cfiFontColor</tt>, respectively. Link their <tt>WorkbookSource</tt> property to the TsWorkbookSource on the form. You may want to increase the width of the font name combobox such that the longest font names are not cut off; the other comboboxes may become narrower. You may also want to turn off the color names of the third combobox by setting its <tt>ColorRectWidth</tt> to <tt>-1</tt>.<br />
<br />
That's all to modify fonts. Compile and run. Enter some text and play with these new features of the program.<br />
<br />
==== Using standard actions ====<br />
[[file:sFontStyleAction_selected.png|right|300px]]<br />
FPSpreadsheet supports a lot of formats that can be applied to cells, such as text alignment, text rotation, text font, or cell borders or background colors. Typical gui applications contain menu commands and/or toolbar buttons which are assigned to each of these properties and allow to set them by a simple mouse click. In addition, the state of these controls often reflects the properties of the active cell. For example, if there is a button for using a bold type-face this button should be drawn as being pressed if the active cell is bold, but as released if it is not. To simplify the coding of these tasks a large number of standard actions has been added to the library. <br />
<br />
* '''TsWorksheetAddAction''': adds an empty worksheet to the workbook. Specify its name in the <tt>NameMask</tt> property. The <tt>NameMask</tt> must contain the format specifier <tt>%d</tt> which is replaced at runtime by a number such that the worksheet name is unique.<br />
* '''TsWorksheetDeleteAction''': deletes the active worksheet from the workbook after a confirmation dialog. The last worksheet cannot be deleted.<br />
* '''TsWorksheetRenameAction''': renames the active worksheet.<br />
* '''TsCopyAction''': Copies the currently selected cells to an internal list ("CellClipboard") from where they can be pasted back into the spreadsheet to another location. The process can occur in a clipboard-manner ("copy"/"cut", then "paste") or in the way of the "copy brush" of the Office applications. The property <tt>CopyItem</tt> determines whether the entire cell, or only cell values, cell formulas, or cell formats are transferred. <br />
* '''TsFontStyleAction''': Modifies the font style of the selected cells. The property <tt>FontStyle</tt> defines whether the action makes the font bold, italic, underlined or striked-out. Normally each font style is handles by its own action. See the example below.<br />
* '''TsHorAlignmentAction''': Can be used to modify the horizontal alignment of text in the selected cells. Select <tt>HorAlignment</tt> to define which kind of alignment (left, center, right) is covered by the action. Like with the TsFontStyleAction, several actions should be provided to offer all available alignments. They are grouped in a mutually exclusive way like radiobuttons.<br />
* '''TsVertAlignmentAction''': Changes the vertical alignment of text in the selected cells: the kind of alignment is defined by the <tt>VertAlignment</tt> property. Again, these actions work like radiobuttons.<br />
* '''TsTextRotationAction''': Allows to specify the text orientation in the selected cells as defined by the property <tt>TextRotation</tt> in a mutially exclusive way.<br />
* '''TsWordWrapAction''': Activates the word-wrapping feature for the selected cells: if text is longer than the width of the cell (or height, if the text is rotated) then it is wrapped into multiple lines.<br />
* '''TsNumberFormatAction''': Defines the number format to be used for the selected cells. The format to be used is defined by the properties <tt>NumberFormat</tt> (such as <tt>nfFixed</tt>) for built-in formats, and <tt>NumberFormatStr</tt> for specialized formatting.<br />
* '''TsDecimalsAction''': Allows to increase or decrease the number of decimal places shown in the selected cells. The property <tt>Delta</tt> controls whether an increase (+1) or decrease (-1) is wanted.<br />
* '''TsCellBorderAction''': Allows to specify if a border will be drawn around the selected cells. The subproperties <tt>East</tt>, <tt>West</tt>, <tt>North</tt>, <tt>South</tt>, <tt>InnerHor</tt>, <tt>InnerVert</tt> of <tt>Borders</tt> define what the border will look like at each side of the cell range. Note that each rectangular range of cells is considere as a single block; the properties <tt>East</tt>, <tt>West</tt>, <tt>North</tt> and <tt>South</tt> are responsible for the outer borders of the entire block, inner borders are defined by <tt>InnerHor</tt> and <tt>InnerVert</tt>. Using these properties, borders can be switched on and off (<tt>Visible</tt>), and in addition, the line style and line color can be changed.<br />
* '''TsMergeAction''': If checked, the cells of each selected rectangular range are merged to a single block. Unchecking the action separates the block to individual cells. Note that the block's content and formatting is defined by the top-left cell of each block; content and formats of other cells will be lost.<br />
* '''TsCellProtectionAction''': comes in two flavors, one for protecting cells from modifications, and one for hiding formulas, depending on the value of the property <tt>Protection</tt>. Note that this action is effective only if worksheet protection has been enabled by calling <tt>Workbooksource.Worksheet.Protect(true)</tt>.<br />
<br />
==== Adding buttons for "Bold", "Italic", and "Underline" ====<br />
[[file:sFontStyleAction_in_ActionListEditor.png|right|350px]]<br />
If you have never worked with '''standard actions''' before here are some detailed '''step-by-step instructions'''. Let us stick to above example and provide the possibility to switch the font style of the selected cells to '''bold'''. The standard action which is responsible for this feature is the <tt>TsFontStyleAction</tt>. <br />
<br />
* At first, we add this action to the form: Double-click on the '''TActionList''' to open the "ActionList Editor". <br />
* Click on the down-arrow next to the "+" button, and select the item "New standard action" from the drop-down menu. <br />
* This opens a dialog with the list of registered "Standard Action Classes". <br />
* Scroll down until you find a group named "FPSpreadsheet". <br />
* In this group, select the item "TsFontStyleAction" by double-clicking. <br />
* Now an item <tt>sFontStyleAction1</tt> appears in the ActionList Editor. <br />
* It should already be selected like in the screenshot at the right. If not, select <tt>sFontStyleAction1</tt> in the ActionList Editor to bring it up in the Object Inspector and to set up its properties:<br />
** Use the text "Bold" for the <tt>Caption</tt> - this is the text that will be assigned to the corresponding menu item.<br />
** Similarly, assign "Bold font" to the <tt>Hint</tt> property.<br />
** Set the <tt>ImageIndex</tt> to the index of the icon in the form's ImageList that you want to see in the toolbar.<br />
** Make sure that the item <tt>fssBold</tt> is highlighted in the dropdown list of the property <tt>FontStyle</tt>. If not, select it. Since <tt>TsFontStyleAction</tt> can handle several font styles (bold, italic, underline, strikeout) we have to tell the action which font style it should be responsible of. <br />
** Like with the visual controls, don't forget to assign the TsWorkbookSource to the corresponding property <tt>WorkbookSource</tt> of the action. This activates the communication between the worksheet/workbook on the one hand, and the action and the related controls on the other hand. <br />
<br />
Having set up the standard action we add a menu item to the form's '''MainMenu'''. Double-click on the TMainMenu of the form to bring up the "Menu Editor". Since the menu is empty so far there is only a dummy item, "New item1". This will become our "Format" menu. Select the item, and type "Format" into the <tt>Caption</tt> property field. Now the dummy item is re-labelled as "Format". Right-click on this "Format" item, and select "Create submenu" from the popup menu which brings up another new menu item, "New item2". Select it. In the dropdown list of the property <tt>Action</tt> of the object inspector, pick the <tt>sFontStyle1</tt> action - this is the action that we have just set up - and the menu item automatically shows the caption provided by the action component, "Bold". <br />
<br />
Finally we add a '''toolbar button''' for the "bold" action. Right-click onto the TToolbar, and add a new toolbutton by selecting item "New button" from the popup menu. Go to the property <tt>Action</tt> in the object inspector again, pick the <tt>sFontStyle1</tt> item, and this is enough to give the tool button the ability to set a cell font to bold!<br />
<br />
Repeat this procedure with two other buttons. Design them to set the font style to '''italic''' and '''underlined'''.<br />
<br />
Test the program by compiling. Type some text into cells. Select one of them and click the "Bold" toolbutton - voila, the cell is in bold font. Select another cell. Note that the toolbutton is automatically drawn in the down state if the cell has bold font. Repeat with the other buttons.<br />
<br />
=== Saving to file ===<br />
<br />
After having entered data into the grid you will certainly want to '''save the grid to a spreadsheet file'''. Lazarus provides all the necessary infrastructure for saving available in the standard action <tt>TFileSaveAs</tt>. This action automatically opens a '''FileDialog''' for entering the file name. <br />
<br />
Select the <tt>TFileSaveAs</tt> standard action from the list of standard action classes. Note that it cannot be found in the "FPSpreadsheet" category, but in the "File" group since it is a standard action of the LCL.<br />
<br />
[[file:sFileFormatsForSaving.png|right|400px]]<br />
<br />
At first, let us specify the properties of the FileDialog. Select the property <tt>Dialog</tt> of the <tt>TFileSaveAs</tt> action in the object inspector. It is convenient to be able to store the workbook in various file formats; this can be prepared by providing a file format list in the <tt>Filter</tt> property of the dialog. Paste the following text into this property:<br />
<br />
:<tt>Excel XML spreadsheet (*.xlsx)|*.xlsx|Excel 97-2003 spreadsheets (*.xls)|*.xls|Excel 5 spreadsheet (*.xls)|*.xls|Excel 2.1 spreadsheets (*.xls)|*.xls|LibreOffice/OpenOffice spreadsheet (*.ods)|*.ods|Comma-delimited files (*.csv)|*.csv|WikiTable (WikiMedia-Format, *.wikitable_wikimedia)|*.wikitable_wikimedia</tt><br />
<br />
When you click on the ellipsis button next to <tt>Filter</tt> the file list appears in a more clearly arranged dialog shown at the right.<br />
<br />
Make one of these file extensions, e.g. xlsx, the default of the file dialog by assigning its list index to the <tt>FilterIndex</tt> property. The xlsx file is the first format in the filter list. <tt>FilterIndex</tt>, therefore, must be set to 1. <br />
<br />
{{Note|The indexes in the filter list are 1-based, in contrast to the convention of Lazarus and FPC using 0-based indexes.}}<br />
<br />
Next, we define what happens after a file name has been selected in the file dialog. For this purpose, the <tt>TFileSaveAs</tt> action provides the event <tt>OnAccept</tt>. This is one of the few places where we have to write code in this project... But it is short: We check which file format has been selected in the format list and write the corresponding spreadsheet file by calling the method <tt>SaveToSpreadsheetFile</tt> of the TWorkbookSource:<br />
<br />
<syntaxhighlight><br />
uses<br />
..., fpstypes, ...; // for TsSpreadsheetFormat<br />
<br />
procedure TForm1.FileSaveAs1Accept(Sender: TObject);<br />
var<br />
fmt: TsSpreadsheetFormat;<br />
begin<br />
Screen.Cursor := crHourglass;<br />
try<br />
case FileSaveAs1.Dialog.FilterIndex of<br />
1: fmt := sfOOXML; // Note: Indexes are 1-based here!<br />
2: fmt := sfExcel8;<br />
3: fmt := sfExcel5;<br />
4: fmt := sfExcel2;<br />
5: fmt := sfOpenDocument;<br />
6: fmt := sfCSV;<br />
7: fmt := sfWikiTable_WikiMedia;<br />
end;<br />
sWorkbookSource1.SaveToSpreadsheetFile(FileSaveAs1.Dialog.FileName, fmt);<br />
finally<br />
Screen.Cursor := crDefault;<br />
end;<br />
end; <br />
</syntaxhighlight><br />
<br />
We will make the FileSaveAs action available in the toolbar and in the menu:<br />
* '''Toolbar''': Add a TToolButton to the first toolbar and drag it to its left edge. Assign the FileSaveAs action to its <tt>Action</tt> property.<br />
* '''Menu''': The "Save" command is usually in a submenu called "File". Therefore, double click on the TMainMenu, right-click on the "Format" item and insert a new item "before" the current one. Name it "File". Add a submenu to it. Click at the default menu item and assign the FileSaveAs action to its <tt>Action</tt> property.<br />
<br />
=== Reading from file ===<br />
<br />
What is left is '''reading of a spreadsheet file''' into our application. Of course, FPSpreadsheet is well-prepared for this task. The operations are very similar to saving. But instead of using a TFileSaveAs standard action, we use a '''TFileOpen''' standard action. Again, this standard action has a built-in file dialog where we have to set the <tt>DefaultExtension</tt> (".xls" or ".xlsx", most probably) and the format <tt>Filter</tt>:<br />
<br />
:<tt>All spreadsheet files|*.xls;*.xlsx;*.ods;*.csv|All Excel files (*.xls, *.xlsx)|*.xls;*.xlsx|Excel XML spreadsheet (*.xlsx)|*.xlsx|Excel 97-2003 spreadsheets (*.xls)|*.xls|Excel 5 spreadsheet (*.xls)|*.xls|Excel 2.1 spreadsheets (*.xls)|*.xls|LibreOffice/OpenOffice spreadsheet (*.ods)|*.ods|Comma-delimited files (*.csv)|*.csv</tt><br />
<br />
(Copy this string into the field <tt>Filter</tt> of the action's <tt>Dialog</tt>). As you may notice the <tt>Filter</tt> contains selections which cover various file formats, such as "All spreadsheet files", or "All Excel files". This is possible because the TsWorkbookSource has a property <tt>AutoDetectFormat</tt> for automatic detection of the spreadsheet file format. In the other cases, like "Libre/OpenOffice", we can specify the format, <tt>sfOpenDocument</tt>, explicitly. Evaluation of the correct file format and reading of the file is done in the <tt>OnAccept</tt> event handler of the action:<br />
<br />
<syntaxhighlight><br />
{ Loads the spreadsheet file selected by the FileOpen standard action }<br />
procedure TForm1.FileOpen1Accept(Sender: TObject);<br />
begin<br />
sWorkbookSource1.AutodetectFormat := false;<br />
case FileOpen1.Dialog.FilterIndex of<br />
1: sWorkbookSource1.AutoDetectFormat := true; // All spreadsheet files<br />
2: sWorkbookSource1.AutoDetectFormat := true; // All Excel files<br />
3: sWorkbookSource1.FileFormat := sfOOXML; // Excel 2007+<br />
4: sWorkbookSource1.FileFormat := sfExcel8; // Excel 97-2003<br />
5: sWorkbookSource1.FileFormat := sfExcel5; // Excel 5.0<br />
6: sWorkbookSource1.FileFormat := sfExcel2; // Excel 2.1<br />
7: sWorkbookSource1.FileFormat := sfOpenDocument; // Open/LibreOffice<br />
8: sWorkbookSource1.FileFormat := sfCSV; // Text files<br />
end;<br />
sWorkbookSource1.FileName :=FileOpen1.Dialog.FileName; // This loads the file<br />
end;<br />
</syntaxhighlight><br />
<br />
In order to see this action in the toolbar and menu, add a TToolButton to the '''toolbar''' and assign the TFileOpenAction to its <tt>Action</tt> property. In the '''menu''', add a new item before the "Save as" item, and assign its <tt>Action</tt> accordingly.<br />
<br />
{{Note|You can see a spreadsheet file even at designtime if you assign its name to the <tt>Filename</tt> property of the TsWorkbookSource. But be aware that the file probably cannot be found at runtime if it is specified by a relative path and if the application is to run on another computer with a different directory structure! }}<br />
<br />
== Summary ==<br />
If you followed us through the steps of this tutorial you have programmed a complex spreadsheet gui application almost without having written any line of code (with the exception of the loading and saving routines). If you did not, have a look at the demo "fps_ctrls" in the ''examples'' folder of the FPSpreadsheet installation; it shows the result of this tutorial with some add-ons. <br />
<br />
[[Category:Tutorials]]<br />
[[Category:Data import and export]]<br />
[[Category:FPSpreadsheet]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=DateTimeCtrls_Package&diff=114970DateTimeCtrls Package2018-01-08T10:47:21Z<p>Zoran: /* Getting the package */</p>
<hr />
<div>== About ==<br />
The DateTimeCtrls package contains two controls:<br />
<br />
[[Image:TDateTimePicker.png]] [[#TDateTimePicker|TDateTimePicker]]<br />
and<br />
[[Image:TDBDateTimePicker.png]][[#TDBDateTimePicker|TDBDateTimePicker]]<br />
<br />
Delphi's VCL has a control named [http://docwiki.embarcadero.com/Libraries/en/Vcl.ComCtrls.TDateTimePicker TDateTimePicker], which I find very useful for editing dates. LCL, however, does not have this control.<br />
<br />
Therefore, I tried to create a cross-platform Lazarus control which would resemble VCL's TDateTimePicker as much as possible.<br />
<br />
The TDateTimePicker does not use [http://msdn.microsoft.com/en-us/library/system.windows.forms.datetimepicker.aspx native Win control]. It descends from LCL-s TCustomControl to be cross-platform. It has been tested on Windows with Win32/64 and qt widgetsets and on Linux with gtk2 and qt widgetsets.<br />
<br />
Note that the TDateTimePicker control does not descend from TEdit, so it does not have unnecessary caret. The VCL's control doesn't have caret either.<br />
<br />
== Author ==<br />
:[[User:Zoran| Zoran Vučenović]]<br />
<br />
== License ==<br />
Modified LGPL (same as the FPC RTL and the Lazarus LCL).<br />
<br />
== Getting the package ==<br />
This package is part of Lazarus distribution (since Lazarus 1.4). You can find it in <''your_lazarus_folder''>/components/datetimectrls.<br />
<br />
The components are installed on Component palette by default (if you build the IDE yourself, they are installed with "make bigide"). The components TDateTimePicker and TDBDateTimePicker are located in [[Common_Controls_tab|Common Controls]] and [[Data_Controls_tab|Data Controls]] palette pages, respectively.<br />
<br />
Since Lazarus 1.8 the designtime code is separated to another package DateTimeCtrlsDsgn. Normally, you should not care about this, but only if you are installing the controls in the IDE manually, you should know that it is actually the designtime package DateTimeCtrlsDsgn which is installed in the IDE.<br />
<br />
This separation of designtime code to different package is done to prevent the code which is needed only in design time to be linked in final executable. [[Lazarus_1.8.0_release_notes#DateTimeCtrls|See the 1.8 release notes.]]<br />
<br />
== TDateTimePicker [[Image:TDateTimePicker.png]] ==<br />
<br />
=== Properties ===<br />
<br />
I'll explain some properties of TDateTimePicker control:<br />
<br />
==== DateTime: TDateTime (public) ====<br />
<br />
:The DateTime value displayed on the control. This property is not published in object inspector, but its value is actually the same as Date and Time properties composed in one value. This property is provided to allow setting or reading of both date and time value at once in program code. In design time, Date and Time can be set in object inspector. [[#DateTimePicker Editor|There is also component editor]] which provides easy way of setting this property in design time.<br />
<br />
==== Date: TDate ====<br />
<br />
:The date displayed on the control which the user can edit.<br />
<br />
==== Time: TTime ====<br />
<br />
:The time displayed on the control which the user can edit.<br />
<br />
==== MinDate: TDate ====<br />
<br />
:The minimal date user can enter.<br />
<br />
==== MaxDate: TDate ====<br />
<br />
:The maximal date user can enter.<br />
<br />
==== NullInputAllowed: Boolean ====<br />
<br />
:When True, the user can set the date to NullDate constant by pressing N key.<br />
<br />
==== CenturyFrom: Word ====<br />
<br />
:When user enters the year in two-digit format, then the CenturyFrom property is used to determine which century the year belongs to. The default is 1941, which means that when two digit year is entered, it falls in interval 1941 – 2040. Note that [[#MinDate: TDate|MinDate]] and [[#MaxDate: TDate|MaxDate]] properties can also have influence on the decision – for example, if the CenturyFrom is set to 1941 and MaxDate to 31. 12. 2010, if user enters year 23, it will be set to 1923, because it can’t be 2033, due to MaxDate limit.<br />
<br />
==== Kind: TDateTimeKind ====<br />
<br />
:'''type''' TDateTimeKind = (dtkDate, dtkTime, dtkDateTime);<br />
<br />
:The control displays only date, only time or both.<br />
:[[Image:DateControls03.PNG]]<br />
<br />
==== DateMode: TDTDateMode ====<br />
<br />
:'''type''' TDTDateMode = (dmComboBox, dmUpDown, dmNone)<br />
<br />
:When DateMode is set to dmComboBox, there is a button on the right side of the control. When user clicks the button, [http://lazarus-ccr.sourceforge.net/docs/lcl/calendar/tcalendar.html the calendar control] is shown, allowing the user to pick the date. When set to dmUpDown, then UpDown buttons are shown.<br />
<br />
:In my opinion the UpDown buttons aren't really useful in this control, they are provided for compatibility with Delphi's TDateTimePicker. Up and down keys can always serve for same purpose, so can mouse wheel.<br />
<br />
:In the picture the first control's DateMode is set to dmComboBox and the second control's to dmUpDown:<br />
:[[Image:DateControls01.PNG]]<br />
<br />
==== ShowCheckBox: Boolean ====<br />
<br />
:When set, there is a check box on the left side of the control.<br />
:By default, when unchecked, the display appears grayed and user interaction with the date is not possible. (The control is still enabled, though, only in sense that the check box remains enabled). This behaviour may be changed if dtpoEnabledIfUnchecked is added to [[#Options:_TDateTimePickerOptions|Options property]] -- then this check box is shown, but does nothing by itself (doesn't disable the control) and the programmer can decide how it will be used (by using OnCheckBoxChange event).<br />
:[[Image:DateControls02.PNG]]<br />
<br />
==== Checked: Boolean ====<br />
<br />
:If [[#ShowCheckBox: Boolean|ShowCheckBox]] is set to True, this property determines whether the check box is checked or not. If ShowCheckBox is False, this property has no purpose and is automatically set to True.<br />
<br />
==== DateDisplayOrder: TDateDisplayOrder ====<br />
<br />
:'''type''' TDateDisplayOrder = (ddoDMY, ddoMDY, ddoYMD, ddoTryDefault);<br />
<br />
:Defines the order for displaying day, month and year part of the date. When ddoTryDefault is set, then the controls tries to determine the order from [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/shortdateformat.html ShortDateFormat global variable].<br />
:This is similar to DateEdit's [http://lazarus-ccr.sourceforge.net/docs/lcl/editbtn/tdateedit.dateorder.html DateOrder] property.<br />
<br />
==== DateSeparator: String ====<br />
<br />
:Defines the string used to separate date, month and year date parts. Setting this property automatically sets the [[#UseDefaultSeparators: Boolean|UseDefaultSeparators]] property to False. To ensure that date and time separators are set to user's system defaults, set UseDefaultSeparators property to True.<br />
<br />
==== TimeSeparator: String ====<br />
<br />
:Defines the string used to separate hour, minute, second and millisecond time parts. Setting this property automatically sets the [[#UseDefaultSeparators: Boolean|UseDefaultSeparators]] property to False. To ensure that date and time separators are set to user's system defaults, set UseDefaultSeparators property to True.<br />
<br />
==== UseDefaultSeparators: Boolean ====<br />
<br />
:When this property is set to True, then the [[#DateSeparator: String|DateSeparator]] and [[#TimeSeparator: String|TimeSeparator]] properties will be set to [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/dateseparator.html DateSeparator] and [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/timeseparator.html TimeSeparator] global variables, which are set to user system defaults when application initializes.<br />
<br />
==== TrailingSeparator: Boolean ====<br />
<br />
:When set to True, then the [[#DateSeparator: String|DateSeparator]] is shown once more, after the last date part. This property exists because in some languages the correct format is 31. 1. 2010. including the last point, after the year.<br />
<br />
==== LeadingZeros: Boolean ====<br />
<br />
:Determines whether the date parts are displayed with or without leading zeros (this actually affects day, month and hour parts of date and time display).<br />
<br />
==== TimeDisplay: TTimeDisplay ====<br />
<br />
:'''type''' TTimeDisplay = (tdHM, tdHMS, tdHMSMs);<br />
<br />
:If Kind is dtkTime or dtkDateTime, then TimeDisplay value of tdHM means that only hours and minutes are displayed, tdHMS adds displaying of seconds and value of tdHMSMs means that milliseconds are displayed too.<br />
<br />
==== TimeFormat: TTimeFormat ====<br />
<br />
:'''type''' TTimeFormat = (tf12, tf24);<br />
<br />
:The value of tf12 sets the display of time to 12 hours format, with AM/PM string and tf24 sets to 24 hours format.<br />
<br />
==== TextForNullDate: String ====<br />
<br />
:Text which appears when the null date is set and control does not have focus. When control is focused, the text changes to defined format, but displaying zeros, which is appropriate to user input. User can set the date to NullDate by pressing N key, provided [[#NullInputAllowed: Boolean|NullInputAllowed]] property is True.<br />
:When TextForNullDate is set to empty string, zeros/nines format is displayed even when control does not have focus. If you want empty display, this can be acchieved by setting TextForNullDate to one or more space characters.<br />
<br />
==== AutoAdvance: Boolean ====<br />
<br />
:When true, then when user is entering valid text, the selection automatically advances to next part of date/time. The default is True, because it makes user interaction easier and this behaviour is what a user should expect.<br />
<br />
==== Cascade: Boolean ====<br />
<br />
:When true, then when user is increasing or decreasing one date/time part (using up-down keys or mouse wheel), it can increase or decrease by one another date/time part. For example, when date is 31.08.2013. and user increases the day, the day becomes 1 and month increases by one and becomes 9, so the date becomes 01.09.2013. If Cascade were set to False, the month would not change and the date would become 01.08.2013.<br />
<br />
==== AutoButtonSize: Boolean ====<br />
<br />
:When true, the width of the arrow button (or up-down control, if it is shown instead) is automatically adjusted proportionally to the height.<br />
<br />
==== HideDateTimeParts: TDateTimeParts ====<br />
<br />
:'''type'''<br />
<br />
::TDateTimePart = (dtpDay, dtpMonth, dtpYear, dtpHour, dtpMinute, dtpSecond, dtpMiliSec, dtpAMPM);<br />
<br />
::TDateTimeParts = set of dtpDay..dtpMiliSec;<br />
<br />
:With HideDateTimeParts property, you can chose which date/time parts will not be shown. Most of the time you do not need to use this property and you can get the format you want by using other properties (see Kind, TimeDisplay). However, if you need more control (for example, you might want to let user edit only days, months and hours), you can additionally hide any date/time parts with this control. Keep in mind that, with this property, you cannot show any date/time part which is hidden by another property (for example, if TimeDisplay is tdHM, the second part is not shown, regardless of this property).<br />
<br />
==== CalendarWrapperClass: TCalendarControlWrapperClass (public) ====<br />
<br />
:When assigned, this property determines the type of the calendar control used for drop-down calendar. When set to nil, which is the default, the value of global variable DefaultCalendarWrapperClass is used. [[#Using some custom calendar control for drop-down|More details here]].<br />
<br />
==== ShowMonthNames: Boolean ====<br />
:When this property is set to True, month names, set in [[#MonthNames: String|MonthNames]] property, will be displayed instead of numbers.<br />
<br />
==== MonthNames: String ====<br />
:When [[#ShowMonthNames: Boolean|ShowMonthNames]] is set to True, this property determines which month names should appear.<br />
:If MonthNames is set to 'Short' or 'Long', then month names are set to [http://www.freepascal.org/docs-html/rtl/sysutils/shortmonthnames.html ShortMonthNames] or [http://www.freepascal.org/docs-html/rtl/sysutils/longmonthnames.html LongMonthNames] respectively.<br />
:To set the month names explicitly, this property should be set to string which starts with a character which will be used to separate months and then twelve names separated by this character. For example ',I,II,III,IV,V,VI,VII,VIII,IX,X,XI,XII' — roman numbers are used for months — the first character is comma, which means that comma is used to separate the months. Another valid example ';jan;feb;mar;apr;maj;jun;jul;avg;sep;okt;nov;dec'.<br />
:So, the separator should be the first character, before the first month name, and then only if there are twelve month names separated by that separator, the format is valid.<br />
:The default value of this property is 'Long'.<br />
<br />
==== Options: TDateTimePickerOptions ====<br />
<br />
* dtpoDoChangeOnSetDateTime: the OnChange handler will be called also when DateTime is programatically changed.<br />
* dtpoEnabledIfUnchecked: enable the date time picker if the checkbox is unchecked.<br />
* dtpoAutoCheck: auto-check an unchecked checkbox when DateTime is changed (makes sense only if dtpoEnabledIfUnchecked is set).<br />
* dtpoFlatButton: use flat button for calender picker.<br />
<br />
<br />
=== DateTimePicker Editor ===<br />
<br />
[[Image:datetimepickereditor.png]]<br />
<br />
:DateTimePicker Editor is a dialog which provides easy way to edit Date, Time, MinDate and MaxDate properties in design time. It is invoked when DateTimePicker control is double-clicked in form designer. It is also shown when the ellipsis (…) button shown in Date, Time, MinDate and MaxDate properties in Object inspector gets clicked.<br />
<br />
<br />
=== User interaction in runtime ===<br />
<br />
:The goal was to provide same user interaction as native DateTimePicker from Windows. The keyboard input is restricted to valid date/time values only. Up and down buttons can be used too. The contros behaves just like VCL's control.<br />
<br />
:After this behaviour is achieved it is further improved with mouse wheel interaction. Date and time parts can be edited using mouse wheel, which is very fast and comes handy.<br />
<br />
<br />
=== Using some custom calendar control for drop-down ===<br />
<br />
Instead of LCL's TCalendar, some other calendar control can be used for drop-down. This calendar control has to provide a way to determine if the coordinates (within the calendar control) are on the date or not [[#AreCoordinatesOnDate|(see AreCoordinatesOnDate function)]]. DateTimePicker needs it to decide if it should close the drop-down calendar and take its date when the calendar gets clicked (see AreCoordinatesOnDate function). The calendar only needs to be derived from TControl. The mouse interaction with calendar will work and, if it is also TWinControl's descendent, the keyboard interaction will also work.<br />
<br />
Now, let's see how to use some calendar control for drop-down control. First, we need to define the "wrapper" class, then we will have to tell the DateTimePicker control to use this wrapper instead of default one.<br />
<br />
==== Definning the wrapper class ====<br />
<br />
:In unit calendarcontrolwrapper.pas, there is an abstract class TCalendarControlWrapper. We need to derive a new class from this abstract class. There are four abstract methods which new class has to override - GetCalendarControlClass, SetDate, GetDate and AreCoordinatesOnDate.<br />
:This should be pretty simple, please see the file lclcalendarwrapper.pas, where TLCLCalendarWrapper (the default wrapper) is defined.<br />
<br />
===== GetCalendarControlClass =====<br />
<br />
:<syntaxhighlight><br />
class function GetCalendarControlClass: TControlClass; virtual abstract;<br />
</syntaxhighlight><br />
:This class function should return the class of the wrapped calendar control.<br />
<br />
===== SetDate =====<br />
<br />
:<syntaxhighlight><br />
procedure SetDate(Date: TDate); virtual abstract;<br />
</syntaxhighlight><br />
:Should be overriden to set the date in the calendar control.<br />
<br />
===== GetDate =====<br />
<br />
:<syntaxhighlight><br />
function GetDate: TDate; virtual abstract;<br />
</syntaxhighlight><br />
:Should be overriden to get the date from the calendar control.<br />
<br />
===== AreCoordinatesOnDate =====<br />
<br />
:<syntaxhighlight><br />
function AreCoordinatesOnDate(X, Y: Integer): Boolean; virtual abstract; <br />
</syntaxhighlight><br />
:This function should return True if coordinates (X, Y) are on the date in the calendar control (DateTimePicker calls this function when the calendar is clicked, to determine whether the drop-down calendar should return the date or not). The calendar control must at least provide a way to determine whether the coordinates are on the date (when this control gets clicked, we must decide if the date has just been chosen — then we should respond by closing the drop-down form and setting the date from calendar to DateTimePicker — for example in LCL's TCalendar we will respond when the calendar is clicked on date, but not when the user clicks in title area changing months or years, then we let the user keep browsing the calendar).<br />
<br />
==== Telling to the DateTimePicker to use new wrapper ====<br />
<br />
:Now, when we have the wrapper class defined, the easiest way to make all DateTimePickers use the new control is setting global variable DefaultCalendarWrapperClass (declared in unit datetimepicker.pas) and all DateTimePickers in the project will use the new control.<br />
<br />
:There is public property CalendarWrapperClass in DateTimePicker. We can set it and DateTimePicker will use it.<br />
<br />
:This pseudo code shows how a DateTimePicker decides which calendar control to use:<br />
<br />
<syntaxhighlight lang="text"><br />
if property DateTimePicker.CalendarWrapperClass is not nil<br />
the property is used<br />
else if global variable DefaultCalendarWrapperClass is not nil<br />
the global variable is used<br />
else<br />
LCL's TCalendar (implemented through TLCLCalendarWrapper class) is used<br />
</syntaxhighlight><br />
<br />
== TDBDateTimePicker [[Image:TDBDateTimePicker.png]] ==<br />
<br />
:TDBDateTimePicker is a data-aware version of TDateTimePicker, with nice way of handling null database values.<br />
<br />
=== Handling of null values ===<br />
<br />
==== Displaying null values ====<br />
<br />
:When the underlying DB field has null value, then:<br />
<br />
::If the control is not focused, then it displays the text defined in [[#TextForNullDate: String|TextForNullDate]] property. The default is "NULL".<br />
<br />
::When the control gets focus, the text changes to defined format, but displaying zeros for date parts and nines for time parts (for example "00/00/0000 99:99:99"), which is appropriate to user input.<br />
<br />
==== Setting the field value to null ====<br />
<br />
::If [[#NullInputAllowed: Boolean|NullInputAllowed]] property is True, the user can set the date and time to null, by pressing N key.<br />
<br />
[[Category:Components]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=DateTimeCtrls_Package&diff=114969DateTimeCtrls Package2018-01-08T10:38:25Z<p>Zoran: /* Getting the package */</p>
<hr />
<div>== About ==<br />
The DateTimeCtrls package contains two controls:<br />
<br />
[[Image:TDateTimePicker.png]] [[#TDateTimePicker|TDateTimePicker]]<br />
and<br />
[[Image:TDBDateTimePicker.png]][[#TDBDateTimePicker|TDBDateTimePicker]]<br />
<br />
Delphi's VCL has a control named [http://docwiki.embarcadero.com/Libraries/en/Vcl.ComCtrls.TDateTimePicker TDateTimePicker], which I find very useful for editing dates. LCL, however, does not have this control.<br />
<br />
Therefore, I tried to create a cross-platform Lazarus control which would resemble VCL's TDateTimePicker as much as possible.<br />
<br />
The TDateTimePicker does not use [http://msdn.microsoft.com/en-us/library/system.windows.forms.datetimepicker.aspx native Win control]. It descends from LCL-s TCustomControl to be cross-platform. It has been tested on Windows with Win32/64 and qt widgetsets and on Linux with gtk2 and qt widgetsets.<br />
<br />
Note that the TDateTimePicker control does not descend from TEdit, so it does not have unnecessary caret. The VCL's control doesn't have caret either.<br />
<br />
== Author ==<br />
:[[User:Zoran| Zoran Vučenović]]<br />
<br />
== License ==<br />
Modified LGPL (same as the FPC RTL and the Lazarus LCL).<br />
<br />
== Getting the package ==<br />
This package is part of Lazarus distribution (since Lazarus 1.4). You can find it in <''your_lazarus_folder''>/components/datetimectrls.<br />
<br />
The components are installed on Component palette by default (if you build the IDE yourself, they are installed with "make bigide"). The components TDateTimePicker and TDBDateTimePicker are located in "Common Controls" and "Data Controls" palette pages, respectively.<br />
<br />
Since Lazarus 1.8 the designtime code is separated to another package DateTimeCtrlsDsgn. Normally, you should not care about this, but only if you are installing the controls in the IDE manually, you should know that it is actually the designtime package DateTimeCtrlsDsgn which is installed in the IDE.<br />
<br />
This separation of designtime code to different package is done to prevent the code which is needed only in design time to be linked in final executable. [[Lazarus_1.8.0_release_notes#DateTimeCtrls|See the 1.8 release notes.]]<br />
<br />
== TDateTimePicker [[Image:TDateTimePicker.png]] ==<br />
<br />
=== Properties ===<br />
<br />
I'll explain some properties of TDateTimePicker control:<br />
<br />
==== DateTime: TDateTime (public) ====<br />
<br />
:The DateTime value displayed on the control. This property is not published in object inspector, but its value is actually the same as Date and Time properties composed in one value. This property is provided to allow setting or reading of both date and time value at once in program code. In design time, Date and Time can be set in object inspector. [[#DateTimePicker Editor|There is also component editor]] which provides easy way of setting this property in design time.<br />
<br />
==== Date: TDate ====<br />
<br />
:The date displayed on the control which the user can edit.<br />
<br />
==== Time: TTime ====<br />
<br />
:The time displayed on the control which the user can edit.<br />
<br />
==== MinDate: TDate ====<br />
<br />
:The minimal date user can enter.<br />
<br />
==== MaxDate: TDate ====<br />
<br />
:The maximal date user can enter.<br />
<br />
==== NullInputAllowed: Boolean ====<br />
<br />
:When True, the user can set the date to NullDate constant by pressing N key.<br />
<br />
==== CenturyFrom: Word ====<br />
<br />
:When user enters the year in two-digit format, then the CenturyFrom property is used to determine which century the year belongs to. The default is 1941, which means that when two digit year is entered, it falls in interval 1941 – 2040. Note that [[#MinDate: TDate|MinDate]] and [[#MaxDate: TDate|MaxDate]] properties can also have influence on the decision – for example, if the CenturyFrom is set to 1941 and MaxDate to 31. 12. 2010, if user enters year 23, it will be set to 1923, because it can’t be 2033, due to MaxDate limit.<br />
<br />
==== Kind: TDateTimeKind ====<br />
<br />
:'''type''' TDateTimeKind = (dtkDate, dtkTime, dtkDateTime);<br />
<br />
:The control displays only date, only time or both.<br />
:[[Image:DateControls03.PNG]]<br />
<br />
==== DateMode: TDTDateMode ====<br />
<br />
:'''type''' TDTDateMode = (dmComboBox, dmUpDown, dmNone)<br />
<br />
:When DateMode is set to dmComboBox, there is a button on the right side of the control. When user clicks the button, [http://lazarus-ccr.sourceforge.net/docs/lcl/calendar/tcalendar.html the calendar control] is shown, allowing the user to pick the date. When set to dmUpDown, then UpDown buttons are shown.<br />
<br />
:In my opinion the UpDown buttons aren't really useful in this control, they are provided for compatibility with Delphi's TDateTimePicker. Up and down keys can always serve for same purpose, so can mouse wheel.<br />
<br />
:In the picture the first control's DateMode is set to dmComboBox and the second control's to dmUpDown:<br />
:[[Image:DateControls01.PNG]]<br />
<br />
==== ShowCheckBox: Boolean ====<br />
<br />
:When set, there is a check box on the left side of the control.<br />
:By default, when unchecked, the display appears grayed and user interaction with the date is not possible. (The control is still enabled, though, only in sense that the check box remains enabled). This behaviour may be changed if dtpoEnabledIfUnchecked is added to [[#Options:_TDateTimePickerOptions|Options property]] -- then this check box is shown, but does nothing by itself (doesn't disable the control) and the programmer can decide how it will be used (by using OnCheckBoxChange event).<br />
:[[Image:DateControls02.PNG]]<br />
<br />
==== Checked: Boolean ====<br />
<br />
:If [[#ShowCheckBox: Boolean|ShowCheckBox]] is set to True, this property determines whether the check box is checked or not. If ShowCheckBox is False, this property has no purpose and is automatically set to True.<br />
<br />
==== DateDisplayOrder: TDateDisplayOrder ====<br />
<br />
:'''type''' TDateDisplayOrder = (ddoDMY, ddoMDY, ddoYMD, ddoTryDefault);<br />
<br />
:Defines the order for displaying day, month and year part of the date. When ddoTryDefault is set, then the controls tries to determine the order from [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/shortdateformat.html ShortDateFormat global variable].<br />
:This is similar to DateEdit's [http://lazarus-ccr.sourceforge.net/docs/lcl/editbtn/tdateedit.dateorder.html DateOrder] property.<br />
<br />
==== DateSeparator: String ====<br />
<br />
:Defines the string used to separate date, month and year date parts. Setting this property automatically sets the [[#UseDefaultSeparators: Boolean|UseDefaultSeparators]] property to False. To ensure that date and time separators are set to user's system defaults, set UseDefaultSeparators property to True.<br />
<br />
==== TimeSeparator: String ====<br />
<br />
:Defines the string used to separate hour, minute, second and millisecond time parts. Setting this property automatically sets the [[#UseDefaultSeparators: Boolean|UseDefaultSeparators]] property to False. To ensure that date and time separators are set to user's system defaults, set UseDefaultSeparators property to True.<br />
<br />
==== UseDefaultSeparators: Boolean ====<br />
<br />
:When this property is set to True, then the [[#DateSeparator: String|DateSeparator]] and [[#TimeSeparator: String|TimeSeparator]] properties will be set to [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/dateseparator.html DateSeparator] and [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/timeseparator.html TimeSeparator] global variables, which are set to user system defaults when application initializes.<br />
<br />
==== TrailingSeparator: Boolean ====<br />
<br />
:When set to True, then the [[#DateSeparator: String|DateSeparator]] is shown once more, after the last date part. This property exists because in some languages the correct format is 31. 1. 2010. including the last point, after the year.<br />
<br />
==== LeadingZeros: Boolean ====<br />
<br />
:Determines whether the date parts are displayed with or without leading zeros (this actually affects day, month and hour parts of date and time display).<br />
<br />
==== TimeDisplay: TTimeDisplay ====<br />
<br />
:'''type''' TTimeDisplay = (tdHM, tdHMS, tdHMSMs);<br />
<br />
:If Kind is dtkTime or dtkDateTime, then TimeDisplay value of tdHM means that only hours and minutes are displayed, tdHMS adds displaying of seconds and value of tdHMSMs means that milliseconds are displayed too.<br />
<br />
==== TimeFormat: TTimeFormat ====<br />
<br />
:'''type''' TTimeFormat = (tf12, tf24);<br />
<br />
:The value of tf12 sets the display of time to 12 hours format, with AM/PM string and tf24 sets to 24 hours format.<br />
<br />
==== TextForNullDate: String ====<br />
<br />
:Text which appears when the null date is set and control does not have focus. When control is focused, the text changes to defined format, but displaying zeros, which is appropriate to user input. User can set the date to NullDate by pressing N key, provided [[#NullInputAllowed: Boolean|NullInputAllowed]] property is True.<br />
:When TextForNullDate is set to empty string, zeros/nines format is displayed even when control does not have focus. If you want empty display, this can be acchieved by setting TextForNullDate to one or more space characters.<br />
<br />
==== AutoAdvance: Boolean ====<br />
<br />
:When true, then when user is entering valid text, the selection automatically advances to next part of date/time. The default is True, because it makes user interaction easier and this behaviour is what a user should expect.<br />
<br />
==== Cascade: Boolean ====<br />
<br />
:When true, then when user is increasing or decreasing one date/time part (using up-down keys or mouse wheel), it can increase or decrease by one another date/time part. For example, when date is 31.08.2013. and user increases the day, the day becomes 1 and month increases by one and becomes 9, so the date becomes 01.09.2013. If Cascade were set to False, the month would not change and the date would become 01.08.2013.<br />
<br />
==== AutoButtonSize: Boolean ====<br />
<br />
:When true, the width of the arrow button (or up-down control, if it is shown instead) is automatically adjusted proportionally to the height.<br />
<br />
==== HideDateTimeParts: TDateTimeParts ====<br />
<br />
:'''type'''<br />
<br />
::TDateTimePart = (dtpDay, dtpMonth, dtpYear, dtpHour, dtpMinute, dtpSecond, dtpMiliSec, dtpAMPM);<br />
<br />
::TDateTimeParts = set of dtpDay..dtpMiliSec;<br />
<br />
:With HideDateTimeParts property, you can chose which date/time parts will not be shown. Most of the time you do not need to use this property and you can get the format you want by using other properties (see Kind, TimeDisplay). However, if you need more control (for example, you might want to let user edit only days, months and hours), you can additionally hide any date/time parts with this control. Keep in mind that, with this property, you cannot show any date/time part which is hidden by another property (for example, if TimeDisplay is tdHM, the second part is not shown, regardless of this property).<br />
<br />
==== CalendarWrapperClass: TCalendarControlWrapperClass (public) ====<br />
<br />
:When assigned, this property determines the type of the calendar control used for drop-down calendar. When set to nil, which is the default, the value of global variable DefaultCalendarWrapperClass is used. [[#Using some custom calendar control for drop-down|More details here]].<br />
<br />
==== ShowMonthNames: Boolean ====<br />
:When this property is set to True, month names, set in [[#MonthNames: String|MonthNames]] property, will be displayed instead of numbers.<br />
<br />
==== MonthNames: String ====<br />
:When [[#ShowMonthNames: Boolean|ShowMonthNames]] is set to True, this property determines which month names should appear.<br />
:If MonthNames is set to 'Short' or 'Long', then month names are set to [http://www.freepascal.org/docs-html/rtl/sysutils/shortmonthnames.html ShortMonthNames] or [http://www.freepascal.org/docs-html/rtl/sysutils/longmonthnames.html LongMonthNames] respectively.<br />
:To set the month names explicitly, this property should be set to string which starts with a character which will be used to separate months and then twelve names separated by this character. For example ',I,II,III,IV,V,VI,VII,VIII,IX,X,XI,XII' — roman numbers are used for months — the first character is comma, which means that comma is used to separate the months. Another valid example ';jan;feb;mar;apr;maj;jun;jul;avg;sep;okt;nov;dec'.<br />
:So, the separator should be the first character, before the first month name, and then only if there are twelve month names separated by that separator, the format is valid.<br />
:The default value of this property is 'Long'.<br />
<br />
==== Options: TDateTimePickerOptions ====<br />
<br />
* dtpoDoChangeOnSetDateTime: the OnChange handler will be called also when DateTime is programatically changed.<br />
* dtpoEnabledIfUnchecked: enable the date time picker if the checkbox is unchecked.<br />
* dtpoAutoCheck: auto-check an unchecked checkbox when DateTime is changed (makes sense only if dtpoEnabledIfUnchecked is set).<br />
* dtpoFlatButton: use flat button for calender picker.<br />
<br />
<br />
=== DateTimePicker Editor ===<br />
<br />
[[Image:datetimepickereditor.png]]<br />
<br />
:DateTimePicker Editor is a dialog which provides easy way to edit Date, Time, MinDate and MaxDate properties in design time. It is invoked when DateTimePicker control is double-clicked in form designer. It is also shown when the ellipsis (…) button shown in Date, Time, MinDate and MaxDate properties in Object inspector gets clicked.<br />
<br />
<br />
=== User interaction in runtime ===<br />
<br />
:The goal was to provide same user interaction as native DateTimePicker from Windows. The keyboard input is restricted to valid date/time values only. Up and down buttons can be used too. The contros behaves just like VCL's control.<br />
<br />
:After this behaviour is achieved it is further improved with mouse wheel interaction. Date and time parts can be edited using mouse wheel, which is very fast and comes handy.<br />
<br />
<br />
=== Using some custom calendar control for drop-down ===<br />
<br />
Instead of LCL's TCalendar, some other calendar control can be used for drop-down. This calendar control has to provide a way to determine if the coordinates (within the calendar control) are on the date or not [[#AreCoordinatesOnDate|(see AreCoordinatesOnDate function)]]. DateTimePicker needs it to decide if it should close the drop-down calendar and take its date when the calendar gets clicked (see AreCoordinatesOnDate function). The calendar only needs to be derived from TControl. The mouse interaction with calendar will work and, if it is also TWinControl's descendent, the keyboard interaction will also work.<br />
<br />
Now, let's see how to use some calendar control for drop-down control. First, we need to define the "wrapper" class, then we will have to tell the DateTimePicker control to use this wrapper instead of default one.<br />
<br />
==== Definning the wrapper class ====<br />
<br />
:In unit calendarcontrolwrapper.pas, there is an abstract class TCalendarControlWrapper. We need to derive a new class from this abstract class. There are four abstract methods which new class has to override - GetCalendarControlClass, SetDate, GetDate and AreCoordinatesOnDate.<br />
:This should be pretty simple, please see the file lclcalendarwrapper.pas, where TLCLCalendarWrapper (the default wrapper) is defined.<br />
<br />
===== GetCalendarControlClass =====<br />
<br />
:<syntaxhighlight><br />
class function GetCalendarControlClass: TControlClass; virtual abstract;<br />
</syntaxhighlight><br />
:This class function should return the class of the wrapped calendar control.<br />
<br />
===== SetDate =====<br />
<br />
:<syntaxhighlight><br />
procedure SetDate(Date: TDate); virtual abstract;<br />
</syntaxhighlight><br />
:Should be overriden to set the date in the calendar control.<br />
<br />
===== GetDate =====<br />
<br />
:<syntaxhighlight><br />
function GetDate: TDate; virtual abstract;<br />
</syntaxhighlight><br />
:Should be overriden to get the date from the calendar control.<br />
<br />
===== AreCoordinatesOnDate =====<br />
<br />
:<syntaxhighlight><br />
function AreCoordinatesOnDate(X, Y: Integer): Boolean; virtual abstract; <br />
</syntaxhighlight><br />
:This function should return True if coordinates (X, Y) are on the date in the calendar control (DateTimePicker calls this function when the calendar is clicked, to determine whether the drop-down calendar should return the date or not). The calendar control must at least provide a way to determine whether the coordinates are on the date (when this control gets clicked, we must decide if the date has just been chosen — then we should respond by closing the drop-down form and setting the date from calendar to DateTimePicker — for example in LCL's TCalendar we will respond when the calendar is clicked on date, but not when the user clicks in title area changing months or years, then we let the user keep browsing the calendar).<br />
<br />
==== Telling to the DateTimePicker to use new wrapper ====<br />
<br />
:Now, when we have the wrapper class defined, the easiest way to make all DateTimePickers use the new control is setting global variable DefaultCalendarWrapperClass (declared in unit datetimepicker.pas) and all DateTimePickers in the project will use the new control.<br />
<br />
:There is public property CalendarWrapperClass in DateTimePicker. We can set it and DateTimePicker will use it.<br />
<br />
:This pseudo code shows how a DateTimePicker decides which calendar control to use:<br />
<br />
<syntaxhighlight lang="text"><br />
if property DateTimePicker.CalendarWrapperClass is not nil<br />
the property is used<br />
else if global variable DefaultCalendarWrapperClass is not nil<br />
the global variable is used<br />
else<br />
LCL's TCalendar (implemented through TLCLCalendarWrapper class) is used<br />
</syntaxhighlight><br />
<br />
== TDBDateTimePicker [[Image:TDBDateTimePicker.png]] ==<br />
<br />
:TDBDateTimePicker is a data-aware version of TDateTimePicker, with nice way of handling null database values.<br />
<br />
=== Handling of null values ===<br />
<br />
==== Displaying null values ====<br />
<br />
:When the underlying DB field has null value, then:<br />
<br />
::If the control is not focused, then it displays the text defined in [[#TextForNullDate: String|TextForNullDate]] property. The default is "NULL".<br />
<br />
::When the control gets focus, the text changes to defined format, but displaying zeros for date parts and nines for time parts (for example "00/00/0000 99:99:99"), which is appropriate to user input.<br />
<br />
==== Setting the field value to null ====<br />
<br />
::If [[#NullInputAllowed: Boolean|NullInputAllowed]] property is True, the user can set the date and time to null, by pressing N key.<br />
<br />
[[Category:Components]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=fcl-web&diff=114170fcl-web2017-12-08T11:30:40Z<p>Zoran: /* See also */</p>
<hr />
<div>{{Web and Networking Programming}}<br />
<br />
== What is fpWeb ==<br />
fpWeb can be used to build cgi applications. '''We need more details here: what other functionality does it have, how does it compare to other frameworks/tools, e.g. Brook'''<br />
<br />
== Using fpWeb together with Lazarus ==<br />
<br />
===Installing the weblaz fpWeb Lazarus Package===<br />
<br />
The first step to do is installing the package which comes in the path lazarus/components/fpweb/weblaz.lpk. As usual with design-time packages, you'll have to rebuild Lazarus.<br />
<br />
===Creating a CGI application===<br />
<br />
After the weblaz package is installed, a very simple CGI web application which displays an HTML page can be created by going to the Lazarus menu "File->New...". From the list of possible applications select "CGI Application" as in the image below, which will create a main CGI project file and a fpweb web module.<br />
<br />
[[Image:New_cgi.PNG]]<br />
<br />
The TFPWebModule allows you to manipulate properties and events using the Object Inspector.<br />
<br />
To add code to show the page, a request handler should be added. To do this, double click the OnRequest property in the object inspector, as in the image below:<br />
<br />
[[Image:Webmodule.PNG]]<br />
<br />
In the event handler one should write the HTML code which will be displayed by the browser. To avoid mixing Pascal and HTML, this page can be loaded from the directory the CGI executable is in by using AResponse.Contents.LoadFromFile(). <br />
<br />
The type of the response should be set in AResponse.ContentType. For HTML pages this should have the value 'text/html;charset=utf-8'. <br />
<br />
Finally, Handled should be set to True to indicate that the request was successfully handled (and return a 200 OK status code to the browser). After adding this code, the web module should look like this:<br />
<br />
<syntaxhighlight>unit mainpage;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
interface<br />
<br />
uses<br />
Classes, SysUtils, FileUtil, HTTPDefs, websession, fpHTTP, fpWeb; <br />
<br />
type<br />
<br />
{ TFPWebModule1 }<br />
<br />
TFPWebModule1 = class(TFPWebModule)<br />
procedure DataModuleRequest(Sender: TObject; ARequest: TRequest;<br />
AResponse: TResponse; var Handled: Boolean);<br />
private<br />
{ private declarations }<br />
public<br />
{ public declarations }<br />
end; <br />
<br />
var<br />
FPWebModule1: TFPWebModule1; <br />
<br />
implementation<br />
<br />
{$R *.lfm}<br />
<br />
{ TFPWebModule1 }<br />
<br />
procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;<br />
AResponse: TResponse; var Handled: Boolean);<br />
begin<br />
AResponse.ContentType := 'text/html;charset=utf-8';<br />
AResponse.Contents.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'mainpage.html');<br />
Handled := True;<br />
end;<br />
<br />
begin<br />
RegisterHTTPModule('TFPWebModule1', TFPWebModule1);<br />
end.</syntaxhighlight><br />
<br />
=== Deploying the CGI application ===<br />
<br />
This section assumes the Apache web server is used. Of course, other web servers that support CGI (nginx, cherokee) can be used, too.<br />
<br />
Apache can be downloaded here: http://httpd.apache.org/download.cgi or installed using your distribution's package manager.<br />
<br />
The default installation of Apache will treat all files located in its cgi-bin directory as CGI programs, so the user won't be able to access plain HTML files placed there. This directory can be set in the file httpd.conf in the following section:<br />
<br />
<pre><br />
#<br />
# ScriptAlias: This controls which directories contain server scripts.<br />
# ScriptAliases are essentially the same as Aliases, except that<br />
# documents in the target directory are treated as applications and<br />
# run by the server when requested rather than as documents sent to the<br />
# client. The same rules about trailing "/" apply to ScriptAlias<br />
# directives as to Alias.<br />
#<br />
ScriptAlias /cgi-bin/ "C:/Program Files/Apache Software Foundation/Apache2.2/cgi-bin/"<br />
</pre><br />
<br />
If you place an executable called "mywebpage.cgi" in this directory, then the page can be accessed as http://localhost/cgi-bin/mywebpage.cgi (or remotely with the corresponding IP address or domain name).<br />
<br />
fcl-web with Lazarus on Windows produces .exe files. For Apache to serve these files you have to add this:<br />
<pre><br />
AddHandler cgi-script .exe<br />
</pre><br />
<br />
And to serve your executables from another directory, you add this:<br />
<pre><br />
ScriptAlias /bin/ "C:/lazarus_projects/test-fclweb/bin/"<br />
<Directory "C:/lazarus_projects/test-fclweb/bin/"><br />
AllowOverride None<br />
Options None<br />
Order allow,deny<br />
Allow from all<br />
</Directory><br />
</pre><br />
<br />
===Formatting the HTML and Reading Query fields===<br />
<br />
The previous example just showed a plain HTML page. One might wish to e.g.<br />
* dynamically change the HTML page output, and<br />
* as read the variables the browser passed to the webpage in Query fields (in e.g. HTTP GET and HTTP POST actions). <br />
<br />
A simple solution for the first problem is simply using the standard Pascal routine Format and adding %s or %d in the HTML file in the appropriate places which will receive a custom value.<br />
<br />
===Reading GET data===<br />
To read the GET variables one can use ARequest.QueryFields, which is a TStrings descendent. Each variable will be in a separate line in the TStrings in the format variablename=value, similarly to how they are shown in the browser page address. To search for a specific variable one can use ARequest.QueryFields.Values[], passing the variable name in the brackets to receive its value back, instead of parsing the string manually. <br />
<br />
The resulting code:<br />
<br />
<syntaxhighlight>procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;<br />
AResponse: TResponse; var Handled: Boolean);<br />
var<br />
HexText, AsciiText: string;<br />
begin<br />
HexText := ARequest.QueryFields.Values['hex'];<br />
AsciiText := HexToAnsii(HexText);<br />
<br />
AResponse.ContentType := 'text/html;charset=utf-8';<br />
AResponse.Contents.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'mainpage.html');<br />
AResponse.Contents.Text := Format(AResponse.Contents.Text,<br />
[HexText, AsciiText]);<br />
Handled := True;<br />
end;</syntaxhighlight><br />
<br />
===Reading POST data===<br />
<br />
Data submitted by POST requests can be obtained from <code>TRequest.Content</code>. It will come without the request headers - in other words it contains the body of the request.<br />
<br />
Submitted form data can also be accessed using <code>TRequest.ContentFields</code> which is the content parsed as fields separated by & and decoded. For example, the following set of form values:<br />
<br />
login: dfg 345&&<br />
login_senha: ====<br />
email: dfg<br />
<br />
Will be encoded in 'APPLICATION/X-WWW-FORM-URLENCODED' like this (see [http://en.wikipedia.org/wiki/POST_%28HTTP%29 wikipedia POST (HTTP)]):<br />
<br />
login=dfg+345%26%26&login_senha=%3D%3D%3D%3D&email=dfg<br />
<br />
And will be available in TRequest.ContentFields via the line index or using the Values property, which is more convenient:<br />
<br />
TRequest.ContentFields[0]: login=dfg 345&&<br />
TRequest.ContentFields[1]: login_senha=====<br />
TRequest.ContentFields[2]: email=dfg<br />
TRequest.ContentFields.Values['email']: dfg<br />
<br />
If using other mime-types than 'MULTIPART/FORM-DATA' and 'APPLICATION/X-WWW-FORM-URLENCODED' (the types supported by HTML forms):<br />
* content is only available in TRequest.Content, not TRequest.ContentFields<br />
* use of these mime-types raises an exception for FPC 2.4.2 (fixed in FPC 2.5+).<br />
<br />
Note that HTTP POST can also send Query fields (in the URL), e.g. http://server/bla?question=how, and those are accessed by TRequest.QueryFields as explained in the previous section.<br />
<br />
<syntaxhighlight>procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;<br />
AResponse: TResponse; var Handled: Boolean);<br />
var<br />
lData: String;<br />
begin<br />
lData := ARequest.Content;<br />
</syntaxhighlight><br />
<br />
=== Reading POST binary data ===<br />
To receive data on the server that has been POSTed e.g. as multipart/form-data, use something like this:<br />
<syntaxhighlight><br />
procedure TMainWebModule.TFPWebActions2Request(Sender: TObject;<br />
ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);<br />
var<br />
i: Integer;<br />
begin <br />
// Process all received files<br />
for i := 0 to ARequest.Files.Count - 1 do<br />
begin<br />
// Doing something else than writeln is highly recommended ;)<br />
writeln ('Received Filename: '+ARequest.Files[i].LocalFileName);<br />
end;<br />
<br />
Handled := true;<br />
end;<br />
</syntaxhighlight><br />
<br />
The client can send data using e.g. FileFormPost.<br />
<br />
== Using multiple modules ==<br />
If there is only one module in the web application, all requests will be directed to this module. <br />
<br />
As your web application grows, multiple modules can be used. A new module can be added by choosing 'File - New' and then one of 'Web module' or 'HTML Web Module'. <br />
<br />
FCL-web uses the URL to determine how a HTTP request should be handled. It must therefore know which web-modules exist in the application. To achieve this, each module must be registered.<br />
<br />
Each module is registered with fcl-web in the initialization section of the unit it is defined in:<br />
<br />
<syntaxhighlight>RegisterHTTPModule('location', TMyModule);</syntaxhighlight><br />
<br />
The module will then be invoked if an URL of the form <br />
<nowiki>http://www.mysite.org/mycgi.cgi/location</nowiki><br />
or<br />
<nowiki>http://www.mysite.org/mycgi.cgi?module=location</nowiki><br />
is used.<br />
<br />
If multiple modules are present, the name of the module must appear in the URL, or an error will be raised.<br />
<br />
This behaviour can also be forced for applications that have only a single module by setting the Application's property AllowDefaultModule to false:<br />
<br />
<syntaxhighlight>Application.AllowDefaultModule := False;</syntaxhighlight><br />
<br />
In that case, the fcl-web application will always require the name of the module in the URL.<br />
<br />
The name of the request variable that determines the module name (by default, this is 'module') can be set in the Application.ModuleVariable property. The following code<br />
<br />
<syntaxhighlight>Application.ModuleVariable := 'm';</syntaxhighlight><br />
<br />
ensures that the following URL is directed to TMyModule:<br />
<nowiki>http://www.mysite.org/mycgi.cgi?m=location</nowiki><br />
<br />
If all this is not enough to determine the module to which the request should be passed, the Application.OnGetModule event can be used. It is of type TGetModuleEvent:<br />
<br />
<syntaxhighlight>type<br />
TGetModuleEvent = Procedure (Sender : TObject; ARequest : TRequest;<br />
Var ModuleClass : TCustomHTTPModuleClass) of object;<br />
</syntaxhighlight><br />
<br />
Creating an event handler for this event allows fine control over the module that is created to handle the request: the request (passed in ARequest) can be examined, and the 'ModuleClass' variable must be set to the class of the module that should handle the request.<br />
<br />
If 'ModuleClass' is 'Nil' on return, an error will be sent to the browser.<br />
<br />
== Using Actions ==<br />
<br />
A module can be used to group certain kinds of actions that logically belong together. Imagine a module TUserModule that is used to handle user management in the webpages. There can be multiple actions associated with a user:<br />
* Creating<br />
* Deleting<br />
* Editing<br />
* Displaying<br />
These different actions can be handled by the same module. One can determine the action from the URL manually, as in:<br />
<nowiki>http://mysite/mycgi.cgi/user?action=delete&id=12</nowiki><br />
This can be automated.<br />
<br />
In order to make it easier to distinguish between various actions, the module has a property actions: this is a collection, in which each item is associated with a different response to the request. The actions have various properties:<br />
; Name : The name of the action. The URL will be examined to determine the name of the action.<br />
; Content : A string. If set, this is sent to the browser.<br />
; Contents :A stringlist. If set, this is sent to the browser.<br />
; ContentProducer :If set, the contentproducer will handle the request.<br />
; Default: if set to 'True', then this action is the default action. That means that if FCL-Web cannot determine the action name, this action will be executed.<br />
; Template : If set, this template will be processed, and the results sent to the browser.<br />
There are also some events:<br />
; BeforeRequest : executed before the request is processed. Can be used to set the 'Content' or other properties.<br />
; AfterRequest : executed after the request is processed.<br />
; OnRequest : an event handler to handle the request. If set, the handler is used to handle the request.<br />
<br />
Again, as in the case of multiple modules, the URL is used to determine which action to execute. The part right after the module part ("user" in this example) is used:<br />
<nowiki>http://mysite/mycgi.cgi/user/delete&id=12</nowiki><br />
would execute the action named 'delete'.<br />
<br />
The 'ActionVar' property of the module can be used to set the name of the request variable to use. Setting<br />
<br />
<syntaxhighlight>UserModule.ActionVar := 'a';</syntaxhighlight><br />
<br />
may be used to change the above URL to<br />
<nowiki>http://mysite/mycgi.cgi/user?a=delete&id=12</nowiki><br />
<br />
If there is only one module in the application, the URL can be shortened to<br />
<nowiki>http://mysite/mycgi.cgi/delete&id=12</nowiki><br />
<br />
== Using HTML Templates ==<br />
For information about using templates, template tags and template tag parameters to generate response pages, please refer to the fptemplate.txt file under your FPC directory in /packages/fcl-base/texts/.<br />
<br />
Example projects that demonstrate using templates can be found under your FPC directory in /packages/fcl-web/examples/fptemplate/ (see the README.txt in there for more).<br />
(In earlier versions, these examples were in the Lazarus directory in /components/fpweb/demo/fptemplate/)<br />
<br />
== Tips & troubleshooting==<br />
<br />
=== Sending result codes ===<br />
To send a different HTTP response than 200 OK, use '''AResponse.Code''' and '''AResponse.CodeText''', e.g.<br />
<syntaxhighlight><br />
AResponse.Code:=404;<br />
AResponse.CodeText:='Document not found';<br />
</syntaxhighlight><br />
<br />
=== Sending binary data ===<br />
An approach that seems to work to send e.g. a tiff file from the web server to the client - adapted from $(fpcdirectory)\packages\fcl-web\examples\jsonrpc\demo1\wmdemo.pp - something like:<br />
<syntaxhighlight><br />
var<br />
<br />
AResponse.ContentStream := TMemoryStream.Create;<br />
try<br />
AResponse.ContentStream.LoadFromFile('/tmp/sample.tiff');<br />
AResponse.ContentType := 'image/tiff'; //or whatever MIME type you want to send<br />
// to do: there is an fpweb example that gets the mime type from the file extension...<br />
AResponse.ContentLength:=AResponse.ContentStream.Size; //apparently doesn't happen automatically?<br />
AResponse.SendContent;<br />
finally<br />
AResponse.ContentStream.Free;<br />
end;<br />
Handled := true; <br />
</syntaxhighlight><br />
<br />
<br />
=== Error: Could not determine HTTP module for request ===<br />
You may have multiple modules and multiple actions. If you specify an URL with only 1 item, like:<br />
<syntaxhighlight lang="html4strict">http://localhost/cgi-bin/somemodule</syntaxhighlight><br />
then fpweb assumes you're specifying an action. If you don't have a default module set, you will get a 500 internal server error (Could not determine HTTP module for request)<br />
<br />
You can modify this behaviour to let fpweb map to a module name instead by setting the application's '''PreferModuleName''' property to true.<br />
<br />
=== Error: response code 500 Internal Server error when trying to handle DELETE method ===<br />
In FPC 2.6.2 and lower, fcl-web does not accept the DELETE method and generates an error.<br />
<br />
=== fpWeb/FCGI and Apache 2.4 (mod_proxy_fcgi mode) ===<br />
When you want to deploy FCGI application behind Apache 2.4+ reverse proxy, you need PATH_INFO variable in headers that are sent via socket to FCGI daemon.<br />
<br />
1, Enable following modules: setenvif_module, proxy_module and proxy_fcgi_module <br/><br />
(on CentOS 7 modules are defined in file /etc/httpd/conf.modules.d/00-base.conf and /etc/httpd/conf.modules.d/00-proxy.conf).<br />
<pre><br />
LoadModule setenvif_module modules/mod_setenvif.so<br />
LoadModule proxy_module modules/mod_proxy.so<br />
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so<br />
</pre><br />
<br />
2, Reverse proxy configuration for fpWeb/FCGI listening on port 9000.<br/><br />
$1 = module name<br/><br />
$2 = action name<br />
<pre><br />
SetEnvIf Request_URI . proxy-fcgi-pathinfo=full<br />
ProxyPassMatch "/fcgi/(.*)/(.*)" "fcgi://127.0.0.1:9000/$1/$2"<br />
</pre><br />
<br />
== Notes ==<br />
<br />
* The cgiapp unit is deprecated, please use fpcgi as much as possible.<br />
<br />
* The fastcgi, custfcgi, and fpfcgi units are not supported on Darwin at this time, because it uses a different mechanism than the MSG_NOSIGNAL option for recv() to indicate that no SIGPIPE should be raised in case the connection breaks. See http://lists.apple.com/archives/macnetworkprog/2002/Dec/msg00091.html for how this should be fixed.<br />
<br />
* If you deploy your CGI application and get errors like "Error: No action name and no default action", you should make sure there's an action assigned to the URL, or catch non-supported actions with an action marked Default. In both cases, an OnRequest event handler for the action that sets Handled:=true should be used.<br />
<br />
== See also ==<br />
* [https://bitbucket.org/leledumbo/books/downloads/fpwebtutorial-chromeprint.pdf fcl-web tutorial (in pdf) by Leledumbo]<br />
* [[fphttpclient]] Part of fcl-web that can be used stand-alone in client applications<br />
* ''write me!''fphttpserver Small stand alone Object Pascal web server. Can be used to serve fcl-web CGI applications.<br />
* [[CGI_Web_Programming#Debugging_CGI]] Information about debugging CGI applications<br />
* [https://github.com/leonardorame/Ext4-MVC-Tutorial/wiki/Lazarus-Server Part of a tutorial by Leonardo Ramé that shows how to program an fcl-web CGI application; includes example of generating JSON content]<br />
<br />
[[Category:FPC]]<br />
[[Category:FCL]]<br />
[[Category:Components]]<br />
[[Category:Packages]]<br />
[[Category:Networking]]<br />
[[Category:Lazarus]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=Installing_Lazarus&diff=113441Installing Lazarus2017-10-30T07:27:31Z<p>Zoran: /* Installing Free Pascal from source under Linux/BSD */</p>
<hr />
<div>{{Installing Lazarus}}<br />
<br />
== Overview ==<br />
<br />
A real "in depth" build guide is [http://www.stack.nl/~marcov/buildfaq.pdf here].<br />
<br />
For binary downloads of Lazarus see [http://wiki.lazarus.freepascal.org/Getting_Lazarus#Download_and_install_Lazarus_release_version Download and install Lazarus release version]<br />
<br />
For people who simply want to install Lazarus and start using it for programming, the easiest approach is to download and install a recent, reasonably stable binary release (such as a Linux ".rpm" package, a Windows ".exe" installer, or a Mac OS X ".dmg" package). You can read the sections under Linux or Windows entitled "fpc binaries" or the first paragraphs in the sections on installing Lazarus in Linux or Windows; most of the remaining information can be safely ignored.<br />
<br />
<br />
<br />
For those who want to participate in the development of the compiler or the IDE, or for those who want the most up-to-date tools, an installation from source files is necessary, and much of the rest of this information is relevant.<br />
<br />
Lazarus provides two main parts:<br />
* LCL - the Lazarus Component Library<br />
* IDE - the RAD tool<br />
<br />
These in turn are dependent on:<br />
* FPC - the Free Pascal compiler<br />
* FCL - the Free Pascal Component library, containing most of the non-graphic components used by Lazarus<br />
<br />
=== Lazarus System Requirements ===<br />
<br />
# Free Pascal compiler, packages, and sources. (*important*: of the same version/date)<br />
# A supported widget tool-kit<br />
#:;Win32: The native Win32 API can be used, or Qt widgetset.<br />
#:;Linux/xxxBSD: GTK+ 2.x or Qt : Most Linux distributions and *BSDs already install the GTK+ 2.x libraries. You can also find them at http://www.gtk.org. <br>Qt is also supported with all distributions (auto installed if you prefer KDE). <br> <br />
#:;Mac OS X: You need the Apple developer tools. See Installing under Mac OS X below. Qt can be used too.<br />
<br />
The FAQ - Frequently Asked Questions file is available at http://www.lazarus.freepascal.org . Some of the questions can be found in the local file 'FAQ'.<br />
<br />
Qt widget set is supported on Linux 32/64,Win32/64,Mac OS X, Haiku and embedded linux (qtopia) platforms.<br><br />
More about installation http://wiki.lazarus.freepascal.org/index.php/Qt_Interface <br><br />
<br />
The following sections describe how to get Free Pascal and how to install Lazarus properly.<br />
<br />
== Installing The Free Pascal Compiler ==<br />
<br />
There is an extensive discussion of how to install and build Free Pascal compilers available here http://www.stack.nl/~marcov/buildfaq.pdf - it may be a little too detailed for some users, but is very comprehensive.<br />
<br />
Lazarus 1.4 requires fpc (Free Pascal) version 2.6.4 or higher and it requires both the compiled fpc libs (binaries) and the fpc sources and of the same version.<br />
<br />
=== Installing Free Pascal under Linux ===<br />
<br />
==== FPC Binaries ====<br />
<br />
The latest release of Free Pascal, version 3.0.2, can be obtained from the Free Pascal website (http://www.freepascal.org/download.var, then choose a mirror) or from SourceForge (http://sourceforge.net/projects/lazarus). <br />
<br />
At the Lazarus downloads section (http://www.lazarus.freepascal.org ) you can get the RPM or DEB of the Free Pascal compiler (compiler / Linux) and its packages. If you don't have an RPM-based or debian-based distribution, you can download and install the tarballs from http://www.freepascal.org. If you want to compile the binaries for yourself, see the BSD section.<br />
<br />
Instructions: <br />
<br />
{{Warning| if you're '''not using RPMs or debian packages''' (even if you plan to use alien) it's best to get latest stable fpc (3.0.2 as of now) and install Lazarus from source.}}<br><br />
<br />
Start Linux and login as '''root'''.<br />
<br />
Download latest files from https://sourceforge.net/projects/lazarus/files/.<br />
As example:<br />
:* fpc-3.0.2-0.laz.i686.rpm<br />
:* fpc-src-3.0.2-0.laz.i686.rpm<br />
:* lazarus-1.6.4-0.i686.rpm<br />
and install them with:<br />
:* rpm -Uvh *.rpm<br />
<br />
Debian users are recommended to use the deb packages, but may use either alien (warning, it doesn't generate fpc.cfg) or the tarball install.<br />
<br />
==== FPC Sources ====<br />
FPC source files are stored in a Subversion (SVN) repository that keeps track of all changes of the source tree.<br />
Once you have the sources, please see [[#Installing Free Pascal from source under Linux/BSD]] for instructions on how to install them.<br />
<br />
===== Download Daily Source Snapshot of Development Tree =====<br />
You can download today's development sources in the form of a packed snapshot from the SVN source repository: these snapshots are updated on a daily basis, and reflect the state of the source repository. They are not tested, might not work or even crash your system. The files are kept at the site which has the SVN archive. The version of FPC used can vary.<br />
Location: [http://www.hu.freepascal.org/lazarus/]<br />
<br />
===== Up to date source repository using SVN =====<br />
As an alternative to the daily zip files of the SVN sources, the SVN repository has been made accessible for everyone, with read-only access. This means that you can directly access the code, and you will have really the last-minute sources available. It is also a method which requires less bandwidth once you have done the first download (checkout in SVN lingo).<br />
<br />
===== Getting the source =====<br />
How to do this?<br />
First, you need to have an SVN client installed. Use your package manager, install a tool like TortoiseSVN on Windows, or look at [http://subversion.tigris.org/] for more details.<br />
<br />
Using command line SVN: change directory (cd) to the parent directory of your development area, eg <br />
To retrieve the full source repository for the first time into an fpc subdirectory under your home directory, type<br />
<syntaxhighlight lang="bash"><br />
cd ~<br />
svn checkout http://svn.freepascal.org/svn/fpc/trunk fpc<br />
</syntaxhighlight><br />
<br />
To update the sources which were downloaded (checked out) above<br />
<syntaxhighlight lang="bash"><br />
cd ~<br />
svn update fpc<br />
</syntaxhighlight><br />
<br />
===== Getting a separate branch =====<br />
If the current trunk version is in a state of rapid change and unsuitable for much use unless you want to work on the compiler itself, you can stay on a version that is updated with fixes.<br />
To do this, you have to find out a stable '''branch''' that you want to track instead of the default '''trunk''' development version.<br />
The example below shows how you can track the fixes_2_6 version; of course replace as needed depending on what branches you want to track.<br />
<br />
This example keeps the fixes in another directory under your home directory - it wouldn't make sense to put two versions of the source in one directory...<br />
<syntaxhighlight lang="bash"><br />
cd ~<br />
svn checkout http://svn.freepascal.org/svn/fpc/branches/fixes_2_6 fpc_fixes_2_6<br />
</syntaxhighlight><br />
Update as usual:<br />
<syntaxhighlight lang="bash"><br />
cd ~<br />
svn update fpc_fixes_2_6<br />
</syntaxhighlight><br />
<br />
===== Documentation =====<br />
The documentation sources are in a separate repository called fpcdocs, so the command to get them is<br />
<syntaxhighlight lang="bash"><br />
cd ~<br />
svn checkout http://svn.freepascal.org/svn/fpcdocs/trunk fpcdocs<br />
</syntaxhighlight><br />
<br />
If you want to learn more about subversion, read this excellent [http://svnbook.red-bean.com/ Subversion book] which is also available online in different formats for free.<br />
<br />
For more information, see the [http://www.freepascal.org/develop.var Free Pascal] website.<br />
<br />
=== Installing Free Pascal under Windows ===<br />
<br />
==== FPC Binaries for Windows ====<br />
<br />
By far the easiest way to get a working installation of Free Pascal is to download the current binary Windows release of Lazarus from the SourceForge repository [http://sourceforge.net/projects/lazarus/files/] - the release contains the current versions of the Free Pascal Compiler and the Free Pascal libraries as well as the Lazarus IDE.<br />
<br />
If you want to install from sources, read on!<br />
<br />
You can get the installer zip for fpc at Free Pascal's download section (http://www.freepascal.org/download.var, then choose a mirror). <br />
Installing from the sources -- see the next section to know how to get them -- is not for novices, since you need a starting compiler as well.<br />
<br />
==== FPC Sources for Windows ====<br />
<<<< See section above under [[#FPC Sources|FPC Sources]] for Linux, where the use of SVN is described >>>><br />
<br />
The easiest way to get the Free Pascal sources is via SVN; see the next section for more on that. You can also download the package as a whole -- see http://www.freepascal.org/develop.var for the daily snapshot of the 2.5.x release tree.<br />
<br />
Windows FPC Sources via SVN<br />
<br />
You will need to have a SVN client such as TortoiseSVN installed in order to perform the steps below. The exact commands vary between SVN clients; the ones given below are to be used under SVN home's client, which is available for download here.<br />
<br />
First create a directory in which you'd like to put the sources. Any normal user can do this. Create a directory for fpc (e.g. C:\Source), then do the following at the command prompt:<br />
<syntaxhighlight lang="bash">C:\Source> svn co http://svn.freepascal.org/svn/fpc/trunk fpc</syntaxhighlight><br />
Hint: To download/update the latest changes you can simply do<br />
<syntaxhighlight lang="dos"><br />
C:\> cd Source\FPC<br />
C:\Source\FPC> svn up<br />
</syntaxhighlight><br />
<br />
See: http://www.freepascal.org/down/i386/win32.var . Download FPC as one big file, unzip it and run the install.exe. <br />
<br />
Extending your PATH variable to the fpc directory:<br />
<br />
* Win98: Edit autoexec.bat and add the line: PATH=%PATH%;C:\pp\bin\bin\win32<br />
* WinXP/2k: My Computer (Right Click) -> Properties -> Advanced (Page) -> Environment Variables -> System Variables -> Edit "PATH", Add "C:\pp\bin\bin\win32" there.<br />
<br />
Then restart windows.<br />
<br />
After you have FPC binaries installed you can build FPC source from subversion. <br />
<br />
Hints: <br />
* Windows (7+) requires that an elevated user status command prompt be used. From the start menu for "Command Prompt" right click and select "Run as Administrator".<br />
* YOUR-PREFIX is totally dependent on where you installed FPC to. At the time of this writing, the binaries are instructed to use a default location of "C:\FPC" and they were placed in "C:\FPC\2.6.4". Under Linux, the make install scripts were adjusted to create a new sub-folder IF the FPC version changed since last build. The Windows scripts do not. So if you know the sub-folder name ie. 3.1.1 you can specify that. However, since versions change frequently, it is recommended that you just select and maintain a single PREFIX with no respect for FPC versions. A good prefix is C:\FPC but you must also make sure that the C:\FPC\bin\i386-win32\ folder is added to your path environment variable (see above on how to set your path and change it from the binary version to the newly compiled one).<br />
Instructions:<br />
* In command Prompt navigate to the localized FPC source. ie.) type "cd c:\Developer\FPC"<br />
* To build FPC type "make all"<br />
* To overwrite existing FPC type "make install PREFIX=YOUR-PREFIX"<br />
* To install source type "make install sourceinstall PREFIX=YOUR-PREFIX"<br />
<br />
==== Compiling/installing FPC and Lazarus from Sources of SVN (Win32) ====<br />
<br />
17 Feb 2017 Version FPC '''3.0.2''' version Lazarus '''1.7.x'''<br />
<br />
===== STEP #1: Create directories and get the sources =====<br />
Create the following directories:<br />
c:\freepascal\<br />
c:\freepascal\binutils\<br />
c:\freepascal\binutils\i386-win32\<br />
c:\freepascal\fpc\<br />
c:\freepascal\fpc\3.0.2\<br />
c:\freepascal\laz\<br />
<br />
You will need the latest '''released'' compiler to build a new compiler.<br />
Get the ppc386 (the compiler) in FTP (below) and unzip it in c:\freepascal\binutils\<br />
ftp://ftp.freepascal.org/pub/fpc/dist/3.0.0/bootstrap/i386-win32-ppc386.zip<br />
<br />
After installing [http://tortoisesvn.tigris.org/ TortoiseSVN], download the sources from SVN using a URL for each directory, see:<br />
Dir: c:\freepascal\binutils\i386-win32\<br />
URL: http://svn.freepascal.org/svn/fpcbuild/branches/fixes_3_0/install/binw32<br />
<br />
Dir: c:\freepascal\fpc\3.0.2<br />
URL: http://svn.freepascal.org/svn/fpc/branches/fixes_3_0/<br />
<br />
Dir: c:\freepascal\laz<br />
URL: http://svn.freepascal.org/svn/lazarus/trunk<br />
<br />
===== STEP #2: Create a BAT file to compile FPC =====<br />
After everything is downloaded, we need a BAT file to compile the FPC sources.<br />
Create a new file c:\freepascal\makefpc.bat and copy/paste the following script:<br />
<br />
<syntaxhighlight lang="dos"><br />
@echo on<br />
set myroot=c:\freepascal<br />
set myFPC=%myroot%\fpc\3.0.2<br />
set mybinutils=%myroot%\binutils<br />
set PATH=%mybinutils%\i386-win32;%myFPC%\bin\i386-win32;%PATH%<br />
cd %myFPC%<br />
rd /s /q %myfpc%\examples<br />
make distclean all install INSTALL_PREFIX=%myFPC% PP=%mybinutils%\ppc386.exe DATA2INC=%myFPC%\utils\data2inc.exe<br />
</syntaxhighlight><br />
<br />
===== STEP #3: Make and install FPC =====<br />
At the prompt (cmd.exe), navigate to the directory c:\freepascal and execute the script we just wrote: <br />
<br />
<syntaxhighlight lang="dos"><br />
cd /d c:\freepascal<br />
makefpc.bat<br />
</syntaxhighlight><br />
<br />
===== STEP #4: Create fpc.cfg file =====<br />
Now we need to have a configuration file for FPC. This can be done with the fpcmkcfg tool in the fpc bin directory. Steps:<br />
<br />
# Open a command line console<br />
# cd /d C:\freepascal\fpc\3.0.2\bin\i386-win32<br />
# fpcmkcfg -d basepath=C:\freepascal\fpc\3.0.2 -o .\fpc.cfg<br />
<br />
{{Note|earlier instructions had \bin\i386-win32 appended to the basepath. This is not needed since at least FPC 2.4.4}}<br />
<br />
===== STEP #5: Create a BAT file to compile Lazarus =====<br />
To compile Lazarus for the first time, create a new file c:\freepascal\makelaz.bat and copy/paste the following script:<br />
<br />
<syntaxhighlight lang="dos"><br />
set myroot=c:\freepascal<br />
set myFPC=%myroot%\fpc\3.0.2<br />
set mybinutils=%myroot%\binutils<br />
set PATH=%mybinutils%\i386-win32;%myFPC%\bin\i386-win32;%PATH%<br />
cd %myroot%\laz<br />
make clean all OPT="-glw2 -Xg"<br />
</syntaxhighlight><br />
<br />
<br />
Tip: You only need to use this BAT at the first time. Then you can just build Lazarus using the menu Tools menu> Build Lazarus.<br />
<br />
===== STEP #6: Make Lazarus =====<br />
At the prompt, navigate to the directory c:\freepascal and type: makelaz.bat<br />
<br />
<br />
Done!<br />
<br />
=== Installing Free Pascal under FreeBSD/auto ===<br />
<br />
Latest FreeBSD ports collection has 3.0.2 stable version in ports/lang/fpc. But they are scattered over 96 (!) packages and the source can be obtained from /usr/ports/distfiles/freepascal.<br />
<br />
This can be done as root.<br />
<br />
<syntaxhighlight lang="bash">[]# cd /usr/ports/lang/fpc && make install && make clean</syntaxhighlight><br />
<br />
Other possibility is to use portinstall.<br />
<br />
<syntaxhighlight lang="bash">[]# portinstall fpc</syntaxhighlight><br />
<br />
Once fpc is installed you can check if it's working by simply running<br />
<br />
<syntaxhighlight lang="bash">[]$ fpc</syntaxhighlight><br />
<br />
=== Installing Free Pascal from source under Linux/BSD ===<br />
<br />
Effectively, you need<br />
<br />
If you have a file with all FPC sources, or two (FPC and Lazarus):<br />
1. e.g. for fpc ftp://ftp.freepascal.org/fpc/dist/Linux/separate/sources.tar preferably an export (no SVN/ dirs).<br />
2. Lazarus source snapshot.<br />
* a starting compiler, the latest release version (fpc 3.0.2) is the only version guaranteed to work. You can download a bootstrap compiler or use your distribution's package management/software system to install one<br />
<br />
FPC build process:<br />
* Fetch necessary files (starting compiler), FPC source file or source svn directory<br />
* If using FPC source files: extract/de-tgz in work directory,<br />
* Build: enter work/fpc/ and run<br />
<syntaxhighlight lang="bash"><br />
# maybe use: <br />
export MAKE=`which make` ; echo $MAKE <br />
$MAKE all OPT='-gl' PP=/path/to/startingcompiler-name-ppc386<br />
# $MAKE is make on linux and gmake on BSD <br />
# /path/to/ can be omitted when ppc386 is in the path<br />
</syntaxhighlight><br />
<br />
* Install fpc. Again in work/fpc, run<br />
<syntaxhighlight lang="bash"><br />
$MAKE install PP=compiler/ppc386 PREFIX=$THEPREFIX<br />
#replace the PP=compiler/ppc386 with the relevant compiler if not on Intel x86<br />
#THEPREFIX= usually is /usr/local or just /usr, but e.g. on NetBSD it is /usr/pkg for ports)<br />
</syntaxhighlight><br />
<br />
* Create a symlink:<br />
<syntaxhighlight lang="bash"><br />
ln -s $THEPREFIX/lib/fpc/3.0.0/ppc386 $THEPREFIX/bin/ppc386<br />
</syntaxhighlight><br />
<br />
* Install sources:<br />
<syntaxhighlight lang="bash"><br />
$MAKE install sourceinstall PREFIX=$THEPREFIX<br />
</syntaxhighlight><br />
* Create a symlink for default Lazarus source path:<br />
<syntaxhighlight lang=bash><br />
ln -sf $THEPREFIX/share/src/3.0.0/fpc /usr/share/fpcsrc<br />
</syntaxhighlight><br />
<br />
* Set up fpc.cfg configuration file:<br />
<syntaxhighlight lang="bash">$THEPREFIX/lib/fpc/3.0.0/samplecfg $THEPREFIX/lib/fpc/3.0.0 $ETCDIR</syntaxhighlight><br />
<br />
* Optionally test to see if ppc386 -i (or whatever compiler your architecture uses) gives output, else give a warning that user need to add $PREFIX/bin to his current path. Try to compiler a program with -viwn, and see if that gives errors.<br />
<br />
Notes:<br />
* If you need fpcmake package lists, you need to generate or supply them yourself, (in the port, or in an extra archive) either way, do a dummy install to /tmp/pack and determine the files installed with <syntaxhighlight lang="bash">find . >ll</syntaxhighlight><br />
<br />
* $THEPREFIX and $ETCDIR should be user configurable. Otherwise local installs aren't possible.<br />
<br />
* BSDHIER=1 on all make commands forces BSD hierarchy conventions.<br />
<br />
=== Installing Free Pascal under Mac OS X ===<br />
<br />
See [[Installing Lazarus on MacOS X]]<br />
<br />
==Installing Lazarus==<br />
<br />
===Installing Lazarus under Linux===<br />
<br />
==== Ubuntu / Debian ====<br />
<br />
It is recommended to use the [[fpcup|fpcUP]] updater-installer for first time users of Lazarus, which installs fpc & Lazarus in one go into a single subdirectory structure ( ~/development ). <br />
<br />
A way to get a current working installation of Lazarus is to download the .deb files for Free Pascal and Lazarus from the SourceForge repository. Here is how: [[Lazarus release version for Ubuntu|Getting Lazarus from our Ubuntu repository]].<br />
<br />
Note that installing from the default Ubuntu sources will not install the Free Pascal Source Libraries - use the method above.<br />
<br />
{{Note|on Linux Ubuntu at least, the command to start Lazarus from a console is [[startlazarus]]. Else, if you installed it from a Debian package, you should have a Lazarus menu entry under Application/Programming. <br />
(Issue: there is an ambiguity with a program also called "lazarus" from a "tct" package available for Ubuntu).}}<br />
<br />
<br />
===== Building debs the easy way =====<br />
<br />
The easiest way to get a current working installation of Lazarus is to download build your own .deb packages by following the instructions at:<br />
<br />
[[How to setup a FPC and Lazarus Ubuntu repository]]<br />
<br />
===== Installing using rpms =====<br />
<br />
The next easiest way is to the RPMs for Free Pascal and Lazarus from the SourceForge repository.<br />
<br />
You need to download the selected version of<br />
* the compiler (eg fpc-2.6.4-0.i686.rpm)<br />
* the pascal source library (eg fpc-src-2.6.4-0.i686.rpm)<br />
* the Lazarus package (eg lazarus-1.4.4-0.i686.rpm).<br />
<br />
Uninstall the old packages:<br />
<syntaxhighlight lang="bash"><br />
rpm -ev lazarus<br />
rpm -ev fpc<br />
rpm -ev fpc-src<br />
</syntaxhighlight><br />
Install the new packages:<br />
<syntaxhighlight lang="bash"><br />
rpm -ivh fpc-*<br />
rpm -ivh lazarus-*<br />
</syntaxhighlight><br />
<br />
=====Installing on Raspbian Wheezy=====<br />
<br />
Raspbian is a custom version of Debian for the Raspberry Pi creditcard-size computer. See [[Lazarus on Raspberry Pi]] for details.<br />
<br />
====Installing on Mandriva====<br />
<br />
===== Lazarus 0.9.30 on Mandriva 2010. =====<br />
<br />
Install as given lower down however on compiling a program you may get two error messages telling you that you are missing pixbuf 2.0 and lgtk-x11-2.0. to fix this install from the the software installer libdgk_pixbuf2.0_0-devel and libgtk+2.0_0-devel.<br />
<br />
====Installing on Slackware====<br />
===== Installing Lazarus on Slackware 13.0 =====<br />
There is no real difference from the slackware 12.2 or 12.0 version, therefore the procedure described below should work just as well. <br />
<br />
===== Installing Lazarus 0.9.30, for Slackware 12.0 users =====<br />
This have worked in Slackware-12.0 on a Pentium-3 computer: <br />
<br />
* The Free Pascal Compiler (FPC) is installed in SUPERUSER mode <br />
* The lazarus in installed in USER mode<br />
* The FPC compiler will be recompiled<br />
* The lazarus Integrated Development Environment (IDE) source code is loaded from the SVN server <br />
<br />
-- Download "fpc-2.4.2.i386-linux.tar" in a user folder and install it. Go to this folder and type: <br />
<syntaxhighlight lang="bash"><br />
bash-3.1$ tar -xvf fpc-2.4.2.i386-linux.tar<br />
bash-3.1$ su <br />
bash-3.1$ password:xxxxxx<br />
bash-3.1# sh install.sh<br />
...<br />
(use the default answers for the next 6 questions: press "return" 6 times)<br />
...<br />
bash-3.1# exit<br />
bash-3.2$ fpc<br />
free pascal compiler version 2.4.2 [] for i386 <br />
Copyright (c) 2010 ...<br />
...<br />
...<br />
</syntaxhighlight><br />
You have installed the compiler. You may want to recompile it or not, but in any case you will need to download the source code as it is required by the lazarus IDE.<br />
<br />
-- Download "fpc-2.4.2.source.tar.gz" in a temporary folder. If you do not want to recompile the source then just skip this section. If you would like to run your own compiled version of FPC, then just type:<br />
<syntaxhighlight lang="bash"><br />
bash-3.1$ tar -xvzf fpc-2.4.2.source.tar.gz<br />
bash-3.1$ cd fpc-2.4.2<br />
bash-3.1$ make clean all<br />
bash-3.1$ su<br />
bash-3.1$ password: xxxxxxxx<br />
bash-3.1# make install<br />
bash-3.1# exit<br />
bash-3.1$ fpc<br />
free pascal compiler version 2.4.2 [...] for i386 <br />
Copyright (c) 2010 ...<br />
...<br />
...<br />
</syntaxhighlight><br />
Now you have your own compiled version working. <br />
<br />
-- The Lazarus IDE editor needs to look at the FPC source code. Even if you do not want to recompile FPC, you need its source code. However, in order to save some space, I only keep a clean (not compiled) copy of the source code. I start from the file "fpc-2.4.2.source.tar.gz" again and I copy it to the /usr/local/src/FPC folder:<br />
<syntaxhighlight lang="bash"><br />
bash-3.1$ su<br />
bash-3.1$ password: xxxxxxxx<br />
bash-3.1# cp fpc-2.4.2.source.tar.gz /usr/local/src/<br />
bash-3.1# cd /usr/local/src/<br />
bash-3.1# tar -xvzf fpc-2.4.2.source.tar.gz<br />
bash-3.1# rm fpc-2.4.2.source.tar.gz<br />
bash-3.1# exit<br />
</syntaxhighlight><br />
The version number is included in the folder name "fpc-2.4.2". This way I can keep many versions the the compiler source and eventually switch between them.<br />
<br />
-- The lazarus IDE is kept in my USER "~/lazarus" folder and always compiled in USER mode. I usually download the "lazarus-0.9.30-0.tar.gz" file, but here we can also get the latest development version from the SVN server:<br />
<syntaxhighlight lang="bash">bash-3.1$ svn co http://svn.freepascal.org/svn/lazarus/trunk lazarus</syntaxhighlight><br />
This is for the first time you load it. Next time you will only need to type:<br />
<syntaxhighlight lang="bash">bash-3.1$ svn update</syntaxhighlight><br />
If you do not have SVN installed on your computer, here is how to get it quickly:<br />
<br />
--Download the two files: "subversion-1.4.6.tar.gz" and "subversion-deps-1.4.5.tar.gz" (or later versions). Type:<br />
<syntaxhighlight lang="bash"><br />
bash-3.1$ tar -xvzf subversion-1.4.6.tar.gz<br />
bash-3.1$ tar -xvzf subversion-deps-1.4.6.tar.gz<br />
bash-3.1$ cd subversion-1.4.6<br />
bash-3.1$ ./configure<br />
bash-3.1$ make<br />
bash-3.1$ su<br />
bash-3.1$ password:xxxxxx<br />
bash-3.1# make install<br />
bash-3.1$ exit<br />
</syntaxhighlight><br />
<br />
-- At this point you have the folder "~/lazarus" containing the source code. You should compile it very simply:<br />
bash-3.1$ make clean all<br />
After a few minutes, the compiler stops:<br />
<syntaxhighlight lang="bash"><br />
...<br />
...<br />
Linking ../Lazbuild<br />
987 linescompiled ...<br />
make [2] leaving ...<br />
make [1] leaving ...<br />
bash-3.1$<br />
</syntaxhighlight><br />
<br />
-- Just type:<br />
<syntaxhighlight lang="bash">bash-3.1$ ./lazarus</syntaxhighlight><br />
WOW! You get a message: "Free Pascal sources not found". Just follow the instructions and indicate your Free Pascal Compiler source directory in the panel: "Environment->Options->Files". As explained earlier, on my computer this should point to "/usr/local/src/fpc-2.4.2". Note that when you change this folder, you should click on "Environment / Rescan_FPC_source_directory".<br />
<br />
Voila!<br />
<br />
====Installing on openSUSE====<br />
<br />
===== Installing Lazarus 0.9.30. For openSUSE 11.1 users =====<br />
<br />
Free Pascal Compiler requires:<br><br />
- Gnu binutils (gnu as, gnu ld, gnu make)<br />
These utils can be installed by:<br />
<syntaxhighlight lang="bash">zypper in -t pattern devel_basis</syntaxhighlight><br />
<br />
Lazarus also requires these components:<br />
1) glib2 devel<br />
2) gtk2 devel<br />
<br />
'''Important''': The lazarus rpm requires the gtk 2 version, not the version 1.2.<br />
<br />
These libraries can be installed by:<br />
<syntaxhighlight lang="bash">zypper -n install gtk2 glib2</syntaxhighlight><br />
<br />
Download these binary files (RPM)<br />
:* fpc-2.4.2-0.i686.rpm (yes i686 and not i386)<br />
:* fpc-src-2.4.2-0.i686.rpm<br />
:* lazarus-0.9.30-0.i686.rpm<br />
<br />
install them opening a terminal session (mouse's right button -> Menu: Open in terminal)<br />
<syntaxhighlight lang="bash"><br />
rpm -Uvh fpc-2.4.2-0.i686.rpm<br />
rpm -Uvh fpc-src-2.4.2-0.i686.rpm<br />
rpm -Uvh lazarus-0.9.30-0.i686.rpm<br />
</syntaxhighlight><br />
<br />
====Installing Lazarus 1.0.8 on Fedora Core 18====<br />
<br />
After a fresh install of Fedora Core 18 '''yum''' is installed, but fails with an error messages.<br />
*Open '''Software''', check for new software and update all packages.<br />
*Reboot.<br />
*Install packages '''gtk2-devel''', '''fpc''' and '''subversion'''. Fedora Core 18 already has fpc 2.6.2. <br />
*Open a Terminal and do<br />
svn co svn+ssh://svn.freepascal.org/FPC/svn/lazarus/tags/lazarus_1_0_8 lazarus<br />
cd lazarus<br />
make bigide<br />
./lazarus<br />
<br />
==== Installing Lazarus on Scientific Linux ====<br />
<br />
Scientific Linux is an RPM-based distribution focussing on science and research. See [[Scientific Linux]] for details. <br />
<br />
==== Installing from source ====<br />
<br />
If you prefer to install from source and compile the files yourself, follow these instructions. Because the whole lazarus stuff is installed into one directory, uninstall is very easy and you don't need to be root to install lazarus. You can get tgz files for fpc, fpcsrc and lazarus from the downloads section or you can download it directly via svn.<br />
<br />
Here is an example of installing 0.9.28 to Ubuntu 6.06. If you understand Linux commands and bash script, you can get what steps are needed. Just copy the script (change the version number when new version has been released), paste it into a text editor, and save it as something like "install_lazarus.sh". Give it execute permission, and run it in <br />
a console.<br />
{{Note|In this example, fpc is installed in /opt. So when prompted ''''Install prefix'''', enter '/opt/fpc'. }}<br />
<br />
<syntaxhighlight lang="bash"><br />
#!/bin/sh<br />
<br />
#installing required packages<br />
sudo apt-get install build-essential<br />
sudo apt-get install libgtk2.0-dev<br />
sudo apt-get install libgdk-pixbuf-dev<br />
<br />
#installing Free Pascal source<br />
cd /opt<br />
sudo wget http://nchc.dl.sourceforge.net/lazarus/fpc-src-2.4.2.source.tgz<br />
sudo tar -xvf fpc-src-2.4.2.source.tgz<br />
sudo mv fpc fpcsrc<br />
<br />
#installing Free Pascal<br />
sudo mkdir fpc<br />
cd fpc<br />
sudo wget http://nchc.dl.sourceforge.net/lazarus/fpc-2.4.2.i686-linux.tar<br />
sudo tar -xvf fpc-2.4.2.i686-linux.tar<br />
echo "Enter '/opt/fpc' when prompted 'Install prefix'"<br />
sudo sh install.sh<br />
<br />
#adding fpc path to the PATH<br />
echo "#FPC PATH" >> ~/.bash_profile<br />
echo "if [ -d /opt/fpc/bin ] ; then" >> ~/.bash_profile <br />
echo PATH=/opt/fpc/bin:"${PATH}" >> ~/.bash_profile<br />
echo "fi" >> ~/.bash_profile<br />
<br />
#installing Lazarus<br />
cd ../<br />
sudo wget http://nchc.dl.sourceforge.net/lazarus/lazarus-0.9.30-0.tar.gz<br />
# sudo wget https://downloads.sourceforge.net/project/lazarus/Lazarus%20Zip%20_%20GZip/Lazarus%201.8.0RC5/lazarus-1.8.0-RC5.tar.gz<br />
sudo tar -zxvf lazarus-0.9.30-0.tar.gz<br />
PATH=/opt/fpc/bin:"${PATH}"<br />
sudo chmod -R 777 lazarus<br />
cd lazarus<br />
make clean all<br />
./lazarus<br />
</syntaxhighlight><br />
<br />
{{Note|You have to manually set fpc-src path in the Environmental Options.}}<br />
<br />
===== Downloading Lazarus Source Code =====<br />
<br />
Both the Lazarus and FPC source code reside in SVN/subversion repositories. SVN provides an easy way to update your sources by only downloading the changes. This is the recommended way and saves you a lot of time. A connection to the internet is needed for this, but you don't need to be root. <br />
<br />
Please note these instructions are for subversion, but there is also a Git mirror repository of Free Pascal Compiler and Lazarus: see [[git mirrors|Git mirror]]. You can also use git directly with the subversion server using git-svn link: see [[Lazarus git-svn|Lazarus git-svn]].<br />
<br />
Lazarus does not need any special permissions, neither during installation nor at runtime.<br />
<br />
<font color="red">If you decide to use TortoiseSVN, remember to check "command line client tools" during its installation if you want Lazarus to show the SVN revision number in the About dialog.</font><br />
<br />
;Now getting the sources:<br />
<br />
<syntaxhighlight lang="bash"> svn checkout http://svn.freepascal.org/svn/lazarus/trunk/ lazarus</syntaxhighlight><br />
(replace the last lazarus with any other dir where you want to place your sources)<br />
<br />
On subsequent occasions, to update simply type<br />
<br />
<syntaxhighlight lang="bash"> svn update lazarus</syntaxhighlight><br />
<br />
For more information on Subversion, see:<br />
http://subversion.tigris.org/<br />
<br />
===== Compiling and running =====<br />
<br />
Whether you checkout from cvs or svn, the next step is: <br />
<br />
;compile lazarus:<br />
<syntaxhighlight lang="bash"><br />
cd lazarus<br />
make (gmake on BSD)<br />
</syntaxhighlight><br />
<br />
If fpc is installed correctly, the compilation should work without problems. If not, see FAQ.<br />
<br />
;Start lazarus<br />
<syntaxhighlight lang="bash"> ./lazarus</syntaxhighlight><br />
<br />
The IDE should start. If you started lazarus in a terminal, you can see some notes about missing settings. This is normal at first start. The IDE automatically tries to find out where the Free Pascal compiler and its sources are installed by searching in the most common directories.<br />
<br />
;Check the paths:<br />
: Use the IDE menu to go to<br />
:: Environment -> Environment Options -> Files<br />
<br />
The 'FPC Source directory' should point to your fpc source directory. This directory normally ends with /fpc/ or /fpcsrc/ (e.g. /usr/src/fpcsrc or /home/username/freepascal/fpc) and contains directories like 'compiler', 'docs', 'fcl', 'rtl' and 'packages'.<br />
<br />
See here for the documentation about this dialog: [[IDE_Window:_Environment_Options|IDE Options]].<br />
<br />
;Hint:<br />
To update lazarus you can use<br />
<syntaxhighlight lang="bash"> svn update lazarus</syntaxhighlight><br />
then for either update pathway:<br />
<syntaxhighlight lang="bash"> make clean all (gmake on BSD)</syntaxhighlight><br />
This will rebuild lazarus and create an IDE without lazarus packages. To link your installed packages do '''after''' the above:<br />
<syntaxhighlight lang="bash"> ./lazbuild --build-ide=</syntaxhighlight><br />
<br />
You may have to append other options if for example you use a custom config directory (ie. add --pcp="C:\Documents and Settings\<USER>\Local Settings\Application Data\lazarus-tests"). See [[lazbuild]].<br />
<br />
==== Installing Lazarus under Debian GNU/Linux ====<br />
<br />
There are preliminary Debian packages for lazarus available for download. They are not the latest versions, however. Make sure you read /usr/share/doc/lazarus/README.Debian carefully before you start using it. Feedback is needed and appreciated; please send your comments to Carlos Laviola <claviola@debian.org>.<br />
<br />
Note that for a fully working Lazarus install, no older or incompatible versions of, for example, the fpc source or fpc compiler must be installed. Remove them by typing<br />
<br />
<syntaxhighlight lang="bash">dpkg -r <package name></syntaxhighlight><br />
<br />
without .deb extension. And then install the newest versions as described.<br />
<br />
=== Installing Lazarus under Windows ===<br />
<br />
The current releases of the Windows Lazarus binary packages install very easily, and should work 'out-of-the-box'. <br />
<br />
Except for Win98 and ME, which needs a special flag to compile. See [[#Installing_from_source_2|Installing from source]].<br />
<br />
==== Installing Lazarus on Portable USB Driver ====<br />
<br />
It is even possible to install the whole Lazarus/FPC package on a portable USB drive (capacity at least 256 MB), for use in environments where you are not allowed to install software on your Windows workstation or where you haven't got administrator privileges. You do have to be a little careful about adjusting the paths in the compiler and environment options and the fpc.cfg file. It may also be necessary to keep the directory for test compilation on your portable drive. <br />
<br />
<< Q:DOES ANYONE KNOW HOW TO SET UP RELATIVE PATHS IN THESE TAGS AND FILES, SO THAT THE ADDRESSING WORKS WHEN YOU MOVE THE USB DEVICE TO ANOTHER MACHINE WHERE IT HAS A DIFFERENT DRIVE LETTER? <br />
<br />
A: This is what I do. It's relatively convoluted, but it's the best solution I've found. I have a "bin" directory on my USB drive, where I have several scripts and utilities installed. Inside that directory is a batch file called "setenv.bat" which sets an environment variable called THUMBDRIVE. It is set by using this command in the batch file:<br />
<syntaxhighlight lang="dos">set THUMBDRIVE=%CD:~0,2%</syntaxhighlight><br />
This is used in setenv.bat to set some paths to other things I have installed on the USB drive. I also have a link in the root directory of the thumb drive with this property:<br />
<syntaxhighlight lang="dos">%SystemRoot%\system32\cmd.exe /k bin\setenv</syntaxhighlight><br />
so that when I click on that link when the thumb drive folder is diplayed after inserting it, it will open a command prompt at the thumb drive with the environment variables set from setenv.bat.<br />
<br />
Also inside the bin directory is [http://sed.sf.net sed] (the actual binary is one I obtained from the mingw distribution). So I created another batch file called fixlaz.bat which takes one argument, the drive letter which is currently in the Lazarus/fpc settings files that you want to change (note that this is the previous drive letter the last time you ran fixlaz.bat, not the current one of your USB drive which fixlaz.bat already knows). You will need to create this batch file to fit where you installed Lazarus in the root directory structure of the drive if you didn't install it directly in the root folder, and then repeat these lines also for the editoroptions.xml and fpc.cfg files (fpc.cfg is the the fpc bin directory, which might be buried deep in the lazarus folder):<br />
<syntaxhighlight lang="dos"><br />
copy %THUMBDRIVE%\lazarus\environmentoptions.xml %THUMBDRIVE%\lazarus\environmentoptions.bak<br />
sed -e 's/%1/%THUMBDRIVE%/g' %THUMBDRIVE%\lazarus\environmentoptions.bak > %THUMBDRIVE%\lazarus\environmentoptions.xml<br />
</syntaxhighlight><br />
<br />
So to use it, I would type at the command prompt of the USB drive:<br />
<syntaxhighlight lang="dos">fixlaz G:</syntaxhighlight><br />
if "G:" was the previous drive letter used the last time I ran it. This will then scan the file(s) and replace "G:" with the current drive letter of the USB drive, which is in the %THUMBDRIVE% environment variable (after running setenv.bat). Note that you could write it to save the current drive letter in a separate file, so that you wouldn't have to remember it yourself the next time. But this works well enough for me right now.<br />
>><br />
<br />
The binary package is available for Linux and Windows from<br />
<br />
http://sourceforge.net/project/showfiles.php?group_id=89339<br />
<br />
Download the latest release (currently Lazarus-0.9.30-0-win32.exe) and launch the application. You will be taken through a typical Windows installation, in which the FPC compiler and source libraries are installed within the same directory structure as Lazarus, and the IDE should launch and operate without significant problems, provided you have uninstalled(!!!) any previous version of Lazarus and/or FPC (often found in the C:\pp directory).<br />
<br />
You can also use a Lazarus Snapshot. For download locations see [[Lazarus Snapshots Downloads]].<br />
<br />
Tip:<br />
It's perhaps a good idea to reboot your Windows after you installed Lazarus and before you try to install additional lazarus components as zeoslib fore example.<br />
<br />
==== Installing from source ====<br />
If you prefer to install from sources, then follow these instructions.<br />
<br />
Please note these instructions are for SubVersion, but there is also a Git mirror repository of Free Pascal Compiler and Lazarus. See [[git mirrors|Git mirror]] for details. You can also use git directly with SubVersion server using git-svn link. See [[Lazarus git-svn|Lazarus git-svn]] for details.<br />
<br />
Open a command prompt window. Start->Run...>CMD or choose MS-DOS icon. You will use this window to enter the commands below<br />
<br />
You have to download the lazarus source from one of the [[Lazarus Snapshots Downloads|snapshots servers]].<br />
Then unzip it to c:\lazarus for example [below called $(LazarusDir)].<br />
<br />
Or you use SVN (example for text mode SVN; adapt to GUI tools such as TortoiseSVN if you want to):<br />
<syntaxhighlight lang="bash"><br />
mkdir c:\lazarus<br />
cd /d c:\lazarus<br />
svn checkout http://svn.freepascal.org/svn/lazarus/trunk/ c:\lazarus<br />
</syntaxhighlight><br />
<br />
You have to install at least the latests stable FPC version (e.g. FPC 2.6.4, but an FPC 3.1.1 snapshot is also possible). <br />
<br />
<br />
Type (replace $(LazarusDir) with the path you have unzipped/checked out Lazarus; replace <br />
<syntaxhighlight lang="dos"><br />
cd /d $(LazarusDir)<br />
rem Of course change the first path variable to<br />
rem the path of your FPC compiler<br />
set path=c:\freepascal\bin\i386-win32;$(LazarusDir)<br />
make<br />
</syntaxhighlight><br />
<br />
*Win9x: use make OPT="-dWIN9XPLATFORM" (Lazarus trunk (and upcoming 1.4)) otherwise the lazarus.exe will not be able to run on this platform.<br />
<br />
If this works, you can type: lazarus.exe. <br />
<br />
You can compile examples also:<br />
<br />
<syntaxhighlight lang="dos"><br />
cd /d $(LazarusDir)\examples<br />
make<br />
</syntaxhighlight><br />
<br />
<s>If you extracted lazarus to another drive, eg.: d:\lazarus, tt can happen that you need the gnu make utility to make it. If you have it, you can take its path to set path=...;<gmakepath> but it is simpler not to choose drive d:\</s><br />
<br />
==== Installing from source starting with a stable release====<br />
An alternative version of the instructions above.<br />
<br />
1> First of all install the latest stable Lazarus to obtain a good starting FPC, for example in C:\lazarus_1_4<br />
<br />
2> Now use TortoiseSVN to checkout http://svn.freepascal.org/svn/lazarus/trunk/ into c:\lazarus<br />
<br />
3> Make the following C:\lazarus\build.bat file:<br />
<br />
Replace $(LazarusDir) with your Lazarus did and make sure the FPC version number matches<br />
<syntaxhighlight lang="dos"><br />
SET PATH=$(LazarusDir)\fpc\2.6.4\bin\i386-win32\<br />
make bigide<br />
</syntaxhighlight><br />
<br />
Now create a shortcut in your desktop to start Lazarus and put the following command to start Lazarus which will make sure that it separates the config files from the stable and the SVN versions:<br />
<br />
<syntaxhighlight lang="dos">$(LazarusDir)\startlazarus.exe --pcp=$(LazarusDir)\configdir </syntaxhighlight><br />
<br />
Always start Lazarus from this shortcut, never directly from the executable. At the first time you start Lazarus configure you FPC dir, FPC sources dir and Lazarus dir.<br />
<br />
==== Building Lazarus on Win98 and WinME ====<br />
<br />
Because the Lazarus IDE by default links to a dll-call "CreateToolhelp32Snapshot", which does not exist on the Win9x platform, the IDE will not run on Win9x out of the box.<br />
<br />
In order to make it run you have to rebuild the IDE with "-dWIN9XPLATFORM".<br />
<br />
=== Installing Lazarus under FreeBSD ===<br />
The following applies to FreeBSD 9+ only.<br />
<br />
====via Ports tree====<br />
The latest version of Lazarus available in the FreeBSD port tree, is v 1.6.4 We can use that to install Lazarus.<br />
<br />
<syntaxhighlight lang="bash">[]# cd /usr/ports/editors/lazarus && make install clean clean-depends</syntaxhighlight><br />
<br />
If you start Lazarus IDE and you get a messages about missing source files, then go to '''Environment - Environment Options - Files Tab: FPC source library''' and enter the directory of the FPC source files. It can be obtained from /usr/ports/distfiles/freepascal<br />
<br />
====via pkgng====<br />
<syntaxhighlight lang="bash">[]# pkg install editors/lazarus</syntaxhighlight><br />
<br />
At this point Lazarus might complain about missing source files. If you don't have them:-<br />
<br />
mkdir /usr/ports/distfiles<br />
mkdir /usr/ports/distfiles/freepscal<br />
cd /usr/ports/distfiles/freepascal<br />
wget http://distcache.freebsd.org/ports-distfiles/freepascal/fpc-3.0.2.source.tar.gz<br />
tar xvfz fpc-3.0.2.source.tar.gz<br />
<br />
Change the 3.0.2 to whatever version of fpc you have installed. Then go to '''Environment - Environment Options - Files Tab: FPC source library''' and enter<br />
<br />
/usr/ports/distfiles/freepascal/fpc-3.0.2<br />
<br />
If when you try to compile something you get this error<br />
<br />
Error: resource compiler "fpcres" not found, switching to external mode<br />
<br />
Then you can install fpcres like this<br />
<br />
<syntaxhighlight lang="bash">[]# pkg install lang/fpc-utils</syntaxhighlight><br />
<br />
====via Lazarus repository====<br />
This option will often be used if you want to follow Lazarus Trunk, a Fixes branch, or some other release (eg: compiling from a source tarball).<br />
* Use the SubVersion or Git repositories to checkout a copy of the source code you want, or unpack a downloaded source archive into a suitable location<br />
* The readme.txt file in Lazarus directory mentions 'make clean all'. This works if you are using Linux. Under FreeBSD 9.1 I had to replace 'make' with 'gmake'.<br />
<br />
cd /patch/to/lazarus_source<br />
gmake clean all<br />
<br />
=== Installing Lazarus under PC-BSD 1.0rc1+ ===<br />
<br />
You can install Lazarus on PB-BSD by simply downloading the Lazarus PBI from [http://www.pbidir.com/ PBI Dir]<br />
<br />
Note that you must install glib* port from /usr/port/devel/glib* or glib packages by pkg_add -r glib12 glib20.<br />
I will fix this in new PBI releases.<br />
<br />
'''[other OpenBSD/NetBSD/DragonFlyBSD goes here]'''<br />
<br />
=== Installing Lazarus under Mac OS X ===<br />
<br />
See [[Installing Lazarus on MacOS X]].<br />
<br />
=== Installing Lazarus under Haiku ===<br />
<br />
Lazarus requires Qt under Haiku. Qt is not installed by default under Haiku. You need to install package available from this site : http://qt-haiku.ru/<br />
<br />
Currently, there is no binary package to install Lazarus.<br />
<br />
You will have to compile Lazarus from sources.<br />
<br />
Detailed instructions to build Lazarus under Haiku are available here : [[Installing Lazarus on Haiku]]<br />
<br />
=Multiple Lazarus installs=<br />
Please see [[Multiple Lazarus]] for details on having more than one Lazarus version installed on one system. We cover issues that can arise due to multiple Lazarus installs here, because they can also happen when installing over a previous version.<br />
<br />
=Troubleshooting=<br />
These hints should (hopefully) be applicable across platforms; please adjust paths etc. according to your platform. Also, problems and solutions might be mentioned in the [[Lazarus_Faq]]. <br />
<br />
== Isolating the component ==<br />
There may be multiple problems when installing Free Pascal and Lazarus.<br />
If you have problems with starting Lazarus or compiling with it, first make sure your underlying FPC installation works, e.g. by trying to compile a simple example program.<br />
<br />
== Location of installed components ==<br />
On Linux/Unix/OSX, package/distribution provided installations will often install FPC in /bin or /usr/bin, with the libraries in /lib or /usr/lib, and probably the configuration in /etc/fpc.cfg<br />
Source (Subversion) downloads often install in /usr/local/bin (libraries in /usr/local/lib).<br />
<br />
== Missing or corrupt fpc.cfg file ==<br />
The fpc.cfg file is the configuration file for FPC. For windows, it can normally be found in the fpc compiler's executable directory. On Unix/Linux/OSX, it is first searched in /etc/fpc.cfg, then<br />
Only some problems that often occur are covered. Please see the Free Pascal User's guide, e.g. chapter Compiler usage/configuration file for more details, including the search order for fpc.cfg<br />
<br />
If it is not present, FPC will in general not compile your programs properly.<br />
<br />
=== Create fpc.cfg automatically ===<br />
You can try to create fpc.cfg via a tool:<br />
<br />
<syntaxhighlight lang="dos"><br />
rem Change to the FPC executable directory, adjust to taste<br />
cd /d C:\freepascal\fpc\2.4.5\bin\i386-win32<br />
rem Create fpc.cfg; basepath is the "root" of your fpc tree<br />
fpcmkcfg -d basepath=C:\freepascal\fpc\2.4.5 -o .\fpc.cfg<br />
</syntaxhighlight><br />
{{Note|earlier instructions had \bin\i386-win32 appended to the basepath. This is not needed since at least FPC 2.4.4.}}<br />
<br />
=== Create fpc.cfg manually ===<br />
If the above doesn't work for you, you can copy/paste the code below to a new file named fpc.cfg to your fpc executable directory (or ~/.fpc.cfg or other location). Modify the paths so they match your situation.<br />
<br />
$FPCVERSION is used to automatically determine the version of FPC being used for the compile. Using this variable allows this single fpc.cfg to be used with multiple fpc versions that have the same path structure making it easier to maintain multiple FPC installs.<br />
<br />
<pre><br />
#<br />
# Config file generated by fpcmkcfg on 24/12/2010 - 18:17:42<br />
# Example fpc.cfg for Free Pascal Compiler<br />
#<br />
<br />
# ----------------------<br />
# Defines (preprocessor)<br />
# ----------------------<br />
<br />
#<br />
# nested #IFNDEF, #IFDEF, #ENDIF, #ELSE, #DEFINE, #UNDEF are allowed<br />
#<br />
# -d is the same as #DEFINE<br />
# -u is the same as #UNDEF<br />
#<br />
<br />
#<br />
# Some examples (for switches see below, and the -? helppages)<br />
#<br />
# Try compiling with the -dRELEASE or -dDEBUG on the commandline<br />
#<br />
<br />
# For a release compile with optimizes and strip debuginfo<br />
#IFDEF RELEASE<br />
-O2<br />
-Xs<br />
#WRITE Compiling Release Version<br />
#ENDIF<br />
<br />
# For a debug version compile with debuginfo and all codegeneration checks on<br />
#IFDEF DEBUG<br />
-glh<br />
-Crtoi<br />
#WRITE Compiling Debug Version<br />
#ENDIF<br />
<br />
# ----------------<br />
# Parsing switches<br />
# ----------------<br />
<br />
# Pascal language mode<br />
# -Mfpc free pascal dialect (default)<br />
# -Mobjfpc switch some Delphi 2 extensions on<br />
# -Mdelphi tries to be Delphi compatible<br />
# -Mtp tries to be TP/BP 7.0 compatible<br />
# -Mgpc tries to be gpc compatible<br />
# -Mmacpas tries to be compatible to the macintosh pascal dialects<br />
#<br />
# Turn on Object Pascal extensions by default<br />
#-Mobjfpc<br />
<br />
# Assembler reader mode<br />
# -Rdefault use default assembler<br />
# -Ratt read AT&T style assembler<br />
# -Rintel read Intel style assembler<br />
#<br />
# All assembler blocks are AT&T styled by default<br />
#-Ratt<br />
<br />
# Semantic checking<br />
# -S2 same as -Mobjfpc<br />
# -Sc supports operators like C (*=,+=,/= and -=)<br />
# -Sa include assertion code.<br />
# -Sd same as -Mdelphi<br />
# -Se<x> compiler stops after the <x> errors (default is 1)<br />
# -Sg allow LABEL and GOTO<br />
# -Sh Use ansistrings<br />
# -Si support C++ styled INLINE<br />
# -SI<x> set interface style to <x><br />
# -SIcomCOM compatible interface (default)<br />
# -SIcorbaCORBA compatible interface<br />
# -Sm support macros like C (global)<br />
# -So same as -Mtp<br />
# -Sp same as -Mgpc<br />
# -Ss constructor name must be init (destructor must be done)<br />
#<br />
# Allow goto, inline, C-operators, C-vars<br />
-Sgic<br />
<br />
# ---------------<br />
# Code generation<br />
# ---------------<br />
<br />
# Uncomment the next line if you always want static/dynamic units by default<br />
# (can be overruled with -CD, -CS at the commandline)<br />
#-CS<br />
#-CD<br />
<br />
# Set the default heapsize to 8Mb<br />
#-Ch8000000<br />
<br />
# Set default codegeneration checks (iocheck, overflow, range, stack)<br />
#-Ci<br />
#-Co<br />
#-Cr<br />
#-Ct<br />
<br />
# Optimizer switches for i386 compiler<br />
# -Os generate smaller code<br />
# -O1 level 1 optimizations (quick optimizations)<br />
# -O2 level 2 optimizations (-O1 + slower optimizations)<br />
# -O3 level 3 optimizations (same as -O2u)<br />
# -Oa=N set alignment to N<br />
# -OoX switch on optimalization X. <br />
# -OoNOX switch off optimalization X.<br />
# X is one of REGVAR UNCERTAIN STACKFRAME PEEPHOLE ASMCSE LOOPUNROLL<br />
# -OpCPU set target processor.<br />
# CPU is one of 386, PENTIUM, PENTIUM2, PENTIUM3, PENTIUM4, PENTIUMM <br />
<br />
<br />
# -----------------------<br />
# Set Filenames and Paths<br />
# -----------------------<br />
<br />
# Both slashes and backslashes are allowed in paths<br />
<br />
# path to the messagefile, not necessary anymore but can be used to override<br />
# the default language<br />
#-FrC:\freepascal\fpc\$FPCVERSION/msg/errore.msg<br />
#-FrC:\freepascal\fpc\$FPCVERSION/msg/errorn.msg<br />
#-FrC:\freepascal\fpc\$FPCVERSION/msg/errores.msg<br />
#-FrC:\freepascal\fpc\$FPCVERSION/msg/errord.msg<br />
#-FrC:\freepascal\fpc\$FPCVERSION/msg/errorr.msg<br />
<br />
#IFDEF FPCAPACHE_1_13<br />
-FuC:\freepascal\fpc\$FPCVERSION/units/$FPCTARGET/httpd13/<br />
#ELSE<br />
#IFDEF FPCAPACHE_2_0<br />
-FuC:\freepascal\fpc\$FPCVERSION/units/$FPCTARGET/httpd20<br />
#ELSE<br />
-FuC:\freepascal\fpc\$FPCVERSION/units/$FPCTARGET/httpd22<br />
#ENDIF<br />
#ENDIF<br />
<br />
# searchpath for units and other system dependent things<br />
-FuC:\freepascal\fpc\$FPCVERSION/units/$FPCTARGET/<br />
-FuC:\freepascal\fpc\$FPCVERSION/units/$FPCTARGET/*<br />
-FuC:\freepascal\fpc\$FPCVERSION/units/$FPCTARGET/rtl<br />
<br />
# path to the gcclib<br />
<br />
<br />
# searchpath for libraries<br />
#-FlC:\freepascal\fpc\$FPCVERSION/lib<br />
#-Fl/lib;/usr/lib<br />
<br />
# searchpath for tools<br />
-FDC:\freepascal\fpc\$FPCVERSION/bin/$FPCTARGET<br />
<br />
#IFNDEF CPUI386<br />
#IFNDEF CPUAMD64<br />
#DEFINE NEEDCROSSBINUTILS<br />
#ENDIF<br />
#ENDIF<br />
<br />
#IFNDEF Win32<br />
#DEFINE NEEDCROSSBINUTILS<br />
#ENDIF<br />
<br />
# binutils prefix for cross compiling<br />
#IFDEF FPC_CROSSCOMPILING<br />
#IFDEF NEEDCROSSBINUTILS<br />
-XP$FPCTARGET-<br />
#ENDIF<br />
#ENDIF<br />
<br />
<br />
# -------------<br />
# Linking<br />
# -------------<br />
<br />
# generate always debugging information for GDB (slows down the compiling<br />
# process)<br />
# -gc generate checks for pointers<br />
# -gd use dbx<br />
# -gg use gsym<br />
# -gh use heap trace unit (for memory leak debugging)<br />
# -gl use line info unit to show more info for backtraces<br />
# -gv generates programs tracable with valgrind<br />
# -gw generate dwarf debugging info<br />
#<br />
# Enable debuginfo and use the line info unit by default<br />
#-gl<br />
<br />
# always pass an option to the linker<br />
#-k-s<br />
<br />
# Always strip debuginfo from the executable<br />
-Xs<br />
<br />
<br />
# -------------<br />
# Miscellaneous<br />
# -------------<br />
<br />
# Write always a nice FPC logo ;)<br />
-l<br />
<br />
# Verbosity<br />
# e : Show errors (default) d : Show debug info<br />
# w : Show warnings u : Show unit info<br />
# n : Show notes t : Show tried/used files<br />
# h : Show hints m : Show defined macros<br />
# i : Show general info p : Show compiled procedures<br />
# l : Show linenumbers c : Show conditionals<br />
# a : Show everything 0 : Show nothing (except errors)<br />
# b : Show all procedure r : Rhide/GCC compatibility mode<br />
# declarations if an error x : Executable info (Win32 only)<br />
# occurs<br />
#<br />
# Display Info, Warnings, Notes and Hints<br />
-viwn<br />
# If you don't want so much verbosity use<br />
#-vw<br />
</pre><br />
<br />
=== Testing and batch file/shortcut usage ===<br />
You can also explicitly specify a fpc.cfg to use:<bash>fpc -n -@/path/to/fpc.cfg</bash>. Handy for testing, creating shortcuts/shell scripts, having multiple FPC installations etc.<br />
<br />
If that does not work, you can try to create fpc.cfg manually: copy over an existing fpc.cfg from a working installation and change the paths.<br />
<br />
== Mixing FPC versions ==<br />
If your FPC cannot find its sources/ppu files while fpc.cfg has the right paths, another cause could be that the fpc executable is a different version than the (link to) ppc<architecture> compiler.<br />
<br />
When compiling with fpc, fpc starts e.g ppc386 to compile for x86 targets. Please make sure fpc and ppc<architecture> are compiled by the same version/sourced from the same package.<br />
<br />
== Lazarus can't find sources ==<br />
There may be a message saying Lazarus can't find the sources; if so, go to the menu at the top and select Environment -> Environment options and insert the name of the directory where you expect to find your lazarus files (eg ~/FreePascal/lazarus), your compiler (e.g. /usr/local/bin/ppc386), the Free Pascal source directory (e.g. /usr/local/lib/fpc/$version/fpc or a directory in your own space e.g. ~/FreePascal/fpc). Then close the dialog and start programming.<br />
<br />
== Paths in Lazarus options and project options ==<br />
Note that you can set paths (for compiler, FPC units etc) in the general options settings. Options you specify in Project Options can override these per project.<br />
If you experience path-related problems in a project but not in another, you might check if you mistakenly override general options.<br />
<br />
=Installing old versions=<br />
See [[Installation hints for old versions]]<br />
<br />
<br />
{{AutoCategory}}<br />
[[Category:FPC]]<br />
[[Category:Lazarus]]<br />
[[Category:Install]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=tiOPF&diff=113230tiOPF2017-10-23T14:15:33Z<p>Zoran: Fixed link</p>
<hr />
<div>{{tiOPF}}<br />
<br />
=== About ===<br />
The [http://tiopf.sourceforge.net TechInsite Object Persistence Framework] (tiOPF) is an Open Source framework of Delphi/Object Pascal code that simplifies the mapping of an object oriented business model into a relational database. The framework is mature and robust. It has been in use on production sites since 1999. It is free, open source, and available for immediate download with full source code. <br />
<br />
Some of the key features of the tiOPF include:<br />
<br />
* Lets you build an object oriented application that can swap databases with the flick of a switch like a command line parameter or a change of a compiler directive. Currently there are persistence layers for:<br />
**Interbase via IBX<br />
**Oracle via DOA<br />
**MySQL via [[Zeos tutorial|Zeos]]<br />
**MySQL via [[SQLdb_Package|SqlDB]]<br />
**XML via MSDOM<br />
**XML via XMLLite<br />
**Paradox via BDE<br />
**MS Access via ADO<br />
**MS SQL Server via ADO<br />
**MS SQL Server via SqlDB<br />
**Firebird via FBLib<br />
**Firebird via SqlDB<br />
**Firebird via Zeos<br />
**PostgreSQL via SqlDB<br />
**HTTP Remote Persistence (for n-tier applications with built-in generic application server)<br />
**Text files (CSV and TAB files)<br />
* Family of abstract base classes for building a complex object model<br />
* 27 Persistent Object-aware components for building complex GUIs (Delphi only).<br />
* Model-GUI-Mediators implementation for enabling any standard GUI component to become Object-aware. MGM currently has mediators defined for: VCL, LCL and [[fpGUI|fpGUI Toolkit]].<br />
* 1600+ DUnit2/[[FPTest]] tests to guarantee stability<br />
* 160+ pages of documentation to get you started<br />
* News groups for support<br />
* Automated, daily builds and unit testing. This is done under Linux and Windows and uses FPC & Delphi compilers.<br />
* Lots of demos focusing on specific parts of the framework for easy learning.<br />
* Cross platform. Currently tested on Windows, Linux and FreeBSD (32 & 64-bit).<br />
<br />
=== Authors ===<br />
Peter Hinrichsen - Original Developer.<br><br />
[[User:ggeldenhuys|Graeme Geldenhuys]] - Ported to Free Pascal and current maintainer.<br />
<br />
=== License ===<br />
tiOPF uses a dual license. Developers can use the [http://opensource.org/licenses/mozilla1.1.php Mozilla Public License 1.1] or the Modified LGPL license (as used by libraries of FPC and Lazarus).<br />
<br />
=== Support ===<br />
The best way to get support is by signing up to tiopf.support news group — see here: http://tiopf.sourceforge.net/Support.shtml<br />
<br />
=== Download ===<br />
For some years now, the tiOPF project does not make official release downloads. The tiOPF projects works on a similar principal to a "rolling release". Thus if you want the latest version with the latest features and fixes, you must get the source code from the Git code repository.<br />
<br />
You can use the following commands to check out the source:<br />
<br />
git clone <nowiki>git://tiopf.git.sourceforge.net/gitroot/tiopf/tiopf</nowiki><br />
<br />
You will now have a 'tiopf' directory containing the tiOPF repository. By default Git will also have checked out the 'master' branch for you. The tiOPF project doesn't use the 'master' branch for development, so switch to the 'tiopf2' branch as follows:<br />
<br />
git branch tiopf2 origin/tiopf2 (1)<br />
git checkout tiopf2 (2)<br />
<br />
# Creates a local branch named 'tiopf2', which points to the remote tiopf2 branch.<br />
# Switch to your local 'tiopf2' branch. <br />
<br />
Another simpler way of doing this is:<br />
<br />
git clone <nowiki>--branch=tiopf2 git://tiopf.git.sourceforge.net/gitroot/tiopf/tiopf</nowiki><br />
<br />
You will now have a 'tiopf' directory containing the tiOPF repository.<br />
<br />
{{Note|When working with Free Pascal and tiOPF, the only supported compiler is the latest released FPC (and the related fixes branch), and the 'tiopf2' branch of tiOPF.}}<br />
<br />
<br />
For a short introduction to using Git, you can refer to a message posted in the tiopf.development newsgroup. [http://geldenhuys.co.uk/webnews/webnews.cgi?user=anonymous;group=tiopf.development;article=3272 tiopf.development;article=3272]. For very good and detailed documentation on Git, we highly recommend you browse through the official Git documentation as well, located here: [http://git-scm.com/documentation http://git-scm.com/documentation]<br />
<br />
=== Dependencies / System Requirements ===<br />
* Compiler: FPC 2.6.4. The latest released FPC version.<br />
* Components for your required persistence layer, if it is not included with the compiler.<br />
<br />
'''Status''': Stable (tested on Windows, Linux and FreeBSD)<br />
<br />
'''Issues''': None<br />
<br />
=== Installation ===<br />
==== The Packages ====<br />
Inside the <tiopf>\Compilers\FPC directory there are four packages.<br />
; tiOPF.lpk : Core units (run-time only package)<br />
; tiOPFGUI.lpk : GUI related units and tiOPF+LCL custom components ['''deprecated'''] (run-time only package). <br />
; tiOPFGUIDsgn.lpk : Registers/Installs the tiOPF+LCL custom components into the Lazarus component palette (design-time only package). The tiOPF+LCL custom GUI components used under Lazarus are unmaintained and '''deprecated'''. It is preferred to use the Model-GUI-Mediator components instead. See tiOPFLCL.lpk package instead.<br />
; tiOPFLCL.lpk : GUI related units which replaces tiOPFGUI.lpk and does not contain any of the tiOPF custom GUI components. This packages uses the Mediators which makes standard LCL components "object-aware" and is the preferred way of hooking up your UI to you business objects. (run-time only package)<br />
; tiOPFHelpIntegration.lpk : Integrates the fpdoc generated help files into Lazarus's help system (design-time only package)<br />
<br />
==== The Setup ====<br />
* Open Lazarus IDE<br />
* Open the package ''tiOPF.lpk'' with 'Package -> Open Package File (.lpk)' located in the <tiopf>\Compilers\FPC\ directory.<br />
* Click on Compile<br />
* Open the ''tiOPFLCL.lpk'' package and click Compile<br />
<br />
Optional<br />
<br />
* Open the ''tiOPFHelpIntegration.lpk'' package and click Compile and the Install (Lazarus should rebuild and restart).<br />
<br />
<u>NOTE #1</u><br><br />
The SqlDB+Firebird database components are set as the default persistence layer for Free Pascal in the tiOPF.lpk package. This was simply done because SqlDB in included with the Free Pascal Compiler, and Firebird is a popular database option. If you don't need this persistence layer you can simply disable it as described below.<br />
<br />
Persistence layers are controlled by a Compiler Directive under ''Compiler Options'' -> ''Other'' -> ''Custom Options''. eg: The LINK_FBL directive relates to the FBLib components. The LINK_SQLDB_IB directive relates to the SqlDB (Interbase/Firebird) components.<br />
For all the available options see the end of the ''tiOPFManager.pas'' unit.<br />
<br />
<u>NOTE #2</u><br><br />
For the Integrated Help to work, Lazarus needs to know how to find the html help files. Please read the ''tiOPFHelpIntegration.txt'' file located in <tiopf>\Compilers\FPC\ for further instructions.<br />
<br />
=== Usage ===<br />
In Lazarus, open your project and add tiOPF as a Required Package ''(Project -> Project Inspector -> Add)''. Include ''tiObject'' in your uses clause.<br />
You are now ready to create objects descending from TtiObject or TtiObjectList.<br />
<br />
See the example projects in the Demos directory for additional examples.<br />
<br />
=== Links ===<br />
tiOPF home page: http://tiopf.sourceforge.net/<br />
<br />
==== Basics about tiOPF and Design Patterns ====<br />
For people who are searching basics about tiOPF or Design Patterns, there are a couple of published articles at (http://geldenhuys.co.uk/articles/)<br />
<br />
Written by Graeme Geldenhuys<br />
<br />
;2008-08: Simple Factory Pattern (download pdf) [150KB]<br />
;2008-09: Model-GUI-Mediator (download pdf - 251KB) & (source code - 9KB)<br />
;2008-11: Iterator Pattern (download pdf - 147KB) & (source code - 4KB)<br />
;2009-01: The Adapter Pattern (download pdf) [237KB]<br />
;2009-02: Intro to Git - source code management (download pdf) [257KB]<br />
;2009-03: The State Pattern (download pdf) [217KB]<br />
;2009-07: Relationship Manager (download pdf) [375KB]<br />
;2009-09: Hierarchies in SQL - Nested Sets (download pdf) [163KB]<br />
;2011-12: The Facade Design Pattern (download pdf) [297KB]<br />
<br />
[[Category:Databases]]<br />
[[Category:Framework]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=FPReport_FAQ&diff=113140FPReport FAQ2017-10-21T13:43:08Z<p>Zoran: /* I did not find FPReport in my FPC dir */</p>
<hr />
<div>== General ==<br />
=== Read the README.txt file ===<br />
Before you compile the FPReport, have a look at the README.txt<br />
<br />
=== FPReport <> LazReport ===<br />
FPReport is not LazReport !! This are 2 differnet reportsystems. <br />
* LazReport is based on FreeReport 2.32 see [[LazReport_Documentation]]. <br />
* FPReport is written from scatch, see [[FPReport]] documanetation.<br />
<br />
=== Papermanager must registered ===<br />
You never registered the standard page sizes with:<br />
<syntaxhighlight><br />
if PaperManager.PaperCount=0 then<br />
PaperManager.RegisterStandardSi<br />
</syntaxhighlight><br />
=== Fontmanager ===<br />
Your report used LiberationSans font. Make sure you added the search paths to the font cache. eg:<br />
<syntaxhighlight><br />
{$IFDEF UNIX}<br />
gTTFontCache.SearchPath.Add(GetUserDir + '.fonts/');<br />
gTTFontCache.SearchPath.Add('/data/devel/Wisa/fonts/Liberation/');<br />
gTTFontCache.BuildFontCache;<br />
{$ENDIF}<br />
</syntaxhighlight><br />
In windows this useable<br />
<syntaxhighlight><br />
gTTFontCache.ReadStandardFonts;<br />
gTTFontCache.BuildFontCache;<br />
</syntaxhighlight><br />
<br />
== FPC FPReport ==<br />
=== I did not find FPReport in my FPC dir ===<br />
It is located in the FPC\packages\fcl-report dir, but only in FPC trunk (=3.1.1) since rev. 36962 (20.Aug. 2017)<br />
<br />
=== What is the minimum requirement ===<br />
Info from Mailinglist - FPC 2.6.4 (and Lazarus 1.6.2), but you will need to copy some additional units from the<br />
trunk version of the FPC source repo (in particular, fpexprpars, and fcl-pdf) and the Lazarus trunk repro too<br />
<br />
== Lazarus FPReport ==<br />
=== I did not find FPReport in my Lazarus dir ===<br />
Only in Lazarus trunk since rev. 55719 (20.Aug. 2017). Look in LAZARUSDIR\components\fpreport the is a runtimepackage lclfpreport.lpk.<br />
<br />
=== My Report does not run/start in Windows ===<br />
Start the applikation from the console, so you can see if some dll's are missing. Normally freetype-6.dll and zlib1.dll are missing. For more information look into the README.txt.<br />
<br />
=== My Font is not found in windows ===<br />
You have to use the PostScriptName of the font. Eg. under windows you have the font 'Arial' - this is one of the standard font - but you have to write Memo.Font.Name := 'ArialMT' because ArialMT is the correct PostScriptname.<br />
<br />
<syntaxhighlight><br />
for i:= 0 to gTTFontCache.Count-1 do begin<br />
try<br />
Memo1.Append(TFPFontCacheItem(gTTFontCache.Items[i]).HumanFriendlyName +'...'+ TFPFontCacheItem(gTTFontCache.Items[i]).PostScriptName);<br />
except<br />
Memo1.Append('ERROR in...'+ TFPFontCacheItem(gTTFontCache.Items[i]).FileName)<br />
end;<br />
end;<br />
</syntaxhighlight></div>Zoranhttps://wiki.freepascal.org/index.php?title=Helper_types&diff=111653Helper types2017-08-24T09:05:41Z<p>Zoran: /* General */</p>
<hr />
<div>Helper types allow you to extend the scope which the compiler uses to search symbols for. This is useful if you can't extend a class because it's sealed or you can't control of which final type the class instance is (e.g. Lines/Items properties in various LCL components are presented as TStrings, but could be every TStrings descendant).<br />
This page describes helper types in Object Pascal. For the equivalent in Objective Pascal see [[FPC_PasCocoa#Category_declaration|here]].<br />
<br />
==General==<br />
<br />
This feature is in FPC since 2.6 release.<br />
<br />
==Declaration==<br />
Helper types allow you to extend a given class or record with methods that add more functionality to that type. So you can add default properties, enumerators and wrappers for existing functionality. <br />
===Syntax===<br />
Currently helper types can be used to extend classes (''class helper'') and records (''record helper'') as those are supported by Delphi as well. In theory they could be extended to support interfaces, objects or even primitive types as well (although the latter would need bigger adjustments inside the compiler).<br />
<br />
The general syntax for helper types is the following:<br />
<syntaxhighlight><br />
HelperName = class|record helper[(BaseHelper)] for ExtendedType<br />
[properties, procedures, functions, constructors]<br />
end [hint modifiers];<br />
</syntaxhighlight><br />
<br />
The sequence ''class helper'' declares a helper for a class while ''record helper'' declares a helper for a record. The inheritance ''(BaseHelper)'' is optional.<br />
<br />
''HelperName'', ''BaseHelper'' and ''ExtendedType'' have to be valid Pascal identifiers. In case of a class helper ''ExtendedType'' must be a class and ''BaseHelper'' must be another class helper that extends either the same class as ''ExtendedType'' or a parent class of it. For record helpers ''ExtendedType'' must be a record and ''BaseHelper'' must be a record helper that extend the same record type.<br />
The declarations inside a helper are very similiar to the declaration of a normal class, but you must not use fields, destructors and class constructors/destructors.<br />
<br />
''Note'':<br />
In mode ObjFPC record helpers need the modeswitch ''advancedrecords'' to be declared; in mode Delphi no further modeswitch is necessary.<br />
===Implementation===<br />
The rules when implementing a helper are a bit differently then when implementing a normal class or record. First of the type of ''Self'' is always of the type of the extended type (e.g. ''TObject'' in case of a helper declared as ''class helper for TObject'').<br />
If a method is not defined in the helper type itself or it's prepended by "inherited", it is first searched in the extended type, then in all the parent helpers of the current helper and then (for class helpers) in the parent classes of the extended class. Please note that in the last case helpers for the parent classes are available as well.<br />
<br />
===Restrictions===<br />
A helper type may not<br />
* contain (class) destructors<br />
* contain class constructors<br />
* contain (class) fields<br />
* contain abstract methods<br />
* "override" virtual methods of the extended class (they can be hidden by the helper though)<br />
* for record helpers: contain constructors (this is not a restriction of helper types themselves, but of FPC, as constructors are not yet supported inside advanced records)<br />
<br />
Methods of the extended type can be overloaded (thus they are not hidden by the helper) by using the ''overload'' keyword.<br />
<br />
Example:<br />
<syntaxhighlight><br />
{$mode objfpc}<br />
{$MODESWITCH ADVANCEDRECORDS}<br />
<br />
TObjectHelper = class helper for TObject<br />
function ToString(const aFormat: String): String; overload;<br />
end;<br />
<br />
function TObjectHelper.ToString(const aFormat: String): String;<br />
begin<br />
Result := Format(aFormat, [ToString]);<br />
end;<br />
<br />
var<br />
o: TObject;<br />
begin<br />
Writeln(o.ToString('The object''s name is %s'));<br />
end.<br />
</syntaxhighlight><br />
<br />
===Inheritance===<br />
Inheritance for helper types fullfills two purposes:<br />
* use the methods of multiple helpers at once<br />
* (class helpers only) bring methods that were added to a parent class to the topmost visiblilty (because equally named methods of helpers are hidden by subclasses)<br />
====class helper====<br />
A class helper can inherit from another class helper if it extends a subclass of the class or the same class which is extend by the parent class helper.<br />
<br />
Example:<br />
<syntaxhighlight><br />
TObjectHelper = class helper for TObject<br />
procedure SomeMethod;<br />
end;<br />
<br />
TFoo = class(TObject)<br />
end;<br />
<br />
TFooHelper = class helper(TObjectHelper) for TFoo<br />
end;<br />
</syntaxhighlight><br />
<br />
====record helper====<br />
A record helper can inherit from another record helper if it extends the same record which is extend by the parent record helper.<br />
'''Important''':<br />
Inheritance for record helpers is not enabled in mode Delphi, because there this is not allowed.<br />
<br />
Example:<br />
<syntaxhighlight><br />
TTest = record<br />
end;<br />
<br />
TTestHelper = record helper for TTest<br />
procedure SomeMethod;<br />
end;<br />
<br />
TTestHelperSub = record helper(TTestHelper) for TTest<br />
end;<br />
</syntaxhighlight><br />
<br />
===Example===<br />
A simple example for extending existing functionality is the following:<br />
The LCL's ''TCheckListBox'', which is a list with checkoxes, does not provide a property to get the count of all checked items. Using a class helper this can be added rather easily.<br />
<syntaxhighlight><br />
type<br />
TCheckListBoxHelper = class helper for TCheckListBox<br />
private<br />
function GetCheckedCount: Integer;<br />
public<br />
property CheckedCount: Integer read GetCheckedCount;<br />
end;<br />
<br />
function TCheckListBoxHelper.GetCheckedCount: Integer;<br />
var<br />
i: Integer;<br />
begin<br />
Result := 0;<br />
for i := 0 to Items.Count - 1 do<br />
if Checked[i] then<br />
Inc(Result);<br />
end;<br />
<br />
// somewhere else (TCheckListBoxHelper needs to be in scope)<br />
if MyCheckList.CheckedCount > 0 then ...<br />
</syntaxhighlight><br />
<br />
Please note that it's not trivial to implement a cached variation, as helper types can't introduce fields.<br />
<br />
==Usage==<br />
===Scope===<br />
The methods declared inside the helper are always available once it is in scope and can be called as if they'd belong to the extended type.<br />
<br />
Example:<br />
<syntaxhighlight><br />
type<br />
TObjectHelper = class helper for TObject<br />
function TheAnswer: Integer;<br />
end;<br />
<br />
function TObjectHelper.TheAnswer: Integer;<br />
begin<br />
Result := 42;<br />
end;<br />
<br />
begin<br />
o := TObject.Create;<br />
o.TheAnswer;<br />
end.<br />
</syntaxhighlight><br />
<br />
Because of Delphi compatibility only one helper type can be active for a type at the same time. This helper is always the last one that is in scope (which follows the usual rules of Pascal).<br />
<br />
Example:<br />
<syntaxhighlight><br />
type<br />
TTestRecord = record<br />
end;<br />
TTestClass = class<br />
end;<br />
<br />
TTestRecordHelper = record helper for TTestRecord<br />
procedure Test;<br />
end;<br />
<br />
TTestClassHelper1 = class helper for TTestClass<br />
procedure Test1;<br />
end;<br />
<br />
TTestClassHelper2 = class helper for TTestClass<br />
procedure Test2;<br />
end;<br />
<br />
var<br />
tr: TTestRecord;<br />
tc: TTestClass;<br />
begin<br />
tr.Test; // this compiles<br />
tc.Test1; // this won't compile<br />
tc.Test2; // this compiles<br />
end.<br />
</syntaxhighlight><br />
===Method hiding===<br />
The methods of helper types hide the methods declared in the extended type as long as they are not declared with "overload". <br />
<br />
If a class helper is in scope for a parent class then only the methods of the parent class are hidden while the methods of the subclass will stay visible.<br />
<br />
Example:<br />
<syntaxhighlight><br />
type<br />
TTest = class<br />
procedure Test;<br />
end;<br />
TTestSub = class(TTest)<br />
procedure Test;<br />
end;<br />
<br />
TTestHelper = class helper for TTest<br />
procedure Test;<br />
end;<br />
<br />
var<br />
t: TTest;<br />
ts: TTestSub;<br />
begin<br />
t.Test; // this calls "Test" of the helper<br />
ts.Test; // this calls "Test" of TTestSub<br />
end.<br />
</syntaxhighlight><br />
<br />
===Restrictions===<br />
You can not reference helper types anywhere in the code except for the following cases:<br />
* inheriting from one<br />
* using one in (Bit)SizeOf<br />
* using one in TypeInfo<br />
<br />
==Differences==<br />
===Differences between mode Delphi and ObjFPC===<br />
In mode Delphi you can use ''virtual'', ''dynamic'', ''override'' and ''message'' identifiers. As the concepts behind those identifiers (virtual methods, message dispatching) isn't applicable for helpers, those keywords are ignored in mode Delphi and not allowed in mode ObjFPC.<br />
<br />
In mode Delphi you can't inherit a record helper from another one and you can not use the "inherited" keyword (not even to call the method of the extended record).<br />
<br />
===Differences between Delphi and FPC===<br />
While the helper feature was implemented as Delphi compatible as possible there is one difference between the two:<br /><br />
In Delphi a helper is basically a class which inherits from a class ''TClassHelperBase'' (both class and record helpers) which in turn inherits from ''TInterfacedObject''. In FPC helpers are a type for themself and don't have a basetype.<br /><br />
As this difference is only visible in the RTTI (which is seldomly used for helpers) this difference was considered neglectable.<br />
<br />
Because helper are their own type in FPC they also have a custom type kind (''tkHelper'') and their own fields in TTypeData:<br />
* HelperParent: a PTypeInfo field that points to the type info of the parent helper (can be '''nil''')<br />
* ExtendedInfo: a PTypeInfo field that points to the type info of the extended type<br />
* HelperProps: contains the count of the (published) properties the helper contains<br />
* HelperUnit: contains the name of the unit the helper is defined in<br /><br />
The usual RTTI methods can be used to query for properties of the helper.<br />
<br />
''Note'':<br />
''ExtendedInfo'' and ''HelperUnit'' don't have a Delphi equivalent.<br />
<br />
Example:<br />
<syntaxhighlight><br />
type<br />
TObjectHelper = class helper for TObject<br />
end;<br />
<br />
var<br />
ti: PTypeInfo;<br />
td: PTypeInfo;<br />
begin<br />
ti := TypeInfo(TObjectHelper);<br />
td := GetTypeData(ti);<br />
{$ifdef fpc}<br />
// you must use this in FPC<br />
if ti^.Kind = tkHelper then begin<br />
if Assigned(td^.HelperParent) then<br />
Writeln(td^.HelperParent^.Name)<br />
else<br />
Writeln('no helper parent');<br />
Writeln(td^.ExtendedInfo^.Name);<br />
Writeln(td^.HelperProps);<br />
Writeln(td^.HelperUnit);<br />
end;<br />
{$else}<br />
// you must use this in Delphi<br />
if ti^.Kind = tkHelper then begin<br />
if td^.ParentInfo <> TypeInfo(TClassHelperBase) then<br />
Writeln(td^.HelperParent^.Name)<br />
else<br />
Writeln('no helper parent');<br />
Writeln(td^.PropCount);<br />
end;<br />
{$endif} <br />
end.<br />
</syntaxhighlight><br />
<br />
==Code examples==<br />
<br />
The following table lists some examples for class helpers found on the web and whether they work with the current implementation.<br />
<br />
{| class="wikitable"<br />
! URL !! State<br />
|-<br />
| [http://wiert.wordpress.com/2009/05/07/delphi-class-helper-to-add-for-%E2%80%A6-in-support-for-tcomponentcomponentscomponentcount/ Class helper to add for ... in support for TComponent.Components / ComponentCount] || ok<br />
|-<br />
| [http://delphi.about.com/od/delphitips2008/qt/tstrings-helper.htm Class helper for Delphi's TStrings: Implemented Add(Variant)] || ok <br />
|}<br />
<br />
[[Category:Data types]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=DateTimeCtrls_Package&diff=110249DateTimeCtrls Package2017-06-09T14:46:33Z<p>Zoran: /* ShowCheckBox: Boolean */</p>
<hr />
<div>== About ==<br />
The DateTimeCtrls package contains two controls:<br />
<br />
[[Image:TDateTimePicker.png]] [[#TDateTimePicker|TDateTimePicker]]<br />
and<br />
[[Image:TDBDateTimePicker.png]][[#TDBDateTimePicker|TDBDateTimePicker]]<br />
<br />
Delphi's VCL has a control named [http://docwiki.embarcadero.com/Libraries/en/Vcl.ComCtrls.TDateTimePicker TDateTimePicker], which I find very useful for editing dates. LCL, however, does not have this control.<br />
<br />
Therefore, I tried to create a cross-platform Lazarus control which would resemble VCL's TDateTimePicker as much as possible.<br />
<br />
The TDateTimePicker does not use [http://msdn.microsoft.com/en-us/library/system.windows.forms.datetimepicker.aspx native Win control]. It descends from LCL-s TCustomControl to be cross-platform. It has been tested on Windows with Win32/64 and qt widgetsets and on Linux with gtk2 and qt widgetsets.<br />
<br />
Note that the TDateTimePicker control does not descend from TEdit, so it does not have unnecessary caret. The VCL's control doesn't have caret either.<br />
<br />
== Author ==<br />
:[[User:Zoran| Zoran Vučenović]]<br />
<br />
== License ==<br />
Modified LGPL (same as the FPC RTL and the Lazarus LCL).<br />
<br />
== Getting the package ==<br />
This package is part of Lazarus distribution (since Lazarus 1.4). You can find it in <''your_lazarus_folder''>/components/datetimectrls.<br />
<br />
The package is installed on Component palette by default (if you build the IDE yourself, it is installed with "make bigide"). The components TDateTimePicker and TDBDateTimePicker are located in "Common Controls" and "Data Controls" palette pages, respectively.<br />
<br />
'''Note for trunk users''' - in Lazarus trunk 1.7 the package got divided into two packages - runtime package DateTimeCtrls and designtime pakcage DateTimeCtrlsDsgn. So, in order to get the controls on component palette, DateTimeCtrlsDsgn is now the package which should be installed in the IDE. The bigide script should be corrected before Lazarus version 1.8 is released, to automatically get the controls on component palette.<br />
[[Lazarus_1.8.0_release_notes#DateTimeCtrls|See the 1.8 release notes.]]<br />
<br />
== TDateTimePicker [[Image:TDateTimePicker.png]] ==<br />
<br />
=== Properties ===<br />
<br />
I'll explain some properties of TDateTimePicker control:<br />
<br />
==== DateTime: TDateTime (public) ====<br />
<br />
:The DateTime value displayed on the control. This property is not published in object inspector, but its value is actually the same as Date and Time properties composed in one value. This property is provided to allow setting or reading of both date and time value at once in program code. In design time, Date and Time can be set in object inspector. [[#DateTimePicker Editor|There is also component editor]] which provides easy way of setting this property in design time.<br />
<br />
==== Date: TDate ====<br />
<br />
:The date displayed on the control which the user can edit.<br />
<br />
==== Time: TTime ====<br />
<br />
:The time displayed on the control which the user can edit.<br />
<br />
==== MinDate: TDate ====<br />
<br />
:The minimal date user can enter.<br />
<br />
==== MaxDate: TDate ====<br />
<br />
:The maximal date user can enter.<br />
<br />
==== NullInputAllowed: Boolean ====<br />
<br />
:When True, the user can set the date to NullDate constant by pressing N key.<br />
<br />
==== CenturyFrom: Word ====<br />
<br />
:When user enters the year in two-digit format, then the CenturyFrom property is used to determine which century the year belongs to. The default is 1941, which means that when two digit year is entered, it falls in interval 1941 – 2040. Note that [[#MinDate: TDate|MinDate]] and [[#MaxDate: TDate|MaxDate]] properties can also have influence on the decision – for example, if the CenturyFrom is set to 1941 and MaxDate to 31. 12. 2010, if user enters year 23, it will be set to 1923, because it can’t be 2033, due to MaxDate limit.<br />
<br />
==== Kind: TDateTimeKind ====<br />
<br />
:'''type''' TDateTimeKind = (dtkDate, dtkTime, dtkDateTime);<br />
<br />
:The control displays only date, only time or both.<br />
:[[Image:DateControls03.PNG]]<br />
<br />
==== DateMode: TDTDateMode ====<br />
<br />
:'''type''' TDTDateMode = (dmComboBox, dmUpDown, dmNone)<br />
<br />
:When DateMode is set to dmComboBox, there is a button on the right side of the control. When user clicks the button, [http://lazarus-ccr.sourceforge.net/docs/lcl/calendar/tcalendar.html the calendar control] is shown, allowing the user to pick the date. When set to dmUpDown, then UpDown buttons are shown.<br />
<br />
:In my opinion the UpDown buttons aren't really useful in this control, they are provided for compatibility with Delphi's TDateTimePicker. Up and down keys can always serve for same purpose, so can mouse wheel.<br />
<br />
:In the picture the first control's DateMode is set to dmComboBox and the second control's to dmUpDown:<br />
:[[Image:DateControls01.PNG]]<br />
<br />
==== ShowCheckBox: Boolean ====<br />
<br />
:When set, there is a check box on the left side of the control.<br />
:By default, when unchecked, the display appears grayed and user interaction with the date is not possible. (The control is still enabled, though, only in sense that the check box remains enabled). This behaviour may be changed if dtpoEnabledIfUnchecked is added to [[#Options:_TDateTimePickerOptions|Options property]] -- then this check box is shown, but does nothing by itself (doesn't disable the control) and the programmer can decide how it will be used (by using OnCheckBoxChange event).<br />
:[[Image:DateControls02.PNG]]<br />
<br />
==== Checked: Boolean ====<br />
<br />
:If [[#ShowCheckBox: Boolean|ShowCheckBox]] is set to True, this property determines whether the check box is checked or not. If ShowCheckBox is False, this property has no purpose and is automatically set to True.<br />
<br />
==== DateDisplayOrder: TDateDisplayOrder ====<br />
<br />
:'''type''' TDateDisplayOrder = (ddoDMY, ddoMDY, ddoYMD, ddoTryDefault);<br />
<br />
:Defines the order for displaying day, month and year part of the date. When ddoTryDefault is set, then the controls tries to determine the order from [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/shortdateformat.html ShortDateFormat global variable].<br />
:This is similar to DateEdit's [http://lazarus-ccr.sourceforge.net/docs/lcl/editbtn/tdateedit.dateorder.html DateOrder] property.<br />
<br />
==== DateSeparator: String ====<br />
<br />
:Defines the string used to separate date, month and year date parts. Setting this property automatically sets the [[#UseDefaultSeparators: Boolean|UseDefaultSeparators]] property to False. To ensure that date and time separators are set to user's system defaults, set UseDefaultSeparators property to True.<br />
<br />
==== TimeSeparator: String ====<br />
<br />
:Defines the string used to separate hour, minute, second and millisecond time parts. Setting this property automatically sets the [[#UseDefaultSeparators: Boolean|UseDefaultSeparators]] property to False. To ensure that date and time separators are set to user's system defaults, set UseDefaultSeparators property to True.<br />
<br />
==== UseDefaultSeparators: Boolean ====<br />
<br />
:When this property is set to True, then the [[#DateSeparator: String|DateSeparator]] and [[#TimeSeparator: String|TimeSeparator]] properties will be set to [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/dateseparator.html DateSeparator] and [http://lazarus-ccr.sourceforge.net/docs/rtl/sysutils/timeseparator.html TimeSeparator] global variables, which are set to user system defaults when application initializes.<br />
<br />
==== TrailingSeparator: Boolean ====<br />
<br />
:When set to True, then the [[#DateSeparator: String|DateSeparator]] is shown once more, after the last date part. This property exists because in some languages the correct format is 31. 1. 2010. including the last point, after the year.<br />
<br />
==== LeadingZeros: Boolean ====<br />
<br />
:Determines whether the date parts are displayed with or without leading zeros (this actually affects day, month and hour parts of date and time display).<br />
<br />
==== TimeDisplay: TTimeDisplay ====<br />
<br />
:'''type''' TTimeDisplay = (tdHM, tdHMS, tdHMSMs);<br />
<br />
:If Kind is dtkTime or dtkDateTime, then TimeDisplay value of tdHM means that only hours and minutes are displayed, tdHMS adds displaying of seconds and value of tdHMSMs means that milliseconds are displayed too.<br />
<br />
==== TimeFormat: TTimeFormat ====<br />
<br />
:'''type''' TTimeFormat = (tf12, tf24);<br />
<br />
:The value of tf12 sets the display of time to 12 hours format, with AM/PM string and tf24 sets to 24 hours format.<br />
<br />
==== TextForNullDate: String ====<br />
<br />
:Text which appears when the null date is set and control does not have focus. When control is focused, the text changes to defined format, but displaying zeros, which is appropriate to user input. User can set the date to NullDate by pressing N key, provided [[#NullInputAllowed: Boolean|NullInputAllowed]] property is True.<br />
:When TextForNullDate is set to empty string, zeros/nines format is displayed even when control does not have focus. If you want empty display, this can be acchieved by setting TextForNullDate to one or more space characters.<br />
<br />
==== AutoAdvance: Boolean ====<br />
<br />
:When true, then when user is entering valid text, the selection automatically advances to next part of date/time. The default is True, because it makes user interaction easier and this behaviour is what a user should expect.<br />
<br />
==== Cascade: Boolean ====<br />
<br />
:When true, then when user is increasing or decreasing one date/time part (using up-down keys or mouse wheel), it can increase or decrease by one another date/time part. For example, when date is 31.08.2013. and user increases the day, the day becomes 1 and month increases by one and becomes 9, so the date becomes 01.09.2013. If Cascade were set to False, the month would not change and the date would become 01.08.2013.<br />
<br />
==== AutoButtonSize: Boolean ====<br />
<br />
:When true, the width of the arrow button (or up-down control, if it is shown instead) is automatically adjusted proportionally to the height.<br />
<br />
==== HideDateTimeParts: TDateTimeParts ====<br />
<br />
:'''type'''<br />
<br />
::TDateTimePart = (dtpDay, dtpMonth, dtpYear, dtpHour, dtpMinute, dtpSecond, dtpMiliSec, dtpAMPM);<br />
<br />
::TDateTimeParts = set of dtpDay..dtpMiliSec;<br />
<br />
:With HideDateTimeParts property, you can chose which date/time parts will not be shown. Most of the time you do not need to use this property and you can get the format you want by using other properties (see Kind, TimeDisplay). However, if you need more control (for example, you might want to let user edit only days, months and hours), you can additionally hide any date/time parts with this control. Keep in mind that, with this property, you cannot show any date/time part which is hidden by another property (for example, if TimeDisplay is tdHM, the second part is not shown, regardless of this property).<br />
<br />
==== CalendarWrapperClass: TCalendarControlWrapperClass (public) ====<br />
<br />
:When assigned, this property determines the type of the calendar control used for drop-down calendar. When set to nil, which is the default, the value of global variable DefaultCalendarWrapperClass is used. [[#Using some custom calendar control for drop-down|More details here]].<br />
<br />
==== ShowMonthNames: Boolean ====<br />
:When this property is set to True, month names, set in [[#MonthNames: String|MonthNames]] property, will be displayed instead of numbers.<br />
<br />
==== MonthNames: String ====<br />
:When [[#ShowMonthNames: Boolean|ShowMonthNames]] is set to True, this property determines which month names should appear.<br />
:If MonthNames is set to 'Short' or 'Long', then month names are set to [http://www.freepascal.org/docs-html/rtl/sysutils/shortmonthnames.html ShortMonthNames] or [http://www.freepascal.org/docs-html/rtl/sysutils/longmonthnames.html LongMonthNames] respectively.<br />
:To set the month names explicitly, this property should be set to string which starts with a character which will be used to separate months and then twelve names separated by this character. For example ',I,II,III,IV,V,VI,VII,VIII,IX,X,XI,XII' — roman numbers are used for months — the first character is comma, which means that comma is used to separate the months. Another valid example ';jan;feb;mar;apr;maj;jun;jul;avg;sep;okt;nov;dec'.<br />
:So, the separator should be the first character, before the first month name, and then only if there are twelve month names separated by that separator, the format is valid.<br />
:The default value of this property is 'Long'.<br />
<br />
==== Options: TDateTimePickerOptions ====<br />
<br />
* dtpoDoChangeOnSetDateTime: the OnChange handler will be called also when DateTime is programatically changed.<br />
* dtpoEnabledIfUnchecked: enable the date time picker if the checkbox is unchecked.<br />
* dtpoAutoCheck: auto-check an unchecked checkbox when DateTime is changed (makes sense only if dtpoEnabledIfUnchecked is set).<br />
* dtpoFlatButton: use flat button for calender picker.<br />
<br />
<br />
=== DateTimePicker Editor ===<br />
<br />
[[Image:datetimepickereditor.png]]<br />
<br />
:DateTimePicker Editor is a dialog which provides easy way to edit Date, Time, MinDate and MaxDate properties in design time. It is invoked when DateTimePicker control is double-clicked in form designer. It is also shown when the ellipsis (…) button shown in Date, Time, MinDate and MaxDate properties in Object inspector gets clicked.<br />
<br />
<br />
=== User interaction in runtime ===<br />
<br />
:The goal was to provide same user interaction as native DateTimePicker from Windows. The keyboard input is restricted to valid date/time values only. Up and down buttons can be used too. The contros behaves just like VCL's control.<br />
<br />
:After this behaviour is achieved it is further improved with mouse wheel interaction. Date and time parts can be edited using mouse wheel, which is very fast and comes handy.<br />
<br />
<br />
=== Using some custom calendar control for drop-down ===<br />
<br />
Instead of LCL's TCalendar, some other calendar control can be used for drop-down. This calendar control has to provide a way to determine if the coordinates (within the calendar control) are on the date or not [[#AreCoordinatesOnDate|(see AreCoordinatesOnDate function)]]. DateTimePicker needs it to decide if it should close the drop-down calendar and take its date when the calendar gets clicked (see AreCoordinatesOnDate function). The calendar only needs to be derived from TControl. The mouse interaction with calendar will work and, if it is also TWinControl's descendent, the keyboard interaction will also work.<br />
<br />
Now, let's see how to use some calendar control for drop-down control. First, we need to define the "wrapper" class, then we will have to tell the DateTimePicker control to use this wrapper instead of default one.<br />
<br />
==== Definning the wrapper class ====<br />
<br />
:In unit calendarcontrolwrapper.pas, there is an abstract class TCalendarControlWrapper. We need to derive a new class from this abstract class. There are four abstract methods which new class has to override - GetCalendarControlClass, SetDate, GetDate and AreCoordinatesOnDate.<br />
:This should be pretty simple, please see the file lclcalendarwrapper.pas, where TLCLCalendarWrapper (the default wrapper) is defined.<br />
<br />
===== GetCalendarControlClass =====<br />
<br />
:<syntaxhighlight><br />
class function GetCalendarControlClass: TControlClass; virtual abstract;<br />
</syntaxhighlight><br />
:This class function should return the class of the wrapped calendar control.<br />
<br />
===== SetDate =====<br />
<br />
:<syntaxhighlight><br />
procedure SetDate(Date: TDate); virtual abstract;<br />
</syntaxhighlight><br />
:Should be overriden to set the date in the calendar control.<br />
<br />
===== GetDate =====<br />
<br />
:<syntaxhighlight><br />
function GetDate: TDate; virtual abstract;<br />
</syntaxhighlight><br />
:Should be overriden to get the date from the calendar control.<br />
<br />
===== AreCoordinatesOnDate =====<br />
<br />
:<syntaxhighlight><br />
function AreCoordinatesOnDate(X, Y: Integer): Boolean; virtual abstract; <br />
</syntaxhighlight><br />
:This function should return True if coordinates (X, Y) are on the date in the calendar control (DateTimePicker calls this function when the calendar is clicked, to determine whether the drop-down calendar should return the date or not). The calendar control must at least provide a way to determine whether the coordinates are on the date (when this control gets clicked, we must decide if the date has just been chosen — then we should respond by closing the drop-down form and setting the date from calendar to DateTimePicker — for example in LCL's TCalendar we will respond when the calendar is clicked on date, but not when the user clicks in title area changing months or years, then we let the user keep browsing the calendar).<br />
<br />
==== Telling to the DateTimePicker to use new wrapper ====<br />
<br />
:Now, when we have the wrapper class defined, the easiest way to make all DateTimePickers use the new control is setting global variable DefaultCalendarWrapperClass (declared in unit datetimepicker.pas) and all DateTimePickers in the project will use the new control.<br />
<br />
:There is public property CalendarWrapperClass in DateTimePicker. We can set it and DateTimePicker will use it.<br />
<br />
:This pseudo code shows how a DateTimePicker decides which calendar control to use:<br />
<br />
<syntaxhighlight lang="text"><br />
if property DateTimePicker.CalendarWrapperClass is not nil<br />
the property is used<br />
else if global variable DefaultCalendarWrapperClass is not nil<br />
the global variable is used<br />
else<br />
LCL's TCalendar (implemented through TLCLCalendarWrapper class) is used<br />
</syntaxhighlight><br />
<br />
== TDBDateTimePicker [[Image:TDBDateTimePicker.png]] ==<br />
<br />
:TDBDateTimePicker is a data-aware version of TDateTimePicker, with nice way of handling null database values.<br />
<br />
=== Handling of null values ===<br />
<br />
==== Displaying null values ====<br />
<br />
:When the underlying DB field has null value, then:<br />
<br />
::If the control is not focused, then it displays the text defined in [[#TextForNullDate: String|TextForNullDate]] property. The default is "NULL".<br />
<br />
::When the control gets focus, the text changes to defined format, but displaying zeros for date parts and nines for time parts (for example "00/00/0000 99:99:99"), which is appropriate to user input.<br />
<br />
==== Setting the field value to null ====<br />
<br />
::If [[#NullInputAllowed: Boolean|NullInputAllowed]] property is True, the user can set the date and time to null, by pressing N key.<br />
<br />
[[Category:Components]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=SqlDBHowto&diff=109830SqlDBHowto2017-05-18T05:16:59Z<p>Zoran: /* How to connect to a database server? */</p>
<hr />
<div>{{SqlDBHowto}}<br />
<br />
{{Note|This page started as a translation of [[SqlDBHowto/nl]]. Initially, the Dutch text was leading, meanwhile, the two articles have been synchronized.}}<br />
<br />
{{Infobox databases}}<br />
<br />
This text is setup as a 'how-to'. I want to answer a number of questions one by one, and explain how you can use the various classes. All those questions are put one after the other and form a sort of tutorial. <br />
<br />
I will try to word it in such a way that the text can be used for Lazarus as well as FreePascal. However, the examples are for FreePascal (i.e. they are console applications.)<br />
<br />
== Where can I find official documentation? ==<br />
Please see the official documentation at [http://www.freepascal.org/docs-html/fcl/sqldb/index.html SQLDB documentation]. <br />
<br />
== How to connect to a database server? ==<br />
SqlDB doesn't connect to a database server directly but uses a client that corresponds to the used database server. SqlDB sends the commands to the client library; the client library connects to the database and and transfers the commands. This means that a client library must be installed on the computer to make a connection to a database. Under Windows a client is usually a .dll, under Linux an .so and under OS/X a .dylib.<br />
<br />
When the client library is installed properly you can connect to a database server using a TSQLConnection component. Various TSQLConnection components are available for different database servers (see [[SQLdb_Package]]):<br />
* Firebird/Interbase: [[TIBConnection]]<br />
* MS SQL Server: [[TMSSQLConnection]] (available since FPC 2.6.1)<br />
* MySQL v4.0: [[TMySQL40Connection]] <br />
* MySQL v4.1: [[TMySQL41Connection]] <br />
* MySQL v5.0: [[TMySQL50Connection]] <br />
* MySQL v5.1: [[TMySQL51Connection]] (available since FPC version 2.5.1<br />
* MySQL v5.5: [[TMySQL55Connection]] (available since Lazarus 1.0.8/FPC version 2.6.2<br />
* MySQL v5.6: [[TMySQL56Connection]] (available in Lazarus 1.2.4/FPC version 2.6.4<br />
* ODBC: [[TODBCConnection]] (see [[ODBCConn#TODBCConnection]])<br />
* Oracle: [[TOracleConnection]] (see [[Oracle]])<br />
* PostgreSQL: [[TPQConnection]] (see [[postgresql#SQLDB]])<br />
* Sqlite3: [[TSQLite3Connection]] (available since FPC version 2.2.2, see [[SQLite#Built-in_SQLDB]]) <br />
* Sybase ASE: [[TSybaseConnection]] (available since FPC 2.6.1, see [[Lazarus_Database_Overview#Lazarus_and_MSSQL.2FSybase|Lazarus and MSSQL/Sybase]])<br />
<br />
Note for MySQL - There are many differences between the client versions to the extent that the clients and connections cannot be interchanged. If a MySQL client library version 4.1 is installed, you have to use a TMySQL41Connection. This is not related to the MySQL server; using the MySQL 4.1 client library you can probably connect to a MySQL 5.0 server (see MySQL documentation regarding what combinations are supported).<br />
<br />
Although details differ for the various databases, in general you need to set four properties to connect to a database server: <br />
* the server name or IP address<br />
* the name of the database<br />
* the username <br />
* the password<br />
When these properties are set, you can create a connection with the 'open' method. If the connection fails, a EDatabaseError exception is thrown. Use the property 'connected' to test if a connection has been made with the database server. Use the 'close' method to end the connection with the server.<br />
<br />
<syntaxhighlight><br />
Program ConnectDB;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
uses<br />
IBConnection;<br />
<br />
function CreateConnection: TIBConnection;<br />
begin<br />
result := TIBConnection.Create(nil);<br />
result.Hostname := 'localhost';<br />
result.DatabaseName := '/opt/firebird/examples/employee.fdb';<br />
result.UserName := 'sysdba';<br />
result.Password := 'masterkey';<br />
end;<br />
<br />
var <br />
AConnection : TIBConnection;<br />
<br />
begin<br />
AConnection := CreateConnection;<br />
AConnection.Open;<br />
if Aconnection.Connected then<br />
writeln('Successful connect!')<br />
else<br />
writeln('This is not possible, because if the connection failed, ' +<br />
'an exception should be raised, so this code would not ' +<br />
'be executed');<br />
AConnection.Close;<br />
AConnection.Free;<br />
end.<br />
</syntaxhighlight><br />
<br />
If an exception is thrown, read the error message carefully. It may be that the database server is not running, the user name or password are incorrect or the database name or IP address are typed incorrectly. If the error message states that the client library cannot be found, then check if the client is installed correctly. Often the error message states literally the name of the file looked for.<br />
<br />
== How to execute direct queries/make a table? ==<br />
<br />
SqlDB - the name says it all - only works with database server that make use of SQL. SQL stands for 'Structured Query Language' SQL is a language developed to allow working with relational databases. Virtually every database system has its own dialect, but a large number of SQL statements are the same for all database systems.<br />
<br />
In FPC, there is a difference between:<br />
* SQL statements that return information (a dataset). For this, you have to use the TSQLQuery component; see [[#How to read data from a table?]]. <br />
* statements that do not return information but do something else, e.g. update data. For this, you may also use the 'ExecuteDirect' method of a TSQLConnection. (You can also use this if you get a dataset back but are not interested in the results, e.g. in a selectable stored procedure).<br />
<br />
Most database system execute SQL statements within a transaction. If you want changes made within a transaction available in other transactions, or have those changes available even after closing the transaction(!), then you have to 'commit' the transaction. <br />
<br />
To support transactions Sqldb contains the TSQLTransaction component. A SQL statement that is executed by Sqldb must always be executed within a transaction, even if the database system does not support transactions. Also, there are database systems that do support transaction for which TSQLConnection does not (yet) support transaction. Even then, you must use the TSQLTransaction component.<br />
<br />
To use <tt>TSQLConnection.ExecuteDirect</tt> to execute a SQL statement you must specify which 'Transaction' must be used. In turn, to use TSQLTransaction you must specify which TSQLConnection component must be used.<br />
<br />
The following example creates a table 'TBLNAMES' with fields 'NAME' and 'ID' and inserts two records. The used SQL statements are not explained. For more information about the SQL statements, their use and syntax, please refer to the database system documentation. The procedure 'CreateConnection' is defined in the code example in [[#How to connect to a database server?]] above.<br />
<br />
<syntaxhighlight>program CreateTable;<br />
<br />
function CreateTransaction(pDB: TIBConnection): TSQLTransaction;<br />
begin<br />
result := TSQLTransaction.Create(pDB);<br />
result.Database := pDB;<br />
end;<br />
<br />
var <br />
AConnection : TSQLConnection;<br />
ATransaction : TSQLTransaction;<br />
<br />
begin<br />
AConnection := CreateConnection;<br />
ATransaction := CreateTransaction(AConnection);<br />
AConnection.Transaction := ATransaction;<br />
AConnection.Open;<br />
ATransaction.StartTransaction;<br />
AConnection.ExecuteDirect('create table TBLNAMES (ID integer, NAME varchar(40));'); <br />
<br />
// Some database-server types need a commit before you can use a newly created table. (Firebird)<br />
// With .Commit you also close the transaction<br />
ATransaction.Commit; <br />
<br />
ATransaction.StartTransaction;<br />
AConnection.ExecuteDirect('insert into TBLNAMES (ID,NAME) values (1,'Name1');'); <br />
AConnection.ExecuteDirect('insert into TBLNAMES (ID,NAME) values (2,'Name2');'); <br />
ATransaction.Commit; <br />
AConnection.Close;<br />
AConnection.Free;<br />
ATransaction.Free;<br />
end.</syntaxhighlight><br />
<br />
== How to read data from a table? ==<br />
Use the TSQLQuery component to read data from a table. A TSQLQuery component must be connected to a TSQLConnection component and a TSQLTransaction component to do its work. Setting the TSQLConnection and TSQLTransaction is discussed in [[#How to connect to a database server? ]] and [[#How to execute direct queries/make a table?]]. <br />
<br />
When the TSQLConnection, TSQLTransaction and TSQLQuery are connected, then TSQLQuery needs to be further configured to work. TSQLQuery has a 'SQL' property containing a TStrings object. The 'SQL' property contains a SQL statement that must be executed. If all data from a table <tt>tablename</tt> must be read, then set the 'SQL' property to:<br />
<syntaxhighlight lang="sql">'SELECT * FROM tablename;'</syntaxhighlight>.<br />
<br />
Use 'open' to read the table from the server and put the data in the TSQLQuery dataset. The data can be accessed through TSQLQuery until the query is closed using 'close'.<br />
<br />
TSQLQuery is a subclass of TDataset. TDataset has a 'Fields' collection that contains all columns of the table. The TDataset also keeps track of the current record. Use 'First', 'Next', 'Prior' and 'Last' to change the current record. 'Bof' returns 'True' if the first record is reached, and 'Eof' returns 'True' if the last record is reached. To read the value of a field in the current record, first find the right 'TField' object and then use 'AsString', 'AsInteger', etc.<br />
<br />
=== Example: reading data from a table ===<br />
Below is an example that displays all values of the table as it was made in [[#How to execute direct queries/make a table?]] above.<br />
<br />
<syntaxhighlight>Program ShowData;<br />
<br />
function CreateQuery(pConnection: TIBConnection; pTransaction: TSQLTransaction): TSQLQuery;<br />
begin<br />
result := TSQLQuery.Create(nil);<br />
result.Database := pConnection;<br />
result.Transaction := pTransaction<br />
end;<br />
<br />
var <br />
AConnection : TSQLConnection;<br />
ATransaction : TSQLTransaction;<br />
Query : TSQLQuery;<br />
<br />
begin<br />
AConnection := CreateConnection;<br />
ATransaction := CreateTransaction(AConnection);<br />
Query := CreateQuery(AConnection, ATransaction);<br />
Query.SQL.Text := 'select * from tblNames';<br />
AConnection.Open;<br />
Query.Open;<br />
while not Query.Eof do<br />
begin<br />
Writeln('ID: ', Query.FieldByName('ID').AsInteger, 'Name: ' +<br />
Query.FieldByName('Name').AsString);<br />
Query.Next;<br />
end;<br />
Query.Close;<br />
AConnection.Close;<br />
Query.Free;<br />
ATransaction.Free;<br />
AConnection.Free;<br />
end.</syntaxhighlight><br />
<br />
(The code above of course is not quite finished, it misses 'try...finally' blocks. However, the above code intends to show the database code and thus the finishing touches are left out.)<br />
Please note that 'TSQLTransaction.StartTransaction' is not used. This is not necessary. When TSQLQuery is opened, the SQL statement is executed and if no transaction is available then a transaction is automatically started. The programmer does not need to start the transaction explicitly.<br />
The same applies for the connection maintained by TSQLConnection. The connection is opened as needed, the line 'Aconnection.Open' is not really required.<br />
If a TSQLTransaction is destroyed, an automatic 'rollback' will be executed. '''Possible changes to data contained in the transaction will be lost.'''<br />
<br />
=== Why does TSQLQuery.RecordCount always return 10? ===<br />
To count the records in a dataset, use '.RecordCount'. However, notice that '.RecordCount' shows the number of records that is already loaded from the server. For performance reasons, SqlDB does not read all records when opening TSQLQuery by default, only the first 10. Only when the eleventh record is accessed will the next set of 10 records be loaded, etc. Using '.Last', all records will be loaded. <br />
<br />
When you want to know the real number of records on the server you can first call '.Last' and then call '.RecordCount'. <br />
<br />
An alternative is available. The number of records returned by the server is set by the '.PacketRecords' property. The default value is 10; if you make it -1 then all records will be loaded at once.<br />
<br />
In current stable FPC, '.RecordCount' does not take filters into account, i.e. it shows the unfiltered total.<br />
<br />
If you need the exact number of records, it often is a better idea to directly query the number of records in a query using another SQL query, but you would have to do that in the same transaction, as other transactions may have changed the number of records in the meanwhile.<br />
<br />
=== Lazarus ===<br />
Lazarus has various components to show data from a TDataset on a form. Instead of a While-loop and Writeln statements as used above, you can use the components to show the data in a table. Place the right TSQLConnection, TSQLTransaction and TSQLQuery components on a form, then connect them and set them properly. In addition you will need a TDatasource; set the 'TDatasource.Dataset' property to the TSQLQuery component you used. ('''Note''' do not set the 'TSQLQuery.Datasource' property to the TDatasource compnent you used. The 'TSQLQuery.Datasource' property is used only in master-detail tables - see [[MasterDetail]]) Subsequently you may put a TDBGrid onto the form and set the 'Datasource' property of the grid to the TDatasource component you added before.<br />
<br />
To see if it all works, set the 'Connected' property of the TSQLConnection to 'True' in the Lazarus IDE. The IDE will try to connect to the database server immediately. If this works you can set the 'TSQLQuery.Active' property to 'True'. If everything is right, you will see - within the IDE - all data from the table immediately on the screen.<br />
<br />
== How to change data in a table? ==<br />
To change the data in a record, the [[TDataset]] (from which TSQLQuery is derived) must be set to edit mode. To enter edit mode call the '.Edit', '.Insert' or '.Append' methods. Use the '.Edit' method to change the current record. Use '.Insert' to insert a new record before the current record. Use '.Append' to insert a new record at the end of the table. In edit mode you can change field values through the 'Fields' property. Use 'Post' to validate the new data, if the data is valid then the edit mode is left. If you move to another record - for example by using '.Next' - and the dataset is in edit mode, then first '.Post' is called. Use '.Cancel' to discard all changes you made since the last '.Post' call and leave the edit mode.<br />
<br />
<syntaxhighlight>Query.Edit;<br />
Query.FieldByName('NAME').AsString := 'Edited name';<br />
Query.Post;</syntaxhighlight><br />
<br />
The above is not the complete story yet. TSQLQuery is derived from TBufDataset which makes use of buffered updates. Buffered update means that after you called 'Post' the changes in the dataset are visible immediately, but they are not sent to the database server. What does happen is that the changes are maintained in a change log. When the '.ApplyUpdates' method is called, then all changes in the change log are sent to the database. Only then will database server know of the changes. The changes are sent to the server within a transaction of TSQLTransaction. Make sure to properly set the transaction before 'ApplyUpdates'. After applying the updates, a commit must be executed to save the changes on the database server.<br />
<br />
The below is an example of changing the data in a table, sending the changes to the server and comitting the transaction.<br />
<br />
<syntaxhighlight>Program EditData;<br />
<br />
var <br />
AConnection : TSQLConnection;<br />
ATransaction : TSQLTransaction;<br />
Query : TSQLQuery;<br />
<br />
begin<br />
AConnection := CreateConnection;<br />
ATransaction := CreateTransaction(AConnection);<br />
AConnection.Transaction := ATransaction;<br />
Query := CreateQuery(AConnection, ATransaction);<br />
Query.SQL.Text := 'select * from tblNames';<br />
Query.Open;<br />
Query.Edit;<br />
Query.FieldByName('NAME').AsString := 'Edited name';<br />
Query.Post;<br />
Query.UpdateMode := upWhereAll;<br />
Query.ApplyUpdates;<br />
ATransaction.Commit;<br />
Query.Free;<br />
ATransaction.Free;<br />
AConnection.Free;<br />
end.</syntaxhighlight><br />
<br />
For a discussion of 'UpdateMode' continue reading.<br />
<br />
== How does SqlDB send the changes to the database server? ==<br />
In the code example in [[#How to change data in a table?]], you will find the line<br />
<syntaxhighlight>Query.UpdateMode := upWhereAll;</syntaxhighlight><br />
without explanation of what it does. The best way to find out what that line does is to leave it out. If you leave out the statement and the followed this howto precisely, then you will receive the following error message:<br />
No update query specified and failed to generate one. (No fields for inclusion in where statement found)<br />
To understand what went wrong, you must understand how changes are sent to the database server. The only way to get data in a SQL server is by executing SQL queries. SQL has three types of queries for three different ways of manupulating a record. To create a new record, change or delete a record insert, update and delete statements are executed respectively. An update statement may be as follows:<br />
<syntaxhighlight lang="sql">update TBLNAMES set NAME='Edited name' where ID=1;</syntaxhighlight><br />
To send a change to the database server, Sqldb must assemble an update query. To assemble the query, three things are needed:<br />
; The name of the table : The table name is retrieved from parsing the select query, although this doesn't always work. <br />
; <tt>UPDATE</tt> or <tt>INSERT</tt> clause : These contain the fields that must be changed.<br />
; <tt>WHERE</tt> clause : This contains the fields that determine which records should be changed.<br />
<br />
Every field (each ''TField'' in ''Fields'') has a ProviderFlags property. Only fields with '''pfInUpdate''' in ''ProviderFlags'' will be used in the update or insert cluase of a query. By default all fields have ''pfInUpdate'' set in their ''ProviderFlags'' property.<br />
<br />
Which fields are used in the <tt>WHERE</tt> clause depends on the ''UpdateMode'' property of the query and the ''ProviderFlags'' property of the fields. Fields with ''pfInkey'' in their ''ProviderFlags'' are always used in the <tt>WHERE</tt> clause. A field will have the ''pfInKey'' flag set automatically if the field is part of the primary key of the table and 'TSQLQuery.UsePrimaryKeyAsKey' returns 'True'.<br />
<br />
The default value for ''UpdateMode'' of the query is ''upWhereKeyOnly''. In this update mode only fields with ''pfInkey'' in their ''ProviderFlags'' property are used in the <tt>WHERE</tt> clause. If none of the fields have their ''pfInKey'' flag set, then no fields are available for the <tt>WHERE</tt> clause and the error message from the beginning of this section will be returned. You can solve the issue by:<br />
* Adding a primary key to the table and set ''TSQLQuery.UsePrimaryKeyAsKey'' to 'True', or<br />
* Setting the ''pfInkey'' flag for one or more fields in code.<br />
<br />
The '''UpdateMode''' property knows two more possible values. 'upWhereAll' can be used to add all fields with the 'pfInWhere' flag set to the <tt>WHERE</tt> clause. By default all fields have this flag set. 'upWhereChanged' can be used to add only those fields that have the 'pfInWhere' flag set '''and''' that are changed in the current record.<br />
<br />
== How to execute a query using TSQLQuery? ==<br />
Next to statements that return a dataset (see [[#How to read data from a table?]]) SQL has statements that do not return data. For example <tt>INSERT</tt>, <tt>UPDATE</tt> and <tt>DELETE</tt> statements do not return data. These statements can be executed using ''[[#How to execute direct queries/make a table?|TSQLConnection.ExecuteDirect]]'', but TSQLQuery can also be used. If you do not expect return data use ''TSQLQuery.ExecSQL'' instead of ''TSQLQuery.Open''. As mentioned earlier, use ''TSQLQuery.Open'' to open the dataset returned by the SQL statement. <br />
<br />
The following procedure creates a table and inserts two records using TSQLQuery.<br />
<br />
<syntaxhighlight>procedure CreateTable;<br />
<br />
var <br />
Query : TSQLQuery;<br />
<br />
begin<br />
Query := CreateQuery(AConnection, ATransaction);<br />
Query.SQL.Text := 'create table TBLNAMES (ID integer, NAME varchar(40));';<br />
Query.ExecSQL;<br />
<br />
Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (1,''Name1'');';<br />
Query.ExecSQL;<br />
<br />
Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (2,''Name2'');';<br />
Query.ExecSQL;<br />
<br />
Query.Close;<br />
Query.Free;<br />
end;</syntaxhighlight><br />
<br />
== How to use parameters in a query? ==<br />
In the code example of [[#How to execute a query using TSQLQuery?]] the same query is used twice, only the values to be inserted differ. A better way to do this is by using parameters in the query. <br />
<br />
The syntax of parameters in queries is different per database system, but the differences are handled by TSQLQuery. Replace the values in the query with a colon followed by the name of the parameter you want to use. For example:<br />
<syntaxhighlight>Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (:ID,:NAME);';</syntaxhighlight><br />
<br />
This query will create two parameters: 'ID' and 'NAME'.<br />
To determine the parameters, the query is parsed at the moment the text of ''TSQLQuery.SQL'' is assigned or changed. All existing parameters will be removed and the new parameters will be added to the 'TSQLQuery.Params' property. Assigning a value to a parameter is similar to assigning a value to a field in the dataset:<br />
<syntaxhighlight>Query.Params.ParamByName('Name').AsString := 'Name1';</syntaxhighlight><br />
<br />
You can't tell from the query what kind of data must be stored in the parameter. The data type of the parameter is determined at the moment a value is first assigned to the parameter. By assigning a value using '.AsString', the parameter is assigned the data type 'ftString'. You can determine the data type directly by setting the 'DataType' property. If an incorrect datatype is assigned to the parameter, then problems will occur during opening or executing the query.<br />
See [[Database field type]] for more information on data types.<br />
<br />
=== Select query ===<br />
An example of a select query with parameters would be to change something like this:<br />
<syntaxhighlight><br />
Query.SQL.Text := 'select ID,NAME from TBLNAMES where NAME = '''+Edit1.Text+''' ORDER BY NAME ';<br />
</syntaxhighlight><br />
to something like this:<br />
<syntaxhighlight><br />
Query.SQL.Text := 'select ID,NAME from TBLNAMES where NAME = :NAMEPARAM ORDER BY NAME ';<br />
Query.Params.ParamByName('NAMEPARAM').AsString := Edit1.Text;<br />
</syntaxhighlight><br />
<br />
=== Example ===<br />
The following example creates the same table as the previous example, but now parameters are used:<br />
<br />
<syntaxhighlight><br />
procedure CreateTableUsingParameters;<br />
<br />
var <br />
Query : TSQLQuery;<br />
<br />
begin<br />
Query := CreateQuery(AConnection, ATransaction);<br />
Query.SQL.Text := 'create table TBLNAMES (ID integer, NAME varchar(40));';<br />
Query.ExecSQL;<br />
<br />
Query.SQL.Text := 'insert into TBLNAMES (ID,NAME) values (:ID,:NAME);';<br />
Query.Prepare;<br />
<br />
Query.Params.ParamByName('ID').AsInteger := 1;<br />
Query.Params.ParamByName('NAME').AsString := 'Name1';<br />
Query.ExecSQL;<br />
<br />
Query.Params.ParamByName('ID').AsInteger := 2;<br />
Query.Params.ParamByName('NAME').AsString := 'Name2';<br />
Query.ExecSQL;<br />
<br />
//Query.UnPrepare; // no need to call this; should be called by Query.Close<br />
Query.Close;<br />
Query.Free;<br />
end;<br />
</syntaxhighlight><br />
<br />
Notice that this example requires more code than the example without the parameters. Then what is the use of using parameters? <br />
<br />
Speed is one of the reasons. The example with parameters is faster, because the database server parses the query only once (in the .Prepare statement or at first run). <br />
<br />
Another reason to use prepared statements is prevention of [http://en.wikipedia.org/wiki/SQL_injection SQL-injection] (see also [[Secure programming]]. <br />
<br />
Finally, in some cases it just simplifies coding.<br />
<br />
== Troubleshooting: TSQLConnection logging ==<br />
You can let a TSQLConnection log what it is doing. This can be handy to see what your Lazarus program sends to the database exactly, to debug the database components themselves and perhaps to optimize your queries.<br />
NB: if you use prepared statements/parametrized queries (see section above), the parameters are often sent in binary by the TSQLConnection descendent (e.g. TIBConnection), so you can't just copy/paste the logged SQL into a database query tool.<br />
Regardless, connection logging can give a lot of insight in what your program is doing.<br />
<br />
Alternatives are: <br />
# you can use the debugger to step through the database code if you have built FPC (and Lazarus) with debugging enabled. <br />
# if you use ODBC drivers (at least on Windows) you could enable tracelog output in the ODBC control panel.<br />
# many databases allow you to monitor all statements sent to it from a certain IP address/connection.<br />
<br />
<br />
If you use TSQLConnection logging, two things are required:<br />
# indicate which event types your TSQLConnection should log<br />
# point TSQLConnection at a function that receives the events and processes them (logs them to file, prints them to screen, etc.).<br />
That function must be of type TDBLogNotifyEvent (see sqldb.pp), so it needs this signature:<br />
<syntaxhighlight><br />
TDBLogNotifyEvent = Procedure (Sender : TSQLConnection; EventType : TDBEventType; Const Msg : String) of object;<br />
</syntaxhighlight><br />
<br />
=== FPC (or: the manual way) ===<br />
A code snippet can illustrate this:<br />
<syntaxhighlight><br />
uses<br />
...<br />
TSQLConnection, //or a child object like TIBConnection, TMSSQLConnection<br />
...<br />
var<br />
type <br />
TMyApplication = class(TCustomApplication); //this is our application that uses the connection<br />
...<br />
private<br />
// This example stores the logged events in this stringlist:<br />
FConnectionLog: TStringList;<br />
...<br />
protected<br />
// This procedure will receive the events that are logged by the connection:<br />
procedure GetLogEvent(Sender: TSQLConnection; EventType: TDBEventType; Const Msg : String);<br />
...<br />
procedure TMyApplication.GetLogEvent(Sender: TSQLConnection;<br />
EventType: TDBEventType; const Msg: String);<br />
// The procedure is called by TSQLConnection and saves the received log messages<br />
// in the FConnectionLog stringlist<br />
var<br />
Source: string;<br />
begin<br />
// Nicely right aligned...<br />
case EventType of<br />
detCustom: Source:='Custom: ';<br />
detPrepare: Source:='Prepare: ';<br />
detExecute: Source:='Execute: ';<br />
detFetch: Source:='Fetch: ';<br />
detCommit: Source:='Commit: ';<br />
detRollBack: Source:='Rollback:';<br />
else Source:='Unknown event. Please fix program code.';<br />
end;<br />
FConnectionLog.Add(Source + ' ' + Msg);<br />
end;<br />
<br />
...<br />
// We do need to tell our TSQLConnection what to log:<br />
FConnection.LogEvents:=LogAllEvents; //= [detCustom, detPrepare, detExecute, detFetch, detCommit, detRollBack]<br />
// ... and to which procedure the connection should send the events:<br />
FConnection.OnLog:=@Self.GetLogEvent;<br />
...<br />
// now we can use the connection and the FConnectionLog stringlist will fill with log messages.<br />
</syntaxhighlight><br />
<br />
You can also use TSQLConnection's GlobalDBLogHook instead to log everything from multiple connections.<br />
<br />
=== Lazarus (or: the quick way) ===<br />
Finally, the description above is the FPC way of doing things as indicated in the introduction; if using Lazarus, a quicker way is to assign an event handler to the TSQLConnection's OnLog event.<br />
<br />
== See also ==<br />
* [[Working With TSQLQuery]]<br />
<br/></div>Zoranhttps://wiki.freepascal.org/index.php?title=Bit_manipulation&diff=109812Bit manipulation2017-05-17T13:19:45Z<p>Zoran: corrected links</p>
<hr />
<div>{{Bit manipulation}}<br />
<br><br><br />
<br />
==Masked operations==<br />
<br />
This is a basic low level approach to handle bit manipulation. Main advantage is that operations can be performed with groups of bits at once. But the user has to deal with all operations by himself. Another problem is max range of used values in function parameters. There must be separate implemented functions for each ordinal type for best performance (as opposed to C templates using preprocessor).<br />
<br />
<syntaxhighlight><br />
procedure ClearBit(var Value: QWord; Index: Byte);<br />
begin<br />
Value := Value and ((QWord(1) shl Index) xor High(QWord));<br />
end;<br />
<br />
procedure SetBit(var Value: QWord; Index: Byte);<br />
begin<br />
Value:= Value or (QWord(1) shl Index);<br />
end;<br />
<br />
procedure PutBit(var Value: QWord; Index: Byte; State: Boolean); <br />
begin<br />
Value := (Value and ((QWord(1) shl Index) xor High(QWord))) or (QWord(State) shl Index);<br />
end;<br />
<br />
function GetBit(Value: QWord; Index: Byte): Boolean;<br />
begin<br />
Result := ((Value shr Index) and 1) = 1;<br />
end;<br />
</syntaxhighlight><br />
<br />
==Bitpacked record==<br />
<br />
FPC has useful extension which allow not only byte packing but also bit packing of records. This allow not only to define bit structures using Boolean type or subrange type 0..1 but also n-state or n-bits fields in record, e.g. subrange type 0..3 for 2 bits. This conjunction with record case construction combined structure can be defined which allow to access memory as byte or as individual bits.<br />
<br />
<syntaxhighlight><br />
TByteBits = bitpacked record<br />
Bit0, Bit1, Bit2, Bit3, Bit4, Bit5, Bit6, Bit7: Boolean;<br />
end;<br />
<br />
TByteEx = packed record<br />
case Integer of<br />
0: (ByteAccess: Byte); <br />
1: (BitAccess: TByteBits);<br />
end;<br />
<br />
TSomeBitLevelStructure = bitpacked record<br />
OneBit: 0..1;<br />
TwoBits: 0..3;<br />
FourBits: 0..15;<br />
EightBits: 0..255<br />
end;<br />
</syntaxhighlight><br />
<br />
Bitpacking can be controlled using compiler directive [http://www.freepascal.org/docs-html/prog/progsu6.html $BITPACKING]<br />
<br />
==Set==<br />
<br />
Because sets are basically an array of all states which is of boolean type, then set can be also used as bit array. But it requires use of [http://www.freepascal.org/docs-html/prog/progsu61.html $PACKSET] and [http://www.freepascal.org/docs-html/prog/progsu59.html $PACKENUM] compiler directives to change set size. It has also some limitations on max size of set.<br />
<br />
<syntaxhighlight><br />
{$packset 1}<br />
{$packenum 1}<br />
<br />
type<br />
TByteBits = set of (Bit0, Bit1, Bit2, Bit3, Bit4, Bit5, Bit6, Bit7);<br />
</syntaxhighlight><br />
<br />
==TBits==<br />
<br />
This class is part of FPC RTL library contained in Classes unit and has a similar use as in Delphi. It provides only some basic methods for bit manipulation.<br />
<br />
<syntaxhighlight><br />
TBits = class(TObject)<br />
public<br />
constructor Create(TheSize : longint = 0); virtual;<br />
destructor Destroy; override;<br />
function GetFSize : longint;<br />
procedure SetOn(Bit : longint);<br />
procedure Clear(Bit : longint);<br />
procedure Clearall;<br />
procedure AndBits(BitSet : TBits);<br />
procedure OrBits(BitSet : TBits);<br />
procedure XorBits(BitSet : TBits);<br />
procedure NotBits(BitSet : TBits);<br />
function Get(Bit : longint) : boolean;<br />
procedure Grow(NBit : longint);<br />
function Equals(Obj : TObject): Boolean; override; overload;<br />
function Equals(BitSet : TBits) : Boolean; overload;<br />
procedure SetIndex(Index : longint);<br />
function FindFirstBit(State : boolean) : longint;<br />
function FindNextBit : longint;<br />
function FindPrevBit : longint;<br />
<br />
{ functions and properties to match TBits class }<br />
function OpenBit: longint;<br />
property Bits[Bit: longint]: Boolean read get write SetBit; default;<br />
property Size: longint read FBSize write setSize;<br />
end;<br />
</syntaxhighlight><br />
<br />
==Record property and value index==<br />
<br />
Another interesting implementation of bit aligned structure can be used with capabilities of advanced records (FPC 2.6.0+). For all properties you have to use setter and getter which can handle general bit manipulations and set index which is passed to these methods. For more information refer to [http://www.freepascal.org/docs-html/ref/refsu31.html Indexed properties]. Because index is only one, it has to be divided to two parameters to describe location and size on bit value. In case of the following example, the offset can be 0..255 and size of value 0..255 bits. Another problem is that you have to ensure that defined structure components will not overlay.<br />
<br />
<syntaxhighlight><br />
{$mode delphi}<br />
<br />
TSomeBitStructure = record<br />
private<br />
RawData: Word;<br />
function GetBits(const AIndex: Integer): Integer; inline;<br />
procedure SetBits(const AIndex: Integer; const AValue: Integer); inline;<br />
public<br />
// High byte of index offset, low byte of index is bit count<br />
property OneBit: Integer index $0001 read GetBits write SetBits;<br />
property TwoBits: Integer index $0102 read GetBits write SetBits;<br />
property FourBits: Integer index $0304 read GetBits write SetBits;<br />
property EightBits: Integer index $0708 read GetBits write SetBits;<br />
end;<br />
<br />
{$OPTIMIZATION ON}<br />
{$OVERFLOWCHECKS OFF}<br />
function TSomeBitStructure.GetBits(const AIndex: Integer): Integer;<br />
var<br />
Offset: Integer;<br />
BitCount: Integer;<br />
Mask: Integer;<br />
begin<br />
BitCount := AIndex and $FF;<br />
Offset := AIndex shr 8;<br />
Mask := ((1 shl BitCount) - 1);<br />
Result := (RawData shr Offset) and Mask;<br />
end;<br />
<br />
procedure TSomeBitStructure.SetBits(const AIndex: Integer; const AValue: Integer);<br />
var<br />
Offset: Integer;<br />
BitCount: Integer;<br />
Mask: Integer;<br />
begin<br />
BitCount := AIndex and $FF;<br />
Offset := AIndex shr 8;<br />
Mask := ((1 shl BitCount) - 1);<br />
Assert(aValue <= Mask);<br />
RawData := (RawData and (not (Mask shl Offset))) or (AValue shl Offset);<br />
end;<br />
</syntaxhighlight><br />
<br />
[[Category:Tutorials]]<br />
[[Category:FPC]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=Multiplatform_Programming_Guide&diff=109806Multiplatform Programming Guide2017-05-17T11:00:14Z<p>Zoran: /* Detecting bitness of external library before loading it */</p>
<hr />
<div>{{Multiplatform Programming Guide}}<br />
<br />
Most [[LCL]] applications work in a cross-platform way without any extra effort.<br />
<br />
This is a tutorial on writing cross-platform applications with Lazarus and Free Pascal. It will cover the necessary precautions to aid in creating a cross-platform ready program that is ready to [[Deploying Your Application|deploy]].<br />
<br />
__TOC__<br />
<br />
== Introduction to Multiplatform (Cross-platform) Programming ==<br />
<br />
=== How many platforms do you need? ===<br />
<br />
To answer this question, you should first determine who your potential users are and how your program will be used. This question depends on where you are deploying your application.<br />
<br />
If you are developing generic desktop software in 2014, Microsoft Windows may be the most important platform. Note that Mac OS X and/or Linux are making gaining in popularity, and may be a significant target for your application.<br />
<br />
The popularity of the various desktop operating systems differs by country, by the type of software used, and with the target audience; there's no general rule. For example, Mac OS X is quite popular in North America and western Europe, while in South America Macs are mostly restricted to video and sound work.<br />
<br />
On many contract projects, only one platform is relevant. Free Pascal and Lazarus are quite capable of writing software targeted at a specific platform. You can, for example, access the full Windows API to write a well integrated Windows program.<br />
<br />
If you're developing software that will run on a Web server, a Unix platform in one of its various flavors is commonly used. In this case, perhaps only Linux, Solaris, *BSD and other Unixes make sense as your target platforms, although you may want to add support for Windows for completeness.<br />
<br />
Once you've addressed any cross-platform issues in your design, you can largely ignore the other platforms, much as you would when developing for a single platform. However, at some point you'll need to test deploying and running your program on the other platforms. For that, it will be helpful to have unrestricted access to machines running the target operating systems. If you don't want multiple physical computers, investigate dual-booting or VM solutions like VMware or Parallels.<br />
<br />
== Cross-platform Programming ==<br />
<br />
=== Working with files and folders ===<br />
<br />
When working with files and folders, this is important to use non-platform specific path delimiters and [[End_of_Line|line ending]] sequences. Here is a list of declared [[Constant|constants]] in Lazarus to be used when working with files and folders.<br />
<br />
* '''PathSep''', '''PathSeparator''': path separator when adding many paths together (';', ...)<br />
* '''PathDelim''', '''DirectorySeparator''': directory separator for each platform ('/', '\', ...)<br />
* '''LineEnding''': proper line ending character sequence (#13#10 - CRLF, #10 - [[Line_feed|LF]], ...)<br />
<br />
Another important thing to be noted is the case sensitiveness of the file system.<br />
On Windows filenames are usually not case sensitive, while they usually are on Linux and BSD platforms. But if a EXT2, EXT3, etc file system is mounted on Windows, it would be case-sensitive. Respectively a FAT file system mounted on Linux should not be case sensitive.<br />
<br />
It shall be paid special attention, that NTFS is non-case sensitive when used in Windows, but it is case sensitive when mounted by POSIX OSes. This could cause '''various problems, including loss of files''' if files with same filenames in different cases exist on a NTFS partition, mounted in Windows. Using custom functions for checking and preventing creation of several files with the same names on NTFS should be considered by the developers.<br />
<br />
Mac OS X use case insensitive filenames by default. This can be the cause of annoying bugs, so any portable application should use consistently filenames.<br />
<br />
The RTL file functions use the system encoding for file names. Under Windows this is one of the windows code pages, while Linux, BSD and Mac OS X usually use UTF-8. The unit '''FileUtil''' of the LCL provides file functions which takes UTF-8 strings like the rest of the LCL.<br />
<br />
<syntaxhighlight>// AnsiToUTF8 and UTF8ToAnsi need a widestring manager under Linux, BSD, MacOSX<br />
// but normally these OS use UTF-8 as system encoding so the widestringmanager<br />
// is not needed.<br />
function NeedRTLAnsi: boolean;// true if system encoding is not UTF-8<br />
procedure SetNeedRTLAnsi(NewValue: boolean);<br />
function UTF8ToSys(const s: string): string;// as UTF8ToAnsi but more independent of widestringmanager<br />
function SysToUTF8(const s: string): string;// as AnsiToUTF8 but more independent of widestringmanager<br />
function UTF8ToConsole(const s: string): string;// converts UTF8 string to console encoding (used by Write, WriteLn)<br />
<br />
// file operations<br />
function FileExistsUTF8(const Filename: string): boolean;<br />
function FileAgeUTF8(const FileName: string): Longint;<br />
function DirectoryExistsUTF8(const Directory: string): Boolean;<br />
function ExpandFileNameUTF8(const FileName: string): string;<br />
function ExpandUNCFileNameUTF8(const FileName: string): string;<br />
function ExtractShortPathNameUTF8(Const FileName : String) : String;<br />
function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec): Longint;<br />
function FindNextUTF8(var Rslt: TSearchRec): Longint;<br />
procedure FindCloseUTF8(var F: TSearchrec);<br />
function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;<br />
function FileGetAttrUTF8(const FileName: String): Longint;<br />
function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint;<br />
function DeleteFileUTF8(const FileName: String): Boolean;<br />
function RenameFileUTF8(const OldName, NewName: String): Boolean;<br />
function FileSearchUTF8(const Name, DirList : String): String;<br />
function FileIsReadOnlyUTF8(const FileName: String): Boolean;<br />
function GetCurrentDirUTF8: String;<br />
function SetCurrentDirUTF8(const NewDir: String): Boolean;<br />
function CreateDirUTF8(const NewDir: String): Boolean;<br />
function RemoveDirUTF8(const Dir: String): Boolean;<br />
function ForceDirectoriesUTF8(const Dir: string): Boolean;<br />
<br />
// environment<br />
function ParamStrUTF8(Param: Integer): string;<br />
function GetEnvironmentStringUTF8(Index: Integer): string;<br />
function GetEnvironmentVariableUTF8(const EnvVar: string): String;<br />
function GetAppConfigDirUTF8(Global: Boolean): string;<br />
<br />
// other<br />
function SysErrorMessageUTF8(ErrorCode: Integer): String;</syntaxhighlight><br />
<br />
===Empty file names and double path delimiters===<br />
<br />
There are differences in file/directory name handling in Windows versus Linux/Unix/Unix like systems.<br />
<br />
* Windows allows empty file names. That's why FileExistsUTF8('..\') checks under Windows in the parent directory for a file without name.<br />
* On Linux/Unix/Unix-like systems an empty file is mapped to the directory and directories are treated as files. This means that FileExistsUTF8('../') under Unix checks for the existence of the parent directory, which normally results true.<br />
<br />
Double path delimiters in file names are also treated differently: <br />
* Windows: 'C:\' is not the same as 'C:\\'<br />
* Unix like OS: the path '/usr//' is the same as '/usr/'. If '/usr' is a directory then even all three are the same. <br />
<br />
This is important when concatenating file names. For example:<br />
<br />
<syntaxhighlight>FullFilename:=FilePath+PathDelim+ShortFilename; // can result in two PathDelims which gives different results under Windows and Linux<br />
FullFilename:=AppendPathDelim(FilePath)+ShortFilename); // creates only one PathDelim<br />
FullFilename:=TrimFilename(FilePath+PathDelim+ShortFilename); // creates only one PathDelim and do some more clean up</syntaxhighlight><br />
<br />
The function TrimFilename replaces double path delimiters with single ones and shorten '..' paths. For example /usr//lib/../src is trimmed to /usr/src.<br />
<br />
If you want to know if a directory exists use '''DirectoryExistsUTF8'''.<br />
<br />
Another common task is to check if the path part of a file name exists. You can get the path with ExtractFilePath, but this will contain the path delimiter. <br />
* Under Unix like system you can simply use FileExistsUTF8 on the path. For example FileExistsUTF8('/home/user/') will return true if the directory /home/user exists. <br />
* Under Windows you must use the DirectoryExistsUTF8 function, but before that you must delete the path delimiter, for example with the ChompPathDelim function. <br />
<br />
Under Unix like systems the root directory is '/' and using the ChompPathDelim function will create an empty string. The function DirPathExists works like the DirectoryExistsUTF8 function, but trims the given path.<br />
<br />
Note that Unix/Linux uses the '~' (tilde) symbol to stand for the home directory, typically '/home/jim/' for a user called jim. So '~/myapp/myfile' and '/home/jim/myapp/myfile' are identical on the command line and in scripts. However, the tilde is not automatically expanded by Lazarus. It is necessary to use ExpandFileNameUTF8('~/myapp/myfile') to get the full path.<br />
<br />
=== Text encoding ===<br />
<br />
Text files are often encoded in the current system encoding. Under Windows this is usually one of the windows code pages, while Linux, BSD, and Mac OS X usually use UTF-8. <br />
There is no 100% rule to find out which encoding a text file uses. The LCL unit '''lconvencoding''' has a function to guess the encoding:<br />
<br />
<syntaxhighlight>function GuessEncoding(const s: string): string;<br />
function GetDefaultTextEncoding: string;</syntaxhighlight><br />
<br />
And it contains functions to convert from one encoding to another:<br />
<br />
<syntaxhighlight>function ConvertEncoding(const s, FromEncoding, ToEncoding: string): string;<br />
<br />
function UTF8BOMToUTF8(const s: string): string; // UTF8 with BOM<br />
function ISO_8859_1ToUTF8(const s: string): string; // central europe<br />
function CP1250ToUTF8(const s: string): string; // central europe<br />
function CP1251ToUTF8(const s: string): string; // cyrillic<br />
function CP1252ToUTF8(const s: string): string; // latin 1<br />
...<br />
function UTF8ToUTF8BOM(const s: string): string; // UTF8 with BOM<br />
function UTF8ToISO_8859_1(const s: string): string; // central europe<br />
function UTF8ToCP1250(const s: string): string; // central europe<br />
function UTF8ToCP1251(const s: string): string; // cyrillic<br />
function UTF8ToCP1252(const s: string): string; // latin 1<br />
...</syntaxhighlight><br />
<br />
For example to load a text file and convert it to UTF-8 you can use:<br />
<br />
<syntaxhighlight>var<br />
sl: TStringList;<br />
OriginalText: String;<br />
TextAsUTF8: String;<br />
begin<br />
sl:=TStringList.Create;<br />
try<br />
sl.LoadFromFile('sometext.txt'); // beware: this changes line endings to system line endings<br />
OriginalText:=sl.Text;<br />
TextAsUTF8:=ConvertEncoding(OriginalText,GuessEncoding(OriginalText),EncodingUTF8);<br />
...<br />
finally<br />
sl.Free;<br />
end;<br />
end;</syntaxhighlight><br />
<br />
And to save a text file in the system encoding you can use:<br />
<syntaxhighlight>sl.Text:=ConvertEncoding(TextAsUTF8,EncodingUTF8,GetDefaultTextEncoding);<br />
sl.SaveToFile('sometext.txt');</syntaxhighlight><br />
<br />
=== Configuration files ===<br />
<br />
You can use the [[doc:rtl/sysutils/getappconfigdir.html|GetAppConfigDir]] function from SysUtils unit to get a suitable place to store configuration files on different system. The function has one parameter, called Global. If it is True then the directory returned is a global directory, i.e. valid for all users on the system. If the parameter Global is false, then the directory is specific for the user who is executing the program. On systems that do not support multi-user environments, these two directories may be the same.<br />
<br />
There is also the [[doc:rtl/sysutils/getappconfigfile.html|GetAppConfigFile]] which will return an appropriate name for an application configuration file. You can use it like this:<br />
<br />
ConfigFilePath := GetAppConfigFile(False);<br />
<br />
Below are examples of the output of default path functions on different systems:<br />
<br />
<syntaxhighlight>program project1;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
uses<br />
SysUtils;<br />
<br />
begin<br />
WriteLn(GetAppConfigDir(True));<br />
WriteLn(GetAppConfigDir(False));<br />
WriteLn(GetAppConfigFile(True));<br />
WriteLn(GetAppConfigFile(False));<br />
end.</syntaxhighlight><br />
<br />
The output on a GNU/Linux system with FPC 2.2.2. Note that using True is buggy, already fixed in 2.2.3:<br />
<br />
<pre>/etc/project1/<br />
/home/user/.config/project1/<br />
/etc/project1.cfg<br />
/home/user/.config/project1.cfg</pre><br />
<br />
You can notice that global configuration files are stored on the /etc directory and local configurations are stored on a hidden folder on the user's home directory. Directories whose name begin with a dot (.) are hidden on Linux. You can create a directory on the location returned by GetAppConfigDir and then store configuration files there.<br />
<br />
{{Note| Normal users are not allowed to write to the /etc directory. Only users with administration rights can do this.}}<br />
<br />
The output on recent versions of Windows with FPC 3.0.0 + :<br />
<br />
<pre>C:\ProgramData\project1\<br />
C:\Users\user\AppData\Local\project1\<br />
C:\ProgramData\project1\project1.cfg<br />
C:\Users\user\AppData\Local\project1\project1.cfg</pre><br />
<br />
Notice that before FPC 2.2.4 the function was using the directory where the application was to store global configurations on Windows.<br />
<br />
The output on Windows 98 with FPC 2.2.0:<br />
<br />
<pre>C:\Program Files\PROJECT1<br />
C:\Windows\Local Settings\Application Data\PROJECT1<br />
C:\Program Files\PROJECT1\PROJECT1.cfg<br />
C:\Windows\Local Settings\Application Data\PROJECT1\PROJECT1.cfg</pre><br />
<br />
The output on Mac OS X with FPC 2.2.0:<br />
<br />
<pre>/etc<br />
/Users/user/.config/project1<br />
/etc/project1.cfg<br />
/Users/user/.config/project1/project1.cfg</pre><br />
<br />
{{Note| The use of UPX interferes with the use of the GetAppConfigDir and GetAppConfigFile functions.}}<br />
<br />
{{Note| Under Mac OS X, in most cases config files are preference files, which should be XML files with the ending ".plist" and be stored in /Library/Preferences or ~/Library/Preferences with Names taken from the field "Bundle identifier" in the Info.plist of the application bundle. Using the Carbon calls CFPreference... is probably the easiest way to achieve this. .config files in the User directory are a violation of the programming guide lines.}}<br />
<br />
=== Data and resource files ===<br />
<br />
A very common question is where to store data files an application might need, such as Images, Music, XML files, database files, help files, etc. Unfortunately there is no cross-platform function to get the best location to look for data files. The solution is to implement differently on each platform using IFDEFs.<br />
<br />
==== Windows ====<br />
On Windows, application data that the program modifies should not be put in the application's directory (e.g. C:\Program Files\) but in a specific location (see e.g. [http://support.microsoft.com/kb/310294], under "Classify Application Data"). Windows Vista and newer actively enforce this (users only have write access to these directories when using elevation or disabling UAC) but uses a folder redirection mechanism to accommodate older, wrongly programmed applications. Just reading, not writing, data from application directories would still work.<br />
<br />
In short: use such folder:<br />
<syntaxhighlight><br />
OpDirLocal:= GetEnvironmentVariableUTF8('appdata')+'\MyAppName';<br />
</syntaxhighlight><br />
<br />
<br />
See [[Windows_Programming_Tips#Getting_special_folders_.28My_documents.2C_Desktop.2C_local_application_data.2C_etc.29]]<br />
<br />
==== Unix/Linux ====<br />
On most Unixes (like Linux, FreeBSD, OpenBSD, Solaris), application data files are located in a fixed location, which can be something like: /usr/share/app_name or /opt/app_name.<br />
<br />
Application data that needs to be written to by the application often gets stored in places like /var/<programname>, with appropriate permissions set.<br />
<br />
User-specific read/write config/data will normally be stored somewhere under the user's home directory (e.g. in ~/.myfancyprogram).<br />
<br />
How to get this home dir path:<br />
<syntaxhighlight><br />
OpDirLocal:= GetEnvironmentVariableUTF8('HOME')+'/.myappname';<br />
</syntaxhighlight><br />
<br />
==== OS X ====<br />
macOS (Mac OS X) is an exception among UNIXes. Application is published in a bundle - directory with "app" extension, which is treated by file-manager as a file (you can also do "cd path/myapp.app"). Your resource files should be located inside the bundle. If bundle is "path/MyApp.app", then:<br />
<br />
* executable file is "path/MyApp.app/Contents/MacOS/myapp"<br />
* resources dir is "path/MyApp.app/Contents/Resources"<br />
<br />
Save config files to the home dir:<br />
<br />
<syntaxhighlight><br />
OpDirLocal:= GetEnvironmentVariableUTF8('HOME')+'/.myappname';<br />
</syntaxhighlight><br />
<br />
Read resources from:<br />
<br />
<syntaxhighlight><br />
OpDirRes:= ExtractFileDir(ExtractFileDir(Application.ExeName))+'/Resources';<br />
</syntaxhighlight><br />
<br />
Warning: '''never''' use paramstr(0) on any Unix platform to determine the location of the executable, as this is a Dos-Windows-OS/2 convention and has several conceptual problems, which cannot be solved using emulation on other platforms. The only thing paramstr(0) is guaranteed to return on Unix platforms is the name using which the program was started. The directory in which it is located and the name of the actual binary (in case it was started using a symbolic link) are not guaranteed to be available via paramstr(0).<br />
<br />
==== Example code ====<br />
<br />
Code: [[Cross-platform resources path]]<br />
<br />
=== 32/64 bit ===<br />
<br />
====Detecting bitness at runtime====<br />
While you can control whether you compile for 32 or 64 bit with compiler defines, sometimes you want to know what bitness the operating system runs.<br />
For example, if you are running a 32 bit Lazarus program on 64 bit Windows, you might want to run an external program in a 32 bit program files directory, or you might want to give different information to users: I need this in my LazUpdater Lazarus installer to offer the user a choice of 32 and 64 bit compilers. Code: [[Detect Windows x32-x64 example]].<br />
<br />
====Detecting bitness of external library before loading it====<br />
When you want to load functions from dynamic library into your program, it has to have same bitness as your application. On 64 bit Windows, your application might be 32-bit or 64-bit, and there can be 32-bit and 64-bit libraries on your system.<br />
So you might want to check whether dll's bitness is same as your application's bitness before loading the dll dynamically. Here is a function which tests dll's bitness ([http://forum.lazarus.freepascal.org/index.php/topic,36834.msg245859.html#msg245859 contributed in forum by GetMem]):<br />
<syntaxhighlight><br />
<br />
uses {..., } JwaWindows;<br />
<br />
function GetPEType(const APath: WideString): Byte;<br />
const<br />
PE_UNKNOWN = 0; //if the file is not a valid dll, 0 is returned<br />
// PE_16BIT = 1; // not supported by this function<br />
PE_32BIT = 2;<br />
PE_64BIT = 3;<br />
var<br />
hFile, hFileMap: THandle;<br />
PMapView: Pointer;<br />
PIDH: PImageDosHeader;<br />
PINTH: PImageNtHeaders;<br />
Base: Pointer;<br />
begin<br />
Result := PE_UNKNOWN;<br />
<br />
hFile := CreateFileW(PWideChar(APath), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);<br />
if hFile = INVALID_HANDLE_VALUE then<br />
begin<br />
CloseHandle(hFile);<br />
Exit;<br />
end;<br />
<br />
hFileMap := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);<br />
if hFileMap = 0 then<br />
begin<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
Exit;<br />
end;<br />
<br />
PMapView := MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);<br />
if PMapView = nil then<br />
begin<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
Exit;<br />
end;<br />
<br />
PIDH := PImageDosHeader(PMapView);<br />
if PIDH^.e_magic <> IMAGE_DOS_SIGNATURE then<br />
begin<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
UnmapViewOfFile(PMapView);<br />
Exit;<br />
end;<br />
<br />
Base := PIDH;<br />
PINTH := PIMAGENTHEADERS(Base + LongWord(PIDH^.e_lfanew));<br />
if PINTH^.Signature = IMAGE_NT_SIGNATURE then<br />
begin<br />
case PINTH^.OptionalHeader.Magic of<br />
$10b: Result := PE_32BIT;<br />
$20b: Result := PE_64BIT<br />
end;<br />
end;<br />
<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
UnmapViewOfFile(PMapView);<br />
end;<br />
<br />
//Now, if you compile your application for 32-bit and 64-bit windows, you can check if dll's bitness is same as your application's:<br />
function IsCorrectBitness(const APath: WideString): Boolean;<br />
begin <br />
{$ifdef CPU32}<br />
Result := GetPEType(APath) = 2; //the application is compiled as 32-bit, we ask if GetPeType returns 2<br />
{$endif}<br />
{$ifdef CPU64}<br />
Result := GetPEType(APath) = 3; //the application is compiled as 64-bit, we ask if GetPeType returns 3<br />
{$endif}<br />
end;<br />
</syntaxhighlight><br />
<br />
==== Pointer / Integer Typecasts ====<br />
<br />
Pointers under 64bit need 8 bytes instead of 4 on 32bit. The 'Integer' type remains 32bit on all platforms for compatibility. This means you can not typecast pointers into integers and back. <br />
<br />
FPC defines two types for this: PtrInt and PtrUInt. PtrInt is a 32bit signed integer on 32 bit platforms and a 64bit signed integer on 64bit platforms. The same for PtrUInt, but ''unsigned'' integer instead.<br />
<br />
Use for code that should work with Delphi and FPC:<br />
{$IFNDEF FPC}<br />
type<br />
PtrInt = integer;<br />
PtrUInt = cardinal;<br />
{$ENDIF}<br />
<br />
Replace all '''integer(SomePointerOrObject)''' with '''PtrInt(SomePointerOrObject)'''.<br />
<br />
=== Endianess ===<br />
<br />
Intel platforms are little endian, that means the least significant byte comes first. For example the two bytes of a word $1234 is stored as $34 $12 on little endian systems.<br />
On big endian systems like the powerpc the two bytes of a word $1234 are stored as $12 $34. The difference is important when reading files created on other systems.<br />
<br />
Use for code that should work on both:<br />
<syntaxhighlight>{$IFDEF ENDIAN_BIG}<br />
...<br />
{$ELSE}<br />
...<br />
{$ENDIF}</syntaxhighlight><br />
<br />
The opposite is ENDIAN_LITTLE.<br />
<br />
The system unit provides plenty of endian converting functions, like SwapEndian, BEtoN (big endian to current endian), LEtoN (little endian to current endian), NtoBE (current endian to big endian) and NtoLE (current endian to little endian).<br />
<br />
<br />
==== Libc and other special units ====<br />
<br />
Avoid legacy units like "oldlinux" and "libc" that are not supported outside of linux/i386.<br />
<br />
==== Assembler ====<br />
<br />
Avoid assembler.<br />
<br />
==== Compiler defines ====<br />
<br />
<syntaxhighlight>{$ifdef CPU32}<br />
...write here code for 32 bit processors<br />
{$ENDIF}<br />
{$ifdef CPU64}<br />
...write here code for 64 bit processors<br />
{$ENDIF}</syntaxhighlight><br />
<br />
=== Projects, packages and search paths ===<br />
<br />
Lazarus projects and packages are designed for multi platforms. Normally you can simply copy the project and the required packages to another machine and compile them there. You don't need to create one project per platform.<br />
<br />
Some advice to achieve this<br />
<br />
The compiler creates for every unit a ppu with the same name. This ppu can be used by other projects and packages. The unit source files (e.g. unit1.pas) should not be shared. Simply give the compiler a unit output directory where to create the ppu files. The IDE does that by default, so nothing to do for you here.<br />
<br />
Every unit file must be part of '''one''' project or package. If a unit file is only used by a single project, add it to this project. Otherwise add it to a package. If you have not yet created a package for your shared units, see here: [[Lazarus_Packages#Creating a package for your common units|Creating a package for your common units]]<br />
<br />
Every project and every package should have '''disjunct directories''' - they should not share directories. Otherwise you must be an expert in the art of compiler search paths. If you are not an expert or if others who may use your project/package are not experts: do not share directories between projects/packages.<br />
<br />
==== Platform specific units ====<br />
For example the unit wintricks.pas should only be used under Windows. In the uses section use:<br />
<br />
<syntaxhighlight>uses<br />
Classes, SysUtils<br />
{$IFDEF Windows}<br />
,WinTricks<br />
{$ENDIF}<br />
;</syntaxhighlight><br />
<br />
If the unit is part of a package, you must also select the unit in the package editor of the package and disable the ''Use unit'' checkbox.<br />
<br />
See also [[Lazarus_Packages#Platform_specific_units|Platform specific units]]<br />
<br />
==== Platform specific search paths ====<br />
<br />
When you target several platforms and access the operating system directly, then you will quickly get tired of endless IFDEF constructions. One solution that is used often in the FPC and Lazarus sources is to use include files. Create one sub directory per target. For example win32, linux, bsd, darwin. Put into each directory an include file with the same name. Then use a macro in the include path. The unit can use a normal include directive. <br />
An example for one include file for each LCL widget set:<br />
<br />
Create one file for each widget set you want to support:<br />
win32/example.inc<br />
gtk/example.inc<br />
gtk2/example.inc<br />
carbon/example.inc<br />
<br />
You do not need to add the files to the package or project.<br />
Add the include search path ''$(LCLWidgetType)'' to the compiler options of your package or project.<br />
<br />
In your unit use the directive:<br />
{$I example.inc}<br />
<br />
Here are some useful macros and common values:<br />
*LCLWidgetType: win32, gtk, gtk2, qt, carbon, fpgui, nogui<br />
*TargetOS: linux, win32, win64, wince, freebsd, netbsd, openbsd, darwin (many more)<br />
*TargetCPU: i386, x86_64, arm, powerpc, sparc<br />
*SrcOS: win, unix<br />
<br />
You can use the $Env() macro to use environment variables.<br />
<br />
And of course you can use combinations. For example the LCL uses: <br />
<br />
$(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS);$(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS)/$(LCLWidgetType)<br />
<br />
See here the complete list of macros: [[IDE Macros in paths and filenames]]<br />
<br />
==== Machine / User specific search paths ====<br />
<br />
For example you have two windows machines stan and oliver. On stan your units are in ''C:\units'' and on oliver your units are in ''D:\path''. The units belong to the package ''SharedStuff'' which is ''C:\units\sharedstuff.lpk'' on stan and ''D:\path\sharedstuff.lpk'' on oliver.<br />
Once you opened the lpk in the IDE or by lazbuild, the path is automatically stored in its configuration files (packagefiles.xml).<br />
When compiling a project that requires the package ''SharedStuff'', the IDE and lazbuild knows where it is. So no configuration is needed.<br />
<br />
If you have want to deploy a package over many machine or for all users of a machine (e.g. a pool for students), then you can add a lpl file in the lazarus source directory. See packager/globallinks for examples.<br />
<br />
=== Locale differences ===<br />
<br />
Some functions from Free Pascal, like StrToFloat behave differently depending on the current [locale]]. For example, in the USA the [[DecimalSeparator|decimal separator]] is usually ".", but in many European and South American countries it is ",". This can be a problem as sometimes it is desired to have these functions behave in a fixed way, independently from the locale. <br />
An example is a file format with decimal points that always needs to be interpreted the same way.<br />
<br />
The next sections explain how to do that.<br />
<br />
<br />
====StrToFloat====<br />
<br />
A new set of format settings which set a fixed decimal separator can be created with the following code:<br />
<br />
<syntaxhighlight><br />
// in your .lpr project file<br />
uses<br />
...<br />
{$IFDEF UNIX}<br />
clocale <br />
{ required on Linux/Unix for formatsettings support. Should be one of the first (probably after cthreads?}<br />
{$ENDIF}<br />
</syntaxhighlight><br />
<br />
and:<br />
<br />
<syntaxhighlight><br />
// in your code:<br />
var<br />
FPointSeparator, FCommaSeparator: TFormatSettings;<br />
begin<br />
// Format settings to convert a string to a float<br />
FPointSeparator := DefaultFormatSettings;<br />
FPointSeparator.DecimalSeparator := '.';<br />
FPointSeparator.ThousandSeparator := '#';// disable the thousand separator<br />
FCommaSeparator := DefaultFormatSettings;<br />
FCommaSeparator.DecimalSeparator := ',';<br />
FCommaSeparator.ThousandSeparator := '#';// disable the thousand separator</syntaxhighlight><br />
<br />
Later on you can use this format settings when calling StrToFloat, like this:<br />
<br />
<syntaxhighlight>// This function works like StrToFloat, but simply tries two possible decimal separator<br />
// This will avoid an exception when the string format doesn't match the locale<br />
function AnSemantico.StringToFloat(AStr: string): Double;<br />
begin<br />
if Pos('.', AStr) > 0 then Result := StrToFloat(AStr, FPointSeparator)<br />
else Result := StrToFloat(AStr, FCommaSeparator);<br />
end;</syntaxhighlight><br />
<br />
=== Gtk2 and masking FPU exceptions ===<br />
<br />
Gtk2 library changes the default value of FPU (floating point unit) exception mask. The consequence of this is that some floating point exceptions do not get raised if Gtk2 library is used by the application. That means that, if for example you develop a LCL application on Windows with win32/64 widgetset (which is Windows default) and plan to compile for Linux (where Gtk2 is default widgetset), you should keep this incompatibilities in mind.<br />
<br />
After [http://www.lazarus.freepascal.org/index.php/topic,13460.0.html this forum topic] and answers on [http://bugs.freepascal.org/view.php?id=19674 this bug report] it became clear that nothing can be done about this, so we must know what actually these differences are.<br />
<br />
Therefore, let's do a test:<br />
<br />
<syntaxhighlight><br />
uses<br />
..., math,...<br />
<br />
{...}<br />
<br />
var<br />
FPUException: TFPUException;<br />
FPUExceptionMask: TFPUExceptionMask;<br />
begin<br />
FPUExceptionMask := GetExceptionMask;<br />
for FPUException := Low(TFPUException) to High(TFPUException) do begin<br />
write(FPUException, ' - ');<br />
if not (FPUException in FPUExceptionMask) then<br />
write('not ');<br />
<br />
writeln('masked!');<br />
end;<br />
readln;<br />
end.<br />
</syntaxhighlight><br />
<br />
Our simple program will get what FPC default is:<br />
<br />
<code><br />
exInvalidOp - not masked!<br />
exDenormalized - masked!<br />
exZeroDivide - not masked!<br />
exOverflow - not masked!<br />
exUnderflow - masked!<br />
exPrecision - masked!<br />
</code><br />
<br />
However, with Gtk2, only exOverflow is not masked.<br />
<br />
The consequence is that EInvalidOp and EZeroDivide exceptions do not get raised if the application links to Gtk2 library! Normally, dividing non-zero value by zero raises EZeroDivide exception and dividing zero by zero raises EInvalidOp. For example the code like this:<br />
<br />
<syntaxhighlight><br />
var<br />
X, A, B: Double;<br />
// ...<br />
<br />
try<br />
X := A / B;<br />
// code block 1<br />
except <br />
// code block 2<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
will take different direction when compiled in application with Gtk2 widgetset. On win widgetset, when B equals zero, an exception will get raised (EZeroDivide or EInvalidOp, depending on whether A is zero) and "code block 2" will be executed. On Gtk2 X becomes [http://www.freepascal.org/docs-html/rtl/math/infinity.html Infinity], [http://www.freepascal.org/docs-html/rtl/math/neginfinity.html NegInfinity], or [http://www.freepascal.org/docs-html/rtl/math/nan.html NaN] and "code block 1" will be executed.<br />
<br />
We can think of different ways to overcome this inconsistency. Most of the time you can simply test if B equals zero and don't try the dividing in that case. However, sometimes you will need some different approach. So, take a look at the following examples:<br />
<br />
<syntaxhighlight><br />
uses<br />
..., math,...<br />
<br />
//...<br />
var<br />
X, A, B: Double;<br />
Ind: Boolean;<br />
// ...<br />
try<br />
X := A / B;<br />
Ind := IsInfinite(X) or IsNan(X); // with gtk2, we fall here<br />
except <br />
Ind := True; // in windows, we fall here when B equals zero<br />
end;<br />
if Ind then begin<br />
// code block 2<br />
end else begin<br />
// code block 1<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
Or:<br />
<syntaxhighlight><br />
uses<br />
..., math,...<br />
<br />
//...<br />
var<br />
X, A, B: Double;<br />
FPUExceptionMask: TFPUExceptionMask;<br />
// ...<br />
<br />
try<br />
FPUExceptionMask := GetExceptionMask;<br />
SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]); // unmask<br />
try<br />
X := A / B;<br />
finally<br />
SetExceptionMask(FPUExceptionMask); // return previous masking immediately, we must not let Gtk2 internals to be called without the mask<br />
end;<br />
// code block 1<br />
except <br />
// code block 2<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
Be cautious, do not do something like this (call LCL with still removed mask):<br />
<syntaxhighlight><br />
try<br />
FPUExceptionMask := GetExceptionMask;<br />
SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]);<br />
try<br />
Edit1.Text := FloatToStr(A / B); // NO! Setting Edit's text goes down to widgetset internals and Gtk2 API must not be called without the mask!<br />
finally<br />
SetExceptionMask(FPUExceptionMask);<br />
end;<br />
// code block 1<br />
except <br />
// code block 2<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
But use an auxiliary variable:<br />
<syntaxhighlight><br />
try<br />
FPUExceptionMask := GetExceptionMask;<br />
SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]);<br />
try<br />
X := A / B; // First, we set auxiliary variable X<br />
finally<br />
SetExceptionMask(FPUExceptionMask);<br />
end;<br />
Edit1.Text := FloatToStr(X); // Now we can set Edit's text.<br />
// code block 1<br />
except <br />
// code block 2<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
In all situations, when developing LCL applications, it is most important to know about this and to keep in mind that some floating point operations can go different way with different widgetsets. Then you can think of an appropriate way to workaround this, but this should not go unnoticed.<br />
<br />
==Issues when moving from Windows to *nix etc==<br />
Issues specific to Linux, OSX, Android and other Unixes are described here. Not all subjects may apply to all platforms<br />
<br />
=== On Unix there is no "application directory" ===<br />
Many programmers are used to call ExtractFilePath(ParamStr(0)) or Application.ExeName to get the location of the executable, and then search for the necessary files for the program execution (Images, XML files, database files, etc) based on the location of the executable. This is wrong on unixes. The string on ParamStr(0) may contain a directory other than the one of the executable, and it also varies between different shell programs (sh, bash, etc).<br />
<br />
Even if Application.ExeName could in fact know the directory where the executable is, that file could be a symbolic link, so you could get the directory of the link instead (depending on the Linux kernel version, you either get the directory of the link or of the program binary itself).<br />
<br />
To avoid this read the sections about [[Multiplatform_Programming_Guide#Configuration_files|configuration files]] and [[Multiplatform_Programming_Guide#Data_and_resource_files|data files]].<br />
<br />
=== Making do without Windows COM Automation ===<br />
<br />
With Windows, COM Automation is a powerful way not only of manipulating other programs remotely but also for allowing other programs to manipulate your program. With Delphi you can make your program both an COM Automation client and a COM Automation server, meaning it can both manipulate other programs and in turn be manipulated by other programs. For examples, <br />
see [http://wiki.lazarus.freepascal.org/Office_Automation#Using_COM_Automation_to_interact_with_OpenOffice_and_Microsoft_Office Using COM Automation to interact with OpenOffice and Microsoft Office].<br />
<br />
==== OSX alternative ====<br />
Unfortunately, COM Automation isn't available on OS X and Linux. However, you can simulate some of the functionality of COM Automation on OS X using AppleScript.<br />
<br />
AppleScript is similar to COM Automation in some ways. For example, you can write scripts that manipulate other programs. Here's a very simple example of AppleScript that starts NeoOffice (the Mac version of OpenOffice.org):<br />
<br />
tell application "NeoOffice"<br />
launch<br />
end tell<br />
<br />
An app that is designed to be manipulated by AppleScript provides a "dictionary" of classes and commands that can be used with the app, similar to the classes of a Windows Automation server. However, even apps like NeoOffice that don't provide a dictionary will still respond to the commands "launch", "activate" and "quit". AppleScript can be run from the OS X Script Editor or Finder or even converted to an app that you can drop on the dock just like any app. You can also run AppleScript from your program, as in this example:<br />
<br />
fpsystem('myscript.applescript');<br />
<br />
This assumes the script is in the indicated file. You can also run scripts on the fly from your app using the OS X OsaScript command:<br />
<br />
fpsystem('osascript -e '#39'tell application "NeoOffice"'#39 +<br />
' -e '#39'launch'#39' -e '#39'end tell'#39);<br />
{Note use of #39 to single-quote the parameters}<br />
<br />
However, these examples are just the equivalent of the following Open command:<br />
<br />
fpsystem('open -a NeoOffice');<br />
<br />
Similarly, in OS X you can emulate the Windows shell commands to launch a web browser and launch an email client with:<br />
<br />
fpsystem('open -a safari "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');<br />
<br />
and <br />
<br />
fpsystem('open -a mail "mailto:ss4200@invalid.org"');<br />
<br />
which assumes, fairly safely, that an OS X system will have the Safari and Mail applications installed. Of course, you should never make assumptions like this, and for the two previous examples, you can in fact just rely on OS X to do the right thing and pick the user's default web browser and email client if you instead use these variations:<br />
<br />
fpsystem('open "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');<br />
<br />
and <br />
<br />
fpsystem('open "mailto:ss4200@invalid.org"');<br />
<br />
Do not forget to include the Unix unit in your uses clause if you use <code>fpsystem</code> or <code>shell</code> (interchangeable).<br />
<br />
The real power of AppleScript is to manipulate programs remotely to create and open documents and automate other activities. How much you can do with a program depends on how extensive its AppleScript dictionary is (if it has one). For example, Microsoft's Office X programs are not very usable with AppleScript, whereas the newer Office 2004 programs have completely rewritten AppleScript dictionaries that compare in many ways with what's available via the Windows Office Automation servers.<br />
<br />
==== Linux alternatives ====<br />
While Linux shells support sophisticated command line scripting, the type of scripting is limited to what can be passed to a program on the command line. There is no single, unified way to access a program's internal classes and commands with Linux the way they are via Windows COM Automation and OS X AppleScript. However, individual desktop environments (GNOME/KDE) and application frameworks often provide such methods of interprocess communication. On GNOME see Bonobo Components. KDE has the KParts framework, DCOP. OpenOffice has a platform neutral API for controlling the office remotely (google OpenOffice SDK) - though you would probably have to write glue code in another language that has bindings (such as Python) to use it. In addition, some applications have "server modes" activated by special command-line options that allow them to be controlled from another process. It is also possible (Borland did it with Kylix document browser) to "embed" one top-level X application window into another using XReparentWindow (I think).<br />
<br />
As with Windows, many OS X and Linux programs are made up of multiple library files (.dylib and .so extensions). Sometimes these libraries are designed so you can also use them in programs you write. While this can be a way of adding some of the functionality of an external program to your program, it's not really the same as running and manipulating the external program itself. Instead, your program is just linking to and using the external program's library similar to the way it would use any programming library.<br />
<br />
=== Alternatives for Windows API functions ===<br />
<br />
Many Windows programs use the Windows API extensively. In cross-platform applications Win API functions in the Windows unit should not be used, or should be enclosed by a conditional compile (e.g. {$IFDEF MSWINDOWS} ).<br />
<br />
Fortunately many of the commonly used Windows API functions are implemented in a multiplatform way in the unit [[lclintf]]. This can be a solution for programs which rely heavily on the Windows API, although the best solution is to replace these calls with true cross-platform components from the LCL. You can replace calls to GDI painting functions with calls to a TCanvas object's methods, for example.<br />
<br />
=== Key codes ===<br />
Fortunately, detecting key codes (e.g. on KeyUp events) is portable: see [[LCL Key Handling]].<br />
<br />
===Installing your application===<br />
See [[Deploying Your Application]].<br />
<br />
== See Also ==<br />
* [[Writing portable code regarding the processor architecture]]<br />
* [[Introduction to platform-sensitive development]]<br />
* http://www.midnightbeach.com/KylixForDelphiProgrammers.html A guide for Windows programmers starting with Kylix. Many of concepts / code snippets apply to Lazarus.<br />
* http://www.stack.nl/~marcov/porting.pdf A guide for writing portable source code, mainly between different compilers.<br />
<br />
[[Category:Tutorials]]<br />
[[Category:Multiplatform Programming]]<br />
[[Category:Platform-sensitive development]]<br />
[[Category:Inter-process communication]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=Multiplatform_Programming_Guide&diff=109804Multiplatform Programming Guide2017-05-17T06:39:51Z<p>Zoran: /* Detecting bitness of external library before loading it */</p>
<hr />
<div>{{Multiplatform Programming Guide}}<br />
<br />
Most [[LCL]] applications work in a cross-platform way without any extra effort.<br />
<br />
This is a tutorial on writing cross-platform applications with Lazarus and Free Pascal. It will cover the necessary precautions to aid in creating a cross-platform ready program that is ready to [[Deploying Your Application|deploy]].<br />
<br />
__TOC__<br />
<br />
== Introduction to Multiplatform (Cross-platform) Programming ==<br />
<br />
=== How many platforms do you need? ===<br />
<br />
To answer this question, you should first determine who your potential users are and how your program will be used. This question depends on where you are deploying your application.<br />
<br />
If you are developing generic desktop software in 2014, Microsoft Windows may be the most important platform. Note that Mac OS X and/or Linux are making gaining in popularity, and may be a significant target for your application.<br />
<br />
The popularity of the various desktop operating systems differs by country, by the type of software used, and with the target audience; there's no general rule. For example, Mac OS X is quite popular in North America and western Europe, while in South America Macs are mostly restricted to video and sound work.<br />
<br />
On many contract projects, only one platform is relevant. Free Pascal and Lazarus are quite capable of writing software targeted at a specific platform. You can, for example, access the full Windows API to write a well integrated Windows program.<br />
<br />
If you're developing software that will run on a Web server, a Unix platform in one of its various flavors is commonly used. In this case, perhaps only Linux, Solaris, *BSD and other Unixes make sense as your target platforms, although you may want to add support for Windows for completeness.<br />
<br />
Once you've addressed any cross-platform issues in your design, you can largely ignore the other platforms, much as you would when developing for a single platform. However, at some point you'll need to test deploying and running your program on the other platforms. For that, it will be helpful to have unrestricted access to machines running the target operating systems. If you don't want multiple physical computers, investigate dual-booting or VM solutions like VMware or Parallels.<br />
<br />
== Cross-platform Programming ==<br />
<br />
=== Working with files and folders ===<br />
<br />
When working with files and folders, this is important to use non-platform specific path delimiters and [[End_of_Line|line ending]] sequences. Here is a list of declared [[Constant|constants]] in Lazarus to be used when working with files and folders.<br />
<br />
* '''PathSep''', '''PathSeparator''': path separator when adding many paths together (';', ...)<br />
* '''PathDelim''', '''DirectorySeparator''': directory separator for each platform ('/', '\', ...)<br />
* '''LineEnding''': proper line ending character sequence (#13#10 - CRLF, #10 - [[Line_feed|LF]], ...)<br />
<br />
Another important thing to be noted is the case sensitiveness of the file system.<br />
On Windows filenames are usually not case sensitive, while they usually are on Linux and BSD platforms. But if a EXT2, EXT3, etc file system is mounted on Windows, it would be case-sensitive. Respectively a FAT file system mounted on Linux should not be case sensitive.<br />
<br />
It shall be paid special attention, that NTFS is non-case sensitive when used in Windows, but it is case sensitive when mounted by POSIX OSes. This could cause '''various problems, including loss of files''' if files with same filenames in different cases exist on a NTFS partition, mounted in Windows. Using custom functions for checking and preventing creation of several files with the same names on NTFS should be considered by the developers.<br />
<br />
Mac OS X use case insensitive filenames by default. This can be the cause of annoying bugs, so any portable application should use consistently filenames.<br />
<br />
The RTL file functions use the system encoding for file names. Under Windows this is one of the windows code pages, while Linux, BSD and Mac OS X usually use UTF-8. The unit '''FileUtil''' of the LCL provides file functions which takes UTF-8 strings like the rest of the LCL.<br />
<br />
<syntaxhighlight>// AnsiToUTF8 and UTF8ToAnsi need a widestring manager under Linux, BSD, MacOSX<br />
// but normally these OS use UTF-8 as system encoding so the widestringmanager<br />
// is not needed.<br />
function NeedRTLAnsi: boolean;// true if system encoding is not UTF-8<br />
procedure SetNeedRTLAnsi(NewValue: boolean);<br />
function UTF8ToSys(const s: string): string;// as UTF8ToAnsi but more independent of widestringmanager<br />
function SysToUTF8(const s: string): string;// as AnsiToUTF8 but more independent of widestringmanager<br />
function UTF8ToConsole(const s: string): string;// converts UTF8 string to console encoding (used by Write, WriteLn)<br />
<br />
// file operations<br />
function FileExistsUTF8(const Filename: string): boolean;<br />
function FileAgeUTF8(const FileName: string): Longint;<br />
function DirectoryExistsUTF8(const Directory: string): Boolean;<br />
function ExpandFileNameUTF8(const FileName: string): string;<br />
function ExpandUNCFileNameUTF8(const FileName: string): string;<br />
function ExtractShortPathNameUTF8(Const FileName : String) : String;<br />
function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec): Longint;<br />
function FindNextUTF8(var Rslt: TSearchRec): Longint;<br />
procedure FindCloseUTF8(var F: TSearchrec);<br />
function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;<br />
function FileGetAttrUTF8(const FileName: String): Longint;<br />
function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint;<br />
function DeleteFileUTF8(const FileName: String): Boolean;<br />
function RenameFileUTF8(const OldName, NewName: String): Boolean;<br />
function FileSearchUTF8(const Name, DirList : String): String;<br />
function FileIsReadOnlyUTF8(const FileName: String): Boolean;<br />
function GetCurrentDirUTF8: String;<br />
function SetCurrentDirUTF8(const NewDir: String): Boolean;<br />
function CreateDirUTF8(const NewDir: String): Boolean;<br />
function RemoveDirUTF8(const Dir: String): Boolean;<br />
function ForceDirectoriesUTF8(const Dir: string): Boolean;<br />
<br />
// environment<br />
function ParamStrUTF8(Param: Integer): string;<br />
function GetEnvironmentStringUTF8(Index: Integer): string;<br />
function GetEnvironmentVariableUTF8(const EnvVar: string): String;<br />
function GetAppConfigDirUTF8(Global: Boolean): string;<br />
<br />
// other<br />
function SysErrorMessageUTF8(ErrorCode: Integer): String;</syntaxhighlight><br />
<br />
===Empty file names and double path delimiters===<br />
<br />
There are differences in file/directory name handling in Windows versus Linux/Unix/Unix like systems.<br />
<br />
* Windows allows empty file names. That's why FileExistsUTF8('..\') checks under Windows in the parent directory for a file without name.<br />
* On Linux/Unix/Unix-like systems an empty file is mapped to the directory and directories are treated as files. This means that FileExistsUTF8('../') under Unix checks for the existence of the parent directory, which normally results true.<br />
<br />
Double path delimiters in file names are also treated differently: <br />
* Windows: 'C:\' is not the same as 'C:\\'<br />
* Unix like OS: the path '/usr//' is the same as '/usr/'. If '/usr' is a directory then even all three are the same. <br />
<br />
This is important when concatenating file names. For example:<br />
<br />
<syntaxhighlight>FullFilename:=FilePath+PathDelim+ShortFilename; // can result in two PathDelims which gives different results under Windows and Linux<br />
FullFilename:=AppendPathDelim(FilePath)+ShortFilename); // creates only one PathDelim<br />
FullFilename:=TrimFilename(FilePath+PathDelim+ShortFilename); // creates only one PathDelim and do some more clean up</syntaxhighlight><br />
<br />
The function TrimFilename replaces double path delimiters with single ones and shorten '..' paths. For example /usr//lib/../src is trimmed to /usr/src.<br />
<br />
If you want to know if a directory exists use '''DirectoryExistsUTF8'''.<br />
<br />
Another common task is to check if the path part of a file name exists. You can get the path with ExtractFilePath, but this will contain the path delimiter. <br />
* Under Unix like system you can simply use FileExistsUTF8 on the path. For example FileExistsUTF8('/home/user/') will return true if the directory /home/user exists. <br />
* Under Windows you must use the DirectoryExistsUTF8 function, but before that you must delete the path delimiter, for example with the ChompPathDelim function. <br />
<br />
Under Unix like systems the root directory is '/' and using the ChompPathDelim function will create an empty string. The function DirPathExists works like the DirectoryExistsUTF8 function, but trims the given path.<br />
<br />
Note that Unix/Linux uses the '~' (tilde) symbol to stand for the home directory, typically '/home/jim/' for a user called jim. So '~/myapp/myfile' and '/home/jim/myapp/myfile' are identical on the command line and in scripts. However, the tilde is not automatically expanded by Lazarus. It is necessary to use ExpandFileNameUTF8('~/myapp/myfile') to get the full path.<br />
<br />
=== Text encoding ===<br />
<br />
Text files are often encoded in the current system encoding. Under Windows this is usually one of the windows code pages, while Linux, BSD, and Mac OS X usually use UTF-8. <br />
There is no 100% rule to find out which encoding a text file uses. The LCL unit '''lconvencoding''' has a function to guess the encoding:<br />
<br />
<syntaxhighlight>function GuessEncoding(const s: string): string;<br />
function GetDefaultTextEncoding: string;</syntaxhighlight><br />
<br />
And it contains functions to convert from one encoding to another:<br />
<br />
<syntaxhighlight>function ConvertEncoding(const s, FromEncoding, ToEncoding: string): string;<br />
<br />
function UTF8BOMToUTF8(const s: string): string; // UTF8 with BOM<br />
function ISO_8859_1ToUTF8(const s: string): string; // central europe<br />
function CP1250ToUTF8(const s: string): string; // central europe<br />
function CP1251ToUTF8(const s: string): string; // cyrillic<br />
function CP1252ToUTF8(const s: string): string; // latin 1<br />
...<br />
function UTF8ToUTF8BOM(const s: string): string; // UTF8 with BOM<br />
function UTF8ToISO_8859_1(const s: string): string; // central europe<br />
function UTF8ToCP1250(const s: string): string; // central europe<br />
function UTF8ToCP1251(const s: string): string; // cyrillic<br />
function UTF8ToCP1252(const s: string): string; // latin 1<br />
...</syntaxhighlight><br />
<br />
For example to load a text file and convert it to UTF-8 you can use:<br />
<br />
<syntaxhighlight>var<br />
sl: TStringList;<br />
OriginalText: String;<br />
TextAsUTF8: String;<br />
begin<br />
sl:=TStringList.Create;<br />
try<br />
sl.LoadFromFile('sometext.txt'); // beware: this changes line endings to system line endings<br />
OriginalText:=sl.Text;<br />
TextAsUTF8:=ConvertEncoding(OriginalText,GuessEncoding(OriginalText),EncodingUTF8);<br />
...<br />
finally<br />
sl.Free;<br />
end;<br />
end;</syntaxhighlight><br />
<br />
And to save a text file in the system encoding you can use:<br />
<syntaxhighlight>sl.Text:=ConvertEncoding(TextAsUTF8,EncodingUTF8,GetDefaultTextEncoding);<br />
sl.SaveToFile('sometext.txt');</syntaxhighlight><br />
<br />
=== Configuration files ===<br />
<br />
You can use the [[doc:rtl/sysutils/getappconfigdir.html|GetAppConfigDir]] function from SysUtils unit to get a suitable place to store configuration files on different system. The function has one parameter, called Global. If it is True then the directory returned is a global directory, i.e. valid for all users on the system. If the parameter Global is false, then the directory is specific for the user who is executing the program. On systems that do not support multi-user environments, these two directories may be the same.<br />
<br />
There is also the [[doc:rtl/sysutils/getappconfigfile.html|GetAppConfigFile]] which will return an appropriate name for an application configuration file. You can use it like this:<br />
<br />
ConfigFilePath := GetAppConfigFile(False);<br />
<br />
Below are examples of the output of default path functions on different systems:<br />
<br />
<syntaxhighlight>program project1;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
uses<br />
SysUtils;<br />
<br />
begin<br />
WriteLn(GetAppConfigDir(True));<br />
WriteLn(GetAppConfigDir(False));<br />
WriteLn(GetAppConfigFile(True));<br />
WriteLn(GetAppConfigFile(False));<br />
end.</syntaxhighlight><br />
<br />
The output on a GNU/Linux system with FPC 2.2.2. Note that using True is buggy, already fixed in 2.2.3:<br />
<br />
<pre>/etc/project1/<br />
/home/user/.config/project1/<br />
/etc/project1.cfg<br />
/home/user/.config/project1.cfg</pre><br />
<br />
You can notice that global configuration files are stored on the /etc directory and local configurations are stored on a hidden folder on the user's home directory. Directories whose name begin with a dot (.) are hidden on Linux. You can create a directory on the location returned by GetAppConfigDir and then store configuration files there.<br />
<br />
{{Note| Normal users are not allowed to write to the /etc directory. Only users with administration rights can do this.}}<br />
<br />
The output on recent versions of Windows with FPC 3.0.0 + :<br />
<br />
<pre>C:\ProgramData\project1\<br />
C:\Users\user\AppData\Local\project1\<br />
C:\ProgramData\project1\project1.cfg<br />
C:\Users\user\AppData\Local\project1\project1.cfg</pre><br />
<br />
Notice that before FPC 2.2.4 the function was using the directory where the application was to store global configurations on Windows.<br />
<br />
The output on Windows 98 with FPC 2.2.0:<br />
<br />
<pre>C:\Program Files\PROJECT1<br />
C:\Windows\Local Settings\Application Data\PROJECT1<br />
C:\Program Files\PROJECT1\PROJECT1.cfg<br />
C:\Windows\Local Settings\Application Data\PROJECT1\PROJECT1.cfg</pre><br />
<br />
The output on Mac OS X with FPC 2.2.0:<br />
<br />
<pre>/etc<br />
/Users/user/.config/project1<br />
/etc/project1.cfg<br />
/Users/user/.config/project1/project1.cfg</pre><br />
<br />
{{Note| The use of UPX interferes with the use of the GetAppConfigDir and GetAppConfigFile functions.}}<br />
<br />
{{Note| Under Mac OS X, in most cases config files are preference files, which should be XML files with the ending ".plist" and be stored in /Library/Preferences or ~/Library/Preferences with Names taken from the field "Bundle identifier" in the Info.plist of the application bundle. Using the Carbon calls CFPreference... is probably the easiest way to achieve this. .config files in the User directory are a violation of the programming guide lines.}}<br />
<br />
=== Data and resource files ===<br />
<br />
A very common question is where to store data files an application might need, such as Images, Music, XML files, database files, help files, etc. Unfortunately there is no cross-platform function to get the best location to look for data files. The solution is to implement differently on each platform using IFDEFs.<br />
<br />
==== Windows ====<br />
On Windows, application data that the program modifies should not be put in the application's directory (e.g. C:\Program Files\) but in a specific location (see e.g. [http://support.microsoft.com/kb/310294], under "Classify Application Data"). Windows Vista and newer actively enforce this (users only have write access to these directories when using elevation or disabling UAC) but uses a folder redirection mechanism to accommodate older, wrongly programmed applications. Just reading, not writing, data from application directories would still work.<br />
<br />
In short: use such folder:<br />
<syntaxhighlight><br />
OpDirLocal:= GetEnvironmentVariableUTF8('appdata')+'\MyAppName';<br />
</syntaxhighlight><br />
<br />
<br />
See [[Windows_Programming_Tips#Getting_special_folders_.28My_documents.2C_Desktop.2C_local_application_data.2C_etc.29]]<br />
<br />
==== Unix/Linux ====<br />
On most Unixes (like Linux, FreeBSD, OpenBSD, Solaris), application data files are located in a fixed location, which can be something like: /usr/share/app_name or /opt/app_name.<br />
<br />
Application data that needs to be written to by the application often gets stored in places like /var/<programname>, with appropriate permissions set.<br />
<br />
User-specific read/write config/data will normally be stored somewhere under the user's home directory (e.g. in ~/.myfancyprogram).<br />
<br />
How to get this home dir path:<br />
<syntaxhighlight><br />
OpDirLocal:= GetEnvironmentVariableUTF8('HOME')+'/.myappname';<br />
</syntaxhighlight><br />
<br />
==== OS X ====<br />
macOS (Mac OS X) is an exception among UNIXes. Application is published in a bundle - directory with "app" extension, which is treated by file-manager as a file (you can also do "cd path/myapp.app"). Your resource files should be located inside the bundle. If bundle is "path/MyApp.app", then:<br />
<br />
* executable file is "path/MyApp.app/Contents/MacOS/myapp"<br />
* resources dir is "path/MyApp.app/Contents/Resources"<br />
<br />
Save config files to the home dir:<br />
<br />
<syntaxhighlight><br />
OpDirLocal:= GetEnvironmentVariableUTF8('HOME')+'/.myappname';<br />
</syntaxhighlight><br />
<br />
Read resources from:<br />
<br />
<syntaxhighlight><br />
OpDirRes:= ExtractFileDir(ExtractFileDir(Application.ExeName))+'/Resources';<br />
</syntaxhighlight><br />
<br />
Warning: '''never''' use paramstr(0) on any Unix platform to determine the location of the executable, as this is a Dos-Windows-OS/2 convention and has several conceptual problems, which cannot be solved using emulation on other platforms. The only thing paramstr(0) is guaranteed to return on Unix platforms is the name using which the program was started. The directory in which it is located and the name of the actual binary (in case it was started using a symbolic link) are not guaranteed to be available via paramstr(0).<br />
<br />
==== Example code ====<br />
<br />
Code: [[Cross-platform resources path]]<br />
<br />
=== 32/64 bit ===<br />
<br />
====Detecting bitness at runtime====<br />
While you can control whether you compile for 32 or 64 bit with compiler defines, sometimes you want to know what bitness the operating system runs.<br />
For example, if you are running a 32 bit Lazarus program on 64 bit Windows, you might want to run an external program in a 32 bit program files directory, or you might want to give different information to users: I need this in my LazUpdater Lazarus installer to offer the user a choice of 32 and 64 bit compilers. Code: [[Detect Windows x32-x64 example]].<br />
<br />
====Detecting bitness of external library before loading it====<br />
When you want to load functions from dynamic library into your program, it has to have same bitness as your application. On 64 bit Windows, your application might be 32-bit or 64-bit, and there can be 32-bit and 64-bit libraries on your system.<br />
So you might want to check whether dll's bitness is same as your application's bitness before loading the dll dynamically. Here is a function which tests dll's bitness ([http://forum.lazarus.freepascal.org/index.php/topic,36834.msg245859.html#msg245859 contributed in forum by GetMem]):<br />
<syntaxhighlight><br />
<br />
uses {..., } JwaWindows;<br />
<br />
function GetPEType(const APath: WideString): Byte;<br />
const<br />
PE_UNKNOWN = 0; //if the file is not a valid dll, 0 is returned<br />
PE_16BIT = 1;<br />
PE_32BIT = 2;<br />
PE_64BIT = 3;<br />
var<br />
hFile, hFileMap: THandle;<br />
PMapView: Pointer;<br />
PIDH: PImageDosHeader;<br />
PINTH: PImageNtHeaders;<br />
Base: Pointer;<br />
begin<br />
Result := PE_UNKNOWN;<br />
<br />
hFile := CreateFileW(PWideChar(APath), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);<br />
if hFile = INVALID_HANDLE_VALUE then<br />
begin<br />
CloseHandle(hFile);<br />
Exit;<br />
end;<br />
<br />
hFileMap := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);<br />
if hFileMap = 0 then<br />
begin<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
Exit;<br />
end;<br />
<br />
PMapView := MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);<br />
if PMapView = nil then<br />
begin<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
Exit;<br />
end;<br />
<br />
PIDH := PImageDosHeader(PMapView);<br />
if PIDH^.e_magic <> IMAGE_DOS_SIGNATURE then<br />
begin<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
UnmapViewOfFile(PMapView);<br />
Exit;<br />
end;<br />
<br />
Base := PIDH;<br />
PINTH := PIMAGENTHEADERS(Base + LongWord(PIDH^.e_lfanew));<br />
if PINTH^.Signature <> IMAGE_NT_SIGNATURE then<br />
begin<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
UnmapViewOfFile(PMapView);<br />
Exit;<br />
end;<br />
<br />
if PINTH^.Signature = $4550 then<br />
begin<br />
case PINTH^.OptionalHeader.Magic of<br />
$10b: Result := PE_32BIT;<br />
$20b: Result := PE_64BIT<br />
end;<br />
end<br />
else<br />
Result := PE_16BIT;<br />
<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
UnmapViewOfFile(PMapView);<br />
end;<br />
<br />
//Now, if you compile your application for 32-bit and 64-bit windows, you can check if dll's bitness is same as your application's:<br />
function IsCorrectBitness(const APath: WideString): Boolean;<br />
begin <br />
{$ifdef CPU16}<br />
Result := GetPEType(APath) = 1;<br />
{$endif}<br />
{$ifdef CPU32}<br />
Result := GetPEType(APath) = 2; //the application is compiled as 32-bit, we ask if GetPeType returns 2<br />
{$endif}<br />
{$ifdef CPU64}<br />
Result := GetPEType(APath) = 3; //the application is compiled as 64-bit, we ask if GetPeType returns 3<br />
{$endif}<br />
end;<br />
</syntaxhighlight><br />
<br />
==== Pointer / Integer Typecasts ====<br />
<br />
Pointers under 64bit need 8 bytes instead of 4 on 32bit. The 'Integer' type remains 32bit on all platforms for compatibility. This means you can not typecast pointers into integers and back. <br />
<br />
FPC defines two types for this: PtrInt and PtrUInt. PtrInt is a 32bit signed integer on 32 bit platforms and a 64bit signed integer on 64bit platforms. The same for PtrUInt, but ''unsigned'' integer instead.<br />
<br />
Use for code that should work with Delphi and FPC:<br />
{$IFNDEF FPC}<br />
type<br />
PtrInt = integer;<br />
PtrUInt = cardinal;<br />
{$ENDIF}<br />
<br />
Replace all '''integer(SomePointerOrObject)''' with '''PtrInt(SomePointerOrObject)'''.<br />
<br />
=== Endianess ===<br />
<br />
Intel platforms are little endian, that means the least significant byte comes first. For example the two bytes of a word $1234 is stored as $34 $12 on little endian systems.<br />
On big endian systems like the powerpc the two bytes of a word $1234 are stored as $12 $34. The difference is important when reading files created on other systems.<br />
<br />
Use for code that should work on both:<br />
<syntaxhighlight>{$IFDEF ENDIAN_BIG}<br />
...<br />
{$ELSE}<br />
...<br />
{$ENDIF}</syntaxhighlight><br />
<br />
The opposite is ENDIAN_LITTLE.<br />
<br />
The system unit provides plenty of endian converting functions, like SwapEndian, BEtoN (big endian to current endian), LEtoN (little endian to current endian), NtoBE (current endian to big endian) and NtoLE (current endian to little endian).<br />
<br />
<br />
==== Libc and other special units ====<br />
<br />
Avoid legacy units like "oldlinux" and "libc" that are not supported outside of linux/i386.<br />
<br />
==== Assembler ====<br />
<br />
Avoid assembler.<br />
<br />
==== Compiler defines ====<br />
<br />
<syntaxhighlight>{$ifdef CPU32}<br />
...write here code for 32 bit processors<br />
{$ENDIF}<br />
{$ifdef CPU64}<br />
...write here code for 64 bit processors<br />
{$ENDIF}</syntaxhighlight><br />
<br />
=== Projects, packages and search paths ===<br />
<br />
Lazarus projects and packages are designed for multi platforms. Normally you can simply copy the project and the required packages to another machine and compile them there. You don't need to create one project per platform.<br />
<br />
Some advice to achieve this<br />
<br />
The compiler creates for every unit a ppu with the same name. This ppu can be used by other projects and packages. The unit source files (e.g. unit1.pas) should not be shared. Simply give the compiler a unit output directory where to create the ppu files. The IDE does that by default, so nothing to do for you here.<br />
<br />
Every unit file must be part of '''one''' project or package. If a unit file is only used by a single project, add it to this project. Otherwise add it to a package. If you have not yet created a package for your shared units, see here: [[Lazarus_Packages#Creating a package for your common units|Creating a package for your common units]]<br />
<br />
Every project and every package should have '''disjunct directories''' - they should not share directories. Otherwise you must be an expert in the art of compiler search paths. If you are not an expert or if others who may use your project/package are not experts: do not share directories between projects/packages.<br />
<br />
==== Platform specific units ====<br />
For example the unit wintricks.pas should only be used under Windows. In the uses section use:<br />
<br />
<syntaxhighlight>uses<br />
Classes, SysUtils<br />
{$IFDEF Windows}<br />
,WinTricks<br />
{$ENDIF}<br />
;</syntaxhighlight><br />
<br />
If the unit is part of a package, you must also select the unit in the package editor of the package and disable the ''Use unit'' checkbox.<br />
<br />
See also [[Lazarus_Packages#Platform_specific_units|Platform specific units]]<br />
<br />
==== Platform specific search paths ====<br />
<br />
When you target several platforms and access the operating system directly, then you will quickly get tired of endless IFDEF constructions. One solution that is used often in the FPC and Lazarus sources is to use include files. Create one sub directory per target. For example win32, linux, bsd, darwin. Put into each directory an include file with the same name. Then use a macro in the include path. The unit can use a normal include directive. <br />
An example for one include file for each LCL widget set:<br />
<br />
Create one file for each widget set you want to support:<br />
win32/example.inc<br />
gtk/example.inc<br />
gtk2/example.inc<br />
carbon/example.inc<br />
<br />
You do not need to add the files to the package or project.<br />
Add the include search path ''$(LCLWidgetType)'' to the compiler options of your package or project.<br />
<br />
In your unit use the directive:<br />
{$I example.inc}<br />
<br />
Here are some useful macros and common values:<br />
*LCLWidgetType: win32, gtk, gtk2, qt, carbon, fpgui, nogui<br />
*TargetOS: linux, win32, win64, wince, freebsd, netbsd, openbsd, darwin (many more)<br />
*TargetCPU: i386, x86_64, arm, powerpc, sparc<br />
*SrcOS: win, unix<br />
<br />
You can use the $Env() macro to use environment variables.<br />
<br />
And of course you can use combinations. For example the LCL uses: <br />
<br />
$(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS);$(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS)/$(LCLWidgetType)<br />
<br />
See here the complete list of macros: [[IDE Macros in paths and filenames]]<br />
<br />
==== Machine / User specific search paths ====<br />
<br />
For example you have two windows machines stan and oliver. On stan your units are in ''C:\units'' and on oliver your units are in ''D:\path''. The units belong to the package ''SharedStuff'' which is ''C:\units\sharedstuff.lpk'' on stan and ''D:\path\sharedstuff.lpk'' on oliver.<br />
Once you opened the lpk in the IDE or by lazbuild, the path is automatically stored in its configuration files (packagefiles.xml).<br />
When compiling a project that requires the package ''SharedStuff'', the IDE and lazbuild knows where it is. So no configuration is needed.<br />
<br />
If you have want to deploy a package over many machine or for all users of a machine (e.g. a pool for students), then you can add a lpl file in the lazarus source directory. See packager/globallinks for examples.<br />
<br />
=== Locale differences ===<br />
<br />
Some functions from Free Pascal, like StrToFloat behave differently depending on the current [locale]]. For example, in the USA the [[DecimalSeparator|decimal separator]] is usually ".", but in many European and South American countries it is ",". This can be a problem as sometimes it is desired to have these functions behave in a fixed way, independently from the locale. <br />
An example is a file format with decimal points that always needs to be interpreted the same way.<br />
<br />
The next sections explain how to do that.<br />
<br />
<br />
====StrToFloat====<br />
<br />
A new set of format settings which set a fixed decimal separator can be created with the following code:<br />
<br />
<syntaxhighlight><br />
// in your .lpr project file<br />
uses<br />
...<br />
{$IFDEF UNIX}<br />
clocale <br />
{ required on Linux/Unix for formatsettings support. Should be one of the first (probably after cthreads?}<br />
{$ENDIF}<br />
</syntaxhighlight><br />
<br />
and:<br />
<br />
<syntaxhighlight><br />
// in your code:<br />
var<br />
FPointSeparator, FCommaSeparator: TFormatSettings;<br />
begin<br />
// Format settings to convert a string to a float<br />
FPointSeparator := DefaultFormatSettings;<br />
FPointSeparator.DecimalSeparator := '.';<br />
FPointSeparator.ThousandSeparator := '#';// disable the thousand separator<br />
FCommaSeparator := DefaultFormatSettings;<br />
FCommaSeparator.DecimalSeparator := ',';<br />
FCommaSeparator.ThousandSeparator := '#';// disable the thousand separator</syntaxhighlight><br />
<br />
Later on you can use this format settings when calling StrToFloat, like this:<br />
<br />
<syntaxhighlight>// This function works like StrToFloat, but simply tries two possible decimal separator<br />
// This will avoid an exception when the string format doesn't match the locale<br />
function AnSemantico.StringToFloat(AStr: string): Double;<br />
begin<br />
if Pos('.', AStr) > 0 then Result := StrToFloat(AStr, FPointSeparator)<br />
else Result := StrToFloat(AStr, FCommaSeparator);<br />
end;</syntaxhighlight><br />
<br />
=== Gtk2 and masking FPU exceptions ===<br />
<br />
Gtk2 library changes the default value of FPU (floating point unit) exception mask. The consequence of this is that some floating point exceptions do not get raised if Gtk2 library is used by the application. That means that, if for example you develop a LCL application on Windows with win32/64 widgetset (which is Windows default) and plan to compile for Linux (where Gtk2 is default widgetset), you should keep this incompatibilities in mind.<br />
<br />
After [http://www.lazarus.freepascal.org/index.php/topic,13460.0.html this forum topic] and answers on [http://bugs.freepascal.org/view.php?id=19674 this bug report] it became clear that nothing can be done about this, so we must know what actually these differences are.<br />
<br />
Therefore, let's do a test:<br />
<br />
<syntaxhighlight><br />
uses<br />
..., math,...<br />
<br />
{...}<br />
<br />
var<br />
FPUException: TFPUException;<br />
FPUExceptionMask: TFPUExceptionMask;<br />
begin<br />
FPUExceptionMask := GetExceptionMask;<br />
for FPUException := Low(TFPUException) to High(TFPUException) do begin<br />
write(FPUException, ' - ');<br />
if not (FPUException in FPUExceptionMask) then<br />
write('not ');<br />
<br />
writeln('masked!');<br />
end;<br />
readln;<br />
end.<br />
</syntaxhighlight><br />
<br />
Our simple program will get what FPC default is:<br />
<br />
<code><br />
exInvalidOp - not masked!<br />
exDenormalized - masked!<br />
exZeroDivide - not masked!<br />
exOverflow - not masked!<br />
exUnderflow - masked!<br />
exPrecision - masked!<br />
</code><br />
<br />
However, with Gtk2, only exOverflow is not masked.<br />
<br />
The consequence is that EInvalidOp and EZeroDivide exceptions do not get raised if the application links to Gtk2 library! Normally, dividing non-zero value by zero raises EZeroDivide exception and dividing zero by zero raises EInvalidOp. For example the code like this:<br />
<br />
<syntaxhighlight><br />
var<br />
X, A, B: Double;<br />
// ...<br />
<br />
try<br />
X := A / B;<br />
// code block 1<br />
except <br />
// code block 2<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
will take different direction when compiled in application with Gtk2 widgetset. On win widgetset, when B equals zero, an exception will get raised (EZeroDivide or EInvalidOp, depending on whether A is zero) and "code block 2" will be executed. On Gtk2 X becomes [http://www.freepascal.org/docs-html/rtl/math/infinity.html Infinity], [http://www.freepascal.org/docs-html/rtl/math/neginfinity.html NegInfinity], or [http://www.freepascal.org/docs-html/rtl/math/nan.html NaN] and "code block 1" will be executed.<br />
<br />
We can think of different ways to overcome this inconsistency. Most of the time you can simply test if B equals zero and don't try the dividing in that case. However, sometimes you will need some different approach. So, take a look at the following examples:<br />
<br />
<syntaxhighlight><br />
uses<br />
..., math,...<br />
<br />
//...<br />
var<br />
X, A, B: Double;<br />
Ind: Boolean;<br />
// ...<br />
try<br />
X := A / B;<br />
Ind := IsInfinite(X) or IsNan(X); // with gtk2, we fall here<br />
except <br />
Ind := True; // in windows, we fall here when B equals zero<br />
end;<br />
if Ind then begin<br />
// code block 2<br />
end else begin<br />
// code block 1<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
Or:<br />
<syntaxhighlight><br />
uses<br />
..., math,...<br />
<br />
//...<br />
var<br />
X, A, B: Double;<br />
FPUExceptionMask: TFPUExceptionMask;<br />
// ...<br />
<br />
try<br />
FPUExceptionMask := GetExceptionMask;<br />
SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]); // unmask<br />
try<br />
X := A / B;<br />
finally<br />
SetExceptionMask(FPUExceptionMask); // return previous masking immediately, we must not let Gtk2 internals to be called without the mask<br />
end;<br />
// code block 1<br />
except <br />
// code block 2<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
Be cautious, do not do something like this (call LCL with still removed mask):<br />
<syntaxhighlight><br />
try<br />
FPUExceptionMask := GetExceptionMask;<br />
SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]);<br />
try<br />
Edit1.Text := FloatToStr(A / B); // NO! Setting Edit's text goes down to widgetset internals and Gtk2 API must not be called without the mask!<br />
finally<br />
SetExceptionMask(FPUExceptionMask);<br />
end;<br />
// code block 1<br />
except <br />
// code block 2<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
But use an auxiliary variable:<br />
<syntaxhighlight><br />
try<br />
FPUExceptionMask := GetExceptionMask;<br />
SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]);<br />
try<br />
X := A / B; // First, we set auxiliary variable X<br />
finally<br />
SetExceptionMask(FPUExceptionMask);<br />
end;<br />
Edit1.Text := FloatToStr(X); // Now we can set Edit's text.<br />
// code block 1<br />
except <br />
// code block 2<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
In all situations, when developing LCL applications, it is most important to know about this and to keep in mind that some floating point operations can go different way with different widgetsets. Then you can think of an appropriate way to workaround this, but this should not go unnoticed.<br />
<br />
==Issues when moving from Windows to *nix etc==<br />
Issues specific to Linux, OSX, Android and other Unixes are described here. Not all subjects may apply to all platforms<br />
<br />
=== On Unix there is no "application directory" ===<br />
Many programmers are used to call ExtractFilePath(ParamStr(0)) or Application.ExeName to get the location of the executable, and then search for the necessary files for the program execution (Images, XML files, database files, etc) based on the location of the executable. This is wrong on unixes. The string on ParamStr(0) may contain a directory other than the one of the executable, and it also varies between different shell programs (sh, bash, etc).<br />
<br />
Even if Application.ExeName could in fact know the directory where the executable is, that file could be a symbolic link, so you could get the directory of the link instead (depending on the Linux kernel version, you either get the directory of the link or of the program binary itself).<br />
<br />
To avoid this read the sections about [[Multiplatform_Programming_Guide#Configuration_files|configuration files]] and [[Multiplatform_Programming_Guide#Data_and_resource_files|data files]].<br />
<br />
=== Making do without Windows COM Automation ===<br />
<br />
With Windows, COM Automation is a powerful way not only of manipulating other programs remotely but also for allowing other programs to manipulate your program. With Delphi you can make your program both an COM Automation client and a COM Automation server, meaning it can both manipulate other programs and in turn be manipulated by other programs. For examples, <br />
see [http://wiki.lazarus.freepascal.org/Office_Automation#Using_COM_Automation_to_interact_with_OpenOffice_and_Microsoft_Office Using COM Automation to interact with OpenOffice and Microsoft Office].<br />
<br />
==== OSX alternative ====<br />
Unfortunately, COM Automation isn't available on OS X and Linux. However, you can simulate some of the functionality of COM Automation on OS X using AppleScript.<br />
<br />
AppleScript is similar to COM Automation in some ways. For example, you can write scripts that manipulate other programs. Here's a very simple example of AppleScript that starts NeoOffice (the Mac version of OpenOffice.org):<br />
<br />
tell application "NeoOffice"<br />
launch<br />
end tell<br />
<br />
An app that is designed to be manipulated by AppleScript provides a "dictionary" of classes and commands that can be used with the app, similar to the classes of a Windows Automation server. However, even apps like NeoOffice that don't provide a dictionary will still respond to the commands "launch", "activate" and "quit". AppleScript can be run from the OS X Script Editor or Finder or even converted to an app that you can drop on the dock just like any app. You can also run AppleScript from your program, as in this example:<br />
<br />
fpsystem('myscript.applescript');<br />
<br />
This assumes the script is in the indicated file. You can also run scripts on the fly from your app using the OS X OsaScript command:<br />
<br />
fpsystem('osascript -e '#39'tell application "NeoOffice"'#39 +<br />
' -e '#39'launch'#39' -e '#39'end tell'#39);<br />
{Note use of #39 to single-quote the parameters}<br />
<br />
However, these examples are just the equivalent of the following Open command:<br />
<br />
fpsystem('open -a NeoOffice');<br />
<br />
Similarly, in OS X you can emulate the Windows shell commands to launch a web browser and launch an email client with:<br />
<br />
fpsystem('open -a safari "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');<br />
<br />
and <br />
<br />
fpsystem('open -a mail "mailto:ss4200@invalid.org"');<br />
<br />
which assumes, fairly safely, that an OS X system will have the Safari and Mail applications installed. Of course, you should never make assumptions like this, and for the two previous examples, you can in fact just rely on OS X to do the right thing and pick the user's default web browser and email client if you instead use these variations:<br />
<br />
fpsystem('open "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');<br />
<br />
and <br />
<br />
fpsystem('open "mailto:ss4200@invalid.org"');<br />
<br />
Do not forget to include the Unix unit in your uses clause if you use <code>fpsystem</code> or <code>shell</code> (interchangeable).<br />
<br />
The real power of AppleScript is to manipulate programs remotely to create and open documents and automate other activities. How much you can do with a program depends on how extensive its AppleScript dictionary is (if it has one). For example, Microsoft's Office X programs are not very usable with AppleScript, whereas the newer Office 2004 programs have completely rewritten AppleScript dictionaries that compare in many ways with what's available via the Windows Office Automation servers.<br />
<br />
==== Linux alternatives ====<br />
While Linux shells support sophisticated command line scripting, the type of scripting is limited to what can be passed to a program on the command line. There is no single, unified way to access a program's internal classes and commands with Linux the way they are via Windows COM Automation and OS X AppleScript. However, individual desktop environments (GNOME/KDE) and application frameworks often provide such methods of interprocess communication. On GNOME see Bonobo Components. KDE has the KParts framework, DCOP. OpenOffice has a platform neutral API for controlling the office remotely (google OpenOffice SDK) - though you would probably have to write glue code in another language that has bindings (such as Python) to use it. In addition, some applications have "server modes" activated by special command-line options that allow them to be controlled from another process. It is also possible (Borland did it with Kylix document browser) to "embed" one top-level X application window into another using XReparentWindow (I think).<br />
<br />
As with Windows, many OS X and Linux programs are made up of multiple library files (.dylib and .so extensions). Sometimes these libraries are designed so you can also use them in programs you write. While this can be a way of adding some of the functionality of an external program to your program, it's not really the same as running and manipulating the external program itself. Instead, your program is just linking to and using the external program's library similar to the way it would use any programming library.<br />
<br />
=== Alternatives for Windows API functions ===<br />
<br />
Many Windows programs use the Windows API extensively. In cross-platform applications Win API functions in the Windows unit should not be used, or should be enclosed by a conditional compile (e.g. {$IFDEF MSWINDOWS} ).<br />
<br />
Fortunately many of the commonly used Windows API functions are implemented in a multiplatform way in the unit [[lclintf]]. This can be a solution for programs which rely heavily on the Windows API, although the best solution is to replace these calls with true cross-platform components from the LCL. You can replace calls to GDI painting functions with calls to a TCanvas object's methods, for example.<br />
<br />
=== Key codes ===<br />
Fortunately, detecting key codes (e.g. on KeyUp events) is portable: see [[LCL Key Handling]].<br />
<br />
===Installing your application===<br />
See [[Deploying Your Application]].<br />
<br />
== See Also ==<br />
* [[Writing portable code regarding the processor architecture]]<br />
* [[Introduction to platform-sensitive development]]<br />
* http://www.midnightbeach.com/KylixForDelphiProgrammers.html A guide for Windows programmers starting with Kylix. Many of concepts / code snippets apply to Lazarus.<br />
* http://www.stack.nl/~marcov/porting.pdf A guide for writing portable source code, mainly between different compilers.<br />
<br />
[[Category:Tutorials]]<br />
[[Category:Multiplatform Programming]]<br />
[[Category:Platform-sensitive development]]<br />
[[Category:Inter-process communication]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=FPC_Unicode_support&diff=109643FPC Unicode support2017-05-12T10:45:08Z<p>Zoran: fix link</p>
<hr />
<div>{{FPC Unicode support}}<br />
<br />
= Introduction =<br />
Up to and including FPC 2.6.x, the RTL was based on the ones of Turbo Pascal and Delphi 7. This means it was primarily based around the ''shortstring'', ''ansistring'' and ''pchar'' types. None of these types had any encoding information associated with them, but were implicitly assumed to be encoded in the "default system encoding" and were passed on to OS API calls without any conversion.<br />
<br />
In Delphi 2009, Embarcadero switched the entire RTL over to the ''UnicodeString'' type, which represents strings using UTF-16. Additionally, they also made the AnsiString type "code page-aware". This means that AnsiStrings from then on contain the code page according to which their data should be interpreted.<br />
<br />
FPC's language-level support for these string types is already available in current development versions of the compiler (FPC 3.0.0/trunk). The RTL level support is not yet complete. This page gives an overview of the code page-related behaviour of these string types, the current level of support in the RTL, and possible future ways of how this support may be improved.<br />
<br />
= Backward compatibility =<br />
If you have existing code that works in a defined way (*) with a previous version of FPC and make no changes to it, it should continue to work unmodified with the new FPC version. Guaranteeing this is the main purpose of the multitude of Default*CodePage variables and their default values as described below.<br />
<br />
(*) this primarily means: you do not store data in an ansistring that has been encoded using something else than the system's default code page, and subsequently pass this string as-is to an FPC RTL routine. E.g., current Lazarus code is generally fine, as you are supposed to call UTF8ToAnsi() before passing its strings to FPC RTL routines.<br />
<br />
If your existing code did use ansistrings in an unsupported way, namely by storing data in it that is not encoded in the system's default code page and not taking care when interfacing with other code (such as RTL routines), you still may be able to work around most of the issues if this data always uses the same encoding. In that case, you can call [[#DefaultSystemCodePage|SetMultiByteConversionCodePage()]] when starting your program, with as argument the code page of the data that your ansistrings contain. Note that this will also affect the interpretation of all ShortString, AnsiChar and PAnsiChar data.<br />
<br />
= Code pages =<br />
<br />
A code page defines how the individual bytes of a string should be interpreted, i.e., which letter, symbol or other graphic character corresponds to every byte or sequence of bytes.<br />
<br />
== Code page identifiers ==<br />
A code page identifier is always stored as a ''TSystemCodePage'', which is an alias for [[Word]]. The value represents the corresponding code page as defined by [http://msdn.microsoft.com/en-us/library/windows/desktop/dd317756(v=vs.85).aspx Microsoft Windows]. Additionally, there are 3 special code page values:<br />
* CP_ACP: this value represents the currently set "default system code page". See [[#Code page settings]] for more information.<br />
* CP_OEM: this value represents the OEM code page. On Windows platforms this corresponds to the code page used by the console (e.g. cmd.exe windows). On other platforms this value is interpreted the same as CP_ACP.<br />
* CP_NONE: this value indicates that no code page information has been associated with the string data. The result of any operation on a string that has this [[#Dynamic_code_page|dynamic code page]] is undefined. The same holds for any other code page that is not in the above list, but unlike the other invalid code page values, CP_NONE has a special meaning in case it is used as [[#RawByteString|declared code page]].<br />
<br />
Note: code page identifiers are different from codepage names as used in the ''[http://www.freepascal.org/docs-html/prog/progsu87.html {$codepage xxx}]'' directives (which is available in current stable FPC already). Codepage names are the names of individual codepage units exposed by the charset unit, which have names such as ''cp866'' and ''cp1251'' and ''utf8''.<br />
<br />
== Code page settings ==<br />
The system unit contains several global variables that indicate the default code page used for certain operations.<br />
<br />
=== DefaultSystemCodePage ===<br />
* '''Purpose''': determines how CP_ACP is interpreted<br />
* '''Initial value''':<br />
** Windows: The result of the ''GetACP'' OS call, which returns the Windows ANSI code page.<br />
** iOS: CP_ACP if no [http://www.freepascal.org/docs-html/rtl/system/setwidestringmanager.html widestring manager] is installed, otherwise UTF-8<br />
** Unix (excluding iOS): CP_ACP if no [http://www.freepascal.org/docs-html/rtl/system/setwidestringmanager.html widestring manager] is installed, otherwise it is based on the currently set ''LANG'' or ''LC_CTYPE'' environment variables. This is usually UTF-8, but that is not guaranteed to be the case.<br />
** OS/2: Current code page as provided in the first value returned by DosQueryCP and then translated to the code page number used for the same character set under MS Windows (because that is what has been used under Delphi originally and the FPC implementation tries to be compatible to Delphi); it's possible to enforce always using native OS/2 code page numbers in FPC RTL by changing RTLUsesWinCP boolean variable to false (default is true). Note that the code page numbers are largely identical for OS/2 and MS Windows with code pages allowed for current process code page under OS/2 (the code page numbers are different for the so-called ANSI code pages, ISO-8859-x code pages and Mac OS code pages, but neither of these may be used as current process code page under OS/2).<br />
** Other platforms: CP_ACP (these platforms currently do not support multiple code pages, and are hardcoded to use their OS-specific code page in all cases)<br />
* '''Modifications''': you can modify this value by calling ''SetMultiByteConversionCodePage(CodePage: TSystemCodePage)''<br />
* '''Notes''': Since the value of this variable can be changed, it is not a good idea to use its value to determine the real OS "default system code page" (unless you do it at program startup and are certain no other unit has changed it in its initialisation code).<br />
<br />
=== DefaultFileSystemCodePage ===<br />
* '''Purpose''': defines the code page to which file/path names are translated before they are passed to OS API calls, '''''if''''' the RTL uses a single byte OS API for this purpose on the current platform. This code page is also used for intermediate operations on file paths inside the RTL before making OS API calls. This variable does not exist in Delphi, and has been introduced in FPC to make it possible to change the value of ''DefaultSystemCodePage'' without breaking RTL interfaces with the OS file system API calls.<br />
* '''Initial value''':<br />
** Windows: UTF-8, because the RTL uses UTF-16 OS API calls (so no data is lost in intermediate operations).<br />
** OS X and iOS: DefaultSystemCodePage if no [http://www.freepascal.org/docs-html/rtl/system/setwidestringmanager.html widestring manager] is installed, otherwise UTF-8 (as defined by Apple)<br />
** Unix (excluding OS X and iOS): DefaultSystemCodePage, because the encoding of file names is undefined on Unix platforms (it's an untyped array of bytes that can be interpreted in any way; it is not guaranteed to be valid UTF-8)<br />
** OS/2: DefaultSystemCodePage, because OS/2 provides no possibility for specifying a different code page for file I/O operations other than the current process-wide code page.<br />
** Other platforms: same as DefaultSystemCodePage<br />
* '''Modifications''': you can modify this value by calling ''SetMultiByteFileSystemCodePage(CodePage: TSystemCodePage)''; note that under OS/2 this variable is being synchronized with the current process code page (set e.g. by DosSetProcessCP) during all file I/O operations in order to avoid invalid transformations<br />
* '''Notes''': the Unix/OS X/iOS settings only apply in case the ''cwstring'' widestring manager is installed, otherwise DefaultFileSystemCodePage will have the same value as DefaultSystemCodePage after program startup.<br />
<br />
=== DefaultRTLFileSystemCodePage ===<br />
* '''Purpose''': defines the code page to which file/path names are translated before they are returned from RawByteString file/path RTL routines. Examples include the file/path names returned by the RawbyteString versions of ''SysUtils.FindFirst'' and ''System.GetDir''. The main reason for its existence is to enable the RTL to provide backward compatibility with earlier versions of FPC, as these always returned strings encoded in whatever the OS' single byte API used (which was usually what is now known as ''DefaultSystemCodePage'').<br />
* '''Initial value'''<br />
** Windows: DefaultSystemCodePage, for backward compatibility.<br />
** OS X and iOS: DefaultSystemCodePage if no [http://www.freepascal.org/docs-html/rtl/system/setwidestringmanager.html widestring manager] is installed, otherwise UTF-8 for backward compatibility (it was already always UTF-8 in the past, since that's what the OS file APIs return and we did not convert this data).<br />
** Unix (excluding OS X and iOS): DefaultSystemCodePage, for the same reason as with DefaultFileSystemCodePage. Setting this to a different value than DefaultFileSystemCodePage is a bad idea on these platforms, since any code page conversion can corrupt these strings as their initial encoding is unknown.<br />
** OS/2: same as DefaultSystemCodePage (for backward compatibility and also because it's the most natural choice unless you need to play with different code pages)<br />
** Other platforms: same as DefaultSystemCodePage<br />
* '''Modifications''': you can modify this value by calling ''SetMultiByteRTLFileSystemCodePage(CodePage: TSystemCodePage)''; you may use this possibility for reading and/or writing files with an arbitrary code page<br />
* '''Notes''': same as for DefaultFileSystemCodePage.<br />
<br />
== Source file codepage ==<br />
<br />
The ''source file codepage'' determines how [[#String constants|string constants]] are interpreted, and where the compiler will insert codepage conversion operations when [[#Dynamic code page|assigning one string type to another]].<br />
<br />
The source file codepage is determined as follows:<br />
* if a file contains a ''{$codepage xxx}'' directive (e.g. <code>{$codepage UTF8}</code>), then the source file codepage is this codepage, otherwise<br />
* if the file starts with an UTF-8 BOM, then then the source file codepage is UTF-8, otherwise<br />
* if ''{$modeswitch systemcodepage}'' is active, the source file codepage is the ''DefaultSystemCodePage'' '''''of the computer on which the compiler itself is currently running''''' (i.e., compiling the source code on a different system may result in a program that behaves differently; this switch is available for Delphi compatibility and is enabled by default in ''{$mode delphiunicode}''), otherwise<br />
* the source file codepage is set to CP_ACP (for backward compatibility with previous FPC versions)<br />
<br />
= Strings =<br />
<br />
== String/character types ==<br />
<br />
=== Shortstring ===<br />
The code page of a shortstring is implicitly CP_ACP and hence will always be equal to the current value of DefaultSystemCodePage.<br />
<br />
=== PAnsiChar/AnsiChar ===<br />
These types are the same as the old PChar/Char types. In all compiler modes except for ''{$mode delphiunicode}'', PChar/Char are also still aliases for PAnsiChar/AnsiChar. Their code page is implicitly CP_ACP and hence will always be equal to the current value of DefaultSystemCodePage.<br />
<br />
=== PWideChar/PUnicodeChar and WideChar/UnicodeChar ===<br />
These types remain unchanged. WideChar/UnicodeChar can contain a single UTF-16 code unit, while PWideChar/PUnicodeChar point to a single or an array of UTF-16 code units.<br />
<br />
In ''{$mode delphiunicode}'', PChar becomes an alias for PWideChar/PUnicodeChar and Char becomes an alias for WideChar/UnicodeChar.<br />
<br />
=== UnicodeString/WideString ===<br />
These types behave the same as in previous versions:<br />
* ''Widestring'' is the same as a "COM BSTR" on Windows, and an alias for UnicodeString on all other platforms. Its string data is encoded using UTF-16.<br />
* ''UnicodeString'' is a reference-counted string with a maximum length of high(SizeInt) UTF-16 code units.<br />
<br />
=== Ansistring ===<br />
AnsiStrings are reference-counted types with a maximum length of high(SizeInt) bytes. Additionally, they now also have code page information associated with them.<br />
<br />
The most important thing to understand about the new AnsiString type is that it both has a declared/static/preferred/default code page (called ''declared code page'' from now on), and a dynamic code page. The declared code page tells the compiler that when assigning something to that AnsiString, it should first convert the data to that declared code page (except if it is CP_NONE, see [[#RawByteString|RawByteString]] below). The dynamic code page is a property of the AnsiString which, similar to the length and the reference count, defines the actual code page of the data currently held by that AnsiString.<br />
<br />
==== Declared code page ====<br />
The declared code page of an AnsiString can only be defined by declaring a new type as follows:<br />
<syntaxhighlight><br />
type<br />
CP866String = type AnsiString(866); // note the extra "type"<br />
</syntaxhighlight><br />
<br />
The declared code page of a variable declared as plain ''AnsiString'' is CP_ACP. In effect, the AnsiString type is now semantically defined in the System unit as<br />
<syntaxhighlight><br />
type<br />
AnsiString = type AnsiString(CP_ACP);<br />
</syntaxhighlight><br />
<br />
Another predefined AnsiString(X) type in the System unit is UTF8String:<br />
<syntaxhighlight><br />
type<br />
UTF8String = type AnsiString(CP_UTF8);<br />
</syntaxhighlight><br />
<br />
Once you have defined such a custom AnsiString(X) type, you can use it to declare variables, parameters, fields etc as usual.<br />
<br />
Note that CP_UTF16 and CP_UTF16BE are not valid as code pages for AnsiStrings. The result of defining an AnsiString with such a code page is undefined.<br />
<br />
==== Dynamic code page ====<br />
If a string with a declared code page SOURCE_CP is assigned to a string with declared code page DEST_CP , then<br />
* if (SOURCE_CP = CP_NONE) or (DEST_CP = CP_NONE), see [[#RawByteString|RawByteString]], otherwise<br />
* if (source file codepage <> CP_ACP), then if (DEST_CP = CP_ACP) and (SOURCE_CP = source file codepage) or vice versa, no conversion will occur (even if at run time ''DefaultSystemCodePage'' has a different value from the source file code page). The reason for the "(source file codepage <> CP_ACP)" condition is backward compatibility with previous FPC versions (while they did not support AnsiStrings with arbitrary code pages, they did always reinterpret AnsiStrings according to the current value of the system code page). Otherwise,<br />
* if (SOURCE_CP <> DEST_CP), the string data will be converted from codepage X1 to codepage X2 before assignment, whereby CP_ACP will be interpreted as the current value of ''DefaultSystemCodePage''. Otherwise,<br />
* if (SOURCE_CP = DEST_CP), no codepage conversion will be performed.<br />
<br />
These rules mean that it is perfectly possible for an AnsiString variable to get a dynamic code page that differs from its declared code page. E.g. in the third case SOURCE_CP could be CP_ACP, while after the assignment it may have a dynamic code page equal to ''DefaultSystemCodePage''.<br />
<br />
Note: as mentioned above, whether or not a potential code page conversion happens only depends on the ''declared code pages'' of the involved strings. This means that if you assign one AnsiString(X) to another AnsiString(X) and the former's dynamic code was different from X, the string data will ''not'' be converted to code page X by the assignment.<br />
<br />
==== RawByteString ====<br />
The RawByteString type is defined as<br />
<syntaxhighlight><br />
type<br />
RawByteString = type AnsiString(CP_NONE);<br />
</syntaxhighlight><br />
<br />
As mentioned earlier, the results of operations on strings with the CP_NONE code page are undefined. As it does not make sense to define a type in the RTL whose behaviour is undefined, the behaviour of RawByteString is somewhat different than that of other AnsiString(X) types.<br />
<br />
As a first approximation, ''RawByteString'' can be thought of as an "untyped AnsiString": assigning an AnsiString(X) to a RawByteString has exactly the same behaviour as assigning that AnsiString(X) to another AnsiString(X) variable with the same value of X: no code page conversion or copying occurs, just the reference count is increased.<br />
<br />
Less intuitive is probably that when a RawByteString is assigned to an AnsiString(X), the same happens: no code page conversion or copying, just the reference count is increased. Note that this means that results from functions returning a RawByteString will never be converted to the destination's declared code page. This is another way in which the dynamic code page of an AnsiString(X) can become different from its declared code page.<br />
<br />
This type is mainly used to declare ''const'', ''constref'' and value parameters that accept any AnsiString(X) value without converting it to a predefined declared code page. Note that if you do this, the routine accepting those parameters should be able to handle strings with any possible dynamic code page.<br />
<br />
''var'' and ''out'' parameters can also be declared as ''RawByteString'', but in this case the compiler will give an error if an AnsiString(X) whose declared code page is different from CP_NONE is passed in. This is consistent with ''var'' and ''out'' parameters in general: they require an exactly matching type to be passed in. You can add an explicit RawByteString() typecast around an argument to remove this error, but then you must be prepared to deal with the fact that the returned string can have any dynamic code page.<br />
<br />
== String concatenations ==<br />
Normally, in Pascal the result type of an expression is independent of how its result is used afterwards. E.g. multiplying two longints on a 32 bit platform and assigning the result to an int64 will still perform the multiplication using 32 bit arithmetic, and only afterwards the result is converted to 64 bit.<br />
<br />
Code page-aware strings are the only exception to this rule: concatenating two or more strings always occurs without data loss, although afterwards the resulting string will of course still be converted to the declared code page of the destination (which may result in data loss).<br />
<br />
Assigning the result of a concatenation to a RawByteString is again special:<br />
* if all concatenated strings have the same dynamic code page, the result will have this code page too<br />
* in other cases the result will be converted to CP_ACP (we may add an option in the future to change this RawByteString behaviour, as it is not very practical).<br />
<br />
== String constants ==<br />
The compiler has to know the code page according to which it should interpret string constants, as it may have to convert them at compile time. Normally, a string constant is interpreted according to the [[#Source file codepage|source file codepage]]. If the source file codepage is CP_ACP, a default is used instead: in that case, during conversions the constant strings are assumed to have code page 28591 (''ISO 8859-1 Latin 1; Western European'').<br />
<br />
When a string constant is assigned to an AnsiString(X) either in code or as part of a typed constant or variable initialisation, then<br />
* if X = CP_NONE (i.e., the target is a RawByteString), the result is the same as if the constant string were assigned to an AnsiString(CP_ACP)<br />
* if X = CP_ACP and the code page of the string constant is different from CP_ACP, then the string constant is converted, at compile time, to the source file code page. If the source file code page is also CP_ACP, it will be stored in the program unaltered with a code page of CP_ACP and hence its meaning/interpretation will depend on the actual value of ''DefaultSystemCodePage'' at run time. This ensures compatibility with older versions of FPC when assigning string constants to AnsiString variables without using a ''{$codepage xxx}'' directive or UTF-8 BOM.<br />
* for other values of X, the string constant is converted, at compile time, to code page X<br />
<br />
Similarly, if a string constant is assigned to a UnicodeString, the string constant is converted, at compile time, from the source file code page to UTF-16.<br />
<br />
For ShortString and PChar, the same rule as for AnsiString(CP_ACP) is followed.<br />
<br />
Note that symbolic string constants will be converted at compile time to the appropriate string type and code page whenever they are used. This means that there is no speed overhead when using a single string constant in multiple code page and string type contexts, only some data size overhead.<br />
<br />
From the above it follows that to ensure predictable interpretation of string constants in your source code, it is best to either include an explicit ''{$codepage xxx}'' directive (or use the equivalent ''-Fc'' command line option), or to save the source code in UTF-8 with a BOM.<br />
<br />
== String indexing ==<br />
Nothing changes to string indexing. Every string element of a UnicodeString/WideString is two bytes and every string element of all other strings is one byte. The string indexing mechanism completely ignores code pages and composite code points.<br />
<br />
= RTL changes =<br />
<br />
In order to fully guarantee data integrity in the presence of codepage-aware strings, all routines in the RTL and packages that accept ''AnsiString'' parameters must be adapted. The reason is that if their parameters remain plain ''AnsiString'', then any string with a different declared code page will be converted to ''DefaultSystemCodePage'' when it is passed in. This can result in data loss.<br />
<br />
Until now, primarily routines dealing with file system access have been updated to preserve all character data. Below is an exhaustive list of all routines that preserve the string encoding in FPC 3.0. Unless where explicitly noted otherwise, these routines also all have overloads that accept ''UnicodeString'' parameters.<br />
* '''System''': FExpand, LowerCase, UpperCase, GetDir, MKDir, ChDir, RMDir, Assign, Erase, Rename, standard I/O (Read/Write/Readln/Writeln/Readstr/Writestr), Insert, Copy, Delete, SetString<br />
* '''ObjPas''' (used automatically in Delphi and ObjFPC modes): AssignFile<br />
* '''SysUtils''': FileCreate, FileOpen, FileExists, DirectoryExists, FileSetDate, FileGetAttr, FileSetAttr, DeleteFile, RenameFile, FileSearch, ExeSearch, FindFirst, FindNext, FindClose, FileIsReadOnly, GetCurrentDir, SetCurrentDir, ChangeFileExt, ExtractFilePath, ExtractFileDrive, ExtractFileName, ExtractFileExt, ExtractFileDir, ExtractShortPathName, ExpandFileName, ExpandFileNameCase, ExpandUNCFileName, ExtractRelativepath, IncludeTrailingPathDelimiter, IncludeTrailingBackslash, ExcludeTrailingBackslash, ExcludeTrailingPathDelimiter, IncludeLeadingPathDelimiter, ExcludeLeadingPathDelimiter, IsPathDelimiter, DoDirSeparators, SetDirSeparators, GetDirs, ConcatPaths, GetEnvironmentVariable<br />
* '''Unix''': fp*() routines related to file system operations (no ''UnicodeString'' overloads), POpen<br />
* '''DynLibs''': all routines<br />
<br />
= RTL todos =<br />
As the above list is exhaustive, no other RTL routines support arbitrary code pages yet. This section contains a list of gotchas that some people have identified and, if possible, workarounds. Note that routines not mentioned here nor above are equally unsafe as the ones that are explicitly mentioned.<br />
<br />
==TFormatSettings and DefaultFormatSettings==<br />
The type of ''ThousandSeparator'' and ''DecimalSeparator'' is AnsiChar type. This means that if ''DefaultSystemCodePage'' is UTF-8 and the locale's separator is more than one byte long in that encoding, these fields are not large enough. Examples are the French and Russian non-breaking white space character used to represent the ''ThousandSeparator''.<br />
<br />
= Old/obsolete sections=<br />
{{Warning|These sections are kept for historical reference - please update the sections above with this information if it is still applicable. Since FPC 2.7 (current development version), extensive Unicode support has been implemented.}}<br />
<br />
<br />
==User visible changes==<br />
<br />
Full support of code page aware strings is not possible without breaking some existing code. The following list tries to summarize the most important user visible changes.<br />
* The string header has two new fields: encoding and element size. On 32 Bit platforms this increases the header size by 4 and on 64 bit platforms by 8 bytes.<br />
* WideCharLenToString, UnicodeCharLenToString, WideCharToString, UnicodeCharToString and OleStrToString return an UnicodeString instead of an Ansistring before.<br />
* the type of the dest parameter of WideCharLenToString and UnicodeCharLenToString has been changed from Ansistring to Unicodestring<br />
* UTF8ToAnsi and AnsiToUTF8 take a RawByteString now<br />
<br />
= See Also =<br />
<br />
* [[Character and string types]]<br />
* [[unicode use cases]]<br />
* [[LCL Unicode Support]]<br />
* Suggestion: [[not Delphi compatible enhancement for Unicode Support]]<br />
<br />
[[Category:Unicode]]<br />
[[Category:FPC]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=Multiplatform_Programming_Guide&diff=109634Multiplatform Programming Guide2017-05-12T07:59:15Z<p>Zoran: /* 32/64 bit */</p>
<hr />
<div>{{Multiplatform Programming Guide}}<br />
<br />
Most [[LCL]] applications work in a cross-platform way without any extra effort.<br />
<br />
This is a tutorial on writing cross-platform applications with Lazarus and Free Pascal. It will cover the necessary precautions to aid in creating a cross-platform ready program that is ready to [[Deploying Your Application|deploy]].<br />
<br />
__TOC__<br />
<br />
== Introduction to Multiplatform (Cross-platform) Programming ==<br />
<br />
=== How many platforms do you need? ===<br />
<br />
To answer this question, you should first determine who your potential users are and how your program will be used. This question depends on where you are deploying your application.<br />
<br />
If you are developing generic desktop software in 2014, Microsoft Windows may be the most important platform. Note that Mac OS X and/or Linux are making gaining in popularity, and may be a significant target for your application.<br />
<br />
The popularity of the various desktop operating systems differs by country, by the type of software used, and with the target audience; there's no general rule. For example, Mac OS X is quite popular in North America and western Europe, while in South America Macs are mostly restricted to video and sound work.<br />
<br />
On many contract projects, only one platform is relevant. Free Pascal and Lazarus are quite capable of writing software targeted at a specific platform. You can, for example, access the full Windows API to write a well integrated Windows program.<br />
<br />
If you're developing software that will run on a Web server, a Unix platform in one of its various flavors is commonly used. In this case, perhaps only Linux, Solaris, *BSD and other Unixes make sense as your target platforms, although you may want to add support for Windows for completeness.<br />
<br />
Once you've addressed any cross-platform issues in your design, you can largely ignore the other platforms, much as you would when developing for a single platform. However, at some point you'll need to test deploying and running your program on the other platforms. For that, it will be helpful to have unrestricted access to machines running the target operating systems. If you don't want multiple physical computers, investigate dual-booting or VM solutions like VMware or Parallels.<br />
<br />
== Cross-platform Programming ==<br />
<br />
=== Working with files and folders ===<br />
<br />
When working with files and folders, this is important to use non-platform specific path delimiters and [[End_of_Line|line ending]] sequences. Here is a list of declared [[Constant|constants]] in Lazarus to be used when working with files and folders.<br />
<br />
* '''PathSep''', '''PathSeparator''': path separator when adding many paths together (';', ...)<br />
* '''PathDelim''', '''DirectorySeparator''': directory separator for each platform ('/', '\', ...)<br />
* '''LineEnding''': proper line ending character sequence (#13#10 - CRLF, #10 - [[Line_feed|LF]], ...)<br />
<br />
Another important thing to be noted is the case sensitiveness of the file system.<br />
On Windows filenames are usually not case sensitive, while they usually are on Linux and BSD platforms. But if a EXT2, EXT3, etc file system is mounted on Windows, it would be case-sensitive. Respectively a FAT file system mounted on Linux should not be case sensitive.<br />
<br />
It shall be paid special attention, that NTFS is non-case sensitive when used in Windows, but it is case sensitive when mounted by POSIX OSes. This could cause '''various problems, including loss of files''' if files with same filenames in different cases exist on a NTFS partition, mounted in Windows. Using custom functions for checking and preventing creation of several files with the same names on NTFS should be considered by the developers.<br />
<br />
Mac OS X use case insensitive filenames by default. This can be the cause of annoying bugs, so any portable application should use consistently filenames.<br />
<br />
The RTL file functions use the system encoding for file names. Under Windows this is one of the windows code pages, while Linux, BSD and Mac OS X usually use UTF-8. The unit '''FileUtil''' of the LCL provides file functions which takes UTF-8 strings like the rest of the LCL.<br />
<br />
<syntaxhighlight>// AnsiToUTF8 and UTF8ToAnsi need a widestring manager under Linux, BSD, MacOSX<br />
// but normally these OS use UTF-8 as system encoding so the widestringmanager<br />
// is not needed.<br />
function NeedRTLAnsi: boolean;// true if system encoding is not UTF-8<br />
procedure SetNeedRTLAnsi(NewValue: boolean);<br />
function UTF8ToSys(const s: string): string;// as UTF8ToAnsi but more independent of widestringmanager<br />
function SysToUTF8(const s: string): string;// as AnsiToUTF8 but more independent of widestringmanager<br />
function UTF8ToConsole(const s: string): string;// converts UTF8 string to console encoding (used by Write, WriteLn)<br />
<br />
// file operations<br />
function FileExistsUTF8(const Filename: string): boolean;<br />
function FileAgeUTF8(const FileName: string): Longint;<br />
function DirectoryExistsUTF8(const Directory: string): Boolean;<br />
function ExpandFileNameUTF8(const FileName: string): string;<br />
function ExpandUNCFileNameUTF8(const FileName: string): string;<br />
function ExtractShortPathNameUTF8(Const FileName : String) : String;<br />
function FindFirstUTF8(const Path: string; Attr: Longint; out Rslt: TSearchRec): Longint;<br />
function FindNextUTF8(var Rslt: TSearchRec): Longint;<br />
procedure FindCloseUTF8(var F: TSearchrec);<br />
function FileSetDateUTF8(const FileName: String; Age: Longint): Longint;<br />
function FileGetAttrUTF8(const FileName: String): Longint;<br />
function FileSetAttrUTF8(const Filename: String; Attr: longint): Longint;<br />
function DeleteFileUTF8(const FileName: String): Boolean;<br />
function RenameFileUTF8(const OldName, NewName: String): Boolean;<br />
function FileSearchUTF8(const Name, DirList : String): String;<br />
function FileIsReadOnlyUTF8(const FileName: String): Boolean;<br />
function GetCurrentDirUTF8: String;<br />
function SetCurrentDirUTF8(const NewDir: String): Boolean;<br />
function CreateDirUTF8(const NewDir: String): Boolean;<br />
function RemoveDirUTF8(const Dir: String): Boolean;<br />
function ForceDirectoriesUTF8(const Dir: string): Boolean;<br />
<br />
// environment<br />
function ParamStrUTF8(Param: Integer): string;<br />
function GetEnvironmentStringUTF8(Index: Integer): string;<br />
function GetEnvironmentVariableUTF8(const EnvVar: string): String;<br />
function GetAppConfigDirUTF8(Global: Boolean): string;<br />
<br />
// other<br />
function SysErrorMessageUTF8(ErrorCode: Integer): String;</syntaxhighlight><br />
<br />
===Empty file names and double path delimiters===<br />
<br />
There are differences in file/directory name handling in Windows versus Linux/Unix/Unix like systems.<br />
<br />
* Windows allows empty file names. That's why FileExistsUTF8('..\') checks under Windows in the parent directory for a file without name.<br />
* On Linux/Unix/Unix-like systems an empty file is mapped to the directory and directories are treated as files. This means that FileExistsUTF8('../') under Unix checks for the existence of the parent directory, which normally results true.<br />
<br />
Double path delimiters in file names are also treated differently: <br />
* Windows: 'C:\' is not the same as 'C:\\'<br />
* Unix like OS: the path '/usr//' is the same as '/usr/'. If '/usr' is a directory then even all three are the same. <br />
<br />
This is important when concatenating file names. For example:<br />
<br />
<syntaxhighlight>FullFilename:=FilePath+PathDelim+ShortFilename; // can result in two PathDelims which gives different results under Windows and Linux<br />
FullFilename:=AppendPathDelim(FilePath)+ShortFilename); // creates only one PathDelim<br />
FullFilename:=TrimFilename(FilePath+PathDelim+ShortFilename); // creates only one PathDelim and do some more clean up</syntaxhighlight><br />
<br />
The function TrimFilename replaces double path delimiters with single ones and shorten '..' paths. For example /usr//lib/../src is trimmed to /usr/src.<br />
<br />
If you want to know if a directory exists use '''DirectoryExistsUTF8'''.<br />
<br />
Another common task is to check if the path part of a file name exists. You can get the path with ExtractFilePath, but this will contain the path delimiter. <br />
* Under Unix like system you can simply use FileExistsUTF8 on the path. For example FileExistsUTF8('/home/user/') will return true if the directory /home/user exists. <br />
* Under Windows you must use the DirectoryExistsUTF8 function, but before that you must delete the path delimiter, for example with the ChompPathDelim function. <br />
<br />
Under Unix like systems the root directory is '/' and using the ChompPathDelim function will create an empty string. The function DirPathExists works like the DirectoryExistsUTF8 function, but trims the given path.<br />
<br />
Note that Unix/Linux uses the '~' (tilde) symbol to stand for the home directory, typically '/home/jim/' for a user called jim. So '~/myapp/myfile' and '/home/jim/myapp/myfile' are identical on the command line and in scripts. However, the tilde is not automatically expanded by Lazarus. It is necessary to use ExpandFileNameUTF8('~/myapp/myfile') to get the full path.<br />
<br />
=== Text encoding ===<br />
<br />
Text files are often encoded in the current system encoding. Under Windows this is usually one of the windows code pages, while Linux, BSD, and Mac OS X usually use UTF-8. <br />
There is no 100% rule to find out which encoding a text file uses. The LCL unit '''lconvencoding''' has a function to guess the encoding:<br />
<br />
<syntaxhighlight>function GuessEncoding(const s: string): string;<br />
function GetDefaultTextEncoding: string;</syntaxhighlight><br />
<br />
And it contains functions to convert from one encoding to another:<br />
<br />
<syntaxhighlight>function ConvertEncoding(const s, FromEncoding, ToEncoding: string): string;<br />
<br />
function UTF8BOMToUTF8(const s: string): string; // UTF8 with BOM<br />
function ISO_8859_1ToUTF8(const s: string): string; // central europe<br />
function CP1250ToUTF8(const s: string): string; // central europe<br />
function CP1251ToUTF8(const s: string): string; // cyrillic<br />
function CP1252ToUTF8(const s: string): string; // latin 1<br />
...<br />
function UTF8ToUTF8BOM(const s: string): string; // UTF8 with BOM<br />
function UTF8ToISO_8859_1(const s: string): string; // central europe<br />
function UTF8ToCP1250(const s: string): string; // central europe<br />
function UTF8ToCP1251(const s: string): string; // cyrillic<br />
function UTF8ToCP1252(const s: string): string; // latin 1<br />
...</syntaxhighlight><br />
<br />
For example to load a text file and convert it to UTF-8 you can use:<br />
<br />
<syntaxhighlight>var<br />
sl: TStringList;<br />
OriginalText: String;<br />
TextAsUTF8: String;<br />
begin<br />
sl:=TStringList.Create;<br />
try<br />
sl.LoadFromFile('sometext.txt'); // beware: this changes line endings to system line endings<br />
OriginalText:=sl.Text;<br />
TextAsUTF8:=ConvertEncoding(OriginalText,GuessEncoding(OriginalText),EncodingUTF8);<br />
...<br />
finally<br />
sl.Free;<br />
end;<br />
end;</syntaxhighlight><br />
<br />
And to save a text file in the system encoding you can use:<br />
<syntaxhighlight>sl.Text:=ConvertEncoding(TextAsUTF8,EncodingUTF8,GetDefaultTextEncoding);<br />
sl.SaveToFile('sometext.txt');</syntaxhighlight><br />
<br />
=== Configuration files ===<br />
<br />
You can use the [[doc:rtl/sysutils/getappconfigdir.html|GetAppConfigDir]] function from SysUtils unit to get a suitable place to store configuration files on different system. The function has one parameter, called Global. If it is True then the directory returned is a global directory, i.e. valid for all users on the system. If the parameter Global is false, then the directory is specific for the user who is executing the program. On systems that do not support multi-user environments, these two directories may be the same.<br />
<br />
There is also the [[doc:rtl/sysutils/getappconfigfile.html|GetAppConfigFile]] which will return an appropriate name for an application configuration file. You can use it like this:<br />
<br />
ConfigFilePath := GetAppConfigFile(False);<br />
<br />
Below are examples of the output of default path functions on different systems:<br />
<br />
<syntaxhighlight>program project1;<br />
<br />
{$mode objfpc}{$H+}<br />
<br />
uses<br />
SysUtils;<br />
<br />
begin<br />
WriteLn(GetAppConfigDir(True));<br />
WriteLn(GetAppConfigDir(False));<br />
WriteLn(GetAppConfigFile(True));<br />
WriteLn(GetAppConfigFile(False));<br />
end.</syntaxhighlight><br />
<br />
The output on a GNU/Linux system with FPC 2.2.2. Note that using True is buggy, already fixed in 2.2.3:<br />
<br />
<pre>/etc/project1/<br />
/home/user/.config/project1/<br />
/etc/project1.cfg<br />
/home/user/.config/project1.cfg</pre><br />
<br />
You can notice that global configuration files are stored on the /etc directory and local configurations are stored on a hidden folder on the user's home directory. Directories whose name begin with a dot (.) are hidden on Linux. You can create a directory on the location returned by GetAppConfigDir and then store configuration files there.<br />
<br />
{{Note| Normal users are not allowed to write to the /etc directory. Only users with administration rights can do this.}}<br />
<br />
The output on recent versions of Windows with FPC 3.0.0 + :<br />
<br />
<pre>C:\ProgramData\project1\<br />
C:\Users\user\AppData\Local\project1\<br />
C:\ProgramData\project1\project1.cfg<br />
C:\Users\user\AppData\Local\project1\project1.cfg</pre><br />
<br />
Notice that before FPC 2.2.4 the function was using the directory where the application was to store global configurations on Windows.<br />
<br />
The output on Windows 98 with FPC 2.2.0:<br />
<br />
<pre>C:\Program Files\PROJECT1<br />
C:\Windows\Local Settings\Application Data\PROJECT1<br />
C:\Program Files\PROJECT1\PROJECT1.cfg<br />
C:\Windows\Local Settings\Application Data\PROJECT1\PROJECT1.cfg</pre><br />
<br />
The output on Mac OS X with FPC 2.2.0:<br />
<br />
<pre>/etc<br />
/Users/user/.config/project1<br />
/etc/project1.cfg<br />
/Users/user/.config/project1/project1.cfg</pre><br />
<br />
{{Note| The use of UPX interferes with the use of the GetAppConfigDir and GetAppConfigFile functions.}}<br />
<br />
{{Note| Under Mac OS X, in most cases config files are preference files, which should be XML files with the ending ".plist" and be stored in /Library/Preferences or ~/Library/Preferences with Names taken from the field "Bundle identifier" in the Info.plist of the application bundle. Using the Carbon calls CFPreference... is probably the easiest way to achieve this. .config files in the User directory are a violation of the programming guide lines.}}<br />
<br />
=== Data and resource files ===<br />
<br />
A very common question is where to store data files an application might need, such as Images, Music, XML files, database files, help files, etc. Unfortunately there is no cross-platform function to get the best location to look for data files. The solution is to implement differently on each platform using IFDEFs.<br />
<br />
==== Windows ====<br />
On Windows, application data that the program modifies should not be put in the application's directory (e.g. C:\Program Files\) but in a specific location (see e.g. [http://support.microsoft.com/kb/310294], under "Classify Application Data"). Windows Vista and newer actively enforce this (users only have write access to these directories when using elevation or disabling UAC) but uses a folder redirection mechanism to accommodate older, wrongly programmed applications. Just reading, not writing, data from application directories would still work.<br />
<br />
In short: use such folder:<br />
<syntaxhighlight><br />
OpDirLocal:= GetEnvironmentVariableUTF8('appdata')+'\MyAppName';<br />
</syntaxhighlight><br />
<br />
<br />
See [[Windows_Programming_Tips#Getting_special_folders_.28My_documents.2C_Desktop.2C_local_application_data.2C_etc.29]]<br />
<br />
==== Unix/Linux ====<br />
On most Unixes (like Linux, FreeBSD, OpenBSD, Solaris), application data files are located in a fixed location, which can be something like: /usr/share/app_name or /opt/app_name.<br />
<br />
Application data that needs to be written to by the application often gets stored in places like /var/<programname>, with appropriate permissions set.<br />
<br />
User-specific read/write config/data will normally be stored somewhere under the user's home directory (e.g. in ~/.myfancyprogram).<br />
<br />
How to get this home dir path:<br />
<syntaxhighlight><br />
OpDirLocal:= GetEnvironmentVariableUTF8('HOME')+'/.myappname';<br />
</syntaxhighlight><br />
<br />
==== OS X ====<br />
macOS (Mac OS X) is an exception among UNIXes. Application is published in a bundle - directory with "app" extension, which is treated by file-manager as a file (you can also do "cd path/myapp.app"). Your resource files should be located inside the bundle. If bundle is "path/MyApp.app", then:<br />
<br />
* executable file is "path/MyApp.app/Contents/MacOS/myapp"<br />
* resources dir is "path/MyApp.app/Contents/Resources"<br />
<br />
Save config files to the home dir:<br />
<br />
<syntaxhighlight><br />
OpDirLocal:= GetEnvironmentVariableUTF8('HOME')+'/.myappname';<br />
</syntaxhighlight><br />
<br />
Read resources from:<br />
<br />
<syntaxhighlight><br />
OpDirRes:= ExtractFileDir(ExtractFileDir(Application.ExeName))+'/Resources';<br />
</syntaxhighlight><br />
<br />
Warning: '''never''' use paramstr(0) on any Unix platform to determine the location of the executable, as this is a Dos-Windows-OS/2 convention and has several conceptual problems, which cannot be solved using emulation on other platforms. The only thing paramstr(0) is guaranteed to return on Unix platforms is the name using which the program was started. The directory in which it is located and the name of the actual binary (in case it was started using a symbolic link) are not guaranteed to be available via paramstr(0).<br />
<br />
==== Example code ====<br />
<br />
Code: [[Cross-platform resources path]]<br />
<br />
=== 32/64 bit ===<br />
<br />
====Detecting bitness at runtime====<br />
While you can control whether you compile for 32 or 64 bit with compiler defines, sometimes you want to know what bitness the operating system runs.<br />
For example, if you are running a 32 bit Lazarus program on 64 bit Windows, you might want to run an external program in a 32 bit program files directory, or you might want to give different information to users: I need this in my LazUpdater Lazarus installer to offer the user a choice of 32 and 64 bit compilers. Code: [[Detect Windows x32-x64 example]].<br />
<br />
====Detecting bitness of external library before loading it====<br />
When you want to load functions from dynamic library into your program, it has to have same bitness as your application. On 64 bit Windows, your application might be 32-bit or 64-bit, and there can be 32-bit and 64-bit libraries on your system.<br />
So you might want to check whether dll's bitness is same as your application's bitness before loading the dll dynamically. Here is a function which tests dll's bitness ([http://forum.lazarus.freepascal.org/index.php/topic,36834.msg245859.html#msg245859 contributed in forum by GetMem]):<br />
<syntaxhighlight><br />
<br />
uses {..., } JwaWindows;<br />
<br />
function GetPEType(const APath: WideString): Byte;<br />
const<br />
PE_UNKNOWN = 0; //if the file is not a valid dll, 0 is returned<br />
PE_16BIT = 1;<br />
PE_32BIT = 2;<br />
PE_64BIT = 3;<br />
var<br />
hFile, hFileMap: THandle;<br />
PMapView: Pointer;<br />
PIDH: PImageDosHeader;<br />
PINTH: PImageNtHeaders;<br />
Base: LongWord;<br />
begin<br />
Result := PE_UNKNOWN;<br />
<br />
hFile := CreateFileW(PWideChar(APath), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);<br />
if hFile = INVALID_HANDLE_VALUE then<br />
begin<br />
CloseHandle(hFile);<br />
Exit;<br />
end;<br />
<br />
hFileMap := CreateFileMapping(hFile, nil, PAGE_READONLY, 0, 0, nil);<br />
if hFileMap = 0 then<br />
begin<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
Exit;<br />
end;<br />
<br />
PMapView := MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);<br />
if PMapView = nil then<br />
begin<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
Exit;<br />
end;<br />
<br />
PIDH := PImageDosHeader(PMapView);<br />
if PIDH^.e_magic <> IMAGE_DOS_SIGNATURE then<br />
begin<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
UnmapViewOfFile(PMapView);<br />
Exit;<br />
end;<br />
<br />
Base := LongWord(PIDH);<br />
PINTH := PIMAGENTHEADERS(Base + LongWord(PIDH^.e_lfanew));<br />
if PINTH^.Signature <> IMAGE_NT_SIGNATURE then<br />
begin<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
UnmapViewOfFile(PMapView);<br />
Exit;<br />
end;<br />
<br />
if PINTH^.Signature = $4550 then<br />
begin<br />
case PINTH^.OptionalHeader.Magic of<br />
$10b: Result := PE_32BIT;<br />
$20b: Result := PE_64BIT<br />
end;<br />
end<br />
else<br />
Result := PE_16BIT;<br />
<br />
CloseHandle(hFile);<br />
CloseHandle(hFileMap);<br />
UnmapViewOfFile(PMapView);<br />
end;<br />
<br />
//Now, if you compile your application for 32-bit and 64-bit windows, you can check if dll's bitness is same as your application's:<br />
function IsCorrectBitness(const APath: WideString): Boolean;<br />
begin <br />
{$ifdef CPU16}<br />
Result := GetPEType(APath) = 1;<br />
{$endif}<br />
{$ifdef CPU32}<br />
Result := GetPEType(APath) = 2; //the application is compiled as 32-bit, we ask if GetPeType returns 2<br />
{$endif}<br />
{$ifdef CPU64}<br />
Result := GetPEType(APath) = 3; //the application is compiled as 64-bit, we ask if GetPeType returns 3<br />
{$endif}<br />
end;<br />
</syntaxhighlight><br />
<br />
==== Pointer / Integer Typecasts ====<br />
<br />
Pointers under 64bit need 8 bytes instead of 4 on 32bit. The 'Integer' type remains 32bit on all platforms for compatibility. This means you can not typecast pointers into integers and back. <br />
<br />
FPC defines two types for this: PtrInt and PtrUInt. PtrInt is a 32bit signed integer on 32 bit platforms and a 64bit signed integer on 64bit platforms. The same for PtrUInt, but ''unsigned'' integer instead.<br />
<br />
Use for code that should work with Delphi and FPC:<br />
{$IFNDEF FPC}<br />
type<br />
PtrInt = integer;<br />
PtrUInt = cardinal;<br />
{$ENDIF}<br />
<br />
Replace all '''integer(SomePointerOrObject)''' with '''PtrInt(SomePointerOrObject)'''.<br />
<br />
=== Endianess ===<br />
<br />
Intel platforms are little endian, that means the least significant byte comes first. For example the two bytes of a word $1234 is stored as $34 $12 on little endian systems.<br />
On big endian systems like the powerpc the two bytes of a word $1234 are stored as $12 $34. The difference is important when reading files created on other systems.<br />
<br />
Use for code that should work on both:<br />
<syntaxhighlight>{$IFDEF ENDIAN_BIG}<br />
...<br />
{$ELSE}<br />
...<br />
{$ENDIF}</syntaxhighlight><br />
<br />
The opposite is ENDIAN_LITTLE.<br />
<br />
The system unit provides plenty of endian converting functions, like SwapEndian, BEtoN (big endian to current endian), LEtoN (little endian to current endian), NtoBE (current endian to big endian) and NtoLE (current endian to little endian).<br />
<br />
<br />
==== Libc and other special units ====<br />
<br />
Avoid legacy units like "oldlinux" and "libc" that are not supported outside of linux/i386.<br />
<br />
==== Assembler ====<br />
<br />
Avoid assembler.<br />
<br />
==== Compiler defines ====<br />
<br />
<syntaxhighlight>{$ifdef CPU32}<br />
...write here code for 32 bit processors<br />
{$ENDIF}<br />
{$ifdef CPU64}<br />
...write here code for 64 bit processors<br />
{$ENDIF}</syntaxhighlight><br />
<br />
=== Projects, packages and search paths ===<br />
<br />
Lazarus projects and packages are designed for multi platforms. Normally you can simply copy the project and the required packages to another machine and compile them there. You don't need to create one project per platform.<br />
<br />
Some advice to achieve this<br />
<br />
The compiler creates for every unit a ppu with the same name. This ppu can be used by other projects and packages. The unit source files (e.g. unit1.pas) should not be shared. Simply give the compiler a unit output directory where to create the ppu files. The IDE does that by default, so nothing to do for you here.<br />
<br />
Every unit file must be part of '''one''' project or package. If a unit file is only used by a single project, add it to this project. Otherwise add it to a package. If you have not yet created a package for your shared units, see here: [[Lazarus_Packages#Creating a package for your common units|Creating a package for your common units]]<br />
<br />
Every project and every package should have '''disjunct directories''' - they should not share directories. Otherwise you must be an expert in the art of compiler search paths. If you are not an expert or if others who may use your project/package are not experts: do not share directories between projects/packages.<br />
<br />
==== Platform specific units ====<br />
For example the unit wintricks.pas should only be used under Windows. In the uses section use:<br />
<br />
<syntaxhighlight>uses<br />
Classes, SysUtils<br />
{$IFDEF Windows}<br />
,WinTricks<br />
{$ENDIF}<br />
;</syntaxhighlight><br />
<br />
If the unit is part of a package, you must also select the unit in the package editor of the package and disable the ''Use unit'' checkbox.<br />
<br />
See also [[Lazarus_Packages#Platform_specific_units|Platform specific units]]<br />
<br />
==== Platform specific search paths ====<br />
<br />
When you target several platforms and access the operating system directly, then you will quickly get tired of endless IFDEF constructions. One solution that is used often in the FPC and Lazarus sources is to use include files. Create one sub directory per target. For example win32, linux, bsd, darwin. Put into each directory an include file with the same name. Then use a macro in the include path. The unit can use a normal include directive. <br />
An example for one include file for each LCL widget set:<br />
<br />
Create one file for each widget set you want to support:<br />
win32/example.inc<br />
gtk/example.inc<br />
gtk2/example.inc<br />
carbon/example.inc<br />
<br />
You do not need to add the files to the package or project.<br />
Add the include search path ''$(LCLWidgetType)'' to the compiler options of your package or project.<br />
<br />
In your unit use the directive:<br />
{$I example.inc}<br />
<br />
Here are some useful macros and common values:<br />
*LCLWidgetType: win32, gtk, gtk2, qt, carbon, fpgui, nogui<br />
*TargetOS: linux, win32, win64, wince, freebsd, netbsd, openbsd, darwin (many more)<br />
*TargetCPU: i386, x86_64, arm, powerpc, sparc<br />
*SrcOS: win, unix<br />
<br />
You can use the $Env() macro to use environment variables.<br />
<br />
And of course you can use combinations. For example the LCL uses: <br />
<br />
$(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS);$(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS)/$(LCLWidgetType)<br />
<br />
See here the complete list of macros: [[IDE Macros in paths and filenames]]<br />
<br />
==== Machine / User specific search paths ====<br />
<br />
For example you have two windows machines stan and oliver. On stan your units are in ''C:\units'' and on oliver your units are in ''D:\path''. The units belong to the package ''SharedStuff'' which is ''C:\units\sharedstuff.lpk'' on stan and ''D:\path\sharedstuff.lpk'' on oliver.<br />
Once you opened the lpk in the IDE or by lazbuild, the path is automatically stored in its configuration files (packagefiles.xml).<br />
When compiling a project that requires the package ''SharedStuff'', the IDE and lazbuild knows where it is. So no configuration is needed.<br />
<br />
If you have want to deploy a package over many machine or for all users of a machine (e.g. a pool for students), then you can add a lpl file in the lazarus source directory. See packager/globallinks for examples.<br />
<br />
=== Locale differences ===<br />
<br />
Some functions from Free Pascal, like StrToFloat behave differently depending on the current [locale]]. For example, in the USA the [[DecimalSeparator|decimal separator]] is usually ".", but in many European and South American countries it is ",". This can be a problem as sometimes it is desired to have these functions behave in a fixed way, independently from the locale. <br />
An example is a file format with decimal points that always needs to be interpreted the same way.<br />
<br />
The next sections explain how to do that.<br />
<br />
<br />
====StrToFloat====<br />
<br />
A new set of format settings which set a fixed decimal separator can be created with the following code:<br />
<br />
<syntaxhighlight><br />
// in your .lpr project file<br />
uses<br />
...<br />
{$IFDEF UNIX}<br />
clocale <br />
{ required on Linux/Unix for formatsettings support. Should be one of the first (probably after cthreads?}<br />
{$ENDIF}<br />
</syntaxhighlight><br />
<br />
and:<br />
<br />
<syntaxhighlight><br />
// in your code:<br />
var<br />
FPointSeparator, FCommaSeparator: TFormatSettings;<br />
begin<br />
// Format settings to convert a string to a float<br />
FPointSeparator := DefaultFormatSettings;<br />
FPointSeparator.DecimalSeparator := '.';<br />
FPointSeparator.ThousandSeparator := '#';// disable the thousand separator<br />
FCommaSeparator := DefaultFormatSettings;<br />
FCommaSeparator.DecimalSeparator := ',';<br />
FCommaSeparator.ThousandSeparator := '#';// disable the thousand separator</syntaxhighlight><br />
<br />
Later on you can use this format settings when calling StrToFloat, like this:<br />
<br />
<syntaxhighlight>// This function works like StrToFloat, but simply tries two possible decimal separator<br />
// This will avoid an exception when the string format doesn't match the locale<br />
function AnSemantico.StringToFloat(AStr: string): Double;<br />
begin<br />
if Pos('.', AStr) > 0 then Result := StrToFloat(AStr, FPointSeparator)<br />
else Result := StrToFloat(AStr, FCommaSeparator);<br />
end;</syntaxhighlight><br />
<br />
=== Gtk2 and masking FPU exceptions ===<br />
<br />
Gtk2 library changes the default value of FPU (floating point unit) exception mask. The consequence of this is that some floating point exceptions do not get raised if Gtk2 library is used by the application. That means that, if for example you develop a LCL application on Windows with win32/64 widgetset (which is Windows default) and plan to compile for Linux (where Gtk2 is default widgetset), you should keep this incompatibilities in mind.<br />
<br />
After [http://www.lazarus.freepascal.org/index.php/topic,13460.0.html this forum topic] and answers on [http://bugs.freepascal.org/view.php?id=19674 this bug report] it became clear that nothing can be done about this, so we must know what actually these differences are.<br />
<br />
Therefore, let's do a test:<br />
<br />
<syntaxhighlight><br />
uses<br />
..., math,...<br />
<br />
{...}<br />
<br />
var<br />
FPUException: TFPUException;<br />
FPUExceptionMask: TFPUExceptionMask;<br />
begin<br />
FPUExceptionMask := GetExceptionMask;<br />
for FPUException := Low(TFPUException) to High(TFPUException) do begin<br />
write(FPUException, ' - ');<br />
if not (FPUException in FPUExceptionMask) then<br />
write('not ');<br />
<br />
writeln('masked!');<br />
end;<br />
readln;<br />
end.<br />
</syntaxhighlight><br />
<br />
Our simple program will get what FPC default is:<br />
<br />
<code><br />
exInvalidOp - not masked!<br />
exDenormalized - masked!<br />
exZeroDivide - not masked!<br />
exOverflow - not masked!<br />
exUnderflow - masked!<br />
exPrecision - masked!<br />
</code><br />
<br />
However, with Gtk2, only exOverflow is not masked.<br />
<br />
The consequence is that EInvalidOp and EZeroDivide exceptions do not get raised if the application links to Gtk2 library! Normally, dividing non-zero value by zero raises EZeroDivide exception and dividing zero by zero raises EInvalidOp. For example the code like this:<br />
<br />
<syntaxhighlight><br />
var<br />
X, A, B: Double;<br />
// ...<br />
<br />
try<br />
X := A / B;<br />
// code block 1<br />
except <br />
// code block 2<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
will take different direction when compiled in application with Gtk2 widgetset. On win widgetset, when B equals zero, an exception will get raised (EZeroDivide or EInvalidOp, depending on whether A is zero) and "code block 2" will be executed. On Gtk2 X becomes [http://www.freepascal.org/docs-html/rtl/math/infinity.html Infinity], [http://www.freepascal.org/docs-html/rtl/math/neginfinity.html NegInfinity], or [http://www.freepascal.org/docs-html/rtl/math/nan.html NaN] and "code block 1" will be executed.<br />
<br />
We can think of different ways to overcome this inconsistency. Most of the time you can simply test if B equals zero and don't try the dividing in that case. However, sometimes you will need some different approach. So, take a look at the following examples:<br />
<br />
<syntaxhighlight><br />
uses<br />
..., math,...<br />
<br />
//...<br />
var<br />
X, A, B: Double;<br />
Ind: Boolean;<br />
// ...<br />
try<br />
X := A / B;<br />
Ind := IsInfinite(X) or IsNan(X); // with gtk2, we fall here<br />
except <br />
Ind := True; // in windows, we fall here when B equals zero<br />
end;<br />
if Ind then begin<br />
// code block 2<br />
end else begin<br />
// code block 1<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
Or:<br />
<syntaxhighlight><br />
uses<br />
..., math,...<br />
<br />
//...<br />
var<br />
X, A, B: Double;<br />
FPUExceptionMask: TFPUExceptionMask;<br />
// ...<br />
<br />
try<br />
FPUExceptionMask := GetExceptionMask;<br />
SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]); // unmask<br />
try<br />
X := A / B;<br />
finally<br />
SetExceptionMask(FPUExceptionMask); // return previous masking immediately, we must not let Gtk2 internals to be called without the mask<br />
end;<br />
// code block 1<br />
except <br />
// code block 2<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
Be cautious, do not do something like this (call LCL with still removed mask):<br />
<syntaxhighlight><br />
try<br />
FPUExceptionMask := GetExceptionMask;<br />
SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]);<br />
try<br />
Edit1.Text := FloatToStr(A / B); // NO! Setting Edit's text goes down to widgetset internals and Gtk2 API must not be called without the mask!<br />
finally<br />
SetExceptionMask(FPUExceptionMask);<br />
end;<br />
// code block 1<br />
except <br />
// code block 2<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
But use an auxiliary variable:<br />
<syntaxhighlight><br />
try<br />
FPUExceptionMask := GetExceptionMask;<br />
SetExceptionMask(FPUExceptionMask - [exInvalidOp, exZeroDivide]);<br />
try<br />
X := A / B; // First, we set auxiliary variable X<br />
finally<br />
SetExceptionMask(FPUExceptionMask);<br />
end;<br />
Edit1.Text := FloatToStr(X); // Now we can set Edit's text.<br />
// code block 1<br />
except <br />
// code block 2<br />
end;<br />
// ...<br />
</syntaxhighlight><br />
<br />
In all situations, when developing LCL applications, it is most important to know about this and to keep in mind that some floating point operations can go different way with different widgetsets. Then you can think of an appropriate way to workaround this, but this should not go unnoticed.<br />
<br />
==Issues when moving from Windows to *nix etc==<br />
Issues specific to Linux, OSX, Android and other Unixes are described here. Not all subjects may apply to all platforms<br />
<br />
=== On Unix there is no "application directory" ===<br />
Many programmers are used to call ExtractFilePath(ParamStr(0)) or Application.ExeName to get the location of the executable, and then search for the necessary files for the program execution (Images, XML files, database files, etc) based on the location of the executable. This is wrong on unixes. The string on ParamStr(0) may contain a directory other than the one of the executable, and it also varies between different shell programs (sh, bash, etc).<br />
<br />
Even if Application.ExeName could in fact know the directory where the executable is, that file could be a symbolic link, so you could get the directory of the link instead (depending on the Linux kernel version, you either get the directory of the link or of the program binary itself).<br />
<br />
To avoid this read the sections about [[Multiplatform_Programming_Guide#Configuration_files|configuration files]] and [[Multiplatform_Programming_Guide#Data_and_resource_files|data files]].<br />
<br />
=== Making do without Windows COM Automation ===<br />
<br />
With Windows, COM Automation is a powerful way not only of manipulating other programs remotely but also for allowing other programs to manipulate your program. With Delphi you can make your program both an COM Automation client and a COM Automation server, meaning it can both manipulate other programs and in turn be manipulated by other programs. For examples, <br />
see [http://wiki.lazarus.freepascal.org/Office_Automation#Using_COM_Automation_to_interact_with_OpenOffice_and_Microsoft_Office Using COM Automation to interact with OpenOffice and Microsoft Office].<br />
<br />
==== OSX alternative ====<br />
Unfortunately, COM Automation isn't available on OS X and Linux. However, you can simulate some of the functionality of COM Automation on OS X using AppleScript.<br />
<br />
AppleScript is similar to COM Automation in some ways. For example, you can write scripts that manipulate other programs. Here's a very simple example of AppleScript that starts NeoOffice (the Mac version of OpenOffice.org):<br />
<br />
tell application "NeoOffice"<br />
launch<br />
end tell<br />
<br />
An app that is designed to be manipulated by AppleScript provides a "dictionary" of classes and commands that can be used with the app, similar to the classes of a Windows Automation server. However, even apps like NeoOffice that don't provide a dictionary will still respond to the commands "launch", "activate" and "quit". AppleScript can be run from the OS X Script Editor or Finder or even converted to an app that you can drop on the dock just like any app. You can also run AppleScript from your program, as in this example:<br />
<br />
fpsystem('myscript.applescript');<br />
<br />
This assumes the script is in the indicated file. You can also run scripts on the fly from your app using the OS X OsaScript command:<br />
<br />
fpsystem('osascript -e '#39'tell application "NeoOffice"'#39 +<br />
' -e '#39'launch'#39' -e '#39'end tell'#39);<br />
{Note use of #39 to single-quote the parameters}<br />
<br />
However, these examples are just the equivalent of the following Open command:<br />
<br />
fpsystem('open -a NeoOffice');<br />
<br />
Similarly, in OS X you can emulate the Windows shell commands to launch a web browser and launch an email client with:<br />
<br />
fpsystem('open -a safari "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');<br />
<br />
and <br />
<br />
fpsystem('open -a mail "mailto:ss4200@invalid.org"');<br />
<br />
which assumes, fairly safely, that an OS X system will have the Safari and Mail applications installed. Of course, you should never make assumptions like this, and for the two previous examples, you can in fact just rely on OS X to do the right thing and pick the user's default web browser and email client if you instead use these variations:<br />
<br />
fpsystem('open "http://gigaset.com/shc/0,1935,hq_en_0_141387_rArNrNrNrN,00.html"');<br />
<br />
and <br />
<br />
fpsystem('open "mailto:ss4200@invalid.org"');<br />
<br />
Do not forget to include the Unix unit in your uses clause if you use <code>fpsystem</code> or <code>shell</code> (interchangeable).<br />
<br />
The real power of AppleScript is to manipulate programs remotely to create and open documents and automate other activities. How much you can do with a program depends on how extensive its AppleScript dictionary is (if it has one). For example, Microsoft's Office X programs are not very usable with AppleScript, whereas the newer Office 2004 programs have completely rewritten AppleScript dictionaries that compare in many ways with what's available via the Windows Office Automation servers.<br />
<br />
==== Linux alternatives ====<br />
While Linux shells support sophisticated command line scripting, the type of scripting is limited to what can be passed to a program on the command line. There is no single, unified way to access a program's internal classes and commands with Linux the way they are via Windows COM Automation and OS X AppleScript. However, individual desktop environments (GNOME/KDE) and application frameworks often provide such methods of interprocess communication. On GNOME see Bonobo Components. KDE has the KParts framework, DCOP. OpenOffice has a platform neutral API for controlling the office remotely (google OpenOffice SDK) - though you would probably have to write glue code in another language that has bindings (such as Python) to use it. In addition, some applications have "server modes" activated by special command-line options that allow them to be controlled from another process. It is also possible (Borland did it with Kylix document browser) to "embed" one top-level X application window into another using XReparentWindow (I think).<br />
<br />
As with Windows, many OS X and Linux programs are made up of multiple library files (.dylib and .so extensions). Sometimes these libraries are designed so you can also use them in programs you write. While this can be a way of adding some of the functionality of an external program to your program, it's not really the same as running and manipulating the external program itself. Instead, your program is just linking to and using the external program's library similar to the way it would use any programming library.<br />
<br />
=== Alternatives for Windows API functions ===<br />
<br />
Many Windows programs use the Windows API extensively. In cross-platform applications Win API functions in the Windows unit should not be used, or should be enclosed by a conditional compile (e.g. {$IFDEF MSWINDOWS} ).<br />
<br />
Fortunately many of the commonly used Windows API functions are implemented in a multiplatform way in the unit [[lclintf]]. This can be a solution for programs which rely heavily on the Windows API, although the best solution is to replace these calls with true cross-platform components from the LCL. You can replace calls to GDI painting functions with calls to a TCanvas object's methods, for example.<br />
<br />
=== Key codes ===<br />
Fortunately, detecting key codes (e.g. on KeyUp events) is portable: see [[LCL Key Handling]].<br />
<br />
===Installing your application===<br />
See [[Deploying Your Application]].<br />
<br />
== See Also ==<br />
* [[Writing portable code regarding the processor architecture]]<br />
* [[Introduction to platform-sensitive development]]<br />
* http://www.midnightbeach.com/KylixForDelphiProgrammers.html A guide for Windows programmers starting with Kylix. Many of concepts / code snippets apply to Lazarus.<br />
* http://www.stack.nl/~marcov/porting.pdf A guide for writing portable source code, mainly between different compilers.<br />
<br />
[[Category:Tutorials]]<br />
[[Category:Multiplatform Programming]]<br />
[[Category:Platform-sensitive development]]<br />
[[Category:Inter-process communication]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=Lazarus_1.8_fixes_branch&diff=109057Lazarus 1.8 fixes branch2017-04-21T05:42:23Z<p>Zoran: /* Submitted by developer / committer */</p>
<hr />
<div>This page contains the revisions to be merged from trunk to the [http://svn.freepascal.org/svn/lazarus/branches/fixes_1_8/ Lazarus 1.8 fixes] branch.<br />
<br />
This are only fixes made after the branch was created. For other fixes made since the previous release (1.6) see SVN and [[Lazarus_1.6_fixes_branch|Lazarus 1.6 fixes branch]]<br />
<br />
Release notes can be found [[Lazarus 1.8.0 release notes|here]]<br />
<br />
== Fixes for 1.8.0 RC1 ==<br />
=== Merge requests ===<br />
==== Submitted by developer / committer and waiting for testing (do not commit yet) ====<br />
<br />
==== Submitted by developer / committer ====<br />
*r54648 IDE images: new images menu_view_components, menu_view_inspector, menu_view_messages, menu_view_search_results. Designed by FTurtle, {{MantisLink|31625}}<br />
*r54654 Qt,Qt5: fixed av because parent can be nil.{{MantisLink|31684}}<br />
*r54655 Gtk3: removed gdk_pixbuf_gettext which does not exist in gdk-pixbuf.{{MantisLink|31677}}<br />
*r54651 DateTimePicker - OnChange fires twice {{MantisLink|0031679}}<br />
<br />
==== Submitted by others ====<br />
<br />
=== Merged revisions for 1.8.0 ===<br />
*r54585 Qt,Qt5: do not process events after modal form is hidden, otherwise we can have av if formclose contain caFree.<br />
*r54586 Qt: simplified code.<br />
*r54589 Qt,Qt5: do not eat cpu with gtk theme. {{MantisLink|31191}}<br />
*r54593 Qt,Qt5: do not disable DT_WORDBREAK if we are using DT_CALCRECT, otherwise we'll have wrong calculation in combination with DT_NOCLIP.<br />
*r54599 Qt,Qt5: fixed potential memleak with TQtWSDragImageList when bitmap handle is null.<br />
*r54594 LazControls: Apply filter also at start in ListFilterEdit and ListviewFilterEdit. {{MantisLink|31632}}.<br />
*r54597 LazControls: Apply filter also at start in TreeFilterEdit. {{MantisLink|31632}}.<br />
*r54600 LCL-Win32: Fix range error in ComboBoxWindowProc. Use SetWindowLong from LCLIntf. {{MantisLink|31635}}.<br />
*r54596 translations: German: updates from Swen Heinig<br />
*r54624 LCL: TGroupBox: Win32: Fix for doubled caption. {{MantisLink|31670}}.<br />
*r54618 Lazreport: fix column number {{MantisLink|31544}}.<br />
*r54619 LazReport: fix export from LazReport to pdf (fclpdf), from Aleksey Lagunov {{MantisLink|31659}}<br />
*r54633 GTK2, GTK3: In DrawText support prefixed UTF-8 codepoints. {{MantisLink|31674}}.<br />
<br />
== Roadmap to 1.8.0 ==<br />
The release process will consist of the following steps.<br />
* Creation of the SVN 1.8 fixes branch. (ToDo 8 Apr 2017)<br />
<br />
This is the start of the stabilization process before a new Lazarus release.<br />
* Release of 1.8.0 RC 1 (ToDo)<br />
<br />
This will be the first Release Candidate. We would like to urge all users to test this version.<br />
The testing period is scheduled to last 2 weeks.<br />
* Release of 1.8.0 RC 2 (ToDo)<br />
<br />
The testing period is scheduled to last 2 weeks.<br />
* Release of 1.8.0 RC 3 (maybe)<br />
<br />
The testing period is scheduled to last 2 weeks.<br />
* Release of Lazarus 1.8 (ToDo)<br />
<br />
Users can download the new stable version.<br />
<br />
=== Tests ===<br />
*Menu item exists<br />
*Clear pcp directory and start IDE, a new project application with a form should be visible<br />
*Double click on form - a FormCreate event should be created<br />
*Check View / IDE internals / What needs building - no package should need building, only the project<br />
*Restart the IDE - there should be no warning about upgrading<br />
*Install the package cody, after restart the component palette should show the component TCodyTreeView<br />
<br />
=== Tagging release ===<br />
* Set version in fixes_1_8 branch<br />
** open lazarus/lazarus.lpi in the IDE and change the version numbers in the project options dialog 1.8.0.1 for RC1, 1.8.0.2 for RC2, 1.8.0.3 for RC3, 1.8.0.4 for final<br />
** lazarus/ide/version.inc 1.8.0RC1 for RC1, no spaces! (1.8.0 for final release)<br />
** lazarus/lcl/lclversion.pas 1.8.0.1 for RC1, 1.8.0.2 for RC2, ... 1.8.0.4 for final<br />
** lclbase.lpk<br />
** lcl.lpk<br />
** lazarus/debian/changelog 1.8.0-1 for RC1, 1.8.0-2 for RC2, ... 1.8.0-4 for final<br />
** lazarus/lazarus.app/Contents/Info.plist<br />
*** CFBundleShortVersionString "1.8.0"<br />
*** CFBundleVersion 1 for RC1, 2 for RC2, 3 for RC3, 4 for final release<br />
** tools/install/linux/environmentoptions.xml same as version.inc<br />
** tools/install/win/environmentoptions.xml same as version.inc<br />
** tools/install/macos/environmentoptions.xml same as version.inc<br />
** check if lpl files needs updating by running ./tools/lplupdate -c<br />
** run ./tools/updatemakefiles<br />
* Tag fixes_1_8 branch to tags/release_1_8_0_RC1 (or tags/lazarus_1_8_0 for final release)<br />
svn copy svn+ssh://svn.freepascal.org/FPC/svn/lazarus/branches/fixes_1_8 svn+ssh://svn.freepascal.org/FPC/svn/lazarus/tags/lazarus_1_8_0_RC1 -m 'tagged 1.8.0RC1 as tags/lazarus_1_8_0_RC1'<br />
* Set version to next version in svn<br />
<br />
== How to merge ==<br />
See [[Lazarus_1.0_fixes_branch#How_to_merge]]<br />
<br />
== Other branches ==<br />
{{Navbar Lazarus Release Notes}}<br />
<br />
[[Category:Lazarus]]<br />
[[Category:Branches]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=IBX&diff=108558IBX2017-03-30T07:10:46Z<p>Zoran: fix link</p>
<hr />
<div>== IBX For Lazarus (Firebird Express) ==<br />
<br />
IBX for Lazarus is derived from the Open Source edition of IBX published by Borland/Inprise in 2000 under the InterBase Public License. This version has been brought up-to-date by MWA Software and focused on the Firebird Database API for both Linux and Windows platforms. It is released under the InterBase Public License for the original code and under the compatible Initial Developers Public License for new software.<br />
<br />
Available at [http://www.mwasoftware.co.uk/ibx www.mwasoftware.co.uk/ibx]<br />
<br />
[[Category:Components]]<br />
[[Category:Databases]]<br />
[[Category:Firebird]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=TCHMHelpDatabase&diff=108221TCHMHelpDatabase2017-03-17T09:25:07Z<p>Zoran: </p>
<hr />
<div>'''TCHMLHelpDatabase''' [[image:tchmhelpdatabase.png]] is a non-visual component that offers CHM-context sensitive application help. It is available from the [[System tab]] of the [[Component Palette]]. <br />
<br />
A demo is available in <code>${lazarusdir}/components/chmhelp/democontrol/</code><br />
<br />
<br />
== See also ==<br />
* [[THTMLHelpDatabase]]<br />
* [[Add Help to Your Application]]<br />
* [[Webbrowser]]<br />
<br />
{{LCL Components}}</div>Zoranhttps://wiki.freepascal.org/index.php?title=TEventLog&diff=108220TEventLog2017-03-17T09:24:32Z<p>Zoran: </p>
<hr />
<div>'''TEventLog''' [[image:teventlog.png]] is a non-visual component that assists in event-logging. It is available from [[System tab]] of the [[Component Palette]].<br />
<br />
A TEventLog provides a bunch of procedures to log various levels of severity to either the system log or a named log-file.<br />
<br />
procedure Log();<br />
procedure Info();<br />
procedure Debug();<br />
procedure Warning();<br />
procedure Error();<br />
<br />
==See also==<br />
* [[doc:fcl/eventlog/teventlog.html|TEventLog doc]]<br />
<br />
{{LCL Components}}</div>Zoranhttps://wiki.freepascal.org/index.php?title=System_tab&diff=108219System tab2017-03-17T09:22:54Z<p>Zoran: </p>
<hr />
<div>{{System_tab}}<br />
<br />
The '''System tab''' of the [[Component Palette]] contains non-visual operatingsystem-related components like timers.<br />
<br />
[[Image:Component_Palette_System.png]]<br />
<br />
{| class = "wikitable sortable"<br />
|-<br />
! Icon !! Component !! Description<br />
|-<br />
| [[image:ttimer.png]] || [[TTimer]] ||<br />
|-<br />
| [[image:tidletimer.png]] || [[TIdleTimer]] ||<br />
|-<br />
| [[image:tlazcomponentqueue.png]] || [[TLazComponentQueue]] ||<br />
|-<br />
| [[image:thtmlhelpdatabase.png]] || [[THTMLHelpDatabase]] ||<br />
|-<br />
| [[image:thtmlbrowserhelpviewer.png]] || [[THTMLBrowserHelpViewer]] ||<br />
|-<br />
| [[image:tasyncprocess.png]] || [[TAsyncProcess]] ||<br />
|-<br />
| [[image:tprocessutf8.png]] || [[TProcessUTF8]] ||<br />
|-<br />
| [[image:tprocess.png]] || [[TProcess]] ||<br />
|-<br />
| [[image:tsimpleipcclient.png]] || [[TSimpleIPCClient]] || IPC Client<br />
|-<br />
| [[image:tsimpleipcserver.png]] || [[TSimpleIPCServer]] || IPC Server<br />
|-<br />
| [[image:txmlconfig.png]] || [[TXMLConfig]] ||<br />
|-<br />
| [[image:teventlog.png]] || [[TEventLog]] ||<br />
|-<br />
| [[image:tservicemanager.png]] || [[TServiceManager]] ||<br />
|-<br />
| [[image:tchmhelpdatabase.png]] || [[TCHMHelpDatabase]] ||<br />
|-<br />
| [[image:tlhelpconnector.png]] || [[TLHelpConnector]] ||<br />
|}<br />
<br />
{{NavComponentPalette}}<br />
<br />
[[Category:Component Palette]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=TProcess&diff=108218TProcess2017-03-17T09:22:12Z<p>Zoran: </p>
<hr />
<div>{{TProcess}}<br />
<br />
'''TProcess''' [[image:tprocess.png]] is a non-visual component on the [[System tab]] of the [[Component Palette]] that enables easy execution of external programs.<br />
<br />
Some of the benefits of using TProcess are that it is:<br />
* Platform Independent<br />
* Capable of reading from stdout and writing to stdin.<br />
* Possible to wait for a command to finish or let it run while your program moves on.<br />
<br />
Important notes:<br />
* TProcess is not a terminal/shell! You cannot directly execute scripts or redirect output using operators like "|", ">", "<", "&" etc. It is possible to obtain the same results with TProcess using pascal, some examples are below..<br />
* Presumably on Linux/Unix: you '''must''' specify the full path to the executable. For example '/bin/cp' instead of 'cp'. If the program is in the standard PATH then you can use the function [[doc:lcl/fileutil/finddefaultexecutablepath.html|FindDefaultExecutablePath]] from the [[doc:lcl/fileutil/index.html|FileUtil]] unit of the LCL.<br />
* On Windows, if the command is on the path, you don't need to specify the full path.<br />
<br />
== Example ==<br />
See the TProcess examples in [[Executing External Programs#TProcess|Executing External Programs]].<br />
<br />
If there is need for [[UTF-8]] string passing, use TProcessUTF8 instead.<br />
<br />
== See also ==<br />
* [[doc:fcl/process/tprocess.html|TProcess doc]]<br />
* [[TAsyncProcess]]<br />
* [[TProcessUTF8]]<br />
<br />
{{LCL Components}}</div>Zoranhttps://wiki.freepascal.org/index.php?title=TProcessUTF8&diff=108217TProcessUTF82017-03-17T09:21:13Z<p>Zoran: </p>
<hr />
<div>{{TProcessUTF8}}<br />
<br />
'''TProcessUTF8''' [[image:tprocessutf8.png]] is a non-visual component on the [[System tab]] of the [[Component Palette]] and is a version of the [[TProcess]] component that accepts [[UTF-8]] Unicode characters - normal TProcess uses ANSI/ASCII characters.<br />
<br />
TProcessUTF8 is part of Lazarus (introduced in the development version which will end up as Lazarus 1.4 in future) in the '''UTF8Process''' unit; it can be used from command-line programs by setting a requirement to '''LCLBase'''<br />
<br />
== Example ==<br />
See the [[TProcess]] examples in [[Executing External Programs]]; however, you should pass UTF8 strings instead of [[ASCII]]/ANSI strings.<br />
<br />
== See also ==<br />
* [[doc:lcl/utf8process/tprocessutf8.html|TProcessUTF8 doc]]<br />
* [[TProcess]]<br />
* [[TAsyncProcess]]<br />
* [[LCL Unicode Support]]<br />
<br />
{{LCL Components}}</div>Zoranhttps://wiki.freepascal.org/index.php?title=TAsyncProcess&diff=108216TAsyncProcess2017-03-17T09:20:31Z<p>Zoran: </p>
<hr />
<div>'''TAsyncProcess''' [[image:tasyncprocess.png]] is a non-visual component on the [[System tab]] of the [[Component Palette]] and is a version of the [[TProcessUTF8]] component can handle asynchroneous execution.<br />
<br />
== See also ==<br />
* [[doc:lcl/asyncprocess/tasyncprocess.html TAsyncProcess doc]]<br />
* [[TProcess]]<br />
* [[TProcessUTF8]]<br />
* [[LCL Unicode Support]]<br />
* [[Executing External Programs]]<br />
<br />
{{LCL Components}}<br />
<br />
[[Category:FPC]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=THTMLHelpDatabase&diff=108215THTMLHelpDatabase2017-03-17T09:18:33Z<p>Zoran: </p>
<hr />
<div>'''THTMLHelpDatabase''' [[image:thtmlhelpdatabase.png]] is a non-visual component that offers HTML-context sensitive application help. It is available from the [[System tab]] of the [[Component Palette]]. <br />
<br />
== See also ==<br />
* [[doc:lcl/lazhelphtml/thtmlhelpdatabase.html|THTMLHelpDatabase doc]]<br />
* [[THTMLBrowserHelpViewer]]<br />
* [[TCHMHelpDatabase]]<br />
* [[Add Help to Your Application]]<br />
* [[Webbrowser]]<br />
<br />
{{LCL Components}}</div>Zoranhttps://wiki.freepascal.org/index.php?title=TLazComponentQueue&diff=108214TLazComponentQueue2017-03-17T09:18:25Z<p>Zoran: </p>
<hr />
<div>'''TLazComponentQueue''' [[image:tlazcomponentqueue.png]] is a non-visual component that assists in streaming components when multithreading or networking. It is available from the [[System tab]] of the [[Component Palette]]. <br />
<br />
== See also ==<br />
*[[doc:lcl/lresources/tcustomlazcomponentqueue.html|TLazComponentQueue doc]]<br />
<br />
{{LCL Components}}</div>Zoranhttps://wiki.freepascal.org/index.php?title=UniqueInstance&diff=104145UniqueInstance2016-09-21T09:51:27Z<p>Zoran: /* Download */</p>
<hr />
<div>{{UniqueInstance}}<br />
<br />
=== About ===<br />
UniqueInstance provides an easy way to force only one instance per application running at same time.<br />
<br />
=== Features ===<br />
* Easy of use: just drop a component in the main form<br />
* Provides a mechanism to receive the Command Line of the other instances<br />
<br />
=== How To Use (Component) ===<br />
<br />
Just drop in the main form of a LCL application (it's on the System tab, look for a red full-circle with the digit "1" in the middle).<br />
<br />
Properties:<br />
<br />
* Enabled: enables/disables the component<br />
* Identifier: used to provides a way to identify the application<br />
* UpdateInterval: the interval in milliseconds which the component will monitor messages from new instances. <b>Has meaning only under unix since, under win32, the message is received through the windows message loop</b> <br />
<br />
Event<br />
* OnOtherInstance: called when another instance is initiated. Receives the parameters passed to that instance.<br />
<br />
=== How To Use (Raw) ===<br />
<br />
Add <b>uniqueinstanceraw</b> unit to your uses section.<br />
<br />
Call the <i>InstanceRunning</i> function: it will return true if there's already a instance running. <br />
<br />
There are two variants:<br />
* Without arguments: will use the executable name as identifier and will not send the command line parameters<br />
* With two arguments:<br />
** Identifier: the identifier of the application<br />
** SendParameters(default = false): send the command line parameters to the already running instance, if any, before quit.<br />
<br />
=== Remarks ===<br />
* Tested with win32 (XP SP2) and Linux (Ubuntu 9.04) with fpc 2.4.2.<br />
* If you put two TUniqueInstance components in the same application with the same identifier your application won't load.<br />
* The Identifier is optional both to the function and the component. In the case it's not used, the executable name is used as an Identifier. The consequence is that if someone changes the exe name another instance will not be identified. In the other hand, if two different applications use the same Identifier one will prevent the other from launching<br />
** TIP: to make sure an application will not prevent another from launching you can use a GUID as the Identifier<br />
<br />
=== History ===<br />
* 17/04/11 - Version 1.0<br />
** Fix application being detected as running after a crash under unix<br />
** Fix crash when compiling with Gtk2 widgetset<br />
** Set default values of published properties<br />
** Optimizations and code clean up<br />
** New icon<br />
* 02/10/07 - Version 0.2<br />
** Based in fpc 2.2.0<br />
** Implemented parameter reception under unix<br />
** General optimizations and code clean up <br />
* 16/12/06 - Initial release. See [http://lazarusroad.blogspot.com/2006/12/only-one-instance.html my blog] to know how it began.<br />
<br />
=== Author ===<br />
<br />
[[User:Luizmed|Luiz Américo Pereira Câmara]]<br />
<br />
=== License === <br />
<br />
Modified LGPL<br />
<br />
<br />
=== Download ===<br />
<br />
Version 1.0: [https://code.google.com/archive/p/luipack/downloads Download from Google Code]<br />
<br />
Recent development moved to GitHub repository: https://github.com/blikblum/luipack/tree/master/uniqueinstance<br />
<br />
You can use SVN client:<br />
<nowiki>svn co https://github.com/blikblum/luipack.git/trunk/uniqueinstance</nowiki><br />
<br />
[[Category:Components]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=Firebird&diff=103173Firebird2016-08-04T06:45:36Z<p>Zoran: /* Advanced transactions */</p>
<hr />
<div>This is a guide about using '''Firebird''' database in Lazarus/FPC, using Firebird with SQLDB (the FPC/Lazarus built-in database library). Other access methods are described in [[Other Firebird libraries]].<br />
<br />
__TOC__<br />
<br />
==Overview==<br />
Firebird is an open source, free database server that has been in use and developed for decades (it developed out of the Interbase 6 database that was open sourced by Borland). It includes rich support for SQL statements (e.g. INSERT...RETURNING), stored procedures, triggers, etc. You can write compiled UDFs (User-Defined Functions) libraries for the server in FreePascal if you want to extend the already extensive function list of Firebird.<br />
<br />
The database requires very little manual DBA work once it is set up, making it ideal for small business use or embedded use. It can grow to terabyte scale given proper tuning, although PostgreSQL may be a better choice for such large environments.<br />
<br />
Firebird offers both embedded (file-based) and client-server database - usable without having to change a single line of code in FPC/Lazarus. If used as an embedded database, it offers a richer SQL support than [[SQLite]] as well as seamless migration to a client-server database, although SQLite is a quite capable embedded database itself.<br />
<br />
The latest stable version, Firebird 2.5, runs on Windows (32- and 64-bit), various Linux versions (32- and 64-bit), Solaris (Sparc and Intel), HP-UX (PA-Risc) and OSX.<br />
<br />
It is being ported to Android at the moment; it is not available on Windows CE/Windows Mobile.<br />
<br />
Support for Firebird in FPC's SQLDB is quite good, comparable to the level of [[postgres|PostgreSQL]] support.<br />
<br />
== Documentation ==<br />
Official documentation is included in FPC 2.6.2+: [http://www.freepascal.org/docs-html/fcl/ibconnection/index.html SQLDB documentation for IBConnection]<br />
<br />
==Client/server and embedded installation==<br />
Firebird can run in client/server and embedded mode.<br />
<br />
''Client/Server'' means that you have a physical Firebird server somewhere: either on your local machine or another machine reachable over your network. Connections to the server go through TCP/IP; when specifying the connection, the hostname contains a name or IP address. The Firebird DLL you need for this is ''fbclient.dll'' (''along with its support files'').<br />
<br />
''Embedded Firebird'' means that your application loads the Firebird DLLs to access a Firebird database on the local machine. When specifying the connection, the hostname is always empty. The Firebird DLL you need for this is ''fbembed.dll'' (''along with its support files'').<br />
See [[Firebird embedded|the wiki page on Firebird embedded]] for more details.<br />
<br />
Note that ''fbembed.dll'' can be used both for client/server and embedded use, so installing only this dll may be a smart thing to do.<br />
<br />
===Windows===<br />
Win64: please see warning [[Windows Programming Tips#FPC 2.6.x/Lazarus warning|here]] on not using certain FPC/Lazarus Win64 versions.<br />
<br />
On Windows: (this applies to all SQLDB database drivers) you must have '''fbclient.dll''' (or '''fbembed.dll''') and its support dlls installed in:<br />
* the project directory '''and''' the executable output directory/the application directory (e.g. lib/something under your project directory)<br />
* or a directory in your PATH (not your system directory)<br />
* If you want to use the system directory, please use the official installer and tick "copy fbclient to system directory"<br />
<br />
As with all (database) DLLs, the bitness of the DLL must match your application: use a 32 bit library for a 32 bit compiled program and a 64 bit library for a 64 bit program.<br />
<br />
===Unix/Linux/OSX===<br />
On Linux/OSX/FreeBSD, the Firebird client library should be installed (e.g. by your package manager; install the regular package and the -dev package), or they should be placed in the library search path.<br />
<br />
FPC searches for the most common library names (e.g. libfbclient.so.2.5, libgds.so, and libfbembed.so.2.5; please check ibase60.inc if your version is different). If you want to, you can explicitly specify the library name. There are 2 ways for this:<br />
* use the [[TSQLDBLibraryLoader]] component from sqldblib (FPC 2.7.1). Works for all SQLDB connector components.<br />
* call <syntaxhighlight>function InitialiseIBase60(Const LibraryName : AnsiString) : integer;</syntaxhighlight> with the correct library name (you may need to use unit ibase60dyn for this).<br />
<br />
== Connection examples ==<br />
Example for client/server:<br />
Hostname: 192.168.1.1<br />
* The database is on the server with IP address 192.168.1.1. <br />
DatabaseName: /interdata/example.fdb <br />
* The name of the database file is "example.fdb" in the /interdata directory of the server (the machine with IP address 192.168.1.1).<br />
Username: SYSDBA<br />
Password: masterkey<br />
<br />
Another example for client/server:<br />
Hostname: dbhost<br />
* The database is on the server with the host name dbhost<br />
DatabaseName: F:\Program Files\firebird\examples\employee.fdb <br />
* The name of the database file is "employee.fdb" in the Program Files\firebird\examples directory on the F: drive of dbhost.<br />
Username: SYSDBA<br />
Password: masterkey<br />
<br />
An embedded example:<br />
Hostname: <empty string><br />
* Leaving the hostname empty selects embedded use.<br />
DatabaseName: test.fdb<br />
* The database file is "test.fdb" in the directory where the application runs (make sure fbembed.dll is in the application executable directory)<br />
Username: SYSDBA<br />
* On embedded, you do have to specify a username...<br />
Password: <empty string><br />
* ... but it doesn't matter what password you give.<br />
<br />
== Troubleshooting client/server access issues ==<br />
Make sure you started the Interbase/Firebird server on the server IP/hostname you specified. You can test connectivity by telnetting to the machine. Firebird usually listens on port 3050:<br />
<syntaxhighlight lang="bash"><br />
telnet 192.168.1.1 3050<br />
</syntaxhighlight><br />
You should see something, maybe just a blank screen, but you can type something. This means you can send data to the Firebird database.<br />
<br />
For further information, please see the Firebird documentation.<br />
<br />
== Monitoring Events ==<br />
FPC/Lazarus comes with a component to monitor events coming from Firebird databases; see [[TFBEventMonitor]].<br />
== Creating objects programmatically ==<br />
While you can use tools such as Flamerobin to create tables etc, you can also create these programmatically/dynamically, which could be handy when you want your programs to update existing database schemas to a new schema.<br />
<br />
You can use e.g. TSQLQuery.ExecSQL to perform this task:<br />
<syntaxhighlight><br />
Query.ExecSQL('CREATE TABLE TEST(ID INTEGER NOT NULL, TESTNAME VARCHAR(800))');<br />
// You need to commit the transaction after DDL before any DML - SELECT, INSERT etc statements.<br />
// Otherwise the SQL won't see the created objects<br />
</syntaxhighlight><br />
<br />
To do this, use the TSQLScript object - see [[TSQLScript]].<br />
<br />
== Database Administration ==<br />
FPC/Lazarus has a component for database adminstration; see [[TFBAdmin]]<br />
<br />
== Common problems and solutions ==<br />
Sometimes using Firebird in Lazarus seems to be tricky. Please find solutions below.<br />
<br />
=== Attempted update of read-only column / COMPUTED BY fields ===<br />
If you have COMPUTED BY fields (server-side calculated fields) in your Firebird table, SQLDB will not pick up that these are read only fields (for performance reasons).<br />
<br />
In this case, auto-generated INSERTSQL,UPDATESQL statements can lead to error messages like "attempted update of read-only column".<br />
The solution is to manually specify that the field in question may not be updated after setting the TSQLQuery's SQL property, something like:<br />
<syntaxhighlight><br />
// Disable updating this field or searching for changed values as user cannot change it<br />
sqlquery1.fieldbyname('full_name').ProviderFlags:=[];<br />
</syntaxhighlight><br />
<br />
=== Bigint: lost precision ===<br />
If you use the bigint datatype (64 bit signed integer) in Firebird, please use .AsLargeInt instead of .AsInteger for parameters:<br />
<syntaxhighlight><br />
// Assuming ID is bigint here<br />
sqlquery1.sql.text := 'insert into ADDRESS (ID) values (:ID)';<br />
// Use this:<br />
sqlquery1.params.parambyname('ID').aslargeint := <some qword or 64 bit integer variable>;<br />
// Do not use this:<br />
//sqlquery1.params.parambyname('ID').asinteger := <some qword or 64 bit integer variable>;<br />
...<br />
</syntaxhighlight><br />
... otherwise you might get errors like duplicate PK (primary key) - if using the bigint as a primary key.<br />
<br />
=== Boolean data types ===<br />
Firebird versions below 3.0 do not support boolean data types.<br />
<br />
At least on FPC trunk (2.7.1) this datatype can be emulated:<br />
<br />
Use a DOMAIN that uses a SMALLINT type (other integer types may work as well - please test and adjust text): <syntaxhighlight lang="SQL">CREATE DOMAIN "BOOLEAN"<br />
AS SMALLINT<br />
CHECK (VALUE IS NULL OR VALUE IN (-1,0,1))<br />
/* -1 used for compatibility with FPC SQLDB; 1 is used by many other data access layers */<br />
;<br />
</syntaxhighlight><br />
<br />
<br />
Let your field/column use this domain type e.g. <syntaxhighlight lang="SQL"><br />
CREATE TABLE MYTABLE<br />
(<br />
...<br />
MYBOOLEANCOLUMN "BOOLEAN",<br />
);<br />
</syntaxhighlight><br />
* Now you can use .AsBoolean for assigning field values etc<br />
<br />
'''To do: verify this works with Lazarus grids etc as well.'''<br />
<br />
=== INSERT INTO...RETURNING problems/Cursor is not open ====<br />
If you try to select SQL (e.g. Query.Open) with SQL like this:<br />
<syntaxhighlight lang="SQL"><br />
INSERT INTO PEOPLE (NICKNAME) VALUES ('Superman') RETURNING ID<br />
</syntaxhighlight><br />
and get something like this error:<br />
<syntaxhighlight lang="dos"><br />
Database error: : Fetch :<br />
-Dynamic SQL Error<br />
-SQL error code = -504<br />
-Invalid cursor reference<br />
-Cursor is not open(error code: 335544569)<br />
</syntaxhighlight><br />
while running FPC 2.6.0 (which is supplied with Lazarus 1.0) or lower, then you probably ran into an FPC SQLDB parser bug.<br />
<br />
SQLDB thinks the statement you're running is a normal INSERT statement, which doesn't return data. Obviously, it should return data.<br />
Newer FPC code has fixes for this.<br />
<br />
If you're using generators/sequences for your primary keys (like many do), a workaround is to first get the next sequence number:<br />
<syntaxhighlight lang="SQL"><br />
SELECT NEXT VALUE FOR GEN_PEOPLEID FROM RDB$DATABASE /* If your generator name is GEN_PEOPLEID */ <br />
</syntaxhighlight><br />
then use that to do a regular INSERT.<br />
see [http://www.firebirdfaq.org/faq111/|Firebird FAQ entry]<br />
<br />
=== Locate does not seem to work ===<br />
Source: {{MantisLink|#21988}}<br />
<br />
When running locate on UTF8 (or presumably other multibyte character sets) CHAR fields, locate may not find your record.<br />
<br />
The problem is related mostly to UTF8 charset used and how Firebird reports column length. In case of UTF8 Firebird reports the maximum column length (in bytes) as 4*"character length". So if you have a column defined as char(8), Firebird reports 4*8=32.<br />
<br />
Values are right-padded to this length 32.<br />
When locating say '57200001' there is no match because the field actually stores '57200001 ........................' (with trailing spaces represented by dots here).<br />
<br />
Workaround: rewrite your select query:<br />
<syntaxhighlight><br />
SELECT substring(THEFIELD from 1 for 8) AS THEFIELD ...<br />
</syntaxhighlight><br />
or<br />
<syntaxhighlight><br />
SELECT cast(THEFIELD as varchar(8)) as THEFIELD ...<br />
</syntaxhighlight><br />
or use VARCHAR fields.<br />
<br />
Note: this problem may occur for other databases as well depending on their reporting of field length.<br />
<br />
== Advanced transactions ==<br />
Sources for this information/further reading: <br />
* [http://www.ibphoenix.com/resources/documents/how_to/doc_400 Understanding Firebird Transactions] very detailed article<br />
* Interbase 6 API Guide (valid for Firebird+Interbase), page 63 and further <br />
* [http://conferences.embarcadero.com/article/32280 Overview of transaction settings in Interbase]<br />
* [http://tech.groups.yahoo.com/group/firebird-support/message/58653 Detailed explanation of parameters in Firebird]<br />
* [http://fhasovic.blogspot.com/2005/02/transaction-isolation-levels-in.html Overview of settings in Firebird]<br />
* [http://www.devrace.com/en/fibplus/articles/3292.php Transaction isolation levels in FIBPlus]<br />
* README.set_transaction.txt in Firebird 2.5 documentation folder.<br />
<br />
=== Transaction isolation levels ===<br />
If you want to, you can change the transaction isolation levels by adding a line in the transaction's Parameters property:<br />
* <code>isc_tpb_read_committed</code>: you see all changes committed by other transactions<br />
* <code>isc_tpb_concurrency</code>: also called Snapshot: you see database as it was when the transaction started. Has more overhead than isc_tpb_read_committed. Better than ANSI Serializable because it has no phantom reads.<br />
* <code>isc_tpb_consistency</code>: also called Table Stability: stable, serializable view of data, but locks tables. Unlikely you will need this<br />
<br />
Example:<br />
<syntaxhighlight><br />
SQLTransaction1.Params.text:='isc_tpb_read_committed';<br />
</syntaxhighlight><br />
<br />
You can also add additional parameters that have an effect on the transaction (taken from the ibconnection.pp source file and [http://conferences.embarcadero.com/article/32280]):<br />
<br />
=== Access mode ===<br />
This allow reads only or read/write<br />
* <code>isc_tpb_read</code>: read permission<br />
* <code>isc_tpb_write</code>: read+write permission<br />
<br />
=== Lock resolution ===<br />
* <code>isc_tpb_nowait</code>: if another transaction is editing the record then don't wait<br />
* <code>isc_tpb_wait</code>: if another transaction is editing the record then wait for it to finish. Can mitigate "live locks" in heavy contention ([http://tech.groups.yahoo.com/group/firebird-support/message/58653]). See below for timeout value.<br />
<br />
=== Table reservation ===<br />
Deals with locking entire tables.<br />
* <code>isc_tpb_shared</code>: first specify this, then either lock_read or lock_write for one or more tables. Shared read or write mode for tables.<br />
* <code>isc_tpb_protected</code>: first specify this, then either lock_read or lock_write for one or more tables. Lock on tables; can allow deadlock-free operation at the cost of delayed transactions<br />
* <code>isc_tpb_lock_read</code>: Set a read lock. Specify which table to lock, e.g. isc_tpb_lock_read=CUSTOMERS<br />
* <code>isc_tpb_lock_write</code>: Set a read/write lock. Specify which table to lock, e.g. isc_tpb_lock_read=CUSTOMERS<br />
Combinations:<br />
* Shared, lock_write: write transactions with concurrency or read committed isolation<br />
can update the table. All transactions can read the table<br />
* Shared, lock_read: any transaction can read or update<br />
* Protected, lock_write: Other transactions cannot update the table. Only concurrency and<br />
read committed transactions can read the table<br />
* Protected, lock_read: Other transactions cannot update the table. Any transaction can<br />
read the table<br />
<br />
=== Record versions ===<br />
This setting is apparently only relevant for isc_tpb_read_committed isolation mode for records being modified by other transactions:<br />
* <code>isc_tpb_no_rec_version</code>: only newest record version is read. Can be useful for batch/bulk insert operations together with isc_tpb_read_committed)<br />
* <code>isc_tpb_rec_version</code>: the latest committed version is read, even when the other transaction has other uncommitted changes '''note: verify this'''. More overhead than isc_tpb_no_rec_version<br />
<br />
=== Various options ===<br />
For completeness, some more options appearing in the Firebird/Interbase FPC code.<br />
You will likely only ever use isc_tpb_no_auto_undo to speed up batch inserts/edits.<br />
* isc_tpb_exclusive (apparently translates to protected in Firebird, see [http://tech.groups.yahoo.com/group/firebird-support/message/58653])<br />
* isc_tpb_verb_time (Related to deferred constraints, which could execute at verb time or commit time. Firebird: not implemented, always use verb time)<br />
* isc_tpb_commit_time (Related to deferred constraints, which could execute at verb time or commit time. Firebird: not implemented, always use verb time)<br />
* isc_tpb_ignore_limbo (ignores the records created by transactions in limbo. Limbo transactions are failing two-phase commits in multi-database transactions. Unlikely that you will need this feature)<br />
* isc_tpb_autocommit (autocommit this transaction: every statement is a separate transaction. Probably specifically for JayBird JDBC driver.)<br />
* isc_tpb_restart_requests (apparently looks for requests in the connection which had been active in another transaction, unwinds them, and restarts them under the new transaction.)<br />
* isc_tpb_no_auto_undo (disable transaction-level undo log, handy for getting max throughput when performing a batch update. Has no effect when only reading data.)<br />
* isc_tpb_lock_timeout (specify number of seconds to wait for lock release, if you use isc_tpb_wait. If this value is reached without lock release, an error is reported.)<br />
<br />
=== Firebird and ISO transactions ===<br />
Firebird transaction do not map 1 to 1 to ISO/ANSI transaction levels. An approximation is:<br />
* ISO Read Committed=READ COMMITTED+RECORD_VERSION<br />
* ISO Read Committed=READ COMMITTED+NO RECORD_VERSION<br />
* ISO Repeatable Read=SNAPSHOT (also known as CONCURRENCY)<br />
* ISO Serializable=SNAPSHOT TABLE STABILITY (also known as CONSISTENCY)<br />
<br />
=== Common combinations ===<br />
Default is (probably, will have to check) read committed.<br />
<br />
==== Batch/bulk insert ====<br />
isc_tpb_read_committed and isc_tpb_no_rec_version could be a good combination: it allows other transactions to function while the batch is going on.<br />
<br />
==== Read only transaction ====<br />
If you want to only have read access to the database, you can do so by setting these transaction parameters:<br />
* <code>isc_tpb_read</code><br />
* <code>isc_tpb_read_committed</code><br />
* <code>isc_tpb_rec_version</code><br />
* <code>nowait</code><br />
This combination will not block garbage collection, which is a good thing.<br />
Source: [http://tech.groups.yahoo.com/group/firebird-support/message/118748]<br />
<br />
Note: the [http://www.firebirdfaq.org/faq164/ Firebird FAQ] indicates you will need write access to the database file even if you only read from it, unless you set the database read-only flag (e.g. using gfix).<br />
<br />
== Links and more information ==<br />
<br />
The list below shows links to more information on Firebird and related tools.<br />
* [[Firebird_embedded|Using Firebird embedded with FPC/Lazarus]]<br />
<br />
=== Lazarus Firebird samples ===<br />
* Sample Lazarus/Firebird application source code [http://lazarus.freepascal.org/index.php/topic,13940.msg73617.html#msg73617]<br />
* Firebird/SQLDB tutorials: <br />
** [[SQLdb Tutorial0]]<br />
** [[SQLdb Tutorial1]]<br />
** [[SQLdb Tutorial2]]<br />
** [[SQLdb Tutorial3]]<br />
** [[SQLdb Tutorial4]]<br />
<br />
*PP4S tutorials; cover Firebird installation on Windows as well:<br />
** [http://www.pp4s.co.uk/main/tu-db-plan.html Planning a Database]<br />
** [http://www.pp4s.co.uk/main/tu-db-installingfirebird01.html Installing Firebird]<br />
** [http://www.pp4s.co.uk/main/tu-db-firebird-create.html Creating a Firebird database]<br />
** [http://www.pp4s.co.uk/main/tu-db-firebird-demo1.html Editing a Firebird database]<br />
** [http://www.pp4s.co.uk/main/tu-db-firebird-demo2.html Searching a Firebird database]<br />
** [http://www.pp4s.co.uk/main/tu-db-firebird-demo3.html Creating and printing a report]<br />
** [http://www.pp4s.co.uk/main/tu-db-firebird-demo4.html Creating and using stored procedures]<br />
<br />
=== Tools ===<br />
* FlameRobin [http://www.flamerobin.org/ Flamerobin site] Open source GUI tool to manage Firebird, available for Linux, Windows and Mac OSX. Highly recommended.<br />
* Turbobird [https://github.com/motaz/turbobird Turbobird site] Open source GUI tool to manage Firebird. Written with Lazarus using TIBConnection<br />
* ibconsole : Tool to manage Firebird an Interbase Databases with a GUI, available for Windows and Linux<br />
* Lazarus Data Desktop - included in the Lazarus repository (found in the 'tools/lazdatadesktop/' directory)<br />
* [http://lazsqlx.wordpress.com/ LazSQLX] Multi-database open source database management tool. Written with Lazarus using both SQLDB and Zeos components. Includes support for Firebird. <br />
* tiSQLEditor - included in the "Support Apps" directory of the [[tiOPF]] repository. It is a tool used to write SQL with some code completion support, can run scripts, execute upgrade scripts for applications from one build to a later build, has various handy copy and paste functions for Object Pascal applications, has many features that are useful to tiOPF (creates Object Pascal code templates from query results, for tiOPF's visitor classes), export query results to CSV etc.<br />
<br />
=== Firebird ===<br />
* The Firebird RDBMS [http://firebirdsql.org/ Firebird site]. This site also contains a lot of documentation on Firebird.<br />
* Firebird FAQ [http://firebirdfaq.org/]. Handy site that shows e.g. differences with other RDBMS.<br />
<br />
[[Category:Databases]]<br />
[[Category:Tutorials]]<br />
[[Category:Firebird]]</div>Zoranhttps://wiki.freepascal.org/index.php?title=chm_backend_for_fpdoc&diff=101929chm backend for fpdoc2016-07-14T08:45:50Z<p>Zoran: Add to CHM Category</p>
<hr />
<div>=== Examples === <br />
This link has information about the TOC and Index files in chms: http://www.nongnu.org/chmspec/latest/Sitemap.html<br />
<br />
These formats are based on HTML and use the following doctype:<br />
<br />
<syntaxhighlight lang="html4strict"><!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"></syntaxhighlight><br />
<br />
The <HEAD> tag contains a <meta> tag providing information on the program that generated the files and a comment indicating the version of the file, e.g.:<br />
<br />
<syntaxhighlight lang="html4strict"><br />
<meta name="GENERATOR"content="Microsoft® HTML Help Workshop 4.1"><br />
<!-- Sitemap 1.0 --><br />
</syntaxhighlight><br />
<br />
The <BODY> tag contains an <OBJECT> tag that stores properties of the file in <param> tags, followed by a <nowiki><UL></nowiki> tag, whose <nowiki><LI></nowiki> tags have <OBJECT> tags that store the properties of the Contents/Index items in <param> tags. e.g.:<br />
<syntaxhighlight lang="html4strict"><br />
<BODY><br />
<OBJECT type="text/site properties"><br />
<param name="Property Name" value="Property Value"><br />
…<br />
</OBJECT><br />
<UL><br />
<LI> <OBJECT type="text/sitemap"><br />
<param name="Property Name" value="Property Value"><br />
…<br />
</OBJECT><br />
…<br />
</UL><br />
</BODY><br />
</syntaxhighlight><br />
<br />
Note that the Property Names and Property Values and tags are not case-sensitive, but HHW will always write all three in the default capitalization, when appropriate.<br />
<br />
Note that the tags are mostly in uppercase and the <nowiki><LI></nowiki> tag is not closed; this is in compliance with the doctype.<br />
<br />
Some properties that were seen in HHA.dll that may or may not be used are Background Image, NumberImages, InformationTypeDecl, Secondary, Icon, Display, Keyword, Instruction, Section Title, Favorites, QueryType, SendEvent, SendMessage, HHI, Inclusive & Exclusive.<br />
<br />
This the beginning chunk of an autogenerated (the autogenerated TOC stinks) hhc (Table of Contents/TOC) file for the RTL:<br />
.hhk are in the same format but are for the Index pane and do not have subitems.<br />
<syntaxhighlight lang="html4strict"><br />
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"><br />
<HTML><br />
<HEAD><br />
<meta name="GENERATOR" content="Microsoft® HTML Help Workshop 4.1"><br />
<!-- Sitemap 1.0 --><br />
</HEAD><BODY><br />
<OBJECT type="text/site properties"><br />
<param name="Auto Generated" value="Yes"><br />
</OBJECT><br />
<UL><br />
<LI> <OBJECT type="text/sitemap"><br />
<param name="Name" value="Reference for package 'rtl'"><br />
<param name="Local" value="rtl/index.html"><br />
</OBJECT><br />
<UL><br />
<LI> <OBJECT type="text/sitemap"><br />
<param name="Name" value="Units"><br />
<param name="Local" value="rtl/index.html"><br />
</OBJECT><br />
<LI> <OBJECT type="text/sitemap"><br />
<param name="Name" value="Description"><br />
<param name="Local" value="rtl/index.html"><br />
</OBJECT><br />
</UL><br />
<LI> <OBJECT type="text/sitemap"><br />
<param name="Name" value="Reference for unit 'BaseUnix'"><br />
<param name="Local" value="rtl/baseunix/index.html"><br />
</OBJECT><br />
<UL><br />
<LI> <OBJECT type="text/sitemap"><br />
<param name="Name" value="Overview"><br />
<param name="Local" value="rtl/baseunix/index.html"><br />
</OBJECT><br />
</UL><br />
<br />
......<br />
</UL><br />
</BODY></HTML><br />
</syntaxhighlight><br />
<br />
== Generating FPC CHM docs via fpdoc ==<br />
<br />
As of October 2008, the fpdoc part of the documentation (the part that documents the various units) can be compiled to CHM fairly easily, a <br />
<br />
<syntaxhighlight lang="bash">make clean html HTMLFMT=chm CSSFILE=/path/to/fpdoc.css</syntaxhighlight><br />
<br />
will generate rtl.chm and fcl.chm. Note that the cssfile is currently in a different repository (I used fpc/utils/fpdoc/fpdoc.css since it has the correct name). A script '''fixdocs.sh''' automates most of the CHM related options. (*nix, requires latex + tex4ht)<br />
<br />
Significant fixes to the CHM, XML and makefiles backend were introduced in the sources (fpcdocs,chm, fcl-xml and fpdoc) from the 2.3.1 branch, July 31st 2009 and newer.<br />
<br />
=== Remaining problems ===<br />
<br />
# The text mode IDE crashes on loading the LCL.chm's index on all architectures and OSes. Other CHM viewers don't seem to suffer from this, but there is always a chance it is CHM pkg related. ([b]Provisionally fixed[/b])<br />
# The prog and user documents don't have an index. Ref guide only has keywords<br />
# FCL-XML generates UTF-8, which some CHM browsers (kchmviewer) don't seem to handle properly. ''Note: is this still applicable in March 2014?'' Workaround: ISO8859-1 default? KCHMviewer seem to work fine on windows.<br />
# compiling LCL finds a lot of unknown identifiers. These should be documented?<br />
# crosslinking between user,ref,prog and fpdoc?<br />
<br />
And the major one:<br />
<br />
# How to combine the indexes to a master index (including duplicate handling)? Currently additional disambiguating context is only added to the index if a dupe _within_ a CHM is detected.<br />
<br />
== Generating LCL CHM via build_lcl_docs ==<br />
Lazarus provides the build_lcl_docs.lpr tool: a wrapper which simplifies calling fpdoc to generate an LCL CHM.<br />
<br />
It can be run both in GUI mode and command line mode.<br />
<br />
Useful command line parameters<br />
# <code>--fpdoc</code> <fpdoc executable to be used to generate CHM><br />
# <code>--css-file=</code><css file to be used for layout, e.g. fpdoc.css><br />
# <code>--outfmt chm</code> Set output format to CHM<br />
<br />
== Generating LCL CHM via fpdoc ==<br />
Only tested on *nix<br />
<br />
{{Warning|this script enables all fpdoc bells and whistles, takes about 4 minutes and 400MB memory on a Core2-6600. If you want to do this regularly, make sure you have a fpdoc from trunk, preferably from a checkout that was compiled with optimization on which saves about half a minute}}<br />
<br />
<syntaxhighlight lang="bash"><br />
export HTMLFMT=chm<br />
cd lazarus/docs/html<br />
sh build_lcl_html.sh "fpdoc" "" /path/to/fpcdocs<br />
</syntaxhighlight><br />
<br />
The generated .xct file is an index file for fpdoc cross file links, and is of no use to the end user, unless you want to link to the chm. That is also why we have to specify the fpcdocs archive. If we have previously built the FPC docs there, lcl will use this to craft cross CHM links.<br />
<br />
== Generating FPC CHM docs for the latex documentation ==<br />
<br />
Building CHMs for the documentation that is not in fpdoc format (but normal latex files) is harder. The process can be fairly easily be separated into two stages:<br />
<br />
# generate HTML from latex sources<br />
# compile HTML to CHM<br />
## raw compile<br />
## generating indexes and TOC<br />
<br />
=== Generating HTML ===<br />
<br />
The first is currently the hard part. During the years several converters have been tried, the current one used is tex4ht. While the theory is easy (install latex, install tex4ht, run make html), the practice is difficult (*), and the generated docs are usually corrupt (ligatures converted to pics or unicode escapes, bad "next" links etc). Luckily, these docs mutates less often, so one probably can use a generated set of CHMs for the entire duration of a release cycle. Still it would be great to have a definitive, reproducable solution.<br />
<br />
For the moment, the "bad" output of tex4ht is fixed by a DOM based (FCL-XML) FPC tool, relinkdocs.<br />
Relinkdocs scans the HTML docs and relinks the next/up/prev links. <br />
<br />
(*) to the degree that the version on the FPC front page is more often broken than not.<br />
<br />
=== Compiling and TOC generation ===<br />
<br />
To compile the resulting HTML to CHM, another tool was written, called compilelatexchm, which also relies on FCL-XML for the TOC scanning. The CHM is searchable, but does not yet have an index. (I don't know a suitable algoritm to come up with the keywords)<br />
<br />
A batch file that automates these steps is added to the fpcdocs repository (fixdocs.sh), but is not idiot proof yet. If you have troubles, ask me (Marco/oliebol) on IRC.<br />
<br />
=== Plastex ===<br />
<br />
A week after I did the above fixing of tex4ht output, Andrew Haines came with a patch that supported using PlasTeX as tex2chm converter. I don't entirely like the output (chapter and paragraph numbers drop off, which makes it harder to correspond over documentation, and the template is a bit playful), but I haven't looked into customizing it at all, so maybe something can be done there. I haven't tested the generated CHMs with the textmode IDE yet too.<br />
<br />
One can select PlasTeX by passing USEPLASTEX=1 to the make process.<br />
<br />
== chmcmd understood options ==<br />
<br />
While the chmcmd program responds to e.g. --help by outputting the command-line options it understands, it doesn't report what options are acceptable in the .hhp file. Refer to the OptionKeys constant in fpcsrc/packages/chm/src/chmfilewriter.pas, and documentation available at e.g. [http://www.nongnu.org/chmspec/latest/INI.html#HHP].<br />
<br />
For anybody determined to "roll their own", an absolutely minimal .hhp file reads something like<br />
<br />
[OPTIONS]<br />
Compiled file=test.chm<br />
Contents file=./Default.hhc<br />
[FILES]<br />
./Borg-UI_MainForm_HelpButton.html<br />
./Borg-UM_MainForm_HelpButton.html<br />
<br />
with the TOC file being<br />
<syntaxhighlight lang="html4strict"><br />
<html><head></head><body><object type="text/site properties"></object><ul><br />
<li><object type="text/sitemap"><br />
<param name="Name" value="Borg-UI_MainForm_HelpButton"><br />
<param name="Local" value="Borg-UI_MainForm_HelpButton.html"><br />
</object><br />
<li><object type="text/sitemap"><br />
<param name="Name" value="Borg-UM_MainForm_HelpButton"><br />
<param name="Local" value="Borg-UM_MainForm_HelpButton.html"><br />
</object><br />
</ul></body></html><br />
</syntaxhighlight><br />
<br />
== Troubleshooting ==<br />
<br />
=== Trouble viewing in Vista ===<br />
<br />
The chm file could be marked as blocked. To unblock, use the chmls tool (included in $(fpcdir)\packages\chm\src\):<br />
<syntaxhighlight lang="dos"><br />
chmls unblockchm yourfile.chm<br />
</syntaxhighlight><br />
<br />
==== Alternative ways and background ====<br />
Have a look at [http://blog.crowe.co.nz/archive/2007/04/13/719.aspx this link]. We still must find something that make the installer do this automatically.<br />
<br />
Or turn it off entirely using:<br />
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\HTMLHelp\1.x\HHRestrictions]<br />
"MaxAllowedZone"=dword:00000001<br />
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\HTMLHelp\1.x\ItssRestrictions]<br />
"MaxAllowedZone"=dword:00000001<br />
<br />
You can add a registry key to repair this issue in XP and Vista by setting the ItssRestrictions.MaxAllowedZone to 3 or 4 or 5.<br />
<br />
A simpler solution is to unzip with e.g. infozip, instead of Windows built-in support. (beware, newer infozips might conform at any time!)<br />
<br />
Apparently this is an alternate filestream:<br />
http://stackoverflow.com/questions/1617509/unblock-a-file-with-powershell<br />
<br />
<br />
[[Category:Help and Docs]][[Category:CHM]]</div>Zoran