1{
2 *****************************************************************************
3  See the file COPYING.modifiedLGPL.txt, included in this distribution,
4  for details about the license.
5 *****************************************************************************
6
7  Author: Mattias Gaertner
8
9  Abstract:
10   This unit defines the TObjectInspectorDlg.
11   It uses TOIPropertyGrid and TOIPropertyGridRow which are also defined in this
12   unit. The object inspector uses property editors (see TPropertyEditor) to
13   display and control properties, thus the object inspector is merely an
14   object viewer than an editor. The property editors do the real work.
15
16  ToDo:
17   - backgroundcolor=clNone
18   - Define Init values
19   - Set to init value
20}
21unit ObjectInspector;
22
23{$Mode objfpc}{$H+}
24
25{off $DEFINE DoNotCatchOIExceptions}
26
27interface
28
29uses
30  // IMPORTANT: the object inspector is a tool and can be used in other programs
31  //            too. Don't put Lazarus IDE specific things here.
32  // RTL / FCL
33  Classes, SysUtils, Types, TypInfo, StrUtils, math,
34  // LCL
35  LCLPlatformDef, InterfaceBase, LCLType, LCLIntf, Forms, Buttons, Graphics,
36  StdCtrls, Controls, ComCtrls, ExtCtrls, Menus, Dialogs, Themes, LMessages,
37  ImgList, ActnList,
38  // LazControls
39  {$IFnDEF UseOINormalCheckBox} CheckBoxThemed, {$ENDIF}
40  TreeFilterEdit, ListFilterEdit,
41  // LazUtils
42  GraphType, LazConfigStorage, LazLoggerBase, LazStringUtils, LazUTF8,
43  // IdeIntf
44  IDEImagesIntf, IDEHelpIntf, ObjInspStrConsts,
45  PropEdits, PropEditUtils, ComponentTreeView, OIFavoriteProperties,
46  ComponentEditors, ChangeParentDlg;
47
48const
49  OIOptionsFileVersion = 3;
50
51  DefBackgroundColor = clBtnFace;
52  DefReferencesColor = clMaroon;
53  DefSubPropertiesColor = clGreen;
54  DefNameColor = clWindowText;
55  DefValueColor = clMaroon;
56  DefDefaultValueColor = clWindowText;
57  DefValueDifferBackgrndColor = $F0F0FF; // Sort of pink.
58  DefReadOnlyColor = clGrayText;
59  DefHighlightColor = clHighlight;
60  DefHighlightFontColor = clHighlightText;
61  DefGutterColor = DefBackgroundColor;
62  DefGutterEdgeColor = cl3DShadow;
63
64  DefaultOITypeKinds = [
65    tkUnknown, tkInteger, tkChar, tkEnumeration, tkFloat, tkSet,{ tkMethod,}
66    tkSString, tkLString, tkAString, tkWString, tkVariant,
67    {tkArray, tkRecord,} tkInterface, tkClass, tkObject, tkWChar, tkBool,
68    tkInt64, tkQWord, tkUString, tkUChar];
69type
70  EObjectInspectorException = class(Exception);
71
72  TObjectInspectorDlg = class;
73  TOICustomPropertyGrid = class;
74
75  // standard ObjectInspector pages
76  TObjectInspectorPage = (
77    oipgpProperties,
78    oipgpEvents,
79    oipgpFavorite,
80    oipgpRestricted
81    );
82  TObjectInspectorPages = set of TObjectInspectorPage;
83
84
85  { TOIOptions }
86
87  TOIOptions = class
88  private
89    FComponentTreeHeight: integer;
90    FConfigStore: TConfigStorage;
91    FDefaultItemHeight: integer;
92    FGutterColor: TColor;
93    FGutterEdgeColor: TColor;
94    FShowComponentTree: boolean;
95
96    FSaveBounds: boolean;
97    FLeft: integer;
98    FShowPropertyFilter: boolean;
99    FShowGutter: boolean;
100    FShowInfoBox: boolean;
101    FInfoBoxHeight: integer;
102    FShowStatusBar: boolean;
103    FTop: integer;
104    FWidth: integer;
105    FHeight: integer;
106    FGridSplitterX: array[TObjectInspectorPage] of integer;
107
108    FPropertyNameColor: TColor;
109    FSubPropertiesColor: TColor;
110    FValueColor: TColor;
111    FDefaultValueColor: TColor;
112    FValueDifferBackgrndColor: TColor;
113    FReadOnlyColor: TColor;
114    FReferencesColor: TColor;
115    FGridBackgroundColor: TColor;
116    FHighlightColor: TColor;
117    FHighlightFontColor: TColor;
118
119    FShowHints: Boolean;
120    FAutoShow: Boolean;
121    FCheckboxForBoolean: Boolean;
122    FBoldNonDefaultValues: Boolean;
123    FDrawGridLines: Boolean;
124    function FPropertyGridSplitterX(Page: TObjectInspectorPage): integer;
125    procedure FPropertyGridSplitterX(Page: TObjectInspectorPage;
126      const AValue: integer);
127  public
128    constructor Create;
129    function Load: boolean;
130    function Save: boolean;
131    procedure Assign(AnObjInspector: TObjectInspectorDlg);
132    procedure AssignTo(AnObjInspector: TObjectInspectorDlg); overload;
133    procedure AssignTo(AGrid: TOICustomPropertyGrid); overload;
134
135    property ConfigStore: TConfigStorage read FConfigStore write FConfigStore;
136
137    property SaveBounds:boolean read FSaveBounds write FSaveBounds;
138    property Left:integer read FLeft write FLeft;
139    property Top:integer read FTop write FTop;
140    property Width:integer read FWidth write FWidth;
141    property Height:integer read FHeight write FHeight;
142    property GridSplitterX[Page: TObjectInspectorPage]:integer
143                       read FPropertyGridSplitterX write FPropertyGridSplitterX;
144    property DefaultItemHeight: integer read FDefaultItemHeight
145                                        write FDefaultItemHeight;
146    property ShowComponentTree: boolean read FShowComponentTree
147                                        write FShowComponentTree;
148    property ComponentTreeHeight: integer read FComponentTreeHeight
149                                          write FComponentTreeHeight;
150
151    property GridBackgroundColor: TColor read FGridBackgroundColor write FGridBackgroundColor;
152    property SubPropertiesColor: TColor read FSubPropertiesColor write FSubPropertiesColor;
153    property ReferencesColor: TColor read FReferencesColor write FReferencesColor;
154    property ReadOnlyColor: TColor read FReadOnlyColor write FReadOnlyColor;
155    property ValueColor: TColor read FValueColor write FValueColor;
156    property DefaultValueColor: TColor read FDefaultValueColor write FDefaultValueColor;
157    property ValueDifferBackgrndColor: TColor read FValueDifferBackgrndColor write FValueDifferBackgrndColor;
158    property PropertyNameColor: TColor read FPropertyNameColor write FPropertyNameColor;
159    property HighlightColor: TColor read FHighlightColor write FHighlightColor;
160    property HighlightFontColor: TColor read FHighlightFontColor write FHighlightFontColor;
161    property GutterColor: TColor read FGutterColor write FGutterColor;
162    property GutterEdgeColor: TColor read FGutterEdgeColor write FGutterEdgeColor;
163
164    property ShowHints: boolean read FShowHints write FShowHints;
165    property AutoShow: boolean read FAutoShow write FAutoShow;
166    property CheckboxForBoolean: boolean read FCheckboxForBoolean write FCheckboxForBoolean;
167    property BoldNonDefaultValues: boolean read FBoldNonDefaultValues write FBoldNonDefaultValues;
168    property DrawGridLines: boolean read FDrawGridLines write FDrawGridLines;
169    property ShowPropertyFilter: boolean read FShowPropertyFilter write FShowPropertyFilter;
170    property ShowGutter: boolean read FShowGutter write FShowGutter;
171    property ShowStatusBar: boolean read FShowStatusBar write FShowStatusBar;
172    property ShowInfoBox: boolean read FShowInfoBox write FShowInfoBox;
173    property InfoBoxHeight: integer read FInfoBoxHeight write FInfoBoxHeight;
174  end;
175
176  { TOIPropertyGridRow }
177
178  TOIPropertyGridRow = class
179  private
180    FTop: integer;
181    FHeight: integer;
182    FLvl: integer;
183    FName: string;
184    FExpanded: boolean;
185    FTree: TOICustomPropertyGrid;
186    FChildCount:integer;
187    FPriorBrother,
188    FFirstChild,
189    FLastChild,
190    FNextBrother,
191    FParent: TOIPropertyGridRow;
192    FEditor: TPropertyEditor;
193    FWidgetSets: TLCLPlatforms;
194
195    FIndex:integer;
196    LastPaintedValue: string;
197
198    procedure GetLvl;
199  public
200    constructor Create(PropertyTree: TOICustomPropertyGrid;
201       PropEditor:TPropertyEditor; ParentNode:TOIPropertyGridRow; WidgetSets: TLCLPlatforms);
202    destructor Destroy; override;
203    function ConsistencyCheck: integer;
204    function HasChild(Row: TOIPropertyGridRow): boolean;
205    procedure WriteDebugReport(const Prefix: string);
206
207    function GetBottom: integer;
208    function IsReadOnly: boolean;
209    function IsDisabled: boolean;
210    procedure MeasureHeight(ACanvas: TCanvas);
211    function Sort(const Compare: TListSortCompare): boolean; // true if changed
212    function IsSorted(const Compare: TListSortCompare): boolean;
213    function Next: TOIPropertyGridRow;
214    function NextSkipChilds: TOIPropertyGridRow;
215
216    property Editor: TPropertyEditor read FEditor;
217    property Top: integer read FTop write FTop;
218    property Height: integer read FHeight write FHeight;
219    property Bottom: integer read GetBottom;
220    property Lvl: integer read FLvl;
221    property Name: string read FName;
222    property Expanded: boolean read FExpanded;
223    property Tree: TOICustomPropertyGrid read FTree;
224    property Parent: TOIPropertyGridRow read FParent;
225    property ChildCount: integer read FChildCount;
226    property FirstChild: TOIPropertyGridRow read FFirstChild;
227    property LastChild: TOIPropertyGridRow read FLastChild;
228    property NextBrother: TOIPropertyGridRow read FNextBrother;
229    property PriorBrother: TOIPropertyGridRow read FPriorBrother;
230    property Index: integer read FIndex;
231  end;
232
233  //----------------------------------------------------------------------------
234  TOIPropertyGridState = (
235    pgsChangingItemIndex,
236    pgsApplyingValue,
237    pgsUpdatingEditControl,
238    pgsBuildPropertyListNeeded,
239    pgsGetComboItemsCalled,
240    pgsIdleEnabled,
241    pgsCallingEdit,                 // calling property editor Edit
242    pgsFocusPropertyEditorDisabled  // by building PropertyList no editor should be focused
243    );
244  TOIPropertyGridStates = set of TOIPropertyGridState;
245
246  { TOICustomPropertyGrid }
247
248  TOICustomPropertyGridColumn = (
249    oipgcName,
250    oipgcValue
251  );
252
253  TOILayout = (
254   oilHorizontal,
255   oilVertical
256  );
257
258  TOIQuickEdit = (
259    oiqeEdit,
260    oiqeShowValue
261  );
262
263  TOIPropertyHintEvent = function(Sender: TObject; PointedRow: TOIPropertyGridRow;
264            out AHint: string): boolean of object;
265
266  TOIEditorFilterEvent = procedure(Sender: TObject; aEditor: TPropertyEditor;
267            var aShow: boolean) of object;
268
269  TOICustomPropertyGrid = class(TCustomControl)
270  private
271    FBackgroundColor: TColor;
272    FColumn: TOICustomPropertyGridColumn;
273    FGutterColor: TColor;
274    FGutterEdgeColor: TColor;
275    FHighlightColor: TColor;
276    FLayout: TOILayout;
277    FOnEditorFilter: TOIEditorFilterEvent;
278    FOnOIKeyDown: TKeyEvent;
279    FOnPropertyHint: TOIPropertyHintEvent;
280    FOnSelectionChange: TNotifyEvent;
281    FReferencesColor: TColor;
282    FReadOnlyColor: TColor;
283    FRowSpacing: integer;
284    FShowGutter: Boolean;
285    FCheckboxForBoolean: Boolean;
286    FSubPropertiesColor: TColor;
287    FChangeStep: integer;
288    FCurrentButton: TControl; // nil or ValueButton
289    FCurrentEdit: TWinControl;  // nil or ValueEdit or ValueComboBox or ValueCheckBox
290    FCurrentEditorLookupRoot: TPersistent;
291    FDefaultItemHeight:integer;
292    FDragging: boolean;
293    FExpandedProperties: TStringList;// used to restore expanded state when switching selected component(s)
294    FExpandingRow: TOIPropertyGridRow;
295    FFavorites: TOIFavoriteProperties;
296    FFilter: TTypeKinds;
297    FIndent: integer;
298    FItemIndex: integer;
299    FNameFont, FDefaultValueFont, FValueFont, FHighlightFont: TFont;
300    FValueDifferBackgrndColor: TColor;
301    FNewComboBoxItems: TStringListUTF8Fast;
302    FOnModified: TNotifyEvent;
303    FRows: TFPList;// list of TOIPropertyGridRow
304    FSelection: TPersistentSelectionList;
305    FNotificationComponents: TFPList;
306    FPropertyEditorHook: TPropertyEditorHook;
307    FPreferredSplitterX: integer; // best splitter position
308    FSplitterX: integer; // current splitter position
309    FStates: TOIPropertyGridStates;
310    FTopY: integer;
311    FDrawHorzGridLines: Boolean;
312    FActiveRowImages: TLCLGlyphs;
313    FFirstClickTime: DWORD;
314    FKeySearchText: string;
315    FHideClassNames: Boolean;
316    FPropNameFilter : String;
317    FPaintRc: TRect;
318
319    // hint stuff
320    FLongHintTimer: TTimer;
321    FHintManager: THintWindowManager;
322    FHintIndex: integer;
323    FHintType: TPropEditHint;
324    FShowingLongHint: boolean; // last hint was activated by the hinttimer
325
326    ValueEdit: TEdit;
327    ValueComboBox: TComboBox;
328    {$IFnDEF UseOINormalCheckBox}
329    ValueCheckBox: TCheckBoxThemed;
330    {$ELSE}
331    ValueCheckBox: TCheckBox;
332    {$ENDIF}
333    ValueButton: TSpeedButton;
334
335    procedure ActiveRowImagesGetWidthForPPI(Sender: TCustomImageList;
336      {%H-}AImageWidth, {%H-}APPI: Integer; var AResultWidth: Integer);
337    procedure HintMouseLeave(Sender: TObject);
338    procedure HintTimer(Sender: TObject);
339    procedure HideHint;
340    procedure HintMouseDown(Sender: TObject; Button: TMouseButton;
341                            Shift: TShiftState; X, Y: Integer);
342    procedure IncreaseChangeStep;
343    function GridIsUpdating: boolean;
344
345    function GetRow(Index:integer):TOIPropertyGridRow;
346    function GetRowCount:integer;
347    procedure ClearRows;
348    function GetCurrentEditValue: string;
349    procedure SetActiveControl(const AControl: TWinControl);
350    procedure SetCheckboxState(NewValue: string);
351    procedure SetColumn(const AValue: TOICustomPropertyGridColumn);
352    procedure SetCurrentEditValue(const NewValue: string);
353    procedure SetDrawHorzGridLines(const AValue: Boolean);
354    procedure SetFavorites(const AValue: TOIFavoriteProperties);
355    procedure SetFilter(const AValue: TTypeKinds);
356    procedure SetGutterColor(const AValue: TColor);
357    procedure SetGutterEdgeColor(const AValue: TColor);
358    procedure SetHighlightColor(const AValue: TColor);
359    procedure SetItemIndex(NewIndex:integer);
360    function IsCurrentEditorAvailable: Boolean;
361
362    function GetNameRowHeight: Integer; // temp solution untill TFont.height returns its actual value
363
364    procedure SetItemsTops;
365    procedure AlignEditComponents;
366    procedure EndDragSplitter;
367    procedure SetRowSpacing(const AValue: integer);
368    procedure SetShowGutter(const AValue: Boolean);
369    procedure SetSplitterX(const NewValue:integer);
370    procedure SetTopY(const NewValue:integer);
371
372    function GetPropNameColor(ARow: TOIPropertyGridRow):TColor;
373    function GetTreeIconX(Index: integer):integer;
374    function RowRect(ARow: integer):TRect;
375    procedure PaintRow(ARow: integer);
376    procedure DoPaint(PaintOnlyChangedValues: boolean);
377
378    procedure SetSelection(const ASelection:TPersistentSelectionList);
379    procedure SetPropertyEditorHook(NewPropertyEditorHook:TPropertyEditorHook);
380    procedure UpdateSelectionNotifications;
381    procedure HookGetCheckboxForBoolean(var Value: Boolean);
382
383    procedure AddPropertyEditor(PropEditor: TPropertyEditor);
384    procedure AddStringToComboBox(const s: string);
385    procedure ExpandRow(Index: integer);
386    procedure ShrinkRow(Index: integer);
387    procedure AddSubEditor(PropEditor: TPropertyEditor);
388    procedure SortSubEditors(ParentRow: TOIPropertyGridRow);
389    function CanExpandRow(Row: TOIPropertyGridRow): boolean;
390
391    procedure SetRowValue(CheckFocus, ForceValue: boolean);
392    procedure DoCallEdit(Edit: TOIQuickEdit = oiqeEdit);
393    procedure RefreshValueEdit;
394    procedure ToggleRow;
395    procedure ValueEditDblClick(Sender : TObject);
396    procedure ValueControlMouseDown(Sender: TObject; {%H-}Button:TMouseButton;
397      {%H-}Shift: TShiftState; {%H-}X,{%H-}Y:integer);
398    procedure ValueControlMouseMove(Sender: TObject; {%H-}Shift: TShiftState;
399      {%H-}X,{%H-}Y:integer);
400    procedure ValueEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
401    procedure ValueEditKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
402    procedure ValueEditExit(Sender: TObject);
403    procedure ValueEditChange(Sender: TObject);
404    procedure ValueEditMouseUp(Sender: TObject; Button: TMouseButton;
405                               Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
406    procedure ValueCheckBoxKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
407    procedure ValueCheckBoxKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
408    procedure ValueCheckBoxExit(Sender: TObject);
409    procedure ValueCheckBoxClick(Sender: TObject);
410    procedure ValueComboBoxExit(Sender: TObject);
411    procedure ValueComboBoxKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
412    procedure ValueComboBoxKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
413    procedure ValueComboBoxMouseUp(Sender: TObject; Button: TMouseButton;
414                                   Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
415    procedure ValueComboBoxCloseUp(Sender: TObject);
416    procedure ValueComboBoxGetItems(Sender: TObject);
417    procedure ValueButtonClick(Sender: TObject);
418    procedure ValueComboBoxMeasureItem({%H-}Control: TWinControl; Index: Integer;
419          var AHeight: Integer);
420    procedure ValueComboBoxDrawItem({%H-}Control: TWinControl; Index: Integer;
421          ARect: TRect; State: TOwnerDrawState);
422    procedure OnIdle(Sender: TObject; var {%H-}Done: Boolean);
423    procedure SetIdleEvent(Enable: boolean);
424    procedure OnGridMouseWheel(Sender: TObject; {%H-}Shift: TShiftState;
425      WheelDelta: Integer; {%H-}MousePos: TPoint; var Handled: Boolean);
426
427    procedure WMVScroll(var Msg: TLMScroll); message LM_VSCROLL;
428    procedure SetBackgroundColor(const AValue: TColor);
429    procedure SetReferences(const AValue: TColor);
430    procedure SetSubPropertiesColor(const AValue: TColor);
431    procedure SetReadOnlyColor(const AValue: TColor);
432    procedure SetValueDifferBackgrndColor(AValue: TColor);
433    procedure UpdateScrollBar;
434    function FillComboboxItems: boolean; // true if something changed
435    function EditorFilter(const AEditor: TPropertyEditor): Boolean;
436  protected
437    procedure CreateParams(var Params: TCreateParams); override;
438    procedure CreateWnd; override;
439    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
440
441    function DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean; override;
442    procedure MouseDown(Button:TMouseButton; Shift:TShiftState; X,Y:integer); override;
443    procedure MouseMove(Shift:TShiftState; X,Y:integer);  override;
444    procedure MouseUp(Button:TMouseButton; Shift:TShiftState; X,Y:integer); override;
445    procedure MouseLeave; override;
446
447    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
448    procedure HandleStandardKeys(var Key: Word; Shift: TShiftState); virtual;
449    procedure HandleKeyUp(var Key: Word; Shift: TShiftState); virtual;
450    procedure DoTabKey; virtual;
451    procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;
452    procedure DoSelectionChange;
453  public
454    constructor Create(TheOwner: TComponent); override;
455    constructor CreateWithParams(AnOwner: TComponent;
456                                 APropertyEditorHook: TPropertyEditorHook;
457                                 TypeFilter: TTypeKinds;
458                                 DefItemHeight: integer);
459    destructor Destroy;  override;
460    function CanEditRowValue(CheckFocus: boolean): boolean;
461    procedure FocusCurrentEditor;
462    procedure SaveChanges;
463    function ConsistencyCheck: integer;
464    procedure EraseBackground({%H-}DC: HDC); override;
465    function GetActiveRow: TOIPropertyGridRow;
466    function GetHintTypeAt(RowIndex: integer; X: integer): TPropEditHint;
467
468    function GetRowByPath(const PropPath: string): TOIPropertyGridRow;
469    function GridHeight: integer;
470    function RealDefaultItemHeight: integer;
471    function MouseToIndex(y: integer; MustExist: boolean): integer;
472    function PropertyPath(Index: integer):string;
473    function PropertyPath(Row: TOIPropertyGridRow):string;
474    function PropertyEditorByName(const PropName: string): TPropertyEditor;
475    function TopMax: integer;
476    procedure BuildPropertyList(OnlyIfNeeded: Boolean = False; FocusEditor: Boolean = True);
477    procedure Clear;
478    procedure Paint; override;
479    procedure PropEditLookupRootChange;
480    procedure RefreshPropertyValues;
481    procedure ScrollToActiveItem;
482    procedure ScrollToItem(NewIndex: Integer);
483    procedure SetBounds(aLeft, aTop, aWidth, aHeight: integer); override;
484    procedure SetCurrentRowValue(const NewValue: string);
485    procedure SetItemIndexAndFocus(NewItemIndex: integer;
486                                   WasValueClick: Boolean = False);
487
488    property BackgroundColor: TColor read FBackgroundColor
489                                     write SetBackgroundColor default DefBackgroundColor;
490    property GutterColor: TColor read FGutterColor write SetGutterColor default DefGutterColor;
491    property GutterEdgeColor: TColor read FGutterEdgeColor write SetGutterEdgeColor default DefGutterEdgeColor;
492    property HighlightColor: TColor read FHighlightColor write SetHighlightColor default DefHighlightColor;
493    property ReferencesColor: TColor read FReferencesColor
494                                     write SetReferences default DefReferencesColor;
495    property SubPropertiesColor: TColor read FSubPropertiesColor
496                                     write SetSubPropertiesColor default DefSubPropertiesColor;
497    property ReadOnlyColor: TColor read FReadOnlyColor
498                                     write SetReadOnlyColor default DefReadOnlyColor;
499    property ValueDifferBackgrndColor: TColor read FValueDifferBackgrndColor
500           write SetValueDifferBackgrndColor default DefValueDifferBackgrndColor;
501
502    property NameFont: TFont read FNameFont write FNameFont;
503    property DefaultValueFont: TFont read FDefaultValueFont write FDefaultValueFont;
504    property ValueFont: TFont read FValueFont write FValueFont;
505    property HighlightFont: TFont read FHighlightFont write FHighlightFont;
506
507    property BorderStyle default bsSingle;
508    property Column: TOICustomPropertyGridColumn read FColumn write SetColumn;
509    property CurrentEditValue: string read GetCurrentEditValue
510                                      write SetCurrentEditValue;
511    property DefaultItemHeight:integer read FDefaultItemHeight
512                                       write FDefaultItemHeight default 0;
513    property DrawHorzGridLines: Boolean read FDrawHorzGridLines write
514      SetDrawHorzGridLines default True;
515    property ExpandedProperties: TStringList read FExpandedProperties;
516    property Indent: integer read FIndent write FIndent;
517    property ItemIndex: integer read FItemIndex write SetItemIndex;
518    property Layout: TOILayout read FLayout write FLayout default oilHorizontal;
519    property OnEditorFilter: TOIEditorFilterEvent read FOnEditorFilter write FOnEditorFilter;
520    property OnModified: TNotifyEvent read FOnModified write FOnModified;
521    property OnOIKeyDown: TKeyEvent read FOnOIKeyDown write FOnOIKeyDown;
522    property OnSelectionChange: TNotifyEvent read FOnSelectionChange write FOnSelectionChange;
523    property OnPropertyHint: TOIPropertyHintEvent read FOnPropertyHint write FOnPropertyHint;
524    property PropertyEditorHook: TPropertyEditorHook read FPropertyEditorHook
525                                                    write SetPropertyEditorHook;
526    property RowCount: integer read GetRowCount;
527    property Rows[Index: integer]: TOIPropertyGridRow read GetRow;
528    property RowSpacing: integer read FRowSpacing write SetRowSpacing;
529    property Selection: TPersistentSelectionList read FSelection write SetSelection;
530    property ShowGutter: Boolean read FShowGutter write SetShowGutter default True;
531    property CheckboxForBoolean: Boolean read FCheckboxForBoolean write FCheckboxForBoolean;
532    property PreferredSplitterX: integer read FPreferredSplitterX
533                                         write FPreferredSplitterX default 100;
534    property SplitterX: integer read FSplitterX write SetSplitterX default 100;
535    property TopY: integer read FTopY write SetTopY default 0;
536    property Favorites: TOIFavoriteProperties read FFavorites write SetFavorites;
537    property Filter : TTypeKinds read FFilter write SetFilter;
538    property HideClassNames: Boolean read FHideClassNames write FHideClassNames;
539    property PropNameFilter : String read FPropNameFilter write FPropNameFilter;
540  end;
541
542
543  { TOIPropertyGrid }
544
545  TOIPropertyGrid = class(TOICustomPropertyGrid)
546  published
547    property Align;
548    property Anchors;
549    property BackgroundColor;
550    property BorderStyle;
551    property Constraints;
552    property DefaultItemHeight;
553    property DefaultValueFont;
554    property Indent;
555    property NameFont;
556    property OnChangeBounds;
557    property OnClick;
558    property OnDblClick;
559    property OnEnter;
560    property OnExit;
561    property OnKeyDown;
562    property OnKeyPress;
563    property OnKeyUp;
564    property OnModified;
565    property OnMouseDown;
566    property OnMouseEnter;
567    property OnMouseLeave;
568    property OnMouseMove;
569    property OnMouseUp;
570    property OnResize;
571    property OnSelectionChange;
572    property PopupMenu;
573    property PreferredSplitterX;
574    property SplitterX;
575    property Tabstop;
576    property ValueFont;
577    property Visible;
578  end;
579
580
581  { TCustomPropertiesGrid }
582
583  TCustomPropertiesGrid = class(TOICustomPropertyGrid)
584  private
585    FAutoFreeHook: boolean;
586    FSaveOnChangeTIObject: boolean;
587    function GetTIObject: TPersistent;
588    procedure SetAutoFreeHook(const AValue: boolean);
589    procedure SetTIObject(const AValue: TPersistent);
590  public
591    constructor Create(TheOwner: TComponent); override;
592    destructor Destroy; override;
593    property TIObject: TPersistent read GetTIObject write SetTIObject;
594    property AutoFreeHook: boolean read FAutoFreeHook write SetAutoFreeHook;
595    property SaveOnChangeTIObject: boolean read FSaveOnChangeTIObject
596                                           write FSaveOnChangeTIObject
597                                           default true;
598  end;
599
600
601  //============================================================================
602
603  TAddAvailablePersistentEvent = procedure(APersistent: TPersistent;
604    var Allowed: boolean) of object;
605  //copy of TGetPersistentImageIndexEvent
606  TOnOINodeGetImageEvent = procedure(APersistent: TPersistent;
607    var AImageIndex: integer) of object;
608
609  TOIFlag = (
610    oifRebuildPropListsNeeded
611    );
612  TOIFlags = set of TOIFlag;
613
614  { TObjectInspectorDlg }
615
616  TObjectInspectorDlg = class(TForm)
617    MainPopupMenu: TPopupMenu;
618    AvailPersistentComboBox: TComboBox;
619    ComponentPanel: TPanel;
620    CompFilterLabel: TLabel;
621    CompFilterEdit: TTreeFilterEdit;
622    PnlClient: TPanel;
623    StatusBar: TStatusBar;
624    procedure FormResize(Sender: TObject);
625    procedure MainPopupMenuClose(Sender: TObject);
626    procedure MainPopupMenuPopup(Sender: TObject);
627    procedure AvailComboBoxCloseUp(Sender: TObject);
628  private
629    // These are created at run-time, no need for default published section.
630    PropertyPanel: TPanel;
631    PropFilterPanel: TPanel;
632    PropFilterLabel: TLabel;
633    PropFilterEdit: TListFilterEdit;
634    RestrictedPanel: TPanel;
635    RestrictedInnerPanel: TPanel;
636    WidgetSetsRestrictedLabel: TLabel;
637    WidgetSetsRestrictedBox: TPaintBox;
638    ComponentRestrictedLabel: TLabel;
639    ComponentRestrictedBox: TPaintBox;
640    NoteBook: TPageControl;
641    Splitter1: TSplitter;
642    Splitter2: TSplitter;
643    // MenuItems
644    AddToFavoritesPopupMenuItem: TMenuItem;
645    ViewRestrictedPropertiesPopupMenuItem: TMenuItem;
646    CopyPopupmenuItem: TMenuItem;
647    CutPopupmenuItem: TMenuItem;
648    PastePopupmenuItem: TMenuItem;
649    DeletePopupmenuItem: TMenuItem;
650    ChangeClassPopupmenuItem: TMenuItem;
651    ChangeParentPopupmenuItem: TMenuItem;
652    FindDeclarationPopupmenuItem: TMenuItem;
653    OptionsSeparatorMenuItem: TMenuItem;
654    OptionsSeparatorMenuItem2: TMenuItem;
655    OptionsSeparatorMenuItem3: TMenuItem;
656    RemoveFromFavoritesPopupMenuItem: TMenuItem;
657    ShowComponentTreePopupMenuItem: TMenuItem;
658    ShowPropertyFilterPopupMenuItem: TMenuItem;
659    ShowHintsPopupMenuItem: TMenuItem;
660    ShowInfoBoxPopupMenuItem: TMenuItem;
661    ShowStatusBarPopupMenuItem: TMenuItem;
662    ShowOptionsPopupMenuItem: TMenuItem;
663    UndoPropertyPopupMenuItem: TMenuItem;
664    // Variables
665    FAutoShow: Boolean;
666    FCheckboxForBoolean: Boolean;
667    FComponentEditor: TBaseComponentEditor;
668    FDefaultItemHeight: integer;
669    FEnableHookGetSelection: boolean;
670    FFavorites: TOIFavoriteProperties;
671    FFilter: TTypeKinds;
672    FFlags: TOIFlags;
673    FInfoBoxHeight: integer;
674    FLastActiveRowName: String;
675    FPropertyEditorHook: TPropertyEditorHook;
676    FPropFilterUpdating: Boolean;
677    FRefreshingSelectionCount: integer;
678    FRestricted: TOIRestrictedProperties;
679    FSelection: TPersistentSelectionList;
680    FSettingSelectionCount: integer;
681    FShowComponentTree: Boolean;
682    FShowPropertyFilter: boolean;
683    FShowFavorites: Boolean;
684    FShowInfoBox: Boolean;
685    FShowRestricted: Boolean;
686    FShowStatusBar: Boolean;
687    FStateOfHintsOnMainPopupMenu: Boolean;
688    FUpdateLock: integer;
689    FUpdatingAvailComboBox: Boolean;
690    // Events
691    FOnAddAvailablePersistent: TAddAvailablePersistentEvent;
692    FOnAddToFavorites: TNotifyEvent;
693    FOnAutoShow: TNotifyEvent;
694    FOnFindDeclarationOfProperty: TNotifyEvent;
695    FOnModified: TNotifyEvent;
696    FOnNodeGetImageIndex: TOnOINodeGetImageEvent;
697    FOnOIKeyDown: TKeyEvent;
698    FOnPropertyHint: TOIPropertyHintEvent;
699    FOnRemainingKeyDown: TKeyEvent;
700    FOnRemainingKeyUp: TKeyEvent;
701    FOnRemoveFromFavorites: TNotifyEvent;
702    FOnSelectionChange: TNotifyEvent;
703    FOnSelectPersistentsInOI: TNotifyEvent;
704    FOnShowOptions: TNotifyEvent;
705    FOnUpdateRestricted: TNotifyEvent;
706    FOnViewRestricted: TNotifyEvent;
707    FLastTreeSize: TRect;
708
709    // These event handlers are assigned at run-time, no need for default published section.
710    procedure ComponentTreeDblClick(Sender: TObject);
711    procedure ComponentTreeGetNodeImageIndex(APersistent: TPersistent; var AIndex: integer);
712    procedure ComponentTreeKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
713    procedure ComponentTreeSelectionChanged(Sender: TObject);
714    procedure GridKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
715    procedure GridKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
716    procedure GridDblClick(Sender: TObject);
717    procedure GridModified(Sender: TObject);
718    procedure GridSelectionChange(Sender: TObject);
719    function GridPropertyHint(Sender: TObject; PointedRow: TOIPropertyGridRow;
720      out AHint: string): boolean;
721    procedure PropEditPopupClick(Sender: TObject);
722    procedure AddToFavoritesPopupmenuItemClick(Sender: TObject);
723    procedure RemoveFromFavoritesPopupmenuItemClick(Sender: TObject);
724    procedure ViewRestrictionsPopupmenuItemClick(Sender: TObject);
725    procedure UndoPopupmenuItemClick(Sender: TObject);
726    procedure FindDeclarationPopupmenuItemClick(Sender: TObject);
727    procedure CutPopupmenuItemClick(Sender: TObject);
728    procedure CopyPopupmenuItemClick(Sender: TObject);
729    procedure PastePopupmenuItemClick(Sender: TObject);
730    procedure DeletePopupmenuItemClick(Sender: TObject);
731    procedure ChangeClassPopupmenuItemClick(Sender: TObject);
732    procedure ComponentTreeModified(Sender: TObject);
733    procedure ShowComponentTreePopupMenuItemClick(Sender: TObject);
734    procedure ShowPropertyFilterPopupMenuItemClick(Sender: TObject);
735    procedure ShowHintPopupMenuItemClick(Sender: TObject);
736    procedure ShowInfoBoxPopupMenuItemClick(Sender: TObject);
737    procedure ShowStatusBarPopupMenuItemClick(Sender: TObject);
738    procedure ShowOptionsPopupMenuItemClick(Sender: TObject);
739    procedure RestrictedPageShow(Sender: TObject);
740    procedure WidgetSetRestrictedPaint(Sender: TObject);
741    procedure ComponentRestrictedPaint(Sender: TObject);
742    procedure PropFilterEditAfterFilter(Sender: TObject);
743    procedure NoteBookPageChange(Sender: TObject);
744    procedure ChangeParentItemClick(Sender: TObject);
745    procedure CollectionAddItem(Sender: TObject);
746    procedure ComponentEditorVerbMenuItemClick(Sender: TObject);
747    procedure ZOrderItemClick(Sender: TObject);
748    procedure TopSplitterMoved(Sender: TObject);
749    // Methods
750    procedure DoModified;
751    procedure DoUpdateRestricted;
752    procedure DoViewRestricted;
753    function GetComponentPanelHeight: integer;
754    function GetGridControl(Page: TObjectInspectorPage): TOICustomPropertyGrid;
755    function GetInfoBoxHeight: integer;
756    function GetParentCandidates: TFPList;
757    function GetSelectedPersistent: TPersistent;
758    function GetComponentEditorForSelection: TBaseComponentEditor;
759    procedure CreateBottomSplitter;
760    procedure CreateTopSplitter;
761    procedure DefSelectionVisibleInDesigner;
762    procedure RestrictedPaint(
763      ABox: TPaintBox; const ARestrictions: TWidgetSetRestrictionsArray);
764    function PersistentToString(APersistent: TPersistent): string;
765    procedure AddPersistentToList(APersistent: TPersistent; List: TStrings);
766    procedure HookLookupRootChange;
767    procedure HookRefreshPropertyValues;
768    procedure FillPersistentComboBox;
769    procedure SetAvailComboBoxText;
770    procedure HookGetSelection(const ASelection: TPersistentSelectionList);
771    procedure HookSetSelection(const ASelection: TPersistentSelectionList);
772    procedure DestroyNoteBook;
773    procedure CreateNoteBook;
774    procedure ShowNextPage(Delta: integer);
775    // Setter
776    procedure SetComponentEditor(const AValue: TBaseComponentEditor);
777    procedure SetComponentPanelHeight(const AValue: integer);
778    procedure SetDefaultItemHeight(const AValue: integer);
779    procedure SetEnableHookGetSelection(AValue: boolean);
780    procedure SetFavorites(const AValue: TOIFavoriteProperties);
781    procedure SetFilter(const AValue: TTypeKinds);
782    procedure SetInfoBoxHeight(const AValue: integer);
783    procedure SetOnShowOptions(const AValue: TNotifyEvent);
784    procedure SetPropertyEditorHook(const AValue: TPropertyEditorHook);
785    procedure SetRestricted(const AValue: TOIRestrictedProperties);
786    procedure SetSelection(const ASelection: TPersistentSelectionList);
787    procedure SetShowComponentTree(const AValue: boolean);
788    procedure SetShowPropertyFilter(const AValue: Boolean);
789    procedure SetShowFavorites(const AValue: Boolean);
790    procedure SetShowInfoBox(const AValue: Boolean);
791    procedure SetShowRestricted(const AValue: Boolean);
792    procedure SetShowStatusBar(const AValue: Boolean);
793  protected
794    function CanDeleteSelection: Boolean;
795    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
796    procedure KeyUp(var Key: Word; Shift: TShiftState); override;
797    procedure Resize; override;
798  public
799    // These are created at run-time, no need for default published section.
800    ComponentTree: TComponentTreeView;
801    InfoPanel: TPanel;
802    EventGrid: TOICustomPropertyGrid;
803    FavoriteGrid: TOICustomPropertyGrid;
804    RestrictedGrid: TOICustomPropertyGrid;
805    PropertyGrid: TOICustomPropertyGrid;
806    //
807    constructor Create(AnOwner: TComponent); override;
808    destructor Destroy; override;
809    procedure RefreshSelection;
810    procedure RefreshComponentTreeSelection;
811    procedure SaveChanges;
812    procedure RefreshPropertyValues;
813    procedure RebuildPropertyLists;
814    procedure ChangeCompZOrderInList(APersistent: TPersistent; AZOrder: TZOrderDelete);
815    procedure DeleteCompFromList(APersistent: TPersistent);
816    procedure FillComponentList(AWholeTree: Boolean);
817    procedure UpdateComponentValues;
818    procedure BeginUpdate;
819    procedure EndUpdate;
820    function GetActivePropertyGrid: TOICustomPropertyGrid;
821    function GetActivePropertyRow: TOIPropertyGridRow;
822    function GetCurRowDefaultValue(var DefaultStr: string): Boolean;
823    function HasParentCandidates: Boolean;
824    procedure ChangeParent;
825    procedure ActivateGrid(Grid: TOICustomPropertyGrid);
826    procedure FocusGrid(Grid: TOICustomPropertyGrid = nil);
827  public
828    property ComponentEditor: TBaseComponentEditor read FComponentEditor write SetComponentEditor;
829    property ComponentPanelHeight: integer read GetComponentPanelHeight
830                                          write SetComponentPanelHeight;
831    property DefaultItemHeight: integer read FDefaultItemHeight
832                                        write SetDefaultItemHeight;
833    property EnableHookGetSelection: Boolean read FEnableHookGetSelection
834                                             write SetEnableHookGetSelection;
835    property Favorites: TOIFavoriteProperties read FFavorites write SetFavorites;
836    property Filter: TTypeKinds read FFilter write SetFilter;
837    property GridControl[Page: TObjectInspectorPage]: TOICustomPropertyGrid
838                                                            read GetGridControl;
839    property InfoBoxHeight: integer read GetInfoBoxHeight write SetInfoBoxHeight;
840    property PropertyEditorHook: TPropertyEditorHook read FPropertyEditorHook
841                                                    write SetPropertyEditorHook;
842    property RestrictedProps: TOIRestrictedProperties read FRestricted write SetRestricted;
843    property Selection: TPersistentSelectionList read FSelection write SetSelection;
844    property AutoShow: Boolean read FAutoShow write FAutoShow;
845    property ShowComponentTree: Boolean read FShowComponentTree write SetShowComponentTree;
846    property ShowPropertyFilter: boolean read FShowPropertyFilter write SetShowPropertyFilter;
847    property ShowFavorites: Boolean read FShowFavorites write SetShowFavorites;
848    property ShowInfoBox: Boolean read FShowInfoBox write SetShowInfoBox;
849    property ShowRestricted: Boolean read FShowRestricted write SetShowRestricted;
850    property ShowStatusBar: Boolean read FShowStatusBar write SetShowStatusBar;
851    property LastActiveRowName: string read FLastActiveRowName;
852    // Events
853    property OnAddAvailPersistent: TAddAvailablePersistentEvent
854                 read FOnAddAvailablePersistent write FOnAddAvailablePersistent;
855    property OnAddToFavorites: TNotifyEvent read FOnAddToFavorites write FOnAddToFavorites;
856    property OnAutoShow: TNotifyEvent read FOnAutoShow write FOnAutoShow;
857    property OnFindDeclarationOfProperty: TNotifyEvent read FOnFindDeclarationOfProperty
858                                                      write FOnFindDeclarationOfProperty;
859    property OnModified: TNotifyEvent read FOnModified write FOnModified;
860    property OnOIKeyDown: TKeyEvent read FOnOIKeyDown write FOnOIKeyDown;
861    property OnPropertyHint: TOIPropertyHintEvent read FOnPropertyHint write FOnPropertyHint;
862    property OnRemainingKeyDown: TKeyEvent read FOnRemainingKeyDown
863                                         write FOnRemainingKeyDown;
864    property OnRemainingKeyUp: TKeyEvent read FOnRemainingKeyUp
865                                         write FOnRemainingKeyUp;
866    property OnRemoveFromFavorites: TNotifyEvent read FOnRemoveFromFavorites
867                                                  write FOnRemoveFromFavorites;
868    property OnSelectionChange: TNotifyEvent read FOnSelectionChange write FOnSelectionChange;
869    property OnSelectPersistentsInOI: TNotifyEvent read FOnSelectPersistentsInOI
870                                                  write FOnSelectPersistentsInOI;
871    property OnShowOptions: TNotifyEvent read FOnShowOptions write SetOnShowOptions;
872    property OnUpdateRestricted: TNotifyEvent read FOnUpdateRestricted
873                                         write FOnUpdateRestricted;
874    property OnViewRestricted: TNotifyEvent read FOnViewRestricted write FOnViewRestricted;
875    property OnNodeGetImageIndex : TOnOINodeGetImageEvent read FOnNodeGetImageIndex
876                                                         write FOnNodeGetImageIndex;
877  end;
878
879const
880  DefaultObjectInspectorName: string = 'ObjectInspectorDlg';
881
882// the ObjectInspector descendant of the IDE can be found in FormEditingIntf
883
884function dbgs(s: TOIPropertyGridState): string; overload;
885function dbgs(States: TOIPropertyGridStates): string; overload;
886
887function GetChangeParentCandidates(PropertyEditorHook: TPropertyEditorHook;
888  Selection: TPersistentSelectionList): TFPList;
889
890const
891  DefaultOIPageNames: array[TObjectInspectorPage] of shortstring = (
892    'PropertyPage',
893    'EventPage',
894    'FavoritePage',
895    'RestrictedPage'
896    );
897  DefaultOIGridNames: array[TObjectInspectorPage] of shortstring = (
898    'PropertyGrid',
899    'EventGrid',
900    'FavoriteGrid',
901    'RestrictedGrid'
902    );
903
904implementation
905
906{$R *.lfm}
907{$R images\ideintf_images.res}
908
909function SortGridRows(Item1, Item2 : pointer) : integer;
910begin
911  Result:=CompareText(TOIPropertyGridRow(Item1).Name,
912                      TOIPropertyGridRow(Item2).Name);
913end;
914
915function dbgs(s: TOIPropertyGridState): string;
916begin
917  Result:=GetEnumName(TypeInfo(s),ord(s));
918end;
919
920function dbgs(States: TOIPropertyGridStates): string;
921var
922  s: TOIPropertyGridState;
923begin
924  Result:='';
925  for s in States do
926  begin
927    if not (s in States) then continue;
928    if Result<>'' then Result+=',';
929    Result+=dbgs(s);
930  end;
931  Result:='['+Result+']';
932end;
933
934function GetChangeParentCandidates(PropertyEditorHook: TPropertyEditorHook;
935  Selection: TPersistentSelectionList): TFPList;
936
937  function CanBeParent(Child, Parent: TPersistent): boolean;
938  begin
939    Result:=false;
940    if Child = Parent then exit;
941    if not (Parent is TWinControl) then exit;
942    if not (Child is TControl) then exit;
943    if (Child is TWinControl) and
944       (Child = TWinControl(Parent).Parent) then
945      exit;
946    if not ControlAcceptsStreamableChildComponent(TWinControl(Parent),
947             TComponentClass(Child.ClassType), PropertyEditorHook.LookupRoot)
948    then
949      exit;
950    try
951      TControl(Child).CheckNewParent(TWinControl(Parent));
952    except
953      exit;
954    end;
955    Result:=true;
956  end;
957
958  function CanBeParentOfSelection(Parent: TPersistent): boolean;
959  var
960    i: Integer;
961  begin
962    for i:=0 to Selection.Count-1 do
963      if not CanBeParent(Selection[i],Parent) then exit(false);
964    Result:=true;
965  end;
966
967var
968  i: Integer;
969  Candidate: TWinControl;
970begin
971  Result := TFPList.Create;
972  if not (PropertyEditorHook.LookupRoot is TWinControl) then
973    exit; // only LCL controls are supported at the moment
974
975  // check if any selected control can be moved
976  i := Selection.Count-1;
977  while i >= 0 do
978  begin
979    if (Selection[i] is TControl)
980    and (TControl(Selection[i]).Owner = PropertyEditorHook.LookupRoot)
981    then
982      // this one can be moved
983      break;
984    dec(i);
985  end;
986  if i < 0 then Exit;
987
988  // find possible new parents
989  for i := 0 to TWinControl(PropertyEditorHook.LookupRoot).ComponentCount-1 do
990  begin
991    Candidate := TWinControl(TWinControl(PropertyEditorHook.LookupRoot).Components[i]);
992    if CanBeParentOfSelection(Candidate) then
993      Result.Add(Candidate);
994  end;
995  if CanBeParentOfSelection(PropertyEditorHook.LookupRoot) then
996    Result.Add(PropertyEditorHook.LookupRoot);
997end;
998
999{ TOICustomPropertyGrid }
1000
1001constructor TOICustomPropertyGrid.CreateWithParams(AnOwner:TComponent;
1002  APropertyEditorHook:TPropertyEditorHook; TypeFilter:TTypeKinds; DefItemHeight: integer);
1003var
1004  Details: TThemedElementDetails;
1005begin
1006  inherited Create(AnOwner);
1007  FLayout := oilHorizontal;
1008
1009  FSelection:=TPersistentSelectionList.Create;
1010  FNotificationComponents:=TFPList.Create;
1011  PropertyEditorHook:=APropertyEditorHook;  // Through property setter.
1012  FFilter:=TypeFilter;
1013  FItemIndex:=-1;
1014  FStates:=[];
1015  FColumn := oipgcValue;
1016  FRows:=TFPList.Create;
1017  FExpandingRow:=nil;
1018  FDragging:=false;
1019  FExpandedProperties:=TStringList.Create;
1020  FCurrentEdit:=nil;
1021  FCurrentButton:=nil;
1022
1023  // visible values
1024  FTopY:=0;
1025  FSplitterX:=100;
1026  FPreferredSplitterX:=FSplitterX;
1027  Details := ThemeServices.GetElementDetails(ttGlyphOpened);
1028  FIndent := ThemeServices.GetDetailSize(Details).cx;
1029
1030  FBackgroundColor:=DefBackgroundColor;
1031  FReferencesColor:=DefReferencesColor;
1032  FSubPropertiesColor:=DefSubPropertiesColor;
1033  FReadOnlyColor:=DefReadOnlyColor;
1034  FHighlightColor:=DefHighlightColor;
1035  FGutterColor:=DefGutterColor;
1036  FGutterEdgeColor:=DefGutterEdgeColor;
1037  FValueDifferBackgrndColor:=DefValueDifferBackgrndColor;
1038
1039  FNameFont:=TFont.Create;
1040  FNameFont.Color:=DefNameColor;
1041  FValueFont:=TFont.Create;
1042  FValueFont.Color:=DefValueColor;
1043  FDefaultValueFont:=TFont.Create;
1044  FDefaultValueFont.Color:=DefDefaultValueColor;
1045  FHighlightFont:=TFont.Create;
1046  FHighlightFont.Color:=DefHighlightFontColor;
1047
1048  FDrawHorzGridLines := True;
1049  FShowGutter := True;
1050
1051  SetInitialBounds(0,0,200,130);
1052  ControlStyle:=ControlStyle+[csAcceptsControls,csOpaque];
1053  BorderWidth:=0;
1054  BorderStyle := bsSingle;
1055
1056  // create sub components
1057  ValueEdit:=TEdit.Create(Self);
1058  with ValueEdit do
1059  begin
1060    Name:='ValueEdit';
1061    Visible:=false;
1062    Enabled:=false;
1063    AutoSize:=false;
1064    SetBounds(0,-30,80,25); // hidden
1065    Parent:=Self;
1066    OnMouseDown := @ValueControlMouseDown;
1067    OnMouseMove := @ValueControlMouseMove;
1068    OnDblClick := @ValueEditDblClick;
1069    OnExit:=@ValueEditExit;
1070    OnChange:=@ValueEditChange;
1071    OnKeyDown:=@ValueEditKeyDown;
1072    OnKeyUp:=@ValueEditKeyUp;
1073    OnMouseUp:=@ValueEditMouseUp;
1074    OnMouseWheel:=@OnGridMouseWheel;
1075  end;
1076
1077  ValueComboBox:=TComboBox.Create(Self);
1078  with ValueComboBox do
1079  begin
1080    Name:='ValueComboBox';
1081    Sorted:=true;
1082    AutoSelect:=true;
1083    AutoComplete:=true;
1084    Visible:=false;
1085    Enabled:=false;
1086    AutoSize:=false;
1087    SetBounds(0,-30,Width,Height); // hidden
1088    DropDownCount:=20;
1089    ItemHeight:=MulDiv(17, Screen.PixelsPerInch, 96);
1090    Parent:=Self;
1091    OnMouseDown := @ValueControlMouseDown;
1092    OnMouseMove := @ValueControlMouseMove;
1093    OnDblClick := @ValueEditDblClick;
1094    OnExit:=@ValueComboBoxExit;
1095    //OnChange:=@ValueComboBoxChange; the on change event is called even,
1096                                   // if the user is still editing
1097    OnKeyDown:=@ValueComboBoxKeyDown;
1098    OnKeyUp:=@ValueComboBoxKeyUp;
1099    OnMouseUp:=@ValueComboBoxMouseUp;
1100    OnGetItems:=@ValueComboBoxGetItems;
1101    OnCloseUp:=@ValueComboBoxCloseUp;
1102    OnMeasureItem:=@ValueComboBoxMeasureItem;
1103    OnDrawItem:=@ValueComboBoxDrawItem;
1104    OnMouseWheel:=@OnGridMouseWheel;
1105  end;
1106
1107  ValueCheckBox:={$IFnDEF UseOINormalCheckBox} TCheckBoxThemed.Create(Self); {$ELSE} TCheckBox.Create(Self); {$ENDIF}
1108  with ValueCheckBox do
1109  begin
1110    Name:='ValueCheckBox';
1111    Visible:=false;
1112    Enabled:=false;
1113    {$IFnDEF UseOINormalCheckBox}
1114    AutoSize := false;
1115    {$ELSE}
1116    AutoSize := true;    // SetBounds does not work for CheckBox, AutoSize does.
1117    {$ENDIF}
1118    Parent:=Self;
1119    Top := -30;
1120    OnMouseDown := @ValueControlMouseDown;
1121    OnMouseMove := @ValueControlMouseMove;
1122    OnExit:=@ValueCheckBoxExit;
1123    OnKeyDown:=@ValueCheckBoxKeyDown;
1124    OnKeyUp:=@ValueCheckBoxKeyUp;
1125    OnClick:=@ValueCheckBoxClick;
1126    OnMouseWheel:=@OnGridMouseWheel;
1127  end;
1128
1129  ValueButton:=TSpeedButton.Create(Self);
1130  with ValueButton do
1131  begin
1132    Name:='ValueButton';
1133    Visible:=false;
1134    Enabled:=false;
1135    Transparent:=false;
1136    OnClick:=@ValueButtonClick;
1137    Caption := '...';
1138    SetBounds(0,-30,Width,Height); // hidden
1139    Parent:=Self;
1140    OnMouseWheel:=@OnGridMouseWheel;
1141  end;
1142
1143  FHintManager := THintWindowManager.Create;
1144  FActiveRowImages := TLCLGlyphs.Create(Self);
1145  FActiveRowImages.Width := 9;
1146  FActiveRowImages.Height := 9;
1147  FActiveRowImages.RegisterResolutions([9, 13, 18], [100, 150, 200]);
1148  FActiveRowImages.OnGetWidthForPPI := @ActiveRowImagesGetWidthForPPI;
1149
1150  FDefaultItemHeight:=DefItemHeight;
1151
1152  BuildPropertyList;
1153end;
1154
1155procedure TOICustomPropertyGrid.ActiveRowImagesGetWidthForPPI(
1156  Sender: TCustomImageList; AImageWidth, APPI: Integer;
1157  var AResultWidth: Integer);
1158begin
1159  if (12<=AResultWidth) and (AResultWidth<=16) then
1160    AResultWidth := 13;
1161end;
1162
1163constructor TOICustomPropertyGrid.Create(TheOwner: TComponent);
1164begin
1165  CreateWithParams(TheOwner,nil,AllTypeKinds,0);
1166end;
1167
1168destructor TOICustomPropertyGrid.Destroy;
1169var
1170  a: integer;
1171begin
1172  SetIdleEvent(false);
1173  FItemIndex := -1;
1174  for a := 0 to FRows.Count - 1 do
1175    Rows[a].Free;
1176  FreeAndNil(FRows);
1177  FreeAndNil(FSelection);
1178  FreeAndNil(FNotificationComponents);
1179  FreeAndNil(FValueFont);
1180  FreeAndNil(FDefaultValueFont);
1181  FreeAndNil(FNameFont);
1182  FreeAndNil(FHighlightFont);
1183  FreeAndNil(FExpandedProperties);
1184  FreeAndNil(FLongHintTimer);
1185  FreeAndNil(FHintManager);
1186  FreeAndNil(FNewComboBoxItems);
1187  inherited Destroy;
1188end;
1189
1190procedure TOICustomPropertyGrid.UpdateScrollBar;
1191var
1192  ScrollInfo: TScrollInfo;
1193  ATopMax: Integer;
1194begin
1195  if HandleAllocated then begin
1196    ATopMax := TopMax;
1197    ScrollInfo.cbSize := SizeOf(ScrollInfo);
1198    ScrollInfo.fMask := SIF_ALL or SIF_DISABLENOSCROLL;
1199    ScrollInfo.nMin := 0;
1200    ScrollInfo.nTrackPos := 0;
1201    ScrollInfo.nMax := ATopMax+ClientHeight-1;
1202    if ClientHeight < 2 then
1203      ScrollInfo.nPage := 1
1204    else
1205      ScrollInfo.nPage := ClientHeight-1;
1206    if TopY > ATopMax then
1207      TopY := ATopMax;
1208    ScrollInfo.nPos := TopY;
1209    SetScrollInfo(Handle, SB_VERT, ScrollInfo, True);
1210  end;
1211end;
1212
1213function TOICustomPropertyGrid.FillComboboxItems: boolean;
1214var
1215  ExcludeUpdateFlag: boolean;
1216  CurRow: TOIPropertyGridRow;
1217begin
1218  Result:=false;
1219  ExcludeUpdateFlag:=not (pgsUpdatingEditControl in FStates);
1220  Include(FStates,pgsUpdatingEditControl);
1221  ValueComboBox.Items.BeginUpdate;
1222  try
1223    CurRow:=Rows[FItemIndex];
1224    if FNewComboBoxItems<>nil then FNewComboBoxItems.Clear;
1225    CurRow.Editor.GetValues(@AddStringToComboBox);
1226    if FNewComboBoxItems<>nil then begin
1227      FNewComboBoxItems.Sorted:=paSortList in CurRow.Editor.GetAttributes;
1228      if ValueComboBox.Items.Equals(FNewComboBoxItems) then exit;
1229      ValueComboBox.Items.Assign(FNewComboBoxItems);
1230      //debugln('TOICustomPropertyGrid.FillComboboxItems "',FNewComboBoxItems.Text,'" Cur="',ValueComboBox.Items.Text,'" ValueComboBox.Items.Count=',dbgs(ValueComboBox.Items.Count));
1231    end else if ValueComboBox.Items.Count=0 then begin
1232      exit;
1233    end else begin
1234      ValueComboBox.Items.Text:='';
1235      ValueComboBox.Items.Clear;
1236      //debugln('TOICustomPropertyGrid.FillComboboxItems FNewComboBoxItems=nil Cur="',ValueComboBox.Items.Text,'" ValueComboBox.Items.Count=',dbgs(ValueComboBox.Items.Count));
1237    end;
1238    Result:=true;
1239    //debugln(['TOICustomPropertyGrid.FillComboboxItems CHANGED']);
1240  finally
1241    FreeAndNil(FNewComboBoxItems);
1242    ValueComboBox.Items.EndUpdate;
1243    if ExcludeUpdateFlag then
1244      Exclude(FStates,pgsUpdatingEditControl);
1245  end;
1246end;
1247
1248procedure TOICustomPropertyGrid.CreateParams(var Params: TCreateParams);
1249const
1250  ClassStylesOff = CS_VREDRAW or CS_HREDRAW;
1251begin
1252  inherited CreateParams(Params);
1253  with Params do begin
1254    {$IFOPT R+}{$DEFINE RangeChecking}{$ENDIF}
1255    {$R-}
1256    WindowClass.Style := WindowClass.Style and not ClassStylesOff;
1257    Style := Style or WS_VSCROLL or WS_CLIPCHILDREN;
1258    {$IFDEF RangeChecking}{$R+}{$UNDEF RangeChecking}{$ENDIF}
1259    ExStyle := ExStyle or WS_EX_CLIENTEDGE;
1260  end;
1261end;
1262
1263procedure TOICustomPropertyGrid.CreateWnd;
1264begin
1265  inherited CreateWnd;
1266  // handle just created, set scrollbar
1267  ShowScrollBar(Handle, SB_VERT, True);
1268  UpdateScrollBar;
1269end;
1270
1271procedure TOICustomPropertyGrid.Notification(AComponent: TComponent;
1272  Operation: TOperation);
1273var
1274  i: LongInt;
1275begin
1276  if (Operation=opRemove) and (FNotificationComponents<>nil) then begin
1277    FNotificationComponents.Remove(AComponent);
1278    i:=FSelection.IndexOf(AComponent);
1279    if i>=0 then begin
1280      FSelection.Delete(i);
1281      Include(FStates,pgsBuildPropertyListNeeded);
1282    end;
1283  end;
1284  inherited Notification(AComponent, Operation);
1285end;
1286
1287procedure TOICustomPropertyGrid.WMVScroll(var Msg: TLMScroll);
1288begin
1289  case Msg.ScrollCode of
1290      // Scrolls to start / end of the text
1291    SB_TOP:        TopY := 0;
1292    SB_BOTTOM:     TopY := TopMax;
1293      // Scrolls one line up / down
1294    SB_LINEDOWN:   TopY := TopY + RealDefaultItemHeight div 2;
1295    SB_LINEUP:     TopY := TopY - RealDefaultItemHeight div 2;
1296      // Scrolls one page of lines up / down
1297    SB_PAGEDOWN:   TopY := TopY + ClientHeight - RealDefaultItemHeight;
1298    SB_PAGEUP:     TopY := TopY - ClientHeight + RealDefaultItemHeight;
1299      // Scrolls to the current scroll bar position
1300    SB_THUMBPOSITION,
1301    SB_THUMBTRACK: TopY := Msg.Pos;
1302      // Ends scrolling
1303    SB_ENDSCROLL:  SetCaptureControl(nil); // release scrollbar capture
1304  end;
1305end;
1306
1307function TOICustomPropertyGrid.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean;
1308var
1309  H: Boolean;
1310begin
1311  H := False;
1312  OnGridMouseWheel(Self, Shift, WheelDelta, MousePos, H);
1313  Result:=true;
1314end;
1315
1316procedure TOICustomPropertyGrid.OnGridMouseWheel(Sender: TObject; Shift: TShiftState;
1317      WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
1318begin
1319  if Mouse.WheelScrollLines=-1 then
1320    // -1 : scroll by page
1321    TopY := TopY - (WheelDelta * (ClientHeight - RealDefaultItemHeight)) div 120
1322  else
1323    // scrolling one line -> scroll half an item, see SB_LINEDOWN and SB_LINEUP
1324    // handler in WMVScroll
1325    TopY := TopY - (WheelDelta * Mouse.WheelScrollLines*RealDefaultItemHeight) div 240;
1326  Handled := True;
1327end;
1328
1329function TOICustomPropertyGrid.IsCurrentEditorAvailable: Boolean;
1330begin
1331  Result := (FCurrentEdit <> nil) and InRange(FItemIndex, 0, FRows.Count - 1);
1332end;
1333
1334procedure TOICustomPropertyGrid.FocusCurrentEditor;
1335begin
1336  if (IsCurrentEditorAvailable) and (FCurrentEdit.CanFocus) then
1337  begin
1338    FCurrentEdit.SetFocus;
1339    if (FCurrentEdit is TEdit) then
1340      TEdit(FCurrentEdit).SelStart := Length((FCurrentEdit as TEdit).Text);
1341  end;
1342end;
1343
1344function TOICustomPropertyGrid.ConsistencyCheck: integer;
1345var
1346  i: integer;
1347begin
1348  for i:=0 to FRows.Count-1 do
1349  begin
1350    if Rows[i]=nil then
1351      Exit(-1);
1352    if Rows[i].Index<>i then
1353      Exit(-2);
1354    Result:=Rows[i].ConsistencyCheck;
1355    if Result<>0 then
1356      Exit(Result-100);
1357  end;
1358  Result:=0;
1359end;
1360
1361procedure TOICustomPropertyGrid.SetSelection(const ASelection: TPersistentSelectionList);
1362var
1363  CurRow:TOIPropertyGridRow;
1364  OldSelectedRowPath:string;
1365begin
1366  if ASelection=nil then exit;
1367  if (not ASelection.ForceUpdate) and FSelection.IsEqual(ASelection) then exit;
1368
1369  OldSelectedRowPath:=PropertyPath(ItemIndex);
1370  if FCurrentEdit = ValueEdit then
1371    ValueEditExit(Self);
1372  ItemIndex:=-1;
1373  ClearRows;
1374  FSelection.Assign(ASelection);
1375  UpdateSelectionNotifications;
1376  BuildPropertyList;
1377  CurRow:=GetRowByPath(OldSelectedRowPath);
1378  if CurRow<>nil then
1379    ItemIndex:=CurRow.Index;
1380  Column := oipgcValue;
1381end;
1382
1383procedure TOICustomPropertyGrid.SetPropertyEditorHook(
1384  NewPropertyEditorHook:TPropertyEditorHook);
1385begin
1386  if FPropertyEditorHook=NewPropertyEditorHook then exit;
1387  FPropertyEditorHook:=NewPropertyEditorHook;
1388  IncreaseChangeStep;
1389  SetSelection(FSelection);
1390end;
1391
1392procedure TOICustomPropertyGrid.UpdateSelectionNotifications;
1393var
1394  i: Integer;
1395  AComponent: TComponent;
1396begin
1397  for i:=0 to FSelection.Count-1 do begin
1398    if FSelection[i] is TComponent then begin
1399      AComponent:=TComponent(FSelection[i]);
1400      if FNotificationComponents.IndexOf(AComponent)<0 then begin
1401        FNotificationComponents.Add(AComponent);
1402        AComponent.FreeNotification(Self);
1403      end;
1404    end;
1405  end;
1406  for i:=FNotificationComponents.Count-1 downto 0 do begin
1407    AComponent:=TComponent(FNotificationComponents[i]);
1408    if FSelection.IndexOf(AComponent)<0 then begin
1409      FNotificationComponents.Delete(i);
1410      AComponent.RemoveFreeNotification(Self);
1411    end;
1412  end;
1413  //DebugLn(['TOICustomPropertyGrid.UpdateSelectionNotifications FNotificationComponents=',FNotificationComponents.Count,' FSelection=',FSelection.Count]);
1414end;
1415
1416procedure TOICustomPropertyGrid.HookGetCheckboxForBoolean(var Value: Boolean);
1417begin
1418  Value := FCheckboxForBoolean;
1419end;
1420
1421function TOICustomPropertyGrid.PropertyPath(Index:integer):string;
1422begin
1423  if (Index>=0) and (Index<FRows.Count) then
1424    Result:=PropertyPath(Rows[Index])
1425  else
1426    Result:='';
1427end;
1428
1429function TOICustomPropertyGrid.PropertyPath(Row: TOIPropertyGridRow): string;
1430begin
1431  if Row=nil then
1432    Exit('');
1433  Result:=Row.Name;
1434  Row:=Row.Parent;
1435  while Row<>nil do begin
1436    Result:=Row.Name+'.'+Result;
1437    Row:=Row.Parent;
1438  end;
1439end;
1440
1441function TOICustomPropertyGrid.PropertyEditorByName(const PropName: string): TPropertyEditor;
1442var
1443  AOIPropertyGridRow: TOIPropertyGridRow;
1444  i: Integer;
1445begin
1446  Result := nil;
1447  for i := 0 to FRows.Count - 1 do
1448  begin
1449    AOIPropertyGridRow := TOIPropertyGridRow(FRows[i]);
1450    if Assigned(AOIPropertyGridRow.Editor) and (AOIPropertyGridRow.Editor.GetName = PropName) then
1451      Exit(AOIPropertyGridRow.Editor);
1452  end;
1453end;
1454
1455function TOICustomPropertyGrid.RealDefaultItemHeight: integer;
1456begin
1457  Result := FDefaultItemHeight;
1458  if (Result<=0) then
1459    Result := Canvas.TextHeight('Hg')*5 div 4 + 4;
1460end;
1461
1462function TOICustomPropertyGrid.GetRowByPath(const PropPath: string): TOIPropertyGridRow;
1463// searches PropPath. Expands automatically parent rows
1464var
1465  CurName:string;
1466  s,e:integer;
1467  CurParentRow:TOIPropertyGridRow;
1468begin
1469  Result:=nil;
1470  if (PropPath='') or (FRows.Count=0) then exit;
1471  CurParentRow:=nil;
1472  s:=1;
1473  while (s<=length(PropPath)) do begin
1474    e:=s;
1475    while (e<=length(PropPath)) and (PropPath[e]<>'.') do inc(e);
1476    CurName:=copy(PropPath,s,e-s);
1477    s:=e+1;
1478    // search name in children
1479    if CurParentRow=nil then
1480      Result:=Rows[0]
1481    else
1482      Result:=CurParentRow.FirstChild;
1483    while (Result<>nil) and (CompareText(Result.Name, CurName)<>0) do
1484      Result:=Result.NextBrother;
1485    if Result=nil then begin
1486      exit;
1487    end else begin
1488      // expand row
1489      CurParentRow:=Result;
1490      if s<=length(PropPath) then
1491        ExpandRow(CurParentRow.Index);
1492    end;
1493  end;
1494  if s<=length(PropPath) then Result:=nil;
1495end;
1496
1497procedure TOICustomPropertyGrid.SetRowValue(CheckFocus, ForceValue: boolean);
1498
1499  function GetPropValue(Editor: TPropertyEditor; Index: integer): string;
1500  var
1501    PropKind: TTypeKind;
1502    PropInfo: PPropInfo;
1503    BoolVal: Boolean;
1504  begin
1505    Result:='';
1506    PropInfo := Editor.GetPropInfo;
1507    PropKind := PropInfo^.PropType^.Kind;
1508    case PropKind of
1509      tkInteger, tkInt64:
1510        Result := IntToStr(Editor.GetInt64ValueAt(Index));
1511      tkChar, tkWChar, tkUChar:
1512        Result := Char(Editor.GetOrdValueAt(Index));
1513      tkEnumeration:
1514        Result := GetEnumName(PropInfo^.PropType, Editor.GetOrdValueAt(Index));
1515      tkFloat:
1516        Result := FloatToStr(Editor.GetFloatValueAt(Index));
1517      tkBool: begin
1518        BoolVal := Boolean(Editor.GetOrdValueAt(Index));
1519        if FCheckboxForBoolean then
1520          Result := BoolToStr(BoolVal, '(True)', '(False)')
1521        else
1522          Result := BoolToStr(BoolVal, 'True', 'False');
1523      end;
1524      tkString, tkLString, tkAString, tkUString, tkWString:
1525        Result := Editor.GetStrValueAt(Index);
1526      tkSet:
1527        Result := Editor.GetSetValueAt(Index,true);
1528      tkVariant:
1529        if Editor.GetVarValueAt(Index) <> Null then
1530          Result := Editor.GetVarValueAt(Index)
1531        else
1532          Result := '(Null)';
1533    end;
1534  end;
1535
1536var
1537  CurRow: TOIPropertyGridRow;
1538  NewValue: string;
1539  OldExpanded: boolean;
1540  OldChangeStep: integer;
1541  RootDesigner: TIDesigner;
1542  CompEditDsg: TComponentEditorDesigner;
1543  APersistent: TPersistent;
1544  i: integer;
1545  UndoVal: string;
1546  OldUndoValues: array of string;
1547  isExcept: boolean;
1548  prpInfo: PPropInfo;
1549  Editor: TPropertyEditor;
1550begin
1551  //if FItemIndex > -1 then
1552  //  debugln(['TOICustomPropertyGrid.SetRowValue A, FItemIndex=',dbgs(FItemIndex),
1553  //    ', CanEditRowValue=', CanEditRowValue(CheckFocus), ', IsReadOnly=', Rows[FItemIndex].IsReadOnly]);
1554
1555  if not CanEditRowValue(CheckFocus) or Rows[FItemIndex].IsReadOnly then exit;
1556
1557  NewValue:=GetCurrentEditValue;
1558  CurRow:=Rows[FItemIndex];
1559  if length(NewValue)>CurRow.Editor.GetEditLimit then
1560    NewValue:=LeftStr(NewValue,CurRow.Editor.GetEditLimit);
1561
1562  //DebugLn(['TOICustomPropertyGrid.SetRowValue Old="',CurRow.Editor.GetVisualValue,'" New="',NewValue,'"']);
1563  if (CurRow.Editor.GetVisualValue=NewValue) and not ForceValue then exit;
1564
1565  RootDesigner := FindRootDesigner(FCurrentEditorLookupRoot);
1566  if (RootDesigner is TComponentEditorDesigner) then begin
1567    CompEditDsg := TComponentEditorDesigner(RootDesigner);
1568    if CompEditDsg.IsUndoLocked then Exit;
1569  end else
1570    CompEditDsg := nil;
1571
1572  // store old values for undo
1573  isExcept := false;
1574  Editor:=CurRow.Editor;
1575  prpInfo := nil;
1576  if (CompEditDsg<>nil) and (paRevertable in Editor.GetAttributes) then begin
1577    SetLength(OldUndoValues, Editor.PropCount);
1578    prpInfo := Editor.GetPropInfo;
1579    if prpInfo<>nil then
1580      for i := 0 to Editor.PropCount - 1 do
1581        OldUndoValues[i] := GetPropValue(Editor,i);
1582  end;
1583
1584  OldChangeStep:=fChangeStep;
1585  Include(FStates,pgsApplyingValue);
1586  try
1587    {$IFNDEF DoNotCatchOIExceptions}
1588    try
1589    {$ENDIF}
1590      //debugln(['TOICustomPropertyGrid.SetRowValue B ClassName=',CurRow.Editor.ClassName,' Visual="',CurRow.Editor.GetVisualValue,'" NewValue="',NewValue,'" AllEqual=',CurRow.Editor.AllEqual]);
1591      CurRow.Editor.SetValue(NewValue);
1592      //debugln(['TOICustomPropertyGrid.SetRowValue C ClassName=',CurRow.Editor.ClassName,' Visual="',CurRow.Editor.GetVisualValue,'" NewValue="',NewValue,'" AllEqual=',CurRow.Editor.AllEqual]);
1593    {$IFNDEF DoNotCatchOIExceptions}
1594    except
1595      on E: Exception do begin
1596        MessageDlg(oisError, E.Message, mtError, [mbOk], 0);
1597        isExcept := true;
1598      end;
1599    end;
1600    {$ENDIF}
1601    if (OldChangeStep<>FChangeStep) then begin
1602      // the selection has changed => CurRow does not exist any more
1603      exit;
1604    end;
1605
1606    // add Undo action
1607    if (not isExcept) and (CompEditDsg<>nil) and (paRevertable in Editor.GetAttributes) then
1608    begin
1609      for i := 0 to Editor.PropCount - 1 do
1610      begin
1611        APersistent := Editor.GetComponent(i);
1612        if APersistent=nil then continue;
1613        UndoVal := GetPropValue(Editor,i);
1614        CompEditDsg.AddUndoAction(APersistent, uopChange, i = 0,
1615            Editor.GetName, OldUndoValues[i], UndoVal);
1616      end;
1617    end;
1618
1619    // set value in edit control
1620    SetCurrentEditValue(Editor.GetVisualValue);
1621
1622    // update volatile sub properties
1623    if (paVolatileSubProperties in Editor.GetAttributes)
1624    and ((CurRow.Expanded) or (CurRow.ChildCount>0)) then begin
1625      OldExpanded:=CurRow.Expanded;
1626      ShrinkRow(FItemIndex);
1627      if OldExpanded then
1628        ExpandRow(FItemIndex);
1629    end;
1630    //debugln(['TOICustomPropertyGrid.SetRowValue D ClassName=',CurRow.Editor.ClassName,' Visual="',CurRow.Editor.GetVisualValue,'" NewValue="',NewValue,'" AllEqual=',CurRow.Editor.AllEqual]);
1631  finally
1632    Exclude(FStates,pgsApplyingValue);
1633  end;
1634  if Assigned(FPropertyEditorHook) then
1635    FPropertyEditorHook.RefreshPropertyValues;
1636  if Assigned(FOnModified) then
1637    FOnModified(Self);
1638end;
1639
1640procedure TOICustomPropertyGrid.DoCallEdit(Edit: TOIQuickEdit);
1641var
1642  CurRow:TOIPropertyGridRow;
1643  OldChangeStep: integer;
1644begin
1645  //debugln(['TOICustomPropertyGrid.DoCallEdit ',dbgs(GetFocus),' ',DbgSName(FindControl(GetFocus))]);
1646  if not CanEditRowValue(false) then exit;
1647
1648  OldChangeStep:=fChangeStep;
1649  CurRow:=Rows[FItemIndex];
1650  if paDialog in CurRow.Editor.GetAttributes then begin
1651    {$IFnDEF DoNotCatchOIExceptions}
1652    try
1653    {$ENDIF}
1654      //if FSelection.Count > 0 then
1655      //  DebugLn(['# TOICustomPropertyGrid.DoCallEdit for ', CurRow.Editor.ClassName,
1656      //           ', Edit=', Edit=oiqeEdit, ', SelectionCount=', FSelection.Count,
1657      //           ', SelectionName=', FSelection[0].GetNamePath]);
1658      Include(FStates,pgsCallingEdit);
1659      try
1660        if Edit=oiqeShowValue then
1661          CurRow.Editor.ShowValue
1662        else if (FSelection.Count > 0) and (FSelection[0] is TComponent) then
1663          CurRow.Editor.Edit(TComponent(FSelection[0]))
1664        else
1665          CurRow.Editor.Edit;
1666      finally
1667        Exclude(FStates,pgsCallingEdit);
1668      end;
1669    {$IFnDEF DoNotCatchOIExceptions}
1670    except
1671      on E: Exception do
1672        MessageDlg(oisError, E.Message, mtError, [mbOk], 0);
1673    end;
1674    {$ENDIF}
1675    // CurRow is now invalid, do not access CurRow
1676
1677    if (OldChangeStep<>FChangeStep) then begin
1678      // the selection has changed => CurRow does not exist any more
1679      RefreshPropertyValues;
1680      exit;
1681    end;
1682    RefreshValueEdit;       // update value
1683    Invalidate;             //invalidate changed subproperties
1684  end;
1685end;
1686
1687procedure TOICustomPropertyGrid.RefreshValueEdit;
1688var
1689  CurRow: TOIPropertyGridRow;
1690  NewValue: string;
1691begin
1692  if not GridIsUpdating and IsCurrentEditorAvailable then begin
1693    CurRow:=Rows[FItemIndex];
1694    NewValue:=CurRow.Editor.GetVisualValue;
1695    {$IFDEF LCLCarbon}
1696    NewValue:=StringReplace(NewValue,LineEnding,LineFeedSymbolUTF8,[rfReplaceAll]);
1697    {$ENDIF}
1698    SetCurrentEditValue(NewValue);
1699  end;
1700end;
1701
1702procedure TOICustomPropertyGrid.ValueEditKeyDown(Sender: TObject; var Key: Word;
1703  Shift: TShiftState);
1704begin
1705  ScrollToActiveItem;
1706  HandleStandardKeys(Key,Shift);
1707end;
1708
1709procedure TOICustomPropertyGrid.ValueEditKeyUp(Sender: TObject; var Key: Word;
1710  Shift: TShiftState);
1711begin
1712  HandleKeyUp(Key,Shift);
1713end;
1714
1715procedure TOICustomPropertyGrid.ValueEditExit(Sender: TObject);
1716begin
1717  SetRowValue(false, false);
1718end;
1719
1720procedure TOICustomPropertyGrid.ValueEditChange(Sender: TObject);
1721var CurRow: TOIPropertyGridRow;
1722begin
1723  if (pgsUpdatingEditControl in FStates) or not IsCurrentEditorAvailable then exit;
1724  CurRow:=Rows[FItemIndex];
1725  if paAutoUpdate in CurRow.Editor.GetAttributes then
1726    SetRowValue(true, true);
1727end;
1728
1729procedure TOICustomPropertyGrid.ValueEditMouseUp(Sender: TObject;
1730  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
1731begin
1732  if (Button=mbLeft) and (Shift=[ssCtrl,ssLeft]) then
1733    DoCallEdit(oiqeShowValue);
1734end;
1735
1736procedure TOICustomPropertyGrid.ValueCheckBoxKeyDown(Sender: TObject;
1737  var Key: Word; Shift: TShiftState);
1738begin
1739  ScrollToActiveItem;
1740  HandleStandardKeys(Key,Shift);
1741end;
1742
1743procedure TOICustomPropertyGrid.ValueCheckBoxKeyUp(Sender: TObject;
1744  var Key: Word; Shift: TShiftState);
1745begin
1746  HandleKeyUp(Key,Shift);
1747end;
1748
1749procedure TOICustomPropertyGrid.ValueCheckBoxExit(Sender: TObject);
1750begin
1751  SetRowValue(false, false);
1752end;
1753
1754procedure TOICustomPropertyGrid.ValueCheckBoxClick(Sender: TObject);
1755begin
1756  if (pgsUpdatingEditControl in FStates) or not IsCurrentEditorAvailable then exit;
1757  ValueCheckBox.Caption:=BoolToStr(ValueCheckBox.Checked, '(True)', '(False)');
1758  SetRowValue(true, true);
1759end;
1760
1761procedure TOICustomPropertyGrid.ValueComboBoxExit(Sender: TObject);
1762begin
1763  if pgsUpdatingEditControl in FStates then exit;
1764  SetRowValue(false, false);
1765end;
1766
1767procedure TOICustomPropertyGrid.ValueComboBoxKeyDown(Sender: TObject;
1768  var Key: Word; Shift: TShiftState);
1769begin
1770  ScrollToActiveItem;
1771  HandleStandardKeys(Key,Shift);
1772end;
1773
1774procedure TOICustomPropertyGrid.ValueComboBoxKeyUp(Sender: TObject;
1775  var Key: Word; Shift: TShiftState);
1776begin
1777  HandleKeyUp(Key,Shift);
1778end;
1779
1780procedure TOICustomPropertyGrid.ValueComboBoxMouseUp(Sender: TObject;
1781  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
1782begin
1783  if (Button=mbLeft) then begin
1784    if (Shift=[ssCtrl,ssLeft]) then
1785      DoCallEdit(oiqeShowValue)
1786    else
1787    if (FFirstClickTime<>0) and (GetTickCount <= FFirstClickTime + GetDoubleClickTime)
1788    and (not ValueComboBox.DroppedDown) then
1789    begin
1790      FFirstClickTime:=0;
1791      ToggleRow;
1792    end;
1793  end;
1794end;
1795
1796procedure TOICustomPropertyGrid.ValueButtonClick(Sender: TObject);
1797begin
1798  ScrollToActiveItem;
1799  DoCallEdit;
1800end;
1801
1802procedure TOICustomPropertyGrid.ValueComboBoxMeasureItem(Control: TWinControl;
1803  Index: Integer; var AHeight: Integer);
1804var
1805  CurRow: TOIPropertyGridRow;
1806begin
1807  if (FItemIndex >= 0) and (FItemIndex < FRows.Count) then
1808  begin
1809    CurRow := Rows[FItemIndex];
1810    CurRow.Editor.ListMeasureHeight('Fj', Index, ValueComboBox.Canvas, AHeight);
1811    AHeight := Max(AHeight, ValueComboBox.ItemHeight);
1812  end;
1813end;
1814
1815procedure TOICustomPropertyGrid.SetCheckboxState(NewValue: string);
1816begin
1817  ValueCheckBox.Caption:=NewValue;
1818  if (NewValue='') or (NewValue=oisMixed) then
1819    ValueCheckBox.State:=cbGrayed
1820  else if NewValue='(True)' then
1821    ValueCheckBox.State:=cbChecked
1822  // Note: this condition can be removed when the right propedit is used always.
1823  else if NewValue='(False)' then
1824    ValueCheckBox.State:=cbUnchecked;
1825end;
1826
1827procedure TOICustomPropertyGrid.SetItemIndex(NewIndex:integer);
1828var
1829  NewRow: TOIPropertyGridRow;
1830  NewValue: string;
1831  EditorAttributes: TPropertyAttributes;
1832begin
1833  {if pgsCallingEdit in FStates then begin
1834    DumpStack;
1835    debugln(['TOICustomPropertyGrid.SetItemIndex ',DbgSName(Self),' ',dbgsname(FCurrentEdit),' ',dbgs(FStates),' GridIsUpdating=',GridIsUpdating,' FItemIndex=',FItemIndex,' NewIndex=',NewIndex]);
1836  end;}
1837  if GridIsUpdating or (FItemIndex = NewIndex) then
1838    exit;
1839  // save old edit value
1840  SetRowValue(true, false);
1841
1842  Include(FStates, pgsChangingItemIndex);
1843  if (FItemIndex >= 0) and (FItemIndex < FRows.Count) then
1844    Rows[FItemIndex].Editor.Deactivate;
1845  if CanFocus then
1846    SetCaptureControl(nil);
1847
1848  FItemIndex := NewIndex;
1849  if FCurrentEdit <> nil then
1850  begin
1851    FCurrentEdit.Visible:=false;
1852    FCurrentEdit.Enabled:=false;
1853    FCurrentEdit:=nil;
1854  end;
1855  if FCurrentButton<>nil then
1856  begin
1857    FCurrentButton.Visible:=false;
1858    FCurrentButton.Enabled:=false;
1859    FCurrentButton:=nil;
1860  end;
1861  FCurrentEditorLookupRoot:=nil;
1862  if (NewIndex >= 0) and (NewIndex < FRows.Count) then
1863  begin
1864    NewRow:=Rows[NewIndex];
1865    ScrollToItem(NewIndex);
1866    if CanFocus then
1867      NewRow.Editor.Activate;
1868    EditorAttributes:=NewRow.Editor.GetAttributes;
1869    if paDialog in EditorAttributes then begin
1870      FCurrentButton:=ValueButton;
1871      FCurrentButton.Visible:=true;
1872      //DebugLn(['TOICustomPropertyGrid.SetItemIndex FCurrentButton.BoundsRect=',dbgs(FCurrentButton.BoundsRect)]);
1873    end;
1874    NewValue:=NewRow.Editor.GetVisualValue;
1875    if ((NewRow.Editor is TBoolPropertyEditor) or (NewRow.Editor is TSetElementPropertyEditor))
1876    and FCheckboxForBoolean then
1877    begin
1878      FCurrentEdit:=ValueCheckBox;
1879      ValueCheckBox.Enabled:=not NewRow.IsReadOnly;
1880      SetCheckboxState(NewValue);
1881    end
1882    else if paValueList in EditorAttributes then
1883    begin
1884      FCurrentEdit:=ValueComboBox;
1885      if (paCustomDrawn in EditorAttributes) and (paPickList in EditorAttributes) then
1886        ValueComboBox.Style:=csOwnerDrawVariable
1887      else if paCustomDrawn in EditorAttributes then
1888        ValueComboBox.Style:=csOwnerDrawEditableVariable
1889      else if paPickList in EditorAttributes then
1890        ValueComboBox.Style:=csOwnerDrawFixed
1891      else
1892        ValueComboBox.Style:=csOwnerDrawEditableFixed;
1893      ValueComboBox.MaxLength:=NewRow.Editor.GetEditLimit;
1894      ValueComboBox.Sorted:=paSortList in NewRow.Editor.GetAttributes;
1895      ValueComboBox.Enabled:=not NewRow.IsReadOnly;
1896      // Do not fill the items here, because it can be very slow.
1897      // Just fill in some values and update the values before the combobox popups
1898      ValueComboBox.Items.Text:=NewValue;
1899      Exclude(FStates,pgsGetComboItemsCalled);
1900      SetIdleEvent(true);
1901      ValueComboBox.Text:=NewValue;
1902    end
1903    else begin
1904      FCurrentEdit:=ValueEdit;
1905      ValueEdit.ReadOnly:=NewRow.IsReadOnly;
1906      ValueEdit.Enabled:=true;
1907      ValueEdit.MaxLength:=NewRow.Editor.GetEditLimit;
1908      ValueEdit.Text:=NewValue;
1909    end;
1910    AlignEditComponents;
1911    if FCurrentEdit<>nil then
1912    begin
1913      if FPropertyEditorHook<>nil then
1914        FCurrentEditorLookupRoot:=FPropertyEditorHook.LookupRoot;
1915      if (FCurrentEdit=ValueComboBox) or (FCurrentEdit=ValueEdit) then
1916      begin
1917        if NewRow.Editor.AllEqual then
1918          FCurrentEdit.Color:=clWindow
1919        else
1920          FCurrentEdit.Color:=FValueDifferBackgrndColor;
1921      end;
1922      if NewRow.Editor.ValueIsStreamed then
1923        FCurrentEdit.Font:=FValueFont
1924      else
1925        FCurrentEdit.Font:=FDefaultValueFont;
1926      FCurrentEdit.Visible:=true;
1927      if (FDragging=false) and FCurrentEdit.Showing and FCurrentEdit.Enabled
1928      and (not NewRow.IsReadOnly) and CanFocus and (Column=oipgcValue)
1929      and not (pgsFocusPropertyEditorDisabled in FStates)
1930      then
1931        SetActiveControl(FCurrentEdit);
1932    end;
1933    if FCurrentButton<>nil then
1934      FCurrentButton.Enabled:=not NewRow.IsDisabled;
1935  end;
1936  //DebugLn(['TOICustomPropertyGrid.SetItemIndex Vis=',ValueComboBox.Visible,' Ena=',ValueComboBox.Enabled,
1937  //         ' Items.Count=',ValueComboBox.Items.Count ,' Text=',ValueComboBox.Text]);
1938  Exclude(FStates, pgsChangingItemIndex);
1939  DoSelectionChange;
1940  Invalidate;
1941end;
1942
1943function TOICustomPropertyGrid.GetNameRowHeight: Integer;
1944begin
1945  Result := Abs(FNameFont.Height);
1946  if Result = 0 then
1947    Result := 16;
1948  Inc(Result, 2); // margin
1949end;
1950
1951function TOICustomPropertyGrid.GetRowCount:integer;
1952begin
1953  Result:=FRows.Count;
1954end;
1955
1956procedure TOICustomPropertyGrid.BuildPropertyList(OnlyIfNeeded: Boolean;
1957  FocusEditor: Boolean);
1958var
1959  a: integer;
1960  CurRow: TOIPropertyGridRow;
1961  OldSelectedRowPath: string;
1962begin
1963  if OnlyIfNeeded and (not (pgsBuildPropertyListNeeded in FStates)) then exit;
1964  Exclude(FStates,pgsBuildPropertyListNeeded);
1965  if not FocusEditor then Include(FStates, pgsFocusPropertyEditorDisabled);
1966  OldSelectedRowPath:=PropertyPath(ItemIndex);
1967  // unselect
1968  ItemIndex:=-1;
1969  // clear
1970  for a:=0 to FRows.Count-1 do Rows[a].Free;
1971  FRows.Clear;
1972  // get properties
1973  if FSelection.Count>0 then begin
1974    GetPersistentProperties(FSelection, FFilter + [tkClass], FPropertyEditorHook,
1975      @AddPropertyEditor, @EditorFilter);
1976  end;
1977  // sort
1978  FRows.Sort(@SortGridRows);
1979  for a:=0 to FRows.Count-1 do begin
1980    if a>0 then
1981      Rows[a].FPriorBrother:=Rows[a-1]
1982    else
1983      Rows[a].FPriorBrother:=nil;
1984    if a<FRows.Count-1 then
1985      Rows[a].FNextBrother:=Rows[a+1]
1986    else
1987      Rows[a].FNextBrother:=nil;
1988  end;
1989  // set indices and tops
1990  SetItemsTops;
1991  // restore expands
1992  for a:=FExpandedProperties.Count-1 downto 0 do begin
1993    CurRow:=GetRowByPath(FExpandedProperties[a]);
1994    if CurRow<>nil then
1995      ExpandRow(CurRow.Index);
1996  end;
1997  // update scrollbar
1998  FTopY:=0;
1999  UpdateScrollBar;
2000  // reselect
2001  CurRow:=GetRowByPath(OldSelectedRowPath);
2002  if CurRow<>nil then
2003    ItemIndex:=CurRow.Index;
2004  Exclude(FStates, pgsFocusPropertyEditorDisabled);
2005  // paint
2006  Invalidate;
2007end;
2008
2009procedure TOICustomPropertyGrid.AddPropertyEditor(PropEditor: TPropertyEditor);
2010var
2011  NewRow: TOIPropertyGridRow;
2012  WidgetSets: TLCLPlatforms;
2013begin
2014  WidgetSets := [];
2015  if Favorites<>nil then begin
2016    //debugln('TOICustomPropertyGrid.AddPropertyEditor A ',PropEditor.GetName);
2017    if Favorites is TOIRestrictedProperties then
2018    begin
2019      WidgetSets := TOIRestrictedProperties(Favorites).AreRestricted(
2020                                                  Selection,PropEditor.GetName);
2021      if WidgetSets = [] then
2022      begin
2023        PropEditor.Free;
2024        Exit;
2025      end;
2026    end
2027    else
2028      if not Favorites.AreFavorites(Selection,PropEditor.GetName) then begin
2029        PropEditor.Free;
2030        exit;
2031      end;
2032  end;
2033  if PropEditor is TClassPropertyEditor then
2034  begin
2035    TClassPropertyEditor(PropEditor).SubPropsNameFilter := PropNameFilter;
2036    TClassPropertyEditor(PropEditor).SubPropsTypeFilter := FFilter;
2037    TClassPropertyEditor(PropEditor).HideClassName:=FHideClassNames;
2038  end;
2039  NewRow := TOIPropertyGridRow.Create(Self, PropEditor, nil, WidgetSets);
2040  FRows.Add(NewRow);
2041  if FRows.Count>1 then begin
2042    NewRow.FPriorBrother:=Rows[FRows.Count-2];
2043    NewRow.FPriorBrother.FNextBrother:=NewRow;
2044  end;
2045end;
2046
2047procedure TOICustomPropertyGrid.AddStringToComboBox(const s: string);
2048begin
2049  if FNewComboBoxItems=nil then
2050    FNewComboBoxItems:=TStringListUTF8Fast.Create;
2051  FNewComboBoxItems.Add(s);
2052end;
2053
2054procedure TOICustomPropertyGrid.ExpandRow(Index:integer);
2055var
2056  a: integer;
2057  CurPath: string;
2058  AlreadyInExpandList: boolean;
2059  ActiveRow: TOIPropertyGridRow;
2060begin
2061  // Save ItemIndex
2062  if ItemIndex <> -1 then
2063    ActiveRow := Rows[ItemIndex]
2064  else
2065    ActiveRow := nil;
2066  FExpandingRow := Rows[Index];
2067  if (FExpandingRow.Expanded) or (not CanExpandRow(FExpandingRow)) then
2068  begin
2069    FExpandingRow := nil;
2070    Exit;
2071  end;
2072  FExpandingRow.Editor.GetProperties(@AddSubEditor);
2073  SortSubEditors(FExpandingRow);
2074  SetItemsTops;
2075  FExpandingRow.FExpanded := True;
2076  a := 0;
2077  CurPath:=PropertyPath(FExpandingRow.Index);
2078  AlreadyInExpandList:=false;
2079  while a < FExpandedProperties.Count do
2080  begin
2081    if LazStartsText(FExpandedProperties[a], CurPath) then
2082    begin
2083      if Length(FExpandedProperties[a]) = Length(CurPath) then
2084      begin
2085        AlreadyInExpandList := True;
2086        inc(a);
2087      end
2088      else
2089        FExpandedProperties.Delete(a);
2090    end
2091    else
2092      inc(a);
2093  end;
2094  if not AlreadyInExpandList then
2095    FExpandedProperties.Add(CurPath);
2096  FExpandingRow := nil;
2097  // restore ItemIndex
2098  if ActiveRow <> nil then
2099    FItemIndex := ActiveRow.Index
2100  else
2101    FItemIndex := -1;
2102  UpdateScrollBar;
2103  Invalidate;
2104end;
2105
2106procedure TOICustomPropertyGrid.ShrinkRow(Index:integer);
2107var
2108  CurRow, ARow: TOIPropertyGridRow;
2109  StartIndex, EndIndex, a: integer;
2110  CurPath: string;
2111begin
2112  CurRow := Rows[Index];
2113  if (not CurRow.Expanded) then
2114    Exit;
2115  // calculate all children (between StartIndex..EndIndex)
2116  StartIndex := CurRow.Index + 1;
2117  EndIndex := FRows.Count - 1;
2118  ARow := CurRow;
2119  while ARow <> nil do
2120  begin
2121    if ARow.NextBrother <> nil then
2122    begin
2123      EndIndex := ARow.NextBrother.Index - 1;
2124      break;
2125    end;
2126    ARow := ARow.Parent;
2127  end;
2128  if (FItemIndex >= StartIndex) and (FItemIndex <= EndIndex) then
2129    // current row delete, set new current row
2130    ItemIndex:=0
2131  else
2132  if FItemIndex > EndIndex then
2133    // adjust current index for deleted rows
2134    FItemIndex := FItemIndex - (EndIndex - StartIndex + 1);
2135  for a := EndIndex downto StartIndex do
2136  begin
2137    Rows[a].Free;
2138    FRows.Delete(a);
2139  end;
2140  SetItemsTops;
2141  CurRow.FExpanded := False;
2142  CurPath := PropertyPath(CurRow.Index);
2143  a := 0;
2144  while a < FExpandedProperties.Count do
2145  begin
2146    if LazStartsText(CurPath, FExpandedProperties[a]) then
2147      FExpandedProperties.Delete(a)
2148    else
2149      inc(a);
2150  end;
2151  if CurRow.Parent <> nil then
2152    FExpandedProperties.Add(PropertyPath(CurRow.Parent.Index));
2153  UpdateScrollBar;
2154  Invalidate;
2155end;
2156
2157procedure TOICustomPropertyGrid.AddSubEditor(PropEditor:TPropertyEditor);
2158var
2159  NewRow:TOIPropertyGridRow;
2160  NewIndex:integer;
2161begin
2162  if not EditorFilter(PropEditor) then
2163  begin
2164    PropEditor.Free;
2165    Exit;
2166  end;
2167
2168  if PropEditor is TClassPropertyEditor then
2169  begin
2170    TClassPropertyEditor(PropEditor).SubPropsNameFilter := PropNameFilter;
2171    TClassPropertyEditor(PropEditor).SubPropsTypeFilter := FFilter;
2172    TClassPropertyEditor(PropEditor).HideClassName:=FHideClassNames;
2173  end;
2174  NewRow:=TOIPropertyGridRow.Create(Self,PropEditor,FExpandingRow, []);
2175  NewIndex:=FExpandingRow.Index+1+FExpandingRow.ChildCount;
2176  NewRow.FIndex:=NewIndex;
2177  FRows.Insert(NewIndex,NewRow);
2178  if NewIndex<FItemIndex
2179    then inc(FItemIndex);
2180  if FExpandingRow.FFirstChild=nil then
2181    FExpandingRow.FFirstChild:=NewRow;
2182  NewRow.FPriorBrother:=FExpandingRow.FLastChild;
2183  FExpandingRow.FLastChild:=NewRow;
2184  if NewRow.FPriorBrother<>nil then
2185    NewRow.FPriorBrother.FNextBrother:=NewRow;
2186  inc(FExpandingRow.FChildCount);
2187end;
2188
2189procedure TOICustomPropertyGrid.SortSubEditors(ParentRow: TOIPropertyGridRow);
2190var
2191  Item: TOIPropertyGridRow;
2192  Index: Integer;
2193  Next: TOIPropertyGridRow;
2194begin
2195  if not ParentRow.Sort(@SortGridRows) then exit;
2196  // update FRows
2197  Item:=ParentRow.FirstChild;
2198  Index:=ParentRow.Index+1;
2199  Next:=ParentRow.NextSkipChilds;
2200  while (Item<>nil) and (Item<>Next) do begin
2201    FRows[Index]:=Item;
2202    Item.FIndex:=Index;
2203    Item:=Item.Next;
2204    inc(Index);
2205  end;
2206end;
2207
2208function TOICustomPropertyGrid.CanExpandRow(Row: TOIPropertyGridRow): boolean;
2209var
2210  AnObject: TPersistent;
2211  ParentRow: TOIPropertyGridRow;
2212begin
2213  Result:=false;
2214  if (Row=nil) or (Row.Editor=nil) then exit;
2215  if (not (paSubProperties in Row.Editor.GetAttributes)) then exit;
2216  // check if circling
2217  if (Row.Editor is TPersistentPropertyEditor) then begin
2218    if (Row.Editor is TInterfacePropertyEditor) then
2219      AnObject:={%H-}TPersistent(Row.Editor.GetIntfValue)
2220    else
2221      AnObject:=TPersistent(Row.Editor.GetObjectValue);
2222    if FSelection.IndexOf(AnObject)>=0 then exit;
2223    ParentRow:=Row.Parent;
2224    while ParentRow<>nil do begin
2225      if (ParentRow.Editor is TPersistentPropertyEditor)
2226      and (ParentRow.Editor.GetObjectValue=AnObject) then
2227        exit;
2228      ParentRow:=ParentRow.Parent;
2229    end;
2230  end;
2231  Result:=true;
2232end;
2233
2234function TOICustomPropertyGrid.MouseToIndex(y: integer; MustExist: boolean): integer;
2235var l,r,m:integer;
2236begin
2237  l:=0;
2238  r:=FRows.Count-1;
2239  inc(y,FTopY);
2240  while (l<=r) do
2241  begin
2242    m:=(l+r) shr 1;
2243    if Rows[m].Top>y then
2244      r:=m-1
2245    else if Rows[m].Bottom<=y then
2246      l:=m+1
2247    else
2248      Exit(m);
2249  end;
2250  if (MustExist=false) and (FRows.Count>0) then begin
2251    if y<0 then
2252      Result:=0
2253    else
2254      Result:=FRows.Count-1;
2255  end else
2256    Result:=-1;
2257end;
2258
2259function TOICustomPropertyGrid.GetActiveRow: TOIPropertyGridRow;
2260begin
2261  if InRange(ItemIndex,0,FRows.Count-1) then
2262    Result:=Rows[ItemIndex]
2263  else
2264    Result:=nil;
2265end;
2266
2267procedure TOICustomPropertyGrid.SetCurrentRowValue(const NewValue: string);
2268begin
2269  if not CanEditRowValue(false) or Rows[FItemIndex].IsReadOnly then exit;
2270  // SetRowValue reads the value from the current edit control and writes it
2271  // to the property editor
2272  // -> set the text in the current edit control without changing FLastEditValue
2273  SetCurrentEditValue(NewValue);
2274  SetRowValue(false, true);
2275end;
2276
2277procedure TOICustomPropertyGrid.SetItemIndexAndFocus(NewItemIndex: integer;
2278                                                     WasValueClick: Boolean);
2279begin
2280  if not InRange(NewItemIndex, 0, FRows.Count - 1) then exit;
2281  ItemIndex:=NewItemIndex;
2282  if FCurrentEdit<>nil then
2283  begin
2284    SetActiveControl(FCurrentEdit);
2285    if (FCurrentEdit is TCustomEdit) then
2286      TCustomEdit(FCurrentEdit).SelectAll
2287    {$IFnDEF UseOINormalCheckBox}
2288    else if (FCurrentEdit is TCheckBoxThemed) and WasValueClick then
2289      TCheckBoxThemed(FCurrentEdit).Checked:=not TCheckBoxThemed(FCurrentEdit).Checked;
2290    {$ELSE}
2291    else if (FCurrentEdit is TCheckBox) and WasValueClick then
2292      TCheckBox(FCurrentEdit).Checked:=not TCheckBox(FCurrentEdit).Checked;
2293    {$ENDIF}
2294  end;
2295end;
2296
2297function TOICustomPropertyGrid.CanEditRowValue(CheckFocus: boolean): boolean;
2298var
2299  FocusedControl: TWinControl;
2300begin
2301  Result:=
2302    not GridIsUpdating and IsCurrentEditorAvailable
2303    and (not (pgsCallingEdit in FStates))
2304    and ((FCurrentEditorLookupRoot = nil)
2305      or (FPropertyEditorHook = nil)
2306      or (FPropertyEditorHook.LookupRoot = FCurrentEditorLookupRoot));
2307  if Result and CheckFocus then begin
2308    FocusedControl:=FindOwnerControl(GetFocus);
2309    if (FocusedControl<>nil) and (FocusedControl<>Self)
2310    and (not IsParentOf(FocusedControl)) then
2311      Result:=false;
2312  end;
2313  if Result then begin
2314    {DebugLn(['TOICustomPropertyGrid.CanEditRowValue',
2315      ' pgsChangingItemIndex=',pgsChangingItemIndex in FStates,
2316      ' pgsApplyingValue=',pgsApplyingValue in FStates,
2317      ' pgsUpdatingEditControl=',pgsUpdatingEditControl in FStates,
2318      ' FCurrentEdit=',dbgsName(FCurrentEdit),
2319      ' FItemIndex=',FItemIndex,
2320      ' FCurrentEditorLookupRoot=',dbgsName(FCurrentEditorLookupRoot),
2321      ' FPropertyEditorHook.LookupRoot=',dbgsName(FPropertyEditorHook.LookupRoot)
2322      ]);}
2323  end;
2324end;
2325
2326procedure TOICustomPropertyGrid.SaveChanges;
2327begin
2328  SetRowValue(true, false);
2329end;
2330
2331function TOICustomPropertyGrid.GetHintTypeAt(RowIndex: integer; X: integer): TPropEditHint;
2332var
2333  IconX: integer;
2334begin
2335  Result := pehNone;
2336  if (RowIndex < 0) or (RowIndex >= RowCount) then
2337    Exit;
2338  if SplitterX <= X then
2339  begin
2340    if (FCurrentButton <> nil) and (FCurrentButton.Left <= X) then
2341      Result := pehEditButton
2342    else
2343      Result := pehValue;
2344  end else
2345  begin
2346    IconX := GetTreeIconX(RowIndex);
2347    if IconX + Indent > X then
2348      Result := pehTree
2349    else
2350      Result := pehName;
2351  end;
2352end;
2353
2354procedure TOICustomPropertyGrid.MouseDown(Button:TMouseButton; Shift:TShiftState;
2355  X,Y:integer);
2356var
2357  IconX,Index:integer;
2358  PointedRow:TOIpropertyGridRow;
2359  Details: TThemedElementDetails;
2360  Sz: TSize;
2361begin
2362  //ShowMessageDialog('X'+IntToStr(X)+',Y'+IntToStr(Y));
2363  inherited MouseDown(Button,Shift,X,Y);
2364
2365  HideHint;
2366
2367  if Button=mbLeft then begin
2368    FFirstClickTime:=GetTickCount;
2369    if Cursor=crHSplit then
2370      FDragging:=true
2371    else
2372    begin
2373      Index:=MouseToIndex(Y,false);
2374      if (Index>=0) and (Index<FRows.Count) then
2375      begin
2376        PointedRow:=Rows[Index];
2377        if CanExpandRow(PointedRow) then
2378        begin
2379          IconX:=GetTreeIconX(Index);
2380          if ((X>=IconX) and (X<=IconX+FIndent)) or (ssDouble in Shift) then
2381          begin
2382            if PointedRow.Expanded then
2383              ShrinkRow(Index)
2384            else
2385              ExpandRow(Index);
2386          end;
2387        end;
2388        // WasValueClick param is only for Boolean checkboxes, toggled if user
2389        //  clicks the square. It has no effect for Boolean ComboBox editor.
2390        Details := ThemeServices.GetElementDetails(tbCheckBoxCheckedNormal);
2391        Sz := ThemeServices.GetDetailSize(Details);
2392        SetItemIndexAndFocus(Index, (X>SplitterX) and (X<=SplitterX+Sz.cx));
2393        SetCaptureControl(Self);
2394        Column := oipgcValue;
2395      end;
2396    end;
2397  end;
2398end;
2399
2400procedure TOICustomPropertyGrid.MouseLeave;
2401begin
2402  if Assigned(FHintManager) and Assigned(FHintManager.CurHintWindow)
2403  and FHintManager.CurHintWindow.Visible
2404  and not PtInRect(ClientRect, ScreenToClient(Mouse.CursorPos)) then
2405    FHintManager.HideHint;
2406
2407  inherited MouseLeave;
2408end;
2409
2410procedure TOICustomPropertyGrid.MouseMove(Shift:TShiftState; X,Y:integer);
2411var
2412  TheHint: String;
2413  HintType: TPropEditHint;
2414  fPropRow: TOIPropertyGridRow;
2415
2416  procedure ShowShortHint(pt: TPoint); inline;
2417  //var HintFont: TFont;
2418  begin
2419    if WidgetSet.GetLCLCapability(lcTransparentWindow)=LCL_CAPABILITY_NO then
2420      Inc(pt.Y, fPropRow.Height);
2421{ By Juha :
2422  FValueFont and FDefaultValueFont are nearly unreadable.
2423  We should maybe get their negated color as the hint background is black.
2424
2425    if HintType<>pehValue then
2426      HintFont := Screen.HintFont
2427    else
2428    if fPropRow.Editor.ValueIsStreamed then
2429      HintFont:=FValueFont
2430    else
2431      HintFont:=FDefaultValueFont;  }
2432    FHintManager.ShowHint(ClientToScreen(pt), TheHint, False{, HintFont});
2433    if FHintManager.CurHintWindow<>nil then
2434      FHintManager.CurHintWindow.OnMouseLeave := @HintMouseLeave;
2435  end;
2436
2437var
2438  SplitDistance, Index, TextLeft: Integer;
2439  HintWillChange: Boolean;
2440begin
2441  inherited MouseMove(Shift,X,Y);
2442  SplitDistance := X-SplitterX;
2443  if FDragging then
2444  begin
2445    HideHint;
2446    if ssLeft in Shift then
2447      SplitterX:=SplitterX+SplitDistance
2448    else
2449      EndDragSplitter;
2450  end
2451  else begin
2452    if abs(SplitDistance) <= 2 then
2453      Cursor := crHSplit
2454    else
2455      Cursor := crDefault;
2456    if ssLeft in Shift then
2457    begin
2458      Index := MouseToIndex(Y, False);
2459      SetItemIndexAndFocus(Index);
2460      SetCaptureControl(Self);
2461    end;
2462    // The following code handler 2 kinds of hints :
2463    // 1. Property's name / value when it does not fit in the cell.
2464    // 2. Long description of a property / value, only when ShowHint option is set.
2465    Index := MouseToIndex(y,false);
2466    HintType := GetHintTypeAt(Index, x);
2467    HintWillChange := (Index<>FHintIndex) or (HintType<>FHintType);
2468    if HintWillChange then
2469      HideHint;                        // hide the hint of an earlier row
2470    // Don't show any more hints if the long hint is there.
2471    if FShowingLongHint or (Index = -1) then Exit;
2472    // Show the property text as a hint if it does not fit in its box.
2473    if HintWillChange or not FHintManager.HintIsVisible then
2474    begin
2475      FHintIndex := Index;
2476      FHintType := HintType;
2477      fPropRow := GetRow(Index);
2478      if HintType = pehName then
2479      begin                            // Mouse is over property name...
2480        TheHint := fPropRow.Name;
2481        TextLeft := BorderWidth + GetTreeIconX(Index) + Indent + 5;
2482        if (Canvas.TextWidth(TheHint) + TextLeft) >= SplitterX-2 then
2483          ShowShortHint(Point(TextLeft-3, fPropRow.Top-TopY-1));
2484      end else
2485      if HintType in [pehValue,pehEditButton] then
2486      begin                            // Mouse is over property value...
2487        TheHint := fPropRow.LastPaintedValue;
2488        if length(TheHint) > 100 then
2489          TheHint := copy(TheHint, 1, 100) + '...';
2490        TextLeft := SplitterX+2;
2491        if Canvas.TextWidth(TheHint) > (ClientWidth - BorderWidth - TextLeft) then
2492          ShowShortHint(Point(TextLeft-3, fPropRow.Top-TopY-1));
2493      end;
2494    end;
2495    // Initialize timer for a long hint describing the property and value.
2496    if not ShowHint then Exit;
2497    if FLongHintTimer = nil then
2498    begin
2499      FHintIndex := -1;
2500      FLongHintTimer := TTimer.Create(nil);
2501      FLongHintTimer.Interval := 500;
2502      FLongHintTimer.Enabled := False;
2503      FLongHintTimer.OnTimer := @HintTimer;
2504      FHintManager.OnMouseDown := @HintMouseDown;
2505      FHintManager.WindowName := 'This_is_a_hint_window';
2506      FHintManager.HideInterval := 4000;
2507      FHintManager.AutoHide := True;
2508    end;
2509    FLongHintTimer.Enabled := RowCount > 0;
2510  end; // not FDragging
2511end;
2512
2513procedure TOICustomPropertyGrid.MouseUp(Button:TMouseButton; Shift:TShiftState;
2514  X,Y:integer);
2515begin
2516  if FDragging then EndDragSplitter;
2517  SetCaptureControl(nil);
2518  inherited MouseUp(Button,Shift,X,Y);
2519end;
2520
2521procedure TOICustomPropertyGrid.KeyDown(var Key: Word; Shift: TShiftState);
2522begin
2523  HandleStandardKeys(Key,Shift);
2524  inherited KeyDown(Key, Shift);
2525end;
2526
2527procedure TOICustomPropertyGrid.HandleStandardKeys(var Key: Word; Shift: TShiftState);
2528var
2529  Handled: Boolean;
2530
2531  procedure FindPropertyBySearchText;
2532  var
2533    i, IIndex: Integer;
2534  begin
2535    if Column = oipgcName then
2536    begin
2537      FKeySearchText := FKeySearchText + UpCase(Chr(Key));
2538      if ItemIndex = -1 then
2539        IIndex := 0
2540      else
2541        IIndex := ItemIndex;
2542      for i := 0 to RowCount - 1 do
2543        if (Rows[i].Lvl = Rows[IIndex].Lvl)
2544        and LazStartsText(FKeySearchText, Rows[i].Name) then
2545        begin
2546          // Set item index. To go to Value user must hit either Tab or Enter.
2547          SetItemIndex(i);
2548          exit;
2549        end;
2550      // Left part of phrase not matched, remove added char.
2551      SetLength(FKeySearchText, Length(FKeySearchText) - 1);
2552    end;
2553    Handled := false;
2554  end;
2555
2556  procedure HandleUnshifted;
2557  const
2558    Page = 20;
2559  begin
2560    Handled := true;
2561    case Key of
2562      VK_UP   : SetItemIndexAndFocus(ItemIndex - 1);
2563      VK_DOWN : SetItemIndexAndFocus(ItemIndex + 1);
2564      VK_PRIOR: SetItemIndexAndFocus(Max(ItemIndex - Page, 0));
2565      VK_NEXT : SetItemIndexAndFocus(Min(ItemIndex + Page, FRows.Count - 1));
2566
2567      VK_TAB: DoTabKey;
2568
2569      VK_RETURN:
2570        begin
2571          if Column = oipgcName then
2572            DoTabKey
2573          else
2574            SetRowValue(false, true);
2575          if FCurrentEdit is TCustomEdit then
2576            TCustomEdit(FCurrentEdit).SelectAll;
2577        end;
2578
2579      VK_ESCAPE:
2580        begin
2581          RefreshValueEdit;
2582          FKeySearchText := '';
2583        end;
2584
2585      VK_BACK:
2586        begin
2587          if (Column = oipgcName) then
2588            if (FKeySearchText <> '') then
2589              SetLength(FKeySearchText, Length(FKeySearchText) - 1);
2590          Handled := False;
2591        end;
2592
2593      Ord('A')..Ord('Z'): FindPropertyBySearchText;
2594
2595      else
2596        Handled := false;
2597    end;
2598  end;
2599
2600begin
2601  //writeln('TOICustomPropertyGrid.HandleStandardKeys ',Key);
2602  Handled := false;
2603  if (Shift = []) or (Shift = [ssShift]) then
2604  begin
2605    if not (FCurrentEdit is TCustomCombobox) or
2606       not TCustomCombobox(FCurrentEdit).DroppedDown then
2607      HandleUnshifted;
2608  end
2609  else
2610  if Shift = [ssCtrl] then
2611  begin
2612    case Key of
2613      VK_RETURN:
2614        begin
2615          ToggleRow;
2616          Handled := true;
2617        end;
2618    end;
2619  end
2620  else
2621  if Shift = [ssAlt] then
2622    case Key of
2623      VK_LEFT:
2624        begin
2625          Handled := (ItemIndex >= 0) and Rows[ItemIndex].Expanded;
2626          if Handled then ShrinkRow(ItemIndex);
2627        end;
2628
2629      VK_RIGHT:
2630        begin
2631          Handled := (ItemIndex >= 0) and not Rows[ItemIndex].Expanded and
2632            CanExpandRow(Rows[ItemIndex]);
2633          if Handled then ExpandRow(ItemIndex)
2634        end;
2635    end;
2636
2637
2638  if not Handled and Assigned(OnOIKeyDown) then
2639  begin
2640    OnOIKeyDown(Self, Key, Shift);
2641    Handled := Key = VK_UNKNOWN;
2642  end;
2643
2644  //writeln('TOICustomPropertyGrid.HandleStandardKeys ',Key,' Handled=',Handled);
2645  if Handled then
2646    Key := VK_UNKNOWN;
2647end;
2648
2649procedure TOICustomPropertyGrid.HandleKeyUp(var Key: Word; Shift: TShiftState);
2650begin
2651  if (Key<>VK_UNKNOWN) and Assigned(OnKeyUp) then
2652    OnKeyUp(Self,Key,Shift);
2653end;
2654
2655procedure TOICustomPropertyGrid.DoTabKey;
2656begin
2657  if Column = oipgcValue then
2658  begin
2659    Column := oipgcName;
2660    Self.SetFocus;
2661  end else
2662  begin
2663    Column := oipgcValue;
2664    if FCurrentEdit <> nil then
2665      FCurrentEdit.SetFocus;
2666  end;
2667  FKeySearchText := '';
2668end;
2669
2670function TOICustomPropertyGrid.EditorFilter(const AEditor: TPropertyEditor): Boolean;
2671begin
2672  Result := IsInteresting(AEditor, FFilter, PropNameFilter);
2673  if Result and Assigned(OnEditorFilter) then
2674    OnEditorFilter(Self,AEditor,Result);
2675end;
2676
2677procedure TOICustomPropertyGrid.EraseBackground(DC: HDC);
2678begin
2679  // everything is painted, so erasing the background is not needed
2680end;
2681
2682procedure TOICustomPropertyGrid.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);
2683begin
2684  inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);
2685  UpdateScrollBar;
2686end;
2687
2688procedure TOICustomPropertyGrid.DoSelectionChange;
2689begin
2690  if Assigned(FOnSelectionChange) then
2691    FOnSelectionChange(Self);
2692end;
2693
2694procedure TOICustomPropertyGrid.HintMouseDown(Sender: TObject;
2695  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
2696var
2697  pos: TPoint;
2698begin
2699  if FHintManager.HintIsVisible then begin
2700    pos := ScreenToClient(FHintManager.CurHintWindow.ClientToScreen(Point(X, Y)));
2701    MouseDown(Button, Shift, pos.X, pos.Y);
2702  end;
2703end;
2704
2705procedure TOICustomPropertyGrid.HintMouseLeave(Sender: TObject);
2706begin
2707  if FindLCLControl(Mouse.CursorPos)<>Self then
2708    FHintManager.HideHint;
2709end;
2710
2711procedure TOICustomPropertyGrid.EndDragSplitter;
2712begin
2713  if FDragging then begin
2714    Cursor:=crDefault;
2715    FDragging:=false;
2716    FPreferredSplitterX:=FSplitterX;
2717    if FCurrentEdit<>nil then begin
2718      SetCaptureControl(nil);
2719      if Column=oipgcValue then
2720        FCurrentEdit.SetFocus
2721      else
2722        Self.SetFocus;
2723    end;
2724  end;
2725end;
2726
2727procedure TOICustomPropertyGrid.SetReadOnlyColor(const AValue: TColor);
2728begin
2729  if FReadOnlyColor = AValue then Exit;
2730  FReadOnlyColor := AValue;
2731  Invalidate;
2732end;
2733
2734procedure TOICustomPropertyGrid.SetRowSpacing(const AValue: integer);
2735begin
2736  if FRowSpacing = AValue then exit;
2737  FRowSpacing := AValue;
2738  SetItemsTops;
2739end;
2740
2741procedure TOICustomPropertyGrid.SetShowGutter(const AValue: Boolean);
2742begin
2743  if FShowGutter=AValue then exit;
2744  FShowGutter:=AValue;
2745  invalidate;
2746end;
2747
2748procedure TOICustomPropertyGrid.SetSplitterX(const NewValue:integer);
2749var AdjustedValue:integer;
2750begin
2751  AdjustedValue:=NewValue;
2752  if AdjustedValue>ClientWidth then AdjustedValue:=ClientWidth;
2753  if AdjustedValue<1 then AdjustedValue:=1;
2754  if FSplitterX<>AdjustedValue then begin
2755    FSplitterX:=AdjustedValue;
2756    AlignEditComponents;
2757    Repaint;
2758  end;
2759end;
2760
2761procedure TOICustomPropertyGrid.SetTopY(const NewValue:integer);
2762var
2763  NewTopY, d: integer;
2764  f: UINT;
2765begin
2766  NewTopY := TopMax;
2767  if NewValue < NewTopY then
2768    NewTopY := NewValue;
2769  if NewTopY < 0 then
2770    NewTopY := 0;
2771  if FTopY<>NewTopY then begin
2772    f := SW_INVALIDATE;
2773    d := FTopY-NewTopY;
2774    // SW_SCROLLCHILDREN can only be used, if the active editor is not
2775    // "scrolling in" (i.e., partly outside the clientrect)
2776    if (FCurrentEdit = nil) or
2777       ( (d > 0) and (FCurrentEdit.Top >= 0) ) or
2778       ( (d < 0) and (FCurrentEdit.Top <  Height - FCurrentEdit.Height) )
2779    then
2780      f := f + SW_SCROLLCHILDREN;
2781    if not ScrollWindowEx(Handle,0,d,nil,nil,0,nil, f) then
2782      Invalidate;
2783    FTopY:=NewTopY;
2784    UpdateScrollBar;
2785    AlignEditComponents;
2786  end;
2787end;
2788
2789function TOICustomPropertyGrid.GetPropNameColor(ARow:TOIPropertyGridRow):TColor;
2790
2791 function HasWriter(APropInfo: PPropInfo): Boolean; inline;
2792 begin
2793   Result := Assigned(APropInfo) and Assigned(APropInfo^.SetProc);
2794 end;
2795
2796var
2797  ParentRow:TOIPropertyGridRow;
2798  IsObjectSubProperty:Boolean;
2799begin
2800  // Try to guest if ARow, or one of its parents, is a subproperty
2801  // of an object (and not an item of a set)
2802  IsObjectSubProperty:=false;
2803  ParentRow:=ARow.Parent;
2804  while Assigned(ParentRow) do
2805  begin
2806    if ParentRow.Editor is TPersistentPropertyEditor then
2807      IsObjectSubProperty:=true;
2808    ParentRow:=ParentRow.Parent;
2809  end;
2810
2811  if (ItemIndex <> -1) and (ItemIndex = ARow.Index) then
2812    Result := FHighlightFont.Color
2813  else
2814  if not HasWriter(ARow.Editor.GetPropInfo) then
2815    Result := FReadOnlyColor
2816  else
2817  if ARow.Editor is TPersistentPropertyEditor then
2818    Result := FReferencesColor
2819  else
2820  if IsObjectSubProperty then
2821    Result := FSubPropertiesColor
2822  else
2823    Result := FNameFont.Color;
2824end;
2825
2826procedure TOICustomPropertyGrid.SetBounds(aLeft,aTop,aWidth,aHeight:integer);
2827begin
2828//writeln('[TOICustomPropertyGrid.SetBounds] ',Name,' ',aLeft,',',aTop,',',aWidth,',',aHeight,' Visible=',Visible);
2829  inherited SetBounds(aLeft,aTop,aWidth,aHeight);
2830  if Visible then begin
2831    if not FDragging then begin
2832      if (SplitterX<5) and (aWidth>20) then
2833        SplitterX:=100
2834      else
2835        SplitterX:=FPreferredSplitterX;
2836    end;
2837    AlignEditComponents;
2838  end;
2839end;
2840
2841function TOICustomPropertyGrid.GetTreeIconX(Index:integer):integer;
2842begin
2843  Result:=Rows[Index].Lvl*Indent+2;
2844end;
2845
2846function TOICustomPropertyGrid.TopMax:integer;
2847begin
2848  Result:=GridHeight-ClientHeight+2*integer(BorderWidth);
2849  if Result<0 then Result:=0;
2850end;
2851
2852function TOICustomPropertyGrid.GridHeight:integer;
2853begin
2854  if FRows.Count>0 then
2855    Result:=Rows[FRows.Count-1].Bottom
2856  else
2857    Result:=0;
2858end;
2859
2860procedure TOICustomPropertyGrid.AlignEditComponents;
2861var
2862  RRect, EditCompRect, EditBtnRect: TRect;
2863begin
2864  if ItemIndex>=0 then
2865  begin
2866    RRect := RowRect(ItemIndex);
2867    InflateRect(RRect, 0, 1);
2868    EditCompRect := RRect;
2869
2870    if Layout = oilHorizontal then
2871      EditCompRect.Left := RRect.Left + SplitterX
2872    else begin
2873      EditCompRect.Top := RRect.Top + GetNameRowHeight;
2874      EditCompRect.Left := RRect.Left + GetTreeIconX(ItemIndex) + Indent;
2875    end;
2876
2877    if FCurrentButton<>nil then
2878    begin
2879      // edit dialog button
2880      with EditBtnRect do begin
2881        Top := EditCompRect.Top;
2882        Left := EditCompRect.Right - Scale96ToForm(20);
2883        Bottom := EditCompRect.Bottom - 1;
2884        Right := EditCompRect.Right;
2885        EditCompRect.Right := Left;
2886      end;
2887      if FCurrentButton.BoundsRect <> EditBtnRect then
2888        FCurrentButton.BoundsRect := EditBtnRect;
2889      //DebugLn(['TOICustomPropertyGrid.AlignEditComponents FCurrentButton.BoundsRect=',dbgs(FCurrentButton.BoundsRect),' EditBtnRect=',dbgs(EditBtnRect)]);
2890    end;
2891    if FCurrentEdit<>nil then
2892    begin
2893      // resize the edit component
2894      if (FCurrentEdit is TEdit) or (FCurrentEdit is TComboBox) then
2895      begin
2896        Dec(EditCompRect.Top);
2897      {$IFDEF UseOINormalCheckBox}
2898      end
2899      else if FCurrentEdit is TCheckBox then
2900      begin
2901        with EditCompRect do  // Align "normal" CheckBox to the middle vertically
2902          Inc(Top, (Bottom - Top - ValueCheckBox.Height) div 2);
2903      {$ELSE}
2904      end
2905      else if FCurrentEdit is TCheckBoxThemed then
2906      begin             // Move right as much as in TPropertyEditor.DrawCheckValue.
2907        Inc(EditCompRect.Left, CheckBoxThemedLeftOffs);
2908      {$ENDIF}
2909      end;
2910      //debugln('TOICustomPropertyGrid.AlignEditComponents A ',dbgsName(FCurrentEdit),' ',dbgs(EditCompRect));
2911      if ( ( (FCurrentEdit.BoundsRect.Bottom >= 0) and (FCurrentEdit.BoundsRect.Top <= Height) ) or
2912           ( (EditCompRect.Bottom >= 0) and (EditCompRect.Top <= Height) ) ) and
2913         ( FCurrentEdit.BoundsRect <> EditCompRect )
2914      then begin
2915        FCurrentEdit.BoundsRect := EditCompRect;
2916      end;
2917    end;
2918  end;
2919end;
2920
2921procedure TOICustomPropertyGrid.PaintRow(ARow: integer);
2922var
2923  FullRect, NameRect, NameTextRect, NameIconRect, ValueRect: TRect;
2924  CurRow: TOIPropertyGridRow;
2925
2926  procedure ClearBackground;
2927  var
2928    DrawValuesDiffer: Boolean;
2929  begin
2930    DrawValuesDiffer := (FValueDifferBackgrndColor<>clNone) and not CurRow.Editor.AllEqual;
2931    if FBackgroundColor <> clNone then
2932    begin
2933      Canvas.Brush.Color := FBackgroundColor;
2934      if DrawValuesDiffer then
2935        Canvas.FillRect(NameRect)
2936      else
2937        Canvas.FillRect(FullRect);
2938    end;
2939    if DrawValuesDiffer then
2940    begin
2941      // Make the background color darker than what the active edit control has.
2942      Canvas.Brush.Color := FValueDifferBackgrndColor - $282828;
2943      Canvas.FillRect(ValueRect);
2944    end;
2945    if ShowGutter and (Layout = oilHorizontal) and
2946       (FGutterColor <> FBackgroundColor) and (FGutterColor <> clNone) then
2947    begin
2948      Canvas.Brush.Color := FGutterColor;
2949      Canvas.FillRect(NameIconRect);
2950    end;
2951  end;
2952
2953  procedure DrawIcon(IconX: integer);
2954  var
2955    Details: TThemedElementDetails;
2956    sz: TSize;
2957    IconY: integer;
2958    Res: TScaledImageListResolution;
2959  begin
2960    if CurRow.Expanded then
2961      Details := ThemeServices.GetElementDetails(ttGlyphOpened)
2962    else
2963      Details := ThemeServices.GetElementDetails(ttGlyphClosed);
2964    if CanExpandRow(CurRow) then
2965    begin
2966      sz := ThemeServices.GetDetailSize(Details);
2967      IconY:=((NameRect.Bottom - NameRect.Top - sz.cy) div 2) + NameRect.Top;
2968      ThemeServices.DrawElement(Canvas.Handle, Details,
2969                                Rect(IconX, IconY, IconX + sz.cx, IconY + sz.cy), nil)
2970    end else
2971    if (ARow = FItemIndex) then
2972    begin
2973      Res := FActiveRowImages.ResolutionForControl[0, Self];
2974
2975      IconY:=((NameRect.Bottom - NameRect.Top - Res.Height) div 2) + NameRect.Top;
2976      Res.Draw(Canvas, IconX, IconY, FActiveRowImages.GetImageIndex('pg_active_row'));
2977    end;
2978  end;
2979
2980  procedure DrawName(DrawState: TPropEditDrawState);
2981  var
2982    OldFont: TFont;
2983    NameBgColor: TColor;
2984  begin
2985    if (ARow = FItemIndex) and (FHighlightColor <> clNone) then
2986      NameBgColor := FHighlightColor
2987    else
2988      NameBgColor := FBackgroundColor;
2989    OldFont:=Canvas.Font;
2990    Canvas.Font:=FNameFont;
2991    Canvas.Font.Color := GetPropNameColor(CurRow);
2992    // set bg color to highlight if needed
2993    if (NameBgColor <> FBackgroundColor) and (NameBgColor <> clNone) then
2994    begin
2995      Canvas.Brush.Color := NameBgColor;
2996      Canvas.FillRect(NameTextRect);
2997    end;
2998    CurRow.Editor.PropDrawName(Canvas, NameTextRect, DrawState);
2999    Canvas.Font := OldFont;
3000    if FBackgroundColor <> clNone then // return color back to background
3001      Canvas.Brush.Color := FBackgroundColor;
3002  end;
3003
3004  procedure DrawWidgetsets;
3005  var
3006    OldFont: TFont;
3007    X, Y: Integer;
3008    lclPlatform: TLCLPlatform;
3009    ImagesRes: TScaledImageListResolution;
3010  begin
3011    ImagesRes := IDEImages.Images_16.ResolutionForPPI[0, Font.PixelsPerInch, GetCanvasScaleFactor];
3012    X := NameRect.Right - 2;
3013    Y := (NameRect.Top + NameRect.Bottom - ImagesRes.Height) div 2;
3014    OldFont:=Canvas.Font;
3015    Canvas.Font:=FNameFont;
3016    Canvas.Font.Color := clRed;
3017    for lclPlatform := High(TLCLPlatform) downto Low(TLCLPlatform) do
3018    begin
3019      if lclPlatform in CurRow.FWidgetSets then
3020      begin
3021        Dec(X, ImagesRes.Width);
3022        ImagesRes.Draw(Canvas, X, Y,
3023          IDEImages.LoadImage('issue_'+LCLPlatformDirNames[lclPlatform]));
3024      end;
3025    end;
3026    Canvas.Font:=OldFont;
3027  end;
3028
3029  procedure DrawValue(DrawState: TPropEditDrawState);
3030  var
3031    OldFont: TFont;
3032  begin
3033    if ARow<>ItemIndex then
3034    begin
3035      OldFont:=Canvas.Font;
3036      if CurRow.Editor.ValueIsStreamed then
3037        Canvas.Font:=FValueFont
3038      else
3039        Canvas.Font:=FDefaultValueFont;
3040      CurRow.Editor.PropDrawValue(Canvas,ValueRect,DrawState);
3041      Canvas.Font:=OldFont;
3042    end;
3043    CurRow.LastPaintedValue:=CurRow.Editor.GetVisualValue;
3044  end;
3045
3046  procedure DrawGutterToParent;
3047  var
3048    ParentRect: TRect;
3049    X: Integer;
3050  begin
3051    if ARow > 0 then
3052    begin
3053      ParentRect := RowRect(ARow - 1);
3054      X := ParentRect.Left + GetTreeIconX(ARow - 1) + Indent + 3;
3055      if X <> NameIconRect.Right then
3056      begin
3057        Canvas.MoveTo(NameIconRect.Right, NameRect.Top - 1 - FRowSpacing);
3058        Canvas.LineTo(X - 1, NameRect.Top - 1 - FRowSpacing);
3059      end;
3060    end;
3061    // to parent next sibling
3062    if ARow < FRows.Count - 1 then
3063    begin
3064      ParentRect := RowRect(ARow + 1);
3065      X := ParentRect.Left + GetTreeIconX(ARow + 1) + Indent + 3;
3066      if X <> NameIconRect.Right then
3067      begin
3068        Canvas.MoveTo(NameIconRect.Right, NameRect.Bottom - 1);
3069        Canvas.LineTo(X - 1, NameRect.Bottom - 1);
3070      end;
3071    end;
3072  end;
3073
3074var
3075  IconX: integer;
3076  DrawState: TPropEditDrawState;
3077begin
3078  CurRow := Rows[ARow];
3079  FullRect := RowRect(ARow);
3080  if (FullRect.Bottom < FPaintRc.Top) or (FullRect.Top > FPaintRc.Bottom) then
3081    exit;
3082  NameRect := FullRect;
3083  ValueRect := FullRect;
3084  Inc(FullRect.Bottom, FRowSpacing);
3085
3086  if Layout = oilHorizontal then
3087  begin
3088    NameRect.Right:=SplitterX;
3089    ValueRect.Left:=SplitterX;
3090  end
3091  else begin
3092    NameRect.Bottom := NameRect.Top + GetNameRowHeight;
3093    ValueRect.Top := NameRect.Bottom;
3094  end;
3095
3096  IconX := GetTreeIconX(ARow);
3097  NameIconRect := NameRect;
3098  NameIconRect.Right := IconX + Indent;
3099  NameTextRect := NameRect;
3100  NameTextRect.Left := NameIconRect.Right;
3101
3102  if Layout = oilVertical then
3103    ValueRect.Left := NameTextRect.Left
3104  else
3105  begin
3106    inc(NameIconRect.Right, 2 + Ord(ShowGutter));
3107    inc(NameTextRect.Left, 3 + Ord(ShowGutter));
3108  end;
3109
3110  DrawState:=[];
3111  if ARow = FItemIndex then
3112    Include(DrawState, pedsSelected);
3113
3114  ClearBackground;      // clear background in one go
3115  DrawIcon(IconX);      // draw icon
3116  DrawName(DrawState);  // draw name
3117  DrawWidgetsets;       // draw widgetsets
3118  DrawValue(DrawState); // draw value
3119
3120  with Canvas do
3121  begin
3122    if Layout = oilHorizontal then          // frames
3123    begin
3124      // Row Divider
3125      if DrawHorzGridLines then
3126      begin
3127        Pen.Style := psDot;
3128        Pen.EndCap := pecFlat;
3129        Pen.Cosmetic := False;
3130        Pen.Color := cl3DShadow;
3131        if FRowSpacing <> 0 then
3132        begin
3133          MoveTo(NameTextRect.Left, NameRect.Top - 1);
3134          LineTo(ValueRect.Right, NameRect.Top - 1);
3135        end;
3136        MoveTo(NameTextRect.Left, NameRect.Bottom - 1);
3137        LineTo(ValueRect.Right, NameRect.Bottom - 1);
3138      end;
3139
3140      // Split lines between: icon and name, name and value
3141      Pen.Style := psSolid;
3142      Pen.Cosmetic := True;
3143      Pen.Color := cl3DHiLight;
3144      MoveTo(NameRect.Right - 1, NameRect.Bottom - 1);
3145      LineTo(NameRect.Right - 1, NameRect.Top - 1 - FRowSpacing);
3146      Pen.Color := cl3DShadow;
3147      MoveTo(NameRect.Right - 2, NameRect.Bottom - 1);
3148      LineTo(NameRect.Right - 2, NameRect.Top - 1 - FRowSpacing);
3149
3150      // draw gutter line
3151      if ShowGutter then
3152      begin
3153        Pen.Color := GutterEdgeColor;
3154        MoveTo(NameIconRect.Right, NameRect.Bottom - 1);
3155        LineTo(NameIconRect.Right, NameRect.Top - 1 - FRowSpacing);
3156        if CurRow.Lvl > 0 then
3157          DrawGutterToParent;
3158      end;
3159    end
3160    else begin                              // Layout <> oilHorizontal
3161      Pen.Style := psSolid;
3162      Pen.Color := cl3DLight;
3163      MoveTo(ValueRect.Left, ValueRect.Bottom - 1);
3164      LineTo(ValueRect.Left, NameTextRect.Top);
3165      LineTo(ValueRect.Right - 1, NameTextRect.Top);
3166      Pen.Color:=cl3DHiLight;
3167      LineTo(ValueRect.Right - 1, ValueRect.Bottom - 1);
3168      LineTo(ValueRect.Left, ValueRect.Bottom - 1);
3169
3170      MoveTo(NameTextRect.Left + 1, NametextRect.Bottom);
3171      LineTo(NameTextRect.Left + 1, NameTextRect.Top + 1);
3172      LineTo(NameTextRect.Right - 2, NameTextRect.Top + 1);
3173      Pen.Color:=cl3DLight;
3174      LineTo(NameTextRect.Right - 2, NameTextRect.Bottom - 1);
3175      LineTo(NameTextRect.Left + 2, NameTextRect.Bottom - 1);
3176    end;
3177  end;
3178end;
3179
3180procedure TOICustomPropertyGrid.DoPaint(PaintOnlyChangedValues: boolean);
3181var
3182  a: integer;
3183  SpaceRect: TRect;
3184  GutterX: Integer;
3185begin
3186  FPaintRc := Canvas.ClipRect;
3187
3188  BuildPropertyList(true);
3189  if not PaintOnlyChangedValues then
3190  begin
3191    with Canvas do
3192    begin
3193      // draw properties
3194      for a := 0 to FRows.Count - 1 do
3195        PaintRow(a);
3196      // draw unused space below rows
3197      SpaceRect := Rect(BorderWidth, BorderWidth,
3198                        ClientWidth - BorderWidth + 1, ClientHeight - BorderWidth + 1);
3199      if FRows.Count > 0 then
3200        SpaceRect.Top := Rows[FRows.Count - 1].Bottom - FTopY + BorderWidth;
3201      if FBackgroundColor <> clNone then
3202      begin
3203        Brush.Color := FBackgroundColor;
3204        FillRect(SpaceRect);
3205      end;
3206
3207      // draw gutter if needed
3208      if ShowGutter and (Layout = oilHorizontal) then
3209      begin
3210        if FRows.Count > 0 then
3211          GutterX := RowRect(FRows.Count - 1).Left + GetTreeIconX(FRows.Count - 1)
3212        else
3213          GutterX := BorderWidth + 2;
3214        inc(GutterX, Indent + 3);
3215        SpaceRect.Right := GutterX;
3216        if GutterColor <> clNone then
3217        begin
3218          Brush.Color := GutterColor;
3219          FillRect(SpaceRect);
3220        end;
3221        MoveTo(GutterX, SpaceRect.Top);
3222        LineTo(GutterX, SpaceRect.Bottom);
3223      end;
3224      // don't draw border: borderstyle=bsSingle
3225    end;
3226  end else
3227  begin
3228    for a := 0 to FRows.Count-1 do
3229    begin
3230      if Rows[a].Editor.GetVisualValue <> Rows[a].LastPaintedValue then
3231        PaintRow(a);
3232    end;
3233  end;
3234end;
3235
3236procedure TOICustomPropertyGrid.Paint;
3237begin
3238  inherited Paint;
3239  DoPaint(false);
3240end;
3241
3242procedure TOICustomPropertyGrid.RefreshPropertyValues;
3243begin
3244  RefreshValueEdit;
3245  Invalidate;
3246end;
3247
3248procedure TOICustomPropertyGrid.ScrollToActiveItem;
3249begin
3250  ScrollToItem(FItemIndex);
3251end;
3252
3253procedure TOICustomPropertyGrid.ScrollToItem(NewIndex: Integer);
3254var
3255  NewRow: TOIPropertyGridRow;
3256begin
3257  if (NewIndex >= 0) and (NewIndex < FRows.Count) then
3258  begin
3259    NewRow := Rows[NewIndex];
3260    if NewRow.Bottom >= TopY + (ClientHeight - 2*BorderWidth) then
3261      TopY := NewRow.Bottom- (ClientHeight - 2*BorderWidth) + 1
3262    else
3263      if NewRow.Top < TopY then TopY := NewRow.Top;
3264  end;
3265end;
3266
3267procedure TOICustomPropertyGrid.PropEditLookupRootChange;
3268begin
3269  // When the LookupRoot changes, no changes can be stored
3270  // -> undo the value editor changes
3271  RefreshValueEdit;
3272  if PropertyEditorHook<>nil then
3273    FCurrentEditorLookupRoot:=PropertyEditorHook.LookupRoot;
3274end;
3275
3276function TOICustomPropertyGrid.RowRect(ARow:integer):TRect;
3277const
3278  ScrollBarWidth=0;
3279begin
3280  Result.Left:=BorderWidth;
3281  Result.Top:=Rows[ARow].Top-FTopY+BorderWidth;
3282  Result.Right:=ClientWidth-ScrollBarWidth;
3283  Result.Bottom:=Rows[ARow].Bottom-FTopY+BorderWidth;
3284end;
3285
3286procedure TOICustomPropertyGrid.SetItemsTops;
3287// compute row tops from row heights
3288// set indices of all rows
3289var a:integer;
3290begin
3291  for a:=0 to FRows.Count-1 do begin
3292    Rows[a].FIndex:=a;
3293    Rows[a].MeasureHeight(Canvas);
3294  end;
3295  if FRows.Count>0 then
3296    Rows[0].Top:=0;
3297  for a:=1 to FRows.Count-1 do
3298    Rows[a].FTop:=Rows[a-1].Bottom + FRowSpacing;
3299end;
3300
3301procedure TOICustomPropertyGrid.ClearRows;
3302var i:integer;
3303begin
3304  IncreaseChangeStep;
3305  // reverse order to make sure child rows are freed before parent rows
3306  for i:=FRows.Count-1 downto 0 do begin
3307    //debugln(['TOICustomPropertyGrid.ClearRows ',i,' ',FRows.Count,' ',dbgs(frows[i])]);
3308    Rows[i].Free;
3309    FRows[i]:=nil;
3310  end;
3311  FRows.Clear;
3312end;
3313
3314function TOICustomPropertyGrid.GetCurrentEditValue: string;
3315begin
3316  if FCurrentEdit=ValueEdit then
3317  {$IFDEF LCLCarbon}
3318    Result:=StringReplace(ValueEdit.Text,LineFeedSymbolUTF8,LineEnding,[rfReplaceAll])
3319  {$ELSE}
3320    Result:=ValueEdit.Text
3321  {$ENDIF}
3322  else if FCurrentEdit=ValueComboBox then
3323    Result:=ValueComboBox.Text
3324  else if FCurrentEdit=ValueCheckBox then
3325    Result:=ValueCheckBox.Caption
3326  else
3327    Result:='';
3328end;
3329
3330procedure TOICustomPropertyGrid.SetActiveControl(const AControl: TWinControl);
3331var
3332  F: TCustomForm;
3333begin
3334  F := GetParentForm(Self);
3335  if F <> nil then
3336    F.ActiveControl := AControl;
3337end;
3338
3339procedure TOICustomPropertyGrid.SetColumn(const AValue: TOICustomPropertyGridColumn);
3340begin
3341  if FColumn <> AValue then
3342  begin
3343    FColumn := AValue;
3344    // TODO: indication
3345  end;
3346end;
3347
3348procedure TOICustomPropertyGrid.SetCurrentEditValue(const NewValue: string);
3349begin
3350  if FCurrentEdit=ValueEdit then
3351  {$IFDEF LCLCarbon}
3352    ValueEdit.Text:=StringReplace(StringReplace(NewValue,#13,LineEnding,[rfReplaceAll]),LineEnding,LineFeedSymbolUTF8,[rfReplaceAll])
3353  {$ELSE}
3354    ValueEdit.Text:=NewValue
3355  {$ENDIF}
3356  else if FCurrentEdit=ValueComboBox then
3357  begin
3358    ValueComboBox.Text:=NewValue;
3359    if ValueComboBox.Style=csOwnerDrawVariable then
3360       Exclude(FStates,pgsGetComboItemsCalled);
3361  end
3362  else if FCurrentEdit=ValueCheckBox then
3363    SetCheckboxState(NewValue);
3364
3365  if (FItemIndex>=0) and (FItemIndex<RowCount) and Assigned(FCurrentEdit) then
3366  begin
3367    if Rows[FItemIndex].Editor.ValueIsStreamed then
3368      FCurrentEdit.Font:=FValueFont
3369    else
3370      FCurrentEdit.Font:=FDefaultValueFont;
3371  end;
3372end;
3373
3374procedure TOICustomPropertyGrid.SetDrawHorzGridLines(const AValue: Boolean);
3375begin
3376  if FDrawHorzGridLines = AValue then Exit;
3377  FDrawHorzGridLines := AValue;
3378  Invalidate;
3379end;
3380
3381procedure TOICustomPropertyGrid.SetFavorites(
3382  const AValue: TOIFavoriteProperties);
3383begin
3384  //debugln('TOICustomPropertyGrid.SetFavorites ',dbgsName(Self));
3385  if FFavorites=AValue then exit;
3386  FFavorites:=AValue;
3387  BuildPropertyList;
3388end;
3389
3390procedure TOICustomPropertyGrid.SetFilter(const AValue: TTypeKinds);
3391begin
3392  if (AValue<>FFilter) then
3393  begin
3394    FFilter:=AValue;
3395    BuildPropertyList;
3396  end;
3397end;
3398
3399procedure TOICustomPropertyGrid.SetGutterColor(const AValue: TColor);
3400begin
3401  if FGutterColor=AValue then exit;
3402  FGutterColor:=AValue;
3403  invalidate;
3404end;
3405
3406procedure TOICustomPropertyGrid.SetGutterEdgeColor(const AValue: TColor);
3407begin
3408  if FGutterEdgeColor=AValue then exit;
3409  FGutterEdgeColor:=AValue;
3410  invalidate;
3411end;
3412
3413procedure TOICustomPropertyGrid.SetHighlightColor(const AValue: TColor);
3414begin
3415  if FHighlightColor=AValue then exit;
3416  FHighlightColor:=AValue;
3417  Invalidate;
3418end;
3419
3420procedure TOICustomPropertyGrid.Clear;
3421begin
3422  ClearRows;
3423end;
3424
3425function TOICustomPropertyGrid.GetRow(Index:integer):TOIPropertyGridRow;
3426begin
3427  Result:=TOIPropertyGridRow(FRows[Index]);
3428end;
3429
3430procedure TOICustomPropertyGrid.ValueComboBoxCloseUp(Sender: TObject);
3431begin
3432  SetRowValue(false, false);
3433end;
3434
3435procedure TOICustomPropertyGrid.ValueComboBoxGetItems(Sender: TObject);
3436{ This event is called whenever the widgetset updates the list.
3437  On gtk the list is updated just before the user popups the list.
3438  Other widgetsets need the list always, which is bad, as this means collecting
3439  all items even if the dropdown never happens.
3440}
3441var
3442  CurRow: TOIPropertyGridRow;
3443  MaxItemWidth, CurItemWidth, i, Cnt: integer;
3444  ItemValue, CurValue: string;
3445  NewItemIndex: LongInt;
3446  ExcludeUpdateFlag: boolean;
3447begin
3448  Include(FStates,pgsGetComboItemsCalled);
3449  if (FItemIndex>=0) and (FItemIndex<FRows.Count) then begin
3450    ExcludeUpdateFlag:=not (pgsUpdatingEditControl in FStates);
3451    Include(FStates,pgsUpdatingEditControl);
3452    ValueComboBox.Items.BeginUpdate;
3453    try
3454      CurRow:=Rows[FItemIndex];
3455
3456      // Items
3457      if not FillComboboxItems then exit;
3458
3459      // Text and ItemIndex
3460      CurValue:=CurRow.Editor.GetVisualValue;
3461      ValueComboBox.Text:=CurValue;
3462      NewItemIndex:=ValueComboBox.Items.IndexOf(CurValue);
3463      if NewItemIndex>=0 then
3464        ValueComboBox.ItemIndex:=NewItemIndex;
3465
3466      // ItemWidth
3467      MaxItemWidth:=ValueComboBox.Width;
3468      Cnt:=ValueComboBox.Items.Count;
3469      for i:=0 to Cnt-1 do begin
3470        ItemValue:=ValueComboBox.Items[i];
3471        CurItemWidth:=ValueComboBox.Canvas.TextWidth(ItemValue);
3472        CurRow.Editor.ListMeasureWidth(ItemValue,i,ValueComboBox.Canvas,
3473                                       CurItemWidth);
3474        if MaxItemWidth<CurItemWidth then
3475          MaxItemWidth:=CurItemWidth;
3476      end;
3477      ValueComboBox.ItemWidth:=MaxItemWidth;
3478    finally
3479      ValueComboBox.Items.EndUpdate;
3480      if ExcludeUpdateFlag then
3481        Exclude(FStates,pgsUpdatingEditControl);
3482    end;
3483  end;
3484end;
3485
3486procedure TOICustomPropertyGrid.ValueComboBoxDrawItem(Control: TWinControl;
3487  Index: Integer; ARect: TRect; State: TOwnerDrawState);
3488var
3489  CurRow: TOIPropertyGridRow;
3490  ItemValue: string;
3491  AState: TPropEditDrawState;
3492  FontColor: TColor;
3493begin
3494  if (FItemIndex>=0) and (FItemIndex<FRows.Count) then begin
3495    CurRow:=Rows[FItemIndex];
3496    if (Index>=0) and (Index<ValueComboBox.Items.Count) then
3497      ItemValue:=ValueComboBox.Items[Index]
3498    else
3499      ItemValue:='';
3500    AState:=[];
3501    if odSelected in State then Include(AState,pedsSelected);
3502    if odFocused in State then Include(AState,pedsFocused);
3503    if odComboBoxEdit in State then
3504      Include(AState,pedsInEdit)
3505    else
3506      Include(AState,pedsInComboList);
3507
3508    if not(odBackgroundPainted in State) then
3509      ValueComboBox.Canvas.FillRect(ARect);
3510
3511    FontColor := ValueComboBox.Canvas.Font.Color;
3512    ValueComboBox.Canvas.Font.Assign(FDefaultValueFont);
3513    if odSelected in State then
3514      ValueComboBox.Canvas.Font.Color := FontColor
3515    else
3516      ValueComboBox.Canvas.Font.Color := clWindowText;
3517    if CurRow.Editor.HasDefaultValue and (ItemValue = CurRow.Editor.GetDefaultValue) then
3518      ValueComboBox.Canvas.Font.Style := ValueComboBox.Canvas.Font.Style + [fsItalic];
3519    CurRow.Editor.ListDrawValue(ItemValue,Index,ValueComboBox.Canvas,ARect,AState);
3520  end;
3521end;
3522
3523procedure TOICustomPropertyGrid.OnIdle(Sender: TObject; var Done: Boolean);
3524begin
3525  if (not (pgsGetComboItemsCalled in FStates))
3526  and (FCurrentEdit=ValueComboBox)
3527  and ValueComboBox.Enabled
3528  then begin
3529    ValueComboBoxGetItems(Self);
3530  end;
3531end;
3532
3533procedure TOICustomPropertyGrid.SetIdleEvent(Enable: boolean);
3534begin
3535  if (pgsIdleEnabled in FStates)=Enable then exit;
3536  if Enable then begin
3537    Application.AddOnIdleHandler(@OnIdle);
3538    Include(FStates,pgsIdleEnabled);
3539  end else begin
3540    Application.RemoveOnIdleHandler(@OnIdle);
3541    Exclude(FStates,pgsIdleEnabled);
3542  end;
3543end;
3544
3545procedure TOICustomPropertyGrid.HintTimer(Sender: TObject);
3546var
3547  PointedRow: TOIpropertyGridRow;
3548  Window: TWinControl;
3549  HintType: TPropEditHint;
3550  Position, ClientPosition: TPoint;
3551  Index: integer;
3552  AHint: String;
3553  OkToShow: Boolean;
3554begin
3555  if FLongHintTimer <> nil then
3556    FLongHintTimer.Enabled := False;
3557  Position := Mouse.CursorPos;
3558  Window := FindLCLWindow(Position);
3559  If (Window = Nil) or ((Window <> Self) and not IsParentOf(Window)) then exit;
3560
3561  ClientPosition := ScreenToClient(Position);
3562  if ((ClientPosition.X <=0) or (ClientPosition.X >= Width) or
3563     (ClientPosition.Y <= 0) or (ClientPosition.Y >= Height)) then
3564    Exit;
3565
3566  Index := MouseToIndex(ClientPosition.Y, False);
3567  // Don't show hint for the selected property.
3568  if (Index < 0) or (Index >= FRows.Count) or (Index = ItemIndex) then Exit;
3569
3570  PointedRow := Rows[Index];
3571  if (PointedRow = Nil) or (PointedRow.Editor = Nil) then Exit;
3572
3573  // Get hint
3574  OkToShow := True;
3575  HintType := GetHintTypeAt(Index, ClientPosition.X);
3576  if (HintType = pehName) and Assigned(OnPropertyHint) then
3577    OkToShow := OnPropertyHint(Self, PointedRow, AHint)
3578  else
3579    AHint := PointedRow.Editor.GetHint(HintType, Position.X, Position.Y);
3580  // Show hint if all is well.
3581  if OkToShow and FHintManager.ShowHint(Position, AHint, True, Screen.HintFont) then
3582  begin
3583    FHintIndex := Index;
3584    FHintType := HintType;
3585    FShowingLongHint := True;
3586  end;
3587end;
3588
3589procedure TOICustomPropertyGrid.HideHint;
3590begin
3591  FHintIndex := -1;
3592  FShowingLongHint := False;
3593  FHintManager.HideHint;
3594end;
3595
3596procedure TOICustomPropertyGrid.ValueControlMouseDown(Sender : TObject;
3597  Button: TMouseButton; Shift: TShiftState; X, Y: integer);
3598begin
3599  HideHint;
3600  ScrollToActiveItem;
3601end;
3602
3603procedure TOICustomPropertyGrid.ValueControlMouseMove(Sender: TObject;
3604  Shift: TShiftState; X, Y: integer);
3605begin
3606  // when the cursor is divider change it to default
3607  if (Sender as TControl).Parent.Cursor <> crDefault then
3608    TControl(Sender).Parent.Cursor := crDefault;
3609end;
3610
3611procedure TOICustomPropertyGrid.IncreaseChangeStep;
3612begin
3613  if FChangeStep<>$7fffffff then
3614    inc(FChangeStep)
3615  else
3616    FChangeStep:=-$7fffffff;
3617end;
3618
3619function TOICustomPropertyGrid.GridIsUpdating: boolean;
3620begin
3621  Result:=(FStates*[pgsChangingItemIndex,pgsApplyingValue,
3622                    pgsBuildPropertyListNeeded]<>[])
3623end;
3624
3625procedure TOICustomPropertyGrid.ToggleRow;
3626var
3627  CurRow: TOIPropertyGridRow;
3628  TypeKind : TTypeKind;
3629  NewIndex: Integer;
3630begin
3631  if not CanEditRowValue(false) then exit;
3632
3633  if FLongHintTimer <> nil then
3634    FLongHintTimer.Enabled := False;
3635
3636  if (FCurrentEdit = ValueComboBox) then
3637  begin
3638    CurRow := Rows[FItemIndex];
3639    TypeKind := CurRow.Editor.GetPropType^.Kind;
3640    // Integer (like TImageIndex), Enumeration, Set, Class or Boolean ComboBox
3641    if TypeKind in [tkInteger, tkEnumeration, tkSet, tkClass, tkBool] then
3642    begin
3643      if ValueComboBox.Items.Count = 0 then Exit;
3644      // Pick the next value from list
3645      if ValueComboBox.ItemIndex < (ValueComboBox.Items.Count-1) then
3646      begin
3647        NewIndex := ValueComboBox.ItemIndex + 1;
3648        // Go to first object of tkClass. Skip '(none)' which can be in different
3649        // places depending on widgetset sorting rules.
3650        if (ValueComboBox.ItemIndex = -1) // Only happen at nil value of tkClass
3651        and (ValueComboBox.Items[NewIndex] = oisNone)
3652        and (NewIndex < (ValueComboBox.Items.Count-1)) then
3653          Inc(NewIndex);
3654      end
3655      else
3656        NewIndex := 0;
3657      ValueComboBox.ItemIndex := NewIndex;
3658      SetRowValue(false, false);
3659      exit;
3660    end;
3661  end;
3662  DoCallEdit;
3663end;
3664
3665procedure TOICustomPropertyGrid.ValueEditDblClick(Sender: TObject);
3666begin
3667  FFirstClickTime:=0;
3668  ToggleRow;
3669end;
3670
3671procedure TOICustomPropertyGrid.SetBackgroundColor(const AValue: TColor);
3672begin
3673  if FBackgroundColor=AValue then exit;
3674  FBackgroundColor:=AValue;
3675  Invalidate;
3676end;
3677
3678procedure TOICustomPropertyGrid.SetReferences(const AValue: TColor);
3679begin
3680  if FReferencesColor=AValue then exit;
3681  FReferencesColor:=AValue;
3682  Invalidate;
3683end;
3684
3685procedure TOICustomPropertyGrid.SetSubPropertiesColor(const AValue: TColor);
3686begin
3687  if FSubPropertiesColor=AValue then exit;
3688  FSubPropertiesColor:=AValue;
3689  Invalidate;
3690end;
3691
3692procedure TOICustomPropertyGrid.SetValueDifferBackgrndColor(AValue: TColor);
3693begin
3694  if FValueDifferBackgrndColor=AValue then Exit;
3695  FValueDifferBackgrndColor:=AValue;
3696  Invalidate;
3697end;
3698
3699//------------------------------------------------------------------------------
3700
3701{ TOIPropertyGridRow }
3702
3703constructor TOIPropertyGridRow.Create(PropertyTree: TOICustomPropertyGrid;
3704  PropEditor:TPropertyEditor; ParentNode:TOIPropertyGridRow; WidgetSets: TLCLPlatforms);
3705begin
3706  inherited Create;
3707  // tree pointer
3708  FTree:=PropertyTree;
3709  FParent:=ParentNode;
3710  FNextBrother:=nil;
3711  FPriorBrother:=nil;
3712  FExpanded:=false;
3713  // child nodes
3714  FChildCount:=0;
3715  FFirstChild:=nil;
3716  FLastChild:=nil;
3717  // director
3718  FEditor:=PropEditor;
3719  GetLvl;
3720  FName:=FEditor.GetName;
3721  FTop:=0;
3722  FHeight:=FTree.RealDefaultItemHeight;
3723  FIndex:=-1;
3724  LastPaintedValue:='';
3725  FWidgetSets:=WidgetSets;
3726end;
3727
3728destructor TOIPropertyGridRow.Destroy;
3729begin
3730  //debugln(['TOIPropertyGridRow.Destroy ',fname,' ',dbgs(Pointer(Self))]);
3731  if FPriorBrother<>nil then FPriorBrother.FNextBrother:=FNextBrother;
3732  if FNextBrother<>nil then FNextBrother.FPriorBrother:=FPriorBrother;
3733  if FParent<>nil then begin
3734    if FParent.FFirstChild=Self then FParent.FFirstChild:=FNextBrother;
3735    if FParent.FLastChild=Self then FParent.FLastChild:=FPriorBrother;
3736    dec(FParent.FChildCount);
3737  end;
3738  if FEditor<>nil then FEditor.Free;
3739  inherited Destroy;
3740end;
3741
3742function TOIPropertyGridRow.ConsistencyCheck: integer;
3743var
3744  OldLvl, RealChildCount: integer;
3745  AChild: TOIPropertyGridRow;
3746begin
3747  if Top<0 then
3748    exit(-1);
3749  if Height<0 then
3750    exit(-2);
3751  if Lvl<0 then
3752    exit(-3);
3753  OldLvl:=Lvl;
3754  GetLvl;
3755  if Lvl<>OldLvl then
3756    exit(-4);
3757  if Name='' then
3758    exit(-5);
3759  if NextBrother<>nil then begin
3760    if NextBrother.PriorBrother<>Self then
3761      exit(-6);
3762    if NextBrother.Index<Index+1 then
3763      exit(-7);
3764  end;
3765  if PriorBrother<>nil then begin
3766    if PriorBrother.NextBrother<>Self then
3767      exit(-8);
3768    if PriorBrother.Index>Index-1 then
3769      Result:=-9
3770  end;
3771  if (Parent<>nil) then begin
3772    // has parent
3773    if (not Parent.HasChild(Self)) then
3774      exit(-10);
3775  end else begin
3776    // no parent
3777  end;
3778  if FirstChild<>nil then begin
3779    if Expanded then
3780      if (FirstChild.Index<>Index+1) then
3781        exit(-11);
3782  end else begin
3783    if LastChild<>nil then
3784      exit(-12);
3785  end;
3786  RealChildCount:=0;
3787  AChild:=FirstChild;
3788  while AChild<>nil do begin
3789    if AChild.Parent<>Self then
3790      exit(-13);
3791    inc(RealChildCount);
3792    AChild:=AChild.NextBrother;
3793  end;
3794  if RealChildCount<>ChildCount then
3795    exit(-14);
3796  Result:=0;
3797end;
3798
3799function TOIPropertyGridRow.HasChild(Row: TOIPropertyGridRow): boolean;
3800var
3801  ChildRow: TOIPropertyGridRow;
3802begin
3803  ChildRow:=FirstChild;
3804  while ChildRow<>nil do
3805    if ChildRow=Row then
3806      exit(true);
3807  Result:=false;
3808end;
3809
3810procedure TOIPropertyGridRow.WriteDebugReport(const Prefix: string);
3811var
3812  i: Integer;
3813  Item: TOIPropertyGridRow;
3814begin
3815  DebugLn([Prefix+'TOIPropertyGridRow.WriteDebugReport ',Name]);
3816  i:=0;
3817  Item:=FirstChild;
3818  while Item<>nil do begin
3819    DebugLn([Prefix+'  ',i,' ',Item.Name]);
3820    inc(i);
3821    Item:=Item.NextBrother;
3822  end;
3823end;
3824
3825procedure TOIPropertyGridRow.GetLvl;
3826var n:TOIPropertyGridRow;
3827begin
3828  FLvl:=0;
3829  n:=FParent;
3830  while n<>nil do begin
3831    inc(FLvl);
3832    n:=n.FParent;
3833  end;
3834end;
3835
3836function TOIPropertyGridRow.GetBottom:integer;
3837begin
3838  Result:=FTop+FHeight;
3839  if FTree.Layout = oilVertical
3840  then Inc(Result, FTree.GetNameRowHeight);
3841end;
3842
3843function TOIPropertyGridRow.IsReadOnly: boolean;
3844begin
3845  Result:=Editor.IsReadOnly or IsDisabled;
3846end;
3847
3848function TOIPropertyGridRow.IsDisabled: boolean;
3849var
3850  CurRow: TOIPropertyGridRow;
3851begin
3852  CurRow:=Self;
3853  while (CurRow<>nil) do begin
3854    if paDisableSubProperties in CurRow.Editor.GetAttributes then
3855      exit(true);
3856    CurRow:=CurRow.Parent;
3857  end;
3858  Result:=false;
3859end;
3860
3861procedure TOIPropertyGridRow.MeasureHeight(ACanvas: TCanvas);
3862begin
3863  FHeight:=FTree.RealDefaultItemHeight;
3864  Editor.PropMeasureHeight(Name,ACanvas,FHeight);
3865end;
3866
3867function TOIPropertyGridRow.Sort(const Compare: TListSortCompare): boolean;
3868var
3869  List: TFPList;
3870  Item: TOIPropertyGridRow;
3871  i: Integer;
3872begin
3873  if IsSorted(Compare) then exit(false);
3874  List:=TFPList.Create;
3875  try
3876    // create a TFPList of the children
3877    List.Capacity:=ChildCount;
3878    Item:=FirstChild;
3879    while Item<>nil do begin
3880      List.Add(Item);
3881      Item:=Item.NextBrother;
3882    end;
3883    // sort the TFPList
3884    List.Sort(Compare);
3885    // sort in double linked list
3886    for i:=0 to List.Count-1 do begin
3887      Item:=TOIPropertyGridRow(List[i]);
3888      if i=0 then begin
3889        FFirstChild:=Item;
3890        Item.FPriorBrother:=nil;
3891      end else
3892        Item.FPriorBrother:=TOIPropertyGridRow(List[i-1]);
3893      if i=List.Count-1 then begin
3894        FLastChild:=Item;
3895        Item.FNextBrother:=nil;
3896      end else
3897        Item.FNextBrother:=TOIPropertyGridRow(List[i+1]);
3898    end;
3899  finally
3900    List.Free;
3901  end;
3902  Result:=true;
3903end;
3904
3905function TOIPropertyGridRow.IsSorted(const Compare: TListSortCompare): boolean;
3906var
3907  Item1: TOIPropertyGridRow;
3908  Item2: TOIPropertyGridRow;
3909begin
3910  if ChildCount<2 then exit(true);
3911  Item1:=FirstChild;
3912  while true do begin
3913    Item2:=Item1.NextBrother;
3914    if Item2=nil then break;
3915    if Compare(Item1,Item2)>0 then exit(false);
3916    Item1:=Item2;
3917  end;
3918  Result:=true;
3919end;
3920
3921function TOIPropertyGridRow.Next: TOIPropertyGridRow;
3922begin
3923  if fFirstChild<>nil then
3924    Result:=fFirstChild
3925  else
3926    Result:=NextSkipChilds;
3927end;
3928
3929function TOIPropertyGridRow.NextSkipChilds: TOIPropertyGridRow;
3930begin
3931  Result:=Self;
3932  while (Result<>nil) do begin
3933    if Result.NextBrother<>nil then begin
3934      Result:=Result.NextBrother;
3935      exit;
3936    end;
3937    Result:=Result.Parent;
3938  end;
3939end;
3940
3941//==============================================================================
3942
3943
3944{ TOIOptions }
3945
3946function TOIOptions.FPropertyGridSplitterX(Page: TObjectInspectorPage): integer;
3947begin
3948  Result:=FGridSplitterX[Page];
3949end;
3950
3951procedure TOIOptions.FPropertyGridSplitterX(Page: TObjectInspectorPage;
3952  const AValue: integer);
3953begin
3954  FGridSplitterX[Page]:=AValue;
3955end;
3956
3957constructor TOIOptions.Create;
3958var
3959  p: TObjectInspectorPage;
3960begin
3961  inherited Create;
3962
3963  FSaveBounds:=false;
3964  FLeft:=0;
3965  FTop:=0;
3966  FWidth:=250;
3967  FHeight:=400;
3968  for p:=Low(TObjectInspectorPage) to High(TObjectInspectorPage) do
3969    FGridSplitterX[p]:=110;
3970  FDefaultItemHeight:=0;
3971  FShowComponentTree:=true;
3972  FComponentTreeHeight:=160;
3973  FInfoBoxHeight:=80;
3974
3975  FGridBackgroundColor := DefBackgroundColor;
3976  FSubPropertiesColor := DefSubPropertiesColor;
3977  FValueColor := DefValueColor;
3978  FDefaultValueColor := DefDefaultValueColor;
3979  FValueDifferBackgrndColor := DefValueDifferBackgrndColor;
3980  FReadOnlyColor := DefReadOnlyColor;
3981  FReferencesColor := DefReferencesColor;
3982  FPropertyNameColor := DefNameColor;
3983  FHighlightColor := DefHighlightColor;
3984  FHighlightFontColor := DefHighlightFontColor;
3985  FGutterColor := DefGutterColor;
3986  FGutterEdgeColor := DefGutterEdgeColor;
3987
3988  FCheckboxForBoolean := True;
3989  FBoldNonDefaultValues := True;
3990  FDrawGridLines := True;
3991  FShowPropertyFilter := True;
3992  FShowGutter := True;
3993  FShowStatusBar := True;
3994  FShowInfoBox := True;
3995end;
3996
3997function TOIOptions.Load: boolean;
3998var
3999  Path: String;
4000  FileVersion: integer;
4001  Page: TObjectInspectorPage;
4002begin
4003  Result:=False;
4004  if ConfigStore=nil then exit;
4005  try
4006    Path:='ObjectInspectorOptions/';
4007    FileVersion:=ConfigStore.GetValue(Path+'Version/Value',0);
4008    FSaveBounds:=ConfigStore.GetValue(Path+'Bounds/Valid',False);
4009    if FSaveBounds then begin
4010      FLeft:=ConfigStore.GetValue(Path+'Bounds/Left',0);
4011      FTop:=ConfigStore.GetValue(Path+'Bounds/Top',0);
4012      FWidth:=ConfigStore.GetValue(Path+'Bounds/Width',250);
4013      FHeight:=ConfigStore.GetValue(Path+'Bounds/Height',400);
4014    end;
4015    if FileVersion>=2 then begin
4016      for Page:=Low(TObjectInspectorPage) to High(TObjectInspectorPage) do
4017        FGridSplitterX[Page]:=ConfigStore.GetValue(
4018           Path+'Bounds/'+DefaultOIPageNames[Page]+'/SplitterX',110);
4019    end else begin
4020      FGridSplitterX[oipgpProperties]:=ConfigStore.GetValue(Path+'Bounds/PropertyGridSplitterX',110);
4021      FGridSplitterX[oipgpEvents]:=ConfigStore.GetValue(Path+'Bounds/EventGridSplitterX',110);
4022    end;
4023    for Page:=Low(TObjectInspectorPage) to High(TObjectInspectorPage) do
4024      if FGridSplitterX[Page]<10 then
4025        FGridSplitterX[Page]:=10;
4026
4027    FDefaultItemHeight:=ConfigStore.GetValue(Path+'Bounds/DefaultItemHeight',0);
4028    FShowComponentTree:=ConfigStore.GetValue(Path+'ComponentTree/Show/Value',True);
4029    FComponentTreeHeight:=ConfigStore.GetValue(Path+'ComponentTree/Height/Value',160);
4030
4031    FGridBackgroundColor:=ConfigStore.GetValue(Path+'Color/GridBackground',DefBackgroundColor);
4032    FSubPropertiesColor:=ConfigStore.GetValue(Path+'Color/SubProperties',DefSubPropertiesColor);
4033    FValueColor:=ConfigStore.GetValue(Path+'Color/Value',DefValueColor);
4034    FDefaultValueColor:=ConfigStore.GetValue(Path+'Color/DefaultValue',DefDefaultValueColor);
4035    FValueDifferBackgrndColor:=ConfigStore.GetValue(Path+'Color/ValueDifferBackgrnd',DefValueDifferBackgrndColor);
4036    FReadOnlyColor:=ConfigStore.GetValue(Path+'Color/ReadOnly',DefReadOnlyColor);
4037    FReferencesColor:=ConfigStore.GetValue(Path+'Color/References',DefReferencesColor);
4038    FPropertyNameColor:=ConfigStore.GetValue(Path+'Color/PropertyName',DefNameColor);
4039    FHighlightColor:=ConfigStore.GetValue(Path+'Color/Highlight',DefHighlightColor);
4040    FHighlightFontColor:=ConfigStore.GetValue(Path+'Color/HighlightFont',DefHighlightFontColor);
4041    FGutterColor:=ConfigStore.GetValue(Path+'Color/Gutter',DefGutterColor);
4042    FGutterEdgeColor:=ConfigStore.GetValue(Path+'Color/GutterEdge',DefGutterEdgeColor);
4043
4044    FShowHints:=ConfigStore.GetValue(Path+'ShowHints',FileVersion>=3);
4045    FAutoShow := ConfigStore.GetValue(Path+'AutoShow',True);
4046    FCheckboxForBoolean := ConfigStore.GetValue(Path+'CheckboxForBoolean',True);
4047    FBoldNonDefaultValues := ConfigStore.GetValue(Path+'BoldNonDefaultValues',True);
4048    FDrawGridLines := ConfigStore.GetValue(Path+'DrawGridLines',True);
4049    FShowPropertyFilter := ConfigStore.GetValue(Path+'ShowPropertyFilter',True);
4050    FShowGutter := ConfigStore.GetValue(Path+'ShowGutter',True);
4051    FShowStatusBar := ConfigStore.GetValue(Path+'ShowStatusBar',True);
4052    FShowInfoBox := ConfigStore.GetValue(Path+'ShowInfoBox',True);
4053    FInfoBoxHeight := ConfigStore.GetValue(Path+'InfoBoxHeight',80);
4054  except
4055    on E: Exception do begin
4056      DebugLn('ERROR: TOIOptions.Load: ',E.Message);
4057      exit;
4058    end;
4059  end;
4060  Result:=True;
4061end;
4062
4063function TOIOptions.Save: boolean;
4064var
4065  Page: TObjectInspectorPage;
4066  Path: String;
4067begin
4068  Result:=False;
4069  if ConfigStore=nil then exit;
4070  try
4071    Path:='ObjectInspectorOptions/';
4072    ConfigStore.SetValue(Path+'Version/Value',OIOptionsFileVersion);
4073    ConfigStore.SetDeleteValue(Path+'Bounds/Valid',FSaveBounds,False);
4074    if FSaveBounds then begin
4075      ConfigStore.SetValue(Path+'Bounds/Left',FLeft);
4076      ConfigStore.SetValue(Path+'Bounds/Top',FTop);
4077      ConfigStore.SetValue(Path+'Bounds/Width',FWidth);
4078      ConfigStore.SetValue(Path+'Bounds/Height',FHeight);
4079    end;
4080    for Page:=Low(TObjectInspectorPage) to High(TObjectInspectorPage) do
4081      ConfigStore.SetDeleteValue(Path+'Bounds/'+DefaultOIPageNames[Page]+'/SplitterX',
4082                                 FGridSplitterX[Page],110);
4083    ConfigStore.SetDeleteValue(Path+'Bounds/DefaultItemHeight',FDefaultItemHeight,0);
4084    ConfigStore.SetDeleteValue(Path+'ComponentTree/Show/Value',FShowComponentTree,True);
4085    ConfigStore.SetDeleteValue(Path+'ComponentTree/Height/Value',FComponentTreeHeight,160);
4086
4087    ConfigStore.SetDeleteValue(Path+'Color/GridBackground',FGridBackgroundColor,DefBackgroundColor);
4088    ConfigStore.SetDeleteValue(Path+'Color/SubProperties',FSubPropertiesColor,DefSubPropertiesColor);
4089    ConfigStore.SetDeleteValue(Path+'Color/Value',FValueColor,DefValueColor);
4090    ConfigStore.SetDeleteValue(Path+'Color/DefaultValue',FDefaultValueColor,DefDefaultValueColor);
4091    ConfigStore.SetDeleteValue(Path+'Color/ValueDifferBackgrnd',FValueDifferBackgrndColor,DefValueDifferBackgrndColor);
4092    ConfigStore.SetDeleteValue(Path+'Color/ReadOnly',FReadOnlyColor,DefReadOnlyColor);
4093    ConfigStore.SetDeleteValue(Path+'Color/References',FReferencesColor,DefReferencesColor);
4094    ConfigStore.SetDeleteValue(Path+'Color/PropertyName',FPropertyNameColor,DefNameColor);
4095    ConfigStore.SetDeleteValue(Path+'Color/Highlight',FHighlightColor,DefHighlightColor);
4096    ConfigStore.SetDeleteValue(Path+'Color/HighlightFont',FHighlightFontColor,DefHighlightFontColor);
4097    ConfigStore.SetDeleteValue(Path+'Color/Gutter',FGutterColor,DefGutterColor);
4098    ConfigStore.SetDeleteValue(Path+'Color/GutterEdge',FGutterEdgeColor,DefGutterEdgeColor);
4099
4100    ConfigStore.SetDeleteValue(Path+'ShowHints',FShowHints, True);
4101    ConfigStore.SetDeleteValue(Path+'AutoShow',FAutoShow, True);
4102    ConfigStore.SetDeleteValue(Path+'CheckboxForBoolean',FCheckboxForBoolean, True);
4103    ConfigStore.SetDeleteValue(Path+'BoldNonDefaultValues',FBoldNonDefaultValues, True);
4104    ConfigStore.SetDeleteValue(Path+'DrawGridLines',FDrawGridLines, True);
4105    ConfigStore.SetDeleteValue(Path+'ShowPropertyFilter',FShowPropertyFilter, True);
4106    ConfigStore.SetDeleteValue(Path+'ShowGutter',FShowGutter, True);
4107    ConfigStore.SetDeleteValue(Path+'ShowStatusBar',FShowStatusBar, True);
4108    ConfigStore.SetDeleteValue(Path+'ShowInfoBox',FShowInfoBox, True);
4109    ConfigStore.SetDeleteValue(Path+'InfoBoxHeight',FInfoBoxHeight,80);
4110  except
4111    on E: Exception do begin
4112      DebugLn('ERROR: TOIOptions.Save: ',E.Message);
4113      exit;
4114    end;
4115  end;
4116  Result:=true;
4117end;
4118
4119procedure TOIOptions.Assign(AnObjInspector: TObjectInspectorDlg);
4120var
4121  Page: TObjectInspectorPage;
4122begin
4123  FLeft:=AnObjInspector.Left;
4124  FTop:=AnObjInspector.Top;
4125  FWidth:=AnObjInspector.Width;
4126  FHeight:=AnObjInspector.Height;
4127  for Page:=Low(TObjectInspectorPage) to High(TObjectInspectorPage) do
4128    if AnObjInspector.GridControl[Page]<>nil then
4129      FGridSplitterX[Page]:=AnObjInspector.GridControl[Page].PreferredSplitterX;
4130  FDefaultItemHeight:=AnObjInspector.DefaultItemHeight;
4131  FShowComponentTree:=AnObjInspector.ShowComponentTree;
4132  FComponentTreeHeight:=AnObjInspector.ComponentPanelHeight;
4133
4134  FGridBackgroundColor:=AnObjInspector.PropertyGrid.BackgroundColor;
4135  FSubPropertiesColor:=AnObjInspector.PropertyGrid.SubPropertiesColor;
4136  FReferencesColor:=AnObjInspector.PropertyGrid.ReferencesColor;
4137  FValueColor:=AnObjInspector.PropertyGrid.ValueFont.Color;
4138  FDefaultValueColor:=AnObjInspector.PropertyGrid.DefaultValueFont.Color;
4139  FValueDifferBackgrndColor:=AnObjInspector.PropertyGrid.ValueDifferBackgrndColor;
4140  FReadOnlyColor:=AnObjInspector.PropertyGrid.ReadOnlyColor;
4141  FPropertyNameColor:=AnObjInspector.PropertyGrid.NameFont.Color;
4142  FHighlightColor:=AnObjInspector.PropertyGrid.HighlightColor;
4143  FHighlightFontColor:=AnObjInspector.PropertyGrid.HighlightFont.Color;
4144  FGutterColor:=AnObjInspector.PropertyGrid.GutterColor;
4145  FGutterEdgeColor:=AnObjInspector.PropertyGrid.GutterEdgeColor;
4146
4147  FShowHints := AnObjInspector.PropertyGrid.ShowHint;
4148  FAutoShow := AnObjInspector.AutoShow;
4149  FCheckboxForBoolean := AnObjInspector.FCheckboxForBoolean;
4150  FBoldNonDefaultValues := fsBold in AnObjInspector.PropertyGrid.ValueFont.Style;
4151  FDrawGridLines := AnObjInspector.PropertyGrid.DrawHorzGridLines;
4152  FShowPropertyFilter := AnObjInspector.ShowPropertyFilter;
4153  FShowGutter := AnObjInspector.PropertyGrid.ShowGutter;
4154  FShowStatusBar := AnObjInspector.ShowStatusBar;
4155  FShowInfoBox := AnObjInspector.ShowInfoBox;
4156  FInfoBoxHeight := AnObjInspector.InfoBoxHeight;
4157end;
4158
4159procedure TOIOptions.AssignTo(AnObjInspector: TObjectInspectorDlg);
4160var
4161  Page: TObjectInspectorPage;
4162  Grid: TOICustomPropertyGrid;
4163begin
4164  if FSaveBounds then
4165  begin
4166    AnObjInspector.SetBounds(FLeft,FTop,FWidth,FHeight);
4167  end;
4168
4169  for Page := Low(TObjectInspectorPage) to High(TObjectInspectorPage) do
4170  begin
4171    Grid := AnObjInspector.GridControl[Page];
4172    if Grid = nil then
4173      Continue;
4174    Grid.PreferredSplitterX := FGridSplitterX[Page];
4175    Grid.SplitterX := FGridSplitterX[Page];
4176    AssignTo(Grid);
4177  end;
4178  AnObjInspector.DefaultItemHeight := DefaultItemHeight;
4179  AnObjInspector.AutoShow := AutoShow;
4180  AnObjInspector.FCheckboxForBoolean := FCheckboxForBoolean;
4181  AnObjInspector.ShowComponentTree := ShowComponentTree;
4182  AnObjInspector.ShowPropertyFilter := ShowPropertyFilter;
4183  AnObjInspector.ShowInfoBox := ShowInfoBox;
4184  AnObjInspector.ComponentPanelHeight := ComponentTreeHeight;
4185  AnObjInspector.InfoBoxHeight := InfoBoxHeight;
4186  AnObjInspector.ShowStatusBar := ShowStatusBar;
4187end;
4188
4189procedure TOIOptions.AssignTo(AGrid: TOICustomPropertyGrid);
4190begin
4191  AGrid.BackgroundColor := FGridBackgroundColor;
4192  AGrid.SubPropertiesColor := FSubPropertiesColor;
4193  AGrid.ReferencesColor := FReferencesColor;
4194  AGrid.ReadOnlyColor := FReadOnlyColor;
4195  AGrid.ValueDifferBackgrndColor := FValueDifferBackgrndColor;
4196  AGrid.ValueFont.Color := FValueColor;
4197  if FBoldNonDefaultValues then
4198    AGrid.ValueFont.Style := [fsBold]
4199  else
4200    AGrid.ValueFont.Style := [];
4201  AGrid.DefaultValueFont.Color := FDefaultValueColor;
4202  AGrid.NameFont.Color := FPropertyNameColor;
4203  AGrid.HighlightColor := FHighlightColor;
4204  AGrid.HighlightFont.Color := FHighlightFontColor;
4205  AGrid.GutterColor := FGutterColor;
4206  AGrid.GutterEdgeColor := FGutterEdgeColor;
4207  AGrid.ShowHint := FShowHints;
4208  AGrid.DrawHorzGridLines := FDrawGridLines;
4209  AGrid.ShowGutter := FShowGutter;
4210  AGrid.CheckboxForBoolean := FCheckboxForBoolean;
4211end;
4212
4213
4214//==============================================================================
4215
4216{ TObjectInspectorDlg }
4217
4218constructor TObjectInspectorDlg.Create(AnOwner: TComponent);
4219
4220  procedure AddPopupMenuItem(var NewMenuItem: TMenuItem;
4221    ParentMenuItem: TMenuItem; const AName, ACaption, AHint, AResourceName: string;
4222    AnOnClick: TNotifyEvent; CheckedFlag, EnabledFlag, VisibleFlag: boolean);
4223  begin
4224    NewMenuItem:=TMenuItem.Create(Self);
4225    with NewMenuItem do
4226    begin
4227      Name:=AName;
4228      Caption:=ACaption;
4229      Hint:=AHint;
4230      OnClick:=AnOnClick;
4231      Checked:=CheckedFlag;
4232      Enabled:=EnabledFlag;
4233      Visible:=VisibleFlag;
4234      if AResourceName <> '' then
4235        ImageIndex := IDEImages.LoadImage(AResourceName);
4236    end;
4237    if ParentMenuItem<>nil then
4238      ParentMenuItem.Add(NewMenuItem)
4239    else
4240      MainPopupMenu.Items.Add(NewMenuItem);
4241  end;
4242
4243  function AddSeparatorMenuItem(ParentMenuItem: TMenuItem; const AName: string; VisibleFlag: boolean): TMenuItem;
4244  begin
4245    Result := TMenuItem.Create(Self);
4246    with Result do
4247    begin
4248      Name := AName;
4249      Caption := cLineCaption;
4250      Visible := VisibleFlag;
4251    end;
4252    if ParentMenuItem <> nil then
4253      ParentMenuItem.Add(Result)
4254    else
4255      MainPopupMenu.Items.Add(Result);
4256  end;
4257
4258begin
4259  inherited Create(AnOwner);
4260  FEnableHookGetSelection := true;
4261  FPropertyEditorHook := nil;
4262  FSelection := TPersistentSelectionList.Create;
4263  FAutoShow := True;
4264  FDefaultItemHeight := 0;
4265  ComponentPanelHeight := 160;
4266  FShowComponentTree := True;
4267  FShowPropertyFilter := True;
4268  FShowFavorites := False;
4269  FShowRestricted := False;
4270  FShowStatusBar := True;
4271  FInfoBoxHeight := 80;
4272  FPropFilterUpdating := False;
4273  FShowInfoBox := True;
4274  FComponentEditor := nil;
4275  FFilter := DefaultOITypeKinds;
4276
4277  Caption := oisObjectInspector;
4278  CompFilterLabel.Caption := oisBtnComponents;
4279  MainPopupMenu.Images := IDEImages.Images_16;
4280
4281  AddPopupMenuItem(AddToFavoritesPopupMenuItem,nil,'AddToFavoritePopupMenuItem',
4282     oisAddtofavorites,'Add property to favorites properties', '',
4283     @AddToFavoritesPopupmenuItemClick,false,true,true);
4284  AddPopupMenuItem(RemoveFromFavoritesPopupMenuItem,nil,
4285     'RemoveFromFavoritesPopupMenuItem',
4286     oisRemovefromfavorites,'Remove property from favorites properties', '',
4287     @RemoveFromFavoritesPopupmenuItemClick,false,true,true);
4288  AddPopupMenuItem(ViewRestrictedPropertiesPopupMenuItem,nil,
4289     'ViewRestrictedPropertiesPopupMenuItem',
4290     oisViewRestrictedProperties,'View restricted property descriptions', '',
4291     @ViewRestrictionsPopupmenuItemClick,false,true,true);
4292  AddPopupMenuItem(UndoPropertyPopupMenuItem,nil,'UndoPropertyPopupMenuItem',
4293     oisUndo,'Set property value to last valid value', '',
4294     @UndoPopupmenuItemClick,false,true,true);
4295  AddPopupMenuItem(FindDeclarationPopupmenuItem,nil,'FindDeclarationPopupmenuItem',
4296     oisFinddeclaration,'Jump to declaration of property', '',
4297     @FindDeclarationPopupmenuItemClick,false,true,false);
4298  OptionsSeparatorMenuItem := AddSeparatorMenuItem(nil, 'OptionsSeparatorMenuItem', true);
4299
4300  AddPopupMenuItem(CutPopupMenuItem,nil,'CutPopupMenuItem',
4301     oisCutComponents,'Cut selected item', 'laz_cut',
4302     @CutPopupmenuItemClick,false,true,true);
4303  AddPopupMenuItem(CopyPopupMenuItem,nil,'CopyPopupMenuItem',
4304     oisCopyComponents,'Copy selected item', 'laz_copy',
4305     @CopyPopupmenuItemClick,false,true,true);
4306  AddPopupMenuItem(PastePopupMenuItem,nil,'PastePopupMenuItem',
4307     oisPasteComponents,'Paste selected item', 'laz_paste',
4308     @PastePopupmenuItemClick,false,true,true);
4309  AddPopupMenuItem(DeletePopupMenuItem,nil,'DeletePopupMenuItem',
4310     oisDeleteComponents,'Delete selected item', 'delete_selection',
4311     @DeletePopupmenuItemClick,false,true,true);
4312  OptionsSeparatorMenuItem2 := AddSeparatorMenuItem(nil, 'OptionsSeparatorMenuItem2', true);
4313
4314  // Change class of the component. ToDo: create a 'change_class' icon resource
4315  AddPopupMenuItem(ChangeClassPopupMenuItem,nil,'ChangeClassPopupMenuItem',
4316     oisChangeClass,'Change Class of component', '',
4317     @ChangeClassPopupmenuItemClick,false,true,true);
4318  AddPopupMenuItem(ChangeParentPopupMenuItem, nil, 'ChangeParentPopupMenuItem',
4319     oisChangeParent+' ...', 'Change Parent of component', '',
4320     @ChangeParentItemClick, False, True, True);
4321  OptionsSeparatorMenuItem3 := AddSeparatorMenuItem(nil, 'OptionsSeparatorMenuItem3', true);
4322
4323  AddPopupMenuItem(ShowComponentTreePopupMenuItem,nil
4324     ,'ShowComponentTreePopupMenuItem',oisShowComponentTree, '', ''
4325     ,@ShowComponentTreePopupMenuItemClick,FShowComponentTree,true,true);
4326  ShowComponentTreePopupMenuItem.ShowAlwaysCheckable:=true;
4327
4328  AddPopupMenuItem(ShowPropertyFilterPopupMenuItem,nil
4329     ,'ShowPropertyFilterPopupMenuItem',oisShowPropertyFilter, '', ''
4330     ,@ShowPropertyFilterPopupMenuItemClick,FShowPropertyFilter,true,true);
4331  ShowPropertyFilterPopupMenuItem.ShowAlwaysCheckable:=true;
4332
4333  AddPopupMenuItem(ShowHintsPopupMenuItem,nil
4334     ,'ShowHintPopupMenuItem',oisShowHints,'Grid hints', ''
4335     ,@ShowHintPopupMenuItemClick,false,true,true);
4336  ShowHintsPopupMenuItem.ShowAlwaysCheckable:=true;
4337
4338  AddPopupMenuItem(ShowInfoBoxPopupMenuItem,nil
4339     ,'ShowInfoBoxPopupMenuItem',oisShowInfoBox, '', ''
4340     ,@ShowInfoBoxPopupMenuItemClick,FShowInfoBox,true,true);
4341  ShowInfoBoxPopupMenuItem.ShowAlwaysCheckable:=true;
4342
4343  AddPopupMenuItem(ShowStatusBarPopupMenuItem,nil
4344     ,'ShowStatusBarPopupMenuItem',oisShowStatusBar, '', ''
4345     ,@ShowStatusBarPopupMenuItemClick,FShowStatusBar,true,true);
4346  ShowStatusBarPopupMenuItem.ShowAlwaysCheckable:=true;
4347
4348  AddPopupMenuItem(ShowOptionsPopupMenuItem,nil
4349     ,'ShowOptionsPopupMenuItem',oisOptions, '', 'oi_options'
4350     ,@ShowOptionsPopupMenuItemClick,false,true,FOnShowOptions<>nil);
4351
4352  // combobox at top (filled with available persistents)
4353  with AvailPersistentComboBox do
4354  begin
4355    Sorted := true;
4356    AutoSelect := true;
4357    AutoComplete := true;
4358    DropDownCount := 12;
4359    Visible := not FShowComponentTree;
4360  end;
4361
4362  // Component Tree at top (filled with available components)
4363  ComponentTree := TComponentTreeView.Create(Self);
4364  with ComponentTree do
4365  begin
4366    Name := 'ComponentTree';
4367    Parent := ComponentPanel;
4368    AnchorSideTop.Control := CompFilterEdit;
4369    AnchorSideTop.Side := asrBottom;
4370    AnchorSideBottom.Control := ComponentPanel;
4371    AnchorSideBottom.Side := asrBottom;
4372    BorderSpacing.Top := 3;
4373    BorderSpacing.Bottom := 3;
4374    Left := 3;
4375    Height := ComponentPanel.Height - BorderSpacing.Top
4376            - CompFilterEdit.Top - CompFilterEdit.Height;
4377    Width := ComponentPanel.Width-6;
4378    Anchors := [akTop, akLeft, akRight, akBottom];
4379    OnDblClick := @ComponentTreeDblClick;
4380    OnKeyDown := @ComponentTreeKeyDown;
4381    OnSelectionChanged := @ComponentTreeSelectionChanged;
4382    OnComponentGetImageIndex := @ComponentTreeGetNodeImageIndex;
4383    OnModified := @ComponentTreeModified;
4384    Scrollbars := ssAutoBoth;
4385    PopupMenu := MainPopupMenu;
4386  end;
4387
4388  // ComponentPanel encapsulates TreeFilterEdit and ComponentTree
4389  ComponentPanel.Constraints.MinHeight := 8;
4390  ComponentPanel.Visible := FShowComponentTree;
4391  CompFilterEdit.FilteredTreeview := ComponentTree;
4392
4393  InfoPanel := TPanel.Create(Self);
4394  with InfoPanel do
4395  begin
4396    Name := 'InfoPanel';
4397    Constraints.MinHeight := 8;
4398    Caption := '';
4399    Height := InfoBoxHeight;
4400    Parent := PnlClient;
4401    BevelOuter := bvNone;
4402    BevelInner := bvNone;
4403    Align := alBottom;
4404    PopupMenu := MainPopupMenu;
4405    Visible := FShowInfoBox;
4406  end;
4407
4408  if ShowComponentTree then
4409    CreateTopSplitter;
4410  if ShowInfoBox then
4411    CreateBottomSplitter;
4412
4413  //Create properties filter
4414  PropertyPanel := TPanel.Create(Self);
4415  with PropertyPanel do
4416  begin
4417    Name := 'PropertyPanel';
4418    Caption := '';
4419    Parent := PnlClient;
4420    BevelOuter := bvNone;
4421    BevelInner := bvNone;
4422    Align := alClient;
4423    Visible := True;
4424  end;
4425
4426  PropFilterPanel := TPanel.Create(Self);
4427  with PropFilterPanel do
4428  begin
4429    Name := 'PropFilterPanel';
4430    Caption := '';
4431    Parent := PropertyPanel;
4432    BevelOuter := bvNone;
4433    BevelInner := bvNone;
4434    AutoSize := true;
4435    Align := alTop;
4436    Visible := True;
4437  end;
4438
4439  PropFilterLabel := TLabel.Create(Self);
4440  PropFilterEdit:= TListFilterEdit.Create(Self);
4441  with PropFilterLabel do
4442  begin
4443    Parent := PropFilterPanel;
4444    BorderSpacing.Left := Scale96ToForm(5);
4445    BorderSpacing.Top := Scale96ToForm(7);
4446    Width := Scale96ToForm(53);
4447    Caption := oisBtnProperties;
4448    FocusControl := PropFilterEdit;
4449  end;
4450
4451  with PropFilterEdit do
4452  begin
4453    Parent := PropFilterPanel;
4454    AnchorSideLeft.Control := PropFilterLabel;
4455    AnchorSideLeft.Side := asrBottom;
4456    AnchorSideTop.Control := PropFilterLabel;
4457    AnchorSideTop.Side := asrCenter;
4458    Width := PropertyPanel.Width - ( Left + 3);
4459    AutoSelect := False;
4460    ButtonWidth := Scale96ToForm(23);
4461    Anchors := [akTop, akLeft, akRight];
4462    BorderSpacing.Left := 5;
4463    OnAfterFilter := @PropFilterEditAfterFilter;
4464  end;
4465
4466  CreateNoteBook;
4467  // TabOrder has no effect. TAB key is handled by TObjectInspectorDlg.KeyDown().
4468  CompFilterEdit.TabOrder := 0;
4469  ComponentTree.TabOrder := 1;
4470  PropFilterEdit.TabOrder := 2;
4471end;
4472
4473destructor TObjectInspectorDlg.Destroy;
4474begin
4475  FreeAndNil(FSelection);
4476  FreeAndNil(FComponentEditor);
4477  FreeAndNil(PropFilterLabel);
4478  FreeAndNil(PropFilterEdit);
4479  FreeAndNil(PropFilterPanel);
4480  FreeAndNil(PropertyPanel);
4481  inherited Destroy;
4482  FreeAndNil(FFavorites);
4483end;
4484
4485procedure TObjectInspectorDlg.PropFilterEditAfterFilter(Sender: TObject);
4486begin
4487  FPropFilterUpdating := True;
4488  GetActivePropertyGrid.PropNameFilter := PropFilterEdit.Filter;
4489  RebuildPropertyLists;
4490  FPropFilterUpdating := False;
4491end;
4492
4493procedure TObjectInspectorDlg.NoteBookPageChange(Sender: TObject);
4494begin
4495  PropFilterEditAfterFilter(Sender);
4496end;
4497
4498procedure TObjectInspectorDlg.SetPropertyEditorHook(const AValue:TPropertyEditorHook);
4499var
4500  Page: TObjectInspectorPage;
4501  OldSelection: TPersistentSelectionList;
4502begin
4503  if FPropertyEditorHook=AValue then exit;
4504  if FPropertyEditorHook<>nil then begin
4505    FPropertyEditorHook.RemoveAllHandlersForObject(Self);
4506  end;
4507  FPropertyEditorHook:=AValue;
4508  if FPropertyEditorHook<>nil then begin
4509    FPropertyEditorHook.AddHandlerChangeLookupRoot(@HookLookupRootChange);
4510    FPropertyEditorHook.AddHandlerRefreshPropertyValues(@HookRefreshPropertyValues);
4511    FPropertyEditorHook.AddHandlerSetSelection(@HookSetSelection);
4512    Selection := nil;
4513    for Page:=Low(TObjectInspectorPage) to High(TObjectInspectorPage) do
4514      if GridControl[Page]<>nil then
4515      begin
4516        if Page=oipgpProperties then  // Add HookGetCheckboxForBoolean only once.
4517          FPropertyEditorHook.AddHandlerGetCheckboxForBoolean(
4518                                   @GridControl[Page].HookGetCheckboxForBoolean);
4519        GridControl[Page].PropertyEditorHook:=FPropertyEditorHook;
4520      end;
4521    OldSelection:=TPersistentSelectionList.Create;
4522    try
4523      FPropertyEditorHook.GetSelection(OldSelection);
4524      if EnableHookGetSelection then begin
4525        // the propertyeditorhook gets the selection from the OI
4526        if OldSelection.Count>0 then
4527          FSelection.Assign(OldSelection); // if propertyeditorhook has a selection use that
4528        FPropertyEditorHook.AddHandlerGetSelection(@HookGetSelection);
4529        if OldSelection.Count=0 then begin
4530          // select root component
4531          FSelection.Clear;
4532          if FPropertyEditorHook.LookupRoot is TComponent then
4533            FSelection.Add(TComponent(FPropertyEditorHook.LookupRoot));
4534        end;
4535      end
4536      else
4537        Selection := OldSelection; // OI gets the selection from propertyeditorhook
4538    finally
4539      OldSelection.Free;
4540    end;
4541    ComponentTree.PropertyEditorHook:=FPropertyEditorHook;
4542    FillComponentList(True);
4543    RefreshSelection;
4544  end;
4545end;
4546
4547function TObjectInspectorDlg.PersistentToString(APersistent: TPersistent): string;
4548begin
4549  if APersistent is TComponent then
4550    Result:=TComponent(APersistent).GetNamePath+': '+APersistent.ClassName
4551  else
4552    Result:=APersistent.ClassName;
4553end;
4554
4555procedure TObjectInspectorDlg.SetComponentPanelHeight(const AValue: integer);
4556begin
4557  if ComponentPanel.Height <> AValue then
4558    ComponentPanel.Height := AValue;
4559end;
4560
4561procedure TObjectInspectorDlg.SetDefaultItemHeight(const AValue: integer);
4562var
4563  NewValue: Integer;
4564  Page: TObjectInspectorPage;
4565begin
4566  NewValue:=AValue;
4567  if NewValue<0 then
4568    NewValue:=0
4569  else if (NewValue>0) and (NewValue<10) then
4570    NewValue:=10
4571  else if NewValue>100 then NewValue:=100;
4572  if FDefaultItemHeight=NewValue then exit;
4573  FDefaultItemHeight:=NewValue;
4574  for Page:=Low(TObjectInspectorPage) to High(TObjectInspectorPage) do
4575    if GridControl[Page]<>nil then
4576      GridControl[Page].DefaultItemHeight:=FDefaultItemHeight;
4577  RebuildPropertyLists;
4578end;
4579
4580procedure TObjectInspectorDlg.SetInfoBoxHeight(const AValue: integer);
4581begin
4582  if FInfoBoxHeight <> AValue then
4583  begin
4584    FInfoBoxHeight := AValue;
4585    Assert(Assigned(InfoPanel), 'TObjectInspectorDlg.SetInfoBoxHeight: InfoPanel=nil');
4586    InfoPanel.Height := AValue;
4587  end;
4588end;
4589
4590procedure TObjectInspectorDlg.SetRestricted(const AValue: TOIRestrictedProperties);
4591begin
4592  if FRestricted = AValue then exit;
4593  //DebugLn('TObjectInspectorDlg.SetRestricted Count: ', DbgS(AValue.Count));
4594  FRestricted := AValue;
4595  RestrictedGrid.Favorites := FRestricted;
4596end;
4597
4598procedure TObjectInspectorDlg.SetOnShowOptions(const AValue: TNotifyEvent);
4599begin
4600  if FOnShowOptions=AValue then exit;
4601  FOnShowOptions:=AValue;
4602  ShowOptionsPopupMenuItem.Visible:=FOnShowOptions<>nil;
4603end;
4604
4605procedure TObjectInspectorDlg.AddPersistentToList(APersistent: TPersistent;
4606  List: TStrings);
4607var
4608  Allowed: boolean;
4609begin
4610  if (APersistent is TComponent)
4611  and (csDestroying in TComponent(APersistent).ComponentState) then exit;
4612  Allowed:=true;
4613  if Assigned(FOnAddAvailablePersistent) then
4614    FOnAddAvailablePersistent(APersistent,Allowed);
4615  if Allowed then
4616    List.AddObject(PersistentToString(APersistent),APersistent);
4617end;
4618
4619procedure TObjectInspectorDlg.HookLookupRootChange;
4620var
4621  Page: TObjectInspectorPage;
4622begin
4623  for Page:=Low(TObjectInspectorPage) to High(TObjectInspectorPage) do
4624    if GridControl[Page]<>nil then
4625      GridControl[Page].PropEditLookupRootChange;
4626  CompFilterEdit.Filter:='';
4627  FillComponentList(True);
4628end;
4629
4630procedure TObjectInspectorDlg.HookRefreshPropertyValues;
4631begin
4632  RefreshPropertyValues;
4633end;
4634
4635procedure TObjectInspectorDlg.ChangeCompZOrderInList(APersistent: TPersistent;
4636  AZOrder: TZOrderDelete);
4637begin
4638  if FShowComponentTree then
4639    ComponentTree.ChangeCompZOrder(APersistent, AZOrder)
4640  else
4641    FillPersistentComboBox;
4642end;
4643
4644procedure TObjectInspectorDlg.DeleteCompFromList(APersistent: TPersistent);
4645begin
4646  if FShowComponentTree then begin
4647    if APersistent=nil then
4648      ComponentTree.BuildComponentNodes(True)
4649    else
4650      ComponentTree.DeleteComponentNode(APersistent);
4651  end
4652  else
4653    FillPersistentComboBox;
4654end;
4655
4656procedure TObjectInspectorDlg.FillComponentList(AWholeTree: Boolean);
4657begin
4658  if FShowComponentTree then
4659    ComponentTree.BuildComponentNodes(AWholeTree)
4660  else
4661    FillPersistentComboBox;
4662end;
4663
4664procedure TObjectInspectorDlg.UpdateComponentValues;
4665begin
4666  if FShowComponentTree then
4667    ComponentTree.UpdateComponentNodesValues
4668  else
4669    FillPersistentComboBox;
4670end;
4671
4672procedure TObjectInspectorDlg.FillPersistentComboBox;
4673var
4674  a: integer;
4675  Root: TComponent;
4676  OldText: AnsiString;
4677  NewList: TStringList;
4678begin
4679  Assert(not FUpdatingAvailComboBox,
4680         'TObjectInspectorDlg.FillPersistentComboBox: Updating Avail ComboBox');
4681  //if FUpdatingAvailComboBox then exit;
4682  FUpdatingAvailComboBox:=true;
4683  NewList:=TStringList.Create;
4684  try
4685    if (FPropertyEditorHook<>nil)
4686    and (FPropertyEditorHook.LookupRoot<>nil) then begin
4687      AddPersistentToList(FPropertyEditorHook.LookupRoot,NewList);
4688      if FPropertyEditorHook.LookupRoot is TComponent then begin
4689        Root:=TComponent(FPropertyEditorHook.LookupRoot);
4690  //writeln('[TObjectInspectorDlg.FillComponentComboBox] B  ',Root.Name,'  ',Root.ComponentCount);
4691        for a:=0 to Root.ComponentCount-1 do
4692          AddPersistentToList(Root.Components[a],NewList);
4693      end;
4694    end;
4695
4696    if AvailPersistentComboBox.Items.Equals(NewList) then exit;
4697
4698    AvailPersistentComboBox.Items.BeginUpdate;
4699    if AvailPersistentComboBox.Items.Count=1 then
4700      OldText:=AvailPersistentComboBox.Text
4701    else
4702      OldText:='';
4703    AvailPersistentComboBox.Items.Assign(NewList);
4704    AvailPersistentComboBox.Items.EndUpdate;
4705    a:=AvailPersistentComboBox.Items.IndexOf(OldText);
4706    if (OldText='') or (a<0) then
4707      SetAvailComboBoxText
4708    else
4709      AvailPersistentComboBox.ItemIndex:=a;
4710
4711  finally
4712    NewList.Free;
4713    FUpdatingAvailComboBox:=false;
4714  end;
4715end;
4716
4717procedure TObjectInspectorDlg.BeginUpdate;
4718begin
4719  inc(FUpdateLock);
4720end;
4721
4722procedure TObjectInspectorDlg.EndUpdate;
4723begin
4724  dec(FUpdateLock);
4725  if FUpdateLock<0 then begin
4726    DebugLn('ERROR TObjectInspectorDlg.EndUpdate');
4727  end;
4728  if FUpdateLock=0 then begin
4729    if oifRebuildPropListsNeeded in FFLags then
4730      RebuildPropertyLists;
4731  end;
4732end;
4733
4734function TObjectInspectorDlg.GetActivePropertyGrid: TOICustomPropertyGrid;
4735begin
4736  Result:=nil;
4737  if NoteBook=nil then exit;
4738  case NoteBook.PageIndex of
4739  0: Result:=PropertyGrid;
4740  1: Result:=EventGrid;
4741  2: Result:=FavoriteGrid;
4742  3: Result:=RestrictedGrid;
4743  end;
4744end;
4745
4746function TObjectInspectorDlg.GetActivePropertyRow: TOIPropertyGridRow;
4747var
4748  CurGrid: TOICustomPropertyGrid;
4749begin
4750  Result:=nil;
4751  CurGrid:=GetActivePropertyGrid;
4752  if CurGrid=nil then exit;
4753  Result:=CurGrid.GetActiveRow;
4754end;
4755
4756function TObjectInspectorDlg.GetCurRowDefaultValue(var DefaultStr: string): Boolean;
4757var
4758  CurRow: TOIPropertyGridRow;
4759begin
4760  Result:=False;
4761  DefaultStr:='';
4762  CurRow:=GetActivePropertyRow;
4763  if Assigned(CurRow) and (CurRow.Editor.HasDefaultValue) then
4764  begin
4765    try
4766      DefaultStr:=CurRow.Editor.GetDefaultValue;
4767      Result:=true;
4768    except
4769      DefaultStr:='';
4770    end;
4771  end;
4772end;
4773
4774function TObjectInspectorDlg.GetParentCandidates: TFPList;
4775begin
4776  Result:=GetChangeParentCandidates(FPropertyEditorHook,Selection);
4777end;
4778
4779function TObjectInspectorDlg.HasParentCandidates: Boolean;
4780var
4781  Candidates: TFPList=nil;
4782begin
4783  try
4784    Candidates := GetParentCandidates;
4785    Result := (Candidates.Count>1);  // single candidate is current parent
4786  finally
4787    Candidates.Free;
4788  end;
4789end;
4790
4791procedure TObjectInspectorDlg.ChangeParent;
4792var
4793  i: Integer;
4794  Control: TControl;
4795  NewParentName: String;
4796  NewParent: TPersistent;
4797  NewSelection: TPersistentSelectionList;
4798  Candidates: TFPList = nil;
4799  RootDesigner: TIDesigner;
4800  CompEditDsg: TComponentEditorDesigner;
4801begin
4802  if (Selection.Count < 1) then Exit;
4803  try
4804    Candidates := GetParentCandidates;
4805    if not ShowChangeParentDlg(Selection, Candidates, NewParentName) then
4806      Exit;
4807  finally
4808    Candidates.Free;
4809  end;
4810
4811  if NewParentName = TWinControl(FPropertyEditorHook.LookupRoot).Name then
4812    NewParent := FPropertyEditorHook.LookupRoot
4813  else
4814    NewParent := TWinControl(FPropertyEditorHook.LookupRoot).FindComponent(NewParentName);
4815  if not (NewParent is TWinControl) then Exit;
4816
4817  // Find designer for Undo actions.
4818  RootDesigner := FindRootDesigner(FPropertyEditorHook.LookupRoot);
4819  if (RootDesigner is TComponentEditorDesigner) then
4820    CompEditDsg := TComponentEditorDesigner(RootDesigner) //if CompEditDsg.IsUndoLocked then Exit;
4821  else
4822    CompEditDsg := nil;
4823
4824  for i := 0 to Selection.Count-1 do
4825  begin
4826    if not (Selection[i] is TControl) then Continue;
4827    Control := TControl(Selection[i]);
4828    if Control.Parent = nil then Continue;
4829    if Assigned(CompEditDsg) then
4830      CompEditDsg.AddUndoAction(Control, uopChange, i=0, 'Parent',
4831                                Control.Parent.Name, NewParentName);
4832    Control.Parent := TWinControl(NewParent);
4833  end;
4834
4835  // Ensure the order of controls in the OI now reflects the new ZOrder
4836  // This code is based on ZOrderItemClick().
4837  NewSelection := TPersistentSelectionList.Create;
4838  try
4839    NewSelection.ForceUpdate:=True;
4840    NewSelection.Add(NewParent);
4841    for i:=0 to Selection.Count-1 do
4842      NewSelection.Add(Selection.Items[i]);
4843    SetSelection(NewSelection);
4844    NewSelection.ForceUpdate:=True;
4845    NewSelection.Delete(0);
4846    SetSelection(NewSelection);
4847  finally
4848    NewSelection.Free;
4849  end;
4850  DoModified;
4851  FillComponentList(True);
4852end;
4853
4854procedure TObjectInspectorDlg.SetSelection(const ASelection: TPersistentSelectionList);
4855begin
4856  if FSettingSelectionCount > 0 then Exit; // Prevent a recursive loop.
4857  Inc(FSettingSelectionCount);
4858  try
4859    if ASelection<>nil then begin
4860      // Nothing changed or endless loop -> quit.
4861      if FSelection.IsEqual(ASelection) and not ASelection.ForceUpdate then
4862        Exit;
4863    end else begin
4864      if FSelection.Count=0 then
4865        Exit;
4866    end;
4867    if ASelection<>nil then
4868      FSelection.Assign(ASelection)
4869    else
4870      FSelection.Clear;
4871    SetAvailComboBoxText;
4872    RefreshSelection;
4873    if Assigned(FOnSelectPersistentsInOI) then
4874      FOnSelectPersistentsInOI(Self);
4875  finally
4876    Dec(FSettingSelectionCount);
4877  end;
4878end;
4879
4880procedure TObjectInspectorDlg.RefreshSelection;
4881var
4882  Page: TObjectInspectorPage;
4883begin
4884  if FRefreshingSelectionCount > 0 then Exit; // Prevent a recursive loop.
4885  Inc(FRefreshingSelectionCount);
4886
4887  if NoteBook.Page[3].Visible then
4888  begin
4889    DoUpdateRestricted;
4890    // invalidate RestrictedProps
4891    WidgetSetsRestrictedBox.Invalidate;
4892    ComponentRestrictedBox.Invalidate;
4893  end;
4894
4895  for Page := Low(TObjectInspectorPage) to High(TObjectInspectorPage) do
4896    if GridControl[Page] <> nil then
4897      GridControl[Page].Selection := FSelection;
4898  RefreshComponentTreeSelection;
4899  if (not Visible) and AutoShow and (FSelection.Count > 0) then
4900    if Assigned(OnAutoShow) then
4901      OnAutoShow(Self)
4902    else
4903      Visible := True;
4904  Dec(FRefreshingSelectionCount);
4905end;
4906
4907procedure TObjectInspectorDlg.RefreshComponentTreeSelection;
4908begin
4909  ComponentTree.Selection := FSelection;
4910  ComponentTree.MakeSelectionVisible;
4911end;
4912
4913procedure TObjectInspectorDlg.SaveChanges;
4914var
4915  Page: TObjectInspectorPage;
4916begin
4917  for Page:=Low(TObjectInspectorPage) to High(TObjectInspectorPage) do
4918    if GridControl[Page]<>nil then
4919      GridControl[Page].SaveChanges;
4920end;
4921
4922procedure TObjectInspectorDlg.RefreshPropertyValues;
4923var
4924  Page: TObjectInspectorPage;
4925begin
4926  for Page:=Low(TObjectInspectorPage) to High(TObjectInspectorPage) do
4927    if GridControl[Page]<>nil then
4928      GridControl[Page].RefreshPropertyValues;
4929end;
4930
4931procedure TObjectInspectorDlg.RebuildPropertyLists;
4932var
4933  Page: TObjectInspectorPage;
4934begin
4935  if FUpdateLock>0 then
4936    Include(FFLags,oifRebuildPropListsNeeded)
4937  else begin
4938    Exclude(FFLags,oifRebuildPropListsNeeded);
4939    for Page:=Low(TObjectInspectorPage) to High(TObjectInspectorPage) do
4940      if GridControl[Page]<>nil then
4941        GridControl[Page].BuildPropertyList(False, not FPropFilterUpdating);
4942  end;
4943end;
4944
4945procedure TObjectInspectorDlg.AvailComboBoxCloseUp(Sender:TObject);
4946var
4947  NewComponent,Root:TComponent;
4948  a:integer;
4949
4950  procedure SetSelectedPersistent(c:TPersistent);
4951  begin
4952    if (FSelection.Count=1) and (FSelection[0]=c) then exit;
4953    FSelection.Clear;
4954    FSelection.Add(c);
4955    RefreshSelection;
4956    if Assigned(FOnSelectPersistentsInOI) then
4957      FOnSelectPersistentsInOI(Self);
4958  end;
4959
4960begin
4961  if FUpdatingAvailComboBox then exit;
4962  if (FPropertyEditorHook=nil) or (FPropertyEditorHook.LookupRoot=nil) then
4963    exit;
4964  if not (FPropertyEditorHook.LookupRoot is TComponent) then begin
4965    // not a TComponent => no children => select always only the root
4966    SetSelectedPersistent(FPropertyEditorHook.LookupRoot);
4967    exit;
4968  end;
4969  Root:=TComponent(FPropertyEditorHook.LookupRoot);
4970  if (AvailPersistentComboBox.Text=PersistentToString(Root)) then begin
4971    SetSelectedPersistent(Root);
4972  end else begin
4973    for a:=0 to Root.ComponentCount-1 do begin
4974      NewComponent:=Root.Components[a];
4975      if AvailPersistentComboBox.Text=PersistentToString(NewComponent) then
4976      begin
4977        SetSelectedPersistent(NewComponent);
4978        break;
4979      end;
4980    end;
4981  end;
4982end;
4983
4984function TObjectInspectorDlg.GetComponentEditorForSelection: TBaseComponentEditor;
4985var
4986  APersistent: TPersistent;
4987  AComponent: TComponent absolute APersistent;
4988  ADesigner: TIDesigner;
4989begin
4990  APersistent := GetSelectedPersistent;
4991  if not (APersistent is TComponent) then
4992    Exit(nil);
4993  ADesigner := FindRootDesigner(AComponent);
4994  if not (ADesigner is TComponentEditorDesigner) then
4995    Exit(nil);
4996  Result := GetComponentEditor(AComponent, TComponentEditorDesigner(ADesigner));
4997end;
4998
4999procedure TObjectInspectorDlg.ComponentTreeDblClick(Sender: TObject);
5000var
5001  CompEditor: TBaseComponentEditor;
5002begin
5003  if (PropertyEditorHook = nil) or (PropertyEditorHook.LookupRoot = nil) then
5004    Exit;
5005  if not FSelection.IsEqual(ComponentTree.Selection) then
5006    ComponentTreeSelectionChanged(Sender);
5007  CompEditor := GetComponentEditorForSelection;
5008  if Assigned(CompEditor) then
5009  begin
5010    try
5011      CompEditor.Edit;
5012    finally
5013      CompEditor.Free;
5014    end;
5015  end;
5016end;
5017
5018function TObjectInspectorDlg.CanDeleteSelection: Boolean;
5019var
5020  persistent: TPersistent;
5021  intf: IObjInspInterface;
5022  i: Integer;
5023begin
5024  Result := true;
5025  for i:=0 to ComponentTree.Selection.Count - 1 do begin
5026    persistent := ComponentTree.Selection[i];
5027    if persistent.GetInterface(GUID_ObjInspInterface, intf) and not intf.AllowDelete then
5028      exit(false);
5029  end;
5030end;
5031
5032procedure TObjectInspectorDlg.ComponentTreeKeyDown(Sender: TObject;
5033  var Key: Word; Shift: TShiftState);
5034begin
5035  if (Shift = []) and (Key = VK_DELETE) and
5036     (Selection.Count > 0) and CanDeleteSelection and
5037     (MessageDlg(oiscDelete, mtConfirmation,[mbYes, mbNo],0) = mrYes) then
5038  begin
5039    DeletePopupmenuItemClick(nil);
5040  end;
5041end;
5042
5043procedure TObjectInspectorDlg.ComponentTreeSelectionChanged(Sender: TObject);
5044begin
5045  if (PropertyEditorHook=nil) or (PropertyEditorHook.LookupRoot=nil) then exit;
5046  if FSelection.IsEqual(ComponentTree.Selection) then exit;
5047  FSelection.Assign(ComponentTree.Selection);
5048  RefreshSelection;
5049  DefSelectionVisibleInDesigner;
5050  if Assigned(FOnSelectPersistentsInOI) then
5051    FOnSelectPersistentsInOI(Self);
5052end;
5053
5054procedure TObjectInspectorDlg.MainPopupMenuClose(Sender: TObject);
5055begin
5056  if FStateOfHintsOnMainPopupMenu then ShowHintPopupMenuItemClick(nil);
5057end;
5058
5059procedure TObjectInspectorDlg.FormResize(Sender: TObject);
5060begin
5061  ComponentPanel.Constraints.MaxHeight := Height-50;
5062end;
5063
5064procedure TObjectInspectorDlg.GridKeyDown(Sender: TObject; var Key: Word;
5065  Shift: TShiftState);
5066var
5067  Handled: Boolean;
5068begin
5069  Handled := false;
5070
5071  //CTRL-[Shift]-TAB will select next or previous notebook tab
5072  if (Key=VK_TAB) and (ssCtrl in Shift) then
5073  begin
5074    Handled := true;
5075    if ssShift in Shift then
5076      ShowNextPage(-1)
5077    else
5078      ShowNextPage(1);
5079  end;
5080
5081  //CTRL-ArrowDown will dropdown the component combobox
5082  if (not Handled) and ((Key=VK_DOWN) or (Key=VK_UP)) and (ssCtrl in Shift) then
5083  begin
5084    Handled := true;
5085    if AvailPersistentComboBox.Canfocus then
5086      AvailPersistentComboBox.SetFocus;
5087    AvailPersistentComboBox.DroppedDown := true;
5088  end;
5089
5090  if not Handled then
5091  begin
5092    if Assigned(OnOIKeyDown) then
5093      OnOIKeyDown(Self,Key,Shift);
5094    if (Key<>VK_UNKNOWN) and Assigned(OnRemainingKeyDown) then
5095      OnRemainingKeyDown(Self,Key,Shift);
5096  end
5097  else
5098    Key := VK_UNKNOWN;
5099end;
5100
5101procedure TObjectInspectorDlg.GridKeyUp(Sender: TObject; var Key: Word;
5102  Shift: TShiftState);
5103begin
5104  if Assigned(OnRemainingKeyUp) then OnRemainingKeyUp(Self,Key,Shift);
5105end;
5106
5107procedure TObjectInspectorDlg.GridDblClick(Sender: TObject);
5108begin
5109  //
5110end;
5111
5112procedure TObjectInspectorDlg.PropEditPopupClick(Sender: TObject);
5113var
5114  CurGrid: TOICustomPropertyGrid;
5115  CurRow: TOIPropertyGridRow;
5116  s: String;
5117begin
5118  CurGrid:=GetActivePropertyGrid;
5119  CurRow := GetActivePropertyRow;
5120  CurRow.Editor.ExecuteVerb((Sender as TMenuItem).Tag);
5121  s := CurRow.Editor.GetVisualValue;
5122  CurGrid.CurrentEditValue := s;
5123  RefreshPropertyValues;
5124  Invalidate;
5125  DebugLn(['Executed verb number ', (Sender as TMenuItem).Tag, ', VisualValue: ', s, ', CurRow: ', CurRow]);
5126end;
5127
5128procedure TObjectInspectorDlg.AddToFavoritesPopupmenuItemClick(Sender: TObject);
5129begin
5130  //debugln('TObjectInspectorDlg.OnAddToFavoritePopupmenuItemClick');
5131  if Assigned(OnAddToFavorites) then OnAddToFavorites(Self);
5132end;
5133
5134procedure TObjectInspectorDlg.RemoveFromFavoritesPopupmenuItemClick(Sender: TObject);
5135begin
5136  if Assigned(OnRemoveFromFavorites) then OnRemoveFromFavorites(Self);
5137end;
5138
5139procedure TObjectInspectorDlg.ViewRestrictionsPopupmenuItemClick(Sender: TObject);
5140begin
5141  DoViewRestricted;
5142end;
5143
5144procedure TObjectInspectorDlg.UndoPopupmenuItemClick(Sender: TObject);
5145var
5146  CurGrid: TOICustomPropertyGrid;
5147  CurRow: TOIPropertyGridRow;
5148begin
5149  CurGrid:=GetActivePropertyGrid;
5150  CurRow:=GetActivePropertyRow;
5151  if CurRow=nil then exit;
5152  CurGrid.CurrentEditValue:=CurRow.Editor.GetVisualValue;
5153end;
5154
5155procedure TObjectInspectorDlg.FindDeclarationPopupmenuItemClick(Sender: TObject);
5156begin
5157  if Assigned(OnFindDeclarationOfProperty) then
5158    OnFindDeclarationOfProperty(Self);
5159end;
5160
5161procedure TObjectInspectorDlg.CutPopupmenuItemClick(Sender: TObject);
5162var
5163  ADesigner: TIDesigner;
5164begin
5165  if (Selection.Count > 0) and (Selection[0] is TComponent) then
5166  begin
5167    ADesigner := FindRootDesigner(Selection[0]);
5168    if ADesigner is TComponentEditorDesigner then
5169      TComponentEditorDesigner(ADesigner).CutSelection;
5170  end;
5171end;
5172
5173procedure TObjectInspectorDlg.CopyPopupmenuItemClick(Sender: TObject);
5174var
5175  ADesigner: TIDesigner;
5176begin
5177  if (Selection.Count > 0) and (Selection[0] is TComponent) then
5178  begin
5179    ADesigner := FindRootDesigner(Selection[0]);
5180    if ADesigner is TComponentEditorDesigner then
5181      TComponentEditorDesigner(ADesigner).CopySelection;
5182  end;
5183end;
5184
5185procedure TObjectInspectorDlg.PastePopupmenuItemClick(Sender: TObject);
5186var
5187  ADesigner: TIDesigner;
5188begin
5189  if Selection.Count > 0 then
5190  begin
5191    ADesigner := FindRootDesigner(Selection[0]);
5192    if ADesigner is TComponentEditorDesigner then
5193      TComponentEditorDesigner(ADesigner).PasteSelection([]);
5194  end;
5195end;
5196
5197procedure TObjectInspectorDlg.DeletePopupmenuItemClick(Sender: TObject);
5198var
5199  ADesigner: TIDesigner;
5200  ACollection: TCollection;
5201  i: integer;
5202begin
5203  if (Selection.Count > 0) then
5204  begin
5205    ADesigner := FindRootDesigner(Selection[0]);
5206    if ADesigner is TComponentEditorDesigner then
5207    begin
5208      if Selection[0] is TCollection then
5209      begin
5210        ACollection := TCollection(Selection[0]);
5211        Selection.BeginUpdate;
5212        Selection.Clear;
5213        for i := 0 to ACollection.Count - 1 do
5214          Selection.Add(ACollection.Items[i]);
5215        Selection.EndUpdate;
5216        if Assigned(FOnSelectPersistentsInOI) then
5217          FOnSelectPersistentsInOI(Self);
5218      end;
5219      TComponentEditorDesigner(ADesigner).DeleteSelection;
5220    end;
5221  end;
5222end;
5223
5224procedure TObjectInspectorDlg.ChangeClassPopupmenuItemClick(Sender: TObject);
5225var
5226  ADesigner: TIDesigner;
5227begin
5228  if (Selection.Count = 1) then
5229  begin
5230    ADesigner := FindRootDesigner(Selection[0]);
5231    if ADesigner is TComponentEditorDesigner then
5232      TComponentEditorDesigner(ADesigner).ChangeClass;
5233  end;
5234end;
5235
5236procedure TObjectInspectorDlg.GridModified(Sender: TObject);
5237begin
5238  DoModified;
5239end;
5240
5241procedure TObjectInspectorDlg.GridSelectionChange(Sender: TObject);
5242var
5243  Row: TOIPropertyGridRow;
5244begin
5245  Row := GetActivePropertyRow;
5246  if Assigned(Row) then
5247    FLastActiveRowName := Row.Name;
5248  if Assigned(FOnSelectionChange) then
5249    FOnSelectionChange(Self);
5250end;
5251
5252function TObjectInspectorDlg.GridPropertyHint(Sender: TObject;
5253  PointedRow: TOIPropertyGridRow; out AHint: string): boolean;
5254begin
5255  Result := False;
5256  if Assigned(FOnPropertyHint) then
5257    Result := FOnPropertyHint(Sender, PointedRow, AHint);
5258end;
5259
5260procedure TObjectInspectorDlg.SetAvailComboBoxText;
5261begin
5262  case FSelection.Count of
5263    0: // none selected
5264       AvailPersistentComboBox.Text:='';
5265    1: // single selection
5266       AvailPersistentComboBox.Text:=PersistentToString(FSelection[0]);
5267  else
5268    // multi selection
5269    AvailPersistentComboBox.Text:=Format(oisItemsSelected, [FSelection.Count]);
5270  end;
5271end;
5272
5273procedure TObjectInspectorDlg.HookGetSelection(const ASelection: TPersistentSelectionList);
5274begin
5275  if ASelection=nil then exit;
5276  ASelection.Assign(FSelection);
5277end;
5278
5279procedure TObjectInspectorDlg.HookSetSelection(const ASelection: TPersistentSelectionList);
5280begin
5281  Selection := ASelection;
5282end;
5283
5284procedure TObjectInspectorDlg.SetShowComponentTree(const AValue: boolean);
5285begin
5286  if FShowComponentTree = AValue then Exit;
5287  FShowComponentTree := AValue;
5288  BeginUpdate;
5289  try
5290    ShowComponentTreePopupMenuItem.Checked := FShowComponentTree;
5291    // hide / show / rebuild controls
5292    AvailPersistentComboBox.Visible := not FShowComponentTree;
5293    ComponentPanel.Visible := FShowComponentTree;
5294    if FShowComponentTree then
5295      CreateTopSplitter
5296    else
5297      FreeAndNil(Splitter1);
5298    FillComponentList(True);
5299  finally
5300    EndUpdate;
5301  end;
5302end;
5303
5304procedure TObjectInspectorDlg.SetShowPropertyFilter(const AValue: Boolean);
5305begin
5306  if FShowPropertyFilter = AValue then exit;
5307  FShowPropertyFilter := AValue;
5308  PropFilterPanel.Visible := AValue;
5309  ShowPropertyFilterPopupMenuItem.Checked := AValue;
5310end;
5311
5312procedure TObjectInspectorDlg.SetShowInfoBox(const AValue: Boolean);
5313begin
5314  if FShowInfoBox = AValue then exit;
5315  FShowInfoBox := AValue;
5316  ShowInfoBoxPopupMenuItem.Checked := AValue;
5317  InfoPanel.Visible := AValue;
5318  if AValue then begin
5319    CreateBottomSplitter;
5320    if Assigned(FOnSelectionChange) then
5321      FOnSelectionChange(Self);
5322  end
5323  else
5324    FreeAndNil(Splitter2);
5325end;
5326
5327procedure TObjectInspectorDlg.SetShowStatusBar(const AValue: Boolean);
5328begin
5329  if FShowStatusBar = AValue then exit;
5330  FShowStatusBar := AValue;
5331  StatusBar.Visible := AValue;
5332  ShowStatusBarPopupMenuItem.Checked := AValue;
5333  if ShowInfoBox then // make sure StatusBar goes below InfoPanel.
5334    StatusBar.Top := InfoPanel.Top + InfoPanel.Height + 1;
5335end;
5336
5337procedure TObjectInspectorDlg.SetShowFavorites(const AValue: Boolean);
5338begin
5339  if FShowFavorites = AValue then exit;
5340  FShowFavorites := AValue;
5341  NoteBook.Page[2].TabVisible := AValue;
5342end;
5343
5344procedure TObjectInspectorDlg.SetShowRestricted(const AValue: Boolean);
5345begin
5346  if FShowRestricted = AValue then exit;
5347  FShowRestricted := AValue;
5348  NoteBook.Page[3].TabVisible := AValue;
5349end;
5350
5351procedure TObjectInspectorDlg.ShowNextPage(Delta: integer);
5352var
5353  NewPageIndex: Integer;
5354begin
5355  NewPageIndex := NoteBook.PageIndex;
5356  repeat
5357    NewPageIndex := NewPageIndex + Delta;
5358    if NewPageIndex >= NoteBook.PageCount then
5359      NewPageIndex := 0;
5360    if NewPageIndex < 0 then
5361      NewPageIndex := NoteBook.PageCount - 1;
5362    if NoteBook.Page[NewPageIndex].TabVisible then
5363    begin
5364      NoteBook.PageIndex := NewPageIndex;
5365      break;
5366    end;
5367  until NewPageIndex = NoteBook.PageIndex;
5368end;
5369
5370procedure TObjectInspectorDlg.RestrictedPageShow(Sender: TObject);
5371begin
5372  //DebugLn('RestrictedPageShow');
5373  DoUpdateRestricted;
5374end;
5375
5376procedure TObjectInspectorDlg.RestrictedPaint(
5377  ABox: TPaintBox; const ARestrictions: TWidgetSetRestrictionsArray);
5378
5379  function OutVertCentered(AX: Integer; const AStr: String): TSize;
5380  begin
5381    Result := ABox.Canvas.TextExtent(AStr);
5382    ABox.Canvas.TextOut(AX, (ABox.Height - Result.CY) div 2, AStr);
5383  end;
5384
5385var
5386  X, Y: Integer;
5387  lclPlatform: TLCLPlatform;
5388  None: Boolean;
5389  OldStyle: TBrushStyle;
5390  ImagesRes: TScaledImageListResolution;
5391  dist: Integer;
5392begin
5393  ImagesRes := IDEImages.Images_16.ResolutionForPPI[0, Font.PixelsPerInch, GetCanvasScaleFactor];
5394  dist := Scale96ToForm(4);
5395  X := 0;
5396  Y := (ABox.Height - ImagesRes.Height) div 2;
5397  OldStyle := ABox.Canvas.Brush.Style;
5398  try
5399    ABox.Canvas.Brush.Style := bsClear;
5400    None := True;
5401    for lclPlatform := Low(TLCLPlatform) to High(TLCLPlatform) do
5402    begin
5403      if ARestrictions[lclPlatform] = 0 then continue;
5404      None := False;
5405      ImagesRes.Draw(
5406        ABox.Canvas, X, Y,
5407        IDEImages.LoadImage('issue_'+LCLPlatformDirNames[lclPlatform]));
5408      Inc(X, ImagesRes.Width);
5409      Inc(X, Scale96ToForm(OutVertCentered(X, IntToStr(ARestrictions[lclPlatform])).CX));
5410      Inc(X, dist);
5411    end;
5412
5413    if None then
5414      OutVertCentered(4, oisNone);
5415  finally
5416    ABox.Canvas.Brush.Style := OldStyle;
5417  end;
5418end;
5419
5420procedure TObjectInspectorDlg.WidgetSetRestrictedPaint(Sender: TObject);
5421begin
5422  if RestrictedProps <> nil then
5423    RestrictedPaint(WidgetSetsRestrictedBox, RestrictedProps.WidgetSetRestrictions);
5424end;
5425
5426procedure TObjectInspectorDlg.ComponentRestrictedPaint(Sender: TObject);
5427var
5428  I, J: Integer;
5429  WSRestrictions: TWidgetSetRestrictionsArray;
5430  RestrProp: TOIRestrictedProperty;
5431begin
5432  if (RestrictedProps = nil) or (Selection = nil) then exit;
5433
5434  FillChar(WSRestrictions{%H-}, SizeOf(WSRestrictions), 0);
5435  for I := 0 to RestrictedProps.Count - 1 do
5436  begin
5437    if not (RestrictedProps.Items[I] is TOIRestrictedProperty) then continue;
5438    RestrProp:=TOIRestrictedProperty(RestrictedProps.Items[I]);
5439    for J := 0 to Selection.Count - 1 do
5440      with RestrProp do
5441        CheckRestrictions(Selection[J].ClassType, WSRestrictions);
5442  end;
5443
5444  RestrictedPaint(ComponentRestrictedBox, WSRestrictions);
5445end;
5446
5447procedure TObjectInspectorDlg.TopSplitterMoved(Sender: TObject);
5448begin
5449  Assert(Assigned(ComponentTree));
5450  ComponentTree.Invalidate;  // Update Scrollbars.
5451end;
5452
5453procedure TObjectInspectorDlg.CreateTopSplitter;
5454// vertical splitter between component tree and notebook
5455begin
5456  Splitter1 := TSplitter.Create(Self);
5457  with Splitter1 do
5458  begin
5459    Name := 'Splitter1';
5460    Parent := PnlClient;
5461    Align := alTop;
5462    Top := ComponentPanelHeight;
5463    Height := 5;
5464    OnMoved := @TopSplitterMoved;
5465  end;
5466end;
5467
5468procedure TObjectInspectorDlg.DefSelectionVisibleInDesigner;
5469  procedure ShowPage(const aPage: TTabSheet);
5470  begin
5471    if aPage.Parent is TPageControl then
5472      TPageControl(aPage.Parent).PageIndex := aPage.PageIndex;
5473  end;
5474  procedure ShowPage(const aPage: TPage);
5475  begin
5476    if aPage.Parent is TNotebook then
5477      TNotebook(aPage.Parent).PageIndex := aPage.PageIndex;
5478  end;
5479var
5480  Cnt: TControl;
5481begin
5482  if (Selection.Count = 0) or (Selection[0] = nil) or not(Selection[0] is TControl) then
5483    Exit;
5484
5485  Cnt := TControl(Selection[0]);
5486  while Cnt<>nil do
5487  begin
5488    if Cnt is TTabSheet then
5489      ShowPage(TTabSheet(Cnt))
5490    else
5491    if Cnt is TPage then
5492      ShowPage(TPage(Cnt));
5493
5494    Cnt := Cnt.Parent;
5495  end;
5496end;
5497
5498procedure TObjectInspectorDlg.CreateBottomSplitter;
5499// vertical splitter between notebook and info panel
5500begin
5501  Splitter2 := TSplitter.Create(Self);
5502  with Splitter2 do
5503  begin
5504    Name := 'Splitter2';
5505    Parent := PnlClient;
5506    Align := alBottom;
5507    Top := InfoPanel.Top - 1;
5508    Height := 5;
5509  end;
5510end;
5511
5512procedure TObjectInspectorDlg.DestroyNoteBook;
5513begin
5514  if NoteBook<>nil then
5515    NoteBook.Visible:=false;
5516  FreeAndNil(PropertyGrid);
5517  FreeAndNil(EventGrid);
5518  FreeAndNil(FavoriteGrid);
5519  FreeAndNil(RestrictedGrid);
5520  FreeAndNil(NoteBook);
5521end;
5522
5523procedure TObjectInspectorDlg.CreateNoteBook;
5524
5525  function CreateGrid(
5526    ATypeFilter: TTypeKinds; AOIPage: TObjectInspectorPage;
5527    ANotebookPage: Integer): TOICustomPropertyGrid;
5528  begin
5529    Result:=TOICustomPropertyGrid.CreateWithParams(
5530      Self, PropertyEditorHook, ATypeFilter, FDefaultItemHeight);
5531    with Result do
5532    begin
5533      Name := DefaultOIGridNames[AOIPage];
5534      Selection := Self.FSelection;
5535      Align := alClient;
5536      PopupMenu := MainPopupMenu;
5537      OnModified := @GridModified;
5538      OnSelectionChange := @GridSelectionChange;
5539      OnPropertyHint := @GridPropertyHint;
5540      OnOIKeyDown := @GridKeyDown;
5541      OnKeyUp := @GridKeyUp;
5542      OnDblClick := @GridDblClick;
5543      OnMouseWheel := @OnGridMouseWheel;
5544
5545      Parent := NoteBook.Page[ANotebookPage];
5546    end;
5547  end;
5548
5549  function AddPage(PageName, TabCaption: string): TTabSheet;
5550  begin
5551    Result:=TTabSheet.Create(Self);
5552    Result.Name:=PageName;
5553    Result.Caption:=TabCaption;
5554    Result.Parent:=NoteBook;
5555  end;
5556var
5557  APage: TTabSheet;
5558begin
5559  DestroyNoteBook;
5560
5561  // NoteBook
5562  NoteBook:=TPageControl.Create(Self);
5563  with NoteBook do
5564  begin
5565    Name := 'NoteBook';
5566    Parent := PropertyPanel;
5567    Align := alClient;
5568    PopupMenu := MainPopupMenu;
5569    OnChange := @NoteBookPageChange;
5570    BorderSpacing.Top := 2;
5571  end;
5572
5573  AddPage(DefaultOIPageNames[oipgpProperties],oisProperties);
5574
5575  AddPage(DefaultOIPageNames[oipgpEvents],oisEvents);
5576
5577  APage:=AddPage(DefaultOIPageNames[oipgpFavorite],oisFavorites);
5578  APage.TabVisible := ShowFavorites;
5579
5580  APage:=AddPage(DefaultOIPageNames[oipgpRestricted],oisRestricted);
5581  APage.TabVisible := ShowRestricted;
5582  APage.OnShow := @RestrictedPageShow;
5583
5584  NoteBook.PageIndex:=0;
5585
5586  PropertyGrid := CreateGrid(Filter - [tkMethod], oipgpProperties, 0);
5587  EventGrid := CreateGrid([tkMethod], oipgpEvents, 1);
5588  FavoriteGrid := CreateGrid(Filter + [tkMethod], oipgpFavorite, 2);
5589  FavoriteGrid.Favorites := FFavorites;
5590  RestrictedGrid := CreateGrid(Filter + [tkMethod], oipgpRestricted, 3);
5591
5592  RestrictedPanel := TPanel.Create(Self);
5593  with RestrictedPanel do
5594  begin
5595    Align := alTop;
5596    BevelOuter := bvNone;
5597    Parent := NoteBook.Page[3];
5598  end;
5599
5600  RestrictedInnerPanel := TPanel.Create(Self);
5601  with RestrictedInnerPanel do
5602  begin
5603    BevelOuter := bvNone;
5604    BorderSpacing.Around := 6;
5605    Parent := RestrictedPanel;
5606  end;
5607
5608  WidgetSetsRestrictedLabel := TLabel.Create(Self);
5609  with WidgetSetsRestrictedLabel do
5610  begin
5611    Caption := oisWidgetSetRestrictions;
5612    Top := 1;
5613    Align := alTop;
5614    AutoSize := True;
5615    Parent := RestrictedInnerPanel;
5616  end;
5617
5618  WidgetSetsRestrictedBox := TPaintBox.Create(Self);
5619  with WidgetSetsRestrictedBox do
5620  begin
5621    Top := 2;
5622    Align := alTop;
5623    Height := 24;
5624    OnPaint := @WidgetSetRestrictedPaint;
5625    Parent := RestrictedInnerPanel;
5626  end;
5627
5628  ComponentRestrictedLabel := TLabel.Create(Self);
5629  with ComponentRestrictedLabel do
5630  begin
5631    Caption := oisComponentRestrictions;
5632    Top := 3;
5633    Align := alTop;
5634    AutoSize := True;
5635    Parent := RestrictedInnerPanel;
5636  end;
5637
5638  ComponentRestrictedBox := TPaintBox.Create(Self);
5639  with ComponentRestrictedBox do
5640  begin
5641    Top := 4;
5642    Align := alTop;
5643    Height := 24;
5644    OnPaint := @ComponentRestrictedPaint;
5645    Parent := RestrictedInnerPanel;
5646  end;
5647
5648  RestrictedInnerPanel.AutoSize := True;
5649  RestrictedPanel.AutoSize := True;
5650end;
5651
5652procedure TObjectInspectorDlg.KeyDown(var Key: Word; Shift: TShiftState);
5653var
5654  CurGrid: TOICustomPropertyGrid;
5655begin
5656  // ToDo: Allow TAB key to FilterEdit, TreeView and Grid. Now the Grid gets seleted always.
5657  //DebugLn(['TObjectInspectorDlg.KeyDown: Key=', Key, ', ActiveControl=', ActiveControl]);
5658  //Do not disturb the combobox navigation while it has focus
5659  if not AvailPersistentComboBox.DroppedDown then begin
5660    CurGrid:=GetActivePropertyGrid;
5661    if CurGrid<>nil then begin
5662      CurGrid.HandleStandardKeys(Key,Shift);
5663      if Key=VK_UNKNOWN then exit;
5664    end;
5665  end;
5666  inherited KeyDown(Key, Shift);
5667  if (Key<>VK_UNKNOWN) and Assigned(OnRemainingKeyDown) then
5668    OnRemainingKeyDown(Self,Key,Shift);
5669end;
5670
5671procedure TObjectInspectorDlg.KeyUp(var Key: Word; Shift: TShiftState);
5672begin
5673  inherited KeyUp(Key, Shift);
5674  if (Key<>VK_UNKNOWN) and Assigned(OnRemainingKeyUp) then
5675    OnRemainingKeyUp(Self,Key,Shift);
5676end;
5677
5678procedure TObjectInspectorDlg.Resize;
5679begin
5680  inherited Resize;
5681  // BUG: resize gets called, even if nothing changed
5682  if Assigned(ComponentTree) and (FLastTreeSize <> ComponentTree.BoundsRect) then begin
5683    ComponentTree.Invalidate;  // Update Scrollbars.
5684    FLastTreeSize := ComponentTree.BoundsRect;
5685  end;
5686end;
5687
5688procedure TObjectInspectorDlg.ComponentTreeModified(Sender: TObject);
5689begin
5690  DoModified;
5691end;
5692
5693function TObjectInspectorDlg.GetSelectedPersistent: TPersistent;
5694begin
5695  if ComponentTree.Selection.Count = 1 then
5696    Result := ComponentTree.Selection[0]
5697  else
5698    Result := nil;
5699end;
5700
5701procedure TObjectInspectorDlg.ShowComponentTreePopupMenuItemClick(Sender: TObject);
5702begin
5703  ShowComponentTree:=not ShowComponentTree;
5704end;
5705
5706procedure TObjectInspectorDlg.ShowPropertyFilterPopupMenuItemClick(Sender: TObject);
5707begin
5708  ShowPropertyFilter := not ShowPropertyFilter;
5709end;
5710
5711procedure TObjectInspectorDlg.ShowHintPopupMenuItemClick(Sender : TObject);
5712var
5713  Page: TObjectInspectorPage;
5714begin
5715  for Page := Low(TObjectInspectorPage) to High(TObjectInspectorPage) do
5716    if GridControl[Page] <> nil then
5717      GridControl[Page].ShowHint := not GridControl[Page].ShowHint;
5718end;
5719
5720procedure TObjectInspectorDlg.ShowInfoBoxPopupMenuItemClick(Sender: TObject);
5721begin
5722  ShowInfoBox:=not ShowInfoBox;
5723end;
5724
5725procedure TObjectInspectorDlg.ShowStatusBarPopupMenuItemClick(Sender: TObject);
5726begin
5727  ShowStatusBar:=not ShowStatusBar;
5728end;
5729
5730procedure TObjectInspectorDlg.ShowOptionsPopupMenuItemClick(Sender: TObject);
5731begin
5732  if Assigned(FOnShowOptions) then FOnShowOptions(Sender);
5733end;
5734// ---
5735
5736procedure TObjectInspectorDlg.MainPopupMenuPopup(Sender: TObject);
5737const
5738  PropertyEditorMIPrefix = 'PropertyEditorVerbMenuItem';
5739  ComponentEditorMIPrefix = 'ComponentEditorVerbMenuItem';
5740var
5741  ComponentEditorVerbSeparator: TMenuItem;
5742  PropertyEditorVerbSeparator: TMenuItem;
5743
5744  procedure RemovePropertyEditorMenuItems;
5745  var
5746    I: Integer;
5747  begin
5748    PropertyEditorVerbSeparator := nil;
5749    for I := MainPopupMenu.Items.Count - 1 downto 0 do
5750      if Pos(PropertyEditorMIPrefix, MainPopupMenu.Items[I].Name) = 1 then
5751        MainPopupMenu.Items[I].Free;
5752  end;
5753
5754  procedure AddPropertyEditorMenuItems(Editor: TPropertyEditor);
5755  var
5756    I, VerbCount: Integer;
5757    Item: TMenuItem;
5758  begin
5759    VerbCount := Editor.GetVerbCount;
5760    for I := 0 to VerbCount - 1 do
5761    begin
5762      Item := NewItem(Editor.GetVerb(I), 0, False, True,
5763        @PropEditPopupClick, 0, PropertyEditorMIPrefix + IntToStr(i));
5764      Editor.PrepareItem(I, Item);
5765      Item.Tag:=I;
5766      MainPopupMenu.Items.Insert(I, Item);
5767    end;
5768    // insert the separator
5769    if VerbCount > 0 then
5770    begin
5771      PropertyEditorVerbSeparator := Menus.NewLine;
5772      PropertyEditorVerbSeparator.Name := PropertyEditorMIPrefix + IntToStr(VerbCount);
5773      MainPopupMenu.Items.Insert(VerbCount, PropertyEditorVerbSeparator);
5774    end;
5775  end;
5776
5777  procedure RemoveComponentEditorMenuItems;
5778  var
5779    I: Integer;
5780  begin
5781    ComponentEditorVerbSeparator:=nil;
5782    for I := MainPopupMenu.Items.Count - 1 downto 0 do
5783      if Pos(ComponentEditorMIPrefix, MainPopupMenu.Items[I].Name) = 1 then
5784        MainPopupMenu.Items[I].Free;
5785  end;
5786
5787  procedure AddComponentEditorMenuItems;
5788  var
5789    I, VerbCount: Integer;
5790    Item: TMenuItem;
5791  begin
5792    VerbCount := ComponentEditor.GetVerbCount;
5793    for I := 0 to VerbCount - 1 do
5794    begin
5795      Item := NewItem(ComponentEditor.GetVerb(I), 0, False, True,
5796        @ComponentEditorVerbMenuItemClick, 0, ComponentEditorMIPrefix + IntToStr(i));
5797      ComponentEditor.PrepareItem(I, Item);
5798      Item.Tag:=I;
5799      MainPopupMenu.Items.Insert(I, Item);
5800    end;
5801    // insert the separator
5802    if VerbCount > 0 then
5803    begin
5804      ComponentEditorVerbSeparator := Menus.NewLine;
5805      ComponentEditorVerbSeparator.Name := ComponentEditorMIPrefix + IntToStr(VerbCount);
5806      MainPopupMenu.Items.Insert(VerbCount, ComponentEditorVerbSeparator);
5807    end;
5808  end;
5809
5810  procedure AddCollectionEditorMenuItems({%H-}ACollection: TCollection);
5811  var
5812    Item: TMenuItem;
5813    intf: IObjInspInterface;
5814  begin
5815    if ACollection.GetInterface(GUID_ObjInspInterface, intf) and not intf.AllowAdd then
5816      exit;
5817
5818    Item := NewItem(oisAddCollectionItem, 0, False, True,
5819      @CollectionAddItem, 0, ComponentEditorMIPrefix+'0');
5820    MainPopupMenu.Items.Insert(0, Item);
5821    ComponentEditorVerbSeparator := NewLine;
5822    ComponentEditorVerbSeparator.Name := ComponentEditorMIPrefix+'1';
5823    MainPopupMenu.Items.Insert(1, ComponentEditorVerbSeparator);
5824  end;
5825
5826  procedure AddZOrderMenuItems;
5827  var
5828    ZItem, Item: TMenuItem;
5829  begin
5830    ZItem := NewSubMenu(oisZOrder, 0, ComponentEditorMIPrefix+'ZOrder', [], True);
5831    Item := NewItem(oisOrderMoveToFront, 0, False, True, @ZOrderItemClick, 0, '');
5832    Item.ImageIndex := IDEImages.LoadImage('Order_move_front');
5833    Item.Tag := 0;
5834    ZItem.Add(Item);
5835    Item := NewItem(oisOrderMoveToBack, 0, False, True, @ZOrderItemClick, 0, '');
5836    Item.ImageIndex := IDEImages.LoadImage('Order_move_back');
5837    Item.Tag := 1;
5838    ZItem.Add(Item);
5839    Item := NewItem(oisOrderForwardOne, 0, False, True, @ZOrderItemClick, 0, '');
5840    Item.ImageIndex := IDEImages.LoadImage('Order_forward_one');
5841    Item.Tag := 2;
5842    ZItem.Add(Item);
5843    Item := NewItem(oisOrderBackOne, 0, False, True, @ZOrderItemClick, 0, '');
5844    Item.ImageIndex := IDEImages.LoadImage('Order_back_one');
5845    Item.Tag := 3;
5846    ZItem.Add(Item);
5847    if ComponentEditorVerbSeparator <> nil then
5848      MainPopupMenu.Items.Insert(ComponentEditorVerbSeparator.MenuIndex + 1, ZItem)
5849    else
5850      MainPopupMenu.Items.Insert(0, ZItem);
5851    Item := NewLine;
5852    Item.Name := ComponentEditorMIPrefix+'ZOrderSeparator';
5853    MainPopupMenu.Items.Insert(ZItem.MenuIndex + 1, Item);
5854  end;
5855
5856var
5857  b, CanBeCopyPasted, CanBeDeleted, CanChangeClass, HasParentCand: Boolean;
5858  i: Integer;
5859  CurRow: TOIPropertyGridRow;
5860  Persistent: TPersistent;
5861  Page: TObjectInspectorPage;
5862begin
5863  RemovePropertyEditorMenuItems;
5864  RemoveComponentEditorMenuItems;
5865  ShowHintsPopupMenuItem.Checked := PropertyGrid.ShowHint;
5866  FStateOfHintsOnMainPopupMenu:=PropertyGrid.ShowHint;
5867  for Page := Low(TObjectInspectorPage) to High(TObjectInspectorPage) do
5868    if GridControl[Page] <> nil then
5869      GridControl[Page].ShowHint := False;
5870  Persistent := GetSelectedPersistent;
5871  CanBeCopyPasted := False;
5872  CanBeDeleted := False;
5873  CanChangeClass := False;
5874  HasParentCand := False;
5875  // show component editors only for component treeview
5876  if MainPopupMenu.PopupComponent = ComponentTree then
5877  begin
5878    ComponentEditor := GetComponentEditorForSelection;
5879    if ComponentEditor <> nil then
5880      AddComponentEditorMenuItems
5881    else
5882    begin
5883      // check if it is a TCollection
5884      if Persistent is TCollection then
5885        AddCollectionEditorMenuItems(TCollection(Persistent))
5886      else if Persistent is TCollectionItem then
5887        AddCollectionEditorMenuItems(TCollectionItem(Persistent).Collection);
5888    end;
5889    for i:=0 to Selection.Count-1 do
5890      if Selection[i] is TComponent then
5891      begin
5892        CanBeDeleted := True;
5893        // ToDo: Figure out why TMenuItem or TAction cannot be copy / pasted in OI,
5894        if not (Selection[i] is TMenuItem) and
5895           not (Selection[i] is TAction) then     // then fix it.
5896          CanBeCopyPasted := True;
5897      end;
5898    CanChangeClass := (Selection.Count = 1) and (Selection[0] is TComponent)
5899                  and (Selection[0] <> FPropertyEditorHook.LookupRoot);
5900    // add Z-Order menu
5901    if (Selection.Count = 1) and (Selection[0] is TControl) then
5902      AddZOrderMenuItems;
5903    // check existing of Change Parent candidates
5904    if CanBeCopyPasted then
5905      HasParentCand := HasParentCandidates;
5906  end;
5907  CutPopupMenuItem.Visible := CanBeCopyPasted;
5908  CopyPopupMenuItem.Visible := CanBeCopyPasted;
5909  PastePopupMenuItem.Visible := CanBeCopyPasted;
5910  DeletePopupMenuItem.Visible := CanBeDeleted;
5911  OptionsSeparatorMenuItem2.Visible := CanBeCopyPasted or CanBeDeleted;
5912  ChangeClassPopupmenuItem.Visible := CanChangeClass;
5913  ChangeParentPopupmenuItem.Visible := HasParentCand;
5914  OptionsSeparatorMenuItem3.Visible := CanChangeClass or HasParentCand;
5915
5916  // The editors can do menu actions, for example set defaults and constraints
5917  CurRow := GetActivePropertyRow;
5918  if (MainPopupMenu.PopupComponent is TOICustomPropertyGrid) then
5919  begin
5920    // popup menu of property grid
5921    if CurRow<>nil then
5922      AddPropertyEditorMenuItems(CurRow.Editor);
5923
5924    b := (Favorites <> nil) and ShowFavorites and (GetActivePropertyRow <> nil);
5925    AddToFavoritesPopupMenuItem.Visible := b and
5926      (GetActivePropertyGrid <> FavoriteGrid) and Assigned(OnAddToFavorites);
5927    RemoveFromFavoritesPopupMenuItem.Visible := b and
5928      (GetActivePropertyGrid = FavoriteGrid) and Assigned(OnRemoveFromFavorites);
5929
5930    UndoPropertyPopupMenuItem.Visible := True;
5931    UndoPropertyPopupMenuItem.Enabled := (CurRow<>nil)
5932      and (CurRow.Editor.GetVisualValue <> GetActivePropertyGrid.CurrentEditValue);
5933    if CurRow=nil then begin
5934      FindDeclarationPopupmenuItem.Visible := False;
5935    end
5936    else begin
5937      FindDeclarationPopupmenuItem.Visible := true;
5938      FindDeclarationPopupmenuItem.Caption := Format(oisJumpToDeclarationOf, [CurRow.Name]);
5939      FindDeclarationPopupmenuItem.Hint := Format(oisJumpToDeclarationOf,
5940                                              [CurRow.Editor.GetPropertyPath(0)]);
5941    end;
5942    ViewRestrictedPropertiesPopupMenuItem.Visible := True;
5943    OptionsSeparatorMenuItem.Visible := True;
5944  end
5945  else
5946  begin
5947    // default popup menu
5948    AddToFavoritesPopupMenuItem.Visible := False;
5949    RemoveFromFavoritesPopupMenuItem.Visible := False;
5950    UndoPropertyPopupMenuItem.Visible := False;
5951    FindDeclarationPopupmenuItem.Visible := False;
5952    ViewRestrictedPropertiesPopupMenuItem.Visible := False;
5953    OptionsSeparatorMenuItem.Visible := False;
5954  end;
5955  //debugln(['TObjectInspectorDlg.OnMainPopupMenuPopup ',FindDeclarationPopupmenuItem.Visible]);
5956end;
5957
5958procedure TObjectInspectorDlg.DoModified;
5959begin
5960  if Assigned(FOnModified) then FOnModified(Self);
5961end;
5962
5963procedure TObjectInspectorDlg.DoUpdateRestricted;
5964begin
5965  if Assigned(FOnUpdateRestricted) then FOnUpdateRestricted(Self);
5966end;
5967
5968procedure TObjectInspectorDlg.DoViewRestricted;
5969begin
5970  if Assigned(FOnViewRestricted) then FOnViewRestricted(Self);
5971end;
5972
5973procedure TObjectInspectorDlg.ChangeParentItemClick(Sender: TObject);
5974begin
5975  if Selection.Count > 0 then
5976    ChangeParent;
5977end;
5978
5979procedure TObjectInspectorDlg.ComponentEditorVerbMenuItemClick(Sender: TObject);
5980var
5981  Verb: integer;
5982  AMenuItem: TMenuItem;
5983begin
5984  if Sender is TMenuItem then
5985    AMenuItem := TMenuItem(Sender)
5986  else
5987    Exit;
5988  Verb := AMenuItem.Tag;
5989  ComponentEditor.ExecuteVerb(Verb);
5990end;
5991
5992procedure TObjectInspectorDlg.CollectionAddItem(Sender: TObject);
5993var
5994  Persistent: TPersistent;
5995  Collection: TCollection absolute Persistent;
5996  ci: TCollectionItem;
5997begin
5998  Persistent := GetSelectedPersistent;
5999  if Persistent = nil then
6000    Exit;
6001  if Persistent is TCollectionItem then
6002    Persistent := TCollectionItem(Persistent).Collection;
6003  if not (Persistent is TCollection) then
6004    Exit;
6005  ci:=Collection.Add;
6006  GlobalDesignHook.PersistentAdded(ci,false);
6007  DoModified;
6008  Selection.ForceUpdate := True;
6009  try
6010    SetSelection(Selection);
6011  finally
6012    Selection.ForceUpdate := False;
6013  end;
6014end;
6015
6016procedure TObjectInspectorDlg.ZOrderItemClick(Sender: TObject);
6017var
6018  Control: TControl;
6019  ZOrder: TZOrderDelete;
6020  NewSelection: TPersistentSelectionList;
6021begin
6022  if not (Sender is TMenuItem) then Exit;
6023  if (Selection.Count <> 1) or not (Selection[0] is TControl) then Exit;
6024  Control := TControl(Selection[0]);
6025  if Control.Parent = nil then Exit;
6026  // The enum matches with the Tag numbers.
6027  ZOrder := TZOrderDelete(TMenuItem(Sender).Tag);
6028  case ZOrder of
6029    zoToFront: Control.BringToFront;
6030    zoToBack:  Control.SendToBack;
6031    zoForward: Control.Parent.SetControlIndex(Control, Control.Parent.GetControlIndex(Control) + 1);
6032    zoBackward:Control.Parent.SetControlIndex(Control, Control.Parent.GetControlIndex(Control) - 1);
6033  end;
6034
6035  // Ensure controls that belong to a container are rearranged if required.
6036  Control.Parent.ReAlign;
6037
6038  // Ensure the order of controls in the OI now reflects the new ZOrder
6039  NewSelection := TPersistentSelectionList.Create;
6040  try
6041    NewSelection.ForceUpdate:=True;
6042    NewSelection.Add(Control.Parent);
6043    SetSelection(NewSelection);
6044    NewSelection.Clear;
6045    NewSelection.ForceUpdate:=True;
6046    NewSelection.Add(Control);
6047    SetSelection(NewSelection);
6048  finally
6049    NewSelection.Free;
6050  end;
6051  DoModified;
6052  ChangeCompZOrderInList(Control, ZOrder);
6053end;
6054
6055function TObjectInspectorDlg.GetComponentPanelHeight: integer;
6056begin
6057  Result := ComponentPanel.Height;
6058end;
6059
6060function TObjectInspectorDlg.GetInfoBoxHeight: integer;
6061begin
6062  Result := InfoPanel.Height;
6063end;
6064
6065procedure TObjectInspectorDlg.SetEnableHookGetSelection(AValue: boolean);
6066begin
6067  if FEnableHookGetSelection=AValue then Exit;
6068  FEnableHookGetSelection:=AValue;
6069  if PropertyEditorHook<>nil then
6070    if EnableHookGetSelection then
6071      FPropertyEditorHook.AddHandlerGetSelection(@HookGetSelection)
6072    else
6073      FPropertyEditorHook.RemoveHandlerGetSelection(@HookGetSelection)
6074end;
6075
6076procedure TObjectInspectorDlg.SetFilter(const AValue: TTypeKinds);
6077begin
6078  if FFilter=AValue then Exit;
6079  FFilter:=AValue;
6080  PropertyGrid.Filter := Filter - [tkMethod];
6081  FavoriteGrid.Filter := Filter + [tkMethod];
6082  RestrictedGrid.Filter := Filter + [tkMethod];
6083end;
6084
6085procedure TObjectInspectorDlg.ActivateGrid(Grid: TOICustomPropertyGrid);
6086begin
6087  if Grid=PropertyGrid then NoteBook.PageIndex:=0
6088  else if Grid=EventGrid then NoteBook.PageIndex:=1
6089  else if Grid=FavoriteGrid then NoteBook.PageIndex:=2
6090  else if Grid=RestrictedGrid then NoteBook.PageIndex:=3;
6091end;
6092
6093procedure TObjectInspectorDlg.FocusGrid(Grid: TOICustomPropertyGrid);
6094var
6095  Index: Integer;
6096begin
6097  if Grid=nil then
6098    Grid := GetActivePropertyGrid
6099  else
6100    ActivateGrid(Grid);
6101  if Grid <> nil then
6102  begin
6103    Index := Grid.ItemIndex;
6104    if Index < 0 then
6105      Index := 0;
6106    Grid.SetItemIndexAndFocus(Index);
6107  end;
6108end;
6109
6110function TObjectInspectorDlg.GetGridControl(Page: TObjectInspectorPage
6111  ): TOICustomPropertyGrid;
6112begin
6113  case Page of
6114  oipgpFavorite: Result:=FavoriteGrid;
6115  oipgpEvents: Result:=EventGrid;
6116  oipgpRestricted: Result:=RestrictedGrid;
6117  else  Result:=PropertyGrid;
6118  end;
6119end;
6120
6121procedure TObjectInspectorDlg.SetComponentEditor(const AValue: TBaseComponentEditor);
6122begin
6123  if FComponentEditor <> AValue then
6124  begin
6125    FComponentEditor.Free;
6126    FComponentEditor := AValue;
6127  end;
6128end;
6129
6130procedure TObjectInspectorDlg.SetFavorites(const AValue: TOIFavoriteProperties);
6131begin
6132  //debugln('TObjectInspectorDlg.SetFavorites ',dbgsName(Self));
6133  if FFavorites=AValue then exit;
6134  FFavorites:=AValue;
6135  FavoriteGrid.Favorites:=FFavorites;
6136end;
6137
6138procedure TObjectInspectorDlg.ComponentTreeGetNodeImageIndex(
6139  APersistent: TPersistent; var AIndex: integer);
6140begin
6141  //ask TMediator
6142  if assigned(FOnNodeGetImageIndex) then
6143    FOnNodeGetImageIndex(APersistent, AIndex);
6144end;
6145
6146{ TCustomPropertiesGrid }
6147
6148function TCustomPropertiesGrid.GetTIObject: TPersistent;
6149begin
6150  if PropertyEditorHook<>nil then
6151    Result:=PropertyEditorHook.LookupRoot
6152  else
6153    Result:=Nil;
6154end;
6155
6156procedure TCustomPropertiesGrid.SetAutoFreeHook(const AValue: boolean);
6157begin
6158  if FAutoFreeHook=AValue then exit;
6159  FAutoFreeHook:=AValue;
6160end;
6161
6162procedure TCustomPropertiesGrid.SetTIObject(const AValue: TPersistent);
6163var
6164  NewSelection: TPersistentSelectionList;
6165begin
6166  if (TIObject=AValue) then begin
6167    if ((AValue<>nil) and (Selection.Count=1) and (Selection[0]=AValue))
6168    or (AValue=nil) then
6169      exit;
6170  end;
6171  if SaveOnChangeTIObject then
6172    SaveChanges;
6173  if PropertyEditorHook=nil then
6174  begin
6175    fAutoFreeHook:=true;
6176    PropertyEditorHook:=TPropertyEditorHook.Create(Self);
6177  end;
6178  PropertyEditorHook.LookupRoot:=AValue;
6179  if (AValue=nil) or (Selection.Count<>1) or (Selection[0]<>AValue) then
6180  begin
6181    NewSelection:=TPersistentSelectionList.Create;
6182    try
6183      if AValue<>nil then
6184        NewSelection.Add(AValue);
6185      Selection:=NewSelection;
6186    finally
6187      NewSelection.Free;
6188    end;
6189  end;
6190end;
6191
6192constructor TCustomPropertiesGrid.Create(TheOwner: TComponent);
6193var
6194  Hook: TPropertyEditorHook;
6195begin
6196  Hook:=TPropertyEditorHook.Create(Self);
6197  FAutoFreeHook:=true;
6198  FSaveOnChangeTIObject:=true;
6199  CreateWithParams(TheOwner,Hook,AllTypeKinds,0);
6200end;
6201
6202destructor TCustomPropertiesGrid.Destroy;
6203begin
6204  if FAutoFreeHook then
6205    FreeAndNil(FPropertyEditorHook);
6206  inherited Destroy;
6207end;
6208
6209end.
6210
6211