1 {
2 ***************************************************************************
3 * *
4 * This source is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This code is distributed in the hope that it will be useful, but *
10 * WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * General Public License for more details. *
13 * *
14 * A copy of the GNU General Public License is available on the World *
15 * Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
16 * obtain it by writing to the Free Software Foundation, *
17 * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
18 * *
19 ***************************************************************************
20
21 Author: Marius
22 Modified by Juha Manninen, Balazs Szekely
23
24 Abstract:
25 A dialog to quickly find components and to add the found component
26 to the designed form.
27 }
28 unit ComponentList;
29
30 {$mode objfpc}{$H+}
31
32 interface
33
34 uses
35 Classes, SysUtils,
36 // LCL
37 LCLType, Forms, Controls, Graphics, StdCtrls, ExtCtrls, ComCtrls, Menus, Buttons,
38 Dialogs, ImgList,
39 // LazUtils
40 LazLoggerBase, LazUTF8,
41 // LazControls
42 TreeFilterEdit,
43 // IdeIntf
44 FormEditingIntf, IDEImagesIntf, PropEdits, ComponentReg,
45 // IDE
46 LazarusIDEStrConsts, PackageDefs, IDEOptionDefs, EnvironmentOpts, Designer;
47
48 type
49
50 { TComponentListForm }
51
52 TComponentListForm = class(TForm)
53 chbKeepOpen: TCheckBox;
54 ButtonPanel: TPanel;
55 miCollapse: TMenuItem;
56 miCollapseAll: TMenuItem;
57 miExpand: TMenuItem;
58 miExpandAll: TMenuItem;
59 OKButton: TButton;
60 LabelSearch: TLabel;
61 PageControl: TPageControl;
62 FilterPanel: TPanel;
63 ListTree: TTreeView;
64 PalletteTree: TTreeView;
65 InheritanceTree: TTreeView;
66 pnPaletteTree: TPanel;
67 Panel6: TPanel;
68 Panel7: TPanel;
69 pmCollapseExpand: TPopupMenu;
70 TabSheetPaletteTree: TTabSheet;
71 TabSheetInheritance: TTabSheet;
72 TabSheetList: TTabSheet;
73 tmDeselect: TTimer;
74 TreeFilterEd: TTreeFilterEdit;
75 SelectionToolButton: TSpeedButton;
76 procedure chbKeepOpenChange(Sender: TObject);
77 procedure FormActivate(Sender: TObject);
78 procedure FormShow(Sender: TObject);
79 procedure ListTreeSelectionChanged(Sender: TObject);
80 procedure miCollapseAllClick(Sender: TObject);
81 procedure miCollapseClick(Sender: TObject);
82 procedure miExpandAllClick(Sender: TObject);
83 procedure miExpandClick(Sender: TObject);
84 procedure OKButtonClick(Sender: TObject);
85 procedure ComponentsDblClick(Sender: TObject);
86 procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
87 procedure pmCollapseExpandPopup(Sender: TObject);
88 procedure tmDeselectTimer(Sender: TObject);
89 procedure TreeFilterEdAfterFilter(Sender: TObject);
90 procedure PageControlChange(Sender: TObject);
91 procedure TreeKeyPress(Sender: TObject; var Key: char);
92 procedure FormKeyDown(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState);
93 procedure SelectionToolButtonClick(Sender: TObject);
94 private
95 PrevChangeStamp: Integer;
96 // List for Component inheritence view
97 FClassList: TStringListUTF8Fast;
98 FInitialized: Boolean;
99 FIgnoreSelection: Boolean;
100 FPageControlChange: Boolean;
101 FActiveTree: TTreeView;
102 FAddCompNewLeft, FAddCompNewTop: Integer;
103 FAddCompNewParent: TComponent;
104 procedure ClearSelection;
105 procedure SelectionWasChanged;
106 procedure ComponentWasAdded({%H-}ALookupRoot, {%H-}AComponent: TComponent;
107 {%H-}ARegisteredComponent: TRegisteredComponent);
108 procedure DoComponentInheritence(Comp: TRegisteredComponent);
109 procedure UpdateComponents;
110 procedure UpdateButtonState;
IsDockednull111 function IsDocked: Boolean;
112 procedure AddSelectedComponent;
113 protected
114 procedure UpdateShowing; override;
115 public
116 constructor Create(AOwner: TComponent); override;
117 destructor Destroy; override;
GetSelectedComponentnull118 function GetSelectedComponent: TRegisteredComponent;
119 end;
120
121 var
122 ComponentListForm: TComponentListForm;
123
124 implementation
125
126 {$R *.lfm}
127
128 { TComponentListForm }
129
130 constructor TComponentListForm.Create(AOwner: TComponent);
131 begin
132 inherited Create(AOwner);
133
134 Name:=NonModalIDEWindowNames[nmiwComponentList];
135 FActiveTree := ListTree;
136
137 IDEImages.AssignImage(SelectionToolButton, 'tmouse');
138 with SelectionToolButton do begin
139 ShowHint := EnvironmentOptions.ShowHintsForComponentPalette;
140 Width := ComponentPaletteBtnWidth;
141 BorderSpacing.Around := (FilterPanel.Height - ComponentPaletteImageHeight) div 2;
142 end;
143
144 //Translations
145 LabelSearch.Caption := lisMenuFind;
146 Caption := lisCmpLstComponents;
147 TabSheetList.Caption := lisCmpLstList;
148 TabSheetPaletteTree.Caption := lisCmpLstPalette;
149 TabSheetInheritance.Caption := lisCmpLstInheritance;
150 OKButton.Caption := lisUse;
151 chbKeepOpen.Caption := lisKeepOpen;
152 SelectionToolButton.Hint := lisSelectionTool;
153
154 ListTree.Images := TPkgComponent.Images;
155 PalletteTree.Images := TPkgComponent.Images;
156 InheritanceTree.Images := TPkgComponent.Images;
157 if Assigned(IDEComponentPalette) then
158 begin
159 UpdateComponents;
160 TreeFilterEd.InvalidateFilter;
161 IDEComponentPalette.AddHandlerSelectionChanged(@SelectionWasChanged);
162 IDEComponentPalette.AddHandlerComponentAdded(@ComponentWasAdded);
163 end;
164 chbKeepOpen.Checked := EnvironmentOptions.ComponentListKeepOpen;
165 PageControl.PageIndex := EnvironmentOptions.ComponentListPageIndex;
166 PageControlChange(Nil);
167 end;
168
169 procedure TComponentListForm.AddSelectedComponent;
170 var
171 AComponent: TRegisteredComponent;
172 ASelections: TPersistentSelectionList;
173 NewParent: TComponent;
174 CurDesigner: TDesigner;
175 begin
176 AComponent := GetSelectedComponent;
177 ASelections := TPersistentSelectionList.Create;
178 try
179 GlobalDesignHook.GetSelection(ASelections);
180 if (ASelections.Count>0) and (ASelections[0] is TComponent) then
181 NewParent := TComponent(ASelections[0])
182 else if GlobalDesignHook.LookupRoot is TComponent then
183 NewParent := TComponent(GlobalDesignHook.LookupRoot)
184 else
185 NewParent := nil;
186 finally
187 ASelections.Free;
188 end;
189
190 if NewParent=nil then
191 Exit;
192
193 CurDesigner:=TDesigner(FindRootDesigner(NewParent));
194 if CurDesigner=nil then
195 Exit;
196
197 CurDesigner.AddComponentCheckParent(NewParent, NewParent, nil, AComponent.ComponentClass);
198 if NewParent=nil then
199 Exit;
200
201 if FAddCompNewParent<>NewParent then
202 begin
203 FAddCompNewLeft := 0;
204 FAddCompNewTop := 0;
205 FAddCompNewParent := NewParent;
206 end;
207 Inc(FAddCompNewLeft, 8);
208 Inc(FAddCompNewTop, 8);
209 CurDesigner.AddComponent(AComponent, AComponent.ComponentClass, NewParent, FAddCompNewLeft, FAddCompNewTop, 0, 0);
210 end;
211
212 procedure TComponentListForm.chbKeepOpenChange(Sender: TObject);
213 begin
214 EnvironmentOptions.ComponentListKeepOpen := chbKeepOpen.Checked;
215 end;
216
217 destructor TComponentListForm.Destroy;
218 begin
219 if Assigned(IDEComponentPalette) then
220 IDEComponentPalette.RemoveHandlerComponentAdded(@ComponentWasAdded);
221 ComponentListForm := nil;
222 inherited Destroy;
223 end;
224
225 procedure TComponentListForm.FormShow(Sender: TObject);
226 begin
227 //DebugLn(['*** TComponentListForm.FormShow, Parent=', Parent, ', Parent.Parent=', ParentParent]);
228 ButtonPanel.Visible := not IsDocked;
229 if ButtonPanel.Visible then
230 begin // ComponentList is undocked
231 PageControl.AnchorSideBottom.Side := asrTop;
232 UpdateButtonState;
233 if TreeFilterEd.CanFocus then // Focus filter if window is undocked
234 TreeFilterEd.SetFocus;
235 TreeFilterEd.SelectAll;
236 end
237 else // ComponentList is docked
238 PageControl.AnchorSideBottom.Side := asrBottom;
239 end;
240
241 procedure TComponentListForm.FormActivate(Sender: TObject);
242 begin
243 if Assigned(IDEComponentPalette) and (IDEComponentPalette.ChangeStamp<>PrevChangeStamp) then
244 UpdateComponents;
245 end;
246
247 procedure TComponentListForm.ClearSelection;
248 begin
249 ListTree.Selected := Nil;
250 PalletteTree.Selected := Nil;
251 InheritanceTree.Selected := Nil;
252 end;
253
254 procedure SelectTreeComp(aTree: TTreeView);
255 var
256 Node: TTreeNode;
257 begin
258 with IDEComponentPalette do
259 if Assigned(Selected) then
260 Node := aTree.Items.FindNodeWithText(Selected.ComponentClass.ClassName)
261 else
262 Node := Nil;
263 aTree.Selected := Node;
264 if aTree.Selected <> nil then
265 aTree.Selected.MakeVisible;
266 end;
267
268 procedure TComponentListForm.SelectionWasChanged;
269 begin
270 SelectionToolButton.Down := (IDEComponentPalette.Selected = nil);
271
272 // ToDo: Select the component in active treeview.
273 if FIgnoreSelection then
274 Exit;
275
276 if ListTree.IsVisible then
277 SelectTreeComp(ListTree)
278 else if PalletteTree.IsVisible then
279 SelectTreeComp(PalletteTree)
280 else if InheritanceTree.IsVisible then
281 SelectTreeComp(InheritanceTree)
282 end;
283
GetSelectedTreeCompnull284 function GetSelectedTreeComp(aTree: TTreeView): TRegisteredComponent;
285 begin
286 if Assigned(aTree.Selected) then
287 Result := TRegisteredComponent(aTree.Selected.Data)
288 else
289 Result := nil;
290 end;
291
GetSelectedComponentnull292 function TComponentListForm.GetSelectedComponent: TRegisteredComponent;
293 begin
294 Result := nil;
295 if ListTree.IsVisible then
296 Result := GetSelectedTreeComp(ListTree)
297 else if PalletteTree.IsVisible then
298 Result := GetSelectedTreeComp(PalletteTree)
299 else if InheritanceTree.IsVisible then
300 Result := GetSelectedTreeComp(InheritanceTree)
301 end;
302
IsDockednull303 function TComponentListForm.IsDocked: Boolean;
304 begin
305 Result := (HostDockSite<>Nil) and (HostDockSite.Parent<>Nil);
306 end;
307
308 procedure TComponentListForm.ComponentWasAdded(ALookupRoot, AComponent: TComponent;
309 ARegisteredComponent: TRegisteredComponent);
310 begin
311 ClearSelection;
312 UpdateButtonState;
313 end;
314
315 procedure TComponentListForm.UpdateButtonState;
316 begin
317 OKButton.Enabled := Assigned(GetSelectedComponent);
318 end;
319
320 procedure TComponentListForm.UpdateShowing;
321 begin
322 if (ButtonPanel<>nil) and ButtonPanel.Visible then
323 UpdateButtonState;
324 inherited UpdateShowing;
325 end;
326
327 procedure TComponentListForm.DoComponentInheritence(Comp: TRegisteredComponent);
328 // Walk down to parent, stop on TComponent,
329 // since components are at least TComponent descendants.
330 var
331 PalList: TStringList;
332 AClass: TClass;
333 Node: TTreeNode;
334 ClssName: string;
335 i, Ind: Integer;
336 II: TImageIndex;
337 begin
338 PalList := TStringList.Create;
339 try
340 AClass := Comp.ComponentClass;
341 while (AClass.ClassInfo <> nil) and (AClass.ClassType <> TComponent.ClassType) do
342 begin
343 PalList.AddObject(AClass.ClassName, TObject(AClass));
344 AClass := AClass.ClassParent;
345 end;
346 // Build the tree
347 for i := PalList.Count - 1 downto 0 do
348 begin
349 AClass := TClass(PalList.Objects[i]);
350 ClssName := PalList[i];
351 if not FClassList.Find(ClssName, Ind) then
352 begin
353 // Find out parent position
354 if Assigned(AClass.ClassParent)
355 and FClassList.Find(AClass.ClassParent.ClassName, Ind) then
356 Node := TTreeNode(FClassList.Objects[Ind])
357 else
358 Node := nil;
359 // Add the item
360 if ClssName <> Comp.ComponentClass.ClassName then
361 Node := InheritanceTree.Items.AddChild(Node, ClssName)
362 else
363 begin
364 Node := InheritanceTree.Items.AddChildObject(Node, ClssName, Comp);
365 if Comp is TPkgComponent then
366 II := TPkgComponent(Comp).ImageIndex
367 else
368 II := -1;
369 if II>=0 then
370 begin
371 Node.ImageIndex := II;
372 Node.SelectedIndex := Node.ImageIndex;
373 end;
374 end;
375 FClassList.AddObject(ClssName, Node);
376 end;
377 end;
378 finally
379 PalList.Free;
380 end;
381 end;
382
383 procedure TComponentListForm.UpdateComponents;
384 // Fill all three tabsheets: Flat list, Palette layout and Component inheritence.
385 var
386 Pg: TBaseComponentPage;
387 Comps: TRegisteredCompList;
388 Comp: TRegisteredComponent;
389 ParentNode: TTreeNode;
390 AListNode: TTreeNode;
391 APaletteNode: TTreeNode;
392 i, j: Integer;
393 CurIcon: TImageIndex;
394 begin
395 if [csDestroying,csLoading]*ComponentState<>[] then exit;
396 Screen.BeginWaitCursor;
397 ListTree.BeginUpdate;
398 PalletteTree.BeginUpdate;
399 InheritanceTree.Items.BeginUpdate;
400 FClassList := TStringListUTF8Fast.Create;
401 try
402 ListTree.Items.Clear;
403 PalletteTree.Items.Clear;
404 InheritanceTree.Items.Clear;
405 FClassList.Sorted := true;
406 FClassList.Duplicates := dupIgnore;
407 // ParentInheritence := InheritanceTree.Items.Add(nil, 'TComponent');
408 // FClassList.AddObject('TComponent', ParentInheritence);
409 // Iterate all pages
410 for i := 0 to IDEComponentPalette.Pages.Count-1 do
411 begin
412 Pg := IDEComponentPalette.Pages[i];
413 Comps := IDEComponentPalette.RefUserCompsForPage(Pg.PageName);
414 // Palette layout Page header
415 ParentNode := PalletteTree.Items.AddChild(nil, Pg.PageName);
416 // Iterate components of one page
417 for j := 0 to Comps.Count-1 do begin
418 Comp := Comps[j];
419 // Flat list item
420 AListNode := ListTree.Items.AddChildObject(Nil, Comp.ComponentClass.ClassName, Comp);
421 // Palette layout item
422 APaletteNode := PalletteTree.Items.AddChildObject(ParentNode, Comp.ComponentClass.ClassName, Comp);
423 if Comp is TPkgComponent then
424 CurIcon := TPkgComponent(Comp).ImageIndex
425 else
426 CurIcon := -1;
427 if CurIcon>=0 then
428 begin
429 AListNode.ImageIndex := CurIcon;
430 AListNode.SelectedIndex := AListNode.ImageIndex;
431 APaletteNode.ImageIndex := AListNode.ImageIndex;
432 APaletteNode.SelectedIndex := AListNode.ImageIndex;
433 end;
434 // Component inheritence item
435 DoComponentInheritence(Comp);
436 end;
437 end;
438 InheritanceTree.AlphaSort;
439 {$IFnDEF NoComponentListTreeExpand}
440 InheritanceTree.FullExpand; // Some users may not want the trees expanded.
441 PalletteTree.FullExpand;
442 {$ENDIF}
443 PrevChangeStamp := IDEComponentPalette.ChangeStamp;
444 finally
445 FClassList.Free;
446 InheritanceTree.Items.EndUpdate;
447 PalletteTree.EndUpdate;
448 ListTree.EndUpdate;
449 Screen.EndWaitCursor;
450 end;
451 end;
452
453 procedure TComponentListForm.TreeFilterEdAfterFilter(Sender: TObject);
454 begin
455 if TreeFilterEd.Filter = '' then
456 IDEComponentPalette.SetSelectedComp(nil, False);
457 UpdateButtonState;
458 end;
459
460 procedure TComponentListForm.ComponentsDblClick(Sender: TObject);
461 // This is used for all 3 treeviews
462 begin
463 OKButtonClick(nil); // Select and close this form
464 end;
465
466 procedure TComponentListForm.ListTreeSelectionChanged(Sender: TObject);
467 var
468 AComponent: TRegisteredComponent;
469 begin
470 UpdateButtonState;
471 if FInitialized then
472 begin
473 if FPageControlChange then
474 Exit;
475 AComponent:=GetSelectedComponent;
476 if AComponent<>nil then
477 IDEComponentPalette.SetSelectedComp(AComponent, ssShift in GetKeyShiftState)
478 else
479 begin
480 FIgnoreSelection := True;
481 IDEComponentPalette.SetSelectedComp(nil, False);
482 FIgnoreSelection := False;
483 end;
484 end
485 else begin
486 // Only run once when the IDE starts.
487 FInitialized := True;
488 IDEComponentPalette.SetSelectedComp(nil, False);
489 ListTree.Selected := Nil;
490 PalletteTree.Selected := Nil;
491 InheritanceTree.Selected := Nil;
492 end
493 end;
494
495 procedure TComponentListForm.TreeKeyPress(Sender: TObject; var Key: char);
496 // This is used for all 3 treeviews
497 begin
498 if Key = Char(VK_RETURN) then
499 ComponentsDblClick(Sender);
500 end;
501
502 procedure TComponentListForm.PageControlChange(Sender: TObject);
503 begin
504 //DebugLn(['TComponentListForm.PageControlChange: Start']);
505 FPageControlChange := True;
506 case PageControl.PageIndex of
507 0: begin
508 TreeFilterEd.FilteredTreeview := ListTree;
509 FActiveTree := ListTree;
510 end;
511 1: begin
512 TreeFilterEd.FilteredTreeview := PalletteTree;
513 FActiveTree := PalletteTree;
514 end;
515 2: begin
516 TreeFilterEd.FilteredTreeview := InheritanceTree;
517 FActiveTree := InheritanceTree;
518 end;
519 end;
520 TreeFilterEd.InvalidateFilter;
521 EnvironmentOptions.ComponentListPageIndex := PageControl.PageIndex;
522 FActiveTree.BeginUpdate;
523 tmDeselect.Enabled := True;
524 end;
525
526 procedure TComponentListForm.tmDeselectTimer(Sender: TObject);
527 begin
528 tmDeselect.Enabled := False;
529 FActiveTree.Selected := nil;
530 SelectionWasChanged;
531 FActiveTree.EndUpdate;
532 FPageControlChange := False;
533 end;
534
535 procedure TComponentListForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
536 begin
537 ClearSelection;
538 IDEComponentPalette.Selected := Nil;
539 end;
540
541 procedure TComponentListForm.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
542 begin
543 if Key=VK_ESCAPE then
544 begin
545 if (IDEComponentPalette.Selected = nil) and not IsDocked then //close only if no component is selected
546 Close
547 else
548 ClearSelection; //unselect if component is selected
549 end;
550 end;
551
552 procedure TComponentListForm.OKButtonClick(Sender: TObject);
553 // Select component from palette and close this form. User can insert the component.
554 var
555 AComponent: TRegisteredComponent;
556 OldFocusedControl: TWinControl;
557 begin
558 AComponent := GetSelectedComponent;
559 if AComponent=nil then
560 Exit;
561
562 OldFocusedControl := Screen.ActiveControl;
563 AddSelectedComponent;
564 if (OldFocusedControl<>nil) and OldFocusedControl.CanSetFocus then // AddComponent in docked mode steals focus to designer, get it back
565 OldFocusedControl.SetFocus;
566
567 if not IsDocked and not chbKeepOpen.Checked then
568 Close;
569 end;
570
571 procedure TComponentListForm.miCollapseAllClick(Sender: TObject);
572 begin
573 TreeFilterEd.FilteredTreeview.FullCollapse;
574 end;
575
576 procedure TComponentListForm.miCollapseClick(Sender: TObject);
577 var
578 Node: TTreeNode;
579 begin
580 Node := TreeFilterEd.FilteredTreeview.Selected;
581 if Node = nil then
582 Exit;
583 if (Node.Level > 0) and (Node.HasChildren = False) then
584 Node := Node.Parent;
585 Node.Collapse(True);
586 end;
587
588 procedure TComponentListForm.miExpandAllClick(Sender: TObject);
589 begin
590 TreeFilterEd.FilteredTreeview.FullExpand;
591 end;
592
593 procedure TComponentListForm.miExpandClick(Sender: TObject);
594 var
595 Node: TTreeNode;
596 begin
597 Node := TreeFilterEd.FilteredTreeview.Selected;
598 if Node = nil then
599 Exit;
600 if (Node.Level > 0) and (Node.HasChildren = False) then
601 Node := Node.Parent;
602 Node.Expand(True);
603 end;
604
605 procedure TComponentListForm.pmCollapseExpandPopup(Sender: TObject);
606 var
607 Node: TTreeNode;
608 begin
609 Node := TreeFilterEd.FilteredTreeview.Selected;
610 if Node = nil then
611 begin
612 miExpand.Enabled := False;
613 miCollapse.Enabled := False;
614 end
615 else
616 begin
617 miExpand.Enabled := (Node.HasChildren) and (not Node.Expanded);
618 miCollapse.Enabled := (Node.HasChildren) and (Node.Expanded);
619 end;
620 end;
621
622 procedure TComponentListForm.SelectionToolButtonClick(Sender: TObject);
623 begin
624 SelectionToolButton.Down := True;
625 IDEComponentPalette.SetSelectedComp(nil, False);
626 end;
627
628 end.
629
630