Sign in Join
Wiki copytemporary

Extending the IDE: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11# Extending the IDE
22
33## Overview
44
5-The Lazarus IDE supports several types of plugins:
5+The Lazarus IDE supports several types of plugins:
66
7-Components
7+**Components**
8+
89These are the items found on the Component Palette. For example, TButton (found on the Standard page) can be used to create Buttons.
910
10-Component Editors
11+**Component Editors**
12+
1113When you double-click a component in the Designer, a component editor is invoked. The component editor may also add an extra item to the Designer's popup menu (accessed when you right-click on a component in the Designer).
1214
13-Property Editors
15+**Property Editors**
16+
1417Each row in the grid section of the Object Inspector uses a property editor to allow that property's value to be set or altered.
1518
16-Experts
19+**Experts**
20+
1721Experts encompass all other plugin types. They might register new menu item(s), register short cuts, or extend other IDE features.
1822
19-There are two possible ways to add your own plugins to the Lazarus IDE:
23+
24+There are two possible ways to add your own plugins to the Lazarus IDE:
2025
21-1. Write a package, install it and register your plugin(s) in the 'Register' procedure of a unit.
22-2. Extend the Lazarus code, and send your svn diff to the Lazarus mailing list.
26+ 1. Write a package, install it and register your plugin(s) in the 'Register' procedure of a unit.
27+ 2. Extend the Lazarus code, and send your svn diff to the Lazarus mailing list.
2328
2429## Packages and Makefile Dependencies
2530
26-The following steps are necessary, if adding new package dependencies.
31+The following steps are necessary, if adding new package dependencies.
2732
28-- Create/Update the Makefile for the package via Package editor / More ... / Create Makefile.
29-- Fix all dependencies. See tools/updatemakefiles.lpi
33+ * Create/Update the Makefile for the package via Package editor / More ... / Create Makefile.
34+ * Fix all dependencies. See tools/updatemakefiles.lpi
3035
3136# Menu items
3237
33-You can add new menu items, hide or move existing ones and add sub menus, sections. For your own forms you can register the TMainMenu/TPopupMenu so that other packages can extend your form further. The unit **MenuIntf** of the package IDEIntf contains all registration for menus and many standard menu items of the IDE itself.
38+You can add new menu items, hide or move existing ones and add sub menus, sections. For your own forms you can register the TMainMenu/TPopupMenu so that other packages can extend your form further. The unit **MenuIntf** of the package IDEIntf contains all registration for menus and many standard menu items of the IDE itself.
3439
3540## Adding a menu item
3641
37-A single menu item like 'View Object Inspector' is called a **TIDEMenuCommand**. You can create one with **RegisterIDEMenuCommand**, which has two forms with lots of parameters:
38-
39-``` pascal
42+A single menu item like 'View Object Inspector' is called a **TIDEMenuCommand**. You can create one with **RegisterIDEMenuCommand** , which has two forms with lots of parameters:
43+```
4044function RegisterIDEMenuCommand(Parent: TIDEMenuSection;
41- const Name, Caption: string;
42- const OnClickMethod: TNotifyEvent = nil;
43- const OnClickProc: TNotifyProcedure = nil;
44- const Command: TIDECommand = nil;
45- const ResourceName: String = ''
46- ): TIDEMenuCommand; overload;
47-function RegisterIDEMenuCommand(const Path, Name, Caption: string;
48- const OnClickMethod: TNotifyEvent = nil;
49- const OnClickProc: TNotifyProcedure = nil;
50- const Command: TIDECommand = nil;
51- const ResourceName: String = ''
52- ): TIDEMenuCommand; overload;
45+ const Name, Caption: string;
46+ const OnClickMethod: TNotifyEvent = nil;
47+ const OnClickProc: TNotifyProcedure = nil;
48+ const Command: TIDECommand = nil;
49+ const ResourceName: String = ''
50+ ): TIDEMenuCommand; overload;
51+ function RegisterIDEMenuCommand(const Path, Name, Caption: string;
52+ const OnClickMethod: TNotifyEvent = nil;
53+ const OnClickProc: TNotifyProcedure = nil;
54+ const Command: TIDECommand = nil;
55+ const ResourceName: String = ''
56+ ): TIDEMenuCommand; overload;
5357```
5458
55-The difference between those two is only how you specify the parent menu section. You can give the menu section directly or indirectly via the path. Many standard sections can be found in the *MenuIntf* unit. For example the section **mnuTools**, which is the *Tools* menu of the main IDE bar. It contains a section **itmSecondaryTools**, the recommended section for third party tools.
56-
57-The following registers a new menu command, with the name 'MyTool', the caption 'Start my tool' and when clicked executes the procedure *StartMyTool*:
59+The difference between those two is only how you specify the parent menu section. You can give the menu section directly or indirectly via the path. Many standard sections can be found in the _MenuIntf_ unit. For example the section **mnuTools** , which is the _Tools_ menu of the main IDE bar. It contains a section **itmSecondaryTools** , the recommended section for third party tools.
5860
59-``` pascal
61+The following registers a new menu command, with the name 'MyTool', the caption 'Start my tool' and when clicked executes the procedure _StartMyTool_ :
62+```
6063procedure StartMyTool;
61-begin
62- ...executed when menu item is clicked...
63-end;
64-
65-procedure Register;
66-begin
67- RegisterIDEMenuCommand(itmSecondaryTools, 'MyTool','Start my tool',nil,@StartMyTool);
68-end;
64+ begin
65+ ...executed when menu item is clicked...
66+ end;
67+
68+ procedure Register;
69+ begin
70+ RegisterIDEMenuCommand(itmSecondaryTools, 'MyTool','Start my tool',nil,@StartMyTool);
71+ end;
6972```
7073
71-If you want to call a method instead of a procedure use the **OnClickMethod** parameter.
72-
73-This menu item does not have a shortcut. If you want a shortcut you must create a **TIDECommand** and pass it in the **Command** parameter. For example:
74+If you want to call a method instead of a procedure use the **OnClickMethod** parameter.
7475
75-``` pascal
76+This menu item does not have a shortcut. If you want a shortcut you must create a **TIDECommand** and pass it in the **Command** parameter. For example:
77+```
7678uses ... IDECommands, MenuIntf;
77-...
78-var
79- Key: TIDEShortCut;
80- Cat: TIDECommandCategory;
81- CmdMyTool: TIDECommand;
82-begin
83- // register IDE shortcut and menu item
84- Key := IDEShortCut(VK_UNKNOWN,[],VK_UNKNOWN,[]);
85- Cat:=IDECommandList.FindCategoryByName(CommandCategoryToolMenuName);
86- CmdMyTool := RegisterIDECommand(Cat,'Start my tool', 'Starts my tool to do some stuff', Key, nil, @StartMyTool);
87- RegisterIDEMenuCommand(itmSecondaryTools, 'MyTool', 'Start my tool', nil, nil, CmdMyTool);
88-end;
79+ ...
80+ var
81+ Key: TIDEShortCut;
82+ Cat: TIDECommandCategory;
83+ CmdMyTool: TIDECommand;
84+ begin
85+ // register IDE shortcut and menu item
86+ Key := IDEShortCut(VK_UNKNOWN,[],VK_UNKNOWN,[]);
87+ Cat:=IDECommandList.FindCategoryByName(CommandCategoryToolMenuName);
88+ CmdMyTool := RegisterIDECommand(Cat,'Start my tool', 'Starts my tool to do some stuff', Key, nil, @StartMyTool);
89+ RegisterIDEMenuCommand(itmSecondaryTools, 'MyTool', 'Start my tool', nil, nil, CmdMyTool);
90+ end;
8991```
9092
9193# Shortcuts
9294
93-All shortcuts (keys that invoke some action) are registered in the IDECommandList. Here is an example how to find all commands that react on key Ctrl-D or two key combinations starting with Ctrl-D:
94-
95-``` pascal
95+All shortcuts (keys that invoke some action) are registered in the IDECommandList. Here is an example how to find all commands that react on key Ctrl-D or two key combinations starting with Ctrl-D:
96+```
9697uses
97- Classes, SysUtils, LCLProc, IDECommands;
98-
99-procedure ListCtrlD;
100-// list all idecommands with shortcut Ctrl-D
101-var
102- i: integer;
103- Cmd: TIDECommand;
104- Commands: TFPList;
105-begin
106- Commands:=IDECommandList.FindCommandsByShortCut(IDEShortCut(VK_D,[],VK_UNKNOWN,[]));
107- try
108- for i:=0 to Commands.Count-1 do begin
109- Cmd:=TIDECommand(Commands[i]);
110- writeln('Cmd: ',Cmd.Name,
111- ' A=',dbgs(Cmd.ShortcutA.Shift1),'-',Cmd.ShortcutA.Key1,
112- ',',dbgs(Cmd.ShortcutA.Shift2),'-',Cmd.ShortcutA.Key2,
113- ' B=',dbgs(Cmd.ShortcutB.Shift1),'-',Cmd.ShortcutB.Key1,
114- ',',dbgs(Cmd.ShortcutB.Shift2),'-',Cmd.ShortcutB.Key2
115- );
98+ Classes, SysUtils, LCLProc, IDECommands;
99+
100+ procedure ListCtrlD;
101+ // list all idecommands with shortcut Ctrl-D
102+ var
103+ i: integer;
104+ Cmd: TIDECommand;
105+ Commands: TFPList;
106+ begin
107+ Commands:=IDECommandList.FindCommandsByShortCut(IDEShortCut(VK_D,[],VK_UNKNOWN,[]));
108+ try
109+ for i:=0 to Commands.Count-1 do begin
110+ Cmd:=TIDECommand(Commands[i]);
111+ writeln('Cmd: ',Cmd.Name,
112+ ' A=',dbgs(Cmd.ShortcutA.Shift1),'-',Cmd.ShortcutA.Key1,
113+ ',',dbgs(Cmd.ShortcutA.Shift2),'-',Cmd.ShortcutA.Key2,
114+ ' B=',dbgs(Cmd.ShortcutB.Shift1),'-',Cmd.ShortcutB.Key1,
115+ ',',dbgs(Cmd.ShortcutB.Shift2),'-',Cmd.ShortcutB.Key2
116+ );
117+ end;
118+ finally
119+ Commands.Free;
120+ end;
116121 end;
117- finally
118- Commands.Free;
119- end;
120-end;
121122```
122123
123124# Config files
124125
125126## Load/Save settings
126127
127-All configuration files of the IDE are stored in one directory as xml files. Packages can add their own files there too. The [[pcp|primary config path]] directory can be read with:
128-
129-``` pascal
128+All configuration files of the IDE are stored in one directory as xml files. Packages can add their own files there too. The [[pcp|primary config path]] directory can be read with:
129+```
130130uses LazIDEIntf;
131-...
132- Directory := LazarusIDE.GetPrimaryConfigPath;
131+ ...
132+ Directory := LazarusIDE.GetPrimaryConfigPath;
133133```
134134
135-Packages can create their own xml files with:
136-
137-``` pascal
135+Packages can create their own xml files with:
136+```
138137uses
139- ..., LCLProc, BaseIDEIntf, LazConfigStorage;
140-
141-const
142- Version = 1;
143-var
144- Config: TConfigStorage;
145- SomeValue: String;
146- SomeInteger: Integer;
147- SomeBoolean: Boolean;
148- FileVersion: LongInt;
149-begin
150- SomeValue:='Default';
151- SomeInteger:=3;
152- SomeBoolean:=true;
153-
154- // save settings
155- try
156- Config:=GetIDEConfigStorage('mysettings.xml',false);
157- try
158- // store the version number so future extensions can handle old config files
159- Config.SetDeleteValue('Path/To/The/Version',Version,0);
160- // store string variable "SomeValue"
161- // if SomeValue has the default value the entry is not stored,
162- // so only the differences to the default are stored.
163- // This way the xml is kept short and defaults may change in future.
164- Config.SetDeleteValue('Path/To/Some/Value',SomeValue,'Default');
165- Config.SetDeleteValue('Path/To/Some/Integer',SomeInteger,3);
166- Config.SetDeleteValue('Path/To/Some/Boolean',SomeBoolean,true);
167- // there are more overloads for SetDeleteValue, find out with Ctrl+Space
168- finally
169- Config.Free;
170- end;
171-
172- except
173- on E: Exception do begin
174- DebugLn(['Saving mysettings.xml failed: ',E.Message]);
175- end;
176- end;
177-
178- // load settings
179- try
180- Config:=GetIDEConfigStorage('mysettings.xml',true);
181- try
182- // read the version of the config
183- FileVersion:=Config.GetValue('Path/To/The/Version',0);
184- // read a string variable "SomeValue". If the entry does not exist, use
185- // the Default
186- SomeValue:=Config.GetValue('Path/To/Some/Value','Default');
187- SomeInteger:=Config.GetValue('Path/To/Some/Integer',3);
188- SomeBoolean:=Config.GetValue('Path/To/Some/Boolean',true);
189- finally
190- Config.Free;
191- end;
192- except
193- on E: Exception do begin
194- DebugLn(['Loading mysettings.xml failed: ',E.Message]);
138+ ..., LCLProc, BaseIDEIntf, LazConfigStorage;
139+
140+ const
141+ Version = 1;
142+ var
143+ Config: TConfigStorage;
144+ SomeValue: String;
145+ SomeInteger: Integer;
146+ SomeBoolean: Boolean;
147+ FileVersion: LongInt;
148+ begin
149+ SomeValue:='Default';
150+ SomeInteger:=3;
151+ SomeBoolean:=true;
152+
153+ // save settings
154+ try
155+ Config:=GetIDEConfigStorage('mysettings.xml',false);
156+ try
157+ // store the version number so future extensions can handle old config files
158+ Config.SetDeleteValue('Path/To/The/Version',Version,0);
159+ // store string variable "SomeValue"
160+ // if SomeValue has the default value the entry is not stored,
161+ // so only the differences to the default are stored.
162+ // This way the xml is kept short and defaults may change in future.
163+ Config.SetDeleteValue('Path/To/Some/Value',SomeValue,'Default');
164+ Config.SetDeleteValue('Path/To/Some/Integer',SomeInteger,3);
165+ Config.SetDeleteValue('Path/To/Some/Boolean',SomeBoolean,true);
166+ // there are more overloads for SetDeleteValue, find out with Ctrl+Space
167+ finally
168+ Config.Free;
169+ end;
170+
171+ except
172+ on E: Exception do begin
173+ DebugLn(['Saving mysettings.xml failed: ',E.Message]);
174+ end;
175+ end;
176+
177+ // load settings
178+ try
179+ Config:=GetIDEConfigStorage('mysettings.xml',true);
180+ try
181+ // read the version of the config
182+ FileVersion:=Config.GetValue('Path/To/The/Version',0);
183+ // read a string variable "SomeValue". If the entry does not exist, use
184+ // the Default
185+ SomeValue:=Config.GetValue('Path/To/Some/Value','Default');
186+ SomeInteger:=Config.GetValue('Path/To/Some/Integer',3);
187+ SomeBoolean:=Config.GetValue('Path/To/Some/Boolean',true);
188+ finally
189+ Config.Free;
190+ end;
191+ except
192+ on E: Exception do begin
193+ DebugLn(['Loading mysettings.xml failed: ',E.Message]);
194+ end;
195+ end;
195196 end;
196- end;
197-end;
198197```
199198
200-Notes:
199+Notes:
201200
202-- Every IDE plugin should use its own config file. Especially you must not use the standard IDE option files like [[editoroptions.xml]] or [[environmentoptions.xml]].
203-- GetIDEConfigStorage when loading checks if the file exists and if not it will copy the template from the secondary config directory.
204-- Since 0.9.31 you can use this function to load a file outside the primary config directory.
201+ * Every IDE plugin should use its own config file. Especially you must not use the standard IDE option files like [[editoroptions.xml]] or [[environmentoptions.xml]].
202+ * GetIDEConfigStorage when loading checks if the file exists and if not it will copy the template from the secondary config directory.
203+ * Since 0.9.31 you can use this function to load a file outside the primary config directory.
205204
206205# Components
207206
208207## Extending existing components
209208
210-Source: [1](http://lazarus.freepascal.org/index.php/topic,17733.msg98425.html#msg98425) It is straightforward to customise existing LCL components, e.g. if you want to add properties.
211-
212-Add the properties you need with appropriate setter and getter methods and storage fields.
209+Source: [[1]](<http://lazarus.freepascal.org/index.php/topic,17733.msg98425.html#msg98425>) It is straightforward to customise existing LCL components, e.g. if you want to add properties.
213210
214-Then instantiate them 'by hand', giving an owner component (to handle later destruction), and set Parent and possibly Top and Left and any other defaults your component needs when initialized.
211+Add the properties you need with appropriate setter and getter methods and storage fields.
215212
216-When you are satisfied that your customised component is debugged, add a RegisterComponent() call to stick it on the IDE Component Palette and save you having to do the setup code 'by hand' (look at any component's source to see how to do this).
213+Then instantiate them 'by hand', giving an owner component (to handle later destruction), and set Parent and possibly Top and Left and any other defaults your component needs when initialized.
217214
218-Here is a simple example:
215+When you are satisfied that your customised component is debugged, add a RegisterComponent() call to stick it on the IDE Component Palette and save you having to do the setup code 'by hand' (look at any component's source to see how to do this).
219216
220-``` pascal
217+Here is a simple example:
218+```
221219unit Unit1;
222-
223-{$mode objfpc}{$H+}
224-
225-interface
226-
227-uses
228- Classes, SysUtils, Forms, StdCtrls;
229-
230-type
231- TEdX = class(TEdit)
232- private
233- FNewIntProp: integer;
234- FNewStrProp: string;
235- procedure SetNewIntProp(AValue: integer);
236- procedure SetNewStrProp(AValue: string);
237- public
238- constructor Create(theComponent: TComponent); override;
239- property NewIntProp: integer read FNewIntProp write SetNewIntProp;
240- property NewStrProp: string read FNewStrProp write SetNewStrProp;
241- end;
242-
243- TForm1 = class(TForm)
244- procedure FormCreate(Sender: TObject);
245- private
246- edX: TEdX;
247- public
248- end;
249-
250-var
251- Form1: TForm1;
252-
253-implementation
254-
255-{ TForm1 }
256-
257-procedure TForm1.FormCreate(Sender: TObject);
258-begin
259- edX:= TEdX.Create(Self);
260- edX.Parent:= Self;
261- edX.Caption:= IntToStr(edX.NewIntProp);
262- Caption:= edX.NewStrProp;
263-end;
264-
265-{$R *.lfm}
266-
267-{ TEdX }
268-
269-procedure TEdX.SetNewIntProp(AValue: integer);
270-begin
271- if FNewIntProp=AValue then Exit;
272- FNewIntProp:=AValue;
273- // add custom code here
274-end;
275-
276-procedure TEdX.SetNewStrProp(AValue: string);
277-begin
278- if FNewStrProp=AValue then Exit;
279- FNewStrProp:=AValue;
280- // add custom code here
281-end;
282-
283-constructor TEdX.Create(theComponent: TComponent);
284-begin
285- inherited Create(theComponent);
286- FNewStrProp:= 'new string property value';
287- FNewIntProp:= 99;
288-end;
289-
290-end.
220+
221+ {$mode objfpc}{$H+}
222+
223+ interface
224+
225+ uses
226+ Classes, SysUtils, Forms, StdCtrls;
227+
228+ type
229+ TEdX = class(TEdit)
230+ private
231+ FNewIntProp: integer;
232+ FNewStrProp: string;
233+ procedure SetNewIntProp(AValue: integer);
234+ procedure SetNewStrProp(AValue: string);
235+ public
236+ constructor Create(theComponent: TComponent); override;
237+ property NewIntProp: integer read FNewIntProp write SetNewIntProp;
238+ property NewStrProp: string read FNewStrProp write SetNewStrProp;
239+ end;
240+
241+ TForm1 = class(TForm)
242+ procedure FormCreate(Sender: TObject);
243+ private
244+ edX: TEdX;
245+ public
246+ end;
247+
248+ var
249+ Form1: TForm1;
250+
251+ implementation
252+
253+ { TForm1 }
254+
255+ procedure TForm1.FormCreate(Sender: TObject);
256+ begin
257+ edX:= TEdX.Create(Self);
258+ edX.Parent:= Self;
259+ edX.Caption:= IntToStr(edX.NewIntProp);
260+ Caption:= edX.NewStrProp;
261+ end;
262+
263+ {$R *.lfm}
264+
265+ { TEdX }
266+
267+ procedure TEdX.SetNewIntProp(AValue: integer);
268+ begin
269+ if FNewIntProp=AValue then Exit;
270+ FNewIntProp:=AValue;
271+ // add custom code here
272+ end;
273+
274+ procedure TEdX.SetNewStrProp(AValue: string);
275+ begin
276+ if FNewStrProp=AValue then Exit;
277+ FNewStrProp:=AValue;
278+ // add custom code here
279+ end;
280+
281+ constructor TEdX.Create(theComponent: TComponent);
282+ begin
283+ inherited Create(theComponent);
284+ FNewStrProp:= 'new string property value';
285+ FNewIntProp:= 99;
286+ end;
287+
288+ end.
291289```
292290
293291## Writing components
294292
295-*Main article: [[How To Write Lazarus Component]]*
293+_Main article:[[How To Write Lazarus Component]]_
296294
297-You can create new components via the package editor.
295+You can create new components via the package editor.
298296
299-Create or open a package, click on **add**, click on **New Component**, fill in the items: Ancestor Type = TButton, New class name = TMyButton, palette page = Misc, Unit file name = mybutton.pas (this file will be created), unit name MyButton and click **ok**.
297+Create or open a package, click on **add** , click on **New Component** , fill in the items: Ancestor Type = TButton, New class name = TMyButton, palette page = Misc, Unit file name = mybutton.pas (this file will be created), unit name MyButton and click **ok**.
300298
301299### Giving a new component an icon for the component palette
302300
303-Create an image file of the format .bmp, .xpm or .png with the same name as the component class. For example *tmybutton.png* and save it in the package source directory. The image can be created by any graphic program (e.g. gimp) and should be no bigger than 24x24 pixel. Then convert the image to a resource file with the lazres tool, which can be found in the lazarus/tools directory:
304-
305-``` bash
301+Create an image file of the format .bmp, .xpm or .png with the same name as the component class. For example _tmybutton.png_ and save it in the package source directory. The image can be created by any graphic program (e.g. gimp) and should be no bigger than 24x24 pixel. Then convert the image to a resource file with the lazres tool, which can be found in the lazarus/tools directory:
302+```
306303~/lazarus/tools/lazres mybutton.lrs tmybutton.png
307-# note: Laz trunk/1.4+? uses .res resource files instead of .lrs files:
308-~/lazarus/tools/lazres mybutton.res tmybutton.png
304+ # note: Laz trunk/1.4+? uses .res resource files instead of .lrs files:
305+ ~/lazarus/tools/lazres mybutton.res tmybutton.png
309306```
310307
311-This creates an pascal include file, which is used in the initialization section of mybutton.pas:
312-
313-``` pascal
308+This creates an pascal include file, which is used in the initialization section of mybutton.pas:
309+```
314310initialization
315- {$I mybutton.lrs}
316- // or in newer Lazarus version - to docheck syntax
317- {$R mybutton.res}
311+ {$I mybutton.lrs}
312+ // or in newer Lazarus version - to docheck syntax
313+ {$R mybutton.res}
318314```
319315
320-Install the package.
316+Install the package.
321317
322318### Hiding a component in the component palette
323319
324-Package IDEIntf, unit componentreg:
325-
326-``` pascal
320+Package IDEIntf, unit componentreg:
321+```
327322IDEComponentPalette.FindComponent('TButton').Visible:=false;
328323```
329324
330325## Writing component editors
331326
332-A component editor handles things like double clicking on a component in a designer or adding menu items when right clicking on a component. Writing a component editor is easy. See the unit componenteditors.pas of the package IDEIntf for lots of examples. For instance to show an editor when double clicking see TCheckListBoxComponentEditor.
327+A component editor handles things like double clicking on a component in a designer or adding menu items when right clicking on a component. Writing a component editor is easy. See the unit componenteditors.pas of the package IDEIntf for lots of examples. For instance to show an editor when double clicking see TCheckListBoxComponentEditor.
333328
334329## Writing property editors
335330
336-Each type of property (integer, string, TFont, ...) in the Object Inspector needs a property editor. If your component has a property *Password* of type string, then you can define a property editor for your specific component class, with a property named *Password* of type string. The unit propedits.pp of the package IDEIntf contains many of the standard property editors used by the IDE itself. For example the TStringPropertyEditor is the default property editor for all strings, while TComponentNamePropertyEditor is more specific and handles only TComponent.Name. See the unit for plenty of examples.
331+Each type of property (integer, string, TFont, ...) in the Object Inspector needs a property editor. If your component has a property _Password_ of type string, then you can define a property editor for your specific component class, with a property named _Password_ of type string. The unit propedits.pp of the package IDEIntf contains many of the standard property editors used by the IDE itself. For example the TStringPropertyEditor is the default property editor for all strings, while TComponentNamePropertyEditor is more specific and handles only TComponent.Name. See the unit for plenty of examples.
337332
338333### Hiding a property in the Object Inspector
339334
340-There is a special property editor for hiding:
341-
342-``` pascal
343- RegisterPropertyEditor(TypeInfo(TAnchorSide), TControl, 'AnchorSideLeft', THiddenPropertyEditor);
335+There is a special property editor for hiding:
344336```
337+RegisterPropertyEditor(TypeInfo(TAnchorSide), TControl, 'AnchorSideLeft', THiddenPropertyEditor);
338+```
345339
346340### Find out what property editor is used for a property
347-
348-``` pascal
341+```
349342uses Classes, TypInfo, PropEdits;
350-var
351- Instance: TPersistent; // your instance
352- PropInfo: PPropInfo;
353- EdClass: TPropertyEditorClass;
354-begin
355- PropInfo:=GetPropInfo(Instance.ClassType,'Name'); // replace Name with the property name you are searching for
356- GetEditorClass(PropInfo, Instance);
357-end;
343+ var
344+ Instance: TPersistent; // your instance
345+ PropInfo: PPropInfo;
346+ EdClass: TPropertyEditorClass;
347+ begin
348+ PropInfo:=GetPropInfo(Instance.ClassType,'Name'); // replace Name with the property name you are searching for
349+ GetEditorClass(PropInfo, Instance);
350+ end;
358351```
359352
360353# Forms
361354
362-The IDE allows to easily create descendants of forms and frames. These forms can have new published components and new published methods. But you when you add new published properties, they are not accessible via the Object Inspector. They work only at runtime. The methods in the IDE are fake, they can not be executed. The IDE can create the components, because their classes are registered in the component palette. Your new properties only exist in your source code. The IDE can not fake them yet. That's why you have to register the new class. That means:
363-
364-- create a design time package (or extend an existing one)
365-- your form must be in a unit of this package
366-
367-``` pascal
368-...
369-interface
370-
371-uses FormEditingIntf, Forms;
372-
373-type
374- TYourForm = class(TForm)
375- private
376- FYourProperty: integer;
377- published
378- property YourProperty: integer read FYourProperty write FYourProperty;
379- end;
380-
381-procedure Register;
355+The IDE allows to easily create descendants of forms and frames. These forms can have new published components and new published methods. But you when you add new published properties, they are not accessible via the Object Inspector. They work only at runtime. The methods in the IDE are fake, they can not be executed. The IDE can create the components, because their classes are registered in the component palette. Your new properties only exist in your source code. The IDE can not fake them yet. That's why you have to register the new class. That means:
382356
383-implementation
357+ * create a design time package (or extend an existing one)
358+ * your form must be in a unit of this package
384359
385-procedure Register;
386-begin
387- FormEditingHook.RegisterDesignerBaseClass(TYourForm);
388-end;
360+```
389361...
362+ interface
363+
364+ uses FormEditingIntf, Forms;
365+
366+ type
367+ TYourForm = class(TForm)
368+ private
369+ FYourProperty: integer;
370+ published
371+ property YourProperty: integer read FYourProperty write FYourProperty;
372+ end;
373+
374+ procedure Register;
375+
376+ implementation
377+
378+ procedure Register;
379+ begin
380+ FormEditingHook.RegisterDesignerBaseClass(TYourForm);
381+ end;
382+ ...
390383```
391384
392-- check the "Register" in the package editor for this unit.
393-- Install the package in the IDE
385+ * check the "Register" in the package editor for this unit.
386+ * Install the package in the IDE
394387
395388# Designer
396389
397390## Writing a designer mediator
398391
399-The standard designer allows to visually edit LCL controls, while all others are shown as icons(like TOpenDialog or TDataSource). To visually edit non LCL control, you need to create a **designer mediator**. This can be used to design webpages, UML diagrams or other widgetsets like [[fpGUI]]. There is a complete example in examples/designnonlcl/.
392+The standard designer allows to visually edit LCL controls, while all others are shown as icons(like TOpenDialog or TDataSource). To visually edit non LCL control, you need to create a **designer mediator**. This can be used to design webpages, UML diagrams or other widgetsets like [[fpGUI]]. There is a complete example in examples/designnonlcl/.
400393
401-- Install the example package examples/designnonlcl/notlcldesigner.lpk and restart the IDE. This will register the designer mediator for TMyWidget components and add the new components TMyButton, TMyGroupBox to the component palette.
402-- Open the the example project examples/designnonlcl/project/NonLCL1.lpi.
403-- Open the unit1.pas and show the designer form (F12). You should see the components as red rectangles, which can be selected, moved and resized like LCL controls.
394+ * Install the example package examples/designnonlcl/notlcldesigner.lpk and restart the IDE. This will register the designer mediator for TMyWidget components and add the new components TMyButton, TMyGroupBox to the component palette.
395+ * Open the the example project examples/designnonlcl/project/NonLCL1.lpi.
396+ * Open the unit1.pas and show the designer form (F12). You should see the components as red rectangles, which can be selected, moved and resized like LCL controls.
404397
405398## Create a new unique component name
406-
407-``` pascal
399+```
408400uses FormEditingIntf;
409-
410-...
411-
412-NewName:=FormEditingHook.CreateUniqueComponentName(AComponent);
413-
414-// Or if you need to create the name before creating the component:
415-
416-NewName:=FormEditingHook.CreateUniqueComponentName(ComponentClassName,OwnerComponent);
417-// ComponentClassName will be used as prefix, without a leading T.
418-// OwnerComponent is the new owner of the new component.
401+
402+ ...
403+
404+ NewName:=FormEditingHook.CreateUniqueComponentName(AComponent);
405+
406+ // Or if you need to create the name before creating the component:
407+
408+ NewName:=FormEditingHook.CreateUniqueComponentName(ComponentClassName,OwnerComponent);
409+ // ComponentClassName will be used as prefix, without a leading T.
410+ // OwnerComponent is the new owner of the new component.
419411```
420412
421413## Select a component in the designer/object inspector
422-
423-``` pascal
414+```
424415uses propedits;
425-..
426-GlobalDesignHook.SelectOnlyThis(AComponent);
416+ ..
417+ GlobalDesignHook.SelectOnlyThis(AComponent);
427418```
428419
429420## Get unit, designer, form of a file
430-
431-``` pascal
421+```
432422uses ProjectIntf, LazIDEIntf, ComponentEditors;
433-...
434-// open file
435-if LazarusIDE.DoOpenEditorFile(Filename,-1,-1,[ofAddToRecent])<>mrOk then exit;
436-// get file interface
437-aFile:=LazarusIDE.ActiveProject.FindFile(Filename,[]);
438-// get designer
439-aDesigner:=TComponentEditorDesigner(LazarusIDE.GetDesignerWithProjectFile(AFile,true));
440-if aDesigner=nil then
441- exit; // this unit has no resource (form, frame, datamodule, etc.)
442-// get form
443-aForm:=aDesigner.Form;
423+ ...
424+ // open file
425+ if LazarusIDE.DoOpenEditorFile(Filename,-1,-1,[ofAddToRecent])<>mrOk then exit;
426+ // get file interface
427+ aFile:=LazarusIDE.ActiveProject.FindFile(Filename,[]);
428+ // get designer
429+ aDesigner:=TComponentEditorDesigner(LazarusIDE.GetDesignerWithProjectFile(AFile,true));
430+ if aDesigner=nil then
431+ exit; // this unit has no resource (form, frame, datamodule, etc.)
432+ // get form
433+ aForm:=aDesigner.Form;
444434```
445435
446436## Save unit, designer form
447-
448-``` pascal
437+```
449438uses LazIDEIntf;
450-...
451-if LazarusIDE.DoSaveEditorFile(Filename,[])<>mrOk then exit;
439+ ...
440+ if LazarusIDE.DoSaveEditorFile(Filename,[])<>mrOk then exit;
452441```
453442
454443## Save designer form as Pascal
455444
456-- Use the function **FormEditingHook.SaveComponentAsPascal** of unit *FormEditingfIntf*. See *examples/pascalstream/CopyAsPasPkg/copyaspasdemounit1.pas*.
445+ * Use the function **FormEditingHook.SaveComponentAsPascal** of unit _FormEditingfIntf_. See _examples/pascalstream/CopyAsPasPkg/copyaspasdemounit1.pas_.
457446
458447# Event handlers
459448
460-There are several events in the IDE, for which plugins can add their own handlers.
449+There are several events in the IDE, for which plugins can add their own handlers.
461450
462451## Designer events
463452
464-In propedits.pp there is a "GlobalDesignHook" object, which maintains several events for designing. Each event calls a list of handlers. The default handlers are added by the IDE. You can add your own handlers with the AddHandlerXXX and RemoveHandlerXXX methods. They will be called before the default handlers.
453+In propedits.pp there is a "GlobalDesignHook" object, which maintains several events for designing. Each event calls a list of handlers. The default handlers are added by the IDE. You can add your own handlers with the AddHandlerXXX and RemoveHandlerXXX methods. They will be called before the default handlers.
465454
466-Examples:
455+Examples:
467456
468-- Adding your handler (this is normally done in the constructor of your object):
457+ * Adding your handler (this is normally done in the constructor of your object):
469458
470-```text
471- GlobalDesignHook.AddHandlerPersistentAdded(@YourOnPersistentAdded);
472459```
460+GlobalDesignHook.AddHandlerPersistentAdded(@YourOnPersistentAdded);
461+```
473462
474-- Removing your handler:
463+ * Removing your handler:
475464
476-```text
477- GlobalDesignHook.RemoveHandlerPersistentAdded(@YourOnPersistentAdded);
478465```
466+GlobalDesignHook.RemoveHandlerPersistentAdded(@YourOnPersistentAdded);
467+```
479468
480-- You can remove all handlers at once. For example, it is a good idea to add this line in the destructor of your object:
469+ * You can remove all handlers at once. For example, it is a good idea to add this line in the destructor of your object:
481470
482-```text
483- GlobalDesignHook.RemoveAllHandlersForObject(Self);
484471```
472+GlobalDesignHook.RemoveAllHandlersForObject(Self);
473+```
485474
486-The handlers of GlobalDesignHook:
475+The handlers of GlobalDesignHook:
487476
488-- Lookup root
489- - ChangeLookupRoot - Called when the "LookupRoot" changed. The "LookupRoot" is the owner object of the currently selected components. Normally this is a TForm.
490-- Methods
491- - CreateMethod
492- - GetMethodName
493- - GetMethods
494- - MethodExists
495- - RenameMethod
496- - ShowMethod
497- - MethodFromAncestor
498- - ChainCall
499-- Components
500- - GetComponent
501- - GetComponentName
502- - GetComponentNames
503- - GetRootClassName
504- - AddClicked - Called when the user selected a component class and clicked on the designer to add a new component. You can change the component class and the parent.
505- - ComponentRenamed - Called when a component was renamed
506-- TPersistent objects
507- - PersistentAdded - Called when a new TPersistent was added to the LookupRoot
508- - PersistentDeleting - Called before a TPersistent is freed.
509- - DeletePersistent - Called by the IDE to delete a TPersistent.
510- - GetSelectedPersistents - Called when getting the current selection of TPersistent.
511- - SetSelectedPersistents - Called when setting the current selection of TPersistent.
512- - GetObject
513- - GetObjectName
514- - GetObjectNames
515-- Modifing
516- - Modified
517- - Revert
518- - RefreshPropertyValues
519-- Selection
520- - SetSelection
521- - GetSelection
477+ * Lookup root
478+ * ChangeLookupRoot - Called when the "LookupRoot" changed. The "LookupRoot" is the owner object of the currently selected components. Normally this is a TForm.
479+ * Methods
480+ * CreateMethod
481+ * GetMethodName
482+ * GetMethods
483+ * MethodExists
484+ * RenameMethod
485+ * ShowMethod
486+ * MethodFromAncestor
487+ * ChainCall
488+ * Components
489+ * GetComponent
490+ * GetComponentName
491+ * GetComponentNames
492+ * GetRootClassName
493+ * AddClicked - Called when the user selected a component class and clicked on the designer to add a new component. You can change the component class and the parent.
494+ * ComponentRenamed - Called when a component was renamed
495+ * TPersistent objects
496+ * PersistentAdded - Called when a new TPersistent was added to the LookupRoot
497+ * PersistentDeleting - Called before a TPersistent is freed.
498+ * DeletePersistent - Called by the IDE to delete a TPersistent.
499+ * GetSelectedPersistents - Called when getting the current selection of TPersistent.
500+ * SetSelectedPersistents - Called when setting the current selection of TPersistent.
501+ * GetObject
502+ * GetObjectName
503+ * GetObjectNames
504+ * Modifing
505+ * Modified
506+ * Revert
507+ * RefreshPropertyValues
508+ * Selection
509+ * SetSelection
510+ * GetSelection
522511
523512### Examples for designer events
524513
525514#### Example: When dropping a new component move control to a child
526-
527-``` pascal
528-...
529-type
530- TYourDesignerExtension = class
531- private
532- function AddClicked(ADesigner: TIDesigner;
533- MouseDownComponent: TComponent; Button: TMouseButton;
534- Shift: TShiftState; X, Y: Integer;
535- var AComponentClass: TComponentClass;
536- var NewParent: TComponent): boolean of object;
537- public
538- constructor Create;
539- destructor Destroy; override;
540- end;
541-
515+```
542516...
543-constructor TYourDesignerExtension.Create;
544-begin
545- // register your handler
546- GlobalDesignHook.AddHandlerAddClicked(@AddClicked);
547-end;
548-
549-destructor TYourDesignerExtension.Destroy;
550-begin
551- // remove your handler:
552- GlobalDesignHook.RemoveHandlerAddClicked(@AddClicked);
553-end;
554-
555-function TYourDesignerExtension.AddClicked(ADesigner: TIDesigner;
556- MouseDownComponent: TComponent; Button: TMouseButton;
557- Shift: TShiftState; X, Y: Integer;
558- var AComponentClass: TComponentClass;
559- var NewParent: TComponent): boolean of object;
560-begin
561- // in this example TYourControl is a custom control with a ChildControl.
562- // Whenever the user drops a control on the TYourControl instead of adding
563- // the new control to the TYourControl, the new control is added as child
564- // of ChildControl.
565- // created
566- if MouseDownComponent is TYourControl then
567- NewParent:=TYourControl(MouseDownComponent).ChildControl;
568- Result:=true;
569-end;
517+ type
518+ TYourDesignerExtension = class
519+ private
520+ function AddClicked(ADesigner: TIDesigner;
521+ MouseDownComponent: TComponent; Button: TMouseButton;
522+ Shift: TShiftState; X, Y: Integer;
523+ var AComponentClass: TComponentClass;
524+ var NewParent: TComponent): boolean of object;
525+ public
526+ constructor Create;
527+ destructor Destroy; override;
528+ end;
529+
530+ ...
531+ constructor TYourDesignerExtension.Create;
532+ begin
533+ // register your handler
534+ GlobalDesignHook.AddHandlerAddClicked(@AddClicked);
535+ end;
536+
537+ destructor TYourDesignerExtension.Destroy;
538+ begin
539+ // remove your handler:
540+ GlobalDesignHook.RemoveHandlerAddClicked(@AddClicked);
541+ end;
542+
543+ function TYourDesignerExtension.AddClicked(ADesigner: TIDesigner;
544+ MouseDownComponent: TComponent; Button: TMouseButton;
545+ Shift: TShiftState; X, Y: Integer;
546+ var AComponentClass: TComponentClass;
547+ var NewParent: TComponent): boolean of object;
548+ begin
549+ // in this example TYourControl is a custom control with a ChildControl.
550+ // Whenever the user drops a control on the TYourControl instead of adding
551+ // the new control to the TYourControl, the new control is added as child
552+ // of ChildControl.
553+ // created
554+ if MouseDownComponent is TYourControl then
555+ NewParent:=TYourControl(MouseDownComponent).ChildControl;
556+ Result:=true;
557+ end;
570558```
571559
572560### Disabling the designer mouse handler
573561
574-Normally the designer catches all mouse events of a component. If your custom control should handle the mouse events handle you can either set the **csDesignInteractive** **ControlStyle** or you can fine control it via the message *CM_DESIGNHITTEST* in your control:
575-
576-``` pascal
562+Normally the designer catches all mouse events of a component. If your custom control should handle the mouse events handle you can either set the **csDesignInteractive** **ControlStyle** or you can fine control it via the message _CM_DESIGNHITTEST_ in your control:
563+```
577564type
578- TYourControl = class(TCustomControl)
579- protected
580- procedure CMDesignHitTest(var Message: TLMessage); message CM_DESIGNHITTEST;
581- end;
582-...
583-procedure TYourControl.CMDesignHitTest(var Message: TLMessage);
584-var
585- p: TSmallPoint;
586- aShiftState: TShiftState;
587-begin
588- aShiftState:=KeysToShiftState(PtrUInt(Message.WParam));
589- p:=TSmallPoint(longint(Message.LParam));
590- debugln(['TForm1.CMDesignHitTest ShiftState=',dbgs(aShiftState),' x=',p.x,' y=',p.y]);
591- if SkipDesignerMouseHandler then
592- Message.Result:=1; // now the designer calls the normal MouseUp, MouseMove and MouseDown methods.
593-end;
565+ TYourControl = class(TCustomControl)
566+ protected
567+ procedure CMDesignHitTest(var Message: TLMessage); message CM_DESIGNHITTEST;
568+ end;
569+ ...
570+ procedure TYourControl.CMDesignHitTest(var Message: TLMessage);
571+ var
572+ p: TSmallPoint;
573+ aShiftState: TShiftState;
574+ begin
575+ aShiftState:=KeysToShiftState(PtrUInt(Message.WParam));
576+ p:=TSmallPoint(longint(Message.LParam));
577+ debugln(['TForm1.CMDesignHitTest ShiftState=',dbgs(aShiftState),' x=',p.x,' y=',p.y]);
578+ if SkipDesignerMouseHandler then
579+ Message.Result:=1; // now the designer calls the normal MouseUp, MouseMove and MouseDown methods.
580+ end;
594581```
595582
596583## Getting notified when a designer form is modified
597584
598-Every designed LCL form has a Designer of type TIDesigner. The IDE creates designers of type TComponentEditorDesigner defined in the IDEIntf unit componenteditors. For example:
599-
600-``` pascal
585+Every designed LCL form has a Designer of type TIDesigner. The IDE creates designers of type TComponentEditorDesigner defined in the IDEIntf unit componenteditors. For example:
586+```
601587procedure TYourAddOn.OnDesignerModified(Sender: TObject);
602-var
603- IDEDesigner: TComponentEditorDesigner;
604-begin
605- IDEDesigner:=TComponentEditorDesigner(Sender);
606- ...
607-end;
608-
609-procedure TYourAddOn.ConnectDesignerForm(Form1: TCustomForm);
610-var
611- IDEDesigner: TComponentEditorDesigner;
612-begin
613- IDEDesigner:=TComponentEditorDesigner(Form1.Designer);
614- IDEDesigner.AddHandlerModified(@OnDesignerModified);
615-end;
588+ var
589+ IDEDesigner: TComponentEditorDesigner;
590+ begin
591+ IDEDesigner:=TComponentEditorDesigner(Sender);
592+ ...
593+ end;
594+
595+ procedure TYourAddOn.ConnectDesignerForm(Form1: TCustomForm);
596+ var
597+ IDEDesigner: TComponentEditorDesigner;
598+ begin
599+ IDEDesigner:=TComponentEditorDesigner(Form1.Designer);
600+ IDEDesigner.AddHandlerModified(@OnDesignerModified);
601+ end;
616602```
617603
618604## Project events
619605
620-These events are defined in unit LazIDEIntf.
606+These events are defined in unit LazIDEIntf.
621607
622-- **LazarusIDE.AddHandlerOnProjectClose**: called before a project is closed
623-- **LazarusIDE.AddHandlerOnProjectOpened**: called after the project was completely opened (for example all required packages were loaded, units were opened in the source editor)
624-- **LazarusIDE.AddHandlerOnSavingAll**: called before IDE saves everything
625-- **LazarusIDE.AddHandlerOnSavedAll**: called after IDE saved everything
626-- **LazarusIDE.AddHandlerOnProjectBuilding**: called before IDE builds the project
627-- **LazarusIDE.AddHandlerOnProjectDependenciesCompiling**: called before IDE compiles package dependencies of project
628-- **LazarusIDE.AddHandlerOnProjectDependenciesCompiled**: called after IDE compiled package dependencies of project
629-- **LazarusIDE.AddHandlerOnProjectBuildingFinished**: called after IDE built the project (successful or not) (since 1.5)
630-- **LazarusIDE.AddHandlerOnLoadSaveCustomData**: called after loading/before saving a project's CustomData or a project file's CustomData. On save the CustomData is cloned to a temporary tree, so it is free for altering before save. Use this (since 2.3)
608+ * **LazarusIDE.AddHandlerOnProjectClose** : called before a project is closed
609+ * **LazarusIDE.AddHandlerOnProjectOpened** : called after the project was completely opened (for example all required packages were loaded, units were opened in the source editor)
610+ * **LazarusIDE.AddHandlerOnSavingAll** : called before IDE saves everything
611+ * **LazarusIDE.AddHandlerOnSavedAll** : called after IDE saved everything
612+ * **LazarusIDE.AddHandlerOnProjectBuilding** : called before IDE builds the project
613+ * **LazarusIDE.AddHandlerOnProjectDependenciesCompiling** : called before IDE compiles package dependencies of project
614+ * **LazarusIDE.AddHandlerOnProjectDependenciesCompiled** : called after IDE compiled package dependencies of project
615+ * **LazarusIDE.AddHandlerOnProjectBuildingFinished** : called after IDE built the project (successful or not) (since 1.5)
616+ * **LazarusIDE.AddHandlerOnLoadSaveCustomData** : called after loading/before saving a project's CustomData or a project file's CustomData. On save the CustomData is cloned to a temporary tree, so it is free for altering before save. Use this (since 2.3)
631617
632618## Other IDE events
633619
634-uses LazIDEIntf;
620+uses LazIDEIntf;
635621
636-Call these in the Register procedure of your package:
622+Call these in the Register procedure of your package:
637623
638-- **LazarusIDE.AddHandlerOnIDERestoreWindows**: called when IDE is restores its windows (before opening the first project)
639-- **LazarusIDE.AddHandlerOnIDEClose**: called when IDE is shutting down (after closequery, so no more interactivity)
640-- **LazarusIDE.AddHandlerOnQuickSyntaxCheck**: called when the menu item or the shortcut for Quick syntax check is executed
641-- **LazarusIDE.AddHandlerOnLazarusBuilding**: // called before IDE builds Lazarus IDE (since 1.5)
642-- **LazarusIDE.AddHandlerOnLazarusBuildingFinished**: // called after IDE builds Lazarus IDE (since 1.5)
643-- **LazarusIDE.AddHandlerGetFPCFrontEndParams**: // called when the IDE gets the parameters of the 'fpc' front end tool (since 1.5)
644-- **LazarusIDE.AddHandlerGetFPCFrontEndPath**: // called when the IDE gets the path of the 'fpc' front end tool (since 1.5)
645-- **LazarusIDE.AddHandlerOnUpdateIDEComponentPalette**: ?
646-- **LazarusIDE.AddHandlerOnUpdateComponentPageControl**: ?
647-- **LazarusIDE.AddHandlerOnShowDesignerFormOfSource**: // called after showing a designer form for code editor (AEditor can be nil!) (since 1.5)
648-- **LazarusIDE.AddHandlerOnShowSourceOfActiveDesignerForm**: // called after showing a code of designer form (since 1.5)
649-- **LazarusIDE.AddHandlerOnChangeToolStatus**: //called when IDEToolStatus has changed (e.g. itNone-\>itBuilder etc.) (since 1.5)
624+ * **LazarusIDE.AddHandlerOnIDERestoreWindows** : called when IDE is restores its windows (before opening the first project)
625+ * **LazarusIDE.AddHandlerOnIDEClose** : called when IDE is shutting down (after closequery, so no more interactivity)
626+ * **LazarusIDE.AddHandlerOnQuickSyntaxCheck** : called when the menu item or the shortcut for Quick syntax check is executed
627+ * **LazarusIDE.AddHandlerOnLazarusBuilding** : // called before IDE builds Lazarus IDE (since 1.5)
628+ * **LazarusIDE.AddHandlerOnLazarusBuildingFinished** : // called after IDE builds Lazarus IDE (since 1.5)
629+ * **LazarusIDE.AddHandlerGetFPCFrontEndParams** : // called when the IDE gets the parameters of the 'fpc' front end tool (since 1.5)
630+ * **LazarusIDE.AddHandlerGetFPCFrontEndPath** : // called when the IDE gets the path of the 'fpc' front end tool (since 1.5)
631+ * **LazarusIDE.AddHandlerOnUpdateIDEComponentPalette** : ?
632+ * **LazarusIDE.AddHandlerOnUpdateComponentPageControl** : ?
633+ * **LazarusIDE.AddHandlerOnShowDesignerFormOfSource** : // called after showing a designer form for code editor (AEditor can be nil!) (since 1.5)
634+ * **LazarusIDE.AddHandlerOnShowSourceOfActiveDesignerForm** : // called after showing a code of designer form (since 1.5)
635+ * **LazarusIDE.AddHandlerOnChangeToolStatus** : //called when IDEToolStatus has changed (e.g. itNone->itBuilder etc.) (since 1.5)
650636
651-Call these in the initialization section of one of your units:
637+Call these in the initialization section of one of your units:
652638
653-- **AddBootHandler**
654- - AddBootHandler(**libhTransferMacrosCreated**,@YourProc): called after IDEMacros were created (since 1.3)
655- - AddBootHandler(**libhEnvironmentOptionsLoaded**,@YourProc): called after environment options were loaded (since 1.3)
639+ * **AddBootHandler**
640+ * AddBootHandler(**libhTransferMacrosCreated** ,@YourProc): called after IDEMacros were created (since 1.3)
641+ * AddBootHandler(**libhEnvironmentOptionsLoaded** ,@YourProc): called after environment options were loaded (since 1.3)
656642
657643# Project
658644
659645## Current Project
660646
661-The current main project can be obtained by LazarusIDE.ActiveProject. (unit LazIDEIntf)
647+The current main project can be obtained by LazarusIDE.ActiveProject. (unit LazIDEIntf)
662648
663649## All units of current project
664650
665-Note that the term "units of project" is ambiguous.
651+Note that the term "units of project" is ambiguous.
666652
667-- The project inspector shows the list of the .lpi file. The project might not use all these units on all platforms and maybe the developer forgot to add all used units.
668-- Units from used packages are not project units.
653+ * The project inspector shows the list of the .lpi file. The project might not use all these units on all platforms and maybe the developer forgot to add all used units.
654+ * Units from used packages are not project units.
669655
670656### All units of the project inspector
671657
672-To iterate through all pascal units listed in the project inspector you can use for example:
673-
674-``` pascal
658+To iterate through all pascal units listed in the project inspector you can use for example:
659+```
675660uses
676- LCLProc, FileUtil, LazIDEIntf, ProjectIntf;
677-
678-procedure ListProjectUnits;
679-var
680- LazProject: TLazProject;
681- i: Integer;
682- LazFile: TLazProjectFile;
683-begin
684- LazProject:=LazarusIDE.ActiveProject;
685- if LazProject<>nil then
686- for i:=0 to LazProject.FileCount-1 do
661+ LCLProc, FileUtil, LazIDEIntf, ProjectIntf;
662+
663+ procedure ListProjectUnits;
664+ var
665+ LazProject: TLazProject;
666+ i: Integer;
667+ LazFile: TLazProjectFile;
687668 begin
688- LazFile:=LazProject.Files[i];
689- if LazFile.IsPartOfProject
690- and FilenameIsPascalUnit(LazFile.Filename)
691- then
692- debugln(LazFile.Filename);
669+ LazProject:=LazarusIDE.ActiveProject;
670+ if LazProject<>nil then
671+ for i:=0 to LazProject.FileCount-1 do
672+ begin
673+ LazFile:=LazProject.Files[i];
674+ if LazFile.IsPartOfProject
675+ and FilenameIsPascalUnit(LazFile.Filename)
676+ then
677+ debugln(LazFile.Filename);
678+ end;
693679 end;
694-end;
695680```
696681
697682### All used units of project
698683
699-To find all currently used units of a project/package you can use the following. Note that the IDE needs to parse all units, so it can take some time.
700-
701-``` pascal
684+To find all currently used units of a project/package you can use the following. Note that the IDE needs to parse all units, so it can take some time.
685+```
702686uses
703- LazLoggerBase, LazIDEIntf, ProjectIntf;
704-
705-procedure ListProjectUnits;
706-var
707- LazProject: TLazProject;
708- i: Integer;
709-begin
710- LazProject:=LazarusIDE.ActiveProject;
711- if LazProject<>nil then
712- begin
713- Units:=LazarusIDE.FindUnitsOfOwner(LazProject,[fuooListed,fuooUsed]); // add fuooPackages to include units from packages
714- try
715- for i:=0 to Units.Count-1 do
716- debugln('Filename=',Units[i]);
717- finally
718- Units.Free;
687+ LazLoggerBase, LazIDEIntf, ProjectIntf;
688+
689+ procedure ListProjectUnits;
690+ var
691+ LazProject: TLazProject;
692+ i: Integer;
693+ begin
694+ LazProject:=LazarusIDE.ActiveProject;
695+ if LazProject<>nil then
696+ begin
697+ Units:=LazarusIDE.FindUnitsOfOwner(LazProject,[fuooListed,fuooUsed]); // add fuooPackages to include units from packages
698+ try
699+ for i:=0 to Units.Count-1 do
700+ debugln('Filename=',Units[i]);
701+ finally
702+ Units.Free;
703+ end;
704+ end;
719705 end;
720- end;
721-end;
722706```
723707
724708## The .lpr, .lpi and .lps file of a project
725-
726-``` pascal
709+```
727710uses
728- LCLProc, FileUtil, ProjectIntf, LazIDEIntf;
729-var
730- LazProject: TLazProject;
731-begin
732- LazProject:=LazarusIDE.ActiveProject;
733- // every project has a .lpi file:
734- DebugLn(['Project'' lpi file: ',LazProject.ProjectInfoFile]);
735-
736- // if the project session information is stored in a separate .lps file:
737- if LazProject.SessionStorage<>pssNone then
738- DebugLn(['Project'' lps file: ',LazProject.ProjectSessionFile]);
739-
740- // If the project has a .lpr file it is the main source file:
741- if (LazProject.MainFile<>nil)
742- and (CompareFileExt(LazProject.MainFile.Filename,'lpr')=0) then
743- DebugLn(['Project has lpr file: ',LazProject.MainFile.Filename]);
744-end;
711+ LCLProc, FileUtil, ProjectIntf, LazIDEIntf;
712+ var
713+ LazProject: TLazProject;
714+ begin
715+ LazProject:=LazarusIDE.ActiveProject;
716+ // every project has a .lpi file:
717+ DebugLn(['Project'' lpi file: ',LazProject.ProjectInfoFile]);
718+
719+ // if the project session information is stored in a separate .lps file:
720+ if LazProject.SessionStorage<>pssNone then
721+ DebugLn(['Project'' lps file: ',LazProject.ProjectSessionFile]);
722+
723+ // If the project has a .lpr file it is the main source file:
724+ if (LazProject.MainFile<>nil)
725+ and (CompareFileExt(LazProject.MainFile.Filename,'lpr')=0) then
726+ DebugLn(['Project has lpr file: ',LazProject.MainFile.Filename]);
727+ end;
745728```
746729
747730## The executable / target file name of a project
748731
749-There is a macro \$(TargetFile), which can be used in paths and external tools. You can query the macro in code:
750-
751-``` pascal
732+There is a macro $(TargetFile), which can be used in paths and external tools. You can query the macro in code:
733+```
752734uses MacroIntf;
753-
754-function MyGetProjectTargetFile: string;
755-begin
756- Result:='$(TargetFile)';
757- if not IDEMacros.SubstituteMacros(Result) then
758- raise Exception.Create('unable to retrieve target file of project');
759-end;
735+
736+ function MyGetProjectTargetFile: string;
737+ begin
738+ Result:='$(TargetFile)';
739+ if not IDEMacros.SubstituteMacros(Result) then
740+ raise Exception.Create('unable to retrieve target file of project');
741+ end;
760742```
761743
762-See here for more macros: [[IDE Macros in paths and filenames]].
744+See here for more macros: [[IDE Macros in paths and filenames]].
763745
764746## Add your own project type
765747
766-You can add items to the 'New ...' dialog:
748+You can add items to the 'New ...' dialog:
767749
768-- See the unit ProjectIntf of the package IDEIntf.
769-- See the [[Project Templates]]
750+ * See the unit ProjectIntf of the package IDEIntf.
751+ * See the [[Project Templates]]
770752
753+
754+
771755## Add your own file type
772756
773-You can add items to the 'New ...' dialog:
757+You can add items to the 'New ...' dialog:
774758
775-- See the unit ProjectIntf of the package IDEIntf.
776-- Choose a base class TFileDescPascalUnit for normal units or TFileDescPascalUnitWithResource for a new form/datamodule.
759+ * See the unit ProjectIntf of the package IDEIntf.
760+ * Choose a base class TFileDescPascalUnit for normal units or TFileDescPascalUnitWithResource for a new form/datamodule.
777761
778762### Add a new file type
779-
780-``` pascal
763+```
781764uses ProjectIntf;
782-...
783- { TFileDescText }
784-
785- TFileDescMyText = class(TProjectFileDescriptor)
786- public
787- constructor Create; override;
788- function GetLocalizedName: string; override;
789- function GetLocalizedDescription: string; override;
790- end;
791-...
792-
793-procedure Register;
794-
795-implementation
796-
797-procedure Register;
798-begin
799- RegisterProjectFileDescriptor(TFileDescMyText.Create,FileDescGroupName);
800-end;
801-
802-{ TFileDescMyText }
803-
804-constructor TFileDescMyText.Create;
805-begin
806- inherited Create;
807- Name:='MyText'; // do not translate this
808- DefaultFilename:='text.txt';
809- AddToProject:=false;
810-end;
811-
812-function TFileDescText.GetLocalizedName: string;
813-begin
814- Result:='My Text'; // replace this with a resourcestring
815-end;
816-
817-function TFileDescText.GetLocalizedDescription: string;
818-begin
819- Result:='An empty text file';
820-end;
765+ ...
766+ { TFileDescText }
767+
768+ TFileDescMyText = class(TProjectFileDescriptor)
769+ public
770+ constructor Create; override;
771+ function GetLocalizedName: string; override;
772+ function GetLocalizedDescription: string; override;
773+ end;
774+ ...
775+
776+ procedure Register;
777+
778+ implementation
779+
780+ procedure Register;
781+ begin
782+ RegisterProjectFileDescriptor(TFileDescMyText.Create,FileDescGroupName);
783+ end;
784+
785+ { TFileDescMyText }
786+
787+ constructor TFileDescMyText.Create;
788+ begin
789+ inherited Create;
790+ Name:='MyText'; // do not translate this
791+ DefaultFilename:='text.txt';
792+ AddToProject:=false;
793+ end;
794+
795+ function TFileDescText.GetLocalizedName: string;
796+ begin
797+ Result:='My Text'; // replace this with a resourcestring
798+ end;
799+
800+ function TFileDescText.GetLocalizedDescription: string;
801+ begin
802+ Result:='An empty text file';
803+ end;
821804```
822805
823806### Add a new form type
824-
825-``` pascal
807+```
826808uses ProjectIntf;
827-
828-...
829- TFileDescPascalUnitWithMyForm = class(TFileDescPascalUnitWithResource)
830- public
831- constructor Create; override;
832- function GetInterfaceUsesSection: string; override;
833- function GetLocalizedName: string; override;
834- function GetLocalizedDescription: string; override;
835- end;
836-...
837-
838-procedure Register;
839-
840-implementation
841-
842-procedure Register;
843-begin
844- RegisterProjectFileDescriptor(TFileDescPascalUnitWithMyForm.Create,FileDescGroupName);
845-end;
846-
847-{ TFileDescPascalUnitWithMyForm }
848-
849-constructor TFileDescPascalUnitWithMyForm.Create;
850-begin
851- inherited Create;
852- Name:='MyForm'; // do not translate this
853- ResourceClass:=TMyForm;
854- UseCreateFormStatements:=true;
855-end;
856-
857-function TFileDescPascalUnitWithMyForm.GetInterfaceUsesSection: string;
858-begin
859- Result:='Classes, SysUtils, MyWidgetSet';
860-end;
861-
862-function TFileDescPascalUnitWithForm.GetLocalizedName: string;
863-begin
864- Result:='MyForm'; // replace this with a resourcestring
865-end;
866-
867-function TFileDescPascalUnitWithForm.GetLocalizedDescription: string;
868-begin
869- Result:='Create a new MyForm from example package NotLCLDesigner';
870-end;
809+
810+ ...
811+ TFileDescPascalUnitWithMyForm = class(TFileDescPascalUnitWithResource)
812+ public
813+ constructor Create; override;
814+ function GetInterfaceUsesSection: string; override;
815+ function GetLocalizedName: string; override;
816+ function GetLocalizedDescription: string; override;
817+ end;
818+ ...
819+
820+ procedure Register;
821+
822+ implementation
823+
824+ procedure Register;
825+ begin
826+ RegisterProjectFileDescriptor(TFileDescPascalUnitWithMyForm.Create,FileDescGroupName);
827+ end;
828+
829+ { TFileDescPascalUnitWithMyForm }
830+
831+ constructor TFileDescPascalUnitWithMyForm.Create;
832+ begin
833+ inherited Create;
834+ Name:='MyForm'; // do not translate this
835+ ResourceClass:=TMyForm;
836+ UseCreateFormStatements:=true;
837+ end;
838+
839+ function TFileDescPascalUnitWithMyForm.GetInterfaceUsesSection: string;
840+ begin
841+ Result:='Classes, SysUtils, MyWidgetSet';
842+ end;
843+
844+ function TFileDescPascalUnitWithForm.GetLocalizedName: string;
845+ begin
846+ Result:='MyForm'; // replace this with a resourcestring
847+ end;
848+
849+ function TFileDescPascalUnitWithForm.GetLocalizedDescription: string;
850+ begin
851+ Result:='Create a new MyForm from example package NotLCLDesigner';
852+ end;
871853```
872854
873855## Add new file
874856
875-Adding a new file to the project: uses LazIDEIntf, ProjectIntf;
876-
877-```text
878-LazarusIDE.DoNewEditorFile(FileDescriptorUnit,,,[nfIsPartOfProject,nfOpenInEditor]);
857+Adding a new file to the project: uses LazIDEIntf, ProjectIntf;
879858```
880-
881-The FileDescriptorUnit generates a default code and a default filename. The new file is opened in the source editor and it is not yet saved to disk.
859+LazarusIDE.DoNewEditorFile(FileDescriptorUnit,_,_ ,[nfIsPartOfProject,nfOpenInEditor]);
860+```
882861
883-You can create a new file with specific filename and custom source:
862+The FileDescriptorUnit generates a default code and a default filename. The new file is opened in the source editor and it is not yet saved to disk.
884863
885-```text
864+You can create a new file with specific filename and custom source:
865+```
886866LazarusIDE.DoNewEditorFile(FileDescriptorUnit,NewFilename,NewSource,[nfIsPartOfProject,nfOpenInEditor]);
887867```
888868
889-Keep in mind that the file is not saved to disk. This is done when the user triggers a *Save*.
890-
891-Alternatively you can create a file via codetools or directly on disk (e.g. TFileStream) and open that in the source editor:
869+Keep in mind that the file is not saved to disk. This is done when the user triggers a _Save_.
892870
893-```text
894- LazarusIDE.DoOpenEditorFile(Filename,-1,-1,[ofAddToRecent]);
871+Alternatively you can create a file via codetools or directly on disk (e.g. TFileStream) and open that in the source editor:
895872```
873+LazarusIDE.DoOpenEditorFile(Filename,-1,-1,[ofAddToRecent]);
874+```
896875
897876## Store custom values in lpi/lps
898877
899-You can store your own data via name/value pairs in CustomData and CustomSessionData.
900-
901-``` pascal
878+You can store your own data via name/value pairs in CustomData and CustomSessionData.
879+```
902880LazarusIDE.ActiveProject.CustomData['Name']:=Value; // stored in lpi - x-platform, shared data
903-LazarusIDE.ActiveProject.CustomSessionData['Name']:=Value; // stored in lps - only this machine
881+ LazarusIDE.ActiveProject.CustomSessionData['Name']:=Value; // stored in lps - only this machine
904882```
905883
906884### Store custom filenames in lpi/lps
907885
908-Filenames should be stored relative in the lpi and some care must be taken because platform specifics. Here is an example from the pas2jsdsgn package:
909-
910-Hook the OnLoadSaveCustomData handler:
886+Filenames should be stored relative in the lpi and some care must be taken because platform specifics. Here is an example from the pas2jsdsgn package:
911887
912-``` pascal
888+Hook the OnLoadSaveCustomData handler:
889+```
913890uses
914- LazIDEIntf, ProjectIntf, AvgLvlTree;
915-
916-...
917- // in registration
918- LazarusIDE.AddHandlerOnLoadSaveCustomData(@OnLoadSaveCustomData);
919-
920-...
921-procedure TPJSController.OnLoadSaveCustomData(Sender: TObject; Load: boolean;
922- CustomData: TStringToStringTree; PathDelimChanged: boolean);
923-var
924- fn: String;
925- aProject: TLazProject;
926-begin
927- if Sender is TLazProject then
891+ LazIDEIntf, ProjectIntf, AvgLvlTree;
892+
893+ ...
894+ // in registration
895+ LazarusIDE.AddHandlerOnLoadSaveCustomData(@OnLoadSaveCustomData);
896+
897+ ...
898+ procedure TPJSController.OnLoadSaveCustomData(Sender: TObject; Load: boolean;
899+ CustomData: TStringToStringTree; PathDelimChanged: boolean);
900+ var
901+ fn: String;
902+ aProject: TLazProject;
928903 begin
929- aProject:=TLazProject(Sender);
930- if CustomData[PJSProjectWebBrowser]='1' then
931- begin
932- fn:=CustomData[PJSProjectHTMLFile];
933- if fn<>'' then
904+ if Sender is TLazProject then
934905 begin
935- if Load then
936- aProject.ConvertFromLPIFilename(fn)
937- else
938- aProject.ConvertToLPIFilename(fn);
939- CustomData[PJSProjectHTMLFile]:=fn;
906+ aProject:=TLazProject(Sender);
907+ if CustomData[PJSProjectWebBrowser]='1' then
908+ begin
909+ fn:=CustomData[PJSProjectHTMLFile];
910+ if fn<>'' then
911+ begin
912+ if Load then
913+ aProject.ConvertFromLPIFilename(fn)
914+ else
915+ aProject.ConvertToLPIFilename(fn);
916+ CustomData[PJSProjectHTMLFile]:=fn;
917+ end;
918+ end;
940919 end;
941- end;
920+ if PathDelimChanged then ;
942921 end;
943- if PathDelimChanged then ;
944-end;
945922```
946923
947924# Packages
948925
949-You can perform various actions with [[Lazarus Packages|Packages]]:
926+You can perform various actions with [[Lazarus Packages|Packages]]:
950927
951928## Search in all packages
952929
953-Iterate all packages loaded in the IDE (since 0.9.29).
954-
955-``` pascal
930+Iterate all packages loaded in the IDE (since 0.9.29).
931+```
956932uses PackageIntf;
957-...
958-for i:=0 to PackageEditingInterface.GetPackageCount-1 do
959- writeln(PackageEditingInterface.GetPackages(i).Name);
933+ ...
934+ for i:=0 to PackageEditingInterface.GetPackageCount-1 do
935+ writeln(PackageEditingInterface.GetPackages(i).Name);
960936```
961937
962938## Search a package with a name
963-
964-``` pascal
939+```
965940uses PackageIntf;
966-...
967-var
968- Pkg: TIDEPackage;
969-begin
970- Pkg:=PackageEditingInterface.FindPackageWithName('LCL');
971- if Pkg<>nil then
972941 ...
973-end;
942+ var
943+ Pkg: TIDEPackage;
944+ begin
945+ Pkg:=PackageEditingInterface.FindPackageWithName('LCL');
946+ if Pkg<>nil then
947+ ...
948+ end;
974949```
975950
976-Note: FindPackageWithName does not open the package editor. For that use DoOpenPackageWithName.
951+Note: FindPackageWithName does not open the package editor. For that use DoOpenPackageWithName.
977952
978953## Get lpk filename of an installed package
979-
980-``` pascal
954+```
981955uses PackageIntf;
982-...
983-var
984- Pkg: TIDEPackage;
985-begin
986- Pkg:=PackageEditingInterface.FindPackageWithName('LCL');
987- if Pkg<>nil then
988- LPKFilename:=Pkg.Filename;
989-end;
956+ ...
957+ var
958+ Pkg: TIDEPackage;
959+ begin
960+ Pkg:=PackageEditingInterface.FindPackageWithName('LCL');
961+ if Pkg<>nil then
962+ LPKFilename:=Pkg.Filename;
963+ end;
990964```
991965
992966## Install packages
993967
994-**Note**: Only install packages with IDE plugins. Installing other packages can make the IDE unstable.
995-
996-``` pascal
968+**Note** : Only install packages with IDE plugins. Installing other packages can make the IDE unstable.
969+```
997970uses PackageIntf, contnrs;
998-...
999- PkgList:=TObjectList.create(true);
1000- try
1001- Pkg:=TLazPackageID.Create;
1002- Pkg.Name:='Cody';
1003- PkgList.Add(Pkg);
1004- // check if the IDE can find the cody.lpk and all dependencies
1005- // The IDE will prompt some warnings/confirmations if something looks strange.
1006- if not PackageEditingInterface.CheckInstallPackageList(PkgList,[]) then
1007- exit;
1008- // in this example we have checked already, so skip the warnings
1009- // and rebuild the IDE
1010- if PackageEditingInterface.InstallPackages(PkgList,[piiifSkipChecks,piiifRebuildIDE])<>mrOK then
1011- exit;
1012- finally
1013- PkgList.Free;
1014- end;
971+ ...
972+ PkgList:=TObjectList.create(true);
973+ try
974+ Pkg:=TLazPackageID.Create;
975+ Pkg.Name:='Cody';
976+ PkgList.Add(Pkg);
977+ // check if the IDE can find the cody.lpk and all dependencies
978+ // The IDE will prompt some warnings/confirmations if something looks strange.
979+ if not PackageEditingInterface.CheckInstallPackageList(PkgList,[]) then
980+ exit;
981+ // in this example we have checked already, so skip the warnings
982+ // and rebuild the IDE
983+ if PackageEditingInterface.InstallPackages(PkgList,[piiifSkipChecks,piiifRebuildIDE])<>mrOK then
984+ exit;
985+ finally
986+ PkgList.Free;
987+ end;
1015988```
1016989
1017990## Open a package file (lpk)
1018-
1019-``` pascal
991+```
1020992uses PackageIntf, FileUtil;
1021-...
1022-var
1023- pkg: TIDEPackage;
1024-begin
1025- if PackageEditingInterface.DoOpenPackageFile(LPKFilename,[pofAddToRecent],false)<>mrOk then
1026- exit;
1027- Pkg:=PackageEditingInterface.FindPackageWithName(ExtractFilenameOnly(LPKFilename));
1028- ...
1029-end;
993+ ...
994+ var
995+ pkg: TIDEPackage;
996+ begin
997+ if PackageEditingInterface.DoOpenPackageFile(LPKFilename,[pofAddToRecent],false)<>mrOk then
998+ exit;
999+ Pkg:=PackageEditingInterface.FindPackageWithName(ExtractFilenameOnly(LPKFilename));
1000+ ...
1001+ end;
10301002```
10311003
1032-> **Warning:** The IDE automatically closes unused packages on idle. Never keep a reference to an TIDEPackage.
1004+[![Warning-icon.png](/wiki-images/b/b2/Warning-icon.png)](</File:Warning-icon.png>)
10331005
1034-## Find the package(s) of a unit
1006+**Warning:** The IDE automatically closes unused packages on idle. Never keep a reference to an TIDEPackage.
10351007
1036-You have the file name of a unit, e.g. '/home/user/unit.pas', and you want to know to which package(s) and project(s) it belongs use this:
1008+## Find the package(s) of a unit
10371009
1038-``` pascal
1010+You have the file name of a unit, e.g. '/home/user/unit.pas', and you want to know to which package(s) and project(s) it belongs use this:
1011+```
10391012uses Classes, PackageIntf, ProjectIntf;
1040-...
1041-var
1042- Owners: TFPList;
1043- i: Integer;
1044- o: TObject;
1045-begin
1046- Owners:=PackageEditingInterface.GetPossibleOwnersOfUnit('/full/path/of/unit.pas',[]);
1047- if Owners=nil then begin
1048- // unit is not directly associated with a project/package
1049- // maybe the unit was for some reason not added, but is reachable
1050- // search in all unit paths of all projects/packages
1051- // Beware: This can lead to false hits
1052- Owners:=PackageEditingInterface.GetPossibleOwnersOfUnit('/full/path/of/unit.pas',
1053- [piosfExcludeOwned,piosfIncludeSourceDirectories]);
1054- end;
1055- if Owners=nil then exit;
1056- try
1057- for i:=0 to Owners.Count-1 do begin
1058- o:=TObject(Owners[i]);
1059- if o is TIDEPackage then begin
1060- writeln('Owner is package ',TIDEPackage(o).Name);
1061- end else if o is TLazProject then begin
1062- writeln('Owner is project ',TLazProject(o).ProjectInfoFile);
1013+ ...
1014+ var
1015+ Owners: TFPList;
1016+ i: Integer;
1017+ o: TObject;
1018+ begin
1019+ Owners:=PackageEditingInterface.GetPossibleOwnersOfUnit('/full/path/of/unit.pas',[]);
1020+ if Owners=nil then begin
1021+ // unit is not directly associated with a project/package
1022+ // maybe the unit was for some reason not added, but is reachable
1023+ // search in all unit paths of all projects/packages
1024+ // Beware: This can lead to false hits
1025+ Owners:=PackageEditingInterface.GetPossibleOwnersOfUnit('/full/path/of/unit.pas',
1026+ [piosfExcludeOwned,piosfIncludeSourceDirectories]);
10631027 end;
1064- end;
1065- finally
1066- Owners.Free;
1067- end;
1028+ if Owners=nil then exit;
1029+ try
1030+ for i:=0 to Owners.Count-1 do begin
1031+ o:=TObject(Owners[i]);
1032+ if o is TIDEPackage then begin
1033+ writeln('Owner is package ',TIDEPackage(o).Name);
1034+ end else if o is TLazProject then begin
1035+ writeln('Owner is project ',TLazProject(o).ProjectInfoFile);
1036+ end;
1037+ end;
1038+ finally
1039+ Owners.Free;
1040+ end;
10681041```
10691042
10701043## Find the required package(s) of a project/package
10711044
1072-You have a project or a package and you want to know which package(s) are required use this:
1073-
1074-``` pascal
1045+You have a project or a package and you want to know which package(s) are required use this:
1046+```
10751047uses Classes, PackageIntf, ProjectIntf;
1076-...
1077-var
1078- lPkgList: TFPList;
1079- i: Integer;
1080-begin
1081- PackageEditingInterface.GetRequiredPackages(LazarusIDE.ActiveProject, lPkgList, [pirNotRecursive]);
1082- try
1083- for i := 0 to lPkgList.Count - 1 do begin
1084- writeln('required package is ' + TIDEPackage(lPkgList[i]).Filename);
1085- end;
1086- finally
1087- lPkgList.Free;
1088- end;
1089-...
1048+ ...
1049+ var
1050+ lPkgList: TFPList;
1051+ i: Integer;
1052+ begin
1053+ PackageEditingInterface.GetRequiredPackages(LazarusIDE.ActiveProject, lPkgList, [pirNotRecursive]);
1054+ try
1055+ for i := 0 to lPkgList.Count - 1 do begin
1056+ writeln('required package is ' + TIDEPackage(lPkgList[i]).Filename);
1057+ end;
1058+ finally
1059+ lPkgList.Free;
1060+ end;
1061+ ...
10901062```
10911063
10921064# Windows
10931065
1094-There are basically four types of IDE windows.
1066+There are basically four types of IDE windows.
10951067
1096-- the main IDE bar is the Application.MainForm. It is always present.
1097-- floating/dockable windows like the Source Editors, the Object Inspectors and Messages.
1098-- the modal forms, like the find dialog, options dialogs and questions.
1099-- hints and completion forms
1068+ * the main IDE bar is the Application.MainForm. It is always present.
1069+ * floating/dockable windows like the Source Editors, the Object Inspectors and Messages.
1070+ * the modal forms, like the find dialog, options dialogs and questions.
1071+ * hints and completion forms
11001072
11011073## Adding a new dockable IDE window
11021074
1103-What is a dockable IDE window: Windows like the Source Editor or the Object Inspector are floating windows, that can be docked if a docking package is installed, and its state, position and size is stored and restored on next IDE start. In order to restore a window the IDE needs a creator as defined in the unit **IDEWindowIntf** of the package IDEIntf. Each dockable window must have a unique name. Do not use generic names like 'FileBrowser' because this will easily clash with other packages. And don't use short names like 'XYZ', because the creator is responsible for all forms beginning with this name.
1075+What is a dockable IDE window: Windows like the Source Editor or the Object Inspector are floating windows, that can be docked if a docking package is installed, and its state, position and size is stored and restored on next IDE start. In order to restore a window the IDE needs a creator as defined in the unit **IDEWindowIntf** of the package IDEIntf. Each dockable window must have a unique name. Do not use generic names like 'FileBrowser' because this will easily clash with other packages. And don't use short names like 'XYZ', because the creator is responsible for all forms beginning with this name.
11041076
11051077### How to register a dockable IDE window
11061078
1107-Remember to choose a long unique name that is a valid pascal identifier. Your window can have any caption you want.
1108-
1109-``` pascal
1079+Remember to choose a long unique name that is a valid pascal identifier. Your window can have any caption you want.
1080+```
11101081uses SysUtils, IDEWindowIntf;
1111-...
1112-var MyIDEWindow: TMyIDEWindow = nil;
1113-
1114-procedure CreateMyIDEWindow(Sender: TObject; aFormName: string; var AForm: TCustomForm; DoDisableAutoSizing: boolean);
1115-begin
1116- // check the name
1117- if CompareText(aFormName,MyIDEWindowName)<>0 then exit;
1118- // create the form if not already done and disable autosizing
1119- IDEWindowCreators.CreateForm(MyIDEWindow,TMyIDEWindowm,DoDisableAutosizing,Application);
1120- ... init the window ...
1121- AForm:=MyIDEWindow;
1122-end;
1123-
1124-procedure Register;
1125-begin
1126- IDEWindowCreators.Add('MyIDEWindow',@CreateMyIDEWindow,nil,'100','50%','+300','+20%');
1127- // the default boundsrect of the form is:
1128- // Left=100, Top=50% of Screen.Height, Width=300, Height=20% of Screen.Height
1129- // when the IDE needs an instance of this window it calls the procedure CreateMyIDEWindow.
1130-end;
1082+ ...
1083+ var MyIDEWindow: TMyIDEWindow = nil;
1084+
1085+ procedure CreateMyIDEWindow(Sender: TObject; aFormName: string; var AForm: TCustomForm; DoDisableAutoSizing: boolean);
1086+ begin
1087+ // check the name
1088+ if CompareText(aFormName,MyIDEWindowName)<>0 then exit;
1089+ // create the form if not already done and disable autosizing
1090+ IDEWindowCreators.CreateForm(MyIDEWindow,TMyIDEWindowm,DoDisableAutosizing,Application);
1091+ ... init the window ...
1092+ AForm:=MyIDEWindow;
1093+ end;
1094+
1095+ procedure Register;
1096+ begin
1097+ IDEWindowCreators.Add('MyIDEWindow',@CreateMyIDEWindow,nil,'100','50%','+300','+20%');
1098+ // the default boundsrect of the form is:
1099+ // Left=100, Top=50% of Screen.Height, Width=300, Height=20% of Screen.Height
1100+ // when the IDE needs an instance of this window it calls the procedure CreateMyIDEWindow.
1101+ end;
11311102```
11321103
11331104### Showing an IDE window
11341105
1135-**Do not use Show**. Use:
1136-
1137-``` pascal
1106+**Do not use Show**. Use:
1107+```
11381108IDEWindowCreators.ShowForm(MyIDEWindow,false);
11391109```
11401110
1141-This will work with docking. The docking system might wrap the form into a docking site. The BringToFront parameter tells the docking system to make the form and all its parent sites visible and bring the top level site to the front.
1111+This will work with docking. The docking system might wrap the form into a docking site. The BringToFront parameter tells the docking system to make the form and all its parent sites visible and bring the top level site to the front.
11421112
11431113### Notes about IDEWindowCreators and SimpleLayoutStorage
11441114
1145-The IDEWindowCreators.SimpleLayoutStorage simply stores the BoundsRect and WindowState of all forms that were once opened. It is used as fallback if no dockmaster is installed. It stores the state even if a DockMaster is installed, so that when the dockmaster is uninstalled the forms bounds are restored.
1115+The IDEWindowCreators.SimpleLayoutStorage simply stores the BoundsRect and WindowState of all forms that were once opened. It is used as fallback if no dockmaster is installed. It stores the state even if a DockMaster is installed, so that when the dockmaster is uninstalled the forms bounds are restored.
11461116
1147-The IDEWindowCreators is used by all dockable forms to register themselves and to show forms. When showing a form the Creator checks if a IDEDockMaster is installed and will delegate the showing to it. If no IDEDockMaster is installed it simply shows the form. The IDEDockMaster can use the information in the IDEWindowCreators to create forms by names and get an idea where to place a form when showing it for the first time. For more details see the packages AnchorDockingDsgn and EasyDockMgDsgn.
1117+The IDEWindowCreators is used by all dockable forms to register themselves and to show forms. When showing a form the Creator checks if a IDEDockMaster is installed and will delegate the showing to it. If no IDEDockMaster is installed it simply shows the form. The IDEDockMaster can use the information in the IDEWindowCreators to create forms by names and get an idea where to place a form when showing it for the first time. For more details see the packages AnchorDockingDsgn and EasyDockMgDsgn.
11481118
11491119# CodeTools
11501120
1151-The CodeTools is a package providing a vast amount of functions to parse, search and change pascal sources and some basic functions for other languages as well. You can use them without the IDE too. It is recommended that you read first about the CodeTools in general before using them in the IDE: [[Codetools]].
1152-
1153-Before calling any of the CodeTools functions in the IDE you should commit the current changes of the source editor to the CodeTools buffers:
1121+The CodeTools is a package providing a vast amount of functions to parse, search and change pascal sources and some basic functions for other languages as well. You can use them without the IDE too. It is recommended that you read first about the CodeTools in general before using them in the IDE: [[Codetools]].
11541122
1155-``` pascal
1123+Before calling any of the CodeTools functions in the IDE you should commit the current changes of the source editor to the CodeTools buffers:
1124+```
11561125uses LazIDEIntf;
1157-...
1158- // save changes in source editor to codetools
1159- LazarusIDE.SaveSourceEditorChangesToCodeCache(-1); // -1: commit all source editors
1126+ ...
1127+ // save changes in source editor to codetools
1128+ LazarusIDE.SaveSourceEditorChangesToCodeCache(-1); // -1: commit all source editors
11601129```
11611130
11621131## Adding a resource directive to a file
11631132
1164-This adds a {\$R example.res} to a pascal unit:
1165-
1166-``` pascal
1133+This adds a {$R example.res} to a pascal unit:
1134+```
11671135procedure AddResourceDirectiveToPascalSource(const Filename: string);
1168-var
1169- ExpandedFilename: String;
1170- CodeBuf: TCodeBuffer;
1171-begin
1172- // make sure the filename is trimmed and contains a full path
1173- ExpandedFilename:=CleanAndExpandFilename(Filename);
1174-
1175- // save changes in source editor to codetools
1176- LazarusIDE.SaveSourceEditorChangesToCodeCache(-1);
1177-
1178- // load the file
1179- CodeBuf:=CodeToolBoss.LoadFile(ExpandedFilename,true,false);
1180-
1181- // add the resource directive
1182- if not CodeToolBoss.AddResourceDirective(CodeBuf,'example.res') then
1183- LazarusIDE.DoJumpToCodeToolBossError;
1184-end;
1136+ var
1137+ ExpandedFilename: String;
1138+ CodeBuf: TCodeBuffer;
1139+ begin
1140+ // make sure the filename is trimmed and contains a full path
1141+ ExpandedFilename:=CleanAndExpandFilename(Filename);
1142+
1143+ // save changes in source editor to codetools
1144+ LazarusIDE.SaveSourceEditorChangesToCodeCache(-1);
1145+
1146+ // load the file
1147+ CodeBuf:=CodeToolBoss.LoadFile(ExpandedFilename,true,false);
1148+
1149+ // add the resource directive
1150+ if not CodeToolBoss.AddResourceDirective(CodeBuf,'example.res') then
1151+ LazarusIDE.DoJumpToCodeToolBossError;
1152+ end;
11851153```
11861154
1187-The codetools provides also functions like FindResourceDirective and RemoveDirective.
1155+The codetools provides also functions like FindResourceDirective and RemoveDirective.
11881156
11891157## Getting the search paths for units and include files
11901158
1191-There are many different search paths in the IDE from projects, packages, the fpc and lazarus directory and there are many types of paths: before or after resolving macros, with or without inherited search paths, as relative or absolute paths. All files in a directory share the same set of search paths. You can get the search paths for each directory fully resolved by asking the codetools:
1192-
1193-``` pascal
1159+There are many different search paths in the IDE from projects, packages, the fpc and lazarus directory and there are many types of paths: before or after resolving macros, with or without inherited search paths, as relative or absolute paths. All files in a directory share the same set of search paths. You can get the search paths for each directory fully resolved by asking the codetools:
1160+```
11941161uses CodeToolManager;
1195-...
1196-
1197-Dir:=''; // the empty directory is for new files and has the same settings as the project directory
1198-
1199-// Getting the search paths for include files:
1200-Path:=CodeToolBoss.GetIncludePathForDirectory(Dir);
1201-
1202-// Getting the search paths for units:
1203-// This search path is passed to the compiler.
1204-// It contains the package output directories, but not the package source directories.
1205-Path:=CodeToolBoss.GetUnitPathForDirectory(Dir);
1206-
1207-// There can be additional unit search paths for the IDE only (not passed to the compiler)
1208-Path:=CodeToolBoss.GetSrcPathForDirectory(Dir);
1209-
1210-// The complete search path contains also all package source paths for units:
1211-Path:=CodeToolBoss.GetCompleteSrcPathForDirectory(Dir);
1162+ ...
1163+
1164+ Dir:=''; // the empty directory is for new files and has the same settings as the project directory
1165+
1166+ // Getting the search paths for include files:
1167+ Path:=CodeToolBoss.GetIncludePathForDirectory(Dir);
1168+
1169+ // Getting the search paths for units:
1170+ // This search path is passed to the compiler.
1171+ // It contains the package output directories, but not the package source directories.
1172+ Path:=CodeToolBoss.GetUnitPathForDirectory(Dir);
1173+
1174+ // There can be additional unit search paths for the IDE only (not passed to the compiler)
1175+ Path:=CodeToolBoss.GetSrcPathForDirectory(Dir);
1176+
1177+ // The complete search path contains also all package source paths for units:
1178+ Path:=CodeToolBoss.GetCompleteSrcPathForDirectory(Dir);
12121179```
12131180
12141181# Source Editor
12151182
12161183## Active source editor
1217-
1218-``` pascal
1184+```
12191185uses SrcEditorIntf;
1220-...
1221-Editor:=SourceEditorManagerIntf.ActiveEditor;
1222-if Editor=nil then exit;
1223-Filename:=Editor.FileName;
1224-ScreenPos:=Editor.CursorScreenXY; // glyph/character and line, both 1-based
1225-TextPos:=Editor.CursorTextXY; // byte and line, both 1-based
1186+ ...
1187+ Editor:=SourceEditorManagerIntf.ActiveEditor;
1188+ if Editor=nil then exit;
1189+ Filename:=Editor.FileName;
1190+ ScreenPos:=Editor.CursorScreenXY; // glyph/character and line, both 1-based
1191+ TextPos:=Editor.CursorTextXY; // byte and line, both 1-based
12261192```
12271193
12281194## Find source editor
12291195
1230-You can search for a disk file using the expanded filename. Keep in mind, that newly created files usually have filenames without directory.
1231-
1232-``` pascal
1196+You can search for a disk file using the expanded filename. Keep in mind, that newly created files usually have filenames without directory.
1197+```
12331198uses SrcEditorIntf;
1234-...
1235-Editor:=SourceEditorManagerIntf.SourceEditorIntfWithFilename(aFilename);
1236-if Editor=nil then exit;
1237-Filename:=Editor.FileName; // can be different to what you searchedm e.g. due to UTF-8 and case insensitive file system
1199+ ...
1200+ Editor:=SourceEditorManagerIntf.SourceEditorIntfWithFilename(aFilename);
1201+ if Editor=nil then exit;
1202+ Filename:=Editor.FileName; // can be different to what you searchedm e.g. due to UTF-8 and case insensitive file system
12381203```
12391204
12401205## Edit source
1241-
1242-``` pascal
1206+```
12431207// You can set the whole source editor text and wipe out the undo history
1244-Editor.SourceText:='New content';
1245-
1246-// In order to keep the undo history use the Selection:
1247-Editor.BlockBegin:=Point(1,1);
1248-Editor.BlockEnd:=Point(1,Editor.LineCount+1);
1249-Editor.Selection:='New Content';
1208+ Editor.SourceText:='New content';
1209+
1210+ // In order to keep the undo history use the Selection:
1211+ Editor.BlockBegin:=Point(1,1);
1212+ Editor.BlockEnd:=Point(1,Editor.LineCount+1);
1213+ Editor.Selection:='New Content';
12501214```
12511215
12521216# SynEdit
12531217
12541218## Getting the settings for a TSynEdit
12551219
1256-When you have a dialog using a TSynEdit and you want to use the same font and settings like the source editor use:
1257-
1258-``` pascal
1220+When you have a dialog using a TSynEdit and you want to use the same font and settings like the source editor use:
1221+```
12591222uses SrcEditorIntf;
1260-...
1261-SourceEditorManagerIntf.GetEditorControlSettings(ASynEdit);
1223+ ...
1224+ SourceEditorManagerIntf.GetEditorControlSettings(ASynEdit);
12621225```
12631226
12641227## Getting the settings for a SynEdit highlighter
12651228
1266-When you have a dialog using a TSynEdit with a highlighter and you want to use the same colors like the source editor highlighter for this language use:
1267-
1268-``` pascal
1229+When you have a dialog using a TSynEdit with a highlighter and you want to use the same colors like the source editor highlighter for this language use:
1230+```
12691231uses SrcEditorIntf;
1270-...
1271-SourceEditorManagerIntf.GetHighlighterSettings(ASynHighlighter);
1232+ ...
1233+ SourceEditorManagerIntf.GetHighlighterSettings(ASynHighlighter);
12721234```
12731235
1274-See for an example: TSQLStringsPropertyEditorDlg.Create in the unit [SQLStringsPropertyEditorDlg](http://svn.freepascal.org/svn/lazarus/trunk/components/sqldb/sqlstringspropertyeditordlg.pas).
1236+See for an example: TSQLStringsPropertyEditorDlg.Create in the unit [SQLStringsPropertyEditorDlg](<http://svn.freepascal.org/svn/lazarus/trunk/components/sqldb/sqlstringspropertyeditordlg.pas>).
12751237
12761238# Help
12771239
12781240## Adding help for sources
12791241
1280-First create a THelpDatabase:
1281-
1282-``` pascal
1242+First create a THelpDatabase:
1243+```
12831244HelpDB := TFPDocHTMLHelpDatabase(
1284- HelpDatabases.CreateHelpDatabase('ANameOfYourChoiceForTheDatabase',
1285- TFPDocHTMLHelpDatabase,true));
1286-HelpDB.DefaultBaseURL := 'http://your.help.org/';
1287-
1288-FPDocNode := THelpNode.CreateURL(HelpDB,
1289- 'Package1 - A new package',
1290- 'file://index.html');
1291-HelpDB.TOCNode := THelpNode.Create(HelpDB,FPDocNode);// once as TOC
1292-DirectoryItem := THelpDBISourceDirectory.Create(FPDocNode,'$(PkgDir)/lcl',
1293- '*.pp;*.pas',false);// and once as normal page
1294-HelpDB.RegisterItem(DirectoryItem);
1245+ HelpDatabases.CreateHelpDatabase('ANameOfYourChoiceForTheDatabase',
1246+ TFPDocHTMLHelpDatabase,true));
1247+ HelpDB.DefaultBaseURL := 'http://your.help.org/';
1248+
1249+ FPDocNode := THelpNode.CreateURL(HelpDB,
1250+ 'Package1 - A new package',
1251+ 'file://index.html');
1252+ HelpDB.TOCNode := THelpNode.Create(HelpDB,FPDocNode);// once as TOC
1253+ DirectoryItem := THelpDBISourceDirectory.Create(FPDocNode,'$(PkgDir)/lcl',
1254+ '*.pp;*.pas',false);// and once as normal page
1255+ HelpDB.RegisterItem(DirectoryItem);
12951256```
12961257
1297-## Adding lines to the messages window
1258+
12981259
1299-``` pascal
1260+## Adding lines to the messages window
1261+```
13001262unit IDEMsgIntf, IDEExternToolIntf;
1301-...
1302-var Dir: String;
1303-begin
1304- Dir:=GetCurrentDir;
1305- IDEMessagesWindow.AddCustomMessage(mluProgress,'Some important message','file.pas',0,0,'My section');
1306-end;
1263+ ...
1264+ var Dir: String;
1265+ begin
1266+ Dir:=GetCurrentDir;
1267+ IDEMessagesWindow.AddCustomMessage(mluProgress,'Some important message','file.pas',0,0,'My section');
1268+ end;
13071269```
13081270
13091271# IDE options frame
13101272
13111273## Open options in a specific frame
1312-
1313-``` pascal
1274+```
13141275uses LazIDEIntf;
1315-...
1316-LazarusIDE.DoOpenIDEOptions(TYourIDEOptionsFrame);
1276+ ...
1277+ LazarusIDE.DoOpenIDEOptions(TYourIDEOptionsFrame);
13171278```
13181279
13191280# Miscellaneous
13201281
13211282## Adding a macro
13221283
1323-You can add your own IDE macros:
1324-
1325-``` pascal
1284+You can add your own IDE macros:
1285+```
13261286uses MacroIntf, MacroDefIntf;
1327-
1328-procedure Register;
1329-begin
1330- // registering a Macro with a fixed value:
1331- IDEMacros.Add(TTransferMacro.Create('MyMacro1','Value','Description',nil,[]));
1332-end;
1333-
1334-// registering a Macro with a dynamic value requires a method of an object
1335-procedure TForm1.Init;
1336-begin
1337- IDEMacros.Add(TTransferMacro.Create('MyMacro','Value','Description',@OnResolveMyMacro,[]));
1338-end;
1339-
1340-function TForm1.OnResolveMyMacro(const s: string; const Data: PtrInt;
1341- var Abort: boolean): string;
1342-// s is the parameter. For example $MyMacro(parameter)
1343-// Data is the querying instance. Can be nil.
1344-// If there is an error set Abort to true.
1345-// The result string can contain macros. For example Result:='$(FPCVer)/$(TargetOS)';
1346-begin
1347- Result:='MyValue';
1348-end;
1287+
1288+ procedure Register;
1289+ begin
1290+ // registering a Macro with a fixed value:
1291+ IDEMacros.Add(TTransferMacro.Create('MyMacro1','Value','Description',nil,[]));
1292+ end;
1293+
1294+ // registering a Macro with a dynamic value requires a method of an object
1295+ procedure TForm1.Init;
1296+ begin
1297+ IDEMacros.Add(TTransferMacro.Create('MyMacro','Value','Description',@OnResolveMyMacro,[]));
1298+ end;
1299+
1300+ function TForm1.OnResolveMyMacro(const s: string; const Data: PtrInt;
1301+ var Abort: boolean): string;
1302+ // s is the parameter. For example $MyMacro(parameter)
1303+ // Data is the querying instance. Can be nil.
1304+ // If there is an error set Abort to true.
1305+ // The result string can contain macros. For example Result:='$(FPCVer)/$(TargetOS)';
1306+ begin
1307+ Result:='MyValue';
1308+ end;
13491309```
13501310
13511311## Query an IDE macro
1352-
1353-``` pascal
1312+```
13541313uses MacroIntf;
1355-
1356-function GetCurrentFPCSourceDirectory: string;
1357-begin
1358- Result:='$(FPCSrcDir)';
1359- if not IDEMacros.SubstituteMacros(Result) then
1360- raise Exception.Create('unable to retrieve FPCSrcDir');
1361-end;
1314+
1315+ function GetCurrentFPCSourceDirectory: string;
1316+ begin
1317+ Result:='$(FPCSrcDir)';
1318+ if not IDEMacros.SubstituteMacros(Result) then
1319+ raise Exception.Create('unable to retrieve FPCSrcDir');
1320+ end;
13621321```
13631322
13641323## Debugging the IDE
13651324
1366-- Open a Lazarus instance (called 1st instance of Lazarus).
1367-- Compile the IDE as usual e.g. via Tools/Build Lazarus. Make sure you set up debugging flags like -g or -gw2. Best is to use the profile "Debug IDE" which contains many flags useful for debugging.
1368-- Open the ide\lazarus.lpi project (always with the 1st instance of Lazarus).
1369-- Compile this lazarus project (always with the 1st instance of Lazarus): another instance of Lazarus should appear (called 2nd instance of Lazarus).
1370-- Set breakpoints as needed etc in the opened source code (in the 1st instance of Lazarus), and play with 2nd run-time instance: for example, install a design-time static package *.lpk, then use, interact with this just installed package inside this 2nd instance.
1325+ * Open a Lazarus instance (called 1st instance of Lazarus).
1326+ * Compile the IDE as usual e.g. via Tools/Build Lazarus. Make sure you set up debugging flags like -g or -gw2. Best is to use the profile "Debug IDE" which contains many flags useful for debugging.
1327+ * Open the ide\lazarus.lpi project (always with the 1st instance of Lazarus).
1328+ * Run this lazarus project (always with the 1st instance of Lazarus): another instance of Lazarus should appear (called 2nd instance of Lazarus).
1329+ * Set breakpoints as needed etc in the opened source code (in the 1st instance of Lazarus), and play with 2nd run-time instance: for example, install a design-time static package *.lpk, then use, interact with this just installed package inside this 2nd instance.
13711330
13721331## Adding a parser for external tool
13731332
1374-There is an example parser for the Delphi dcc32.exe compiler in compilers/delphi/lazdelphi.lpk.
1333+There is an example parser for the Delphi dcc32.exe compiler in compilers/delphi/lazdelphi.lpk.
13751334
1376-It parses the output of dcc32.exe for filename and line number, etc, so when you click on an error or hint in the Messages window the IDE jumps to the right position.
1335+It parses the output of dcc32.exe for filename and line number, etc, so when you click on an error or hint in the Messages window the IDE jumps to the right position.
13771336
1378-After installing this package, you can call dcc32.exe in any project/package instead of fpc: In Compiler Options / Compiler Commands:
1337+After installing this package, you can call dcc32.exe in any project/package instead of fpc: In Compiler Options / Compiler Commands:
13791338
1380-- **Execute Before**: pathofdcc32.exe params...
1381-- **Parsers**: check "Delphi Compiler"
1382-- **Compiler**: Skip calling compiler
1339+ * **Execute Before** : pathofdcc32.exe params...
1340+ * **Parsers** : check "Delphi Compiler"
1341+ * **Compiler** : Skip calling compiler
13831342
13841343## See also
13851344
1386-- [[Creating IDE Help]]
1387-- [[Help protocol]]
1388-- [[How To Write Lazarus Component]]
1345+ * [[Creating IDE Help]]
1346+ * [[Help protocol]]
1347+ * [[How To Write Lazarus Component]]
13891348
13901349---
1391-*From the Free Pascal wiki, page "Extending the IDE", as it stood on 2023-12-04 after a change by Jpasj, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
1350+*From the Free Pascal wiki, page "Extending the IDE", as the Internet Archive kept it on 2024-09-20, last changed on 2024-07-06. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history