1 {
2 /***************************************************************************
3 componentpalette.pas
4 --------------------
5
6
7 ***************************************************************************/
8
9 ***************************************************************************
10 * *
11 * This source is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This code is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
19 * General Public License for more details. *
20 * *
21 * A copy of the GNU General Public License is available on the World *
22 * Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
23 * obtain it by writing to the Free Software Foundation, *
24 * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
25 * *
26 ***************************************************************************
27
28 Author: Mattias Gaertner, Juha Manninen
29
30 Abstract:
31 The implementation of the component palette.
32 Supports reordering of pages and components by user settings in environment options.
33 }
34 unit ComponentPalette;
35
36 {$mode objfpc}{$H+}
37
38 interface
39
40 uses
41 Classes, SysUtils, Laz_AVL_Tree,
42 // LCL
43 LCLProc, Controls, Forms, Graphics, ComCtrls, Buttons, Menus, ExtCtrls,
44 // LazUtils
45 LazFileUtils, LazFileCache, AvgLvlTree,
46 // IdeIntf
47 FormEditingIntf, LazIDEIntf, IDEImagesIntf, PropEdits, ComponentReg,
48 // IDE
49 ComponentPalette_Options,
50 MainBase, LazarusIDEStrConsts, DesignerProcs, PackageDefs, EnvironmentOpts,
51 ImgList;
52
53 const
54 CompPalSelectionToolBtnPrefix = 'PaletteSelectBtn';
55 CompPaletteCompBtnPrefix = 'PaletteBtn';
56 type
57 { TComponentPage }
58
59 TComponentPage = class(TBaseComponentPage)
60 private
61 fPageComponent: TCustomPage;
62 fSelectButton: TComponent;
63 fBtnIndex: integer;
64 fRegComps: TRegisteredCompList; // Reference to components.
65 fGuiCreated: Boolean;
66 procedure ReAlignButtons;
67 procedure RemoveSheet;
68 procedure InsertVisiblePage(aCompNames: TRegisteredCompList);
69 procedure CreateSelectionButton(aButtonUniqueName: string; aScrollBox: TScrollBox);
70 procedure CreateOrDelButton(aComp: TPkgComponent; aButtonUniqueName: string;
71 aScrollBox: TScrollBox);
72 procedure CreateButtons;
73 protected
74 public
75 constructor Create(const ThePageName: string);
76 destructor Destroy; override;
GetScrollBoxnull77 function GetScrollBox: TScrollBox;
78 public
79 property PageComponent: TCustomPage read fPageComponent write fPageComponent;
80 property SelectButton: TComponent read fSelectButton write fSelectButton;
81 end;
82
83 { TComponentPalette }
84
85 TComponentPalette = class(TBaseComponentPalette)
86 PalettePopupMenu: TPopupMenu;
87 PopupMenu: TPopupMenu;
88 OpenPackageMenuItem: TMenuItem;
89 OpenUnitMenuItem: TMenuItem;
90 procedure ActivePageChanged(Sender: TObject);
91 procedure OnScrollBoxResize(Sender: TObject);
92 procedure OpenPackageClicked(Sender: TObject);
93 procedure OpenUnitClicked(Sender: TObject);
94 procedure ComponentListClicked(Sender: TObject);
95 procedure OptionsClicked(Sender: TObject);
96 procedure PalettePopupMenuPopup(Sender: TObject);
97 procedure PopupMenuPopup(Sender: TObject);
98 private
99 // Generics TFPGMap<> is bocus, being actually a sorted list, slow adding items.
100 // Instead use a tree container as Map<TComponentClass,TSpeedButton>.
101 fComponentButtons: TPointerToPointerTree;
102 // Visual container for tabs
103 FPageControl: TPageControl;
104 FOnOpenPackage: TNotifyEvent;
105 FOnOpenUnit: TNotifyEvent;
106 FOnChangeActivePage: TNotifyEvent;
107 fSelectButtonIcon: TCustomBitmap;
108 fUpdatingPageControl: boolean;
109 fUpdateLock: integer;
110 // Used by UpdateNoteBookButtons
111 fOldActivePage: TTabSheet;
112 fVisiblePageIndex: integer;
113 procedure ClearButtons;
FindCompByButtonnull114 function FindCompByButton(Button: TSpeedButton): TRegisteredComponent;
FindPkgCompByButtonnull115 function FindPkgCompByButton(Button: TComponent): TPkgComponent;
IndexOfPageComponentnull116 function IndexOfPageComponent(AComponent: TComponent): integer;
117 procedure ReAlignButtons(aSheet: TCustomPage);
118 procedure UpdateNoteBookButtons(ForceUpdateAll: Boolean);
119 procedure RemoveUnneededPage(aSheet: TCustomPage);
120 procedure SetPageControl(const AValue: TPageControl);
121 procedure SelectionToolClick(Sender: TObject);
122 procedure ComponentBtnMouseDown(Sender: TObject; Button: TMouseButton;
123 Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
124 procedure ComponentBtnMouseUp(Sender: TObject; {%H-}Button: TMouseButton;
125 {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
126 procedure ComponentBtnDblClick(Sender: TObject);
127 procedure OnPageMouseWheel(Sender: TObject; {%H-}Shift: TShiftState;
128 WheelDelta: Integer; {%H-}MousePos: TPoint; var Handled: Boolean);
129 procedure CreatePopupMenu;
130 procedure UnselectAllButtons;
131 procedure SelectionWasChanged;
132 procedure GetUnregisteredIcon(var ImageList: TCustomImageList; var ImageIndex: TImageIndex);
GetSelectButtonIconnull133 function GetSelectButtonIcon: TCustomBitmap;
SelectAButtonnull134 function SelectAButton(Button: TSpeedButton): boolean;
135 procedure ComponentWasAdded({%H-}ALookupRoot, {%H-}AComponent: TComponent;
136 {%H-}ARegisteredComponent: TRegisteredComponent);
137 protected
138 procedure DoChange; override;
139 public
140 constructor Create;
141 destructor Destroy; override;
142 procedure OnGetNonVisualCompIcon(Sender: TObject;
143 AComponent: TComponent; var ImageList: TCustomImageList; var ImageIndex: TImageIndex);
144 procedure BeginUpdate; override;
145 procedure EndUpdate; override;
IsUpdateLockednull146 function IsUpdateLocked: boolean;
147 procedure Update(ForceUpdateAll: Boolean); override;
148 public
149 property PageControl: TPageControl read FPageControl write SetPageControl;
150 property OnOpenPackage: TNotifyEvent read FOnOpenPackage write FOnOpenPackage;
151 property OnOpenUnit: TNotifyEvent read FOnOpenUnit write FOnOpenUnit;
152 property OnChangeActivePage: TNotifyEvent read FOnChangeActivePage write FOnChangeActivePage;
153 end;
154
CompareControlsWithTagnull155 function CompareControlsWithTag(Control1, Control2: Pointer): integer;
156
157 implementation
158
159 {$R ../images/components_images.res}
160 {$DEFINE USE_PageIndex}
161
162 const
163 OVERVIEW_PANEL_WIDTH = 20;
164
CompareControlsWithTagnull165 function CompareControlsWithTag(Control1, Control2: Pointer): integer;
166 var
167 Ctrl1: TControl absolute Control1;
168 Ctrl2: TControl absolute Control2;
169 begin
170 if Ctrl1.Tag>Ctrl2.Tag then
171 Result:=1
172 else if Ctrl1.Tag<Ctrl2.Tag then
173 Result:=-1
174 else
175 Result:=0;
176 end;
177
178 { TComponentPage }
179
180 constructor TComponentPage.Create(const ThePageName: string);
181 begin
182 inherited Create(ThePageName);
183 end;
184
185 destructor TComponentPage.Destroy;
186 begin
187 FreeAndNil(fSelectButton);
188 FreeAndNil(fPageComponent);
189 inherited Destroy;
190 end;
191
GetScrollBoxnull192 function TComponentPage.GetScrollBox: TScrollBox;
193 begin
194 if Assigned(PageComponent) and (PageComponent.ComponentCount > 0)
195 and (PageComponent.Components[0] is TScrollBox) then
196 Result := TScrollBox(PageComponent.Components[0])
197 else
198 Result := Nil;
199 end;
200
IsSelectionToolBtnnull201 function IsSelectionToolBtn(aControl: TControl): boolean;
202 begin
203 Result:=(aControl is TSpeedButton)
204 and (LeftStr(aControl.Name,length(CompPalSelectionToolBtnPrefix))=CompPalSelectionToolBtnPrefix);
205 end;
206
207 procedure TComponentPage.ReAlignButtons;
208 var
209 Pal: TComponentPalette;
210 CurButton: TSpeedButton;
211 ButtonTree: TAVLTree;
212 Node: TAVLTreeNode;
213 ScrollBox: TScrollBox;
214 buttonx, MaxBtnPerRow, i, ComponentPaletteBtnWidthScaled,
215 ComponentPaletteBtnHeightScaled: integer;
216 begin
217 if (PageComponent=Nil) or (PageComponent.ComponentCount=0)
218 or not (PageComponent.Components[0] is TScrollBox) then
219 exit;
220 if not fGuiCreated then begin
221 {$IFDEF VerboseComponentPalette}
222 DebugLn(['TComponentPage.ReAlignButtons, ', PageName, ', calling CreateButtons']);
223 {$ENDIF}
224 CreateButtons; // Delayed creation of buttons at startup.
225 end;
226 Pal := TComponentPalette(Palette);
227 if Pal.PageControl<>nil then
228 Pal.PageControl.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TComponentPage.ReAlignButtons'){$ENDIF};
229 ComponentPaletteBtnWidthScaled := Pal.PageControl.Scale96ToForm(ComponentPaletteBtnWidth);
230 ComponentPaletteBtnHeightScaled := Pal.PageControl.Scale96ToForm(ComponentPaletteBtnHeight);
231 ButtonTree:=nil;
232 try
233 ScrollBox:=TScrollBox(PageComponent.Components[0]);
234 ButtonTree:=TAVLTree.Create(@CompareControlsWithTag);
235 for i:=0 to ScrollBox.ControlCount-1 do begin
236 CurButton:=TSpeedbutton(ScrollBox.Controls[i]);
237 if IsSelectionToolBtn(CurButton) then continue;
238 if (CurButton is TSpeedButton) and CurButton.Visible then
239 ButtonTree.Add(CurButton);
240 end;
241 if ButtonTree.Count=0 then exit;
242
243 ButtonX:= ((ComponentPaletteBtnWidthScaled*3) div 2) + 2;
244
245 {$IFDEF VerboseComponentPalette}
246 if PageComponent.Caption = CompPalVerbPgName then
247 DebugLn(['TComponentPage.ReAlignButtons',
248 ' ButtonTree.Count=',ButtonTree.Count,
249 ' ScrollBox.ControlCount=',ScrollBox.ControlCount,
250 ' ScrollBox.Bounds=',dbgs(ScrollBox.BoundsRect),
251 ' VertScrollBar.Size=',ScrollBox.VertScrollBar.Size,
252 ' ClientSizeWithoutBar=',ScrollBox.VertScrollBar.ClientSizeWithoutBar,
253 ' IsScrollBarVisible=',ScrollBox.VertScrollBar.IsScrollBarVisible,
254 ' HorzScrollBar.Size=',ScrollBox.HorzScrollBar.Size,
255 ' Page=',ScrollBox.HorzScrollBar.Page,
256 ' Range=',ScrollBox.HorzScrollBar.Range,
257 ' IsScrollBarVisible=',ScrollBox.HorzScrollBar.IsScrollBarVisible
258 ]);
259 {$ENDIF}
260
261 MaxBtnPerRow:=((ScrollBox.VertScrollBar.ClientSizeWithoutBar - ButtonX) div ComponentPaletteBtnWidthScaled);
262
263 // If we need to wrap, make sure we have space for the scrollbar
264 if MaxBtnPerRow < ButtonTree.Count then
265 MaxBtnPerRow:=((ScrollBox.VertScrollBar.ClientSizeWithBar - ButtonX) div ComponentPaletteBtnWidthScaled);
266 //debugln(['TComponentPage.ReAlignButtons MaxBtnPerRow=',MaxBtnPerRow,' ButtonTree.Count=',ButtonTree.Count,' ',ButtonX + MaxBtnPerRow * ComponentPaletteBtnWidthScaled]);
267 if MaxBtnPerRow<1 then MaxBtnPerRow:=1;
268
269 i:=0;
270 Node:=ButtonTree.FindLowest;
271 while Node<>nil do begin
272 CurButton:=TSpeedbutton(Node.Data);
273 CurButton.SetBounds(ButtonX + (i mod MaxBtnPerRow) * ComponentPaletteBtnWidthScaled,
274 (i div MaxBtnPerRow) * ComponentPaletteBtnHeightScaled,
275 CurButton.Width, CurButton.Height);
276 {$IFDEF VerboseComponentPalette}
277 if PageComponent.Caption = CompPalVerbPgName then
278 DebugLn(['TComponentPage.ReAlignButtons ',CurButton.Name,' ',dbgs(CurButton.BoundsRect)]);
279 {$ENDIF}
280 inc(i);
281 Node:=ButtonTree.FindSuccessor(Node);
282 end;
283 PageComponent.Invalidate;
284 finally
285 if Pal.PageControl<>nil then
286 Pal.PageControl.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TComponentPage.ReAlignButtons'){$ENDIF};
287 FreeAndNil(ButtonTree);
288 end;
289 end;
290
291 procedure TComponentPage.RemoveSheet;
292 var
293 Btn: TSpeedButton;
294 begin
295 Btn:=TSpeedButton(SelectButton);
296 if Btn<>nil then begin
297 SelectButton:=nil;
298 Application.ReleaseComponent(Btn);
299 Btn.Visible:=false;
300 end;
301 PageComponent:=nil;
302 end;
303
304 procedure TComponentPage.InsertVisiblePage(aCompNames: TRegisteredCompList);
305 var
306 Pal: TComponentPalette;
307 TabIndex: Integer;
308 PanelRight: TPanel;
309 BtnRight: TSpeedButton;
310 TabControl: TCustomTabControl;
311 begin
312 if not Visible then begin
313 {$IFDEF VerboseComponentPalette}
314 DebugLn(['TComponentPalette.InsertVisiblePage: Not inserting Page=', PageName]);
315 {$ENDIF}
316 exit;
317 end;
318 fRegComps := aCompNames;
319 Pal := TComponentPalette(Palette);
320 TabControl := TCustomTabControl(Pal.FPageControl);
321 if PageComponent=nil then
322 begin
323 // insert a new PageControl page
324 {$IFDEF VerboseComponentPalette}
325 DebugLn(['TComponentPalette.InsertVisiblePage: Inserting Page=', PageName,
326 ', at index=', Pal.fVisiblePageIndex]);
327 {$ENDIF}
328 {$IFDEF USE_PageIndex}
329 TabIndex:= TabControl.Pages.Add(PageName);
330 PageComponent := Pal.FPageControl.Page[TabIndex];
331 PageComponent.PageIndex := Pal.fVisiblePageIndex;
332 {$ELSE}
333 TabControl.Pages.Insert(Pal.fVisiblePageIndex, PageName);
334 PageComponent := Pal.FPageControl.Page[Pal.fVisiblePageIndex];
335 {$ENDIF}
336 with TScrollBox.Create(PageComponent) do begin
337 Align := alClient;
338 BorderStyle := bsNone;
339 BorderWidth := 0;
340 HorzScrollBar.Visible := false;
341 {$IFDEF LCLCarbon}
342 // carbon has not implemented turning scrollbars on and off
343 VertScrollBar.Visible := false;
344 AutoScroll:=false;
345 {$ENDIF}
346 VertScrollBar.Increment := PageComponent.Scale96ToForm(ComponentPaletteBtnHeight);
347 VertScrollBar.Tracking := True;
348 Parent := PageComponent;
349 end;
350 PanelRight := TPanel.Create(PageComponent);
351 with PanelRight do
352 begin
353 Align := alRight;
354 Caption := '';
355 BevelOuter := bvNone;
356 Visible := True; // EnvironmentOptions.IDESpeedButtonsVisible;
357 Parent := PageComponent;
358 Width := Scale96ToForm(OVERVIEW_PANEL_WIDTH);
359 OnMouseWheel := @Pal.OnPageMouseWheel;
360 end;
361 BtnRight:=TSpeedButton.Create(PageComponent);
362 with BtnRight do
363 begin
364 IDEImages.AssignImage(BtnRight, 'SelCompPage');
365 Flat := True;
366 Hint := lisClickToSelectPalettePage;
367 ShowHint := True;
368 OnMouseDown := @MainIDE.SelComponentPageButtonMouseDown;
369 OnClick := @MainIDE.SelComponentPageButtonClick;
370 OnMouseWheel := @Pal.OnPageMouseWheel;
371 Parent := PanelRight;
372 SetBounds(Scale96ToForm(2), Scale96ToForm(1), Scale96ToForm(16), Scale96ToForm(16));
373 end;
374 end
375 else begin
376 // move to the right position
377 {$IFDEF USE_PageIndex}
378 {$IFDEF VerboseComponentPalette}
379 DebugLn(['TComponentPalette.InsertVisiblePage: Page=', PageName,
380 ' setting PageIndex from ', PageComponent.PageIndex , ' to ', Pal.fVisiblePageIndex]);
381 {$ENDIF}
382 PageComponent.PageIndex := Pal.fVisiblePageIndex;
383 {$ELSE}
384 TabIndex := PageComponent.PageIndex;
385 {$IFDEF VerboseComponentPalette}
386 DebugLn(['TComponentPalette.InsertVisiblePage: Start moving Page=', PageName,
387 ' from ', TabIndex, ' to ', Pal.fVisiblePageIndex]);
388 {$ENDIF}
389 if (TabIndex<>Pal.fVisiblePageIndex)
390 and (Pal.fVisiblePageIndex < TabControl.Pages.Count) then
391 begin
392 {$IFDEF VerboseComponentPalette}
393 if {PageName = CompPalVerbPgName} true then
394 DebugLn(['TComponentPalette.InsertVisiblePage: Moving Page=', PageName,
395 ' from ', TabIndex, ' to ', Pal.fVisiblePageIndex]);
396 {$ENDIF}
397 TabControl.Pages.Move(TabIndex, Pal.fVisiblePageIndex);
398 end;
399 {$ENDIF}
400 end;
401 inc(Pal.fVisiblePageIndex);
402 end;
403
404 procedure TComponentPage.CreateSelectionButton(aButtonUniqueName: string; aScrollBox: TScrollBox);
405 var
406 Pal: TComponentPalette;
407 Btn: TSpeedButton;
408 begin
409 if Assigned(SelectButton) then Exit;
410 Pal := TComponentPalette(Palette);
411 Btn := TSpeedButton.Create(nil);
412 SelectButton:=Btn;
413 IDEImages.AssignImage(Btn, 'tmouse');
414 with Btn do begin
415 Name := CompPalSelectionToolBtnPrefix + aButtonUniqueName;
416 OnClick := @Pal.SelectionToolClick;
417 OnMouseWheel := @Pal.OnPageMouseWheel;
418 Flat := True;
419 GroupIndex:= 1;
420 Down := True;
421 Hint := lisSelectionTool;
422 ShowHint := EnvironmentOptions.ShowHintsForComponentPalette;
423 SetBounds(0,0,aScrollBox.Scale96ToForm(ComponentPaletteBtnWidth),aScrollBox.Scale96ToForm(ComponentPaletteBtnHeight));
424 Parent := aScrollBox;
425 end;
426 end;
427
428 procedure TComponentPage.CreateOrDelButton(aComp: TPkgComponent; aButtonUniqueName: string;
429 aScrollBox: TScrollBox);
430 var
431 Pal: TComponentPalette;
432 CompCls: TComponentClass;
433 Btn: TSpeedButton;
434 begin
435 Pal := TComponentPalette(Palette);
436 CompCls := aComp.ComponentClass;
437 Btn := TSpeedButton(Pal.fComponentButtons[CompCls]);
438 if aComp.Visible then
439 begin
440 inc(fBtnIndex);
441 if Btn=nil then
442 begin
443 Btn := TSpeedButton.Create(nil);
444 Pal.fComponentButtons[CompCls] := Btn;
445 Btn.Name := CompPaletteCompBtnPrefix + aButtonUniqueName + CompCls.ClassName;
446 // Left and Top will be set in ReAlignButtons.
447 Btn.SetBounds(Btn.Left, Btn.Top,
448 aScrollBox.Scale96ToForm(ComponentPaletteBtnWidth),
449 aScrollBox.Scale96ToForm(ComponentPaletteBtnHeight));
450 Btn.Images := aComp.Images;
451 Btn.ImageIndex := aComp.ImageIndex;
452 Btn.GroupIndex := 1;
453 Btn.Flat := true;
454 Btn.OnMouseDown := @Pal.ComponentBtnMouseDown;
455 Btn.OnMouseUp := @Pal.ComponentBtnMouseUp;
456 Btn.OnDblClick := @Pal.ComponentBtnDblClick;
457 Btn.OnMouseWheel := @Pal.OnPageMouseWheel;
458 Btn.ShowHint := EnvironmentOptions.ShowHintsForComponentPalette;
459 Btn.Hint := CompCls.ClassName + sLineBreak +
460 '(' + aComp.ComponentClass.UnitName+', '+aComp.PkgFile.LazPackage.Name + ')';
461 Btn.PopupMenu:=Pal.PopupMenu;
462 {$IFDEF VerboseComponentPalette}
463 if aComp.RealPage.PageName = CompPalVerbPgName then
464 DebugLn(['TComponentPalette.CreateOrDelButton Created Button: ',CompCls.ClassName,' ',Btn.Name]);
465 {$ENDIF}
466 end else
467 begin
468 {$IFDEF VerboseComponentPalette}
469 if aComp.RealPage.PageName = CompPalVerbPgName then
470 DebugLn(['TComponentPalette.CreateOrDelButton Keep Button: ',CompCls.ClassName,' ',Btn.Name,' ',DbgSName(Btn.Parent)]);
471 {$ENDIF}
472 end;
473 Btn.Parent := aScrollBox;
474 Btn.Tag:=fBtnIndex;
475 end
476 else if Btn<>nil then
477 begin
478 {$IFDEF VerboseComponentPalette}
479 if aComp.RealPage.PageName = CompPalVerbPgName then
480 DebugLn(['TComponentPalette.CreateOrDelButton Destroy Button: ',CompCls.ClassName,' ',Btn.Name]);
481 {$ENDIF}
482 Application.ReleaseComponent(Btn);
483 Pal.fComponentButtons.Remove(CompCls);
484 Btn.Visible:=false;
485 end;
486 end;
487
488 procedure TComponentPage.CreateButtons;
489 // Create speedbuttons for every visible component
490 var
491 ScrollBox: TScrollBox;
492 Comp: TPkgComponent;
493 i: Integer;
494 begin
495 if not Visible then Exit;
496 ScrollBox := GetScrollBox;
497 Assert(Assigned(ScrollBox), 'CreateButtons: ScrollBox not assigned.');
498 ScrollBox.OnResize := @TComponentPalette(Palette).OnScrollBoxResize;
499 ScrollBox.OnMouseWheel := @TComponentPalette(Palette).OnPageMouseWheel;
500 {$IFDEF VerboseComponentPalette}
501 if PageName = CompPalVerbPgName then
502 DebugLn(['TComponentPalette.CreateButtons PAGE="',PageName,'", PageIndex=',PageComponent.PageIndex]);
503 {$ENDIF}
504 // create selection button
505 CreateSelectionButton(IntToStr(FIndex), ScrollBox);
506 // create component buttons and delete unneeded ones
507 fBtnIndex := 0;
508 Assert(Assigned(fRegComps), 'TComponentPage.CreateButtons: fCompNames is not assigned.');
509 for i := 0 to fRegComps.Count-1 do begin
510 Comp := fRegComps[i] as TPkgComponent;
511 if Assigned(Comp) then
512 CreateOrDelButton(Comp, Format('%d_%d_',[FIndex,i]), ScrollBox);
513 end;
514 fGuiCreated := True;
515 end;
516
517 { TComponentPalette }
518
519 procedure TComponentPalette.ActivePageChanged(Sender: TObject);
520 begin
521 if (FPageControl=nil) or fUpdatingPageControl then exit;
522 if (Selected<>nil)
523 and ((Selected.RealPage as TComponentPage).PageComponent=FPageControl.ActivePage) then exit;
524 {$IFDEF VerboseComponentPalette}
525 DebugLn('TComponentPalette.ActivePageChanged: Calling ReAlignButtons, setting Selected:=nil.');
526 {$ENDIF}
527 ReAlignButtons(FPageControl.ActivePage);
528 Selected:=nil;
529
530 if MainIDE.IDEStarted and Assigned(FOnChangeActivePage) then
531 FOnChangeActivePage(Sender);
532 end;
533
534 procedure TComponentPalette.OnScrollBoxResize(Sender: TObject);
535 begin
536 if MainIDE.IDEStarted and (TControl(Sender).Parent is TCustomPage) then
537 begin
538 {$IFDEF VerboseComponentPalette}
539 DebugLn(['TComponentPalette.OnScrollBoxResize Calling ReAlignButtons, IDEStarted=', MainIDE.IDEStarted]);
540 {$ENDIF}
541 ReAlignButtons(TCustomPage(TControl(Sender).Parent));
542 end;
543 end;
544
545 procedure TComponentPalette.OpenPackageClicked(Sender: TObject);
546 var
547 PkgComponent: TPkgComponent;
548 begin
549 PkgComponent:=FindPkgCompByButton(PopupMenu.PopupComponent);
550 if (PkgComponent=nil) or (PkgComponent.PkgFile=nil)
551 or (PkgComponent.PkgFile.LazPackage=nil) then exit;
552 if Assigned(OnOpenPackage) then
553 OnOpenPackage(PkgComponent.PkgFile.LazPackage);
554 end;
555
556 procedure TComponentPalette.OpenUnitClicked(Sender: TObject);
557 var
558 PkgComponent: TPkgComponent;
559 begin
560 PkgComponent:=FindPkgCompByButton(PopupMenu.PopupComponent);
561 if (PkgComponent=nil) or (PkgComponent.PkgFile=nil)
562 or (PkgComponent.PkgFile.LazPackage=nil) then exit;
563 if Assigned(OnOpenUnit) then
564 OnOpenUnit(PkgComponent);
565 end;
566
567 procedure TComponentPalette.ComponentListClicked(Sender: TObject);
568 begin
569 MainIDE.DoShowComponentList;
570 end;
571
572 procedure TComponentPalette.OptionsClicked(Sender: TObject);
573 begin
574 MainIDE.DoOpenIDEOptions(TCompPaletteOptionsFrame, '', [], []);
575 end;
576
577 procedure TComponentPalette.PalettePopupMenuPopup(Sender: TObject);
578 begin
579 ;
580 end;
581
582 procedure TComponentPalette.PopupMenuPopup(Sender: TObject);
583 var
584 PkgComponent: TPkgComponent;
585 APackage: TLazPackage;
586 UnitFilename: String;
587 ShownFilename: String;
588 begin
589 PkgComponent:=FindPkgCompByButton(PopupMenu.PopupComponent);
590 APackage:=nil;
591 if (PkgComponent<>nil) and (PkgComponent.PkgFile<>nil) then
592 APackage:=PkgComponent.PkgFile.LazPackage;
593 if APackage=nil then begin
594 OpenPackageMenuItem.Visible:=false;
595 OpenUnitMenuItem.Visible:=false;
596 end else begin
597 OpenPackageMenuItem.Caption:=Format(lisCPOpenPackage, [APackage.IDAsString]);
598 OpenPackageMenuItem.Visible:=true;
599 ShownFilename:=PkgComponent.PkgFile.Filename;
600 UnitFilename:=PkgComponent.PkgFile.GetFullFilename;
601 if not FileExistsCached(UnitFilename) then begin
602 UnitFilename:=LazarusIDE.FindSourceFile(ExtractFilename(UnitFilename),
603 APackage.Directory,[]);
604 if FileExistsUTF8(UnitFilename) then
605 UnitFilename:=ShownFilename;
606 end;
607 OpenUnitMenuItem.Caption:=Format(lisCPOpenUnit, [ShownFilename]);
608 OpenUnitMenuItem.Visible:=true;
609 OpenUnitMenuItem.Enabled:=FileExistsCached(UnitFilename);
610 end;
611 end;
612
613 procedure TComponentPalette.SetPageControl(const AValue: TPageControl);
614 var
615 miCompList,
616 miOptions: TMenuItem;
617 begin
618 if FPageControl=AValue then exit;
619 ClearButtons;
620 FPageControl:=AValue;
621 if FPageControl<>nil then begin
622 FPageControl.OnChange:=@ActivePageChanged;
623 if PalettePopupMenu=nil then begin
624 PalettePopupMenu:=TPopupMenu.Create(nil);
625 PalettePopupMenu.OnPopup:=@PalettePopupMenuPopup;
626 PalettePopupMenu.Name:='PalettePopupMenu';
627 // Component List
628 PalettePopupMenu.Images := IDEImages.Images_16;
629 miCompList:=TMenuItem.Create(PalettePopupMenu);
630 with miCompList do begin
631 Name:='ComponentListMenuItem';
632 Caption:=lisCompPalComponentList;
633 OnClick:=@ComponentListClicked;
634 ImageIndex := IDEImages.LoadImage('menu_view_components');
635 end;
636 PalettePopupMenu.Items.Add(miCompList);
637 miOptions:=TMenuItem.Create(PalettePopupMenu);
638 with miOptions do begin
639 Name:='OptionsMenuItem';
640 Caption:=lisMenuGeneralOptions;
641 OnClick:=@OptionsClicked;
642 ImageIndex := IDEImages.LoadImage('menu_environment_options');
643 end;
644 PalettePopupMenu.Items.Add(miOptions);
645 end;
646 FPageControl.PopupMenu:=PalettePopupMenu;
647 end;
648 {$IFDEF VerboseComponentPalette}
649 DebugLn(['TComponentPalette.SetPageControl, calling UpdateNoteBookButtons, ', AValue]);
650 {$ENDIF}
651 end;
652
653 procedure TComponentPalette.SelectionToolClick(Sender: TObject);
654 begin
655 SelectAButton(TSpeedButton(Sender));
656 end;
657
658 procedure TComponentPalette.ComponentBtnMouseDown(Sender: TObject; Button: TMouseButton;
659 Shift: TShiftState; X, Y: Integer);
660 begin
661 if Button=mbLeft then
662 begin
663 if ssShift in Shift then
664 SelectionMode := csmMulty
665 else
666 SelectionMode := csmSingle;
667 SelectAButton(TSpeedButton(Sender));
668 if Assigned(OnClassSelected) then
669 OnClassSelected(Self);
670 end;
671 end;
672
673 procedure TComponentPalette.ComponentBtnMouseUp(Sender: TObject; Button: TMouseButton;
674 Shift: TShiftState; X, Y: Integer);
675 begin
676 { If the visual state is down, but internal "no selection" then
677 just do visual unselection of all buttons
678 This trick is for double-click handling (to unselect the button visually ). }
679 if ((Sender as TCustomSpeedButton).Down) and (Selected = Nil) then
680 UnselectAllButtons;
681 end;
682
683 procedure TComponentPalette.ComponentBtnDblClick(Sender: TObject);
684 var
685 TypeClass: TComponentClass;
686 ParentComp: TComponent;
687 X, Y: integer;
688 AComponent: TComponent;
689 DisableAutoSize: Boolean;
690 begin
691 //debugln('TComponentPalette.ComponentBtnDblClick ',TComponent(Sender).Name);
692 if SelectAButton(TSpeedButton(Sender)) and (Selected<>nil) then begin
693 if FormEditingHook<>nil then begin
694 TypeClass:=Selected.ComponentClass;
695 if assigned(Selected.OnGetCreationClass) then
696 Selected.OnGetCreationClass(Self,TypeClass);
697 if TypeClass=nil then exit;
698 ParentComp:=FormEditingHook.GetDefaultComponentParent(TypeClass);
699 if ParentComp=nil then exit;
700 if not FormEditingHook.GetDefaultComponentPosition(TypeClass,ParentComp,X,Y)
701 then exit;
702 //debugln('TComponentPalette.ComponentBtnDblClick ',dbgsName(Sender),' ',dbgs(X),',',dbgs(Y));
703 DisableAutoSize:=true;
704 AComponent:=FormEditingHook.CreateComponent(ParentComp,TypeClass,'',X,Y,0,0,
705 DisableAutoSize);
706 if AComponent<>nil then begin
707 if DisableAutoSize and (AComponent is TControl) then
708 TControl(AComponent).EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TComponentPalette.ComponentBtnDblClick'){$ENDIF};
709 GlobalDesignHook.PersistentAdded(AComponent,true);
710 end;
711 end;
712 end;
713 Selected:=nil;
714 if Assigned(OnClassSelected) then
715 OnClassSelected(Self);
716 end;
717
718 // unselect all other buttons on all other PageControl pages
719 procedure TComponentPalette.UnselectAllButtons;
720 var
721 i: Integer;
722 CurPage: TBaseComponentPage;
723 SelectButtonOnPage: TSpeedButton;
724 begin
725 for i:=0 to Pages.Count-1 do begin
726 CurPage:=Pages[i];
727 if (Selected=nil) or (Selected.RealPage<>CurPage) then begin
728 SelectButtonOnPage:=TSpeedButton(TComponentPage(CurPage).SelectButton);
729 if SelectButtonOnPage<>nil then
730 SelectButtonOnPage.Down:=true;
731 end;
732 end;
733 end;
734
735 procedure TComponentPalette.SelectionWasChanged;
736 var
737 Sheet: TTabSheet;
738 Btn: TSpeedButton;
739 begin
740 if FPageControl=nil then exit;
741 UnselectAllButtons;
742 if Selected=nil then exit;
743 Assert(Assigned(Selected.RealPage), 'TComponentPalette.SelectionWasChanged: Selected.RealPage = Nil.');
744 Sheet:=(Selected.RealPage as TComponentPage).PageComponent as TTabSheet;
745 {$IFDEF VerboseComponentPalette}
746 DebugLn(['TComponentPalette.SelectionWasChanged: Setting FPageControl.ActivePage index ',Sheet.PageIndex]);
747 {$ENDIF}
748 // Switch to the new page
749 FPageControl.ActivePage:=Sheet;
750 // Build the GUI layout for this page if not done yet.
751 Btn:=TSpeedButton(fComponentButtons[Selected.ComponentClass]);
752 if Btn=nil then
753 ReAlignButtons(FPageControl.ActivePage);
754 // Select button
755 Btn:=TSpeedButton(fComponentButtons[Selected.ComponentClass]); //find again!
756 if Btn<>nil then
757 Btn.Down:=true;
758 end;
759
760 procedure TComponentPalette.CreatePopupMenu;
761 var
762 MenuItem: TMenuItem;
763 begin
764 if PopupMenu<>nil then exit;
765 PopupMenu:=TPopupMenu.Create(nil);
766 PopupMenu.OnPopup:=@PopupMenuPopup;
767 PopupMenu.Name:='ComponentPopupMenu';
768 PopupMenu.Images:=IDEImages.Images_16;
769
770 OpenPackageMenuItem:=TMenuItem.Create(PopupMenu);
771 with OpenPackageMenuItem do begin
772 Name:='OpenPackageMenuItem';
773 Caption:=lisCompPalOpenPackage;
774 OnClick:=@OpenPackageClicked;
775 end;
776 PopupMenu.Items.Add(OpenPackageMenuItem);
777
778 OpenUnitMenuItem:=TMenuItem.Create(PopupMenu);
779 with OpenUnitMenuItem do begin
780 Name:='OpenUnitMenuItem';
781 Caption:=lisCompPalOpenUnit;
782 OnClick:=@OpenUnitClicked;
783 end;
784 PopupMenu.Items.Add(OpenUnitMenuItem);
785
786 PopupMenu.Items.AddSeparator;
787
788 MenuItem:=TMenuItem.Create(PopupMenu);
789 with MenuItem do begin
790 Name:='ComponentListMenuItem';
791 Caption:=lisCompPalComponentList;
792 OnClick:=@ComponentListClicked;
793 end;
794 PopupMenu.Items.Add(MenuItem);
795
796 MenuItem:=TMenuItem.Create(PopupMenu);
797 with MenuItem do begin
798 Name:='OptionsMenuItem';
799 Caption:=lisMenuGeneralOptions;
800 OnClick:=@OptionsClicked;
801 ImageIndex := IDEImages.LoadImage('menu_environment_options');
802 end;
803 PopupMenu.Items.Add(MenuItem);
804 end;
805
806 procedure TComponentPalette.OnPageMouseWheel(Sender: TObject; Shift: TShiftState;
807 WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
808 begin
809 if (WheelDelta > 0) then
810 begin
811 if (PageControl.ActivePageIndex > 0) then
812 begin
813 PageControl.ActivePageIndex := PageControl.ActivePageIndex - 1;
814 PageControl.OnChange(PageControl);
815 end;
816 end else begin
817 if (PageControl.ActivePageIndex < PageControl.PageCount-1) then
818 begin
819 PageControl.ActivePageIndex := PageControl.ActivePageIndex + 1;
820 PageControl.OnChange(PageControl);
821 end;
822 end;
823 Handled := True;
824 end;
825
826 constructor TComponentPalette.Create;
827 begin
828 inherited Create(EnvironmentOptions.Desktop.ComponentPaletteOptions);
829 fComponentButtons:=TPointerToPointerTree.Create;
830 AddHandlerComponentAdded(@ComponentWasAdded);
831 AddHandlerSelectionChanged(@SelectionWasChanged);
832 ComponentPageClass := TComponentPage; // Used by CreatePagesFromUserOrder
833 end;
834
835 destructor TComponentPalette.Destroy;
836 var
837 AVLNode: TAVLTreeNode;
838 P2PItem: PPointerToPointerItem;
839 begin
840 PageControl:=nil;
841 // Free SpeedButtons stored in the tree map.
842 AVLNode:=fComponentButtons.Tree.FindLowest;
843 while AVLNode<>nil do begin
844 P2PItem:=PPointerToPointerItem(AVLNode.Data);
845 TSpeedButton(P2PItem^.Value).Free;
846 AVLNode:=fComponentButtons.Tree.FindSuccessor(AVLNode);
847 end;
848 FreeAndNil(fComponentButtons);
849 FreeAndNil(fSelectButtonIcon);
850 FreeAndNil(PopupMenu);
851 FreeAndNil(PalettePopupMenu);
852 inherited Destroy;
853 end;
854
855 procedure TComponentPalette.BeginUpdate;
856 begin
857 inc(FUpdateLock);
858 end;
859
860 procedure TComponentPalette.EndUpdate;
861 begin
862 if FUpdateLock<=0 then
863 raise Exception.Create('TBaseComponentPalette.EndUpdate: FUpdateLock<=0');
864 dec(FUpdateLock);
865 if (FUpdateLock=0) and FChanged then
866 Update(False);
867 end;
868
TComponentPalette.IsUpdateLockednull869 function TComponentPalette.IsUpdateLocked: boolean;
870 begin
871 Result:=FUpdateLock>0;
872 end;
873
874 procedure TComponentPalette.Update(ForceUpdateAll: Boolean);
875 begin
876 if not (ForceUpdateAll or FChanged) then Exit;
877 inherited Update(ForceUpdateAll);
878 {$IFDEF VerboseComponentPalette}
879 DebugLn(['TComponentPalette.Update, calling UpdateNoteBookButtons, fUpdatingPageControl=',
880 fUpdatingPageControl, ', ForceUpdateAll=', ForceUpdateAll, ', FChanged=', FChanged]);
881 {$ENDIF}
882 UpdateNoteBookButtons(ForceUpdateAll);
883 FChanged:=False;
884 end;
885
886 procedure TComponentPalette.ClearButtons;
887 begin
888 if FPageControl<>nil then
889 FPageControl.DisableAlign;
890 Selected:=nil;
891 if PopupMenu<>nil then begin
892 PopupMenu.Free;
893 PopupMenu:=nil;
894 OpenPackageMenuItem:=nil;
895 end;
896 if FPageControl<>nil then
897 FPageControl.EnableAlign;
898 end;
899
900 procedure TComponentPalette.GetUnregisteredIcon(
901 var ImageList: TCustomImageList; var ImageIndex: TImageIndex);
902 var
903 IL: TLCLGlyphs;
904 begin
905 IL := IDEImages.Images_24;
906 ImageList := IL;
907 ImageIndex := IL.GetImageIndex('unregisteredcomponent');
908 if ImageIndex<0 then
909 ImageIndex := IL.GetImageIndex('default');
910 end;
911
GetSelectButtonIconnull912 function TComponentPalette.GetSelectButtonIcon: TCustomBitmap;
913 begin
914 if fSelectButtonIcon=nil then
915 fSelectButtonIcon := CreateBitmapFromResourceName(hInstance, 'tmouse');
916 Result:=fSelectButtonIcon;
917 end;
918
SelectAButtonnull919 function TComponentPalette.SelectAButton(Button: TSpeedButton): boolean;
920 var
921 NewComponent: TRegisteredComponent;
922 begin
923 NewComponent := FindCompByButton(Button);
924 Selected := NewComponent;
925 Result := (Selected = NewComponent);
926 end;
927
928 procedure TComponentPalette.ComponentWasAdded(ALookupRoot, AComponent: TComponent;
929 ARegisteredComponent: TRegisteredComponent);
930 begin
931 if not (ssShift in GetKeyShiftState) and (SelectionMode = csmSingle) then
932 Selected := nil;
933 end;
934
935 procedure TComponentPalette.DoChange;
936 begin
937 if FUpdateLock>0 then
938 FChanged:=true
939 else
940 Update(False);
941 end;
942
943 procedure TComponentPalette.ReAlignButtons(aSheet: TCustomPage);
944 var
945 PageInd: Integer;
946 begin
947 if (aSheet=Nil) or not aSheet.Visible then
948 exit;
949 {$IFDEF VerboseComponentPalette}
950 DebugLn(['TComponentPalette.ReAlignButtons Visible="',aSheet.Caption,'", ClientWidth=',aSheet.ClientWidth]);
951 {$ENDIF}
952 PageInd:=IndexOfPageComponent(aSheet);
953 if PageInd>=0 then
954 TComponentPage(Pages[PageInd]).ReAlignButtons;
955 end;
956
957 procedure TComponentPalette.RemoveUnneededPage(aSheet: TCustomPage);
958 var
959 PageInd: Integer;
960 begin
961 PageInd:=IndexOfPageComponent(aSheet);
962 if (PageInd>=0) and Pages[PageInd].Visible then
963 Exit;
964 // page is not needed anymore => delete
965 if PageInd>=0 then
966 TComponentPage(Pages[PageInd]).RemoveSheet;
967 if aSheet=fOldActivePage then
968 fOldActivePage:=nil;
969 aSheet.Visible:=false;
970 {$IFDEF VerboseComponentPalette}
971 if aSheet.Caption = CompPalVerbPgName then
972 DebugLn(['TComponentPalette.RemoveUnneededPage: Removing Page=', aSheet.Caption, ', index=', PageInd]);
973 {$ENDIF}
974 Application.ReleaseComponent(aSheet);
975 end;
976
977 procedure TComponentPalette.UpdateNoteBookButtons(ForceUpdateAll: Boolean);
978 var
979 i: Integer;
980 Pg: TComponentPage;
981 begin
982 if fUpdatingPageControl then exit;
983 Assert(not IsUpdateLocked, 'TComponentPalette.UpdateNoteBookButtons: IsUpdateLocked');
984 if FPageControl=Nil then exit;
985 // lock
986 fUpdatingPageControl:=true;
987 FPageControl.DisableAlign;
988 try
989 fOldActivePage:=FPageControl.ActivePage;
990 CreatePopupMenu;
991 {$IFDEF VerboseComponentPalette}
992 DebugLn(['TComponentPalette.UpdateNoteBookButtons: FPageCount before=', FPageControl.PageCount]);
993 {$ENDIF}
994 // remove every page in the PageControl without a visible page
995 for i:=FPageControl.PageCount-1 downto 0 do
996 RemoveUnneededPage(FPageControl.Pages[i]);
997
998 {$IFDEF VerboseComponentPalette}
999 DebugLn(['TComponentPalette.UpdateNoteBookButtons: FPageCount after=', FPageControl.PageCount,
1000 ' PageCount=', Pages.count]);
1001 {$ENDIF}
1002
1003 // Mark GUIs as not created. They will be created later when page gets selected.
1004 for i := 0 to Pages.Count-1 do
1005 TComponentPage(Pages[i]).fGuiCreated := False;
1006
1007 // insert a PageControl page for every visible palette page
1008 fVisiblePageIndex := 0;
1009 for i := 0 to Pages.Count-1 do
1010 begin
1011 // Pages and UserOrder.ComponentPages are now synchronized, same index applies.
1012 Assert(Pages[i].PageName=UserOrder.ComponentPages[i],
1013 'UpdateNoteBookButtons: Page names do not match.');
1014 Pg := TComponentPage(Pages[i]);
1015 {$IF DEFINED(LCLQt) OR DEFINED(LCLQt5)} // Qt has some problems in moving existing tabs!
1016 if Assigned(Pg.PageComponent) then begin
1017 Pg.PageComponent.Free;
1018 Pg.RemoveSheet;
1019 end;
1020 {$ENDIF}
1021 Pg.InsertVisiblePage(UserOrder.ComponentPages.Objects[i] as TRegisteredCompList);
1022 {$IFDEF VerboseComponentPalette}
1023 DebugLn(['TComponentPalette.UpdateNoteBookButtons: PageIndex=', i, ' PageName=',Pages[i].PageName]);
1024 {$ENDIF}
1025 end;
1026
1027 // OldActivePage can be invalid if a user defined page is just deleted.
1028 if Assigned(fOldActivePage) and (FPageControl.IndexOf(fOldActivePage) = -1) then
1029 fOldActivePage := Nil;
1030 for i := Pages.Count-1 downto 0 do
1031 begin
1032 Pg := TComponentPage(Pages[i]);
1033 // During IDE start create GUI only for the active page.
1034 if ((fOldActivePage=Nil) and (i=0)) // First page is activated by default.
1035 or (Pg.PageComponent=fOldActivePage) // Previous active page will be restored.
1036 or (ForceUpdateAll) then // Forced after changing configuration.
1037 Pg.ReAlignButtons;
1038 end;
1039 // restore active page
1040 if Assigned(fOldActivePage) then
1041 FPageControl.ActivePage:=fOldActivePage
1042 else if FPageControl.PageCount>0 then
1043 FPageControl.PageIndex:=0;
1044 finally
1045 // unlock
1046 fUpdatingPageControl:=false;
1047 FPageControl.EnableAlign;
1048 end;
1049 end;
1050
1051 procedure TComponentPalette.OnGetNonVisualCompIcon(Sender: TObject;
1052 AComponent: TComponent; var ImageList: TCustomImageList;
1053 var ImageIndex: TImageIndex);
1054 var
1055 ARegComp: TRegisteredComponent;
1056 begin
1057 if AComponent<>nil then
1058 ARegComp:=FindRegComponent(AComponent.ClassType)
1059 else
1060 ARegComp:=nil;
1061 if ARegComp<>nil then
1062 begin
1063 ImageList := TPkgComponent(ARegComp).Images;
1064 ImageIndex := TPkgComponent(ARegComp).ImageIndex;
1065 end else
1066 begin
1067 GetUnregisteredIcon(ImageList, ImageIndex);
1068 end;
1069 end;
1070
IndexOfPageComponentnull1071 function TComponentPalette.IndexOfPageComponent(AComponent: TComponent): integer;
1072 begin
1073 if AComponent<>nil then begin
1074 Result:=Pages.Count-1;
1075 while (Result>=0) and ((Pages[Result] as TComponentPage).PageComponent<>AComponent) do
1076 dec(Result);
1077 end else
1078 Result:=-1;
1079 end;
1080
FindCompByButtonnull1081 function TComponentPalette.FindCompByButton(Button: TSpeedButton): TRegisteredComponent;
1082 var
1083 CompClass: TComponentClass;
1084 begin
1085 CompClass := TComponentClass(fComponentButtons.FindByValue(Button));
1086 if Assigned(CompClass) then
1087 Result := FindRegComponent(CompClass)
1088 else
1089 Result := nil;
1090 end;
1091
TComponentPalette.FindPkgCompByButtonnull1092 function TComponentPalette.FindPkgCompByButton(Button: TComponent): TPkgComponent;
1093 begin
1094 Result := FindCompByButton(Button as TSpeedButton) as TPkgComponent;
1095 end;
1096
1097 end.
1098
1099