lazarus/ideintf/objectinspector.pp
vincents 89fe6ac437 replaced writeln by DebugLn
git-svn-id: trunk@6001 -
2004-09-14 21:30:37 +00:00

3084 lines
89 KiB
ObjectPascal

{
*****************************************************************************
* *
* See the file COPYING.modifiedLGPL, included in this distribution, *
* for details about the copyright. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
*****************************************************************************
Author: Mattias Gaertner
Abstract:
This unit defines the TObjectInspector.
It uses TOIPropertyGrid and TOIPropertyGridRow which are also defined in this
unit. The object inspector uses property editors (see TPropertyEditor) to
display and control properties, thus the object inspector is merely an
object viewer than an editor. The property editors do the real work.
ToDo:
- backgroundcolor=clNone
- replace pair splitter with splitter
- Define Init values
- Set to init value
}
unit ObjectInspector;
{$MODE OBJFPC}{$H+}
{off $DEFINE DoNotCatchOIExceptions}
interface
uses
Forms, SysUtils, Buttons, Classes, Graphics, GraphType, StdCtrls, LCLType,
LCLIntf, LCLProc, Controls, ComCtrls, ExtCtrls, TypInfo, Messages,
LResources, PairSplitter, Laz_XMLCfg, Menus, Dialogs, ObjInspStrConsts,
PropEdits, GraphPropEdits, ListViewPropEdit, ImageListEditor,
ComponentTreeView;
type
EObjectInspectorException = class(Exception);
TObjectInspector = class;
{ TOIOptions }
TOIOptions = class
private
FComponentTreeHeight: integer;
FCustomXMLCfg: TXMLConfig;
FDefaultItemHeight: integer;
FFilename:string;
FFileAge: longint;
FShowComponentTree: boolean;
FXMLCfg: TXMLConfig;
FFileHasChangedOnDisk: boolean;
FSaveBounds: boolean;
FLeft: integer;
FTop: integer;
FWidth: integer;
FHeight: integer;
FPropertyGridSplitterX: integer;
FEventGridSplitterX: integer;
FGridBackgroundColor: TColor;
FShowHints: boolean;
procedure SetFilename(const NewFilename: string);
function FileHasChangedOnDisk: boolean;
function GetXMLCfg: TXMLConfig;
procedure FileUpdated;
public
constructor Create;
destructor Destroy; override;
function Load: boolean;
function Save: boolean;
procedure Assign(AnObjInspector: TObjectInspector);
procedure AssignTo(AnObjInspector: TObjectInspector);
public
property Filename:string read FFilename write SetFilename;
property CustomXMLCfg: TXMLConfig read FCustomXMLCfg write FCustomXMLCfg;
property SaveBounds:boolean read FSaveBounds write FSaveBounds;
property Left:integer read FLeft write FLeft;
property Top:integer read FTop write FTop;
property Width:integer read FWidth write FWidth;
property Height:integer read FHeight write FHeight;
property PropertyGridSplitterX:integer read FPropertyGridSplitterX
write FPropertyGridSplitterX;
property EventGridSplitterX:integer read FEventGridSplitterX
write FEventGridSplitterX;
property DefaultItemHeight: integer read FDefaultItemHeight
write FDefaultItemHeight;
property ShowComponentTree: boolean read FShowComponentTree
write FShowComponentTree;
property ComponentTreeHeight: integer read FComponentTreeHeight
write FComponentTreeHeight;
property GridBackgroundColor: TColor read FGridBackgroundColor
write FGridBackgroundColor;
property ShowHints: boolean read FShowHints
write FShowHints;
end;
TOICustomPropertyGrid = class;
{ TOIPropertyGridRow }
TOIPropertyGridRow = class
private
FTop:integer;
FHeight:integer;
FLvl:integer;
FName:string;
FExpanded: boolean;
FTree:TOICustomPropertyGrid;
FChildCount:integer;
FPriorBrother,
FFirstChild,
FLastChild,
FNextBrother,
FParent:TOIPropertyGridRow;
FEditor: TPropertyEditor;
procedure GetLvl;
public
constructor Create(PropertyTree:TOICustomPropertyGrid; PropEditor:TPropertyEditor;
ParentNode:TOIPropertyGridRow);
destructor Destroy; override;
function ConsistencyCheck: integer;
function HasChild(Row: TOIPropertyGridRow): boolean;
public
Index:integer;
LastPaintedValue:string;
function GetBottom:integer;
function IsReadOnly: boolean;
function IsDisabled: boolean;
procedure MeasureHeight(ACanvas: TCanvas);
public
property Editor:TPropertyEditor read FEditor;
property Top:integer read FTop write FTop;
property Height:integer read FHeight write FHeight;
property Bottom: integer read GetBottom;
property Lvl:integer read FLvl;
property Name: string read FName;
property Expanded:boolean read FExpanded;
property Tree:TOICustomPropertyGrid read FTree;
property Parent:TOIPropertyGridRow read FParent;
property ChildCount:integer read FChildCount;
property FirstChild:TOIPropertyGridRow read FFirstChild;
property LastChild:TOIPropertyGridRow read FFirstChild;
property NextBrother:TOIPropertyGridRow read FNextBrother;
property PriorBrother:TOIPropertyGridRow read FPriorBrother;
end;
//----------------------------------------------------------------------------
TOIPropertyGridState = (pgsChangingItemIndex, pgsApplyingValue);
TOIPropertyGridStates = set of TOIPropertyGridState;
TOICustomPropertyGrid = class(TCustomControl)
private
FBackgroundColor:TColor;
FChangeStep: integer;
FCurrentButton: TWinControl; // nil or ValueButton
FCurrentEdit: TWinControl; // nil or ValueEdit or ValueComboBox
FCurrentEditorLookupRoot: TPersistent;
FDefaultItemHeight:integer;
FDragging:boolean;
FExpandedProperties:TStringList;
FExpandingRow:TOIPropertyGridRow;
FFilter: TTypeKinds;
FIndent:integer;
FItemIndex:integer;
FNameFont,FDefaultValueFont,FValueFont:TFont;
FOnModified: TNotifyEvent;
FPreferredSplitterX: integer; // best splitter position
FPropertyEditorHook: TPropertyEditorHook;
FRows:TList;
FSelection: TPersistentSelectionList;
FSplitterX:integer; // current splitter position
FStates: TOIPropertyGridStates;
FTopY:integer;
// hint stuff
FHintTimer : TTimer;
FHintWindow : THintWindow;
Procedure HintTimer(Sender: TObject);
Procedure ResetHintTimer;
procedure OnUserInput(Sender: TObject; Msg: Cardinal);
procedure IncreaseChangeStep;
function GetRow(Index:integer):TOIPropertyGridRow;
function GetRowCount:integer;
procedure ClearRows;
function GetCurrentEditValue: string;
procedure SetCurrentEditValue(const AValue: string);
procedure SetItemIndex(NewIndex:integer);
procedure SetItemsTops;
procedure AlignEditComponents;
procedure EndDragSplitter;
procedure SetSplitterX(const NewValue:integer);
procedure SetTopY(const NewValue:integer);
function GetTreeIconX(Index:integer):integer;
function RowRect(ARow:integer):TRect;
procedure PaintRow(ARow:integer);
procedure DoPaint(PaintOnlyChangedValues:boolean);
procedure SetSelection(const ASelection:TPersistentSelectionList);
procedure SetPropertyEditorHook(NewPropertyEditorHook:TPropertyEditorHook);
procedure AddPropertyEditor(PropEditor: TPropertyEditor);
procedure AddStringToComboBox(const s:string);
procedure ExpandRow(Index:integer);
procedure ShrinkRow(Index:integer);
procedure AddSubEditor(PropEditor:TPropertyEditor);
procedure SetRowValue;
procedure DoCallEdit;
procedure RefreshValueEdit;
Procedure ValueEditDblClick(Sender : TObject);
procedure ValueEditMouseDown(Sender: TObject; Button:TMouseButton;
Shift: TShiftState; X,Y:integer);
procedure ValueEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure ValueEditKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure ValueEditExit(Sender: TObject);
procedure ValueEditChange(Sender: TObject);
procedure ValueComboBoxExit(Sender: TObject);
procedure ValueComboBoxChange(Sender: TObject);
procedure ValueComboBoxKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure ValueComboBoxKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure ValueComboBoxCloseUp(Sender: TObject);
procedure ValueComboBoxDropDown(Sender: TObject);
procedure ValueButtonClick(Sender: TObject);
procedure ValueComboBoxDrawItem(Control: TWinControl; Index: Integer;
ARect: TRect; State: TOwnerDrawState);
procedure WMVScroll(var Msg: TWMScroll); message WM_VSCROLL;
procedure SetBackgroundColor(const AValue: TColor);
procedure UpdateScrollBar;
protected
procedure CreateParams(var Params: TCreateParams); override;
procedure MouseDown(Button:TMouseButton; Shift:TShiftState; X,Y:integer); override;
procedure MouseMove(Shift:TShiftState; X,Y:integer); override;
procedure MouseUp(Button:TMouseButton; Shift:TShiftState; X,Y:integer); override;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure HandleStandardKeys(var Key: Word; Shift: TShiftState); virtual;
procedure HandleKeyUp(var Key: Word; Shift: TShiftState); virtual;
procedure EraseBackground(DC: HDC); override;
procedure DoSetBounds(ALeft, ATop, AWidth, AHeight: integer); override;
public
ValueEdit:TEdit;
ValueComboBox:TComboBox;
ValueButton:TButton;
constructor Create(TheOwner: TComponent); override;
constructor CreateWithParams(AnOwner: TComponent;
APropertyEditorHook: TPropertyEditorHook;
TypeFilter: TTypeKinds;
DefItemHeight: integer);
destructor Destroy; override;
function CanEditRowValue: boolean;
function ConsistencyCheck: integer;
function GetActiveRow: TOIPropertyGridRow;
function GetHintTypeAt(RowIndex: integer; X: integer): TPropEditHint;
function GetRowByPath(const PropPath:string): TOIPropertyGridRow;
function GridHeight:integer;
function MouseToIndex(y:integer;MustExist:boolean):integer;
function PropertyPath(Index:integer):string;
function TopMax:integer;
procedure BuildPropertyList;
procedure Clear;
procedure Paint; override;
procedure PropEditLookupRootChange;
procedure RefreshPropertyValues;
procedure SetBounds(aLeft,aTop,aWidth,aHeight:integer); override;
procedure SetCurrentRowValue(const NewValue: string);
public
property BackgroundColor: TColor read FBackgroundColor
write SetBackgroundColor default clBtnFace;
property BorderStyle default bsSingle;
property CurrentEditValue: string read GetCurrentEditValue
write SetCurrentEditValue;
property DefaultItemHeight:integer read FDefaultItemHeight
write FDefaultItemHeight default 25;
property DefaultValueFont:TFont read FDefaultValueFont write FDefaultValueFont;
property ExpandedProperties:TStringList read FExpandedProperties
write FExpandedProperties;
property Indent:integer read FIndent write FIndent default 9;
property ItemIndex:integer read FItemIndex write SetItemIndex;
property NameFont:TFont read FNameFont write FNameFont;
property OnModified: TNotifyEvent read FOnModified write FOnModified;
property PrefferedSplitterX: integer read FPreferredSplitterX
write FPreferredSplitterX default 100;
property PropertyEditorHook: TPropertyEditorHook read FPropertyEditorHook
write SetPropertyEditorHook;
property RowCount:integer read GetRowCount;
property Rows[Index:integer]:TOIPropertyGridRow read GetRow;
property Selection: TPersistentSelectionList read FSelection
write SetSelection;
property SplitterX:integer read FSplitterX write SetSplitterX default 100;
property TopY:integer read FTopY write SetTopY default 0;
property ValueFont:TFont read FValueFont write FValueFont;
end;
{ TOIPropertyGrid }
TOIPropertyGrid = class(TOICustomPropertyGrid)
published
property Align;
property Anchors;
property BackgroundColor;
property BorderStyle;
property Constraints;
property DefaultItemHeight;
property DefaultValueFont;
property Indent;
property NameFont;
property OnChangeBounds;
property OnClick;
property OnEnter;
property OnExit;
property OnKeyDown;
property OnKeyPress;
property OnKeyUp;
property OnModified;
property OnMouseDown;
property OnMouseEnter;
property OnMouseLeave;
property OnMouseMove;
property OnMouseUp;
property OnResize;
property PopupMenu;
property PrefferedSplitterX;
property SplitterX;
property Tabstop;
property ValueFont;
property Visible;
end;
{ TCustomPropertiesGrid }
TCustomPropertiesGrid = class(TOICustomPropertyGrid)
private
FAutoFreeHook: boolean;
function GetTIObject: TPersistent;
procedure SetAutoFreeHook(const AValue: boolean);
procedure SetTIObject(const AValue: TPersistent);
public
constructor Create(TheOwner: TComponent); override;
property TIObject: TPersistent read GetTIObject write SetTIObject;
property AutoFreeHook: boolean read FAutoFreeHook write SetAutoFreeHook;
end;
//============================================================================
{ TObjectInspector }
TOnAddAvailablePersistent = procedure(APersistent: TPersistent;
var Allowed: boolean) of object;
TOIFlag = (
oifRebuildPropListsNeeded
);
TOIFlags = set of TOIFlag;
TObjectInspector = class (TForm)
AvailPersistentComboBox: TComboBox;
PairSplitter1: TPairSplitter;
ComponentTree: TComponentTreeView;
NoteBook: TNoteBook;
PropertyGrid: TOICustomPropertyGrid;
EventGrid: TOICustomPropertyGrid;
StatusBar: TStatusBar;
MainPopupMenu: TPopupMenu;
ColorsPopupMenuItem: TMenuItem;
SetDefaultPopupMenuItem: TMenuItem;
UndoPropertyPopupMenuItem: TMenuItem;
BackgroundColPopupMenuItem: TMenuItem;
ShowHintsPopupMenuItem: TMenuItem;
ShowComponentTreePopupMenuItem: TMenuItem;
ShowOptionsPopupMenuItem: TMenuItem;
procedure AvailComboBoxCloseUp(Sender: TObject);
procedure ComponentTreeSelectionChanged(Sender: TObject);
procedure ObjectInspectorResize(Sender: TObject);
procedure OnGriddKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure OnSetDefaultPopupmenuItemClick(Sender: TObject);
procedure OnUndoPopupmenuItemClick(Sender: TObject);
procedure OnBackgroundColPopupMenuItemClick(Sender: TObject);
procedure OnShowHintPopupMenuItemClick(Sender: TObject);
procedure OnShowOptionsPopupMenuItemClick(Sender: TObject);
procedure OnShowComponentTreePopupMenuItemClick(Sender: TObject);
procedure OnMainPopupMenuPopup(Sender: TObject);
procedure HookRefreshPropertyValues;
private
FOnRemainingKeyUp: TKeyEvent;
FSelection: TPersistentSelectionList;
FComponentTreeHeight: integer;
FDefaultItemHeight: integer;
FFlags: TOIFlags;
FOnShowOptions: TNotifyEvent;
FPropertyEditorHook:TPropertyEditorHook;
FOnAddAvailablePersistent: TOnAddAvailablePersistent;
FOnSelectPersistentsInOI: TNotifyEvent;
FOnModified: TNotifyEvent;
FShowComponentTree: boolean;
FUpdateLock: integer;
FUpdatingAvailComboBox: boolean;
FUsePairSplitter: boolean;
protected
function PersistentToString(APersistent: TPersistent): string;
procedure SetComponentTreeHeight(const AValue: integer);
procedure SetDefaultItemHeight(const AValue: integer);
procedure SetOnShowOptions(const AValue: TNotifyEvent);
procedure SetPropertyEditorHook(NewValue: TPropertyEditorHook);
procedure SetSelection(const ASelection: TPersistentSelectionList);
procedure AddPersistentToList(APersistent: TPersistent; List: TStrings);
procedure HookLookupRootChange;
procedure OnGridModified(Sender: TObject);
procedure SetAvailComboBoxText;
procedure HookGetSelection(const ASelection: TPersistentSelectionList);
procedure HookSetSelection(const ASelection: TPersistentSelectionList);
procedure SetShowComponentTree(const AValue: boolean);
procedure SetUsePairSplitter(const AValue: boolean);
procedure CreatePairSplitter;
procedure DestroyNoteBook;
procedure CreateNoteBook;
procedure KeyDown(var Key: Word; Shift: TShiftState); override;
procedure KeyUp(var Key: Word; Shift: TShiftState); override;
public
constructor Create(AnOwner: TComponent); override;
destructor Destroy; override;
procedure RefreshSelection;
procedure RefreshPropertyValues;
procedure RebuildPropertyLists;
procedure FillPersistentComboBox;
procedure BeginUpdate;
procedure EndUpdate;
function GetActivePropertyGrid: TOICustomPropertyGrid;
function GetActivePropertyRow: TOIPropertyGridRow;
function GetCurRowDefaultValue(var DefaultStr: string): boolean;
public
property DefaultItemHeight: integer read FDefaultItemHeight
write SetDefaultItemHeight;
property Selection: TPersistentSelectionList
read FSelection write SetSelection;
property OnAddAvailPersistent: TOnAddAvailablePersistent
read FOnAddAvailablePersistent write FOnAddAvailablePersistent;
property OnSelectPersistentsInOI: TNotifyEvent
read FOnSelectPersistentsInOI write FOnSelectPersistentsInOI;
property PropertyEditorHook: TPropertyEditorHook
read FPropertyEditorHook write SetPropertyEditorHook;
property OnModified: TNotifyEvent read FOnModified write FOnModified;
property OnShowOptions: TNotifyEvent read FOnShowOptions write SetOnShowOptions;
property OnRemainingKeyUp: TKeyEvent read FOnRemainingKeyUp write FOnRemainingKeyUp;
property ShowComponentTree: boolean read FShowComponentTree write SetShowComponentTree;
property ComponentTreeHeight: integer read FComponentTreeHeight write SetComponentTreeHeight;
property UsePairSplitter: boolean read FUsePairSplitter write SetUsePairSplitter;
end;
const
DefaultObjectInspectorName: string = 'ObjectInspector';
//******************************************************************************
implementation
const
ScrollBarWidth=0;
function SortGridRows(Item1, Item2 : pointer) : integer;
begin
Result:= AnsiCompareText(TOIPropertyGridRow(Item1).Name, TOIPropertyGridRow(Item2).Name);
end;
{ TOICustomPropertyGrid }
constructor TOICustomPropertyGrid.CreateWithParams(AnOwner:TComponent;
APropertyEditorHook:TPropertyEditorHook; TypeFilter:TTypeKinds;
DefItemHeight: integer);
begin
inherited Create(AnOwner);
FSelection:=TPersistentSelectionList.Create;
FPropertyEditorHook:=APropertyEditorHook;
FFilter:=TypeFilter;
FItemIndex:=-1;
FStates:=[];
FRows:=TList.Create;
FExpandingRow:=nil;
FDragging:=false;
FExpandedProperties:=TStringList.Create;
FCurrentEdit:=nil;
FCurrentButton:=nil;
// visible values
FTopY:=0;
FSplitterX:=100;
FPreferredSplitterX:=FSplitterX;
FIndent:=9;
FBackgroundColor:=clBtnFace;
FNameFont:=TFont.Create;
FNameFont.Color:=clWindowText;
FValueFont:=TFont.Create;
FValueFont.Color:=clMaroon;
FDefaultValueFont:=TFont.Create;
FDefaultValueFont.Color:=clActiveCaption;
SetInitialBounds(0,0,200,130);
ControlStyle:=ControlStyle+[csAcceptsControls,csOpaque];
BorderWidth:=0;
BorderStyle := bsSingle;
// create sub components
ValueEdit:=TEdit.Create(Self);
with ValueEdit do begin
Name:='ValueEdit';
Visible:=false;
Enabled:=false;
SetBounds(0,-30,80,25); // hidden
Parent:=Self;
OnMouseDown := @ValueEditMouseDown;
OnDblClick := @ValueEditDblClick;
OnExit:=@ValueEditExit;
OnChange:=@ValueEditChange;
OnKeyDown:=@ValueEditKeyDown;
OnKeyUp:=@ValueEditKeyUp;
end;
ValueComboBox:=TComboBox.Create(Self);
with ValueComboBox do begin
Name:='ValueComboBox';
Visible:=false;
Enabled:=false;
SetBounds(0,-30,80,25); // hidden
Parent:=Self;
OnMouseDown := @ValueEditMouseDown;
OnDblClick := @ValueEditDblClick;
OnExit:=@ValueComboBoxExit;
//OnChange:=@ValueComboBoxChange; the on change event is called even,
// if the user is editing
OnKeyDown:=@ValueComboBoxKeyDown;
OnKeyUp:=@ValueComboBoxKeyUp;
OnDropDown:=@ValueComboBoxDropDown;
OnCloseUp:=@ValueComboBoxCloseUp;
OnDrawItem:=@ValueComboBoxDrawItem;
end;
ValueButton:=TButton.Create(Self);
with ValueButton do begin
Name:='ValueButton';
Visible:=false;
Enabled:=false;
OnClick:=@ValueButtonClick;
Caption := '...';
SetBounds(0,-30,25,25); // hidden
Parent:=Self;
end;
if DefItemHeight<3 then
FDefaultItemHeight:=ValueComboBox.Height-3
else
FDefaultItemHeight:=DefItemHeight;
BuildPropertyList;
FHintTimer := TTimer.Create(nil);
FHintTimer.Interval := 500;
FHintTimer.Enabled := False;
FHintTimer.OnTimer := @HintTimer;
FHintWindow := THintWindow.Create(nil);
FHIntWindow.Visible := False;
FHintWindow.Caption := 'This is a hint window'#13#10'Neat huh?';
FHintWindow.HideInterval := 4000;
FHintWindow.AutoHide := True;
Application.AddOnUserInputHandler(@OnUserInput,true);
end;
procedure TOICustomPropertyGrid.UpdateScrollBar;
var
ScrollInfo: TScrollInfo;
begin
if HandleAllocated then begin
ScrollInfo.cbSize := SizeOf(ScrollInfo);
ScrollInfo.fMask := SIF_ALL or SIF_DISABLENOSCROLL;
ScrollInfo.nMin := 0;
ScrollInfo.nTrackPos := 0;
ScrollInfo.nMax := TopMax+ClientHeight;
ScrollInfo.nPage := ClientHeight;
if ScrollInfo.nPage<1 then ScrollInfo.nPage:=1;
ScrollInfo.nPos := TopY;
ShowScrollBar(Handle, SB_VERT, True);
SetScrollInfo(Handle, SB_VERT, ScrollInfo, True);
end;
end;
procedure TOICustomPropertyGrid.CreateParams(var Params: TCreateParams);
const
ClassStylesOff = CS_VREDRAW or CS_HREDRAW;
begin
inherited CreateParams(Params);
with Params do begin
{$R-}
WindowClass.Style := WindowClass.Style and not ClassStylesOff;
Style := Style or WS_VSCROLL or WS_CLIPCHILDREN;
{$R+}
ExStyle := ExStyle or WS_EX_CLIENTEDGE;
end;
end;
procedure TOICustomPropertyGrid.WMVScroll(var Msg: TWMScroll);
begin
case Msg.ScrollCode of
// Scrolls to start / end of the text
SB_TOP: TopY := 0;
SB_BOTTOM: TopY := TopMax;
// Scrolls one line up / down
SB_LINEDOWN: TopY := TopY + DefaultItemHeight div 2;
SB_LINEUP: TopY := TopY - DefaultItemHeight div 2;
// Scrolls one page of lines up / down
SB_PAGEDOWN: TopY := TopY + ClientHeight - DefaultItemHeight;
SB_PAGEUP: TopY := TopY - ClientHeight + DefaultItemHeight;
// Scrolls to the current scroll bar position
SB_THUMBPOSITION,
SB_THUMBTRACK: TopY := Msg.Pos;
// Ends scrolling
SB_ENDSCROLL: ;
end;
end;
destructor TOICustomPropertyGrid.Destroy;
var a:integer;
begin
Application.RemoveOnUserInputHandler(@OnUserInput);
FItemIndex:=-1;
for a:=0 to FRows.Count-1 do Rows[a].Free;
FreeAndNil(FRows);
FreeAndNil(FSelection);
FreeAndNil(FValueFont);
FreeAndNil(FDefaultValueFont);
FreeAndNil(FNameFont);
FreeAndNil(FExpandedProperties);
FreeAndNil(FHintTimer);
FreeAndNil(FHintWindow);
inherited Destroy;
end;
function TOICustomPropertyGrid.ConsistencyCheck: integer;
var
i: integer;
begin
for i:=0 to FRows.Count-1 do begin
if Rows[i]=nil then begin
Result:=-1;
exit;
end;
if Rows[i].Index<>i then begin
Result:=-2;
exit;
end;
Result:=Rows[i].ConsistencyCheck;
if Result<>0 then begin
dec(Result,100);
exit;
end;
end;
Result:=0;
end;
procedure TOICustomPropertyGrid.SetSelection(
const ASelection: TPersistentSelectionList);
var
CurRow:TOIPropertyGridRow;
OldSelectedRowPath:string;
begin
OldSelectedRowPath:=PropertyPath(ItemIndex);
ItemIndex:=-1;
ClearRows;
FSelection.Assign(ASelection);
BuildPropertyList;
CurRow:=GetRowByPath(OldSelectedRowPath);
if CurRow<>nil then
ItemIndex:=CurRow.Index;
end;
procedure TOICustomPropertyGrid.SetPropertyEditorHook(
NewPropertyEditorHook:TPropertyEditorHook);
begin
if FPropertyEditorHook=NewPropertyEditorHook then exit;
FPropertyEditorHook:=NewPropertyEditorHook;
IncreaseChangeStep;
SetSelection(FSelection);
end;
function TOICustomPropertyGrid.PropertyPath(Index:integer):string;
var CurRow:TOIPropertyGridRow;
begin
if (Index>=0) and (Index<FRows.Count) then begin
CurRow:=Rows[Index];
Result:=CurRow.Name;
CurRow:=CurRow.Parent;
while CurRow<>nil do begin
Result:=CurRow.Name+'.'+Result;
CurRow:=CurRow.Parent;
end;
end else Result:='';
end;
function TOICustomPropertyGrid.GetRowByPath(
const PropPath: string): TOIPropertyGridRow;
// searches PropPath. Expands automatically parent rows
var CurName:string;
s,e:integer;
CurParentRow:TOIPropertyGridRow;
begin
Result:=nil;
if FRows.Count=0 then exit;
CurParentRow:=nil;
s:=1;
while (s<=length(PropPath)) do begin
e:=s;
while (e<=length(PropPath)) and (PropPath[e]<>'.') do inc(e);
CurName:=uppercase(copy(PropPath,s,e-s));
s:=e+1;
// search name in childs
if CurParentRow=nil then
Result:=Rows[0]
else
Result:=CurParentRow.FirstChild;
while (Result<>nil) and (uppercase(Result.Name)<>CurName) do
Result:=Result.NextBrother;
if Result=nil then begin
exit;
end else begin
// expand row
CurParentRow:=Result;
ExpandRow(CurParentRow.Index);
end;
end;
if s<=length(PropPath) then Result:=nil;
end;
procedure TOICustomPropertyGrid.SetRowValue;
var
CurRow: TOIPropertyGridRow;
NewValue: string;
OldExpanded: boolean;
OldChangeStep: integer;
begin
if not CanEditRowValue then exit;
OldChangeStep:=fChangeStep;
CurRow:=Rows[FItemIndex];
if FCurrentEdit=ValueEdit then
NewValue:=ValueEdit.Text
else
NewValue:=ValueComboBox.Text;
if length(NewValue)>CurRow.Editor.GetEditLimit then
NewValue:=LeftStr(NewValue,CurRow.Editor.GetEditLimit);
if NewValue<>CurRow.Editor.GetVisualValue then begin
Include(FStates,pgsApplyingValue);
try
{$IFNDEF DoNotCatchOIExceptions}
try
{$ENDIF}
//writeln('TOICustomPropertyGrid.SetRowValue B ClassName=',CurRow.Editor.ClassName,' Visual=',CurRow.Editor.GetVisualValue,' NewValue=',NewValue,' AllEqual=',CurRow.Editor.AllEqual);
CurRow.Editor.SetValue(NewValue);
//writeln('TOICustomPropertyGrid.SetRowValue C ClassName=',CurRow.Editor.ClassName,' Visual=',CurRow.Editor.GetVisualValue,' NewValue=',NewValue,' AllEqual=',CurRow.Editor.AllEqual);
{$IFNDEF DoNotCatchOIExceptions}
except
on E: Exception do begin
MessageDlg(oisError, E.Message, mtError, [mbOk], 0);
end;
end;
{$ENDIF}
if (OldChangeStep<>FChangeStep) then begin
// the selection has changed
// => CurRow does not exist any more
exit;
end;
// set value in edit control
if FCurrentEdit=ValueEdit then
ValueEdit.Text:=CurRow.Editor.GetVisualValue
else
ValueComboBox.Text:=CurRow.Editor.GetVisualValue;
// update volatile sub properties
if (paVolatileSubProperties in CurRow.Editor.GetAttributes)
and ((CurRow.Expanded) or (CurRow.ChildCount>0)) then begin
OldExpanded:=CurRow.Expanded;
ShrinkRow(FItemIndex);
if OldExpanded then
ExpandRow(FItemIndex);
end;
//writeln('TOICustomPropertyGrid.SetRowValue D ClassName=',CurRow.Editor.ClassName,' Visual=',CurRow.Editor.GetVisualValue,' NewValue=',NewValue,' AllEqual=',CurRow.Editor.AllEqual);
finally
Exclude(FStates,pgsApplyingValue);
end;
DoPaint(true);
if Assigned(FOnModified) then FOnModified(Self);
end;
end;
procedure TOICustomPropertyGrid.DoCallEdit;
var
CurRow:TOIPropertyGridRow;
OldChangeStep: integer;
begin
//writeln('#################### TOICustomPropertyGrid.DoCallEdit ...');
if (FStates*[pgsChangingItemIndex,pgsApplyingValue]<>[])
or (FCurrentEdit=nil)
or (FItemIndex<0)
or (FItemIndex>=FRows.Count)
or ((FCurrentEditorLookupRoot<>nil)
and (FPropertyEditorHook<>nil)
and (FPropertyEditorHook.LookupRoot<>FCurrentEditorLookupRoot))
then begin
exit;
end;
OldChangeStep:=fChangeStep;
CurRow:=Rows[FItemIndex];
if paDialog in CurRow.Editor.GetAttributes then begin
{$IFNDEF DoNotCatchOIExceptions}
try
{$ENDIF}
DebugLn('#################### TOICustomPropertyGrid.DoCallEdit for ',CurRow.Editor.ClassName);
CurRow.Editor.Edit;
{$IFNDEF DoNotCatchOIExceptions}
except
on E: Exception do begin
MessageDlg(oisError, E.Message, mtError, [mbOk], 0);
end;
end;
{$ENDIF}
if (OldChangeStep<>FChangeStep) then begin
// the selection has changed
// => CurRow does not exist any more
exit;
end;
if FCurrentEdit=ValueEdit then
ValueEdit.Text:=CurRow.Editor.GetVisualValue
else
ValueComboBox.Text:=CurRow.Editor.GetVisualValue;
end;
end;
procedure TOICustomPropertyGrid.RefreshValueEdit;
var
CurRow: TOIPropertyGridRow;
NewValue, OldValue: string;
begin
if (FStates*[pgsChangingItemIndex,pgsApplyingValue]=[])
and (FCurrentEdit<>nil)
and (FItemIndex>=0) and (FItemIndex<FRows.Count) then begin
CurRow:=Rows[FItemIndex];
if FCurrentEdit=ValueEdit then
OldValue:=ValueEdit.Text
else
OldValue:=ValueComboBox.Text;
NewValue:=CurRow.Editor.GetVisualValue;
if OldValue<>NewValue then begin
if FCurrentEdit=ValueEdit then
ValueEdit.Text:=NewValue
else
ValueComboBox.Text:=NewValue;
end;
end;
end;
procedure TOICustomPropertyGrid.ValueEditKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
HandleStandardKeys(Key,Shift);
end;
procedure TOICustomPropertyGrid.ValueEditKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
HandleKeyUp(Key,Shift);
end;
procedure TOICustomPropertyGrid.ValueEditExit(Sender: TObject);
begin
SetRowValue;
end;
procedure TOICustomPropertyGrid.ValueEditChange(Sender: TObject);
var CurRow:TOIPropertyGridRow;
begin
if (FCurrentEdit<>nil) and (FItemIndex>=0) and (FItemIndex<FRows.Count) then
begin
CurRow:=Rows[FItemIndex];
if paAutoUpdate in CurRow.Editor.GetAttributes then
SetRowValue;
end;
end;
procedure TOICustomPropertyGrid.ValueComboBoxExit(Sender: TObject);
begin
SetRowValue;
end;
procedure TOICustomPropertyGrid.ValueComboBoxChange(Sender: TObject);
var i:integer;
begin
i:=TComboBox(Sender).Items.IndexOf(TComboBox(Sender).Text);
if i>=0 then SetRowValue;
end;
procedure TOICustomPropertyGrid.ValueComboBoxKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
HandleStandardKeys(Key,Shift);
end;
procedure TOICustomPropertyGrid.ValueComboBoxKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
HandleKeyUp(Key,Shift);
end;
procedure TOICustomPropertyGrid.ValueButtonClick(Sender: TObject);
begin
DoCallEdit;
end;
procedure TOICustomPropertyGrid.SetItemIndex(NewIndex:integer);
var NewRow:TOIPropertyGridRow;
NewValue:string;
begin
if (FStates*[pgsChangingItemIndex,pgsApplyingValue]<>[])
or (FItemIndex=NewIndex) then
exit;
// save old edit value
SetRowValue;
Include(FStates,pgsChangingItemIndex);
if (FItemIndex>=0) and (FItemIndex<FRows.Count) then
Rows[FItemIndex].Editor.Deactivate;
SetCaptureControl(nil);
FItemIndex:=NewIndex;
if FCurrentEdit<>nil then begin
FCurrentEdit.Visible:=false;
FCurrentEdit.Enabled:=false;
FCurrentEdit:=nil;
end;
if FCurrentButton<>nil then begin
FCurrentButton.Visible:=false;
FCurrentButton.Enabled:=false;
FCurrentButton:=nil;
end;
FCurrentEditorLookupRoot:=nil;
if (NewIndex>=0) and (NewIndex<FRows.Count) then begin
NewRow:=Rows[NewIndex];
if NewRow.Bottom>=TopY+(ClientHeight-2*BorderWidth) then
TopY:=NewRow.Bottom-(ClientHeight-2*BorderWidth)+1
else if NewRow.Top<TopY then
TopY:=NewRow.Top;
NewRow.Editor.Activate;
if paDialog in NewRow.Editor.GetAttributes then begin
FCurrentButton:=ValueButton;
FCurrentButton.Visible:=true;
end;
NewValue:=NewRow.Editor.GetVisualValue;
if paValueList in NewRow.Editor.GetAttributes then begin
FCurrentEdit:=ValueComboBox;
ValueComboBox.MaxLength:=NewRow.Editor.GetEditLimit;
ValueComboBox.Items.BeginUpdate;
ValueComboBox.Items.Text:='';
ValueComboBox.Items.Clear;
ValueComboBox.Sorted:=paSortList in NewRow.Editor.GetAttributes;
ValueComboBox.Enabled:=not NewRow.IsReadOnly;
NewRow.Editor.GetValues(@AddStringToComboBox);
ValueComboBox.Text:=NewValue;
ValueComboBox.Items.EndUpdate;
end else begin
FCurrentEdit:=ValueEdit;
ValueEdit.ReadOnly:=NewRow.IsReadOnly;
ValueEdit.Enabled:=true;
ValueEdit.MaxLength:=NewRow.Editor.GetEditLimit;
ValueEdit.Text:=NewValue;
end;
AlignEditComponents;
if FCurrentEdit<>nil then begin
if FPropertyEditorHook<>nil then
FCurrentEditorLookupRoot:=FPropertyEditorHook.LookupRoot;
FCurrentEdit.Visible:=true;
if (FDragging=false) and (FCurrentEdit.Showing)
and FCurrentEdit.Enabled
and (not NewRow.IsReadOnly) then begin
FCurrentEdit.SetFocus;
end;
end;
if FCurrentButton<>nil then
FCurrentButton.Enabled:=not NewRow.IsDisabled;
end;
Exclude(FStates,pgsChangingItemIndex);
end;
function TOICustomPropertyGrid.GetRowCount:integer;
begin
Result:=FRows.Count;
end;
procedure TOICustomPropertyGrid.BuildPropertyList;
var a:integer;
CurRow:TOIPropertyGridRow;
OldSelectedRowPath:string;
begin
OldSelectedRowPath:=PropertyPath(ItemIndex);
// unselect
ItemIndex:=-1;
// clear
for a:=0 to FRows.Count-1 do Rows[a].Free;
FRows.Clear;
// get properties
GetPersistentProperties(FSelection, FFilter, FPropertyEditorHook,
@AddPropertyEditor,nil);
// sort
FRows.Sort(@SortGridRows);
for a:=0 to FRows.Count-1 do begin
if a>0 then
Rows[a].FPriorBrother:=Rows[a-1]
else
Rows[a].FPriorBrother:=nil;
if a<FRows.Count-1 then
Rows[a].FNextBrother:=Rows[a+1]
else
Rows[a].FNextBrother:=nil;
end;
// set indices and tops
SetItemsTops;
// restore expands
for a:=FExpandedProperties.Count-1 downto 0 do begin
CurRow:=GetRowByPath(FExpandedProperties[a]);
if CurRow<>nil then
ExpandRow(CurRow.Index);
end;
// reselect
CurRow:=GetRowByPath(OldSelectedRowPath);
if CurRow<>nil then begin
ItemIndex:=CurRow.Index;
end;
// update scrollbar
FTopY:=0;
UpdateScrollBar;
// paint
Invalidate;
end;
procedure TOICustomPropertyGrid.AddPropertyEditor(PropEditor: TPropertyEditor);
var NewRow:TOIPropertyGridRow;
begin
NewRow:=TOIPropertyGridRow.Create(Self,PropEditor,nil);
FRows.Add(NewRow);
if FRows.Count>1 then begin
NewRow.FPriorBrother:=Rows[FRows.Count-2];
NewRow.FPriorBrother.FNextBrother:=NewRow;
end;
end;
procedure TOICustomPropertyGrid.AddStringToComboBox(const s:string);
var NewIndex:integer;
begin
NewIndex:=ValueComboBox.Items.Add(s);
if ValueComboBox.Text=s then
ValueComboBox.ItemIndex:=NewIndex;
end;
procedure TOICustomPropertyGrid.ExpandRow(Index:integer);
var a:integer;
CurPath:string;
AlreadyInExpandList:boolean;
begin
FExpandingRow:=Rows[Index];
if (FExpandingRow.Expanded)
or (not (paSubProperties in FExpandingRow.Editor.GetAttributes))
then begin
FExpandingRow:=nil;
exit;
end;
FExpandingRow.Editor.GetProperties(@AddSubEditor);
SetItemsTops;
FExpandingRow.FExpanded:=true;
a:=0;
CurPath:=uppercase(PropertyPath(FExpandingRow.Index));
AlreadyInExpandList:=false;
while a<FExpandedProperties.Count do begin
if FExpandedProperties[a]=copy(CurPath,1,length(FExpandedProperties[a]))
then begin
if length(FExpandedProperties[a])=length(CurPath) then begin
AlreadyInExpandList:=true;
inc(a);
end else begin
FExpandedProperties.Delete(a);
end;
end else begin
inc(a);
end;
end;
if not AlreadyInExpandList then
FExpandedProperties.Add(CurPath);
FExpandingRow:=nil;
UpdateScrollBar;
Invalidate;
end;
procedure TOICustomPropertyGrid.ShrinkRow(Index:integer);
var CurRow, ARow:TOIPropertyGridRow;
StartIndex,EndIndex,a:integer;
CurPath:string;
begin
CurRow:=Rows[Index];
if (not CurRow.Expanded) then exit;
// calculate all childs (between StartIndex..EndIndex)
StartIndex:=CurRow.Index+1;
EndIndex:=FRows.Count-1;
ARow:=CurRow;
while ARow<>nil do begin
if ARow.NextBrother<>nil then begin
EndIndex:=ARow.NextBrother.Index-1;
break;
end;
ARow:=ARow.Parent;
end;
if (FItemIndex>=StartIndex) and (FItemIndex<=EndIndex) then
ItemIndex:=0;
for a:=EndIndex downto StartIndex do begin
Rows[a].Free;
FRows.Delete(a);
end;
SetItemsTops;
CurRow.FExpanded:=false;
CurPath:=uppercase(PropertyPath(CurRow.Index));
a:=0;
while a<FExpandedProperties.Count do begin
if copy(FExpandedProperties[a],1,length(CurPath))=CurPath then
FExpandedProperties.Delete(a)
else
inc(a);
end;
if CurRow.Parent<>nil then
FExpandedProperties.Add(PropertyPath(CurRow.Parent.Index));
UpdateScrollBar;
Invalidate;
end;
procedure TOICustomPropertyGrid.AddSubEditor(PropEditor:TPropertyEditor);
var NewRow:TOIPropertyGridRow;
NewIndex:integer;
begin
NewRow:=TOIPropertyGridRow.Create(Self,PropEditor,FExpandingRow);
NewIndex:=FExpandingRow.Index+1+FExpandingRow.ChildCount;
FRows.Insert(NewIndex,NewRow);
if FExpandingRow.FFirstChild=nil then
FExpandingRow.FFirstChild:=NewRow;
NewRow.FPriorBrother:=FExpandingRow.FLastChild;
FExpandingRow.FLastChild:=NewRow;
if NewRow.FPriorBrother<>nil then
NewRow.FPriorBrother.FNextBrother:=NewRow;
inc(FExpandingRow.FChildCount);
end;
function TOICustomPropertyGrid.MouseToIndex(y:integer;MustExist:boolean):integer;
var l,r,m:integer;
begin
l:=0;
r:=FRows.Count-1;
inc(y,FTopY);
while (l<=r) do begin
m:=(l+r) shr 1;
if Rows[m].Top>y then begin
r:=m-1;
end else if Rows[m].Bottom<=y then begin
l:=m+1;
end else begin
Result:=m; exit;
end;
end;
if (MustExist=false) and (FRows.Count>0) then begin
if y<0 then Result:=0
else Result:=FRows.Count-1;
end else Result:=-1;
end;
function TOICustomPropertyGrid.GetActiveRow: TOIPropertyGridRow;
begin
Result:=nil;
if ItemIndex<0 then exit;
Result:=Rows[ItemIndex];
end;
procedure TOICustomPropertyGrid.SetCurrentRowValue(const NewValue: string);
begin
if not CanEditRowValue then exit;
if FCurrentEdit is TComboBox then
TComboBox(FCurrentEdit).Text:=NewValue
else if FCurrentEdit is TEdit then
TEdit(FCurrentEdit).Text:=NewValue;
SetRowValue;
end;
function TOICustomPropertyGrid.CanEditRowValue: boolean;
begin
if (FStates*[pgsChangingItemIndex,pgsApplyingValue]<>[])
or (FCurrentEdit=nil)
or (FItemIndex<0)
or (FItemIndex>=FRows.Count)
or ((FCurrentEditorLookupRoot<>nil)
and (FPropertyEditorHook<>nil)
and (FPropertyEditorHook.LookupRoot<>FCurrentEditorLookupRoot))
then begin
Result:=false;
end else begin
Result:=true;
end;
end;
function TOICustomPropertyGrid.GetHintTypeAt(RowIndex: integer; X: integer
): TPropEditHint;
var
IconX: integer;
begin
Result:=pehNone;
if (RowIndex<0) or (RowIndex>=RowCount) then exit;
if SplitterX>=X then begin
if (FCurrentButton<>nil)
and (FCurrentButton.Left<=X) then
Result:=pehEditButton
else
Result:=pehValue;
end else begin
IconX:=GetTreeIconX(RowIndex);
if IconX+Indent>X then
Result:=pehTree
else
Result:=pehName;
end;
end;
procedure TOICustomPropertyGrid.MouseDown(Button:TMouseButton; Shift:TShiftState;
X,Y:integer);
begin
//ShowMessageDialog('X'+IntToStr(X)+',Y'+IntToStr(Y));
inherited MouseDown(Button,Shift,X,Y);
//hide the hint
FHintWindow.Visible := False;
if Button=mbLeft then begin
if Cursor=crHSplit then begin
FDragging:=true;
end;
end;
end;
procedure TOICustomPropertyGrid.MouseMove(Shift:TShiftState; X,Y:integer);
var
SplitDistance:integer;
Index: Integer;
fPropRow: TOIPropertyGridRow;
fHint: String;
fpoint: TPoint;
fHintRect: TRect;
begin
inherited MouseMove(Shift,X,Y);
SplitDistance:=X-SplitterX;
if FDragging then begin
if ssLeft in Shift then begin
SplitterX:=SplitterX+SplitDistance;
end else begin
EndDragSplitter;
end;
end else begin
if (abs(SplitDistance)<=2) then begin
Cursor:=crHSplit;
end else begin
Cursor:=crDefault;
end;
// to check if the property text fits in its box, if not show a hint
if ShowHint then begin
Index := MouseToIndex(y,false);
if Index > -1 then begin
fPropRow := GetRow(Index);
if X < SplitterX then begin
// Mouse is over property name...
fHint := fPropRow.Name;
if (Canvas.TextWidth(fHint)+BorderWidth+GetTreeIconX(Index)+Indent)
>= SplitterX
then begin
fHintRect := FHintWindow.CalcHintRect(0,fHint,nil);
fpoint := ClientToScreen(
Point(BorderWidth+GetTreeIconX(Index)+Indent,
fPropRow.Top - TopY-1));
MoveRect(fHintRect,fPoint.x,fPoint.y);
FHintWindow.ActivateHint(fHintRect,fHint);
end;
end
else begin
// Mouse is over property value...
fHint := fPropRow.LastPaintedValue;
if length(fHint)>100 then fHint:=copy(fHint,1,100)+'...';
if Canvas.TextWidth(fHint) > (ClientWidth - BorderWidth - SplitterX)
then begin
fHintRect := FHintWindow.CalcHintRect(0,fHint,nil);
fpoint := ClientToScreen(Point(SplitterX,fPropRow.Top - TopY-1));
MoveRect(fHintRect,fPoint.x,fPoint.y);
FHintWindow.ActivateHint(fHintRect,fHint);
end;
end;
end;
end;
end;
end;
procedure TOICustomPropertyGrid.MouseUp(Button:TMouseButton; Shift:TShiftState;
X,Y:integer);
var
IconX,Index:integer;
PointedRow:TOIpropertyGridRow;
WasDragging: boolean;
begin
WasDragging:=FDragging;
if FDragging then EndDragSplitter;
inherited MouseUp(Button,Shift,X,Y);
if Button=mbLeft then begin
if not WasDragging then begin
Index:=MouseToIndex(Y,false);
if (Index>=0) and (Index<FRows.Count) then begin
IconX:=GetTreeIconX(Index);
if (X>=IconX) and (X<=IconX+FIndent) then begin
PointedRow:=Rows[Index];
if paSubProperties in PointedRow.Editor.GetAttributes then begin
if PointedRow.Expanded then
ShrinkRow(Index)
else
ExpandRow(Index);
ItemIndex:=Index;
end;
end else begin
ItemIndex:=Index;
end;
end;
end;
end;
end;
procedure TOICustomPropertyGrid.KeyDown(var Key: Word; Shift: TShiftState);
begin
HandleStandardKeys(Key,Shift);
inherited KeyDown(Key, Shift);
end;
procedure TOICustomPropertyGrid.HandleStandardKeys(var Key: Word; Shift: TShiftState
);
var
Handled: Boolean;
begin
Handled:=true;
case Key of
VK_UP:
if (ItemIndex>0) then ItemIndex:=ItemIndex-1;
VK_Down:
if (ItemIndex<FRows.Count-1) then ItemIndex:=ItemIndex+1;
VK_TAB:
// ToDo: implement completion
if (ItemIndex<FRows.Count-1) then ItemIndex:=ItemIndex+1;
VK_LEFT:
if (FCurrentEdit=nil)
and (ItemIndex>=0) and (Rows[ItemIndex].Expanded) then
ShrinkRow(ItemIndex)
else
Handled:=false;
VK_RIGHT:
if (FCurrentEdit=nil)
and (ItemIndex>=0) and (not Rows[ItemIndex].Expanded)
and (paSubProperties in Rows[ItemIndex].Editor.GetAttributes) then
ExpandRow(ItemIndex)
else
Handled:=false;
VK_RETURN:
SetRowValue;
else
Handled:=false;
end;
if Handled then Key:=VK_UNKNOWN;
end;
procedure TOICustomPropertyGrid.HandleKeyUp(var Key: Word; Shift: TShiftState);
begin
if (Key<>VK_UNKNOWN) and Assigned(OnKeyUp) then OnKeyUp(Self,Key,Shift);
end;
procedure TOICustomPropertyGrid.EraseBackground(DC: HDC);
begin
// everything is painted, so erasing the background is not needed
end;
procedure TOICustomPropertyGrid.DoSetBounds(ALeft, ATop, AWidth, AHeight: integer);
begin
inherited DoSetBounds(ALeft, ATop, AWidth, AHeight);
UpdateScrollBar;
end;
constructor TOICustomPropertyGrid.Create(TheOwner: TComponent);
begin
CreateWithParams(TheOwner,nil,AllTypeKinds,25);
end;
procedure TOICustomPropertyGrid.OnUserInput(Sender: TObject; Msg: Cardinal);
begin
ResetHintTimer;
end;
procedure TOICustomPropertyGrid.EndDragSplitter;
begin
if FDragging then begin
Cursor:=crDefault;
FDragging:=false;
FPreferredSplitterX:=FSplitterX;
if FCurrentEdit<>nil then begin
SetCaptureControl(nil);
FCurrentEdit.SetFocus;
end;
end;
end;
procedure TOICustomPropertyGrid.SetSplitterX(const NewValue:integer);
var AdjustedValue:integer;
begin
AdjustedValue:=NewValue;
if AdjustedValue>ClientWidth then AdjustedValue:=ClientWidth;
if AdjustedValue<1 then AdjustedValue:=1;
if FSplitterX<>AdjustedValue then begin
FSplitterX:=AdjustedValue;
AlignEditComponents;
Invalidate;
end;
end;
procedure TOICustomPropertyGrid.SetTopY(const NewValue:integer);
begin
if FTopY<>NewValue then begin
FTopY:=NewValue;
if FTopY<0 then FTopY:=0;
UpdateScrollBar;
ItemIndex:=-1;
Invalidate;
end;
end;
procedure TOICustomPropertyGrid.SetBounds(aLeft,aTop,aWidth,aHeight:integer);
begin
//writeln('[TOICustomPropertyGrid.SetBounds] ',Name,' ',aLeft,',',aTop,',',aWidth,',',aHeight,' Visible=',Visible);
inherited SetBounds(aLeft,aTop,aWidth,aHeight);
if Visible then begin
if not FDragging then begin
if (SplitterX<5) and (aWidth>20) then
SplitterX:=100
else
SplitterX:=FPreferredSplitterX;
end;
AlignEditComponents;
end;
end;
function TOICustomPropertyGrid.GetTreeIconX(Index:integer):integer;
begin
Result:=Rows[Index].Lvl*Indent+2;
end;
function TOICustomPropertyGrid.TopMax:integer;
begin
Result:=GridHeight-ClientHeight+2*BorderWidth;
if Result<0 then Result:=0;
end;
function TOICustomPropertyGrid.GridHeight:integer;
begin
if FRows.Count>0 then
Result:=Rows[FRows.Count-1].Bottom
else
Result:=0;
end;
procedure TOICustomPropertyGrid.AlignEditComponents;
var RRect,EditCompRect,EditBtnRect:TRect;
function CompareRectangles(r1,r2:TRect):boolean;
begin
Result:=(r1.Left=r2.Left) and (r1.Top=r2.Top) and (r1.Right=r2.Right)
and (r1.Bottom=r2.Bottom);
end;
// AlignEditComponents
begin
if ItemIndex>=0 then begin
RRect:=RowRect(ItemIndex);
EditCompRect:=RRect;
EditCompRect.Top:=EditCompRect.Top-1;
EditCompRect.Left:=RRect.Left+SplitterX;
if FCurrentButton<>nil then begin
// edit dialog button
with EditBtnRect do begin
Top:=RRect.Top;
Left:=RRect.Right-20;
Bottom:=RRect.Bottom;
Right:=RRect.Right;
EditCompRect.Right:=Left;
end;
if not CompareRectangles(FCurrentButton.BoundsRect,EditBtnRect) then begin
FCurrentButton.BoundsRect:=EditBtnRect;
//FCurrentButton.Invalidate;
end;
end;
if FCurrentEdit<>nil then begin
// resize the edit component
EditCompRect.Left:=EditCompRect.Left-1;
if not CompareRectangles(FCurrentEdit.BoundsRect,EditCompRect) then begin
FCurrentEdit.BoundsRect:=EditCompRect;
FCurrentEdit.Invalidate;
end;
end;
end;
end;
procedure TOICustomPropertyGrid.PaintRow(ARow:integer);
var ARowRect,NameRect,NameIconRect,NameTextRect,ValueRect:TRect;
IconX,IconY:integer;
CurRow:TOIPropertyGridRow;
DrawState:TPropEditDrawState;
OldFont:TFont;
procedure DrawTreeIcon(x,y:integer;Plus:boolean);
begin
with Canvas do begin
Brush.Color:=clWhite;
Pen.Color:=clBlack;
Rectangle(x,y,x+8,y+8);
MoveTo(x+2,y+4);
LineTo(x+7,y+4);
if Plus then begin
MoveTo(x+4,y+2);
LineTo(x+4,y+7);
end;
end;
end;
// PaintRow
begin
CurRow:=Rows[ARow];
ARowRect:=RowRect(ARow);
NameRect:=ARowRect;
ValueRect:=ARowRect;
NameRect.Right:=SplitterX;
ValueRect.Left:=SplitterX;
IconX:=GetTreeIconX(ARow);
IconY:=((NameRect.Bottom-NameRect.Top-9) div 2)+NameRect.Top;
NameIconRect:=NameRect;
NameIconRect.Right:=IconX+Indent;
NameTextRect:=NameRect;
NameTextRect.Left:=NameIconRect.Right;
DrawState:=[];
if ARow=FItemIndex then Include(DrawState,pedsSelected);
with Canvas do begin
// draw name background
if FBackgroundColor<>clNone then begin
Brush.Color:=FBackgroundColor;
FillRect(NameIconRect);
FillRect(NameTextRect);
end;
// draw icon
if paSubProperties in CurRow.Editor.GetAttributes then begin
DrawTreeIcon(IconX,IconY,not CurRow.Expanded);
end;
// draw name
OldFont:=Font;
Font:=FNameFont;
CurRow.Editor.PropDrawName(Canvas,NameTextRect,DrawState);
Font:=OldFont;
// draw frame
Pen.Color:=cl3DDkShadow;
MoveTo(NameRect.Left,NameRect.Bottom-1);
LineTo(NameRect.Right-1,NameRect.Bottom-1);
LineTo(NameRect.Right-1,NameRect.Top-1);
if ARow=FItemIndex then begin
Pen.Color:=cl3DDkShadow;
MoveTo(NameRect.Left,NameRect.Bottom-1);
LineTo(NameRect.Left,NameRect.Top);
LineTo(NameRect.Right-1,NameRect.Top);
Pen.Color:=cl3DLight;
MoveTo(NameRect.Left+1,NameRect.Bottom-2);
LineTo(NameRect.Right-1,NameRect.Bottom-2);
end;
// draw value background
if FBackgroundColor<>clNone then begin
Brush.Color:=FBackgroundColor;
FillRect(ValueRect);
end;
// draw value
if ARow<>ItemIndex then begin
OldFont:=Font;
if CurRow.Editor.IsNotDefaultValue then
Font:=FValueFont
else
Font:=FDefaultValueFont;
CurRow.Editor.PropDrawValue(Canvas,ValueRect,DrawState);
Font:=OldFont;
end;
CurRow.LastPaintedValue:=CurRow.Editor.GetVisualValue;
// draw frame
Pen.Color:=cl3DDkShadow;
MoveTo(ValueRect.Left-1,ValueRect.Bottom-1);
LineTo(ValueRect.Right,ValueRect.Bottom-1);
Pen.Color:=cl3DLight;
MoveTo(ValueRect.Left,ValueRect.Bottom-1);
LineTo(ValueRect.Left,ValueRect.Top);
if ARow=FItemIndex then begin
MoveTo(ValueRect.Left,ValueRect.Bottom-2);
LineTo(ValueRect.Right,ValueRect.Bottom-2);
end;
end;
end;
procedure TOICustomPropertyGrid.DoPaint(PaintOnlyChangedValues:boolean);
var a:integer;
SpaceRect:TRect;
begin
if not PaintOnlyChangedValues then begin
with Canvas do begin
// draw properties
for a:=0 to FRows.Count-1 do begin
PaintRow(a);
end;
// draw unused space below rows
SpaceRect:=Rect(BorderWidth,BorderWidth,
ClientWidth-BorderWidth+1,ClientHeight-BorderWidth+1);
if FRows.Count>0 then
SpaceRect.Top:=Rows[FRows.Count-1].Bottom-FTopY+BorderWidth;
// TWinControl(Parent).InvalidateRect(Self,SpaceRect,true);
if FBackgroundColor<>clNone then begin
Brush.Color:=FBackgroundColor;
FillRect(SpaceRect);
end;
// don't draw border: borderstyle=bsSingle
end;
end else begin
for a:=0 to FRows.Count-1 do begin
if Rows[a].Editor.GetVisualValue<>Rows[a].LastPaintedValue then
PaintRow(a);
end;
end;
end;
procedure TOICustomPropertyGrid.Paint;
begin
inherited Paint;
DoPaint(false);
end;
procedure TOICustomPropertyGrid.RefreshPropertyValues;
begin
RefreshValueEdit;
DoPaint(true);
end;
procedure TOICustomPropertyGrid.PropEditLookupRootChange;
begin
// When the LookupRoot changes, no changes can be stored
// -> undo the value editor changes
RefreshValueEdit;
end;
function TOICustomPropertyGrid.RowRect(ARow:integer):TRect;
begin
Result.Left:=BorderWidth;
Result.Top:=Rows[ARow].Top-FTopY+BorderWidth;
Result.Right:=ClientWidth-ScrollBarWidth;
Result.Bottom:=Rows[ARow].Bottom-FTopY+BorderWidth;
end;
procedure TOICustomPropertyGrid.SetItemsTops;
// compute row tops from row heights
// set indices of all rows
var a,scrollmax:integer;
begin
for a:=0 to FRows.Count-1 do begin
Rows[a].Index:=a;
Rows[a].MeasureHeight(Canvas);
end;
if FRows.Count>0 then
Rows[0].Top:=0;
for a:=1 to FRows.Count-1 do
Rows[a].FTop:=Rows[a-1].Bottom;
if FRows.Count>0 then
scrollmax:=Rows[FRows.Count-1].Bottom-Height
else
scrollmax:=0;
// always show something
if scrollmax<10 then scrollmax:=10;
end;
procedure TOICustomPropertyGrid.ClearRows;
var a:integer;
begin
IncreaseChangeStep;
for a:=0 to FRows.Count-1 do begin
Rows[a].Free;
end;
FRows.Clear;
end;
function TOICustomPropertyGrid.GetCurrentEditValue: string;
begin
if FCurrentEdit=ValueEdit then
Result:=ValueEdit.Text
else if FCurrentEdit=ValueComboBox then
Result:=ValueComboBox.Text
else
Result:='';
end;
procedure TOICustomPropertyGrid.SetCurrentEditValue(const AValue: string);
begin
if FCurrentEdit=ValueEdit then
ValueEdit.Text:=AValue
else if FCurrentEdit=ValueComboBox then
ValueComboBox.Text:=AValue;
end;
procedure TOICustomPropertyGrid.Clear;
begin
ClearRows;
end;
function TOICustomPropertyGrid.GetRow(Index:integer):TOIPropertyGridRow;
begin
Result:=TOIPropertyGridRow(FRows[Index]);
end;
procedure TOICustomPropertyGrid.ValueComboBoxCloseUp(Sender: TObject);
begin
SetRowValue;
end;
procedure TOICustomPropertyGrid.ValueComboBoxDropDown(Sender: TObject);
var
CurRow: TOIPropertyGridRow;
MaxItemWidth, CurItemWidth, i, Cnt: integer;
ItemValue: string;
begin
if (FItemIndex>=0) and (FItemIndex<FRows.Count) then begin
CurRow:=Rows[FItemIndex];
MaxItemWidth:=ValueComboBox.Width;
Cnt:=ValueComboBox.Items.Count;
for i:=0 to Cnt-1 do begin
ItemValue:=ValueComboBox.Items[i];
CurItemWidth:=ValueComboBox.Canvas.TextWidth(ItemValue);
CurRow.Editor.ListMeasureWidth(ItemValue,i,ValueComboBox.Canvas,
CurItemWidth);
if MaxItemWidth<CurItemWidth then
MaxItemWidth:=CurItemWidth;
end;
ValueComboBox.ItemWidth:=MaxItemWidth;
end;
end;
procedure TOICustomPropertyGrid.ValueComboBoxDrawItem(Control: TWinControl;
Index: Integer; ARect: TRect; State: TOwnerDrawState);
var
CurRow: TOIPropertyGridRow;
ItemValue: string;
AState: TPropEditDrawState;
begin
if (FItemIndex>=0) and (FItemIndex<FRows.Count) then begin
CurRow:=Rows[FItemIndex];
if (Index>=0) and (Index<ValueComboBox.Items.Count) then
ItemValue:=ValueComboBox.Items[Index]
else
ItemValue:='';
AState:=[];
if odPainted in State then Include(AState,pedsPainted);
if odSelected in State then Include(AState,pedsSelected);
if odFocused in State then Include(AState,pedsFocused);
if odComboBoxEdit in State then
Include(AState,pedsInEdit)
else
Include(AState,pedsInComboList);
// clear background
with ValueComboBox.Canvas do begin
Brush.Color:=clWhite;
Pen.Color:=clBlack;
Font.Color:=Pen.Color;
FillRect(ARect);
end;
CurRow.Editor.ListDrawValue(ItemValue,Index,ValueComboBox.Canvas,ARect,
AState);
end;
end;
Procedure TOICustomPropertyGrid.HintTimer(sender : TObject);
var
Rect : TRect;
AHint : String;
Position : TPoint;
Index: integer;
PointedRow:TOIpropertyGridRow;
Window: TWinControl;
HintType: TPropEditHint;
begin
// ToDo: use LCL hintsystem
FHintTimer.Enabled := False;
if not ShowHint then exit;
Position := Mouse.CursorPos;
Position := ScreenToClient(Position);
if ((Position.X <=0) or (Position.X >= Width) or (Position.Y <= 0)
or (Position.Y >= Height)) then
Exit;
Window := FindLCLWindow(Position);
if not(Assigned(Window)) then Exit;
If (Window<>Self) and (not IsParentOf(Window)) then exit;
AHint := '';
Index:=MouseToIndex(Position.Y,false);
if (Index>=0) and (Index<FRows.Count) then
begin
//IconX:=GetTreeIconX(Index);
PointedRow:=Rows[Index];
if Assigned(PointedRow) then
Begin
if Assigned(PointedRow.Editor) then begin
HintType := GetHintTypeAt(Index,Position.X);
AHint := PointedRow.Editor.GetHint(HintType,Position.X,Position.Y);
end;
end;
end;
if AHint = '' then Exit;
Rect := FHintWindow.CalcHintRect(0,AHint,nil); //no maxwidth
Position := Mouse.CursorPos;
Rect.Left := Position.X+10;
Rect.Top := Position.Y+10;
Rect.Right := Rect.Left + Rect.Right+3;
Rect.Bottom := Rect.Top + Rect.Bottom+3;
FHintWindow.ActivateHint(Rect,AHint);
end;
Procedure TOICustomPropertyGrid.ResetHintTimer;
begin
if FHintWIndow.Visible then
FHintWindow.Visible := False;
FHintTimer.Enabled := False;
if RowCount > 0 then
FHintTimer.Enabled := not FDragging;
end;
procedure TOICustomPropertyGrid.ValueEditMouseDown(Sender : TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: integer);
begin
//hide the hint window!
if FHintWindow.Visible then FHintWindow.Visible := False;
end;
procedure TOICustomPropertyGrid.IncreaseChangeStep;
begin
if FChangeStep<>$7fffffff then
inc(FChangeStep)
else
FChangeStep:=-$7fffffff;
end;
PRocedure TOICustomPropertyGrid.ValueEditDblClick(Sender : TObject);
var
CurRow: TOIPropertyGridRow;
TypeKind : TTypeKind;
CurValue: string;
begin
if (FStates*[pgsChangingItemIndex,pgsApplyingValue]<>[])
or (FCurrentEdit=nil)
or (FItemIndex<0)
or (FItemIndex>=FRows.Count)
or ((FCurrentEditorLookupRoot<>nil)
and (FPropertyEditorHook<>nil)
and (FPropertyEditorHook.LookupRoot<>FCurrentEditorLookupRoot))
then begin
exit;
end;
FHintTimer.Enabled := False;
if FCurrentEdit=ValueEdit then
CurValue:=ValueEdit.Text
else
CurValue:=ValueComboBox.Text;
if CurValue='' then begin
DoCallEdit;
exit;
end;
if (FCurrentEdit=ValueComboBox) then Begin
//either an Event or an enumeration or Boolean
CurRow:=Rows[FItemIndex];
TypeKind := CurRow.Editor.GetPropType^.Kind;
if TypeKind in [tkEnumeration,tkBool] then begin
// set value to next value in list
if ValueComboBox.Items.Count = 0 then Exit;
if ValueComboBox.ItemIndex < (ValueComboBox.Items.Count-1) then
ValueComboBox.ItemIndex := ValueComboBox.ItemIndex +1
else
ValueComboBox.ItemIndex := 0;
end else if TypeKind=tkMethod then begin
DoCallEdit;
end;
end;
end;
procedure TOICustomPropertyGrid.SetBackgroundColor(const AValue: TColor);
begin
if FBackgroundColor=AValue then exit;
FBackgroundColor:=AValue;
Invalidate;
end;
//------------------------------------------------------------------------------
{ TOIPropertyGridRow }
constructor TOIPropertyGridRow.Create(PropertyTree: TOICustomPropertyGrid;
PropEditor:TPropertyEditor; ParentNode:TOIPropertyGridRow);
begin
inherited Create;
// tree pointer
FTree:=PropertyTree;
FParent:=ParentNode;
FNextBrother:=nil;
FPriorBrother:=nil;
FExpanded:=false;
// child nodes
FChildCount:=0;
FFirstChild:=nil;
FLastChild:=nil;
// director
FEditor:=PropEditor;
GetLvl;
FName:=FEditor.GetName;
FTop:=0;
FHeight:=FTree.DefaultItemHeight;
Index:=-1;
LastPaintedValue:='';
end;
destructor TOIPropertyGridRow.Destroy;
begin
if FPriorBrother<>nil then FPriorBrother.FNextBrother:=FNextBrother;
if FNextBrother<>nil then FNextBrother.FPriorBrother:=FPriorBrother;
if FParent<>nil then begin
if FParent.FFirstChild=Self then FParent.FFirstChild:=FNextBrother;
if FParent.FLastChild=Self then FParent.FLastChild:=FPriorBrother;
dec(FParent.FChildCount);
end;
if FEditor<>nil then FEditor.Free;
inherited Destroy;
end;
function TOIPropertyGridRow.ConsistencyCheck: integer;
var
OldLvl, RealChildCount: integer;
AChild: TOIPropertyGridRow;
begin
if Top<0 then begin
Result:=-1;
exit;
end;
if Height<0 then begin
Result:=-2;
exit;
end;
if Lvl<0 then begin
Result:=-3;
exit;
end;
OldLvl:=Lvl;
GetLvl;
if Lvl<>OldLvl then begin
Result:=-4;
exit;
end;
if Name='' then begin
Result:=-5;
exit;
end;
if NextBrother<>nil then begin
if NextBrother.PriorBrother<>Self then begin
Result:=-6;
exit;
end;
if NextBrother.Index<Index+1 then begin
Result:=-7;
exit;
end;
end;
if PriorBrother<>nil then begin
if PriorBrother.NextBrother<>Self then begin
Result:=-8;
exit;
end;
if PriorBrother.Index>Index-1 then begin
Result:=-9
end;
end;
if (Parent<>nil) then begin
// has parent
if (not Parent.HasChild(Self)) then begin
Result:=-10;
exit;
end;
end else begin
// no parent
end;
if FirstChild<>nil then begin
if Expanded then begin
if (FirstChild.Index<>Index+1) then begin
Result:=-11;
exit;
end;
end;
end else begin
if LastChild<>nil then begin
Result:=-12;
exit;
end;
end;
RealChildCount:=0;
AChild:=FirstChild;
while AChild<>nil do begin
if AChild.Parent<>Self then begin
Result:=-13;
exit;
end;
inc(RealChildCount);
AChild:=AChild.NextBrother;
end;
if RealChildCount<>ChildCount then begin
Result:=-14;
exit;
end;
Result:=0;
end;
function TOIPropertyGridRow.HasChild(Row: TOIPropertyGridRow): boolean;
var
ChildRow: TOIPropertyGridRow;
begin
ChildRow:=FirstChild;
while ChildRow<>nil do begin
if ChildRow=Row then begin
Result:=true;
exit;
end;
end;
Result:=false;
end;
procedure TOIPropertyGridRow.GetLvl;
var n:TOIPropertyGridRow;
begin
FLvl:=0;
n:=FParent;
while n<>nil do begin
inc(FLvl);
n:=n.FParent;
end;
end;
function TOIPropertyGridRow.GetBottom:integer;
begin
Result:=FTop+FHeight;
end;
function TOIPropertyGridRow.IsReadOnly: boolean;
begin
Result:=Editor.IsReadOnly or IsDisabled;
end;
function TOIPropertyGridRow.IsDisabled: boolean;
var
ParentRow: TOIPropertyGridRow;
begin
Result:=false;
ParentRow:=Parent;
while (ParentRow<>nil) do begin
if paDisableSubProperties in ParentRow.Editor.GetAttributes then begin
Result:=true;
exit;
end;
ParentRow:=ParentRow.Parent;
end;
end;
procedure TOIPropertyGridRow.MeasureHeight(ACanvas: TCanvas);
begin
FHeight:=FTree.DefaultItemHeight;
Editor.PropMeasureHeight(Name,ACanvas,FHeight);
end;
//==============================================================================
{ TOIOptions }
procedure TOIOptions.SetFilename(const NewFilename: string);
begin
if FFilename=NewFilename then exit;
FFilename:=NewFilename;
FFileHasChangedOnDisk:=true;
end;
function TOIOptions.FileHasChangedOnDisk: boolean;
begin
Result:=FFileHasChangedOnDisk
or ((FFilename<>'') and (FFileAge<>0) and (FileAge(FFilename)<>FFileAge));
FFileHasChangedOnDisk:=Result;
end;
function TOIOptions.GetXMLCfg: TXMLConfig;
begin
if CustomXMLCfg<>nil then begin
Result:=CustomXMLCfg;
end else begin
if FileHasChangedOnDisk or (FXMLCfg=nil) then begin
FXMLCfg.Free;
FXMLCfg:=TXMLConfig.Create(FFilename);
end;
Result:=FXMLCfg;
end;
end;
procedure TOIOptions.FileUpdated;
begin
FFileHasChangedOnDisk:=false;
if FFilename<>'' then
FFileAge:=FileAge(FFilename)
else
FFileAge:=0;
end;
constructor TOIOptions.Create;
begin
inherited Create;
FFilename:='';
FSaveBounds:=false;
FLeft:=0;
FTop:=0;
FWidth:=250;
FHeight:=400;
FPropertyGridSplitterX:=110;
FEventGridSplitterX:=110;
FDefaultItemHeight:=20;
FShowComponentTree:=true;
FComponentTreeHeight:=100;
FGridBackgroundColor:=clBtnFace;
end;
destructor TOIOptions.Destroy;
begin
FXMLCfg.Free;
inherited Destroy;
end;
function TOIOptions.Load:boolean;
var XMLConfig: TXMLConfig;
begin
Result:=false;
if not FileExists(FFilename) then exit;
try
XMLConfig:=GetXMLCfg;
FSaveBounds:=XMLConfig.GetValue('ObjectInspectorOptions/Bounds/Valid'
,false);
if FSaveBounds then begin
FLeft:=XMLConfig.GetValue('ObjectInspectorOptions/Bounds/Left',0);
FTop:=XMLConfig.GetValue('ObjectInspectorOptions/Bounds/Top',0);
FWidth:=XMLConfig.GetValue('ObjectInspectorOptions/Bounds/Width',250);
FHeight:=XMLConfig.GetValue('ObjectInspectorOptions/Bounds/Height',400);
end;
FPropertyGridSplitterX:=XMLConfig.GetValue(
'ObjectInspectorOptions/Bounds/PropertyGridSplitterX',110);
if FPropertyGridSplitterX<10 then FPropertyGridSplitterX:=10;
FEventGridSplitterX:=XMLConfig.GetValue(
'ObjectInspectorOptions/Bounds/EventGridSplitterX',110);
if FEventGridSplitterX<10 then FEventGridSplitterX:=10;
FDefaultItemHeight:=XMLConfig.GetValue(
'ObjectInspectorOptions/Bounds/DefaultItemHeight',20);
if FDefaultItemHeight<0 then FDefaultItemHeight:=20;
FShowComponentTree:=XMLConfig.GetValue(
'ObjectInspectorOptions/ComponentTree/Show/Value',true);
FComponentTreeHeight:=XMLConfig.GetValue(
'ObjectInspectorOptions/ComponentTree/Height/Value',100);
FGridBackgroundColor:=XMLConfig.GetValue(
'ObjectInspectorOptions/GridBackgroundColor',clBtnFace);
FShowHints:=XMLConfig.GetValue(
'ObjectInspectorOptions/ShowHints',false);
except
on E: Exception do begin
DebugLn('ERROR: TOIOptions.Load: ',E.Message);
exit;
end;
end;
Result:=true;
end;
function TOIOptions.Save:boolean;
var XMLConfig: TXMLConfig;
begin
Result:=false;
try
XMLConfig:=GetXMLCfg;
XMLConfig.SetDeleteValue('ObjectInspectorOptions/Bounds/Valid',FSaveBounds,
false);
if FSaveBounds then begin
XMLConfig.SetValue('ObjectInspectorOptions/Bounds/Left',FLeft);
XMLConfig.SetValue('ObjectInspectorOptions/Bounds/Top',FTop);
XMLConfig.SetValue('ObjectInspectorOptions/Bounds/Width',FWidth);
XMLConfig.SetValue('ObjectInspectorOptions/Bounds/Height',FHeight);
end;
XMLConfig.SetDeleteValue(
'ObjectInspectorOptions/Bounds/PropertyGridSplitterX',
FPropertyGridSplitterX, 110);
XMLConfig.SetDeleteValue(
'ObjectInspectorOptions/Bounds/EventGridSplitterX',
FEventGridSplitterX, 110);
XMLConfig.SetDeleteValue('ObjectInspectorOptions/Bounds/DefaultItemHeight',
FDefaultItemHeight,20);
XMLConfig.SetDeleteValue('ObjectInspectorOptions/ComponentTree/Show/Value',
FShowComponentTree,true);
XMLConfig.SetDeleteValue('ObjectInspectorOptions/ComponentTree/Height/Value',
FComponentTreeHeight,100);
XMLConfig.SetDeleteValue('ObjectInspectorOptions/GridBackgroundColor',
FGridBackgroundColor,clBackground);
XMLConfig.SetDeleteValue('ObjectInspectorOptions/ShowHints',FShowHints,
false);
if XMLConfig<>CustomXMLCfg then XMLConfig.Flush;
except
on E: Exception do begin
DebugLn('ERROR: TOIOptions.Save: ',E.Message);
exit;
end;
end;
Result:=true;
end;
procedure TOIOptions.Assign(AnObjInspector: TObjectInspector);
begin
FLeft:=AnObjInspector.Left;
FTop:=AnObjInspector.Top;
FWidth:=AnObjInspector.Width;
FHeight:=AnObjInspector.Height;
FPropertyGridSplitterX:=AnObjInspector.PropertyGrid.PrefferedSplitterX;
FEventGridSplitterX:=AnObjInspector.EventGrid.PrefferedSplitterX;
FDefaultItemHeight:=AnObjInspector.DefaultItemHeight;
FShowComponentTree:=AnObjInspector.ShowComponentTree;
FComponentTreeHeight:=AnObjInspector.ComponentTreeHeight;
FGridBackgroundColor:=AnObjInspector.PropertyGrid.BackgroundColor;
FShowHints:=AnObjInspector.PropertyGrid.ShowHint;
end;
procedure TOIOptions.AssignTo(AnObjInspector: TObjectInspector);
begin
if FSaveBounds then begin
AnObjInspector.SetBounds(FLeft,FTop,FWidth,FHeight);
end;
AnObjInspector.PropertyGrid.PrefferedSplitterX:=FPropertyGridSplitterX;
AnObjInspector.PropertyGrid.SplitterX:=FPropertyGridSplitterX;
AnObjInspector.EventGrid.PrefferedSplitterX:=FEventGridSplitterX;
AnObjInspector.EventGrid.SplitterX:=FEventGridSplitterX;
AnObjInspector.DefaultItemHeight:=FDefaultItemHeight;
AnObjInspector.ShowComponentTree:=FShowComponentTree;
AnObjInspector.ComponentTreeHeight:=FComponentTreeHeight;
AnObjInspector.PropertyGrid.BackgroundColor:=FGridBackgroundColor;
AnObjInspector.PropertyGrid.ShowHint:=FShowHints;
AnObjInspector.EventGrid.BackgroundColor:=FGridBackgroundColor;
AnObjInspector.EventGrid.ShowHint:=FShowHints;
end;
//==============================================================================
{ TObjectInspector }
constructor TObjectInspector.Create(AnOwner: TComponent);
procedure AddPopupMenuItem(var NewMenuItem: TMenuItem;
ParentMenuItem: TMenuItem; const AName, ACaption, AHint: string;
AnOnClick: TNotifyEvent; CheckedFlag, EnabledFlag, VisibleFlag: boolean);
begin
NewMenuItem:=TMenuItem.Create(Self);
with NewMenuItem do begin
Name:=AName;
Caption:=ACaption;
Hint:=AHint;
OnClick:=AnOnClick;
Checked:=CheckedFlag;
Enabled:=EnabledFlag;
Visible:=VisibleFlag;
end;
if ParentMenuItem<>nil then
ParentMenuItem.Add(NewMenuItem)
else
MainPopupMenu.Items.Add(NewMenuItem);
end;
procedure AddSeparatorMenuItem(ParentMenuItem: TMenuItem;
const AName: string; VisibleFlag: boolean);
var
NewMenuItem: TMenuItem;
begin
NewMenuItem:=TMenuItem.Create(Self);
with NewMenuItem do begin
Name:=AName;
Caption:='-';
Visible:=VisibleFlag;
end;
if ParentMenuItem<>nil then
ParentMenuItem.Add(NewMenuItem)
else
MainPopupMenu.Items.Add(NewMenuItem);
end;
begin
inherited Create(AnOwner);
FPropertyEditorHook:=nil;
FSelection:=TPersistentSelectionList.Create;
FUpdatingAvailComboBox:=false;
FDefaultItemHeight := 22;
FComponentTreeHeight:=100;
FShowComponentTree:=true;
FUsePairSplitter:=TPairSplitter.IsSupportedByInterface;
Caption := oisObjectInspector;
Name := DefaultObjectInspectorName;
KeyPreview:=true;
// StatusBar
StatusBar:=TStatusBar.Create(Self);
with StatusBar do begin
Name:='StatusBar';
Parent:=Self;
SimpleText:=oisAll;
Align:= alBottom;
end;
// PopupMenu
MainPopupMenu:=TPopupMenu.Create(Self);
with MainPopupMenu do begin
Name:='MainPopupMenu';
OnPopup:=@OnMainPopupMenuPopup;
AutoPopup:=true;
end;
AddPopupMenuItem(SetDefaultPopupmenuItem,nil,'SetDefaultPopupMenuItem',
'Set to Default Value','Set property value to Default',
@OnSetDefaultPopupmenuItemClick,false,true,true);
AddPopupMenuItem(UndoPropertyPopupMenuItem,nil,'UndoPropertyPopupMenuItem',
'Undo','Set property value to last valid value',
@OnUndoPopupmenuItemClick,false,true,true);
AddSeparatorMenuItem(nil,'OptionsSeparatorMenuItem',true);
AddPopupMenuItem(ColorsPopupmenuItem,nil,'ColorsPopupMenuItem','Set Colors',''
,nil,false,true,true);
AddPopupMenuItem(BackgroundColPopupMenuItem,ColorsPopupMenuItem
,'BackgroundColPopupMenuItem','Background','Grid background color'
,@OnBackgroundColPopupMenuItemClick,false,true,true);
AddPopupMenuItem(ShowHintsPopupMenuItem,nil
,'ShowHintPopupMenuItem','Show Hints','Grid hints'
,@OnShowHintPopupMenuItemClick,false,true,true);
ShowHintsPopupMenuItem.ShowAlwaysCheckable:=true;
AddPopupMenuItem(ShowComponentTreePopupMenuItem,nil
,'ShowComponentTreePopupMenuItem','Show Component Tree',''
,@OnShowComponentTreePopupMenuItemClick,FShowComponentTree,true,true);
ShowComponentTreePopupMenuItem.ShowAlwaysCheckable:=true;
AddPopupMenuItem(ShowOptionsPopupMenuItem,nil
,'ShowOptionsPopupMenuItem','Options',''
,@OnShowOptionsPopupMenuItemClick,false,true,FOnShowOptions<>nil);
PopupMenu:=MainPopupMenu;
// combobox at top (filled with available persistents)
AvailPersistentComboBox := TComboBox.Create (Self);
with AvailPersistentComboBox do begin
Name:='AvailPersistentComboBox';
Parent:=Self;
Style:=csDropDown;
Text:='';
OnCloseUp:=@AvailComboBoxCloseUp;
//Sorted:=true;
Align:= alTop;
Visible:=not FShowComponentTree;
end;
if FUsePairSplitter and ShowComponentTree then
CreatePairSplitter;
// Component Tree at top (filled with available components)
ComponentTree:=TComponentTreeView.Create(Self);
with ComponentTree do begin
Name:='ComponentTree';
Height:=ComponentTreeHeight;
if PairSplitter1<>nil then begin
Parent:=PairSplitter1.Sides[0];
Align:=alClient;
end else begin
Parent:=Self;
Align:=alTop;
end;
OnSelectionChanged:=@ComponentTreeSelectionChanged;
Visible:=FShowComponentTree;
end;
CreateNoteBook;
OnResize:=@ObjectInspectorResize;
end;
destructor TObjectInspector.Destroy;
begin
FreeAndNil(FSelection);
inherited Destroy;
end;
procedure TObjectInspector.SetPropertyEditorHook(NewValue:TPropertyEditorHook);
begin
if FPropertyEditorHook=NewValue then exit;
if FPropertyEditorHook<>nil then begin
FPropertyEditorHook.RemoveAllHandlersForObject(Self);
end;
FPropertyEditorHook:=NewValue;
if FPropertyEditorHook<>nil then begin
FPropertyEditorHook.AddHandlerChangeLookupRoot(@HookLookupRootChange);
FPropertyEditorHook.AddHandlerRefreshPropertyValues(
@HookRefreshPropertyValues);
FPropertyEditorHook.AddHandlerGetSelection(@HookGetSelection);
FPropertyEditorHook.AddHandlerSetSelection(@HookSetSelection);
// select root component
FSelection.Clear;
if (FPropertyEditorHook<>nil) and (FPropertyEditorHook.LookupRoot<>nil)
and (FPropertyEditorHook.LookupRoot is TComponent) then
FSelection.Add(TComponent(FPropertyEditorHook.LookupRoot));
FillPersistentComboBox;
PropertyGrid.PropertyEditorHook:=FPropertyEditorHook;
EventGrid.PropertyEditorHook:=FPropertyEditorHook;
ComponentTree.PropertyEditorHook:=FPropertyEditorHook;
RefreshSelection;
end;
end;
function TObjectInspector.PersistentToString(APersistent: TPersistent): string;
begin
if APersistent is TComponent then
Result:=TComponent(APersistent).GetNamePath+': '+APersistent.ClassName
else
Result:=APersistent.ClassName;
end;
procedure TObjectInspector.SetComponentTreeHeight(const AValue: integer);
begin
if FComponentTreeHeight=AValue then exit;
FComponentTreeHeight:=AValue;
end;
procedure TObjectInspector.SetDefaultItemHeight(const AValue: integer);
var
NewValue: Integer;
begin
NewValue:=AValue;
if NewValue<0 then
NewValue:=0
else if NewValue=0 then
NewValue:=22
else if (NewValue>0) and (NewValue<10) then
NewValue:=10
else if NewValue>100 then NewValue:=100;
if FDefaultItemHeight=NewValue then exit;
FDefaultItemHeight:=NewValue;
PropertyGrid.DefaultItemHeight:=FDefaultItemHeight;
EventGrid.DefaultItemHeight:=FDefaultItemHeight;
RebuildPropertyLists;
end;
procedure TObjectInspector.SetOnShowOptions(const AValue: TNotifyEvent);
begin
if FOnShowOptions=AValue then exit;
FOnShowOptions:=AValue;
ShowOptionsPopupMenuItem.Visible:=FOnShowOptions<>nil;
end;
procedure TObjectInspector.AddPersistentToList(APersistent: TPersistent;
List: TStrings);
var
Allowed: boolean;
begin
if (APersistent is TComponent)
and (csDestroying in TComponent(APersistent).ComponentState) then exit;
Allowed:=true;
if Assigned(FOnAddAvailablePersistent) then
FOnAddAvailablePersistent(APersistent,Allowed);
if Allowed then
List.AddObject(PersistentToString(APersistent),APersistent);
end;
procedure TObjectInspector.HookLookupRootChange;
begin
PropertyGrid.PropEditLookupRootChange;
EventGrid.PropEditLookupRootChange;
FillPersistentComboBox;
end;
procedure TObjectInspector.FillPersistentComboBox;
var a:integer;
Root:TComponent;
OldText:AnsiString;
NewList: TStringList;
begin
//writeln('[TObjectInspector.FillComponentComboBox] A ',FUpdatingAvailComboBox
//,' ',FPropertyEditorHook<>nil,' ',FPropertyEditorHook.LookupRoot<>nil);
if FUpdatingAvailComboBox then exit;
FUpdatingAvailComboBox:=true;
if ComponentTree<>nil then
ComponentTree.Selection:=FSelection;
NewList:=TStringList.Create;
try
if (FPropertyEditorHook<>nil)
and (FPropertyEditorHook.LookupRoot<>nil) then begin
AddPersistentToList(FPropertyEditorHook.LookupRoot,NewList);
if FPropertyEditorHook.LookupRoot is TComponent then begin
Root:=TComponent(FPropertyEditorHook.LookupRoot);
//writeln('[TObjectInspector.FillComponentComboBox] B ',Root.Name,' ',Root.ComponentCount);
for a:=0 to Root.ComponentCount-1 do
AddPersistentToList(Root.Components[a],NewList);
end;
end;
if AvailPersistentComboBox.Items.Equals(NewList) then exit;
AvailPersistentComboBox.Items.BeginUpdate;
if AvailPersistentComboBox.Items.Count=1 then
OldText:=AvailPersistentComboBox.Text
else
OldText:='';
AvailPersistentComboBox.Items.Assign(NewList);
AvailPersistentComboBox.Items.EndUpdate;
a:=AvailPersistentComboBox.Items.IndexOf(OldText);
if (OldText='') or (a<0) then
SetAvailComboBoxText
else
AvailPersistentComboBox.ItemIndex:=a;
finally
NewList.Free;
FUpdatingAvailComboBox:=false;
end;
end;
procedure TObjectInspector.BeginUpdate;
begin
inc(FUpdateLock);
end;
procedure TObjectInspector.EndUpdate;
begin
dec(FUpdateLock);
if FUpdateLock<0 then begin
DebugLn('ERROR TObjectInspector.EndUpdate');
end;
if FUpdateLock=0 then begin
if oifRebuildPropListsNeeded in FFLags then
RebuildPropertyLists;
end;
end;
function TObjectInspector.GetActivePropertyGrid: TOICustomPropertyGrid;
begin
Result:=nil;
if NoteBook=nil then exit;
if NoteBook.PageIndex=0 then
Result:=PropertyGrid
else if NoteBook.PageIndex=1 then
Result:=EventGrid;
end;
function TObjectInspector.GetActivePropertyRow: TOIPropertyGridRow;
var
CurGrid: TOICustomPropertyGrid;
begin
Result:=nil;
CurGrid:=GetActivePropertyGrid;
if CurGrid=nil then exit;
Result:=CurGrid.GetActiveRow;
end;
function TObjectInspector.GetCurRowDefaultValue(var DefaultStr: string): boolean;
var
CurRow: TOIPropertyGridRow;
begin
Result:=false;
DefaultStr:='';
CurRow:=GetActivePropertyRow;
if (CurRow=nil) or (not (paHasDefaultValue in CurRow.Editor.GetAttributes))
then exit;
try
DefaultStr:=CurRow.Editor.GetDefaultValue;
Result:=true;
except
DefaultStr:='';
end;
end;
procedure TObjectInspector.SetSelection(
const ASelection:TPersistentSelectionList);
begin
if FSelection.IsEqual(ASelection) then exit;
//if (FSelection.Count=1) and (FSelection[0] is TCollectionItem)
//and (ASelection.Count=0) then RaiseGDBException('');
FSelection.Assign(ASelection);
SetAvailComboBoxText;
RefreshSelection;
end;
procedure TObjectInspector.RefreshSelection;
begin
PropertyGrid.Selection := FSelection;
EventGrid.Selection := FSelection;
ComponentTree.Selection := FSelection;
ComponentTree.MakeSelectionVisible;
if (not Visible) and (FSelection.Count>0) then
Visible:=true;
end;
procedure TObjectInspector.RefreshPropertyValues;
begin
PropertyGrid.RefreshPropertyValues;
EventGrid.RefreshPropertyValues;
end;
procedure TObjectInspector.RebuildPropertyLists;
begin
if FUpdateLock>0 then
Include(FFLags,oifRebuildPropListsNeeded)
else begin
Exclude(FFLags,oifRebuildPropListsNeeded);
PropertyGrid.BuildPropertyList;
EventGrid.BuildPropertyList;
end;
end;
procedure TObjectInspector.AvailComboBoxCloseUp(Sender:TObject);
var NewComponent,Root:TComponent;
a:integer;
procedure SetSelectedPersistent(c:TPersistent);
begin
if (FSelection.Count=1) and (FSelection[0]=c) then exit;
FSelection.Clear;
FSelection.Add(c);
RefreshSelection;
if Assigned(FOnSelectPersistentsInOI) then
FOnSelectPersistentsInOI(Self);
end;
// AvailComboBoxChange
begin
if FUpdatingAvailComboBox then exit;
if (FPropertyEditorHook=nil) or (FPropertyEditorHook.LookupRoot=nil) then
exit;
if not (FPropertyEditorHook.LookupRoot is TComponent) then begin
// not a TComponent => no childs => select always only the root
SetSelectedPersistent(FPropertyEditorHook.LookupRoot);
exit;
end;
Root:=TComponent(FPropertyEditorHook.LookupRoot);
if (AvailPersistentComboBox.Text=PersistentToString(Root)) then begin
SetSelectedPersistent(Root);
end else begin
for a:=0 to Root.ComponentCount-1 do begin
NewComponent:=Root.Components[a];
if AvailPersistentComboBox.Text=PersistentToString(NewComponent) then
begin
SetSelectedPersistent(NewComponent);
break;
end;
end;
end;
end;
procedure TObjectInspector.ComponentTreeSelectionChanged(Sender: TObject);
begin
if (PropertyEditorHook=nil) or (PropertyEditorHook.LookupRoot=nil) then exit;
if FSelection.IsEqual(ComponentTree.Selection) then exit;
Fselection.Assign(ComponentTree.Selection);
RefreshSelection;
if Assigned(FOnSelectPersistentsInOI) then
FOnSelectPersistentsInOI(Self);
end;
procedure TObjectInspector.ObjectInspectorResize(Sender: TObject);
begin
if (ComponentTree<>nil) and (ComponentTree.Visible)
and (ComponentTree.Parent=Self) then
ComponentTree.Height:=ClientHeight div 4;
end;
procedure TObjectInspector.OnGriddKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Assigned(OnRemainingKeyUp) then OnRemainingKeyUp(Self,Key,Shift);
end;
procedure TObjectInspector.OnSetDefaultPopupmenuItemClick(Sender: TObject);
var
CurGrid: TOICustomPropertyGrid;
DefaultStr: string;
begin
if not GetCurRowDefaultValue(DefaultStr) then exit;
CurGrid:=GetActivePropertyGrid;
if CurGrid=nil then exit;
CurGrid.SetCurrentRowValue(DefaultStr);
RefreshPropertyValues;
end;
procedure TObjectInspector.OnUndoPopupmenuItemClick(Sender: TObject);
var
CurGrid: TOICustomPropertyGrid;
CurRow: TOIPropertyGridRow;
begin
CurGrid:=GetActivePropertyGrid;
CurRow:=GetActivePropertyRow;
if CurRow=nil then exit;
CurGrid.CurrentEditValue:=CurRow.Editor.GetVisualValue;
end;
procedure TObjectInspector.OnBackgroundColPopupMenuItemClick(Sender :TObject);
var ColorDialog:TColorDialog;
begin
ColorDialog:=TColorDialog.Create(Application);
try
with ColorDialog do begin
Color:=PropertyGrid.BackgroundColor;
if Execute then begin
PropertyGrid.BackgroundColor:=Color;
EventGrid.BackgroundColor:=Color;
end;
end;
finally
ColorDialog.Free;
end;
end;
procedure TObjectInspector.OnGridModified(Sender: TObject);
begin
if Assigned(FOnModified) then FOnModified(Self);
end;
procedure TObjectInspector.SetAvailComboBoxText;
begin
case FSelection.Count of
0: // none selected
AvailPersistentComboBox.Text:='';
1: // single selection
AvailPersistentComboBox.Text:=PersistentToString(FSelection[0]);
else
// multi selection
AvailPersistentComboBox.Text:=Format(oisItemsSelected, [FSelection.Count]);
end;
end;
procedure TObjectInspector.HookGetSelection(
const ASelection: TPersistentSelectionList);
begin
if ASelection=nil then exit;
ASelection.Assign(FSelection);
end;
procedure TObjectInspector.HookSetSelection(
const ASelection: TPersistentSelectionList);
begin
if ASelection=nil then exit;
if FSelection.IsEqual(ASelection) then exit;
Selection:=ASelection;
if Assigned(FOnSelectPersistentsInOI) then
FOnSelectPersistentsInOI(Self);
end;
procedure TObjectInspector.SetShowComponentTree(const AValue: boolean);
begin
if FShowComponentTree=AValue then exit;
FShowComponentTree:=AValue;
BeginUpdate;
ShowComponentTreePopupMenuItem.Checked:=FShowComponentTree;
// hide controls while rebuilding
if PairSplitter1<>nil then
PairSplitter1.Visible:=false;
DestroyNoteBook;
ComponentTree.Visible:=false;
AvailPersistentComboBox.Visible:=false;
// rebuild controls
if FUsePairSplitter and FShowComponentTree then begin
CreatePairSplitter;
ComponentTree.Parent:=PairSplitter1.Sides[0];
ComponentTree.Align:=alClient;
end else begin
ComponentTree.Parent:=Self;
ComponentTree.Align:=alTop;
ComponentTree.Height:=ComponentTreeHeight;
if PairSplitter1<>nil then begin
PairSplitter1.Free;
PairSplitter1:=nil;
end;
end;
ComponentTree.Visible:=FShowComponentTree;
AvailPersistentComboBox.Visible:=not FShowComponentTree;
CreateNoteBook;
EndUpdate;
end;
procedure TObjectInspector.SetUsePairSplitter(const AValue: boolean);
begin
if FUsePairSplitter=AValue then exit;
FUsePairSplitter:=AValue;
end;
procedure TObjectInspector.CreatePairSplitter;
begin
// pair splitter between component tree and notebook
PairSplitter1:=TPairSplitter.Create(Self);
with PairSplitter1 do begin
Name:='PairSplitter1';
Parent:=Self;
SplitterType:=pstVertical;
Align:=alClient;
Position:=ComponentTreeHeight;
Sides[0].Name:=Name+'Side1';
Sides[1].Name:=Name+'Side2';
end;
end;
procedure TObjectInspector.DestroyNoteBook;
begin
if NoteBook<>nil then
NoteBook.Visible:=false;
if PropertyGrid<>nil then begin
PropertyGrid.Free;
PropertyGrid:=nil;
end;
if EventGrid<>nil then begin
EventGrid.Free;
EventGrid:=nil;
end;
if NoteBook<>nil then begin
NoteBook.Free;
NoteBook:=nil;
end;
end;
procedure TObjectInspector.CreateNoteBook;
begin
DestroyNoteBook;
// NoteBook
NoteBook:=TNoteBook.Create(Self);
with NoteBook do begin
Name:='NoteBook';
if PairSplitter1<>nil then begin
Parent:=PairSplitter1.Sides[1];
end else begin
Parent:=Self;
end;
Align:= alClient;
if PageCount>0 then
Pages.Strings[0]:=oisProperties
else
Pages.Add(oisProperties);
Page[0].Name:='PropertyPage';
Pages.Add(oisEvents);
Page[1].Name:='EventPage';
PageIndex:=0;
PopupMenu:=MainPopupMenu;
end;
// property grid
PropertyGrid:=TOICustomPropertyGrid.CreateWithParams(Self,PropertyEditorHook
,[tkUnknown, tkInteger, tkChar, tkEnumeration, tkFloat, tkSet{, tkMethod}
, tkSString, tkLString, tkAString, tkWString, tkVariant
{, tkArray, tkRecord, tkInterface}, tkClass, tkObject, tkWChar, tkBool
, tkInt64, tkQWord],
FDefaultItemHeight);
with PropertyGrid do begin
Name:='PropertyGrid';
Parent:=NoteBook.Page[0];
{
ValueEdit.Parent:=Parent;
ValueComboBox.Parent:=Parent;
ValueButton.Parent:=Parent;
}
Selection:=Self.FSelection;
Align:=alClient;
PopupMenu:=MainPopupMenu;
OnModified:=@OnGridModified;
OnKeyUp:=@OnGriddKeyUp;
end;
// event grid
EventGrid:=TOICustomPropertyGrid.CreateWithParams(Self,PropertyEditorHook,
[tkMethod],FDefaultItemHeight);
with EventGrid do begin
Name:='EventGrid';
Parent:=NoteBook.Page[1];
{
ValueEdit.Parent:=Parent;
ValueComboBox.Parent:=Parent;
ValueButton.Parent:=Parent;
}
Selection:=Self.FSelection;
Align:=alClient;
PopupMenu:=MainPopupMenu;
OnModified:=@OnGridModified;
OnKeyUp:=@OnGriddKeyUp;
end;
end;
procedure TObjectInspector.KeyDown(var Key: Word; Shift: TShiftState);
var
CurGrid: TOICustomPropertyGrid;
begin
CurGrid:=GetActivePropertyGrid;
if CurGrid<>nil then begin
CurGrid.HandleStandardKeys(Key,Shift);
if Key=VK_UNKNOWN then exit;
end;
inherited KeyDown(Key, Shift);
end;
procedure TObjectInspector.KeyUp(var Key: Word; Shift: TShiftState);
begin
inherited KeyUp(Key, Shift);
if (Key<>VK_UNKNOWN) and Assigned(OnRemainingKeyUp) then
OnRemainingKeyUp(Self,Key,Shift);
end;
procedure TObjectInspector.OnShowHintPopupMenuItemClick(Sender : TObject);
begin
PropertyGrid.ShowHint:=not PropertyGrid.ShowHint;
EventGrid.ShowHint:=not EventGrid.ShowHint;
end;
procedure TObjectInspector.OnShowOptionsPopupMenuItemClick(Sender: TObject);
begin
if Assigned(FOnShowOptions) then FOnShowOptions(Sender);
end;
procedure TObjectInspector.OnShowComponentTreePopupMenuItemClick(Sender: TObject
);
begin
ShowComponentTree:=not ShowComponentTree;
end;
procedure TObjectInspector.OnMainPopupMenuPopup(Sender: TObject);
var
DefaultStr: String;
CurGrid: TOICustomPropertyGrid;
CurRow: TOIPropertyGridRow;
begin
SetDefaultPopupMenuItem.Enabled:=GetCurRowDefaultValue(DefaultStr);
if SetDefaultPopupMenuItem.Enabled then
SetDefaultPopupMenuItem.Caption:=Format(oisSetToDefault, [DefaultStr])
else
SetDefaultPopupMenuItem.Caption:=oisSetToDefaultValue;
CurGrid:=GetActivePropertyGrid;
CurRow:=GetActivePropertyRow;
if (CurRow<>nil) and (CurRow.Editor.GetVisualValue<>CurGrid.CurrentEditValue)
then
UndoPropertyPopupMenuItem.Enabled:=true
else
UndoPropertyPopupMenuItem.Enabled:=false;
end;
procedure TObjectInspector.HookRefreshPropertyValues;
begin
RefreshPropertyValues;
end;
{ TCustomPropertiesGrid }
function TCustomPropertiesGrid.GetTIObject: TPersistent;
begin
if PropertyEditorHook<>nil then Result:=PropertyEditorHook.LookupRoot;
end;
procedure TCustomPropertiesGrid.SetAutoFreeHook(const AValue: boolean);
begin
if FAutoFreeHook=AValue then exit;
FAutoFreeHook:=AValue;
end;
procedure TCustomPropertiesGrid.SetTIObject(const AValue: TPersistent);
var
NewSelection: TPersistentSelectionList;
begin
if (TIObject=AValue) then begin
if ((AValue<>nil) and (Selection.Count=1) and (Selection[0]=AValue))
or (AValue=nil) then
exit;
end;
if PropertyEditorHook=nil then
PropertyEditorHook:=TPropertyEditorHook.Create;
PropertyEditorHook.LookupRoot:=AValue;
if (AValue<>nil) and ((Selection.Count<>1) or (Selection[0]<>AValue)) then
begin
NewSelection:=TPersistentSelectionList.Create;
try
if AValue<>nil then
NewSelection.Add(AValue);
Selection:=NewSelection;
finally
NewSelection.Free;
end;
end;
end;
constructor TCustomPropertiesGrid.Create(TheOwner: TComponent);
var
Hook: TPropertyEditorHook;
begin
Hook:=TPropertyEditorHook.Create;
AutoFreeHook:=true;
CreateWithParams(TheOwner,Hook,AllTypeKinds,25);
end;
end.