Sign in Join
Wiki copytemporary

Extending the IDE/es: what changed

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

OldNewLine
1-## Contents
2-
3- * 1 Extendiendo el IDE
4- * 1.1 Visión general
5- * 1.2 Paquetes y Dependencias MakeFile
6- * 2 Elementos del Menú
7- * 2.1 Añadiendo un elemento al menú
8- * 3 Atajos (Shortcuts)
9- * 4 Config files
10- * 4.1 Cargar/Salvar configuraciones
11- * 5 Components
12- * 5.1 Extending existing components
13- * 5.2 Writing components
14- * 5.2.1 Giving a new component an icon for the component palette
15- * 5.2.2 Hiding a component in the component palette
16- * 5.3 Writing component editors
17- * 5.4 Writing property editors
18- * 5.4.1 Hiding a property in the Object Inspector
19- * 6 Forms
20- * 7 Designer
21- * 7.1 Writing a designer mediator
22- * 7.2 Create a new unique component name
23- * 7.3 Select a component in the designer/object inspector
24- * 7.4 Get unit, designer, form of a file
25- * 7.5 Save unit, designer form
26- * 8 Event handlers
27- * 8.1 Designer events
28- * 8.1.1 Examples for designer events
29- * 8.1.1.1 Example: When dropping a new component move control to a child
30- * 8.1.2 Disabling the designer mouse handler
31- * 8.2 Getting notified when a designer form is modified
32- * 8.3 Project events
33- * 8.4 Other IDE events
34- * 9 Project
35- * 9.1 Current Project
36- * 9.2 All units of current project
37- * 9.2.1 All units of the project inspector
38- * 9.2.2 All used units of project
39- * 9.3 The .lpr, .lpi and .lps file of a project
40- * 9.4 The executable / target file name of a project
41- * 9.5 Add your own project type
42- * 9.6 Add your own file type
43- * 9.6.1 Add a new file type
44- * 9.6.2 Add a new form type
45- * 10 Packages
46- * 10.1 Search in all packages
47- * 10.2 Search a package with a name
48- * 10.3 Obtener el nombre de fichero .lpk de un paquete instalado
49- * 10.4 Instalar paquetes
50- * 10.5 Abrir un fichero de paquete (lpk)
51- * 10.6 Find the package(s) of a unit
52- * 11 Ventanas
53- * 11.1 Añadiendo una nueva ventana de acoplado en el IDE
54- * 11.1.1 Como registrar una ventana acoplable del IDE
55- * 11.1.2 Showing an IDE window
56- * 11.1.3 Notes about IDEWindowCreators and SimpleLayoutStorage
57- * 12 CodeTools
58- * 12.1 Adding a resource directive to a file
59- * 12.2 Getting the search paths for units and include files
60- * 13 Source Editor
61- * 13.1 Active source editor
62- * 14 SynEdit
63- * 14.1 Getting the settings for a TSynEdit
64- * 14.2 Getting the settings for a SynEdit highlighter
65- * 15 Help
66- * 15.1 Adding help for sources
67- * 15.2 Adding lines to the messages window
68- * 16 Miscellaneous
69- * 16.1 Adding a macro
70- * 16.2 Depurando el IDE
71- * 16.3 See also
72-
731# Extendiendo el IDE
742
753## Visión general
764
77-El IDE de Lazarus soporta varios tipos de plugins:
5+El IDE de Lazarus soporta varios tipos de plugins:
786
79-Componentes
80- Estos son los elementos que se encuentrar en la Paleta de Componentes. Por ejemplo, TButton (que se encuentra en la solapa Standard) que se utiliza para crear Buttons.
7+Componentes
8+Estos son los elementos que se encuentrar en la Paleta de Componentes. Por ejemplo, TButton (que se encuentra en la solapa Standard) que se utiliza para crear Buttons.
819
82-Component Editors
83- Cuando haces doble click sobre un componente en el Diseñador, se invoka un editor de componente. El editor de componente puede además añadir un elemento extra al menú emergente (accedido cuando haces click en el pulsador derecho en un componente dentro del Diseñador).
10+Component Editors
11+Cuando haces doble click sobre un componente en el Diseñador, se invoka un editor de componente. El editor de componente puede además añadir un elemento extra al menú emergente (accedido cuando haces click en el pulsador derecho en un componente dentro del Diseñador).
8412
85-Property Editors
86- Cada fila en la sección de la rejilla (grid) del Inspector de Objetos utiliza un editor de propiedades que permite establecer o alterar el valor de su propiedad.
13+Property Editors
14+Cada fila en la sección de la rejilla (grid) del Inspector de Objetos utiliza un editor de propiedades que permite establecer o alterar el valor de su propiedad.
8715
88-Experts
89- Experts abarca todos los demás tipos de plugin. Pueden registrar nuevos elementos de menú, registrar atajos (short cuts), o extender otras características del IDE.
16+Experts
17+Experts abarca todos los demás tipos de plugin. Pueden registrar nuevos elementos de menú, registrar atajos (short cuts), o extender otras características del IDE.
9018
91-Hay dos maneras posibles de añadir tus propios plugins al IDE de Lazarus:
19+Hay dos maneras posibles de añadir tus propios plugins al IDE de Lazarus:
9220
93- 1. Escribir un paquete, instalarlo y registrar tu plugin(s) en el procedimiento 'Register' procedure de una unit.
94- 2. Extender el cógigo de Lazarus, y enviar tu svn diff a la lista de correo de Lazarus.
21+1. Escribir un paquete, instalarlo y registrar tu plugin(s) en el procedimiento 'Register' procedure de una unit.
22+2. Extender el cógigo de Lazarus, y enviar tu svn diff a la lista de correo de Lazarus.
9523
9624## Paquetes y Dependencias MakeFile
9725
98-Los siguientes pasos son necesarios para añadir nuevas dependencias de paquete.
26+Los siguientes pasos son necesarios para añadir nuevas dependencias de paquete.
9927
100- * Crear/Actualizar el MakeFile para el paquete vía Editor de Paquete / Más ... / Crear Makefile.
101- * Corregir todas las dependencias. Ver tools/updatemakefiles.lpi
28+- Crear/Actualizar el MakeFile para el paquete vía Editor de Paquete / Más ... / Crear Makefile.
29+- Corregir todas las dependencias. Ver tools/updatemakefiles.lpi
10230
10331# Elementos del Menú
10432
105-Puedes añadir nuevos elementos al menú, ocultar o mover los existentes y añadir sub menús y secciones. Puedes registrar TMainMenu/TPopupMenu para tus propios formularios de forma que otros paquetes puedan extender más tu formulario. La unidad **MenuIntf** del paquete IDEIntf contiene todos todos los registros para menús y algunos elementos menú estandar del propio IDE.
33+Puedes añadir nuevos elementos al menú, ocultar o mover los existentes y añadir sub menús y secciones. Puedes registrar TMainMenu/TPopupMenu para tus propios formularios de forma que otros paquetes puedan extender más tu formulario. La unidad **MenuIntf** del paquete IDEIntf contiene todos todos los registros para menús y algunos elementos menú estandar del propio IDE.
10634
10735## Añadiendo un elemento al menú
10836
109-Un simple elemento de menú tal como 'View Object Inspector' se llama **TIDEMenuCommand**. Puedes crear uno con **RegisterIDEMenuCommand** , que tiene dos fomas con un montón de parámetros:
110-```
37+Un simple elemento de menú tal como 'View Object Inspector' se llama **TIDEMenuCommand**. Puedes crear uno con **RegisterIDEMenuCommand**, que tiene dos fomas con un montón de parámetros:
38+
39+``` pascal
40+
11141function RegisterIDEMenuCommand(Parent: TIDEMenuSection;
112- const Name, Caption: string;
113- const OnClickMethod: TNotifyEvent = nil;
114- const OnClickProc: TNotifyProcedure = nil;
115- const Command: TIDECommand = nil;
116- const ResourceName: String = ''
117- ): TIDEMenuCommand; overload;
118- function RegisterIDEMenuCommand(const Path, Name, Caption: string;
119- const OnClickMethod: TNotifyEvent = nil;
120- const OnClickProc: TNotifyProcedure = nil;
121- const Command: TIDECommand = nil;
122- const ResourceName: String = ''
123- ): TIDEMenuCommand; overload;
42+ const Name, Caption: string;
43+ const OnClickMethod: TNotifyEvent = nil;
44+ const OnClickProc: TNotifyProcedure = nil;
45+ const Command: TIDECommand = nil;
46+ const ResourceName: String = ''
47+ ): TIDEMenuCommand; overload;
48+function RegisterIDEMenuCommand(const Path, Name, Caption: string;
49+ const OnClickMethod: TNotifyEvent = nil;
50+ const OnClickProc: TNotifyProcedure = nil;
51+ const Command: TIDECommand = nil;
52+ const ResourceName: String = ''
53+ ): TIDEMenuCommand; overload;
12454```
12555
126-La diferencia entre los dos consiste solamente en como se especifica la sección padre del menú. Puedes dar la sección del menú directamente o bien indirectamente a través de su trayecto. Algunas secciones estandar las puedes encontrar en la unidad _MenuIntf_ Por ejemplo, la sección **mnuTools** , la cual es el menú _Tools_ de la barra principal del IDE. Contiene una secciónon **itmSecondaryTools** , que es la sección recomendada para utilidades de terceros.
56+La diferencia entre los dos consiste solamente en como se especifica la sección padre del menú. Puedes dar la sección del menú directamente o bien indirectamente a través de su trayecto. Algunas secciones estandar las puedes encontrar en la unidad *MenuIntf* Por ejemplo, la sección **mnuTools**, la cual es el menú *Tools* de la barra principal del IDE. Contiene una secciónon **itmSecondaryTools**, que es la sección recomendada para utilidades de terceros.
12757
128-Lo siguiente registra un nuevo mandato de menú con el nombre 'MyTool', el caption 'Arranca mi utilidad' y cuando es clickeada ejecuta el procedimiento _StartMyTool_ :
129-```
58+Lo siguiente registra un nuevo mandato de menú con el nombre 'MyTool', el caption 'Arranca mi utilidad' y cuando es clickeada ejecuta el procedimiento *StartMyTool*:
59+
60+``` pascal
13061procedure StartMyTool;
131- begin
132- ...ejecutada cuando el elemento del menú es clickeado...
133- end;
134-
135- procedure Register;
136- begin
137- RegisterIDEMenuCommand(itmSecondaryTools, 'MyTool','Aranca mi utilidad',nil,@StartMyTool);
138- end;
62+begin
63+ ...ejecutada cuando el elemento del menú es clickeado...
64+end;
65+
66+procedure Register;
67+begin
68+ RegisterIDEMenuCommand(itmSecondaryTools, 'MyTool','Aranca mi utilidad',nil,@StartMyTool);
69+end;
13970```
14071
141-If you want to call a method instead of a procedure use the **OnClickMethod** parameter.
72+If you want to call a method instead of a procedure use the **OnClickMethod** parameter.
14273
143-Este elemento de menú no tiene un atajo. En caso de necesitar uno debes crear un **TIDECommand** y pasarlo en el parámetro **Command**. Por ejemplo:
144-```
74+Este elemento de menú no tiene un atajo. En caso de necesitar uno debes crear un **TIDECommand** y pasarlo en el parámetro **Command**. Por ejemplo:
75+
76+``` pascal
14577uses ... IDECommands, MenuIntf;
146- ...
147- var
148- Key: TIDEShortCut;
149- Cat: TIDECommandCategory;
150- CmdMyTool: TIDECommand;
151- begin
152- // register IDE shortcut and menu item
153- Key := IDEShortCut(VK_UNKNOWN,[],VK_UNKNOWN,[]);
154- Cat:=IDECommandList.FindCategoryByName(CommandCategoryToolMenuName);
155- CmdMyTool := RegisterIDECommand(Cat,'Start my tool', 'Starts my tool to do some stuff', Key, nil, @StartMyTool);
156- RegisterIDEMenuCommand(itmSecondaryTools, 'MyTool', 'Start my tool', nil, nil, CmdMyTool);
157- end;
78+...
79+var
80+ Key: TIDEShortCut;
81+ Cat: TIDECommandCategory;
82+ CmdMyTool: TIDECommand;
83+begin
84+ // register IDE shortcut and menu item
85+ Key := IDEShortCut(VK_UNKNOWN,[],VK_UNKNOWN,[]);
86+ Cat:=IDECommandList.FindCategoryByName(CommandCategoryToolMenuName);
87+ CmdMyTool := RegisterIDECommand(Cat,'Start my tool', 'Starts my tool to do some stuff', Key, nil, @StartMyTool);
88+ RegisterIDEMenuCommand(itmSecondaryTools, 'MyTool', 'Start my tool', nil, nil, CmdMyTool);
89+end;
15890```
15991
16092# Atajos (Shortcuts)
16193
162-Todos los atajos (teclas que invocan alguna acción) se encuentran registradas en el IDECommandList. Este es un ejemplo sobre como encontrar todos los mandatos que reaccionan a la pulsación de Ctrl-D o la combinación de teclas pulsadas que comiencen por Ctrl-D:
163-```
94+Todos los atajos (teclas que invocan alguna acción) se encuentran registradas en el IDECommandList. Este es un ejemplo sobre como encontrar todos los mandatos que reaccionan a la pulsación de Ctrl-D o la combinación de teclas pulsadas que comiencen por Ctrl-D:
95+
96+``` pascal
16497uses
165- Classes, SysUtils, LCLProc, IDECommands;
166-
167- procedure ListCtrlD;
168- // Lista todos los idecommands con el atajo Ctrl-D
169- var
170- i: integer;
171- Cmd: TIDECommand;
172- Commands: TFPList;
173- begin
174- Commands:=IDECommandList.FindCommandsByShortCut(IDEShortCut(VK_D,[],VK_UNKNOWN,[])); // VK = Virtual Key
175- try
176- for i:=0 to Commands.Count-1 do begin
177- Cmd:=TIDECommand(Commands[i]);
178- writeln('Cmd: ',Cmd.Name,
179- ' A=',dbgs(Cmd.ShortcutA.Shift1),'-',Cmd.ShortcutA.Key1,
180- ',',dbgs(Cmd.ShortcutA.Shift2),'-',Cmd.ShortcutA.Key2,
181- ' B=',dbgs(Cmd.ShortcutB.Shift1),'-',Cmd.ShortcutB.Key1,
182- ',',dbgs(Cmd.ShortcutB.Shift2),'-',Cmd.ShortcutB.Key2
183- );
184- end;
185- finally
186- Commands.Free;
187- end;
98+ Classes, SysUtils, LCLProc, IDECommands;
99+
100+procedure ListCtrlD;
101+// Lista todos los idecommands con el atajo Ctrl-D
102+var
103+ i: integer;
104+ Cmd: TIDECommand;
105+ Commands: TFPList;
106+begin
107+ Commands:=IDECommandList.FindCommandsByShortCut(IDEShortCut(VK_D,[],VK_UNKNOWN,[])); // VK = Virtual Key
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+ );
188117 end;
118+ finally
119+ Commands.Free;
120+ end;
121+end;
189122```
190123
191124# Config files
192125
193126## Cargar/Salvar configuraciones
194127
195-Todos los ficheros de configuración del IDE se encuentran almacenados en un directorio como ficheros xml. Los paquetes también también pueden añadir sus propios ficheros en dicho directorio. El [[pcp|primary config path]] se puede leer con:
196-
197-
128+Todos los ficheros de configuración del IDE se encuentran almacenados en un directorio como ficheros xml. Los paquetes también también pueden añadir sus propios ficheros en dicho directorio. El [[pcp|primary config path]] se puede leer con:
198129
199-```
130+``` pascal
200131uses LazIDEIntf;
201- ...
202- Directory := LazarusIDE.GetPrimaryConfigPath;
132+...
133+ Directory := LazarusIDE.GetPrimaryConfigPath;
203134```
204135
205-Los paquetes pueden crear su propios ficheros xml con:
206-
207-
136+Los paquetes pueden crear su propios ficheros xml con:
208137
209-```
138+``` pascal
210139uses
211- ..., LCLProc, BaseIDEIntf, LazConfigStorage;
212-
213- const
214- Version = 1;
215- var
216- Config: TConfigStorage;
217- SomeValue: String;
218- SomeInteger: Integer;
219- SomeBoolean: Boolean;
220- FileVersion: LongInt;
221- begin
222- SomeValue:='Default';
223- SomeInteger:=3;
224- SomeBoolean:=true;
225-
226- // Salva configuraciones.
227- try
228- Config:=GetIDEConfigStorage('mysettings.xml',false);
229- try
230- // Almacena el número de versión de forma que las extensiones futuras puedan manejar ficheros de configuración antiguos.
231- Config.SetDeleteValue('Path/To/The/Version',Version,0);
232- // Almacena la variable de cadena "SomeValue".
233- // Si SomeValue tiene el valor por defecto entonces la entrada no se almacena,
234- // de forma que únicamente se almacena las diferencias al valor por defecto.
235- // Así el xml se mantiene pequeño y su valor por defecto puede cambiar en el futuro.
236- Config.SetDeleteValue('Path/To/Some/Value',SomeValue,'Default');
237- Config.SetDeleteValue('Path/To/Some/Integer',SomeInteger,3);
238- Config.SetDeleteValue('Path/To/Some/Boolean',SomeBoolean,true);
239- // there are more overloads for SetDeleteValue, find out with Ctrl+Space
240- finally
241- Config.Free;
242- end;
243-
244- except
245- on E: Exception do begin
246- DebugLn(['Saving mysettings.xml failed: ',E.Message]);
247- end;
248- end;
249-
250- // load settings
251- try
252- Config:=GetIDEConfigStorage('mysettings.xml',true);
253- try
254- // read the version of the config
255- FileVersion:=Config.GetValue('Path/To/The/Version',0);
256- // read a string variable "SomeValue". If the entry does not exist, use
257- // the Default
258- SomeValue:=Config.GetValue('Path/To/Some/Value','Default');
259- SomeInteger:=Config.GetValue('Path/To/Some/Integer',3);
260- SomeBoolean:=Config.GetValue('Path/To/Some/Boolean',true);
261- finally
262- Config.Free;
263- end;
264- except
265- on E: Exception do begin
266- DebugLn(['Loading mysettings.xml failed: ',E.Message]);
267- end;
268- end;
140+ ..., LCLProc, BaseIDEIntf, LazConfigStorage;
141+
142+const
143+ Version = 1;
144+var
145+ Config: TConfigStorage;
146+ SomeValue: String;
147+ SomeInteger: Integer;
148+ SomeBoolean: Boolean;
149+ FileVersion: LongInt;
150+begin
151+ SomeValue:='Default';
152+ SomeInteger:=3;
153+ SomeBoolean:=true;
154+
155+ // Salva configuraciones.
156+ try
157+ Config:=GetIDEConfigStorage('mysettings.xml',false);
158+ try
159+ // Almacena el número de versión de forma que las extensiones futuras puedan manejar ficheros de configuración antiguos.
160+ Config.SetDeleteValue('Path/To/The/Version',Version,0);
161+ // Almacena la variable de cadena "SomeValue".
162+ // Si SomeValue tiene el valor por defecto entonces la entrada no se almacena,
163+ // de forma que únicamente se almacena las diferencias al valor por defecto.
164+ // Así el xml se mantiene pequeño y su valor por defecto puede cambiar en el futuro.
165+ Config.SetDeleteValue('Path/To/Some/Value',SomeValue,'Default');
166+ Config.SetDeleteValue('Path/To/Some/Integer',SomeInteger,3);
167+ Config.SetDeleteValue('Path/To/Some/Boolean',SomeBoolean,true);
168+ // there are more overloads for SetDeleteValue, find out with Ctrl+Space
169+ finally
170+ Config.Free;
269171 end;
172+
173+ except
174+ on E: Exception do begin
175+ DebugLn(['Saving mysettings.xml failed: ',E.Message]);
176+ end;
177+ end;
178+
179+ // load settings
180+ try
181+ Config:=GetIDEConfigStorage('mysettings.xml',true);
182+ try
183+ // read the version of the config
184+ FileVersion:=Config.GetValue('Path/To/The/Version',0);
185+ // read a string variable "SomeValue". If the entry does not exist, use
186+ // the Default
187+ SomeValue:=Config.GetValue('Path/To/Some/Value','Default');
188+ SomeInteger:=Config.GetValue('Path/To/Some/Integer',3);
189+ SomeBoolean:=Config.GetValue('Path/To/Some/Boolean',true);
190+ finally
191+ Config.Free;
192+ end;
193+ except
194+ on E: Exception do begin
195+ DebugLn(['Loading mysettings.xml failed: ',E.Message]);
196+ end;
197+ end;
198+end;
270199```
271200
272-Notes:
201+Notes:
273202
274- * Every IDE plugin should use its own config file. Especially you must not use the standard IDE option files like [editoroptions.xml](</index.php?title=editoroptions.xml&action=edit&redlink=1> "editoroptions.xml \(page does not exist\)") or [environmentoptions.xml](</index.php?title=environmentoptions.xml&action=edit&redlink=1> "environmentoptions.xml \(page does not exist\)").
275- * GetIDEConfigStorage when loading checks if the file exists and if not it will copy the template from the secondary config directory.
276- * Since 0.9.31 you can use this function to load a file outside the primary config directory.
203+- 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]].
204+- GetIDEConfigStorage when loading checks if the file exists and if not it will copy the template from the secondary config directory.
205+- Since 0.9.31 you can use this function to load a file outside the primary config directory.
277206
278207# Components
279208
280209## Extending existing components
281210
282-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+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.
283212
284-Add the properties you need with appropriate setter and getter methods and storage fields.
213+Add the properties you need with appropriate setter and getter methods and storage fields.
285214
286-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.
215+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.
287216
288-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).
217+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).
289218
290-Here is a simple example:
291-```
219+Here is a simple example:
220+
221+``` pascal
292222unit Unit1;
293-
294- {$mode objfpc}{$H+}
295-
296- interface
297-
298- uses
299- Classes, SysUtils, Forms, StdCtrls;
300-
301- type
302- TEdX = class(TEdit)
303- private
304- FNewIntProp: integer;
305- FNewStrProp: string;
306- procedure SetNewIntProp(AValue: integer);
307- procedure SetNewStrProp(AValue: string);
308- public
309- constructor Create(theComponent: TComponent); override;
310- property NewIntProp: integer read FNewIntProp write SetNewIntProp;
311- property NewStrProp: string read FNewStrProp write SetNewStrProp;
312- end;
313-
314- TForm1 = class(TForm)
315- procedure FormCreate(Sender: TObject);
316- private
317- edX: TEdX;
318- public
319- end;
320-
321- var
322- Form1: TForm1;
323-
324- implementation
325-
326- { TForm1 }
327-
328- procedure TForm1.FormCreate(Sender: TObject);
329- begin
330- edX:= TEdX.Create(Self);
331- edX.Parent:= Self;
332- edX.Caption:= IntToStr(edX.NewIntProp);
333- Caption:= edX.NewStrProp;
334- end;
335-
336- {$R *.lfm}
337-
338- { TEdX }
339-
340- procedure TEdX.SetNewIntProp(AValue: integer);
341- begin
342- if FNewIntProp=AValue then Exit;
343- FNewIntProp:=AValue;
344- // add custom code here
345- end;
346-
347- procedure TEdX.SetNewStrProp(AValue: string);
348- begin
349- if FNewStrProp=AValue then Exit;
350- FNewStrProp:=AValue;
351- // add custom code here
352- end;
353-
354- constructor TEdX.Create(theComponent: TComponent);
355- begin
356- inherited Create(theComponent);
357- FNewStrProp:= 'new string property value';
358- FNewIntProp:= 99;
359- end;
360-
361- end.
223+
224+{$mode objfpc}{$H+}
225+
226+interface
227+
228+uses
229+ Classes, SysUtils, Forms, StdCtrls;
230+
231+type
232+ TEdX = class(TEdit)
233+ private
234+ FNewIntProp: integer;
235+ FNewStrProp: string;
236+ procedure SetNewIntProp(AValue: integer);
237+ procedure SetNewStrProp(AValue: string);
238+ public
239+ constructor Create(theComponent: TComponent); override;
240+ property NewIntProp: integer read FNewIntProp write SetNewIntProp;
241+ property NewStrProp: string read FNewStrProp write SetNewStrProp;
242+ end;
243+
244+ TForm1 = class(TForm)
245+ procedure FormCreate(Sender: TObject);
246+ private
247+ edX: TEdX;
248+ public
249+ end;
250+
251+var
252+ Form1: TForm1;
253+
254+implementation
255+
256+{ TForm1 }
257+
258+procedure TForm1.FormCreate(Sender: TObject);
259+begin
260+ edX:= TEdX.Create(Self);
261+ edX.Parent:= Self;
262+ edX.Caption:= IntToStr(edX.NewIntProp);
263+ Caption:= edX.NewStrProp;
264+end;
265+
266+{$R *.lfm}
267+
268+{ TEdX }
269+
270+procedure TEdX.SetNewIntProp(AValue: integer);
271+begin
272+ if FNewIntProp=AValue then Exit;
273+ FNewIntProp:=AValue;
274+ // add custom code here
275+end;
276+
277+procedure TEdX.SetNewStrProp(AValue: string);
278+begin
279+ if FNewStrProp=AValue then Exit;
280+ FNewStrProp:=AValue;
281+ // add custom code here
282+end;
283+
284+constructor TEdX.Create(theComponent: TComponent);
285+begin
286+ inherited Create(theComponent);
287+ FNewStrProp:= 'new string property value';
288+ FNewIntProp:= 99;
289+end;
290+
291+end.
362292```
363293
364294## Writing components
365295
366-_Main article:[[How To Write Lazarus Component]]_
296+*Main article: [[How To Write Lazarus Component]]*
367297
368-You can create new components via the package editor.
298+You can create new components via the package editor.
369299
370-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**.
300+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**.
371301
372302### Giving a new component an icon for the component palette
373303
374-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:
375-```
304+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:
305+
306+``` bash
376307~/lazarus/tools/lazres mybutton.lrs tmybutton.png
377- # note: Laz trunk/1.4+? uses .res resource files instead of .lrs files:
378- ~/lazarus/tools/lazres mybutton.res tmybutton.png
308+# note: Laz trunk/1.4+? uses .res resource files instead of .lrs files:
309+~/lazarus/tools/lazres mybutton.res tmybutton.png
379310```
380311
381-This creates an pascal include file, which is used in the initialization section of mybutton.pas:
382-```
312+This creates an pascal include file, which is used in the initialization section of mybutton.pas:
313+
314+``` pascal
383315initialization
384- {$I mybutton.lrs}
385- // or in newer Lazarus version - to docheck syntax
386- {$R mybutton.res}
316+ {$I mybutton.lrs}
317+ // or in newer Lazarus version - to docheck syntax
318+ {$R mybutton.res}
387319```
388320
389-Install the package.
321+Install the package.
390322
391323### Hiding a component in the component palette
392324
393-Package IDEIntf, unit componentreg:
394-```
395-IDEComponentPalette.FindComponent('TButton').Visible:=false;
396-```
325+Package IDEIntf, unit componentreg:
397326
327+`IDEComponentPalette.FindComponent('TButton').Visible:=false;`
328+
398329## Writing component editors
399330
400-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.
331+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.
401332
402333## Writing property editors
403334
404-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.
335+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.
405336
406337### Hiding a property in the Object Inspector
407338
408-There is a special property editor for hiding: ` `
339+There is a special property editor for hiding:
409340
410-`
411-```
412-RegisterPropertyEditor(TypeInfo(TAnchorSide), TControl, 'AnchorSideLeft', THiddenPropertyEditor);
413-```
341+` RegisterPropertyEditor(TypeInfo(TAnchorSide), TControl, 'AnchorSideLeft', THiddenPropertyEditor);`
414342
415-```
343+# Forms
416344
417-``
345+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:
418346
419-# Forms
347+- create a design time package (or extend an existing one)
348+- your form must be in a unit of this package
420349
421-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:
350+``` pascal
351+...
352+interface
422353
423- * create a design time package (or extend an existing one)
424- * your form must be in a unit of this package
354+uses FormEditingIntf, Forms;
425355
426-```
356+type
357+ TYourForm = class(TForm)
358+ private
359+ FYourProperty: integer;
360+ published
361+ property YourProperty: integer read FYourProperty write FYourProperty;
362+ end;
363+
364+procedure Register;
365+
366+implementation
367+
368+procedure Register;
369+begin
370+ FormEditingHook.RegisterDesignerBaseClass(TYourForm);
371+end;
427372...
428- interface
429-
430- uses FormEditingIntf, Forms;
431-
432- type
433- TYourForm = class(TForm)
434- private
435- FYourProperty: integer;
436- published
437- property YourProperty: integer read FYourProperty write FYourProperty;
438- end;
439-
440- procedure Register;
441-
442- implementation
443-
444- procedure Register;
445- begin
446- FormEditingHook.RegisterDesignerBaseClass(TYourForm);
447- end;
448- ...
449373```
450374
451- * check the "Register" in the package editor for this unit.
452- * Install the package in the IDE
375+- check the "Register" in the package editor for this unit.
376+- Install the package in the IDE
453377
454378# Designer
455379
456380## Writing a designer mediator
457381
458-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/.
382+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/.
459383
460- * 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.
461- * Open the the example project examples/designnonlcl/project/NonLCL1.lpi.
462- * 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.
384+- 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.
385+- Open the the example project examples/designnonlcl/project/NonLCL1.lpi.
386+- 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.
463387
464388## Create a new unique component name
465-```
389+
390+``` pascal
466391uses FormEditingIntf;
467-
468- ...
469-
470- NewName:=FormEditingHook.CreateUniqueComponentName(AComponent);
471-
472- // Or if you need to create the name before creating the component:
473-
474- NewName:=FormEditingHook.CreateUniqueComponentName(ComponentClassName,OwnerComponent);
475- // ComponentClassName will be used as prefix, without a leading T.
476- // OwnerComponent is the new owner of the new component.
392+
393+...
394+
395+NewName:=FormEditingHook.CreateUniqueComponentName(AComponent);
396+
397+// Or if you need to create the name before creating the component:
398+
399+NewName:=FormEditingHook.CreateUniqueComponentName(ComponentClassName,OwnerComponent);
400+// ComponentClassName will be used as prefix, without a leading T.
401+// OwnerComponent is the new owner of the new component.
477402```
478403
479404## Select a component in the designer/object inspector
480-```
405+
406+``` pascal
481407uses propedits;
482- ..
483- GlobalDesignHook.SelectOnlyThis(AComponent);
408+..
409+GlobalDesignHook.SelectOnlyThis(AComponent);
484410```
485411
486412## Get unit, designer, form of a file
487-```
413+
414+``` pascal
488415uses ProjectIntf, LazIDEIntf, ComponentEditors;
489- ...
490- // open file
491- if LazarusIDE.DoOpenEditorFile(Filename,-1,-1,[ofAddToRecent])<>mrOk then exit;
492- // get file interface
493- aFile:=LazarusIDE.ActiveProject.FindFile(Filename,[]);
494- // get designer
495- aDesigner:=TComponentEditorDesigner(LazarusIDE.GetDesignerWithProjectFile(AFile,true));
496- if aDesigner=nil then
497- exit; // this unit has no resource (form, frame, datamodule, etc.)
498- // get form
499- aForm:=aDesigner.Form;
416+...
417+// open file
418+if LazarusIDE.DoOpenEditorFile(Filename,-1,-1,[ofAddToRecent])<>mrOk then exit;
419+// get file interface
420+aFile:=LazarusIDE.ActiveProject.FindFile(Filename,[]);
421+// get designer
422+aDesigner:=TComponentEditorDesigner(LazarusIDE.GetDesignerWithProjectFile(AFile,true));
423+if aDesigner=nil then
424+ exit; // this unit has no resource (form, frame, datamodule, etc.)
425+// get form
426+aForm:=aDesigner.Form;
500427```
501428
502429## Save unit, designer form
503-```
430+
431+``` pascal
504432uses LazIDEIntf;
505- ...
506- if LazarusIDE.DoSaveEditorFile(Filename,[])<>mrOk then exit;
433+...
434+if LazarusIDE.DoSaveEditorFile(Filename,[])<>mrOk then exit;
507435```
508436
509437# Event handlers
510438
511-There are several events in the IDE, for which plugins can add their own handlers.
439+There are several events in the IDE, for which plugins can add their own handlers.
512440
513441## Designer events
514442
515-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.
443+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.
516444
517-Examples:
445+Examples:
518446
519- * Adding your handler (this is normally done in the constructor of your object):
447+- Adding your handler (this is normally done in the constructor of your object):
520448
521-```
522-GlobalDesignHook.AddHandlerPersistentAdded(@YourOnPersistentAdded);
523-```
449+` GlobalDesignHook.AddHandlerPersistentAdded(@YourOnPersistentAdded);`
524450
525- * Removing your handler:
451+- Removing your handler:
526452
527-```
528-GlobalDesignHook.RemoveHandlerPersistentAdded(@YourOnPersistentAdded);
529-```
453+` GlobalDesignHook.RemoveHandlerPersistentAdded(@YourOnPersistentAdded);`
530454
531- * You can remove all handlers at once. For example, it is a good idea to add this line in the destructor of your object:
455+- You can remove all handlers at once. For example, it is a good idea to add this line in the destructor of your object:
532456
533-```
534-GlobalDesignHook.RemoveAllHandlersForObject(Self);
535-```
457+` GlobalDesignHook.RemoveAllHandlersForObject(Self);`
536458
537-The handlers of GlobalDesignHook:
459+The handlers of GlobalDesignHook:
538460
539- * Lookup root
540- * ChangeLookupRoot - Called when the "LookupRoot" changed. The "LookupRoot" is the owner object of the currently selected components. Normally this is a TForm.
541- * Methods
542- * CreateMethod
543- * GetMethodName
544- * GetMethods
545- * MethodExists
546- * RenameMethod
547- * ShowMethod
548- * MethodFromAncestor
549- * ChainCall
550- * Components
551- * GetComponent
552- * GetComponentName
553- * GetComponentNames
554- * GetRootClassName
555- * 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.
556- * ComponentRenamed - Called when a component was renamed
557- * TPersistent objects
558- * PersistentAdded - Called when a new TPersistent was added to the LookupRoot
559- * PersistentDeleting - Called before a TPersistent is freed.
560- * DeletePersistent - Called by the IDE to delete a TPersistent.
561- * GetSelectedPersistents - Called when getting the current selection of TPersistent.
562- * SetSelectedPersistents - Called when setting the current selection of TPersistent.
563- * GetObject
564- * GetObjectName
565- * GetObjectNames
566- * Modifing
567- * Modified
568- * Revert
569- * RefreshPropertyValues
570- * Selection
571- * SetSelection
572- * GetSelection
461+- Lookup root
462+ - ChangeLookupRoot - Called when the "LookupRoot" changed. The "LookupRoot" is the owner object of the currently selected components. Normally this is a TForm.
463+- Methods
464+ - CreateMethod
465+ - GetMethodName
466+ - GetMethods
467+ - MethodExists
468+ - RenameMethod
469+ - ShowMethod
470+ - MethodFromAncestor
471+ - ChainCall
472+- Components
473+ - GetComponent
474+ - GetComponentName
475+ - GetComponentNames
476+ - GetRootClassName
477+ - 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.
478+ - ComponentRenamed - Called when a component was renamed
479+- TPersistent objects
480+ - PersistentAdded - Called when a new TPersistent was added to the LookupRoot
481+ - PersistentDeleting - Called before a TPersistent is freed.
482+ - DeletePersistent - Called by the IDE to delete a TPersistent.
483+ - GetSelectedPersistents - Called when getting the current selection of TPersistent.
484+ - SetSelectedPersistents - Called when setting the current selection of TPersistent.
485+ - GetObject
486+ - GetObjectName
487+ - GetObjectNames
488+- Modifing
489+ - Modified
490+ - Revert
491+ - RefreshPropertyValues
492+- Selection
493+ - SetSelection
494+ - GetSelection
573495
574496### Examples for designer events
575497
576498#### Example: When dropping a new component move control to a child
577-```
499+
500+``` pascal
578501...
579- type
580- TYourDesignerExtension = class
581- private
582- function AddClicked(ADesigner: TIDesigner;
583- MouseDownComponent: TComponent; Button: TMouseButton;
584- Shift: TShiftState; X, Y: Integer;
585- var AComponentClass: TComponentClass;
586- var NewParent: TComponent): boolean of object;
587- public
588- constructor Create;
589- destructor Destroy; override;
590- end;
591-
592- ...
593- constructor TYourDesignerExtension.Create;
594- begin
595- // register your handler
596- GlobalDesignHook.AddHandlerAddClicked(@AddClicked);
597- end;
598-
599- destructor TYourDesignerExtension.Destroy;
600- begin
601- // remove your handler:
602- GlobalDesignHook.RemoveHandlerAddClicked(@AddClicked);
603- end;
604-
605- function TYourDesignerExtension.AddClicked(ADesigner: TIDesigner;
606- MouseDownComponent: TComponent; Button: TMouseButton;
607- Shift: TShiftState; X, Y: Integer;
608- var AComponentClass: TComponentClass;
609- var NewParent: TComponent): boolean of object;
610- begin
611- // in this example TYourControl is a custom control with a ChildControl.
612- // Whenever the user drops a control on the TYourControl instead of adding
613- // the new control to the TYourControl, the new control is added as child
614- // of ChildControl.
615- // created
616- if MouseDownComponent is TYourControl then
617- NewParent:=TYourControl(MouseDownComponent).ChildControl;
618- Result:=true;
619- end;
502+type
503+ TYourDesignerExtension = class
504+ private
505+ function AddClicked(ADesigner: TIDesigner;
506+ MouseDownComponent: TComponent; Button: TMouseButton;
507+ Shift: TShiftState; X, Y: Integer;
508+ var AComponentClass: TComponentClass;
509+ var NewParent: TComponent): boolean of object;
510+ public
511+ constructor Create;
512+ destructor Destroy; override;
513+ end;
514+
515+...
516+constructor TYourDesignerExtension.Create;
517+begin
518+ // register your handler
519+ GlobalDesignHook.AddHandlerAddClicked(@AddClicked);
520+end;
521+
522+destructor TYourDesignerExtension.Destroy;
523+begin
524+ // remove your handler:
525+ GlobalDesignHook.RemoveHandlerAddClicked(@AddClicked);
526+end;
527+
528+function TYourDesignerExtension.AddClicked(ADesigner: TIDesigner;
529+ MouseDownComponent: TComponent; Button: TMouseButton;
530+ Shift: TShiftState; X, Y: Integer;
531+ var AComponentClass: TComponentClass;
532+ var NewParent: TComponent): boolean of object;
533+begin
534+ // in this example TYourControl is a custom control with a ChildControl.
535+ // Whenever the user drops a control on the TYourControl instead of adding
536+ // the new control to the TYourControl, the new control is added as child
537+ // of ChildControl.
538+ // created
539+ if MouseDownComponent is TYourControl then
540+ NewParent:=TYourControl(MouseDownComponent).ChildControl;
541+ Result:=true;
542+end;
620543```
621544
622545### Disabling the designer mouse handler
623546
624-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:
625-```
547+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:
548+
549+``` pascal
626550type
627- TYourControl = class(TCustomControl)
628- protected
629- procedure CMDesignHitTest(var Message: TLMessage); message CM_DESIGNHITTEST;
630- end;
631- ...
632- procedure TYourControl.CMDesignHitTest(var Message: TLMessage);
633- var
634- p: TSmallPoint;
635- aShiftState: TShiftState;
636- begin
637- aShiftState:=KeysToShiftState(PtrUInt(Message.WParam));
638- p:=TSmallPoint(longint(Message.LParam));
639- debugln(['TForm1.CMDesignHitTest ShiftState=',dbgs(aShiftState),' x=',p.x,' y=',p.y]);
640- if SkipDesignerMouseHandler then
641- Message.Result:=1; // now the designer calls the normal MouseUp, MouseMove and MouseDown methods.
642- end;
551+ TYourControl = class(TCustomControl)
552+ protected
553+ procedure CMDesignHitTest(var Message: TLMessage); message CM_DESIGNHITTEST;
554+ end;
555+...
556+procedure TYourControl.CMDesignHitTest(var Message: TLMessage);
557+var
558+ p: TSmallPoint;
559+ aShiftState: TShiftState;
560+begin
561+ aShiftState:=KeysToShiftState(PtrUInt(Message.WParam));
562+ p:=TSmallPoint(longint(Message.LParam));
563+ debugln(['TForm1.CMDesignHitTest ShiftState=',dbgs(aShiftState),' x=',p.x,' y=',p.y]);
564+ if SkipDesignerMouseHandler then
565+ Message.Result:=1; // now the designer calls the normal MouseUp, MouseMove and MouseDown methods.
566+end;
643567```
644568
645569## Getting notified when a designer form is modified
646570
647-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:
648-```
571+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:
572+
573+``` pascal
649574procedure TYourAddOn.OnDesignerModified(Sender: TObject);
650- var
651- IDEDesigner: TComponentEditorDesigner;
652- begin
653- IDEDesigner:=TComponentEditorDesigner(Sender);
654- ...
655- end;
656-
657- procedure TYourAddOn.ConnectDesignerForm(Form1: TCustomForm);
658- var
659- IDEDesigner: TComponentEditorDesigner;
660- begin
661- IDEDesigner:=TComponentEditorDesigner(Form1.Designer);
662- IDEDesigner.AddHandlerModified(@OnDesignerModified);
663- end;
575+var
576+ IDEDesigner: TComponentEditorDesigner;
577+begin
578+ IDEDesigner:=TComponentEditorDesigner(Sender);
579+ ...
580+end;
581+
582+procedure TYourAddOn.ConnectDesignerForm(Form1: TCustomForm);
583+var
584+ IDEDesigner: TComponentEditorDesigner;
585+begin
586+ IDEDesigner:=TComponentEditorDesigner(Form1.Designer);
587+ IDEDesigner.AddHandlerModified(@OnDesignerModified);
588+end;
664589```
665590
666591## Project events
667592
668-These events are defined in unit LazIDEIntf.
593+These events are defined in unit LazIDEIntf.
669594
670- * **LazarusIDE.AddHandlerOnProjectClose** : called before a project is closed
671- * **LazarusIDE.AddHandlerOnProjectOpened** : called after the project was completely opened (for example all required packages were loaded, units were opened in the source editor)
672- * **LazarusIDE.AddHandlerOnSavingAll** : called before IDE saves everything
673- * **LazarusIDE.AddHandlerOnSavedAll** : called after IDE saved everything
674- * **LazarusIDE.AddHandlerOnProjectBuilding** : called before IDE builds the project
675- * **LazarusIDE.AddHandlerOnProjectDependenciesCompiling** : called before IDE compiles package dependencies of project
676- * **LazarusIDE.AddHandlerOnProjectDependenciesCompiled** : called after IDE compiled package dependencies of project
677- * **LazarusIDE.AddHandlerOnProjectBuildingFinished** : called after IDE built the project (successful or not) (since 1.5)
595+- **LazarusIDE.AddHandlerOnProjectClose**: called before a project is closed
596+- **LazarusIDE.AddHandlerOnProjectOpened**: called after the project was completely opened (for example all required packages were loaded, units were opened in the source editor)
597+- **LazarusIDE.AddHandlerOnSavingAll**: called before IDE saves everything
598+- **LazarusIDE.AddHandlerOnSavedAll**: called after IDE saved everything
599+- **LazarusIDE.AddHandlerOnProjectBuilding**: called before IDE builds the project
600+- **LazarusIDE.AddHandlerOnProjectDependenciesCompiling**: called before IDE compiles package dependencies of project
601+- **LazarusIDE.AddHandlerOnProjectDependenciesCompiled**: called after IDE compiled package dependencies of project
602+- **LazarusIDE.AddHandlerOnProjectBuildingFinished**: called after IDE built the project (successful or not) (since 1.5)
678603
679604## Other IDE events
680605
681-uses LazIDEIntf;
606+uses LazIDEIntf;
682607
683-Call these in the Register procedure of your package:
608+Call these in the Register procedure of your package:
684609
685- * **LazarusIDE.AddHandlerOnIDERestoreWindows** : called when IDE is restores its windows (before opening the first project)
686- * **LazarusIDE.AddHandlerOnIDEClose** : called when IDE is shutting down (after closequery, so no more interactivity)
687- * **LazarusIDE.AddHandlerOnQuickSyntaxCheck** : called when the menu item or the shortcut for Quick syntax check is executed
688- * **LazarusIDE.AddHandlerOnLazarusBuilding** : // called before IDE builds Lazarus IDE (since 1.5)
689- * **LazarusIDE.AddHandlerOnLazarusBuildingFinished** : // called after IDE builds Lazarus IDE (since 1.5)
690- * **LazarusIDE.AddHandlerGetFPCFrontEndParams** : // called when the IDE gets the parameters of the 'fpc' front end tool (since 1.5)
691- * **LazarusIDE.AddHandlerGetFPCFrontEndPath** : // called when the IDE gets the path of the 'fpc' front end tool (since 1.5)
692- * **LazarusIDE.AddHandlerOnUpdateIDEComponentPalette** : ?
693- * **LazarusIDE.AddHandlerOnUpdateComponentPageControl** : ?
694- * **LazarusIDE.AddHandlerOnShowDesignerFormOfSource** : // called after showing a designer form for code editor (AEditor can be nil!) (since 1.5)
695- * **LazarusIDE.AddHandlerOnShowSourceOfActiveDesignerForm** : // called after showing a code of designer form (since 1.5)
696- * **LazarusIDE.AddHandlerOnChangeToolStatus** : //called when IDEToolStatus has changed (e.g. itNone->itBuilder etc.) (since 1.5)
610+- **LazarusIDE.AddHandlerOnIDERestoreWindows**: called when IDE is restores its windows (before opening the first project)
611+- **LazarusIDE.AddHandlerOnIDEClose**: called when IDE is shutting down (after closequery, so no more interactivity)
612+- **LazarusIDE.AddHandlerOnQuickSyntaxCheck**: called when the menu item or the shortcut for Quick syntax check is executed
613+- **LazarusIDE.AddHandlerOnLazarusBuilding**: // called before IDE builds Lazarus IDE (since 1.5)
614+- **LazarusIDE.AddHandlerOnLazarusBuildingFinished**: // called after IDE builds Lazarus IDE (since 1.5)
615+- **LazarusIDE.AddHandlerGetFPCFrontEndParams**: // called when the IDE gets the parameters of the 'fpc' front end tool (since 1.5)
616+- **LazarusIDE.AddHandlerGetFPCFrontEndPath**: // called when the IDE gets the path of the 'fpc' front end tool (since 1.5)
617+- **LazarusIDE.AddHandlerOnUpdateIDEComponentPalette**: ?
618+- **LazarusIDE.AddHandlerOnUpdateComponentPageControl**: ?
619+- **LazarusIDE.AddHandlerOnShowDesignerFormOfSource**: // called after showing a designer form for code editor (AEditor can be nil!) (since 1.5)
620+- **LazarusIDE.AddHandlerOnShowSourceOfActiveDesignerForm**: // called after showing a code of designer form (since 1.5)
621+- **LazarusIDE.AddHandlerOnChangeToolStatus**: //called when IDEToolStatus has changed (e.g. itNone-\>itBuilder etc.) (since 1.5)
697622
698-Call these in the initialization section of one of your units:
623+Call these in the initialization section of one of your units:
699624
700- * **AddBootHandler**
701- * AddBootHandler(**libhTransferMacrosCreated** ,@YourProc): called after IDEMacros were created (since 1.3)
702- * AddBootHandler(**libhEnvironmentOptionsLoaded** ,@YourProc): called after environment options were loaded (since 1.3)
625+- **AddBootHandler**
626+ - AddBootHandler(**libhTransferMacrosCreated**,@YourProc): called after IDEMacros were created (since 1.3)
627+ - AddBootHandler(**libhEnvironmentOptionsLoaded**,@YourProc): called after environment options were loaded (since 1.3)
703628
704629# Project
705630
706631## Current Project
707632
708-The current main project can be obtained by LazarusIDE.ActiveProject. (unit LazIDEIntf)
633+The current main project can be obtained by LazarusIDE.ActiveProject. (unit LazIDEIntf)
709634
710635## All units of current project
711636
712-Note that the term "units of project" is ambiguous.
637+Note that the term "units of project" is ambiguous.
713638
714- * 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.
715- * Units from used packages are not project units.
639+- 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.
640+- Units from used packages are not project units.
716641
717642### All units of the project inspector
718643
719-To iterate through all pascal units listed in the project inspector you can use for example:
720-```
644+To iterate through all pascal units listed in the project inspector you can use for example:
645+
646+``` pascal
721647uses
722- LCLProc, FileUtil, LazIDEIntf, ProjectIntf;
723-
724- procedure ListProjectUnits;
725- var
726- LazProject: TLazProject;
727- i: Integer;
728- LazFile: TLazProjectFile;
648+ LCLProc, FileUtil, LazIDEIntf, ProjectIntf;
649+
650+procedure ListProjectUnits;
651+var
652+ LazProject: TLazProject;
653+ i: Integer;
654+ LazFile: TLazProjectFile;
655+begin
656+ LazProject:=LazarusIDE.ActiveProject;
657+ if LazProject<>nil then
658+ for i:=0 to LazProject.FileCount-1 do
729659 begin
730- LazProject:=LazarusIDE.ActiveProject;
731- if LazProject<>nil then
732- for i:=0 to LazProject.FileCount-1 do
733- begin
734- LazFile:=LazProject.Files[i];
735- if LazFile.IsPartOfProject
736- and FilenameIsPascalUnit(LazFile.Filename)
737- then
738- debugln(LazFile.Filename);
739- end;
660+ LazFile:=LazProject.Files[i];
661+ if LazFile.IsPartOfProject
662+ and FilenameIsPascalUnit(LazFile.Filename)
663+ then
664+ debugln(LazFile.Filename);
740665 end;
666+end;
741667```
742668
743669### All used units of project
744670
745-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.
746-```
671+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.
672+
673+``` pascal
747674uses
748- LazLoggerBase, LazIDEIntf, ProjectIntf;
749-
750- procedure ListProjectUnits;
751- var
752- LazProject: TLazProject;
753- i: Integer;
754- begin
755- LazProject:=LazarusIDE.ActiveProject;
756- if LazProject<>nil then
757- begin
758- Units:=LazarusIDE.FindUnitsOfOwner(LazProject,[fuooListed,fuooUsed]); // add fuooPackages to include units from packages
759- try
760- for i:=0 to Units.Count-1 do
761- debugln('Filename=',Units[i]);
762- finally
763- Units.Free;
764- end;
765- end;
675+ LazLoggerBase, LazIDEIntf, ProjectIntf;
676+
677+procedure ListProjectUnits;
678+var
679+ LazProject: TLazProject;
680+ i: Integer;
681+begin
682+ LazProject:=LazarusIDE.ActiveProject;
683+ if LazProject<>nil then
684+ begin
685+ Units:=LazarusIDE.FindUnitsOfOwner(LazProject,[fuooListed,fuooUsed]); // add fuooPackages to include units from packages
686+ try
687+ for i:=0 to Units.Count-1 do
688+ debugln('Filename=',Units[i]);
689+ finally
690+ Units.Free;
766691 end;
692+ end;
693+end;
767694```
768695
769696## The .lpr, .lpi and .lps file of a project
770-```
697+
698+``` pascal
771699uses
772- LCLProc, FileUtil, ProjectIntf, LazIDEIntf;
773- var
774- LazProject: TLazProject;
775- begin
776- LazProject:=LazarusIDE.ActiveProject;
777- // every project has a .lpi file:
778- DebugLn(['Project'' lpi file: ',LazProject.ProjectInfoFile]);
779-
780- // if the project session information is stored in a separate .lps file:
781- if LazProject.SessionStorage<>pssNone then
782- DebugLn(['Project'' lps file: ',LazProject.ProjectSessionFile]);
783-
784- // If the project has a .lpr file it is the main source file:
785- if (LazProject.MainFile<>nil)
786- and (CompareFileExt(LazProject.MainFile.Filename,'lpr')=0) then
787- DebugLn(['Project has lpr file: ',LazProject.MainFile.Filename]);
788- end;
700+ LCLProc, FileUtil, ProjectIntf, LazIDEIntf;
701+var
702+ LazProject: TLazProject;
703+begin
704+ LazProject:=LazarusIDE.ActiveProject;
705+ // every project has a .lpi file:
706+ DebugLn(['Project'' lpi file: ',LazProject.ProjectInfoFile]);
707+
708+ // if the project session information is stored in a separate .lps file:
709+ if LazProject.SessionStorage<>pssNone then
710+ DebugLn(['Project'' lps file: ',LazProject.ProjectSessionFile]);
711+
712+ // If the project has a .lpr file it is the main source file:
713+ if (LazProject.MainFile<>nil)
714+ and (CompareFileExt(LazProject.MainFile.Filename,'lpr')=0) then
715+ DebugLn(['Project has lpr file: ',LazProject.MainFile.Filename]);
716+end;
789717```
790718
791719## The executable / target file name of a project
792720
793-There is a macro $(TargetFile), which can be used in paths and external tools. You can query the macro in code:
794-```
721+There is a macro \$(TargetFile), which can be used in paths and external tools. You can query the macro in code:
722+
723+``` pascal
795724uses MacroIntf;
796-
797- function MyGetProjectTargetFile: string;
798- begin
799- Result:='$(TargetFile)';
800- if not IDEMacros.SubstituteMacros(Result) then
801- raise Exception.Create('unable to retrieve target file of project');
802- end;
725+
726+function MyGetProjectTargetFile: string;
727+begin
728+ Result:='$(TargetFile)';
729+ if not IDEMacros.SubstituteMacros(Result) then
730+ raise Exception.Create('unable to retrieve target file of project');
731+end;
803732```
804733
805-See here for more macros: [[IDE Macros in paths and filenames]].
734+See here for more macros: [[IDE Macros in paths and filenames]].
806735
807736## Add your own project type
808737
809-You can add items to the 'New ...' dialog:
810-
811- * See the unit ProjectIntf of the package IDEIntf.
812- * See the [[Project Templates]]
738+You can add items to the 'New ...' dialog:
813739
814-
740+- See the unit ProjectIntf of the package IDEIntf.
741+- See the [[Project Templates]]
815742
816743## Add your own file type
817744
818-You can add items to the 'New ...' dialog:
745+You can add items to the 'New ...' dialog:
819746
820- * See the unit ProjectIntf of the package IDEIntf.
821- * Choose a base class TFileDescPascalUnit for normal units or TFileDescPascalUnitWithResource for a new form/datamodule.
747+- See the unit ProjectIntf of the package IDEIntf.
748+- Choose a base class TFileDescPascalUnit for normal units or TFileDescPascalUnitWithResource for a new form/datamodule.
822749
823750### Add a new file type
824-```
751+
752+``` pascal
825753uses ProjectIntf;
826- ...
827- { TFileDescText }
828-
829- TFileDescMyText = class(TProjectFileDescriptor)
830- public
831- constructor Create; override;
832- function GetLocalizedName: string; override;
833- function GetLocalizedDescription: string; override;
834- end;
835- ...
836-
837- procedure Register;
838-
839- implementation
840-
841- procedure Register;
842- begin
843- RegisterProjectFileDescriptor(TFileDescMyText.Create,FileDescGroupName);
844- end;
845-
846- { TFileDescMyText }
847-
848- constructor TFileDescMyText.Create;
849- begin
850- inherited Create;
851- Name:='MyText'; // do not translate this
852- DefaultFilename:='text.txt';
853- AddToProject:=false;
854- end;
855-
856- function TFileDescText.GetLocalizedName: string;
857- begin
858- Result:='My Text'; // replace this with a resourcestring
859- end;
860-
861- function TFileDescText.GetLocalizedDescription: string;
862- begin
863- Result:='An empty text file';
864- end;
754+...
755+ { TFileDescText }
756+
757+ TFileDescMyText = class(TProjectFileDescriptor)
758+ public
759+ constructor Create; override;
760+ function GetLocalizedName: string; override;
761+ function GetLocalizedDescription: string; override;
762+ end;
763+...
764+
765+procedure Register;
766+
767+implementation
768+
769+procedure Register;
770+begin
771+ RegisterProjectFileDescriptor(TFileDescMyText.Create,FileDescGroupName);
772+end;
773+
774+{ TFileDescMyText }
775+
776+constructor TFileDescMyText.Create;
777+begin
778+ inherited Create;
779+ Name:='MyText'; // do not translate this
780+ DefaultFilename:='text.txt';
781+ AddToProject:=false;
782+end;
783+
784+function TFileDescText.GetLocalizedName: string;
785+begin
786+ Result:='My Text'; // replace this with a resourcestring
787+end;
788+
789+function TFileDescText.GetLocalizedDescription: string;
790+begin
791+ Result:='An empty text file';
792+end;
865793```
866794
867795### Add a new form type
868-```
796+
797+``` pascal
869798uses ProjectIntf;
870-
871- ...
872- TFileDescPascalUnitWithMyForm = class(TFileDescPascalUnitWithResource)
873- public
874- constructor Create; override;
875- function GetInterfaceUsesSection: string; override;
876- function GetLocalizedName: string; override;
877- function GetLocalizedDescription: string; override;
878- end;
879- ...
880-
881- procedure Register;
882-
883- implementation
884-
885- procedure Register;
886- begin
887- RegisterProjectFileDescriptor(TFileDescPascalUnitWithMyForm.Create,FileDescGroupName);
888- end;
889-
890- { TFileDescPascalUnitWithMyForm }
891-
892- constructor TFileDescPascalUnitWithMyForm.Create;
893- begin
894- inherited Create;
895- Name:='MyForm'; // do not translate this
896- ResourceClass:=TMyForm;
897- UseCreateFormStatements:=true;
898- end;
899-
900- function TFileDescPascalUnitWithMyForm.GetInterfaceUsesSection: string;
901- begin
902- Result:='Classes, SysUtils, MyWidgetSet';
903- end;
904-
905- function TFileDescPascalUnitWithForm.GetLocalizedName: string;
906- begin
907- Result:='MyForm'; // replace this with a resourcestring
908- end;
909-
910- function TFileDescPascalUnitWithForm.GetLocalizedDescription: string;
911- begin
912- Result:='Create a new MyForm from example package NotLCLDesigner';
913- end;
799+
800+...
801+ TFileDescPascalUnitWithMyForm = class(TFileDescPascalUnitWithResource)
802+ public
803+ constructor Create; override;
804+ function GetInterfaceUsesSection: string; override;
805+ function GetLocalizedName: string; override;
806+ function GetLocalizedDescription: string; override;
807+ end;
808+...
809+
810+procedure Register;
811+
812+implementation
813+
814+procedure Register;
815+begin
816+ RegisterProjectFileDescriptor(TFileDescPascalUnitWithMyForm.Create,FileDescGroupName);
817+end;
818+
819+{ TFileDescPascalUnitWithMyForm }
820+
821+constructor TFileDescPascalUnitWithMyForm.Create;
822+begin
823+ inherited Create;
824+ Name:='MyForm'; // do not translate this
825+ ResourceClass:=TMyForm;
826+ UseCreateFormStatements:=true;
827+end;
828+
829+function TFileDescPascalUnitWithMyForm.GetInterfaceUsesSection: string;
830+begin
831+ Result:='Classes, SysUtils, MyWidgetSet';
832+end;
833+
834+function TFileDescPascalUnitWithForm.GetLocalizedName: string;
835+begin
836+ Result:='MyForm'; // replace this with a resourcestring
837+end;
838+
839+function TFileDescPascalUnitWithForm.GetLocalizedDescription: string;
840+begin
841+ Result:='Create a new MyForm from example package NotLCLDesigner';
842+end;
914843```
915844
916845# Packages
917846
918-You can perform various actions with [[Lazarus Packages|Packages]]:
847+You can perform various actions with [[Lazarus Packages|Packages]]:
919848
920849## Search in all packages
921850
922-Iterate all packages loaded in the IDE (since 0.9.29).
923-```
851+Iterate all packages loaded in the IDE (since 0.9.29).
852+
853+``` pascal
924854uses PackageIntf;
925- ...
926- for i:=0 to PackageEditingInterface.GetPackageCount-1 do
927- writeln(PackageEditingInterface.GetPackages(i).Name);
855+...
856+for i:=0 to PackageEditingInterface.GetPackageCount-1 do
857+ writeln(PackageEditingInterface.GetPackages(i).Name);
928858```
929859
930860## Search a package with a name
931-```
861+
862+``` pascal
932863uses PackageIntf;
864+...
865+var
866+ Pkg: TIDEPackage;
867+begin
868+ Pkg:=PackageEditingInterface.FindPackageWithName('LCL');
869+ if Pkg<>nil then
933870 ...
934- var
935- Pkg: TIDEPackage;
936- begin
937- Pkg:=PackageEditingInterface.FindPackageWithName('LCL');
938- if Pkg<>nil then
939- ...
940- end;
871+end;
941872```
942873
943-Note: FindPackageWithName does not open the package editor. For that use DoOpenPackageWithName.
874+Note: FindPackageWithName does not open the package editor. For that use DoOpenPackageWithName.
944875
945876## Obtener el nombre de fichero .lpk de un paquete instalado
946-```
877+
878+``` pascal
947879uses PackageIntf;
948- ...
949- var
950- Pkg: TIDEPackage;
951- begin
952- Pkg:=PackageEditingInterface.FindPackageWithName('LCL');
953- if Pkg<>nil then
954- LPKFilename:=Pkg.Filename;
955- end;
880+...
881+var
882+ Pkg: TIDEPackage;
883+begin
884+ Pkg:=PackageEditingInterface.FindPackageWithName('LCL');
885+ if Pkg<>nil then
886+ LPKFilename:=Pkg.Filename;
887+end;
956888```
957889
958890## Instalar paquetes
959891
960-**Note** : Instalar únicamente paquetes con plugins IDE. Instalar otros paquetes puede hacer el IDE inestable.
961-```
892+**Note**: Instalar únicamente paquetes con plugins IDE. Instalar otros paquetes puede hacer el IDE inestable.
893+
894+``` pascal
962895uses PackageIntf, contnrs;
963- ...
964- PkgList:=TObjectList.create(true);
965- try
966- Pkg:=TLazPackageID.Create;
967- Pkg.Name:='Cody';
968- PkgList.Add(Pkg);
969- // Chequear si el IDE puede encontrar cody.lpk y todas las dependencias.
970- // El IDE will muestra algunas advertencias/confirmaciones si algo parece extraño.
971- if not PackageEditingInterface.CheckInstallPackageList(PkgList,[]) then
972- exit;
973- // in this example we have checked already, so skip the warnings
974- // and rebuild the IDE
975- if PackageEditingInterface.InstallPackages(PkgList,[piiifSkipChecks,piiifRebuildIDE])<>mrOK then
976- exit;
977- finally
978- PkgList.Free;
979- end;
896+...
897+ PkgList:=TObjectList.create(true);
898+ try
899+ Pkg:=TLazPackageID.Create;
900+ Pkg.Name:='Cody';
901+ PkgList.Add(Pkg);
902+ // Chequear si el IDE puede encontrar cody.lpk y todas las dependencias.
903+ // El IDE will muestra algunas advertencias/confirmaciones si algo parece extraño.
904+ if not PackageEditingInterface.CheckInstallPackageList(PkgList,[]) then
905+ exit;
906+ // in this example we have checked already, so skip the warnings
907+ // and rebuild the IDE
908+ if PackageEditingInterface.InstallPackages(PkgList,[piiifSkipChecks,piiifRebuildIDE])<>mrOK then
909+ exit;
910+ finally
911+ PkgList.Free;
912+ end;
980913```
981914
982915## Abrir un fichero de paquete (lpk)
983-```
916+
917+``` pascal
984918uses PackageIntf, FileUtil;
985- ...
986- var
987- pkg: TIDEPackage;
988- begin
989- if PackageEditingInterface.DoOpenPackageFile(LPKFilename,[pofAddToRecent],false)<>mrOk then
990- exit;
991- Pkg:=PackageEditingInterface.FindPackageWithName(ExtractFilenameOnly(LPKFilename));
992- ...
993- end;
919+...
920+var
921+ pkg: TIDEPackage;
922+begin
923+ if PackageEditingInterface.DoOpenPackageFile(LPKFilename,[pofAddToRecent],false)<>mrOk then
924+ exit;
925+ Pkg:=PackageEditingInterface.FindPackageWithName(ExtractFilenameOnly(LPKFilename));
926+ ...
927+end;
994928```
995929
996-[![Warning-icon.png](/wiki-images/b/b2/Warning-icon.png)](</File:Warning-icon.png>)
997-
998-**Warning:** El IDE cierra automáticamentes paquetes no utilizados on idle. Nunca mantener referencias a un TIDEPackage.
930+> **Warning:** El IDE cierra automáticamentes paquetes no utilizados on idle. Nunca mantener referencias a un TIDEPackage.
999931
1000932## Find the package(s) of a unit
1001933
1002-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:
1003-```
934+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:
935+
936+``` pascal
1004937uses Classes, PackageIntf, ProjectIntf;
1005- ...
1006- var
1007- Owners: TFPList;
1008- i: Integer;
1009- o: TObject;
1010- begin
1011- Owners:=PackageEditingInterface.GetPossibleOwnersOfUnit('/full/path/of/unit.pas',[]);
1012- if Owners=nil then begin
1013- // unit is not directly associated with a project/package
1014- // maybe the unit was for some reason not added, but is reachable
1015- // search in all unit paths of all projects/packages
1016- // Beware: This can lead to false hits
1017- Owners:=PackageEditingInterface.GetPossibleOwnersOfUnit('/full/path/of/unit.pas',
1018- [piosfExcludeOwned,piosfIncludeSourceDirectories]);
1019- end;
1020- if Owners=nil then exit;
1021- try
1022- for i:=0 to Owners.Count-1 do begin
1023- o:=TObject(Owners[i]);
1024- if o is TIDEPackage then begin
1025- writeln('Owner is package ',TIDEPackage(o).Name);
1026- end else if o is TLazProject then begin
1027- writeln('Owner is project ',TLazProject(o).ProjectInfoFile);
1028- end;
1029- end;
1030- finally
1031- Owners.Free;
938+...
939+var
940+ Owners: TFPList;
941+ i: Integer;
942+ o: TObject;
943+begin
944+ Owners:=PackageEditingInterface.GetPossibleOwnersOfUnit('/full/path/of/unit.pas',[]);
945+ if Owners=nil then begin
946+ // unit is not directly associated with a project/package
947+ // maybe the unit was for some reason not added, but is reachable
948+ // search in all unit paths of all projects/packages
949+ // Beware: This can lead to false hits
950+ Owners:=PackageEditingInterface.GetPossibleOwnersOfUnit('/full/path/of/unit.pas',
951+ [piosfExcludeOwned,piosfIncludeSourceDirectories]);
952+ end;
953+ if Owners=nil then exit;
954+ try
955+ for i:=0 to Owners.Count-1 do begin
956+ o:=TObject(Owners[i]);
957+ if o is TIDEPackage then begin
958+ writeln('Owner is package ',TIDEPackage(o).Name);
959+ end else if o is TLazProject then begin
960+ writeln('Owner is project ',TLazProject(o).ProjectInfoFile);
1032961 end;
962+ end;
963+ finally
964+ Owners.Free;
965+ end;
1033966```
1034967
1035968# Ventanas
1036969
1037-Hay básicamente cuatro tipos de ventandas del IDE:
970+Hay básicamente cuatro tipos de ventandas del IDE:
1038971
1039- * La barra principal del IDE bar que es Application.MainForm. Siempre está presente.
1040- * Ventanas flotantes/acoplables tales como el editor del código fuente, the Object Inspectors and Messages.
1041- * Fomas modales, tales como los diálogos de búsqueda, opciones y preguntas.
1042- * Sugerencias y formas de completado.
972+- La barra principal del IDE bar que es Application.MainForm. Siempre está presente.
973+- Ventanas flotantes/acoplables tales como el editor del código fuente, the Object Inspectors and Messages.
974+- Fomas modales, tales como los diálogos de búsqueda, opciones y preguntas.
975+- Sugerencias y formas de completado.
1043976
1044977## Añadiendo una nueva ventana de acoplado en el IDE
1045978
1046-Que es una ventana del IDE acoplable: Ventanas tales como el Editor de Código Fuente o el Inspector de Objetos son ventanas flotantes que se pueden acoplar si se instala un paquete de acoplamiento, y su estado, posición y tamaño se almacena y restaura en el siguiente inicio del IDE. En orden a restaurar una ventana el IDE necesita un creador como se definie en la unidad **IDEWindowIntf** del paquete IDEIntf. Cada ventana acoplable debe tener un único nombre. No usar por tanto nombres genéricos como 'FileBrowser' porque haría que entre fácilmente en conflicto con otros paquetes. Y no usar nombres cortos como 'XYZ' porque el creador es responsable de todas las formas que comiencen con dicho nombre.
979+Que es una ventana del IDE acoplable: Ventanas tales como el Editor de Código Fuente o el Inspector de Objetos son ventanas flotantes que se pueden acoplar si se instala un paquete de acoplamiento, y su estado, posición y tamaño se almacena y restaura en el siguiente inicio del IDE. En orden a restaurar una ventana el IDE necesita un creador como se definie en la unidad **IDEWindowIntf** del paquete IDEIntf. Cada ventana acoplable debe tener un único nombre. No usar por tanto nombres genéricos como 'FileBrowser' porque haría que entre fácilmente en conflicto con otros paquetes. Y no usar nombres cortos como 'XYZ' porque el creador es responsable de todas las formas que comiencen con dicho nombre.
1047980
1048981### Como registrar una ventana acoplable del IDE
1049982
1050-Recuerda utilizar un nombre largo única que sea un identificador pascal válido. Tus ventanas pueden tener cualquier caption que necesites.
1051-```
983+Recuerda utilizar un nombre largo única que sea un identificador pascal válido. Tus ventanas pueden tener cualquier caption que necesites.
984+
985+``` pascal
1052986uses SysUtils, IDEWindowIntf;
1053- ...
1054- var MyIDEWindow: TMyIDEWindow = nil;
1055-
1056- procedure CreateMyIDEWindow(Sender: TObject; aFormName: string; var AForm: TCustomForm; DoDisableAutoSizing: boolean);
1057- begin
1058- // check the name
1059- if CompareText(aFormName,MyIDEWindowName)<>0 then exit;
1060- // create the form if not already done and disable autosizing
1061- IDEWindowCreators.CreateForm(MyIDEWindow,TMyIDEWindowm,DoDisableAutosizing,Application);
1062- ... init the window ...
1063- AForm:=MyIDEWindow;
1064- end;
1065-
1066- procedure Register;
1067- begin
1068- IDEWindowCreators.Add('MyIDEWindow',@CreateMyIDEWindow,nil,'100','50%','+300','+20%');
1069- // the default boundsrect of the form is:
1070- // Left=100, Top=50% of Screen.Height, Width=300, Height=20% of Screen.Height
1071- // when the IDE needs an instance of this window it calls the procedure CreateMyIDEWindow.
1072- end;
987+...
988+var MyIDEWindow: TMyIDEWindow = nil;
989+
990+procedure CreateMyIDEWindow(Sender: TObject; aFormName: string; var AForm: TCustomForm; DoDisableAutoSizing: boolean);
991+begin
992+ // check the name
993+ if CompareText(aFormName,MyIDEWindowName)<>0 then exit;
994+ // create the form if not already done and disable autosizing
995+ IDEWindowCreators.CreateForm(MyIDEWindow,TMyIDEWindowm,DoDisableAutosizing,Application);
996+ ... init the window ...
997+ AForm:=MyIDEWindow;
998+end;
999+
1000+procedure Register;
1001+begin
1002+ IDEWindowCreators.Add('MyIDEWindow',@CreateMyIDEWindow,nil,'100','50%','+300','+20%');
1003+ // the default boundsrect of the form is:
1004+ // Left=100, Top=50% of Screen.Height, Width=300, Height=20% of Screen.Height
1005+ // when the IDE needs an instance of this window it calls the procedure CreateMyIDEWindow.
1006+end;
10731007```
10741008
10751009### Showing an IDE window
10761010
1077-**Do not use Show**. Use:
1078-```
1011+**Do not use Show**. Use:
1012+
1013+``` pascal
10791014IDEWindowCreators.ShowForm(MyIDEWindow,false);
10801015```
10811016
1082-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.
1017+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.
10831018
10841019### Notes about IDEWindowCreators and SimpleLayoutStorage
10851020
1086-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.
1021+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.
10871022
1088-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.
1023+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.
10891024
10901025# CodeTools
10911026
1092-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]].
1027+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]].
10931028
1094-Before calling any of the CodeTools functions in the IDE you should commit the current changes of the source editor to the CodeTools buffers:
1095-```
1029+Before calling any of the CodeTools functions in the IDE you should commit the current changes of the source editor to the CodeTools buffers:
1030+
1031+``` pascal
10961032uses LazIDEIntf;
1097- ...
1098- // save changes in source editor to codetools
1099- LazarusIDE.SaveSourceEditorChangesToCodeCache(-1); // -1: commit all source editors
1033+...
1034+ // save changes in source editor to codetools
1035+ LazarusIDE.SaveSourceEditorChangesToCodeCache(-1); // -1: commit all source editors
11001036```
11011037
11021038## Adding a resource directive to a file
11031039
1104-This adds a {$R example.res} to a pascal unit:
1105-```
1040+This adds a {\$R example.res} to a pascal unit:
1041+
1042+``` pascal
11061043procedure AddResourceDirectiveToPascalSource(const Filename: string);
1107- var
1108- ExpandedFilename: String;
1109- CodeBuf: TCodeBuffer;
1110- begin
1111- // make sure the filename is trimmed and contains a full path
1112- ExpandedFilename:=CleanAndExpandFilename(Filename);
1113-
1114- // save changes in source editor to codetools
1115- LazarusIDE.SaveSourceEditorChangesToCodeCache(-1);
1116-
1117- // load the file
1118- CodeBuf:=CodeToolBoss.LoadFile(ExpandedFilename,true,false);
1119-
1120- // add the resource directive
1121- if not CodeToolBoss.AddResourceDirective(CodeBuf,'example.res') then
1122- LazarusIDE.DoJumpToCodeToolBossError;
1123- end;
1044+var
1045+ ExpandedFilename: String;
1046+ CodeBuf: TCodeBuffer;
1047+begin
1048+ // make sure the filename is trimmed and contains a full path
1049+ ExpandedFilename:=CleanAndExpandFilename(Filename);
1050+
1051+ // save changes in source editor to codetools
1052+ LazarusIDE.SaveSourceEditorChangesToCodeCache(-1);
1053+
1054+ // load the file
1055+ CodeBuf:=CodeToolBoss.LoadFile(ExpandedFilename,true,false);
1056+
1057+ // add the resource directive
1058+ if not CodeToolBoss.AddResourceDirective(CodeBuf,'example.res') then
1059+ LazarusIDE.DoJumpToCodeToolBossError;
1060+end;
11241061```
11251062
1126-The codetools provides also functions like FindResourceDirective and RemoveDirective.
1063+The codetools provides also functions like FindResourceDirective and RemoveDirective.
11271064
11281065## Getting the search paths for units and include files
11291066
1130-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:
1131-```
1067+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:
1068+
1069+``` pascal
11321070uses CodeToolManager;
1133- ...
1134-
1135- Dir:=''; // the empty directory is for new files and has the same settings as the project directory
1136-
1137- // Getting the search paths for include files:
1138- Path:=CodeToolBoss.GetIncludePathForDirectory(Dir);
1139-
1140- // Getting the search paths for units:
1141- // This search path is passed to the compiler.
1142- // It contains the package output directories, but not the package source directories.
1143- Path:=CodeToolBoss.GetUnitPathForDirectory(Dir);
1144-
1145- // There can be additional unit search paths for the IDE only (not passed to the compiler)
1146- Path:=CodeToolBoss.GetSrcPathForDirectory(Dir);
1147-
1148- // The complete search path contains also all package source paths for units:
1149- Path:=CodeToolBoss.GetCompleteSrcPathForDirectory(Dir);
1071+...
1072+
1073+Dir:=''; // the empty directory is for new files and has the same settings as the project directory
1074+
1075+// Getting the search paths for include files:
1076+Path:=CodeToolBoss.GetIncludePathForDirectory(Dir);
1077+
1078+// Getting the search paths for units:
1079+// This search path is passed to the compiler.
1080+// It contains the package output directories, but not the package source directories.
1081+Path:=CodeToolBoss.GetUnitPathForDirectory(Dir);
1082+
1083+// There can be additional unit search paths for the IDE only (not passed to the compiler)
1084+Path:=CodeToolBoss.GetSrcPathForDirectory(Dir);
1085+
1086+// The complete search path contains also all package source paths for units:
1087+Path:=CodeToolBoss.GetCompleteSrcPathForDirectory(Dir);
11501088```
11511089
11521090# Source Editor
11531091
11541092## Active source editor
1155-```
1093+
1094+``` pascal
11561095uses SrcEditorIntf;
1157- ...
1158- Editor:=SourceEditorManagerIntf.ActiveEditor;
1159- if Editor=nil then exit;
1160- Filename:=Editor.FileName;
1161- ScreenPos:=Editor.CursorScreenXY;
1162- TextPos:=Editor.CursorTextXY;
1096+...
1097+Editor:=SourceEditorManagerIntf.ActiveEditor;
1098+if Editor=nil then exit;
1099+Filename:=Editor.FileName;
1100+ScreenPos:=Editor.CursorScreenXY;
1101+TextPos:=Editor.CursorTextXY;
11631102```
11641103
11651104# SynEdit
11661105
11671106## Getting the settings for a TSynEdit
11681107
1169-When you have a dialog using a TSynEdit and you want to use the same font and settings like the source editor use:
1170-```
1108+When you have a dialog using a TSynEdit and you want to use the same font and settings like the source editor use:
1109+
1110+``` pascal
11711111uses SrcEditorIntf;
1172- ...
1173- SourceEditorManagerIntf.GetEditorControlSettings(ASynEdit);
1112+...
1113+SourceEditorManagerIntf.GetEditorControlSettings(ASynEdit);
11741114```
11751115
11761116## Getting the settings for a SynEdit highlighter
11771117
1178-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:
1179-```
1118+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:
1119+
1120+``` pascal
11801121uses SrcEditorIntf;
1181- ...
1182- SourceEditorManagerIntf.GetHighlighterSettings(ASynHighlighter);
1122+...
1123+SourceEditorManagerIntf.GetHighlighterSettings(ASynHighlighter);
11831124```
11841125
1185-See for an example: TSQLStringsPropertyEditorDlg.Create in the unit [SQLStringsPropertyEditorDlg](<http://svn.freepascal.org/svn/lazarus/trunk/components/sqldb/sqlstringspropertyeditordlg.pas>).
1126+See for an example: TSQLStringsPropertyEditorDlg.Create in the unit [SQLStringsPropertyEditorDlg](http://svn.freepascal.org/svn/lazarus/trunk/components/sqldb/sqlstringspropertyeditordlg.pas).
11861127
11871128# Help
11881129
11891130## Adding help for sources
11901131
1191-First create a THelpDatabase:
1192-```
1132+First create a THelpDatabase:
1133+
1134+``` pascal
11931135HelpDB := TFPDocHTMLHelpDatabase(
1194- HelpDatabases.CreateHelpDatabase('ANameOfYourChoiceForTheDatabase',
1195- TFPDocHTMLHelpDatabase,true));
1196- HelpDB.DefaultBaseURL := 'http://your.help.org/';
1197-
1198- FPDocNode := THelpNode.CreateURL(HelpDB,
1199- 'Package1 - A new package',
1200- 'file://index.html');
1201- HelpDB.TOCNode := THelpNode.Create(HelpDB,FPDocNode);// once as TOC
1202- DirectoryItem := THelpDBISourceDirectory.Create(FPDocNode,'$(PkgDir)/lcl',
1203- '*.pp;*.pas',false);// and once as normal page
1204- HelpDB.RegisterItem(DirectoryItem);
1136+ HelpDatabases.CreateHelpDatabase('ANameOfYourChoiceForTheDatabase',
1137+ TFPDocHTMLHelpDatabase,true));
1138+HelpDB.DefaultBaseURL := 'http://your.help.org/';
1139+
1140+FPDocNode := THelpNode.CreateURL(HelpDB,
1141+ 'Package1 - A new package',
1142+ 'file://index.html');
1143+HelpDB.TOCNode := THelpNode.Create(HelpDB,FPDocNode);// once as TOC
1144+DirectoryItem := THelpDBISourceDirectory.Create(FPDocNode,'$(PkgDir)/lcl',
1145+ '*.pp;*.pas',false);// and once as normal page
1146+HelpDB.RegisterItem(DirectoryItem);
12051147```
12061148
12071149## Adding lines to the messages window
1208-```
1150+
1151+``` pascal
12091152unit IDEMsgIntf;
1210- ...
1211- var Dir: String;
1212- begin
1213- Dir:=GetCurrentDir;
1214- IDEMessagesWindow.BeginBlock;
1215- IDEMessagesWindow.AddMsg('unit1.pas(30,4) Error: Identifier not found "a"',Dir,0);
1216- IDEMessagesWindow.EndBlock;
1217- end;
1153+...
1154+var Dir: String;
1155+begin
1156+ Dir:=GetCurrentDir;
1157+ IDEMessagesWindow.BeginBlock;
1158+ IDEMessagesWindow.AddMsg('unit1.pas(30,4) Error: Identifier not found "a"',Dir,0);
1159+ IDEMessagesWindow.EndBlock;
1160+end;
12181161```
12191162
12201163# Miscellaneous
12211164
12221165## Adding a macro
12231166
1224-You can add your own IDE macros:
1225-```
1167+You can add your own IDE macros:
1168+
1169+``` pascal
12261170uses MacroIntf, MacroDefIntf;
1227-
1228- procedure Register;
1229- begin
1230- // registering a Macro with a fixed value:
1231- IDEMacros.Add(TTransferMacro.Create('MyMacro1','Value','Description',nil,[]));
1232- end;
1233-
1234- // registering a Macro with a dynamic value requires a method of an object
1235- procedure TForm1.Init;
1236- begin
1237- IDEMacros.Add(TTransferMacro.Create('MyMacro','Value','Description',@OnResolveMyMacro,[]));
1238- end;
1239-
1240- function TForm1.OnResolveMyMacro(const s: string; const Data: PtrInt;
1241- var Abort: boolean): string;
1242- // s is the parameter. For example $MyMacro(parameter)
1243- // Data is the querying instance. Can be nil.
1244- // If there is an error set Abort to true.
1245- // The result string can contain macros. For example Result:='$(FPCVer)/$(TargetOS)';
1246- begin
1247- Result:='MyValue';
1248- end;
1171+
1172+procedure Register;
1173+begin
1174+ // registering a Macro with a fixed value:
1175+ IDEMacros.Add(TTransferMacro.Create('MyMacro1','Value','Description',nil,[]));
1176+end;
1177+
1178+// registering a Macro with a dynamic value requires a method of an object
1179+procedure TForm1.Init;
1180+begin
1181+ IDEMacros.Add(TTransferMacro.Create('MyMacro','Value','Description',@OnResolveMyMacro,[]));
1182+end;
1183+
1184+function TForm1.OnResolveMyMacro(const s: string; const Data: PtrInt;
1185+ var Abort: boolean): string;
1186+// s is the parameter. For example $MyMacro(parameter)
1187+// Data is the querying instance. Can be nil.
1188+// If there is an error set Abort to true.
1189+// The result string can contain macros. For example Result:='$(FPCVer)/$(TargetOS)';
1190+begin
1191+ Result:='MyValue';
1192+end;
12491193```
12501194
12511195## Depurando el IDE
12521196
1253- * Compila el IDE como habitualmente e.g. via Tools/Build de Lazarus. Asegúrate de establecer los flags de depuración tales como -g o -gw2. Lo mejor es utilizar el profile "Debug IDE" que contiene algunos flags útiles para depuración.
1254- * Abre el proyecto ide\lazarus.lpi. No se puede compilar directamente este proyecto.
1255- * Establece los puntos de ruptura (breakpoints) acorde a tus necesidades etc y ejecuta el proyecto.
1197+- Compila el IDE como habitualmente e.g. via Tools/Build de Lazarus. Asegúrate de establecer los flags de depuración tales como -g o -gw2. Lo mejor es utilizar el profile "Debug IDE" que contiene algunos flags útiles para depuración.
1198+- Abre el proyecto ide\lazarus.lpi. No se puede compilar directamente este proyecto.
1199+- Establece los puntos de ruptura (breakpoints) acorde a tus necesidades etc y ejecuta el proyecto.
12561200
12571201## See also
12581202
1259- * [[Creating IDE Help]]
1260- * [[Help protocol]]
1261- * [[How To Write Lazarus Component]]
1203+- [[Creating IDE Help]]
1204+- [[Help protocol]]
1205+- [[How To Write Lazarus Component]]
12621206
12631207---
1264-*From the Free Pascal wiki, page "Extending the IDE/es", as the Internet Archive kept it on 2024-07-25. 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).*
1208+*From the Free Pascal wiki, page "Extending the IDE/es", as it stood on 2020-02-14 after a change by Trev, 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).*

Back to history