{
 *****************************************************************************
  See the file COPYING.modifiedLGPL.txt, included in this distribution,
  for details about the license.
 *****************************************************************************

  Author: Mattias Gaertner

  Abstract:
    This units defines the property editors used by the object inspector.
    A Property Editor is the interface between a row of the object inspector
    and a property in the RTTI.
    For more information see the big comment part below.
}
unit PropEdits;

{$mode objfpc}{$H+}

// This unit contains a lot of base type conversions. Disable range checking.
{$R-}
{$ModeSwitch advancedrecords}

{$IF FPC_FULLVERSION>30300}
  {$Define HasExtRtti}
{$ENDIF}

interface

uses
  // RTL / FCL
  Classes, TypInfo, SysUtils, types, RtlConsts, variants, Contnrs, strutils, FGL,
  Math, System.UITypes,
  // LCL
  LCLType, LCLIntf, LCLProc, Forms, Controls, ButtonPanel, Graphics,
  StdCtrls, Buttons, Menus, ExtCtrls, ComCtrls, Dialogs, EditBtn, Grids, ValEdit,
  FileCtrl, PropertyStorage, Themes,
  // LazControls
  CheckBoxThemed,
  // LazUtils
  FileUtil, StringHashList, LazMethodList, LazLoggerBase, LazUtilities,
  GraphType, FPCAdds, // for StrToQWord in older fpc versions
  // IdeIntf
  ObjInspStrConsts, PropEditUtils, TextTools,
  // Forms with .lfm files
  FrmSelectProps, StringsPropEditDlg, KeyValPropEditDlg, CollectionPropEditForm,
  FileFilterPropEditor, PagesPropEditDlg, IDEWindowIntf;

const
  MaxIdentLength: Byte = 63;
  CheckBoxThemedLeftOffs = 3;

  {$IFDEF LCLCarbon}
  // LineFeed symbol (UTF8) to maintain linefeeds in multiline text for Carbon TEdit.
  // In Carbon, linefeeds get stripped from TEdit text, so we replace it temporary with
  // this symbol which displays correctly a LF symbol in the Object Inspector as well.
  LineFeedSymbolUTF8 = #226#144#138;
  {$ENDIF}

type

  TPersistentSelectionList = PropEditUtils.TPersistentSelectionList;
  // For backwards compatibility only. Use TGetStrProc directly.
  TGetStringProc = Classes.TGetStrProc;

{ TPropertyEditor
  Edits a property of a component, or list of components, selected into the
  Object Inspector. The property editor is created based on the type of the
  property being edited as determined by the types registered by
  RegisterPropertyEditor. The Object Inspector uses a TPropertyEditor
  for all modification to a property. GetName and GetValue are called to
  display the name and value of the property. SetValue is called whenever the
  user requests to change the value. Edit is called when the user
  double-clicks the property in the Object Inspector. GetValues is called when
  the drop-down list of a property is displayed. GetProperties is called when
  the property is expanded to show sub-properties. AllEqual is called to decide
  whether or not to display the value of the property when more than one
  component is selected.

  The following are methods that can be overridden to change the behavior of
  the property editor:

    Activate
      Called whenever the property becomes selected in the object inspector.
      This is potentially useful to allow certain property attributes to
      to only be determined whenever the property is selected in the object
      inspector. Only paSubProperties and paMultiSelect,returned from
      GetAttributes,need to be accurate before this method is called.
    Deactivate
      Called whenevr the property becomes unselected in the object inspector.
    AllEqual
      Called whenever there is more than one component selected. If this
      method returns true,GetValue is called,otherwise blank is displayed
      in the Object Inspector. This is called only when GetAttributes
      returns paMultiSelect.
    AutoFill
      Called to determine whether the values returned by GetValues can be
      selected incrementally in the Object Inspector. This is called only when
      GetAttributes returns paValueList.
    Edit
      Called when the '...' button is pressed or the property is double-clicked.
      This can,for example,bring up a dialog to allow the editing the
      component in some more meaningful fashion than by text (e.g. the Font
      property).
    GetAttributes
      Returns the information for use in the Object Inspector to be able to
      show the appropriate tools. GetAttributes returns a set of type
      TPropertyAttributes:
        paValueList:    The property editor can return an enumerated list of
                        values for the property. If GetValues calls Proc
                        with values then this attribute should be set. This
                        will cause the drop-down button to appear to the right
                        of the property in the Object Inspector.
        paSortList:     Object Inspector to sort the list returned by
                        GetValues.
        paPickList:     Usable together with paValueList. The text field is
                        readonly. The user can still select values from drop
                        list. Unless paReadOnly.
        paSubProperties:The property editor has sub-properties that will be
                        displayed indented and below the current property in
                        standard outline format. If GetProperties will
                        generate property objects then this attribute should
                        be set.
        paDynamicSubProps:The sub properties can change. All designer tools
                        (e.g. property editors, component editors) that change
                        the list should call UpdateListPropertyEditors, so that
                        the object inspector will reread the subproperties.
        paDialog:       Indicates that the Edit method will bring up a
                        dialog. This will cause the '...' button to be
                        displayed to the right of the property in the Object
                        Inspector.
        paMultiSelect:  Allows the property to be displayed when more than
                        one component is selected. Some properties are not
                        appropriate for multi-selection (e.g. the Name
                        property).
        paAutoUpdate:   Causes the SetValue method to be called on each
                        change made to the editor instead of after the change
                        has been approved (e.g. the Caption property).
        paReadOnly:     Value is not allowed to change. But if paDialog is set
                        a Dialog can change the value. This disables only the
                        edit and combobox in the object inspector.
        paRevertable:   Allows the property to be reverted to the original
                        value. Things that shouldn't be reverted are nested
                        properties (e.g. Fonts) and elements of a composite
                        property such as set element values.
        paFullWidthName:Tells the object inspector that the value does not
                        need to be rendered and as such the name should be
                        rendered the full width of the inspector.
        paVolatileSubProperties: Any change of property value causes any shown
                        subproperties to be recollected.
        paDisableSubProperties: All subproperties are readonly
                        (not even via Dialog).
        paReference:    property contains a reference to something else. When
                        used in conjunction with paSubProperties the referenced
                        object should be displayed as sub properties to this
                        property.
        paNotNestable:  Indicates that the property is not safe to show when
                        showing the properties of an expanded reference.

    GetComponent
      Returns the Index'th component being edited by this property editor. This
      is used to retrieve the components. A property editor can only refer to
      multiple components when paMultiSelect is returned from GetAttributes.
    GetEditLimit
      Returns the number of character the user is allowed to enter for the
      value. The inplace editor of the object inspector will be have its
      text limited set to the return value. By default this limit is 255.
    GetName
      Returns the name of the property. By default the value is retrieved
      from the type information with all underbars replaced by spaces. This
      should only be overridden if the name of the property is not the name
      that should appear in the Object Inspector.
    GetProperties
      Should be overridden to call PropertyProc for every sub-property (or
      nested property) of the property begin edited and passing a new
      TPropertyEditor for each sub-property. By default, PropertyProc is not
      called and no sub-properties are assumed. TClassPropertyEditor will pass a
      new property editor for each published property in a class.
      TSetPropertyEditor passes a new editor for each element in the set.
    GetPropType
      Returns the type information pointer for the property(s) being edited.
    GetValue
      Returns the string value of the property. By default this returns
      '(unknown)'. This should be overridden to return the appropriate value.
    GetValues
      Called when paValueList is returned in GetAttributes. Should call Proc
      for every value that is acceptable for this property. TEnumPropertyEditor
      will pass every element in the enumeration.
    Initialize
      Called after the property editor has been created but before it is used.
      Many times property editors are created and because they are not a common
      property across the entire selection they are thrown away. Initialize is
      called after it is determined the property editor is going to be used by
      the object inspector and not just thrown away.
    SetValue(Value)
      Called to set the value of the property. The property editor should be
      able to translate the string and call one of the SetXxxValue methods. If
      the string is not in the correct format or not an allowed value,the
      property editor should generate an exception describing the problem. Set
      value can ignore all changes and allow all editing of the property be
      accomplished through the Edit method (e.g. the Picture property).
    ListMeasureWidth(Value,Canvas,AWidth)
      This is called during the width calculation phase of the drop down list
      preparation.
    ListMeasureHeight(Value,Canvas,AHeight)
      This is called during the item/value height calculation phase of the drop
      down list's render. This is very similar to TListBox's OnMeasureItem,
      just slightly different parameters.
    ListDrawValue(Value,Canvas,Rect,Selected)
      This is called during the item/value render phase of the drop down list's
      render. This is very similar to TListBox's OnDrawItem, just slightly
      different parameters.
    PropMeasureHeight(Value,Canvas,AHeight)
      This is called during the item/property height calculation phase of the
      object inspectors rows render. This is very similar to TListBox's
      OnMeasureItem, just slightly different parameters.
    PropDrawName(Canvas,Rect,Selected)
      Called during the render of the name column of the property list. Its
      functionality is very similar to TListBox's OnDrawItem,but once again
      it has slightly different parameters.
    PropDrawValue(Canvas,Rect,Selected)
      Called during the render of the value column of the property list. Its
      functionality is similar to PropDrawName. If multiple items are selected
      and their values don't match this procedure will be passed an empty
      value.

  Properties and methods useful in creating new TPropertyEditor classes:

    Name property
      Returns the name of the property returned by GetName
    PrivateEditory property
      This is the configuration directory of lazarus.
      If the property editor needs auxiliary or state files (templates,
      examples, etc) they should be stored in this editory.
    Value property
      The current value,as a string,of the property as returned by GetValue.
    Modified
      Called to indicate the value of the property has been modified. Called
      automatically by the SetXxxValue methods. If you call a TProperty
      SetXxxValue method directly,you *must* call Modified as well.
    GetXxxValue
      Gets the value of the first property in the Properties property. Calls
      the appropriate TProperty GetXxxValue method to retrieve the value.
    SetXxxValue
      Sets the value of all the properties in the Properties property. Calls
      the approprate TProperty SetXxxxValue methods to set the value.
    GetVisualValue
      This function will return the displayable value of the property. If
      only one item is selected or all the multi-selected items have the same
      property value then this function will return the actual property value.
      Otherwise this function will return an empty string.}

  TPropertyAttribute=(
    paValueList,
    paPickList,
    paSubProperties,
    paDynamicSubProps,
    paDialog,
    paMultiSelect,
    paAutoUpdate,
    paSortList,
    paReadOnly,
    paRevertable,
    paFullWidthName,
    paVolatileSubProperties,
    paDisableSubProperties,
    paReference,
    paNotNestable,
    paCustomDrawn
    );
  TPropertyAttributes = set of TPropertyAttribute;

  TPropertyEditor = class;

  { TInstProp }

  TInstProp = record
    Instance: TPersistent; // can be nil, e.g. record field
    PropInfo: PPropInfo;   // can be nil, e.g. record field
    Field: Pointer; // for record field
    FieldName: PShortString;
    FieldTypeInfo: PTypeInfo;
    // ToDo: add list of parent instances, e.g. Label1.Font.Color: Font needs Label1
    function GetTypeInfo: PTypeInfo;
    function GetKind: TTypeKind;
    function GetOrd: int64;
    procedure SetOrd(const Value: int64);
    function GetEnum: string;
    procedure SetEnum(const Value: string);
    function GetSet(Brackets: boolean): string;
    procedure SetSet(const Value: string);
    function GetString: AnsiString;
    procedure SetString(const Value: string);
    function GetWideString: WideString;
    procedure SetWideString(const Value: WideString);
    function GetUnicodeString: UnicodeString;
    procedure SetUnicodeString(const Value: UnicodeString);
    function GetRawByteString: RawByteString;
    procedure SetRawByteString(const Value: RawByteString);
    function GetFloat: Extended;
    procedure SetFloat(const Value: Extended);
    function GetObject(MinClass: TClass = nil): TObject;
    procedure SetObject(const Value: TObject);
    function GetPointerValue: Pointer;
    procedure SetPointerValue(const Value: Pointer);
    function GetMethod: TMethod;
    procedure SetMethod(const Value: TMethod);
    function GetInterface: IInterface;
    procedure SetInterface(const Value: IInterface);
    function GetVariant: Variant;
    procedure SetVariant(const Value: Variant);
  end;
  PInstProp = ^TInstProp;

  TInstPropList = array[0..999999] of TInstProp;
  PInstPropList = ^TInstPropList;

  TGetPropEditProc = procedure(Prop: TPropertyEditor) of object;

  TPropEditDrawStateType = (pedsSelected, pedsFocused, pedsInEdit, pedsInComboList);
  TPropEditDrawState = set of TPropEditDrawStateType;
  
  TPropEditHint = (
    pehNone,
    pehTree,
    pehName,
    pehValue,
    pehEditButton
    );

  TPropertyEditorHook = class;

  { TPropertyEditor }

  TPropertyEditor = class
  private
    FOnSubPropertiesChanged: TNotifyEvent;
    FPropertyHook: TPropertyEditorHook;
    FOwnerComponent: TComponent;
    FPropCount: Integer;
    FPropList: PInstPropList;
  protected
    // Draw Checkbox for Boolean and Set element editors.
    {$IFDEF UseOINormalCheckBox}
    function DrawCheckbox(ACanvas: TCanvas; const ARect: TRect; IsTrue: Boolean): TRect;
    {$ENDIF}
    function DrawCheckValue(ACanvas: TCanvas; const ARect: TRect;
      {%H-}AState: TPropEditDrawState; {%H-}IsTrue: Boolean): TRect;
    procedure DrawValue(const AValue: string; ACanvas:TCanvas; const ARect:TRect;
      {%H-}AState:TPropEditDrawState);
    function GetPrivateDirectory: ansistring;
  public
    constructor Create(Hook:TPropertyEditorHook; APropCount:Integer); virtual;
    destructor Destroy; override;
    procedure Activate; virtual;
    procedure Deactivate; virtual;
    function AllEqual: Boolean; virtual;
    function AutoFill: Boolean; virtual;
    // Called when clicking on OI property button or double clicking on value.
    procedure Edit; virtual;
    // Needed for method Contraints.OnChange etc.
    procedure Edit(AOwnerComponent: TComponent);
    procedure ShowValue; virtual; // called when Ctrl-Click on value
    function GetAttributes: TPropertyAttributes; virtual;
    function IsReadOnly: boolean; virtual;
    // For Delphi compatibility it is called GetComponent instead of GetPersistent
    function GetComponent(Index: Integer): TPersistent;
    function GetUnitName(Index: Integer = 0): string;
    function GetPropTypeUnitName(Index: Integer = 0): string;
    function GetPropertyPath(Index: integer = 0): string;// e.g. 'TForm1.Color'
    function GetEditLimit: Integer; virtual;
    function GetName: shortstring; virtual;
    procedure GetProperties({%H-}Proc: TGetPropEditProc); virtual;
    function GetPropType: PTypeInfo;
    function GetPropInfo: PPropInfo;
    function GetInstProp: PInstProp;
    function GetEnumValueAt(Index: Integer): string;
    function GetFloatValue: Extended;
    function GetFloatValueAt(Index: Integer): Extended;
    function GetInt64Value: Int64;
    function GetInt64ValueAt(Index: Integer): Int64;
    function GetIntfValue: IInterface;
    function GetIntfValueAt(Index: Integer): IInterface;
    function GetMethodValue: TMethod;
    function GetMethodValueAt(Index: Integer): TMethod;
    function GetOrdValue: Longint;
    function GetOrdValueAt(Index: Integer): Longint;
    function GetObjectValue: TObject;
    function GetObjectValue(MinClass: TClass): TObject;
    function GetObjectValueAt(Index: Integer): TObject;
    function GetObjectValueAt(Index: Integer; MinClass: TClass): TObject;
    function GetDefaultOrdValue: Longint;
    function GetSetValue(Brackets: boolean): AnsiString;
    function GetSetValueAt(Index: Integer; Brackets: boolean): AnsiString;
    function GetStrValue: AnsiString;
    function GetStrValueAt(Index: Integer): AnsiString;
    function GetVarValue: Variant;
    function GetVarValueAt(Index: Integer):Variant;
    function GetWideStrValue: WideString;
    function GetWideStrValueAt(Index: Integer): WideString;
    function GetUnicodeStrValue: UnicodeString;
    function GetUnicodeStrValueAt(Index: Integer): UnicodeString;
    function GetValue: ansistring; virtual;
    function GetHint({%H-}HintType: TPropEditHint; {%H-}x, {%H-}y: integer): string; virtual;
    function HasDefaultValue: Boolean;
    function GetDefaultValue: ansistring; virtual;
    function CallStoredFunction: Boolean; virtual;
    function GetVisualValue: ansistring; virtual;
    procedure GetValues({%H-}Proc: TGetStrProc); virtual;
    procedure Initialize; virtual;
    procedure Revert; virtual;
    procedure RevertToInherited; virtual;
    procedure SetValue(const {%H-}NewValue: ansistring); virtual;
    procedure SetPropEntry(Index: Integer; AnInstance: TPersistent;
                           APropInfo: PPropInfo); virtual;
    procedure SetRecordFieldEntry(Index: Integer; AnInstance: Pointer;
                                  aName: PShortString; AFieldInfo: PTypeInfo); virtual;
    procedure SetFloatValue(const NewValue: Extended);
    procedure SetMethodValue(const NewValue: TMethod);
    procedure SetInt64Value(const NewValue: Int64);
    procedure SetIntfValue(const NewValue: IInterface);
    procedure SetOrdValue(const NewValue: Longint);
    procedure SetPtrValue(const NewValue: Pointer);
    procedure SetStrValue(const NewValue: AnsiString);
    procedure SetWideStrValue(const NewValue: WideString);
    procedure SetUnicodeStrValue(const NewValue: UnicodeString);
    procedure SetVarValue(const NewValue: Variant);
    procedure Modified(Index: integer = 0);
    function ValueAvailable: Boolean;
    procedure ListMeasureWidth(const {%H-}AValue: ansistring; {%H-}Index: Integer;
                               {%H-}ACanvas: TCanvas; var {%H-}AWidth: Integer); virtual;
    procedure ListMeasureHeight(const AValue: ansistring; {%H-}Index: Integer;
                                ACanvas: TCanvas; var AHeight: Integer); virtual;
    procedure ListDrawValue(const AValue: ansistring; {%H-}Index: Integer;
                            ACanvas: TCanvas; const ARect: TRect;
                            {%H-}AState: TPropEditDrawState); virtual;
    procedure PropMeasureHeight(const {%H-}NewValue: ansistring;  {%H-}ACanvas: TCanvas;
                                var {%H-}AHeight: Integer); virtual;
    procedure PropDrawName(ACanvas: TCanvas; const ARect: TRect;
                           {%H-}AState: TPropEditDrawState); virtual;
    procedure PropDrawValue(ACanvas: TCanvas; const ARect: TRect;
                            {%H-}AState: TPropEditDrawState); virtual;
    procedure UpdateSubProperties; virtual;
    function SubPropertiesNeedsUpdate: boolean; virtual;
    function ValueIsStreamed: boolean; virtual; // if value is stored, usually because it differs from default value
    function IsRevertableToInherited: boolean; virtual;
    // These are used for the popup menu in OI
    function GetVerbCount: Integer; virtual;
    function GetVerb(Index: Integer): string; virtual;
    procedure PrepareItem({%H-}Index: Integer; const {%H-}AnItem: TMenuItem); virtual;
    procedure ExecuteVerb({%H-}Index: Integer); virtual;
  public
    property PropertyHook: TPropertyEditorHook read FPropertyHook;
    property PrivateDirectory: ansistring read GetPrivateDirectory;
    property PropCount: Integer read FPropCount;
    property FirstValue: ansistring read GetValue write SetValue;
    property OnSubPropertiesChanged: TNotifyEvent
                     read FOnSubPropertiesChanged write FOnSubPropertiesChanged;
  end;

  TPropertyEditorClass = class of TPropertyEditor;
  TPropertyEditorList = specialize TFPGObjectList<TPropertyEditor>;

{ THiddenPropertyEditor
  A property editor to hide a published property. If you can't unpublish it, hide it. }
  
  THiddenPropertyEditor = class(TPropertyEditor)
  end;

{ TOrdinalPropertyEditor
  The base class of all ordinal property editors. It establishes that ordinal
  properties are all equal if the GetOrdValue all return the same value and
  provide methods to retrieve the default value. }

  TOrdinalPropertyEditor = class(TPropertyEditor)
  public
    function AllEqual: Boolean; override;
    function GetEditLimit: Integer; override;
    function GetValue: ansistring; override;
    function GetDefaultValue: ansistring; override;
    function OrdValueToVisualValue(OrdValue: longint): string; virtual;
  end;

{ TIntegerPropertyEditor
  Default editor for all Longint properties and all subtypes of the Longint
  type (i.e. Integer, Word, 1..10, etc.). Restricts the value entered into
  the property to the range of the sub-type. }

  TIntegerPropertyEditor = class(TOrdinalPropertyEditor)
  public
    function OrdValueToVisualValue(OrdValue: longint): string; override;
    procedure SetValue(const NewValue: ansistring);  override;
  end;

{ TCharPropertyEditor
  Default editor for all Char properties and sub-types of Char (i.e. Char,
  'A'..'Z', etc.). }

  TCharPropertyEditor = class(TOrdinalPropertyEditor)
  public
    function OrdValueToVisualValue(OrdValue: longint): string; override;
    procedure SetValue(const NewValue: ansistring); override;
  end;

{ TEnumPropertyEditor
  The default property editor for all enumerated properties (e.g. TShape =
  (sCircle, sTriangle, sSquare), etc.). }

  TEnumPropertyEditor = class(TOrdinalPropertyEditor)
  private
    FInvalid: Boolean;
  public
    function GetAttributes: TPropertyAttributes; override;
    function OrdValueToVisualValue(OrdValue: longint): string; override;
    function GetVisualValue: ansistring; override;
    procedure GetValues(Proc: TGetStrProc); override;
    procedure SetValue(const NewValue: ansistring); override;
  end;

type
  { TBoolPropertyEditor
    Default property editor for all boolean properties }

  TBoolPropertyEditor = class(TEnumPropertyEditor)
  public
    function OrdValueToVisualValue(OrdValue: longint): string; override;
    function GetVisualValue: ansistring; override;
    procedure GetValues(Proc: TGetStrProc); override;
    procedure SetValue(const NewValue: ansistring); override;
    procedure PropDrawValue(ACanvas: TCanvas; const ARect: TRect;
                            AState: TPropEditDrawState); override;
  end;

{ TInt64PropertyEditor
  Default editor for all Int64 properties and all subtypes of Int64. }

  TInt64PropertyEditor = class(TPropertyEditor)
  public
    function AllEqual: Boolean; override;
    function GetEditLimit: Integer; override;
    function GetValue: ansistring; override;
    procedure SetValue(const NewValue: ansistring); override;
  end;

{ TQWordPropertyEditor
  Default editor for all QWord properties }

  TQWordPropertyEditor = class(TInt64PropertyEditor)
  public
    function GetValue: ansistring; override;
    procedure SetValue(const NewValue: ansistring); override;
  end;

{ TFloatPropertyEditor
  The default property editor for all floating point types (e.g. Float,
  Single, Double, etc.) }

  TFloatPropertyEditor = class(TPropertyEditor)
  protected
    function FloatInRange(AValue: Extended; AType: TFloatType): Boolean;
  public
    function AllEqual: Boolean; override;
    function FormatValue(const AValue: Extended): ansistring;
    function GetDefaultValue: ansistring; override;
    function GetValue: ansistring; override;
    procedure SetValue(const NewValue: ansistring); override;
  end;

{ TStringPropertyEditor
  The default property editor for all strings and sub types (e.g. string,
  string[20], etc.). }

  TStringPropertyEditor = class(TPropertyEditor)
  public
    function AllEqual: Boolean; override;
    function GetEditLimit: Integer; override;
    function GetValue: ansistring; override;
    procedure SetValue(const NewValue: ansistring); override;
  end;

{ TPasswordStringPropertyEditor
  The default property editor for string passwords}

  TPasswordStringPropertyEditor = class(TStringPropertyEditor)
  public
    function GetPassword: string; virtual;
    procedure PropDrawValue(ACanvas: TCanvas; const ARect: TRect;
      AState: TPropEditDrawState); override;
  end;

{ TWideStringPropertyEditor
  The default property editor for widestrings}

  TWideStringPropertyEditor = class(TPropertyEditor)
  public
    function AllEqual: Boolean; override;
    function GetValue: ansistring; override;
    procedure SetValue(const NewValue: ansistring); override;
  end;

{ TPasswordWideStringPropertyEditor
  The default property editor for widestring passwords}

  TPasswordWideStringPropertyEditor = class(TWideStringPropertyEditor)
  public
    function GetPassword: WideString; virtual;
    procedure PropDrawValue(ACanvas: TCanvas; const ARect: TRect;
      AState: TPropEditDrawState); override;
  end;

{ TUnicodeStringPropertyEditor
  The default property editor for unicodestrings}

  TUnicodeStringPropertyEditor = class(TPropertyEditor)
  public
    function AllEqual: Boolean; override;
    function GetValue: ansistring; override;
    procedure SetValue(const NewValue: ansistring); override;
  end;

{ TNestedPropertyEditor
  A property editor that uses the PropertyHook, PropList and PropCount.
  The constructor and destructor do not call inherited, but all derived classes
  should. This is useful for properties like the TSetElementPropertyEditor. }

  TNestedPropertyEditor = class(TPropertyEditor)
  private
    FParentEditor: TPropertyEditor;
  public
    constructor Create(Parent: TPropertyEditor); overload;
    destructor Destroy; override;
    property ParentEditor: TPropertyEditor read FParentEditor;
  end;

{ TSetElementPropertyEditor
  A property editor that edits an individual set element. GetName is
  changed to display the set element name instead of the property name and
  Get/SetValue is changed to reflect the individual element state. This
  editor is created by the TSetPropertyEditor editor. }

  TSetElementPropertyEditor = class(TNestedPropertyEditor)
  private
    FElement: Integer;
  public
    constructor Create(Parent: TPropertyEditor; AElement: Integer); overload;
    function AllEqual: Boolean; override;
    function GetAttributes: TPropertyAttributes; override;
    function GetName: shortstring; override;
    function GetValue: ansistring; override;
    function GetVerbCount: Integer; override;
    function GetVisualValue: ansistring; override;
    procedure GetValues(Proc: TGetStrProc); override;
    procedure SetValue(const NewValue: ansistring); override;
    function ValueIsStreamed: boolean; override;
    procedure PropDrawValue(ACanvas: TCanvas; const ARect: TRect;
                            AState: TPropEditDrawState); override;
   end;

{ TSetPropertyEditor
  Default property editor for all set properties. This editor does not edit
  the set directly but will display sub-properties for each element of the
  set. GetValue displays the value of the set in standard set syntax. }

  TSetPropertyEditor = class(TOrdinalPropertyEditor)
  public
    function GetAttributes: TPropertyAttributes; override;
    function GetEditLimit: Integer; override;
    procedure GetProperties(Proc: TGetPropEditProc); override;
    procedure SetValue(const NewValue: ansistring); override;
    function OrdValueToVisualValue(OrdValue: longint): string; override;
  end;

  { TStructurePropertyEditor }

  TStructurePropertyEditor = class(TPropertyEditor)
  private
    FSubPropsTypeFilter: TTypeKinds;
    FSubPropsNameFilter: String;
    FSubProps: TObjectList;
  protected
    function EditorFilter(const AEditor: TPropertyEditor): Boolean; virtual;
    procedure ListSubProps(Prop: TPropertyEditor); virtual;
    procedure SetSubPropsTypeFilter(const AValue: TTypeKinds); virtual;
  public
    constructor Create(Hook: TPropertyEditorHook; APropCount: Integer); override;
    destructor Destroy; override;
    function AllEqual: Boolean; override;

    property SubPropsTypeFilter: TTypeKinds
      read FSubPropsTypeFilter write SetSubPropsTypeFilter default tkAny;
    property SubPropsNameFilter: String
      read FSubPropsNameFilter write FSubPropsNameFilter;
  end;

{ TClassPropertyEditor
  Default property editor for all objects. Does not allow modifying the
  property but does display the class name of the object and will allow the
  editing of the object's properties as sub-properties of the property. }

  TClassPropertyEditor = class(TStructurePropertyEditor)
  private
    FHideClassName: Boolean;
  protected
    function GetSelections: TPersistentSelectionList; virtual;
  public
    function ValueIsStreamed: boolean; override;
    function GetAttributes: TPropertyAttributes; override;
    procedure GetProperties(Proc: TGetPropEditProc); override;
    function GetValue: String; override;

    property HideClassName: Boolean read FHideClassName write FHideClassName;
  end;

{ TPersistentPropertyEditor
  A base editor for TPersistent. It does allow editing of the properties.
  It allows the user to set the value of this property to point to a component
  in any form or datamodule that is type compatible with the property being
  edited (e.g. the DataSource property). }

  TPersistentPropertyEditor = class(TClassPropertyEditor)
  private
    // Used in AllEqual of TComponentOneFormPropertyEditor and TComponentPropertyEditor.
    function ComponentsAllEqual: Boolean;
  protected
    function FilterFunc(const ATestEditor: TPropertyEditor): Boolean;
    function GetPersistentReference: TPersistent; virtual;
    function GetSelections: TPersistentSelectionList; override;
    function CheckNewValue({%H-}APersistent: TPersistent): boolean; virtual;
  public
    function AllEqual: Boolean; override;
    procedure Edit; override;
    function GetAttributes: TPropertyAttributes; override;
    function GetEditLimit: Integer; override;
    function GetValue: AnsiString; override;
    procedure GetValues(Proc: TGetStrProc); override;
    procedure SetValue(const NewValue: ansistring); override;
  end;

{ TComponentOneFormPropertyEditor
  An editor for TComponents. It allows the user to set the value of this
  property to point to a component in the same form that is type compatible
  with the property being edited (e.g. the ActiveControl property). }

  TComponentOneFormPropertyEditor = class(TPersistentPropertyEditor)
  protected
    fIgnoreClass: TControlClass;
  public
    function AllEqual: Boolean; override;
    procedure GetValues(Proc: TGetStrProc); override;
  end;

{ TCoolBarControlPropertyEditor -
  An editor for TComponents. It allows the user to set the value of this
  property to point to a component in the same form that is type compatible
  with the property being edited and is not a TCustomCoolBar
  (e.g. the TCoolBand.Control property).}

  TCoolBarControlPropertyEditor = class(TComponentOneFormPropertyEditor)
  public
    constructor Create(Hook: TPropertyEditorHook; APropCount: Integer); override;
  end;

{ TComponentPropertyEditor
  The default editor for TComponents. It allows the user to set the value of
  this property to point to a component in any form in the project that is
  type compatible with the property being edited. }

  TComponentPropertyEditor = class(TPersistentPropertyEditor)
  protected
    function GetComponentReference: TComponent; virtual;
  public
    function AllEqual: Boolean; override;
  end;

{ TInterfacePropertyEditor
  The default editor for interface references. It allows the user to set
  the value of this property to refer to an interface implemented by
  a component on the form (or via form linking) that is type compatible
  with the property being edited. }

  TInterfacePropertyEditor = class(TComponentPropertyEditor)
  private
  protected
    function GetComponent(const AInterface: IInterface): TComponent;
    function GetComponentReference: TComponent; override;
    function GetSelections: TPersistentSelectionList; override;
  public
    function AllEqual: Boolean; override;
    procedure Edit; override;
    function GetAttributes: TPropertyAttributes; override;
    procedure GetValues(Proc: TGetStrProc); override;
    procedure SetValue(const NewValue: string); override;
    function GetValue: AnsiString; override;
  end;

  { TNoteBookActiveControlPropertyEditor }

  TNoteBookActiveControlPropertyEditor = class(TComponentPropertyEditor)
  protected
    function CheckNewValue(APersistent: TPersistent): boolean; override;
  public
    function GetAttributes: TPropertyAttributes; override;
    procedure GetValues(Proc: TGetStrProc); override;
  end;

  { TPagesPropertyEditor
    PropertyEditor editor for the TNoteBook.Pages properties.
    Brings up a dialog with a Memo for entering pages. }

  TPagesPropEditorDlg = class;

  TPagesPropertyEditor = class(TClassPropertyEditor)
  public
    procedure AssignItems(OldItmes, NewItems: TStrings);
    procedure Edit; override;
    function CreateDlg(s: TStrings): TPagesPropEditorDlg; virtual;
    function GetAttributes: TPropertyAttributes; override;
  end;

  { TRecordPropertyEditor }

  TRecordPropertyEditor = class(TStructurePropertyEditor)
  private
    FCanReadFields: boolean;
    FCanWriteFields: boolean;
    FHideRecordName: Boolean;
  protected
    FRecordData: PByte; // depending on FRecordRead this points to a temp mem or the instance
    FReadAccess, FWriteAccess: Byte; // see ptField..ptConst
    procedure LoadRecord; virtual;
    procedure GetRecordData(const aMethod: TMethod; WithIndex: boolean; Index: Longint); virtual;
  public
    constructor Create(Hook: TPropertyEditorHook; APropCount: Integer); override;
    destructor Destroy; override;
    function AllEqual: Boolean; override;
    function GetAttributes: TPropertyAttributes; override;
    procedure GetProperties(Proc: TGetPropEditProc); override;
    function GetValue: string; override;
    procedure Initialize; override;
    function ValueIsStreamed: boolean; override;

    property HideRecordName: Boolean read FHideRecordName write FHideRecordName;
    property CanReadFields: boolean read FCanReadFields;
    property CanWriteFields: boolean read FCanWriteFields;
  end;

{ TMethodPropertyEditor
  Property editor for all method properties. }

  TMethodPropertyEditor = class(TPropertyEditor)
  private
    function GetTrimmedEventName: shortstring;
  public
    function AllEqual: Boolean; override;
    procedure Edit; override;
    procedure ShowValue; override;
    function GetAttributes: TPropertyAttributes; override;
    function GetEditLimit: Integer; override;
    function GetValue: ansistring; override;
    procedure GetValues(Proc: TGetStrProc); override;
    procedure SetValue(const NewValue: ansistring); override;
    function GetFormMethodName: shortstring;
    class function GetDefaultMethodName(Root, Component: TComponent;
        const RootClassName, ComponentName, PropName: shortstring): shortstring;
  end;

{ TComponentNamePropertyEditor
  Property editor for the Name property. It restricts the name property
  from being displayed when more than one component is selected. }

  TComponentNamePropertyEditor = class(TStringPropertyEditor)
  public
    function GetAttributes: TPropertyAttributes; override;
    function GetEditLimit: Integer; override;
    function GetValue: ansistring; override;
    procedure SetValue(const NewValue: ansistring); override;
  end;

{ TDatePropertyEditor
  Property editor for date portion of TDateTime type. }

  TDatePropertyEditor = class(TPropertyEditor)
  public
    function GetAttributes: TPropertyAttributes; override;
    function GetValue: string; override;
    procedure SetValue(const Value: string); override;
  end;

{ TTimePropertyEditor
  Property editor for time portion of TDateTime type. }

  TTimePropertyEditor = class(TPropertyEditor)
  public
    function GetAttributes: TPropertyAttributes; override;
    function GetValue: string; override;
    procedure SetValue(const Value: string); override;
  end;

{ TDateTimePropertyEditor
  Edits both date and time data simultaneously  }

  TDateTimePropertyEditor = class(TPropertyEditor)
  public
    function GetAttributes: TPropertyAttributes; override;
    function GetValue: string; override;
    procedure SetValue(const Value: string); override;
  end;

{ TVariantPropertyEditor }

  TVariantPropertyEditor = class(TPropertyEditor)
  public
    function GetAttributes: TPropertyAttributes; override;
    function GetValue: string; override;
    procedure SetValue(const {%H-}Value: string); override;
    procedure GetProperties({%H-}Proc:TGetPropEditProc); override;
  end;

{ TModalResultPropertyEditor }

  TModalResultPropertyEditor = class(TIntegerPropertyEditor)
  public
    function GetAttributes: TPropertyAttributes; override;
    function OrdValueToVisualValue(OrdValue: longint): string; override;
    procedure GetValues(Proc: TGetStrProc); override;
    procedure SetValue(const NewValue:ansistring); override;
  end;

{ TShortCutPropertyEditor
  Property editor the ShortCut property. Allows both typing in a short
  cut value or picking a short-cut value from a list. }

  TShortCutPropertyEditor = class(TOrdinalPropertyEditor)
  public
    procedure Edit; override;
    function GetAttributes: TPropertyAttributes; override;
    function OrdValueToVisualValue(OrdValue: longint): string; override;
    procedure GetValues(Proc: TGetStrProc); override;
    procedure SetValue(const Value: string); override;
  end;

{ TTabOrderPropertyEditor
  Property editor for the TabOrder property. Prevents the property from being
  displayed when more than one component is selected. }

  TTabOrderPropertyEditor = class(TIntegerPropertyEditor)
  public
    function GetAttributes: TPropertyAttributes; override;
  end;
  

{ TCaptionPropertyEditor
  Property editor for the Caption and Text properties. Updates the value of
  the property for each change instead on when the property is approved. }

  TCaptionPropertyEditor = class(TStringPropertyEditor)
  public
    function GetAttributes: TPropertyAttributes; override;
  end;


{ TMenuItemCaptionEditor
  MenuItem's Caption gets its own editor.
  It updates the MenuItem's name when it is turned into a separator. }

  TMenuItemCaptionEditor = class(TStringPropertyEditor)
  public
    procedure SetValue(const NewValue: ansistring); override;
  end;


{ TStringMultilinePropertyEditor
  PropertyEditor editor for a string property when the string can be
  multiline (e.g. TLabel.Caption, TControl.Hint).
  Brings up the dialog for entering text. }

  TStringMultilinePropertyEditor = class(TCaptionPropertyEditor)
  public
    procedure Edit; override;
    function GetAttributes: TPropertyAttributes; override;
  end;


{ TStringsPropertyEditor
  PropertyEditor editor for the TStrings properties.
  Brings up a dialog with a Memo for entering text. }
  
  TStringsPropEditorDlg = class;

  TStringsPropertyEditor = class(TClassPropertyEditor)
  public
    procedure Edit; override;
    function CreateDlg(s: TStrings): TStringsPropEditorDlg; virtual;
    function GetAttributes: TPropertyAttributes; override;
  end;


{ TValueListPropertyEditor
  PropertyEditor editor for the TStrings property of TValueListEditor.
  Brings up a dialog with a ValueListEditor for entering keys and values. }

  TKeyValPropEditorDlg = class;

  TValueListPropertyEditor = class(TClassPropertyEditor)
  public
    procedure Edit; override;
    function CreateDlg(s: TStrings): TKeyValPropEditorDlg; virtual;
    function GetAttributes: TPropertyAttributes; override;
  end;


{ TCursorPropertyEditor
  PropertyEditor editor for the TCursor properties.
  Displays cursor as constant name if exists, otherwise an integer. }

  TCursorPropertyEditor = class(TIntegerPropertyEditor)
  public
    function GetAttributes: TPropertyAttributes; override;
    function OrdValueToVisualValue(OrdValue: longint): string; override;
    procedure GetValues(Proc: TGetStrProc); override;
    procedure SetValue(const NewValue: ansistring); override;
  end;
  
  
{ TFileNamePropertyEditor
  PropertyEditor editor for filename properties.
  Show an TOpenDialog on Edit. }

  TFileNamePropertyEditor = class(TStringPropertyEditor)
  public
    function GetAttributes: TPropertyAttributes; override;
    procedure Edit; override;
    function GetFilter: String; virtual;
    function GetDialogOptions: TOpenOptions; virtual;
    function GetDialogTitle: string; virtual;
    function GetInitialDirectory: string; virtual;
    procedure SetFilename(const Filename: string); virtual;
    function CreateFileDialog: TOpenDialog; virtual;
  end;


{ TDirectoryPropertyEditor
  PropertyEditor editor for directory properties.
  Show an TSelectDirectoryDialog on Edit. }

  TDirectoryPropertyEditor = class(TFileNamePropertyEditor)
  public
    function CreateFileDialog: TOpenDialog; override;
  end;


{ TURLPropertyEditor
  PropertyEditor editor for URL properties.
  Show an TOpenDialog on Edit. }

  TURLPropertyEditor = class(TFileNamePropertyEditor)
  public
    procedure SetFilename(const Filename: string); override;
  end;


{ TURLDirectoryPropertyEditor
  PropertyEditor editor for URL properties.
  Show an TOpenDialog on Edit. }

  TURLDirectoryPropertyEditor = class(TURLPropertyEditor)
  public
    function CreateFileDialog: TOpenDialog; override;
  end;
  

{ TFileDlgFilterProperty
  PropertyEditor editor for TFileDialog filter properties.
  Show a dialog on Edit. }

  TFileDlgFilterProperty = class(TStringPropertyEditor)
  public
    function  GetAttributes: TPropertyAttributes; override;
    procedure Edit; override;
  end;


{ TSessionPropertiesPropertyEditor
  PropertyEditor editor for TControl.SessionProperties properties.
  Show a dialog on Edit. }

  TSessionPropertiesPropertyEditor = class(TStringPropertyEditor)
  public
    function GetAttributes: TPropertyAttributes; override;
    procedure Edit; override;
  end;

  
{ TConstraintsPropertyEditor
  PropertyEditor editor for TControl.Constraints properties.
  Lets a user set the current size as constraints. }

  TConstraintsPropertyEditor = class(TClassPropertyEditor)
  public
    // These are used for the popup menu in OI
    function GetVerbCount: Integer; override;
    function GetVerb(Index: Integer): string; override;
    procedure PrepareItem(Index: Integer; const AnItem: TMenuItem); override;
    procedure ExecuteVerb(Index: Integer); override;
  end;


{ TListElementPropertyEditor
  A property editor for a single element of a TListPropertyEditor
  This editor simply redirects all methods to the TListPropertyEditor }
  TListPropertyEditor = class;

  TListElementPropertyEditor = class(TNestedPropertyEditor)
  private
    FIndex: integer;
    FList: TListPropertyEditor;
  public
    constructor Create(Parent: TListPropertyEditor; AnIndex: integer); overload;
    destructor Destroy; override;
    function GetAttributes: TPropertyAttributes; override;
    function GetName:shortstring; override;
    procedure GetProperties(Proc: TGetPropEditProc); override;
    function GetValue: ansistring; override;
    procedure GetValues(Proc: TGetStrProc); override;
    procedure SetValue(const NewValue: ansistring); override;
    property List: TListPropertyEditor read FList;
    property TheIndex: integer read FIndex;
  end;

{ TListPropertyEditor
  A property editor with dynamic sub properties representing a list of objects.
  The items are shown embedded in the OI and if the user presses the Edit button
  as extra window to select items, which are then shown in the OI.
  UNDER CONSTRUCTION by Mattias
  The problem with all properties is, that we don't get notified, when something
  changes. In this case, the list can change, which means the property editors
  for the list elements must be deleted or created.
  }

  TListPropertyEditor = class(TPropertyEditor)
  private
    FSaveElementLock: integer;
    FSubPropertiesChanged: boolean;
  protected
    procedure BeginSaveElement;
    procedure EndSaveElement;
    function IsSaving: boolean;
    property SaveElementLock: integer read FSaveElementLock;
  protected
    // methods and variables usable for descendent property editors:
    // MWE: hmm... don't like "public" objects
    // TODO: change this ?
    SavedList: TObject;
    SavedElements: TList;
    SavedPropertyEditors: TList;
    function ReadElementCount: integer; virtual;
    function ReadElement(Index: integer): TPersistent; virtual;
    function CreateElementPropEditor(
      Index: integer): TListElementPropertyEditor; virtual;
    procedure DoSaveElements; virtual;
    procedure FreeElementPropertyEditors; virtual;
    function GetElementAttributes(
      {%H-}Element: TListElementPropertyEditor): TPropertyAttributes; virtual;
    function GetElementName(
      {%H-}Element: TListElementPropertyEditor):shortstring; virtual;
    procedure GetElementProperties({%H-}Element: TListElementPropertyEditor;
      {%H-}Proc: TGetPropEditProc); virtual;
    function GetElementValue(
      {%H-}Element: TListElementPropertyEditor): ansistring; virtual;
    procedure GetElementValues({%H-}Element: TListElementPropertyEditor;
      {%H-}Proc: TGetStrProc); virtual;
    procedure SetElementValue({%H-}Element: TListElementPropertyEditor;
      {%H-}NewValue: ansistring); virtual;
  public
    constructor Create(Hook:TPropertyEditorHook; APropCount:Integer); override;
    destructor Destroy; override;
    function GetAttributes: TPropertyAttributes; override;
    function GetElementCount: integer;
    function GetElement(Index: integer): TPersistent;
    function GetElement(Element: TListElementPropertyEditor): TPersistent;
    function GetElementPropEditor(Index: integer): TListElementPropertyEditor;
    procedure GetProperties(Proc: TGetPropEditProc); override;
    function GetValue: AnsiString; override;
    procedure Initialize; override;
    procedure SaveElements;
    function SubPropertiesNeedsUpdate: boolean; override;
  end;

{ TCollectionPropertyEditor
  Default property editor for all TCollections, embedded in the OI
  UNDER CONSTRUCTION by Mattias}

  TCollectionPropertyEditor = class(TListPropertyEditor)
  private
  protected
    function ReadElementCount: integer; override;
    function ReadElement(Index: integer): TPersistent; override;
    function GetElementAttributes(
      {%H-}Element: TListElementPropertyEditor): TPropertyAttributes; override;
    function GetElementName(
      Element: TListElementPropertyEditor):shortstring; override;
    procedure GetElementProperties(Element: TListElementPropertyEditor;
      Proc: TGetPropEditProc); override;
    function GetElementValue(
      Element: TListElementPropertyEditor): ansistring; override;
    procedure GetElementValues(Element: TListElementPropertyEditor;
      Proc: TGetStrProc); override;
    procedure SetElementValue(Element: TListElementPropertyEditor;
      NewValue: ansistring); override;
  public
    function GetAttributes: TPropertyAttributes; override;
    procedure Edit; override;
    class function ShowCollectionEditor(ACollection: TCollection; 
      OwnerPersistent: TPersistent; const PropName: String): TCustomForm; virtual;
  end;

  { TDisabledCollectionPropertyEditor }

  TDisabledCollectionPropertyEditor = class(TCollectionPropertyEditor)
  public
    function GetAttributes: TPropertyAttributes; override;
  end;

  { TNoAddDeleteCollectionPropertyEditor }

  TNoAddDeleteCollectionPropertyEditor = class(TCollectionPropertyEditor)
  public
    class function ShowCollectionEditor(ACollection: TCollection;
      OwnerPersistent: TPersistent; const PropName: String): TCustomForm; override;
  end;

//==============================================================================
// Delphi Compatible Property Editor Classnames

type
  TOrdinalProperty =        TOrdinalPropertyEditor;
  TIntegerProperty =        TIntegerPropertyEditor;
  TCharProperty =           TCharPropertyEditor;
  TEnumProperty =           TEnumPropertyEditor;
  TBoolProperty =           TBoolPropertyEditor;
  TInt64Property =          TInt64PropertyEditor;
  TFloatProperty =          TFloatPropertyEditor;
  TStringProperty =         TStringPropertyEditor;
  TNestedProperty =         TNestedPropertyEditor;
  TSetElementProperty =     TSetElementPropertyEditor;
  TSetProperty =            TSetPropertyEditor;
  TClassProperty =          TClassPropertyEditor;
  TMethodProperty =         TMethodPropertyEditor;
  TComponentProperty =      TPersistentPropertyEditor;
  TComponentNameProperty =  TComponentNamePropertyEditor;
//  TImeNameProperty =        TImeNamePropertyEditor;
  TCursorProperty =         TCursorPropertyEditor;
  TModalResultProperty =    TModalResultPropertyEditor;
  TShortCutProperty =       TShortCutPropertyEditor;
//  TMPFilenameProperty =     TMPFilenamePropertyEditor;
  TTabOrderProperty =       TTabOrderPropertyEditor;
  TCaptionProperty =        TCaptionPropertyEditor;
  TDateProperty =           TDatePropertyEditor;
  TTimeProperty =           TTimePropertyEditor;
  TDateTimeProperty =       TDateTimePropertyEditor;


type
  TSelectionEditorAttribute = (
    seaFilterProperties
  );
  TSelectionEditorAttributes = set of TSelectionEditorAttribute;

  { TBaseSelectionEditor }

  TBaseSelectionEditor = class
    constructor Create({%H-}ADesigner: TIDesigner; {%H-}AHook: TPropertyEditorHook); virtual;
    function GetAttributes: TSelectionEditorAttributes; virtual; abstract;
    procedure FilterProperties(ASelection: TPersistentSelectionList; AProperties: TPropertyEditorList); virtual; abstract;
  end;

  TSelectionEditorClass = class of TBaseSelectionEditor;

  TSelectionEditorClassList = specialize TFPGList<TSelectionEditorClass>;

  { TSelectionEditor }

  TSelectionEditor = class(TBaseSelectionEditor)
  private
    FDesigner: TIDesigner;
    FHook: TPropertyEditorHook;
  public
    constructor Create(ADesigner: TIDesigner; AHook: TPropertyEditorHook); override;
    function GetAttributes: TSelectionEditorAttributes; override;
    procedure FilterProperties({%H-}ASelection: TPersistentSelectionList; {%H-}AProperties: TPropertyEditorList); override;
    property Designer: TIDesigner read FDesigner;
    property Hook: TPropertyEditorHook read FHook;
  end;


//==============================================================================

{ RegisterPropertyEditor
  Registers a new property editor for the given type.
  When a component is selected the Object Inspector will create a property
  editor for each of the component's properties. The property editor is created
  based on the type of the property. If, for example, the property type is an
  Integer, the property editor for Integer will be created (by default
  that would be TIntegerPropertyEditor). Most properties do not need specialized
  property editors.
  For example, if the property is an ordinal type the default property editor
  will restrict the range to the ordinal subtype range (e.g. a property of type
  TMyRange=1..10 will only allow values between 1 and 10 to be entered into the
  property). Enumerated types will display a drop-down list of all the
  enumerated values (e.g. TShapes = (sCircle,sSquare,sTriangle) will be edited
  by a drop-down list containing only sCircle,sSquare and sTriangle).
  A property editor needs only be created if default property editor or none of
  the existing property editors are sufficient to edit the property. This is
  typically because the property is an object.
  The registered types are looked up newest to oldest.
  This allows an existing property editor replaced by a custom property editor.

    PropertyEditorType
      The type information pointer returned by the TypeInfo built-in function
      (e.g. TypeInfo(TMyRange) or TypeInfo(TShapes)).

    PersistentClass
      Type of the persistent object to which to restrict this type editor. This
      parameter can be left nil which will mean this type editor applies to all
      properties of PropertyEditorType.

    PropertyEditorName
      The name of the property to which to restrict this type editor. This
      parameter is ignored if PersistentClass is nil. This parameter can be
      an empty string ('') which will mean that this editor applies to all
      properties of PropertyEditorType in PersistentClass.

    editorClass
      The class of the editor to be created whenever a property of the type
      passed in PropertyEditorTypeInfo is displayed in the Object Inspector.
      The class will be created by calling EditorClass.Create. }

procedure RegisterPropertyEditor(PropertyType: PTypeInfo;
  PersistentClass: TClass;  const PropertyName: shortstring;
  EditorClass: TPropertyEditorClass);

type
  TPropertyEditorMapperFunc=function(Obj: TPersistent;
    PropInfo: PPropInfo): TPropertyEditorClass;
const
  AllTypeKinds = [tkInteger..High(TTypeKind)];

procedure RegisterPropertyEditorMapper(Mapper:TPropertyEditorMapperFunc);

type
  TPropertyEditorFilterFunc =
    function(const ATestEditor: TPropertyEditor): Boolean of object;
  TPropInfoFilterFunc =
    function(const APropInfo: PPropInfo): Boolean of object;

procedure GetPersistentProperties(ASelection: TPersistentSelectionList;
  AFilter: TTypeKinds; AHook: TPropertyEditorHook; AProc: TGetPropEditProc;
  APropInfoFilterFunc: TPropInfoFilterFunc;
  AEditorFilterFunc: TPropertyEditorFilterFunc
  {$IFDEF HasExtRtti};ExtVisibility: TVisibilityClasses = []{$ENDIF});

procedure GetPersistentProperties(ASelection: TPersistentSelectionList;
  AFilter: TTypeKinds; AHook: TPropertyEditorHook; AProc: TGetPropEditProc;
  AEditorFilterFunc: TPropertyEditorFilterFunc);

procedure GetPersistentProperties(AItem: TPersistent;
  AFilter: TTypeKinds; AHook: TPropertyEditorHook; AProc: TGetPropEditProc;
  AEditorFilterFunc: TPropertyEditorFilterFunc);

function GetEditorClass(PropInfo: PPropInfo; Obj: TPersistent): TPropertyEditorClass;
function GetEditorClass(PropName: shortstring; Info: PTypeInfo; Obj: TPersistent): TPropertyEditorClass;

//==============================================================================

procedure RegisterSelectionEditor(AComponentClass: TComponentClass; AEditorClass: TSelectionEditorClass);
procedure GetSelectionEditorClasses(AComponent: TComponent; AEditorList: TSelectionEditorClassList);
procedure GetSelectionEditorClasses(ASelection: TPersistentSelectionList; AEditorList: TSelectionEditorClassList);

//==============================================================================

procedure RegisterListPropertyEditor(AnEditor: TListPropertyEditor);
procedure UnregisterListPropertyEditor(AnEditor: TListPropertyEditor);
procedure UpdateListPropertyEditors(AnObject: TObject);

type
  TSelectableComponentFlag = (
    scfWithoutRoot,
    scfWithoutInlineChilds
  );
  TSelectableComponentFlags = set of TSelectableComponentFlag;

procedure GetSelectableComponents(Root: TComponent;
  Flags: TSelectableComponentFlags; var ComponentList: TFPList);

//==============================================================================
{
  TPropertyEditorHook

  This is the interface for methods, components and objects handling of all
  property editors. Just create such thing and give it the object inspector.
}
type
  // lookup root
  TPropHookChangeLookupRoot = procedure of object;
  // methods
  TPropHookCreateMethod = function(const Name: ShortString; ATypeInfo: PTypeInfo;
      APersistent: TPersistent; const APropertyPath: string): TMethod of object;
  TPropHookGetMethodName = function(const Method: TMethod; CheckOwner: TObject;
      OrigLookupRoot: TPersistent): String of object;
  TPropHookGetCompatibleMethods = procedure(InstProp: PInstProp; const Proc: TGetStrProc) of object;
  TPropHookGetMethods = procedure(TypeData: PTypeData; Proc: TGetStrProc) of object;
  TPropHookCompatibleMethodExists = function(const Name: String; InstProp: PInstProp;
                 var MethodIsCompatible,MethodIsPublished,IdentIsMethod: boolean
                 ):boolean of object;
  TPropHookMethodExists = function(const Name: String; TypeData: PTypeData;
                 var MethodIsCompatible,MethodIsPublished,IdentIsMethod: boolean
                 ):boolean of object;
  TPropHookRenameMethod = procedure(const CurName, NewName: String) of object;
  TPropHookShowMethod = procedure(const Name: String) of object;
  TPropHookMethodFromAncestor = function(const Method:TMethod):boolean of object;
  TPropHookMethodFromLookupRoot = function(const Method:TMethod):boolean of object;
  TPropHookChainCall = procedure(const AMethodName, InstanceName,
                      InstanceMethod:ShortString; TypeData:PTypeData) of object;
  // components
  TPropHookGetComponent = function(const ComponentPath: String):TComponent of object;
  TPropHookGetComponentName = function(AComponent: TComponent):String of object;
  TPropHookGetComponentNames = procedure(TypeData: PTypeData;
                                         Proc: TGetStrProc) of object;
  TPropHookGetRootClassName = function:ShortString of object;
  TPropHookGetAncestorInstProp = function(const InstProp: TInstProp;
                            out AncestorInstProp: TInstProp): boolean of object;
  TPropHookAddClicked = function(ADesigner: TIDesigner;
                            MouseDownComponent: TComponent; Button: TMouseButton;
                            Shift: TShiftState; X, Y: Integer;
                            var AComponentClass: TComponentClass;
                            var NewParent: TComponent): boolean of object;
  TPropHookBeforeAddPersistent = function(Sender: TObject;
                                         APersistentClass: TPersistentClass;
                                         Parent: TPersistent): boolean of object;
  TPropHookComponentRenamed = procedure(AComponent: TComponent) of object;
  TPropHookPersistentAdded = procedure(APersistent: TPersistent; Select: boolean
                                      ) of object;
  TPropHookPersistentDel = procedure(APersistent: TPersistent) of object;
  TPropHookDeletePersistent = procedure(var APersistent: TPersistent) of object;
  TPropHookGetSelection = procedure(const ASelection: TPersistentSelectionList
                                             ) of object;
  TPropHookSetSelection = procedure(const ASelection: TPersistentSelectionList
                                             ) of object;
  TPropHookAddDependency = procedure(const AClass: TClass;
                                     const AnUnitName: shortstring) of object;
  // persistent objects
  TPropHookGetObject = function(const Name:ShortString):TPersistent of object;
  TPropHookGetObjectName = function(Instance:TPersistent):ShortString of object;
  TPropHookGetObjectNames = procedure(TypeData:PTypeData;
                                      Proc: TGetStrProc) of object;
  TPropHookObjectPropertyChanged = procedure(Sender: TObject;
                                             NewObject: TPersistent) of object;
  // modifing
  TPropHookModified = procedure(Sender: TObject) of object;
  TPropHookModifiedWithName = procedure(Sender: TObject; PropName: ShortString) of object;
  TPropHookRevert = procedure(Instance:TPersistent; PropInfo:PPropInfo) of object;
  TPropHookRefreshPropertyValues = procedure of object;
  // other
  TPropHookGetCheckboxForBoolean = procedure(var Value: Boolean) of object;

  TPropHookType = (
    // lookup root
    htChangeLookupRoot,
    // methods
    htCreateMethod,
    htGetMethodName,
    htGetCompatibleMethods,
    htGetMethods,
    htCompatibleMethodExists,
    htMethodExists,
    htRenameMethod,
    htShowMethod,
    htMethodFromAncestor,
    htMethodFromLookupRoot,
    htChainCall,
    // components
    htGetComponent,
    htGetComponentName,
    htGetComponentNames,
    htGetRootClassName,
    htGetAncestorInstProp,
    htAddClicked, // user selected a component class and clicked on a form to add a component
    htComponentRenamed,
    // persistent selection
    htBeforeAddPersistent,
    htPersistentAdded,
    htPersistentDeleting,
    htPersistentDeleted,
    htDeletePersistent,
    htGetSelectedPersistents,
    htSetSelectedPersistents,
    // persistent objects
    htGetObject,
    htGetObjectName,
    htGetObjectNames,
    htObjectPropertyChanged,
    // modifing
    htModified,
    htModifiedWithName,
    htRevert,
    htRefreshPropertyValues,
    // dependencies
    htAddDependency,
    // designer
    htDesignerMouseDown,
    htDesignerMouseUp,
    // other
    htGetCheckboxForBoolean
    );

  { TPropertyEditorHook }

  TPropertyEditorHook = class(TComponent)
  private
    FComponentPropertyOnlyDesign: boolean;
    FHandlers: array[TPropHookType] of TMethodList;
    // lookup root
    FLookupRoot: TPersistent;

    procedure SetLookupRoot(APersistent: TPersistent);
    procedure AddHandler(HookType: TPropHookType; const Handler: TMethod);
    procedure RemoveHandler(HookType: TPropHookType; const Handler: TMethod);
    function GetHandlerCount(HookType: TPropHookType): integer;
    function GetNextHandlerIndex(HookType: TPropHookType;
                                 var i: integer): boolean;
  protected
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  public
    GetPrivateDirectory: AnsiString;
    destructor Destroy; override;

    // lookup root
    property LookupRoot: TPersistent read FLookupRoot write SetLookupRoot;
    // methods
    function CreateMethod(const aName: ShortString; ATypeInfo:PTypeInfo;
                          APersistent: TPersistent;
                          const APropertyPath: string): TMethod;
    function GetMethodName(const Method: TMethod; PropOwner: TObject): String;
    procedure GetMethods(TypeData: PTypeData; const Proc: TGetStrProc);
    procedure GetCompatibleMethods(InstProp: PInstProp; const Proc: TGetStrProc);
    function MethodExists(const aName: String; TypeData: PTypeData;
      var MethodIsCompatible,MethodIsPublished,IdentIsMethod: boolean):boolean;
    function CompatibleMethodExists(const aName: String; InstProp: PInstProp;
      out MethodIsCompatible,MethodIsPublished,IdentIsMethod: boolean):boolean;
    procedure RenameMethod(const CurName, NewName: String);
    procedure ShowMethod(const aName: String);
    function MethodFromAncestor(const Method: TMethod): boolean;
    function MethodFromLookupRoot(const Method: TMethod): boolean;
    procedure ChainCall(const AMethodName, InstanceName,
                        InstanceMethod: ShortString;  TypeData: PTypeData);
    // components
    function GetComponent(const ComponentPath: string): TComponent;
    function GetComponentName(AComponent: TComponent): String;
    procedure GetComponentNames(TypeData: PTypeData; const Proc: TGetStrProc);
    function GetRootClassName: ShortString;
    function GetAncestorInstance(const InstProp: TInstProp;
                                 out AncestorInstProp: TInstProp): boolean;
    function AddClicked(ADesigner: TIDesigner;
                        MouseDownComponent: TComponent; Button: TMouseButton;
                        Shift: TShiftState; X, Y: Integer;
                        var AComponentClass: TComponentClass;
                        var NewParent: TComponent): boolean;
    function BeforeAddPersistent(Sender: TObject;
                                 APersistentClass: TPersistentClass;
                                 Parent: TPersistent): boolean;
    procedure ComponentRenamed(AComponent: TComponent);
    procedure PersistentAdded(APersistent: TPersistent; Select: boolean);
    procedure PersistentDeleting(APersistent: TPersistent);
    procedure PersistentDeleted(APersistent: TPersistent);
    procedure DeletePersistent(var APersistent: TPersistent);
    procedure GetSelection(const ASelection: TPersistentSelectionList);
    procedure SetSelection(const ASelection: TPersistentSelectionList);
    procedure Unselect(const APersistent: TPersistent);
    function IsSelected(const APersistent: TPersistent): boolean;
    procedure SelectOnlyThis(const APersistent: TPersistent);
    procedure DesignerMouseDown(Sender: TObject; Button: TMouseButton;
                        Shift: TShiftState; X, Y: Integer);
    procedure DesignerMouseUp(Sender: TObject; Button: TMouseButton;
                        Shift: TShiftState; X, Y: Integer);
    // persistent objects
    function GetObject(const aName: ShortString): TPersistent;
    function GetObjectName(Instance: TPersistent; AOwnerComp: TComponent): String;
    procedure GetObjectNames(TypeData: PTypeData; const Proc: TGetStrProc);
    procedure ObjectReferenceChanged(Sender: TObject; NewObject: TPersistent);
    // modifing
    procedure Modified(Sender: TObject; PropName: ShortString = '');
    procedure Revert(Instance: TPersistent; PropInfo: PPropInfo);
    procedure RefreshPropertyValues;
    property ComponentPropertyOnlyDesign: boolean read FComponentPropertyOnlyDesign write FComponentPropertyOnlyDesign;
    // dependencies
    procedure AddDependency(const AClass: TClass; const AnUnitname: shortstring);
    // other
    function GetCheckboxForBoolean: Boolean;
  public
    // Handlers
    procedure RemoveAllHandlersForObject(const HandlerObject: TObject);

    // lookup root
    procedure AddHandlerChangeLookupRoot(
                           const OnChangeLookupRoot: TPropHookChangeLookupRoot);
    procedure RemoveHandlerChangeLookupRoot(
                           const OnChangeLookupRoot: TPropHookChangeLookupRoot);
    // method events
    procedure AddHandlerCreateMethod(const OnCreateMethod: TPropHookCreateMethod);
    procedure RemoveHandlerCreateMethod(const OnCreateMethod: TPropHookCreateMethod);
    procedure AddHandlerGetMethodName(const OnGetMethodName: TPropHookGetMethodName);
    procedure RemoveHandlerGetMethodName(const OnGetMethodName: TPropHookGetMethodName);
    procedure AddHandlerGetCompatibleMethods(
                             const OnGetMethods: TPropHookGetCompatibleMethods);
    procedure RemoveHandlerGetCompatibleMethods(
                             const OnGetMethods: TPropHookGetCompatibleMethods);
    procedure AddHandlerGetMethods(const OnGetMethods: TPropHookGetMethods);
    procedure RemoveHandlerGetMethods(const OnGetMethods: TPropHookGetMethods);
    procedure AddHandlerCompatibleMethodExists(
                         const OnMethodExists: TPropHookCompatibleMethodExists);
    procedure RemoveHandlerCompatibleMethodExists(
                         const OnMethodExists: TPropHookCompatibleMethodExists);
    procedure AddHandlerMethodExists(const OnMethodExists: TPropHookMethodExists);
    procedure RemoveHandlerMethodExists(const OnMethodExists: TPropHookMethodExists);
    procedure AddHandlerRenameMethod(const OnRenameMethod: TPropHookRenameMethod);
    procedure RemoveHandlerRenameMethod(const OnRenameMethod: TPropHookRenameMethod);
    procedure AddHandlerShowMethod(const OnShowMethod: TPropHookShowMethod);
    procedure RemoveHandlerShowMethod(const OnShowMethod: TPropHookShowMethod);
    procedure AddHandlerMethodFromAncestor(
                       const OnMethodFromAncestor: TPropHookMethodFromAncestor);
    procedure RemoveHandlerMethodFromAncestor(
                       const OnMethodFromAncestor: TPropHookMethodFromAncestor);
    procedure AddHandlerMethodFromLookupRoot(
                       const OnMethodFromLookupRoot: TPropHookMethodFromLookupRoot);
    procedure RemoveHandlerMethodFromLookupRoot(
                       const OnMethodFromLookupRoot: TPropHookMethodFromLookupRoot);
    procedure AddHandlerChainCall(const OnChainCall: TPropHookChainCall);
    procedure RemoveHandlerChainCall(const OnChainCall: TPropHookChainCall);
    // component event
    procedure AddHandlerGetComponent(const OnGetComponent: TPropHookGetComponent);
    procedure RemoveHandlerGetComponent(const OnGetComponent: TPropHookGetComponent);
    procedure AddHandlerGetComponentName(
                           const OnGetComponentName: TPropHookGetComponentName);
    procedure RemoveHandlerGetComponentName(
                           const OnGetComponentName: TPropHookGetComponentName);
    procedure AddHandlerGetComponentNames(
                         const OnGetComponentNames: TPropHookGetComponentNames);
    procedure RemoveHandlerGetComponentNames(
                         const OnGetComponentNames: TPropHookGetComponentNames);
    procedure AddHandlerAddClicked(const Handler: TPropHookAddClicked);
    procedure RemoveHandlerAddClicked(const Handler: TPropHookAddClicked);
    procedure AddHandlerGetRootClassName(
                           const OnGetRootClassName: TPropHookGetRootClassName);
    procedure RemoveHandlerGetRootClassName(
                           const OnGetRootClassName: TPropHookGetRootClassName);
    procedure AddHandlerGetAncestorInstProp(
                     const OnGetAncestorInstProp: TPropHookGetAncestorInstProp);
    procedure RemoveHandlerGetAncestorInstProp(
                     const OnGetAncestorInstProp: TPropHookGetAncestorInstProp);
    procedure AddHandlerDesignerMouseDown(const OnMouseDown: TMouseEvent);
    procedure RemoveHandlerDesignerMouseDown(const OnMouseDown: TMouseEvent);
    procedure AddHandlerDesignerMouseUp(const OnMouseUp: TMouseEvent);
    procedure RemoveHandlerDesignerMouseUp(const OnMouseUp: TMouseEvent);
    // component create, delete, rename
    procedure AddHandlerComponentRenamed(
                           const OnComponentRenamed: TPropHookComponentRenamed);
    procedure RemoveHandlerComponentRenamed(
                           const OnComponentRenamed: TPropHookComponentRenamed);
    procedure AddHandlerBeforeAddPersistent(
                     const OnBeforeAddPersistent: TPropHookBeforeAddPersistent);
    procedure RemoveHandlerBeforeAddPersistent(
                     const OnBeforeAddPersistent: TPropHookBeforeAddPersistent);
    procedure AddHandlerPersistentAdded(
                             const OnPersistentAdded: TPropHookPersistentAdded);
    procedure RemoveHandlerPersistentAdded(
                             const OnPersistentAdded: TPropHookPersistentAdded);
    procedure AddHandlerPersistentDeleting(
                       const OnPersistentDeleting: TPropHookPersistentDel);
    procedure RemoveHandlerPersistentDeleting(
                       const OnPersistentDeleting: TPropHookPersistentDel);
    procedure AddHandlerPersistentDeleted(
                       const OnPersistentDeleted: TPropHookPersistentDel);
    procedure RemoveHandlerPersistentDeleted(
                       const OnPersistentDeleted: TPropHookPersistentDel);
    procedure AddHandlerDeletePersistent(
                           const OnDeletePersistent: TPropHookDeletePersistent);
    procedure RemoveHandlerDeletePersistent(
                           const OnDeletePersistent: TPropHookDeletePersistent);
    // persistent selection
    procedure AddHandlerGetSelection(const OnGetSelection: TPropHookGetSelection);
    procedure RemoveHandlerGetSelection(const OnGetSelection: TPropHookGetSelection);
    procedure AddHandlerSetSelection(const OnSetSelection: TPropHookSetSelection);
    procedure RemoveHandlerSetSelection(const OnSetSelection: TPropHookSetSelection);
    // persistent object events
    procedure AddHandlerGetObject(const OnGetObject: TPropHookGetObject);
    procedure RemoveHandlerGetObject(const OnGetObject: TPropHookGetObject);
    procedure AddHandlerGetObjectName(const OnGetObjectName: TPropHookGetObjectName);
    procedure RemoveHandlerGetObjectName(const OnGetObjectName: TPropHookGetObjectName);
    procedure AddHandlerGetObjectNames(const OnGetObjectNames: TPropHookGetObjectNames);
    procedure RemoveHandlerGetObjectNames(const OnGetObjectNames: TPropHookGetObjectNames);
    procedure AddHandlerObjectPropertyChanged(
                 const OnObjectPropertyChanged: TPropHookObjectPropertyChanged);
    procedure RemoveHandlerObjectPropertyChanged(
                 const OnObjectPropertyChanged: TPropHookObjectPropertyChanged);
    // modifing events
    procedure AddHandlerModified(const OnModified: TPropHookModified);
    procedure RemoveHandlerModified(const OnModified: TPropHookModified);
    procedure AddHandlerModifiedWithName(const OnModified: TPropHookModifiedWithName);
    procedure RemoveHandlerModifiedWithName(const OnModified: TPropHookModifiedWithName);
    procedure AddHandlerRevert(const OnRevert: TPropHookRevert);
    procedure RemoveHandlerRevert(const OnRevert: TPropHookRevert);
    procedure AddHandlerRefreshPropertyValues(
                 const OnRefreshPropertyValues: TPropHookRefreshPropertyValues);
    procedure RemoveHandlerRefreshPropertyValues(
                 const OnRefreshPropertyValues: TPropHookRefreshPropertyValues);
    procedure AddHandlerAddDependency(const OnAddDependency: TPropHookAddDependency);
    procedure RemoveHandlerAddDependency(const OnAddDependency: TPropHookAddDependency);
    procedure AddHandlerGetCheckboxForBoolean(
                 const OnGetCheckboxForBoolean: TPropHookGetCheckboxForBoolean);
  end;

//==============================================================================

{ TPropInfoList }

type
  TPropInfoList = class
  private
    FList: PPropList;
    FCount: Integer;
    {$IFDEF HasExtRtti}
    FExtVisibility: TVisibilityClasses;
    FListExt: PPropListEx;
    function GetExt(Index: Integer): PPropInfoEx;
    {$ENDIF}
    function Get(Index: Integer): PPropInfo;
  public
    constructor Create(Instance: TPersistent; Filter: TTypeKinds);
    {$IFDEF HasExtRtti}
    constructor Create(Instance: TPersistent; Filter: TTypeKinds; AddExtVisibility: TVisibilityClasses);
    {$ENDIF}
    destructor Destroy; override;
    function Contains(P: PPropInfo): Boolean;
    procedure Delete(Index: Integer);
    procedure Intersect(List: TPropInfoList);
    procedure Sort;
    property Count: Integer read FCount;
    property Items[Index: Integer]: PPropInfo read Get; default;
    {$IFDEF HasExtRtti}
    property ExtVisibility: TVisibilityClasses read FExtVisibility;
    property Ext[Index: Integer]: PPropInfoEx read GetExt;
    {$ENDIF}
  end;

//==============================================================================

type
  TStringsPropEditorDlg = class(TStringsPropEditorFrm)
  public
    Editor: TPropertyEditor;
  end;

  TKeyValPropEditorDlg = class(TKeyValPropEditorFrm)
  public
    Editor: TPropertyEditor;
  end;

  TPagesPropEditorDlg = class(TPagesPropEditorFrm)
  public
    Editor: TPropertyEditor;
  end;

  { TCustomShortCutGrabBox }

  TCustomShortCutGrabBox = class(TCustomPanel)
  private
    FAllowedShifts: TShiftState;
    FGrabButton: TButton;
    FMainOkButton: TCustomButton;
    FKey: Word;
    FKeyComboBox: TComboBox;
    FShiftButtons: TShiftState;
    FShiftState: TShiftState;
    FCheckBoxes: array[TShiftStateEnum] of TCheckBox;
    FGrabForm: TForm;
    function GetKey: Word;
    function GetShiftCheckBox(Shift: TShiftStateEnum): TCheckBox;
    procedure SetAllowedShifts(const AValue: TShiftState);
    procedure SetKey(const AValue: Word);
    procedure SetShiftButtons(const AValue: TShiftState);
    procedure SetShiftState(const AValue: TShiftState);
    // Event handlers
    procedure GrabButtonClick(Sender: TObject);
    procedure ShiftCheckBoxClick(Sender: TObject);
    procedure GrabFormKeyDown(Sender: TObject; var AKey: Word; AShift: TShiftState);
    procedure KeyComboboxEditingDone(Sender: TObject);
  protected
    procedure Loaded; override;
    procedure RealSetText(const {%H-}Value: TCaption); override;
    procedure UpdateShiftButtons;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
    function ShiftToStr(s: TShiftStateEnum): string;
  public
    constructor Create(TheOwner: TComponent); override;
    function GetDefaultShiftButtons: TShiftState;
    property ShiftState: TShiftState read FShiftState write SetShiftState;
    property Key: Word read GetKey write SetKey;
    property ShiftButtons: TShiftState read FShiftButtons write SetShiftButtons;
    property AllowedShifts: TShiftState read FAllowedShifts write SetAllowedShifts;
    property KeyComboBox: TComboBox read FKeyComboBox;
    property GrabButton: TButton read FGrabButton;
    property MainOkButton: TCustomButton read FMainOkButton write FMainOkButton;
    property ShiftCheckBox[Shift: TShiftStateEnum]: TCheckBox read GetShiftCheckBox;
  end;


  { TShortCutGrabBox }

  TShortCutGrabBox = class(TCustomShortCutGrabBox)
  published
    property Align;
    property Alignment;
    property AllowedShifts;
    property Anchors;
    property AutoSize;
    property BevelInner;
    property BevelOuter;
    property BevelWidth;
    property BorderSpacing;
    property BorderStyle;
    property BorderWidth;
    property Caption;
    property ChildSizing;
    property ClientHeight;
    property ClientWidth;
    property Color;
    property Constraints;
    property DockSite;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property Font;
    property FullRepaint;
    property Key;
    property OnClick;
    property OnDblClick;
    property OnDockDrop;
    property OnDockOver;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDock;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnGetDockCaption;
    property OnGetSiteInfo;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnResize;
    property OnStartDock;
    property OnStartDrag;
    property OnUnDock;
    property ParentColor;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ShiftButtons;
    property ShiftState;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property UseDockManager default True;
    property Visible;
  end;

//==============================================================================


// Global flags:
var
  GReferenceExpandable: Boolean = true;
  GShowReadOnlyProps: Boolean = true;

// default Hook - set by IDE
var
  GlobalDesignHook: TPropertyEditorHook;

function ClassTypeInfo(Value: TClass): PTypeInfo;
function GetClassUnitName(Value: TClass): string;
procedure CreateComponentEvent(AComponent: TComponent; const EventName: string);
function ClassNameToComponentName(const AClassName: string): string;
function ControlAcceptsStreamableChildComponent(aControl: TWinControl;
  aComponentClass: TComponentClass; aLookupRoot: TPersistent): boolean;

procedure LazSetMethodProp(Instance : TObject;PropInfo : PPropInfo; Value : TMethod);
procedure WritePublishedProperties(Instance: TPersistent);
procedure EditCollection(AComponent: TComponent; ACollection: TCollection; APropName: String);
procedure EditCollectionNoAddDel(AComponent: TComponent; ACollection: TCollection; APropName: String);

// Returns true if given property should be displayed on the property list
// filtered by AFilter and APropNameFilter.
function IsInteresting(AEditor: TPropertyEditor;
  const AFilter: TTypeKinds; const APropNameFilter: String): Boolean;

function GetOrdField(Field: Pointer; FieldInfo: PTypeInfo): Int64; overload;
procedure SetOrdField(Field: Pointer; FieldInfo: PTypeInfo; Value: Int64); overload;
function GetEnumField(Field: Pointer; FieldInfo: PTypeInfo): String; overload;
procedure SetEnumField(Field: Pointer; FieldInfo: PTypeInfo; const Value: string); overload;
function GetSetField(Field: Pointer; FieldInfo: PTypeInfo; Brackets: Boolean): String; overload;
procedure SetSetField(Field: Pointer; FieldInfo: PTypeInfo; const Value: string); overload;
function GetStringField(Field: Pointer; FieldInfo: PTypeInfo): String; overload;
procedure SetStringField(Field: Pointer; FieldInfo: PTypeInfo; const Value: string); overload;
function GetWideStringField(Field: Pointer; FieldInfo: PTypeInfo): WideString; overload;
procedure SetWideStringField(Field: Pointer; FieldInfo: PTypeInfo; const Value: WideString); overload;
function GetUnicodeStringField(Field: Pointer; FieldInfo: PTypeInfo): UnicodeString; overload;
procedure SetUnicodeStringField(Field: Pointer; FieldInfo: PTypeInfo; const Value: UnicodeString); overload;
function GetRawbyteStringField(Field: Pointer; FieldInfo: PTypeInfo): RawByteString; overload;
procedure SetRawbyteStringField(Field: Pointer; FieldInfo: PTypeInfo; const Value: RawByteString); overload;
function GetFloatField(Field: Pointer; FieldInfo: PTypeInfo): Extended; overload;
procedure SetFloatField(Field: Pointer; FieldInfo: PTypeInfo; const Value: Extended); overload;
function GetObjectField(Field: Pointer; FieldInfo: PTypeInfo; MinClass: TClass = nil): TObject; overload;
procedure SetObjectField(Field: Pointer; FieldInfo: PTypeInfo; const Value: TObject); overload;
function GetPointerField(Field: Pointer; FieldInfo: PTypeInfo): Pointer; overload;
procedure SetPointerField(Field: Pointer; FieldInfo: PTypeInfo; const Value: Pointer); overload;
function GetMethodField(Field: Pointer; FieldInfo: PTypeInfo): TMethod; overload;
procedure SetMethodField(Field: Pointer; FieldInfo: PTypeInfo; const Value: TMethod); overload;
function GetInterfaceField(Field: Pointer; FieldInfo: PTypeInfo): IInterface; overload;
procedure SetInterfaceField(Field: Pointer; FieldInfo: PTypeInfo; const Value: IInterface); overload;
function GetVariantField(Field: Pointer; FieldInfo: PTypeInfo): Variant; overload;
procedure SetVariantField(Field: Pointer; FieldInfo: PTypeInfo; const Value: Variant); overload;

Function GetPointerProp(Instance: TObject; PropInfo: PPropInfo): Pointer; overload;
Procedure SetPointerProp(Instance: TObject; PropInfo: PPropInfo; Value: Pointer); overload;

function dbgs(peh: TPropEditHint): string; overload;

const
  NoDefaultValue = Longint($80000000); // magic number for properties with nodefault modifier

implementation

var
  ListPropertyEditors: TList = nil;
  VirtualKeyStrings: TStringHashList = nil;

procedure RegisterListPropertyEditor(AnEditor: TListPropertyEditor);
begin
  if ListPropertyEditors=nil then
    ListPropertyEditors:=TList.Create;
  ListPropertyEditors.Add(AnEditor);
end;

procedure UnregisterListPropertyEditor(AnEditor: TListPropertyEditor);
begin
  if ListPropertyEditors=nil then exit;
  ListPropertyEditors.Remove(AnEditor);
end;

procedure UpdateListPropertyEditors(AnObject: TObject);
var
  i: integer;
  Editor: TListPropertyEditor;
begin
  if ListPropertyEditors=nil then exit;
  for i:=0 to ListPropertyEditors.Count-1 do begin
    Editor:=TListPropertyEditor(ListPropertyEditors[i]);
    if (Editor.GetComponent(0)=AnObject)
    and (Editor.OnSubPropertiesChanged<>nil) then
      Editor.UpdateSubProperties;
  end;
end;

type

  { TSelectableComponentEnumerator }

  TSelectableComponentEnumerator = class(TComponent)
  public
    List: TFPList;
    Flags: TSelectableComponentFlags;
    Root: TComponent;
    procedure GetSelectableComponents(ARoot: TComponent);
    procedure Gather(Child: TComponent);
  end;

{ TSelectionEditor }

constructor TSelectionEditor.Create(ADesigner: TIDesigner;
  AHook: TPropertyEditorHook);
begin
  inherited Create(ADesigner, AHook);
  FDesigner := ADesigner;
  FHook := AHook;
end;

function TSelectionEditor.GetAttributes: TSelectionEditorAttributes;
begin
  Result := [];
end;

procedure TSelectionEditor.FilterProperties(
  ASelection: TPersistentSelectionList; AProperties: TPropertyEditorList);
begin

end;

{ TBaseSelectionEditor }

constructor TBaseSelectionEditor.Create(ADesigner: TIDesigner;
  AHook: TPropertyEditorHook);
begin

end;

{ TPagesPropertyEditor }

procedure TPagesPropertyEditor.AssignItems(OldItmes, NewItems: TStrings);
var
  Unchanged, Index, PageIndex: Integer;
  DummyNotebook: TNotebook;
  APage: TPage;
  PageComponent: TPersistent;
  NoteBook: TNoteBook;
begin
  // search for unchanged pages
  Unchanged := 0;
  while (Unchanged < NewItems.Count) and (Unchanged < OldItmes.Count)
  and (NewItems.Objects[Unchanged] = OldItmes.Objects[Unchanged])
  and (NewItems[Unchanged] = TPage(OldItmes.Objects[Unchanged]).Name) do
    Inc(Unchanged);
  if (Unchanged = OldItmes.Count) and (Unchanged = NewItems.Count) then Exit;

  NoteBook := TNotebook(FOwnerComponent);
  DummyNotebook := TNotebook.Create(nil);
  try
    // move all unused/changed pages to dummy
    for Index := OldItmes.Count - 1 downto Unchanged do
    begin
      APage := TPage(OldItmes.Objects[Index]);
      APage.Parent := DummyNotebook;
    end;

    // add NewItems or changed pages to notebook
    for Index := Unchanged to NewItems.Count - 1 do
    begin
      if Assigned(NewItems.Objects[Index]) then begin
        APage := TPage(NewItems.Objects[Index]);
      end else begin
        PageIndex := NoteBook.Pages.Add(NewItems[Index]);
        APage := TPage(NoteBook.Pages.Objects[PageIndex]);
      end;
      APage.Parent := NoteBook;
      if IsValidIdent(NewItems[Index]) then APage.Name := NewItems[Index];
      APage.Caption := NewItems[Index];
      PropertyHook.PersistentAdded(APage, False);
    end;

    // delete all unused OldItmes pages
    for Index := DummyNotebook.PageCount - 1 downto 0 do
    begin
      APage := TPage(DummyNotebook.Pages.Objects[Index]);
      APage.Parent := nil;;
      DummyNotebook.Pages.Delete(Index);
      PageComponent := TPersistent(APage);
      PropertyHook.DeletePersistent(PageComponent);
    end;
  finally
    DummyNotebook.Free;
  end;
end;

procedure TPagesPropertyEditor.Edit;
var
  TheDialog: TPagesPropEditorDlg;
  Old, New: TStrings;
begin
  Old := TStrings(GetObjectValue);
  TheDialog := CreateDlg(Old);
  try
    if (TheDialog.ShowModal = mrOK) then begin
      New := TheDialog.ListBox.Items;
      AssignItems(Old, TheDialog.ListBox.Items);
      SetPtrValue(New);
    end;
  finally
    TheDialog.Free;
  end;
end;

function TPagesPropertyEditor.CreateDlg(s: TStrings): TPagesPropEditorDlg;
begin
  Result := TPagesPropEditorDlg.Create(Application);
  Result.Editor := Self;
  Result.ListBox.Items.Assign(s);
end;

function TPagesPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paDialog, paRevertable, paReadOnly];
end;

{ TRecordPropertyEditor }

procedure TRecordPropertyEditor.LoadRecord;
var
  PropInfo: PPropInfo;
  Instance: TPersistent;
  AMethod: TMethod;
begin
  if FRecordData=nil then
    raise EPropertyError.CreateFmt(SErrCannotReadProperty, [GetName]);

  PropInfo:=GetPropInfo;
  Instance:=FPropList^[0].Instance;
  if (PropInfo=nil) or (Instance=nil) then
    raise EPropertyError.CreateFmt(SErrCannotReadProperty, [GetName]);

  case (PropInfo^.PropProcs) and 3 of
  ptField: ;
  ptStatic,
  ptVirtual:
    begin
      if (PropInfo^.PropProcs and 3)=ptStatic then
        AMethod.Code:=PropInfo^.GetProc
      else
        AMethod.Code:=PCodePointer(Pointer(Instance.ClassType)+{%H-}PtrUInt(PropInfo^.GetProc))^;
      AMethod.Data:=Instance;
      if ((PropInfo^.PropProcs shr 6) and 1)<>0 then begin
        GetRecordData(AMethod,true,PropInfo^.Index);
      end else begin
        GetRecordData(AMethod,false,0);
      end;
    end;
  else
    raise EPropertyError.CreateFmt(SErrCannotReadProperty, [GetName]);
  end;
end;

procedure TRecordPropertyEditor.GetRecordData(const aMethod: TMethod; WithIndex: boolean;
  Index: Longint);
begin
  raise EPropertyError.CreateFmt(SErrCannotReadProperty, [GetName]);
  if aMethod.Code<>nil then ;
  if WithIndex and (Index>=0) then ;
end;

constructor TRecordPropertyEditor.Create(Hook: TPropertyEditorHook; APropCount: Integer);
begin
  inherited Create(Hook, APropCount);
end;

destructor TRecordPropertyEditor.Destroy;
begin
  if FReadAccess in [ptStatic,ptVirtual] then
    ReAllocMem(FRecordData,0);
  inherited Destroy;
end;

function TRecordPropertyEditor.AllEqual: Boolean;
begin
  Result:=True; // ToDo: Maybe all sub-properties should be compared for equality.
end;

function TRecordPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [{paMultiSelect, }paSubProperties, paReadOnly];
end;

procedure TRecordPropertyEditor.GetProperties(Proc: TGetPropEditProc);
{$IFDEF HasExtRtti}
var
  PropInfo: PPropInfo;
  aTypeData: PTypeData;
  i: Integer;
  FieldTypeInfo: PTypeInfo;
  FieldCnt: LongInt;
  Editor: TPropertyEditor;
  Instance: TPersistent;
  FieldList: PExtendedFieldInfoTable;
  Field: PExtendedVmtFieldEntry;
  EdClass: TPropertyEditorClass;
{$ENDIF}
begin
  {$IFDEF HasExtRtti}
  Instance:=FPropList^[0].Instance;
  PropInfo:=GetPropInfo;
  if PropInfo=nil then
    exit;

  aTypeData:=GetTypeData(PropInfo^.PropType);

  FieldCnt:=GetFieldList(PropInfo^.PropType,FieldList,[vcPublic,vcPublished]);

  // create field editors
  if CanReadFields then begin
    if FRecordData=nil then
      case (PropInfo^.PropProcs) and 3 of
      ptField:
        // direct access
        FRecordData:=PByte(Instance)+{%H-}PtrUInt(PropInfo^.GetProc);
      else
        // local cache
        FRecordData:=AllocMem(aTypeData^.RecSize);
      end;

    for i:=0 to FieldCnt-1 do begin
      Field:=FieldList^[i];
      FieldTypeInfo:=Field^.FieldType^;

      EdClass:=GetEditorClass('',FieldTypeInfo,nil);
      Editor := EdClass.Create(PropertyHook,1);
      Editor.SetRecordFieldEntry(0, PByte(FRecordData)+Field^.FieldOffset, Field^.Name, FieldTypeInfo);
      Editor.Initialize;

      if ((PropCount > 1) and not (paMultiSelect in Editor.GetAttributes))
      or not Editor.ValueAvailable
      then begin
        Editor.Free;
      end else begin
        Proc(Editor);
      end;
    end;
  end;

  {$ELSE}
  if Proc<>nil then ;
  {$ENDIF}
end;

function TRecordPropertyEditor.GetValue: string;
begin
  if FHideRecordName then
    Result:=''
  else
    Result:='(' + GetPropType^.Name + ')';
end;

procedure TRecordPropertyEditor.Initialize;
var
  PropInfo: PPropInfo;
begin
  inherited Initialize;

  PropInfo:=GetPropInfo;
  if PropInfo=nil then
    exit;

  // check read and write access
  FReadAccess:=(PropInfo^.PropProcs) and 3;
  FCanReadFields:=FReadAccess in [ptField{,ptStatic,ptVirtual}];

  FWriteAccess:=(PropInfo^.PropProcs shr 2) and 3;
  FCanWriteFields:=FWriteAccess=ptField;
end;

function TRecordPropertyEditor.ValueIsStreamed: boolean;
begin
  Result:=false;
end;

{ TSelectableComponentEnumerator }

procedure TSelectableComponentEnumerator.GetSelectableComponents(
  ARoot: TComponent);
begin
  Root:=ARoot;
  if List=nil then
    List:=TFPList.Create;
  if Root=nil then exit;
  if not (scfWithoutRoot in Flags) then List.Add(Root);
  TSelectableComponentEnumerator(Root).GetChildren(@Gather,Root);
end;

procedure TSelectableComponentEnumerator.Gather(Child: TComponent);
var
  OldRoot: TComponent;
begin
  if not ((Child is TControl)
          and (csNoDesignSelectable in TControl(Child).ControlStyle))
  then
    List.Add(Child);
  OldRoot:=Root;
  try
    if csInline in Child.ComponentState then begin
      if scfWithoutInlineChilds in Flags then exit;
      if (Child is TControl)
      and (csOwnedChildrenNotSelectable in TControl(Child).ControlStyle) then
        exit;
      Root:=Child;
    end;
    TSelectableComponentEnumerator(Child).GetChildren(@Gather,Root);
  finally
    Root:=OldRoot;
  end;
end;

procedure GetSelectableComponents(Root: TComponent;
  Flags: TSelectableComponentFlags; var ComponentList: TFPList);
var
  e: TSelectableComponentEnumerator;
begin
  e:=TSelectableComponentEnumerator.Create(nil);
  try
    e.List:=ComponentList;
    e.Flags:=Flags;
    e.GetSelectableComponents(Root);
    ComponentList:=e.List;
  finally
    e.Free;
  end;
end;

procedure LazSetMethodProp(Instance: TObject; PropInfo: PPropInfo;
  Value: TMethod);
type
  PMethod = ^TMethod;
  TSetMethodProcIndex=procedure(index:longint;p:TMethod) of object;
  TSetMethodProc=procedure(p:TMethod) of object;
var
  AMethod : TMethod;
begin
  case (PropInfo^.PropProcs shr 2) and 3 of
    ptfield:
      PMethod(Pointer(Instance)+{%H-}PtrUInt(PropInfo^.SetProc))^ := Value;
    ptstatic,
    ptvirtual :
      begin
        if ((PropInfo^.PropProcs shr 2) and 3)=ptStatic then
          AMethod.Code:=PropInfo^.SetProc
        else
          AMethod.Code:=PPointer(Pointer(Instance.ClassType)+{%H-}PtrUInt(PropInfo^.SetProc))^;
        AMethod.Data:=Instance;
        if (Value.Code=nil) and (Value.Data<>nil) then begin
          // this is a fake method
          // Comparing fake methods with OldValue=NewValue results always in
          // true. Therefore this will fail:
          //   if FMethod=NewValue then exit;
          //   FMethod:=NewValue;
          // Change the method two times
          try
            Value.Code:=Pointer(1);
            if ((PropInfo^.PropProcs shr 6) and 1)<>0 then
              TSetMethodProcIndex(AMethod)(PropInfo^.Index,Value)
            else
              TSetMethodProc(AMethod)(Value);
          except
          end;
          Value.Code:=nil;
        end;
        if ((PropInfo^.PropProcs shr 6) and 1)<>0 then
          TSetMethodProcIndex(AMethod)(PropInfo^.Index,Value)
        else
          TSetMethodProc(AMethod)(Value);
      end;
  end;
end;

// -----------------------------------------------------------

procedure WritePublishedProperties(Instance: TPersistent);
var
  TypeInfo: PTypeInfo;
  TypeData: PTypeData;
  PropInfo: PPropInfo;
  PropData: ^TPropData;
  CurCount: integer;
begin
  TypeInfo:=Instance.ClassInfo;
  TypeData:=GetTypeData(TypeInfo);
  debugln('WritePublishedProperties Instance=',DbgS(Instance),' ',Instance.ClassName,' TypeData^.PropCount=',dbgs(TypeData^.PropCount));
  if Instance is TComponent then
    debugln('  TComponent(Instance).Name=',TComponent(Instance).Name);

  // read all properties and remove doubles
  TypeInfo:=Instance.ClassInfo;
  repeat
    // read all property infos of current class
    TypeData:=GetTypeData(TypeInfo);
    // skip unitname
    PropData:=AlignToPtr(PByte(@TypeData^.UnitName)+Length(TypeData^.UnitName)+1);
    // read property count
    CurCount:=PWord(PropData)^;
    PropInfo:=PPropInfo(@PropData^.PropList);
    debugln('    UnitName=',TypeData^.UnitName,' Type=',TypeInfo^.Name,' CurPropCount=',dbgs(CurCount));

    {writeln('TPropInfoList.Create D ',CurCount,' TypeData^.ClassType=',DbgS(TypeData^.ClassType));
    writeln('TPropInfoList.Create E ClassName="',TypeData^.ClassType.ClassName,'"',
    ' TypeInfo=',DbgS(TypeInfo),
    ' TypeData^.ClassType.ClassInfo=',DbgS(TypeData^.ClassType.ClassInfo),
    ' TypeData^.ClassType.ClassParent=',DbgS(TypeData^.ClassType.ClassParent),
    ' TypeData^.ParentInfo=',DbgS(TypeData^.ParentInfo),
    '');
    CurParent:=TypeData^.ClassType.ClassParent;
    if CurParent<>nil then begin
      writeln('TPropInfoList.Create F CurParent.ClassName=',CurParent.ClassName,
        ' CurParent.ClassInfo=',DbgS(CurParent.ClassInfo),
        '');
    end;}

    // read properties
    while CurCount>0 do begin
      // point PropInfo to next propinfo record.
      // Located at Name[Length(Name)+1] !
      debugln('      Property ',PropInfo^.Name,' Type=',PropInfo^.PropType^.Name);
      PropInfo:=PPropInfo(AlignToPtr(pointer(@PropInfo^.Name)+PByte(@PropInfo^.Name)^+1));
      dec(CurCount);
    end;
    TypeInfo:=TypeData^.ParentInfo;
    if TypeInfo=nil then break;
  until false;
end;


//------------------------------------------------------------------------------

const
{ TypeKinds  see typinfo.pp
       TTypeKind = (tkUnknown,tkInteger,tkChar,tkEnumeration,tkFloat,
                   tkSet,tkMethod,tkSString,tkLString,tkAString,
                   tkWString,tkVariant,tkArray,tkRecord,tkInterface,
                   tkClass,tkObject,tkWChar,tkBool,tkInt64,tkQWord,
                   tkDynArray,tkInterfaceRaw,tkProcVar,tkUString,tkUChar,
                   tkHelper);
}
  // default property editor classes
  PropClassMap:array[TypInfo.TTypeKind] of TPropertyEditorClass=(
    nil,                       // tkUnknown
    TIntegerPropertyEditor,    // tkInteger
    TCharpropertyEditor,       // tkChar
    TEnumPropertyEditor,       // tkEnumeration
    TFloatPropertyEditor,      // tkFloat
    TSetPropertyEditor,        // tkSet
    TMethodPropertyEditor,     // tkMethod
    TStringPropertyEditor,     // tkSString
    TStringPropertyEditor,     // tkLString
    TStringPropertyEditor,     // tkAString
    TWideStringPropertyEditor, // tkWString
    TPropertyEditor,           // tkVariant
    nil,                       // tkArray
    TRecordPropertyEditor,     // tkRecord
    TInterfacePropertyEditor,  // tkInterface
    TClassPropertyEditor,      // tkClass
    nil,                       // tkObject
    TPropertyEditor,           // tkWChar
    TBoolPropertyEditor,       // tkBool
    TInt64PropertyEditor,      // tkInt64
    TQWordPropertyEditor,      // tkQWord
    nil,                       // tkDynArray
    nil,                       // tkInterfaceRaw,
    nil,                       // tkProcVar
    TUnicodeStringPropertyEditor,// tkUString
    nil                        // tkUChar
{$IF declared(tkHelper)}
    ,nil                       // tkHelper
{$ENDIF}
{$IF declared(tkFile)}
    ,nil                       // tkFile
{$ENDIF}
{$IF declared(tkClassRef)}
    ,nil                       // tkClassRef
{$ENDIF}
{$IF declared(tkPointer)}
    ,nil                       // tkPointer
{$ENDIF}
    );

var
  PropertyEditorMapperList:TFPList;
  PropertyClassList:TFPList;
  SelectionEditorClassList:TFPList;

type
  PPropertyClassRec=^TPropertyClassRec;
  TPropertyClassRec=record
    PropertyType:PTypeInfo;
    PropertyName:shortstring;
    PersistentClass:TClass;
    EditorClass:TPropertyEditorClass;
  end;

  PPropertyEditorMapperRec=^TPropertyEditorMapperRec;
  TPropertyEditorMapperRec=record
    Mapper:TPropertyEditorMapperFunc;
  end;

  PSelectionEditorClassRec=^TSelectionEditorClassRec;
  TSelectionEditorClassRec=record
    ComponentClass:TComponentClass;
    EditorClass:TSelectionEditorClass;
  end;

{ TPropInfoList }

constructor TPropInfoList.Create(Instance:TPersistent; Filter:TTypeKinds);
var
  ListCapacity: integer;

  procedure Add(PropInfo: PPropInfo{$IFDEF HasExtRtti}; Ext: PPropInfoEx{$ENDIF});
  var
    i: Integer;
  begin
    if PropInfo^.PropType^.Kind in Filter then begin
      // check if name already exists in list
      i:=FCount-1;
      while (i>=0) and (CompareText(FList^[i]^.Name,PropInfo^.Name)<>0) do
        dec(i);
      if (i<0) then begin
        // add property info
        if FCount=ListCapacity then begin
          if ListCapacity<16 then
            ListCapacity:=16
          else
            ListCapacity:=ListCapacity*2;
          ReAllocMem(FList,ListCapacity*SizeOf(Pointer));
          {$IFDEF HasExtRtti}
          ReAllocMem(FListExt,ListCapacity*SizeOf(Pointer));
          {$ENDIF}
        end;
        FList^[FCount]:=PropInfo;
        {$IFDEF HasExtRtti}
        FListExt^[FCount]:=Ext;
        {$ENDIF}
        inc(FCount);
      end;
    end;
  end;

var
  aTypeInfo: PTypeInfo;
  TypeData: PTypeData;
  PropInfo: PPropInfo;
  PropData: ^TPropData;
  {$IFDEF HasExtRtti}
  PropList: PPropListEx;
  PropInfoEx: PPropInfoEx;
  {$ENDIF}
  CurCount: integer;
  //CurParent: TClass;
begin
  aTypeInfo:=Instance.ClassInfo;

  // read all published properties and remove doubles
  TypeData:=GetTypeData(aTypeInfo);
  ListCapacity:=TypeData^.PropCount;
  ReAllocMem(FList,ListCapacity * SizeOf(Pointer));
  {$IFDEF HASEXTRTTI}
  ReAllocMem(FListExt,ListCapacity * SizeOf(Pointer));
  {$ENDIF}

  aTypeInfo:=Instance.ClassInfo;
  FCount:=0;
  repeat
    // read all property infos of current class
    TypeData:=GetTypeData(aTypeInfo);
    // skip unitname
    PropData:=AlignToPtr(Pointer(@TypeData^.UnitName)+Length(TypeData^.UnitName)+1);
    // read property count
    CurCount:=PropData^.PropCount;
    PropInfo:=PPropInfo(@PropData^.PropList);

    {writeln('TPropInfoList.Create D ',CurCount,' TypeData^.ClassType=',DbgS(TypeData^.ClassType));
    writeln('TPropInfoList.Create E ClassName="',TypeData^.ClassType.ClassName,'"',
    ' TypeInfo=',DbgS(aTypeInfo),
    ' TypeData^.ClassType.ClassInfo=',DbgS(TypeData^.ClassType.ClassInfo),
    ' TypeData^.ClassType.ClassParent=',DbgS(TypeData^.ClassType.ClassParent),
    ' TypeData^.ParentInfo=',DbgS(TypeData^.ParentInfo),
    '');
    CurParent:=TypeData^.ClassType.ClassParent;
    if CurParent<>nil then begin
      writeln('TPropInfoList.Create F CurParent.ClassName=',CurParent.ClassName,
        ' CurParent.ClassInfo=',DbgS(CurParent.ClassInfo),
        '');
    end;}

    // read properties
    while CurCount>0 do begin
      Add(PropInfo{$IFDEF HasExtRtti},nil{$ENDIF});
      // point PropInfo to next propinfo record.
      // Located at Name[Length(Name)+1] !
      PropInfo:=PPropInfo(AlignToPtr(pointer(@PropInfo^.Name)+PByte(@PropInfo^.Name)^+1));
      dec(CurCount);
    end;

    {$IFDEF HasExtRtti}
    if FExtVisibility<>[] then begin
      CurCount:=GetPropListEx(aTypeInfo,PropList,FExtVisibility);
      try
        while CurCount>0 do begin
          dec(CurCount);
          PropInfoEx:=PropList^[CurCount];
          Add(PropInfoEx^.Info,PropInfoEx);
        end;
      finally
        Freemem(PropList);
      end;
    end;
    {$ENDIF}

    aTypeInfo:=TypeData^.ParentInfo;
  until aTypeInfo=nil;

  Sort;
end;

{$IFDEF HasExtRtti}
constructor TPropInfoList.Create(Instance: TPersistent; Filter: TTypeKinds;
  AddExtVisibility: TVisibilityClasses);
begin
  FExtVisibility:=AddExtVisibility;
  Create(Instance,Filter);
end;
{$ENDIF}

destructor TPropInfoList.Destroy;
begin
  if FList<>nil then FreeMem(FList);
  {$IFDEF HasExtRtti}
  if FListExt<>nil then FreeMem(FListExt);
  {$ENDIF HasExtRtti}
end;

function TPropInfoList.Contains(P:PPropInfo):Boolean;
var
  I: Integer;
begin
  for I := 0 to FCount - 1 do
  begin
    with FList^[I]^ do
    begin
      if (PropType^.Kind=P^.PropType^.Kind) and (CompareText(Name,P^.Name)=0) then
      begin
        Result := True;
        Exit;
      end;
    end;
  end;
  Result := False;
end;

procedure TPropInfoList.Delete(Index:Integer);
begin
  Dec(FCount);
  if Index >= FCount then exit;
  System.Move(FList^[Index+1],FList^[Index],(FCount-Index) * SizeOf(Pointer));
  {$IFDEF HasExtRtti}
  System.Move(FListExt^[Index+1],FListExt^[Index],(FCount-Index) * SizeOf(Pointer));
  {$ENDIF}
end;

function TPropInfoList.Get(Index:Integer):PPropInfo;
begin
  Result:=FList^[Index];
end;

{$IFDEF HasExtRtti}
function TPropInfoList.GetExt(Index: Integer): PPropInfoEx;
begin
  Result:=FListExt^[Index];
end;
{$ENDIF}

procedure TPropInfoList.Intersect(List:TPropInfoList);
var
  i:Integer;
begin
  for i:=FCount-1 downto 0 do
    if not List.Contains(FList^[i]) then Delete(i);
end;

procedure TPropInfoList.Sort;

  procedure QuickSort(L, R: Integer);
  var
    i, j: Longint;
    p: PPropInfo;
    h: Pointer;
  begin
    repeat
      i := L;
      j := R;
      p := FList^[(L + R) div 2];
      repeat
        while CompareText(p^.Name, FList^[i]^.Name) > 0 do
          inc(i);
        while CompareText(p^.Name, FList^[j]^.Name) < 0 do
          dec(j);
        if i <= j then
        begin
          h := FList^[i];
          Flist^[i] := FList^[j];
          FList^[j] := h;
          {$IFDEF HasExtRtti}
          h := FListExt^[i];
          FListExt^[i] := FListExt^[j];
          FListExt^[j] := h;
          {$ENDIF}
          inc(i);
          dec(j);
        end;
      until i > j;
      if L < j then
        QuickSort(L, j);
      L := i;
    until i >= R;
  end;
begin
  if Count > 0 then
    QuickSort(0, Count - 1);
end;

//------------------------------------------------------------------------------

procedure RegisterSelectionEditor(AComponentClass: TComponentClass; AEditorClass: TSelectionEditorClass);
var
  p:PSelectionEditorClassRec;
begin
  if not Assigned(AComponentClass) or not Assigned(AEditorClass) then
    Exit;
  if not Assigned(SelectionEditorClassList) then
    SelectionEditorClassList:=TFPList.Create;
  New(p);
  p^.ComponentClass:=AComponentClass;
  p^.EditorClass:=AEditorClass;
  SelectionEditorClassList.Add(p);
end;

procedure GetSelectionEditorClasses(AComponent: TComponent; AEditorList: TSelectionEditorClassList);
var
  i:LongInt;
begin
  if not Assigned(AComponent) or not Assigned(AEditorList) then
    Exit;
  if not Assigned(SelectionEditorClassList) then
    Exit;
  for i:=0 to SelectionEditorClassList.Count-1 do begin
    with PSelectionEditorClassRec(SelectionEditorClassList[i])^ do begin
      if AComponent.InheritsFrom(ComponentClass) then
        AEditorList.Add(EditorClass);
    end;
  end;
end;

procedure GetSelectionEditorClasses(ASelection: TPersistentSelectionList;
  AEditorList: TSelectionEditorClassList);
var
  tmp:TSelectionEditorClassList;
  i,j:LongInt;
  sel:TPersistent;
begin
  if not Assigned(ASelection) or (ASelection.Count=0) or not Assigned(AEditorList) then
    Exit;

  tmp:=TSelectionEditorClassList.Create;
  try
    for i:=0 to ASelection.Count-1 do begin
      sel:=ASelection[i];
      if not (sel is TComponent) then
        Continue;
      GetSelectionEditorClasses(TComponent(sel),tmp);
      { if there are no classes yet, we pick them as is, otherwise we remove all
        those from the existing list that are not part of the new list }
      if AEditorList.Count=0 then
        AEditorList.Assign(tmp)
      else begin
        for j:=AEditorList.Count-1 downto 0 do begin
          if tmp.IndexOf(AEditorList[j])<0 then
            AEditorList.Delete(j);
        end;
      end;
      tmp.Clear;
    end;
  finally
    tmp.Free;
  end;
end;

{ GetComponentProperties }

procedure RegisterPropertyEditor(PropertyType:PTypeInfo;
  PersistentClass: TClass;  const PropertyName:shortstring;
  EditorClass:TPropertyEditorClass);
var
  P:PPropertyClassRec;
begin
  if PropertyType=nil then exit;
  if PropertyClassList=nil then
    PropertyClassList:=TFPList.Create;
  New(P);
  P^.PropertyType:=PropertyType;
  P^.PersistentClass:=PersistentClass;
  P^.PropertyName:=PropertyName;
  P^.EditorClass:=EditorClass;
  PropertyClassList.Insert(0,P);
end;

procedure RegisterPropertyEditorMapper(Mapper:TPropertyEditorMapperFunc);
var
  P:PPropertyEditorMapperRec;
begin
  if PropertyEditorMapperList=nil then
    PropertyEditorMapperList:=TFPList.Create;
  New(P);
  P^.Mapper:=Mapper;
  PropertyEditorMapperList.Insert(0,P);
end;

function GetEditorClass(PropInfo:PPropInfo; Obj:TPersistent): TPropertyEditorClass;
var
  I: Integer;
begin
  Result := nil;
  if PropertyEditorMapperList<>nil then begin
    for I:=0 to PropertyEditorMapperList.Count-1 do begin
      with PPropertyEditorMapperRec(PropertyEditorMapperList[I])^ do begin
        Result:=Mapper(Obj,PropInfo);
        if Result<>nil then break;
      end;
    end;
  end;
  Result:=GetEditorClass(PropInfo^.Name,PropInfo^.PropType,Obj);
end;

function GetEditorClass(PropName: shortstring; Info: PTypeInfo; Obj: TPersistent
  ): TPropertyEditorClass;
var
  P, C: PPropertyClassRec;
  I: Integer;
  TypeData: PTypeData;
begin
  Result := nil;
  I:=0;
  C:=nil;
  TypeData:=GetTypeData(Info);
  while I < PropertyClassList.Count do begin
    P:=PropertyClassList[I];

    if ((P^.PropertyType=Info) or
         ((P^.PropertyType^.Kind=Info^.Kind) and
          (P^.PropertyType^.Name=Info^.Name)
         )
       ) or
       ( (Info^.Kind=tkClass) and
         (P^.PropertyType^.Kind=tkClass) and
         TypeData^.ClassType.InheritsFrom(
           GetTypeData(P^.PropertyType)^.ClassType)
       )
    then
      if ((P^.PersistentClass=nil) or ((Obj<>nil) and (Obj.InheritsFrom(P^.PersistentClass))))
          and ((P^.PropertyName='') or (CompareText(P^.PropertyName,PropName)=0))
      then
        if (C=nil) or   // see if P is better match than C
           ((C^.PersistentClass=nil) and (P^.PersistentClass<>nil)) or
           ((C^.PropertyName='') and (P^.PropertyName<>''))
           or  // P's proptype match is exact,but C's does not
           ((C^.PropertyType<>Info) and (P^.PropertyType=Info))
           or  // P's proptype is more specific than C's proptype
           ((P^.PropertyType<>C^.PropertyType) and
            (P^.PropertyType^.Kind=tkClass) and
            (C^.PropertyType^.Kind=tkClass) and
            GetTypeData(P^.PropertyType)^.ClassType.InheritsFrom(
              GetTypeData(C^.PropertyType)^.ClassType))
           or // P's component class is more specific than C's component class
           ((P^.PersistentClass<>nil) and (C^.PersistentClass<>nil) and
            (P^.PersistentClass<>C^.PersistentClass) and
            (P^.PersistentClass.InheritsFrom(C^.PersistentClass)))
        then
          C:=P;
    Inc(I);
  end;
  if C<>nil then
    Result:=C^.EditorClass
  else begin
    if (Info^.Kind<>tkClass)
    or (TypeData^.ClassType.InheritsFrom(TPersistent))
    or (TypeData^.PropCount > 0) then
      Result:=PropClassMap[Info^.Kind]
    else
      Result:=nil;
  end;
  if (Result<>nil) and Result.InheritsFrom(THiddenPropertyEditor) then
    Result:=nil;
end;

procedure GetPersistentProperties(ASelection: TPersistentSelectionList;
  AFilter: TTypeKinds; AHook: TPropertyEditorHook; AProc: TGetPropEditProc;
  APropInfoFilterFunc: TPropInfoFilterFunc;
  AEditorFilterFunc: TPropertyEditorFilterFunc
  {$IFDEF HasExtRtti};ExtVisibility: TVisibilityClasses = []{$ENDIF});
var
  I, J, SelCount: Integer;
  ClassTyp: TClass;
  Candidates: TPropInfoList;
  PropLists: TFPList;
  PropEditor: TPropertyEditor;
  PropEditorList: TPropertyEditorList;
  SelEditor: TBaseSelectionEditor;
  SelEditorList: TSelectionEditorClassList;
  EdClass: TPropertyEditorClass;
  PropInfo: PPropInfo;
  AddEditor: Boolean;
  Instance: TPersistent;
  Designer: TIDesigner;
begin
  if (ASelection = nil) or (ASelection.Count = 0) then Exit;
  SelCount := ASelection.Count;
  Instance := ASelection[0];
  ClassTyp := Instance.ClassType;
  // Create a property candidate list of all properties that can be found in
  // every component in the list and in the Filter
  Candidates := TPropInfoList.Create(Instance, AFilter{$IFDEF HasExtRtti},ExtVisibility{$ENDIF});
  try
    // check each property candidate
    for I := Candidates.Count - 1 downto 0 do
    begin
      PropInfo := Candidates[I];

      // check if property is readable
      if (PropInfo^.GetProc=nil)
      or ((not GShowReadOnlyProps) and (PropInfo^.PropType^.Kind <> tkClass)
          and (PropInfo^.SetProc = nil))
      or (Assigned(APropInfoFilterFunc) and (not APropInfoFilterFunc(PropInfo)))
      then begin
        Candidates.Delete(I);
        Continue;
      end;

      EdClass := GetEditorClass(PropInfo, Instance);
      if EdClass = nil
      then begin
        Candidates.Delete(I);
        Continue;
      end;

      // create a test property editor for the property
      PropEditor := EdClass.Create(AHook,1);
      PropEditor.SetPropEntry(0, Instance, PropInfo);
      PropEditor.Initialize;
      // check for multiselection, ValueAvailable and customfilter
      if ((SelCount > 1) and not (paMultiSelect in PropEditor.GetAttributes))
      or not PropEditor.ValueAvailable
      or (Assigned(AEditorFilterFunc) and not AEditorFilterFunc(PropEditor))
      then
        Candidates.Delete(I);
      PropEditor.Free;
    end;

    PropEditorList := TPropertyEditorList.Create(True);
    try
      PropLists := TFPList.Create;
      try
        PropLists.Count := SelCount;
        // Create a property info list for each component in the selection
        for I := 0 to SelCount - 1 do
          PropLists[i] := TPropInfoList.Create(ASelection[I], AFilter{$IFDEF HasExtRtti},ExtVisibility{$ENDIF});

        // Eliminate each property in Candidates that is not in all property lists
        for I := 0 to SelCount - 1 do
          Candidates.Intersect(TPropInfoList(PropLists[I]));

        // Eliminate each property in the property list that are not in Candidates
        for I := 0 to SelCount - 1 do
          TPropInfoList(PropLists[I]).Intersect(Candidates);

        // PropList now has a matrix of PropInfo's.
        // -> create a property editor for each property
        for I := 0 to Candidates.Count - 1 do
        begin
          EdClass := GetEditorClass(Candidates[I], Instance);
          if EdClass = nil then Continue;
          PropEditor := EdClass.Create(AHook, SelCount);
          AddEditor := True;
          for J := 0 to SelCount - 1 do
          begin
            if (ASelection[J].ClassType <> ClassTyp) and
              (GetEditorClass(TPropInfoList(PropLists[J])[I], ASelection[J])<>EdClass) then
            begin
              AddEditor := False;
              Break;
            end;
            PropEditor.SetPropEntry(J, ASelection[J], TPropInfoList(PropLists[J])[I]);
          end;
          if AddEditor then
          begin
            PropEditor.Initialize;
            if not PropEditor.ValueAvailable then AddEditor:=false;
          end;
          if AddEditor then
            PropEditorList.Add(PropEditor)
          else
            PropEditor.Free;
        end;
      finally
        for I := 0 to PropLists.Count - 1 do
          TPropInfoList(PropLists[I]).Free;
        PropLists.Free;
      end;

      SelEditorList := TSelectionEditorClassList.Create;
      try
        GetSelectionEditorClasses(ASelection, SelEditorList);
        { is it safe to assume that the whole selection has the same designer? }
        Designer := FindRootDesigner(ASelection[0]);
        for I := 0 to SelEditorList.Count - 1 do begin
          SelEditor := SelEditorList[I].Create(Designer, AHook);
          try
            if seaFilterProperties in SelEditor.GetAttributes then
              SelEditor.FilterProperties(ASelection, PropEditorList);
          finally
            SelEditor.Free;
          end;
        end;
      finally
        SelEditorList.Free;
      end;

      { no longer free the editors }
      PropEditorList.FreeObjects := False;
      for I := 0 to PropEditorList.Count - 1 do
        AProc(PropEditorList[I]);
    finally
      PropEditorList.Free;
    end;
  finally
    Candidates.Free;
  end;
end;

procedure GetPersistentProperties(ASelection: TPersistentSelectionList;
  AFilter: TTypeKinds; AHook: TPropertyEditorHook; AProc: TGetPropEditProc;
  AEditorFilterFunc: TPropertyEditorFilterFunc);
begin
  GetPersistentProperties(ASelection,AFilter,AHook,AProc,nil,AEditorFilterFunc);
end;

procedure GetPersistentProperties(AItem: TPersistent;
  AFilter: TTypeKinds; AHook: TPropertyEditorHook; AProc: TGetPropEditProc;
  AEditorFilterFunc: TPropertyEditorFilterFunc);
var
  Selection: TPersistentSelectionList;
begin
  if AItem = nil then Exit;
  Selection := TPersistentSelectionList.Create;
  try
    Selection.Add(AItem);
    GetPersistentProperties(Selection,AFilter,AHook,AProc,AEditorFilterFunc);
  finally
    Selection.Free;
  end;
end;


{ TPropertyEditor }

constructor TPropertyEditor.Create(Hook: TPropertyEditorHook; APropCount:Integer);
var
  PropListSize: Integer;
begin
  FPropertyHook:=Hook;
  PropListSize:=APropCount * SizeOf(TInstProp);
  GetMem(FPropList,PropListSize);
  FillByte(FPropList^,PropListSize,0);
  FPropCount:=APropCount;
end;

destructor TPropertyEditor.Destroy;
begin
  if FPropList<>nil then
    FreeMem(FPropList,FPropCount * SizeOf(TInstProp));
end;

procedure TPropertyEditor.Activate;
begin
  //
end;

procedure TPropertyEditor.Deactivate;
begin
  //
end;

function TPropertyEditor.AllEqual:Boolean;
begin
  Result:=FPropCount=1;
end;

procedure TPropertyEditor.Edit;
type
  TGetStrFunc = function(const StrValue:ansistring):Integer of object;
var
  I:Integer;
  Values: TStringList;
  AddValue: TGetStrFunc;
begin
  if not AutoFill then Exit;
  Values:=TStringList.Create;
  Values.UseLocale := False;
  Values.Sorted:=paSortList in GetAttributes;
  try
    AddValue := @Values.Add;
    GetValues(TGetStrProc((@AddValue)^));
    if Values.Count > 0 then begin
      I:=Values.IndexOf(FirstValue)+1;
      if I=Values.Count then I:=0;
      FirstValue:=Values[I];
    end;
  finally
    Values.Free;
  end;
end;

procedure TPropertyEditor.Edit(AOwnerComponent: TComponent);
begin
  FOwnerComponent := AOwnerComponent;
  Edit;
  FOwnerComponent := Nil;
end;

procedure TPropertyEditor.ShowValue;
begin

end;

function TPropertyEditor.AutoFill:Boolean;
begin
  Result:=paValueList in GetAttributes;
end;

function TPropertyEditor.CallStoredFunction: Boolean;
begin
  with FPropList^[0] do begin
    if PropInfo=nil then
      exit(false);
    Result := (Instance <> nil) and IsStoredProp(Instance, PropInfo);
  end;
end;

{$IFDEF UseOINormalCheckBox}
function TPropertyEditor.DrawCheckbox(ACanvas: TCanvas; const ARect: TRect;
  IsTrue: Boolean): TRect;
// Draws a Checkbox using theme services for editing booleans.
// Returns the output rectangle adjusted for new text location.
var
  Details: TThemedElementDetails;
  Check: TThemedButton;
  BRect: TRect;
  Sz: TSize;
  TopMargin: Integer;
  VisVal: String;
begin
  VisVal := GetVisualValue;
  // Draw the box using theme services.
  if (VisVal = '') or (VisVal = oisMixed) then
    Check := tbCheckBoxMixedNormal
  else if IsTrue then
    Check := tbCheckBoxCheckedNormal
  else
    Check := tbCheckBoxUncheckedNormal;
  Details := ThemeServices.GetElementDetails(Check);
  Sz := TCustomCheckBoxThemed.GetCheckBoxSize(ScreenInfo.PixelsPerInchX);
  TopMargin := (ARect.Bottom - ARect.Top - Sz.cy) div 2;
  BRect := ARect;
  // Left varies by widgetset and theme etc. Real Checkbox itself has a left margin.
  Inc(BRect.Left, 3);                // ToDo: How to find out the real margin?
  Result := BRect;                   // Result Rect will be used for text.
  Inc(BRect.Top, TopMargin);
  BRect.Right := BRect.Left + Sz.cx;
  BRect.Bottom := BRect.Top + Sz.cy;
  ThemeServices.DrawElement(ACanvas.Handle, Details, BRect, nil);
  // Text will be written after the box.
  Inc(Result.Left, Sz.cx + 4);
end;
{$ENDIF}

function TPropertyEditor.DrawCheckValue(ACanvas: TCanvas; const ARect: TRect;
                           AState: TPropEditDrawState; IsTrue: Boolean): TRect;
// Draws Boolean value as text or Checkbox depending on user setting from PropertyHook.
// Uses either theme services (func DrawCheckbox) or TCheckBoxThemed depending
//  on UseOINormalCheckBox define.
// Returns Rect for textual part if it must be drawn, otherwise Result.Top = -100.
{$IFnDEF UseOINormalCheckBox}
var
  BRect: TRect;
  VisVal: string;
  stat: TCheckBoxState;
{$ENDIF}
begin
  Result.Top := 0;
  if FPropertyHook.GetCheckboxForBoolean then
  begin                         // Checkbox for Booleans.
  {$IFnDEF UseOINormalCheckBox}
    Result.Top := -100;         // No need to call PropDrawValue further.
    BRect := ARect;
    Inc(BRect.Left, CheckBoxThemedLeftOffs);
    VisVal := GetVisualValue;
    if (VisVal = '') or (VisVal = oisMixed) then
      stat := cbGrayed
    else if VisVal = '(True)' then
      stat := cbChecked
    else
      stat := cbUnchecked;
    TCheckBoxThemed.PaintSelf(ACanvas, VisVal, BRect, stat, False, False, False,
                              False, taRightJustify);
  {$ELSE}
    Result := DrawCheckbox(ACanvas, ARect, IsTrue);
  {$ENDIF}
  end
  else
    Result := ARect;           // Classic Combobox for Booleans.
end;

function TPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result:=[paMultiSelect,paRevertable];
end;

function TPropertyEditor.IsReadOnly: boolean;
begin
  Result:=paReadOnly in GetAttributes;
end;

function TPropertyEditor.GetComponent(Index: Integer): TPersistent;
begin
  Result:=FPropList^[Index].Instance;
end;

function TPropertyEditor.GetUnitName(Index: Integer): string;
begin
  Result:=GetClassUnitName(GetComponent(Index).ClassType);
end;

function TPropertyEditor.GetPropTypeUnitName(Index: Integer): string;
type
  PPropData = ^TPropData;
var
  aPersistent: TPersistent;
  CurPropInfo: PPropInfo;
  hp: PTypeData;
  pd: PPropData;
  i: Integer;
  UpperName: ShortString;
  ATypeInfo: PTypeInfo;
  NameFound: Boolean;
  ThePropType: PTypeInfo;
begin
  Result:='';
  aPersistent:=GetComponent(Index);
  UpperName:=UpCase(GetName);
  ThePropType:=GetPropType;
  ATypeInfo:=PTypeInfo(aPersistent.ClassInfo);
  while Assigned(ATypeInfo) do begin
    // skip the name
    hp:=GetTypeData(ATypeInfo);
    // the class info rtti the property rtti follows immediatly
    pd:=AlignToPtr(Pointer(Pointer(@hp^.UnitName)+Length(hp^.UnitName)+1));
    CurPropInfo:=PPropInfo(@pd^.PropList);
    NameFound:=false;
    for i:=1 to pd^.PropCount do begin
      // found a property of that name ?
      if Upcase(CurPropInfo^.Name)=UpperName then begin
        DebugLn(['TPropertyEditor.GetPropTypeUnitName ',hp^.UnitName,' IsSamePropInfo=',CurPropInfo^.PropType=ThePropType]);
        NameFound:=true;
        if CurPropInfo^.PropType=ThePropType then
          Result:=hp^.UnitName;
      end;
      // skip to next property
      CurPropInfo:=PPropInfo(AlignToPtr(Pointer(@CurPropInfo^.Name)+Byte(CurPropInfo^.Name[0])+1));
    end;
    if not NameFound then break;
    // parent class
    ATypeInfo:=hp^.ParentInfo;
  end;
end;

function TPropertyEditor.GetPropertyPath(Index: integer): string;
begin
  Result:=GetComponent(Index).ClassName+'.'+GetName;
end;

function TPropertyEditor.GetFloatValue:Extended;
begin
  Result:=GetFloatValueAt(0);
end;

procedure SetIndexValues(P: PPropInfo; var Index, IValue : Longint);
begin
  Index:=((P^.PropProcs shr 6) and 1);
  if Index<>0 then
    IValue:=P^.Index
  else
    IValue:=0;
end;

function TPropertyEditor.GetFloatValueAt(Index:Integer):Extended;
begin
  Result:=FPropList^[Index].GetFloat;
end;

function TPropertyEditor.GetMethodValue:TMethod;
begin
  Result:=GetMethodValueAt(0);
end;

// workaround for buggy rtl function
function LazGetMethodProp(Instance: TObject; PropInfo: PPropInfo): TMethod;
type
  TGetMethodProcIndex=function(Index: Longint): TMethod of object;
  TGetMethodProc=function(): TMethod of object;
  PMethod = ^TMethod;
var
  value: PMethod;
  AMethod : TMethod;
begin
  Result.Code:=nil;
  Result.Data:=nil;
  case (PropInfo^.PropProcs) and 3 of
    ptfield:
      begin
        Value:=PMethod(Pointer(Instance)+{%H-}PtrUInt(PropInfo^.GetProc));
        if Value<>nil then
          Result:=Value^;
      end;
    ptstatic,
    ptvirtual :
      begin
        if (PropInfo^.PropProcs and 3)=ptStatic then
          AMethod.Code:=PropInfo^.GetProc
        else
          AMethod.Code:=PPointer(Pointer(Instance.ClassType)
                        +{%H-}PtrUInt(PropInfo^.GetProc))^;
        AMethod.Data:=Instance;
        if ((PropInfo^.PropProcs shr 6) and 1)<>0 then
          Result:=TGetMethodProcIndex(AMethod)(PropInfo^.Index)
        else
          Result:=TGetMethodProc(AMethod)();
      end;
  end;
end;

function TPropertyEditor.GetMethodValueAt(Index: Integer):TMethod;
begin
  Result:=FPropList^[Index].GetMethod;
end;

function TPropertyEditor.GetEditLimit: Integer;
begin
  Result := 255;
end;

function TPropertyEditor.GetName: shortstring;
begin
  with FPropList^[0] do begin
    if PropInfo<>nil then
      Result:=PropInfo^.Name
    else if FieldName<>nil then
      Result:=FieldName^
    else
      Result:='';
  end;
end;

function TPropertyEditor.GetOrdValue: Longint;
begin
  Result:=GetOrdValueAt(0);
end;

function TPropertyEditor.GetOrdValueAt(Index: Integer): Longint;
begin
  Result:=FPropList^[Index].GetOrd;
end;

function TPropertyEditor.GetObjectValue: TObject;
begin
  Result:=GetObjectValueAt(0);
end;

function TPropertyEditor.GetObjectValue(MinClass: TClass): TObject;
begin
  Result:=GetObjectValueAt(0, MinClass);
end;

function TPropertyEditor.GetObjectValueAt(Index: Integer): TObject;
begin
  Result:=FPropList^[Index].GetObject;
end;

function TPropertyEditor.GetObjectValueAt(Index: Integer; MinClass: TClass): TObject;
begin
  Result:=FPropList^[Index].GetObject(MinClass);
end;

function TPropertyEditor.GetDefaultOrdValue: Longint;
var
  APropInfo: PPropInfo;
begin
  APropInfo:=FPropList^[0].PropInfo;
  if APropInfo<>nil then
    Result:=APropInfo^.Default
  else
    Result:=0;
end;

function TPropertyEditor.GetSetValue(Brackets: boolean): AnsiString;
begin
  Result:=FPropList^[0].GetSet(Brackets);
end;

function TPropertyEditor.GetSetValueAt(Index: Integer; Brackets: boolean): AnsiString;
begin
  Result:=FPropList^[Index].GetSet(Brackets);
end;

function TPropertyEditor.GetPrivateDirectory:ansistring;
begin
  Result:='';
  if PropertyHook<>nil then
    Result:=PropertyHook.GetPrivateDirectory;
end;

procedure TPropertyEditor.DrawValue(const AValue: string; ACanvas: TCanvas;
  const ARect: TRect; AState: TPropEditDrawState);
var
  Style : TTextStyle;
begin
  FillChar(Style{%H-},SizeOf(Style),0);
  With Style do begin
    Alignment := taLeftJustify;
    Layout := tlCenter;
    Opaque := False;
    Clipping := True;
    ShowPrefix := False;
    WordBreak := False;
    SingleLine := True;
    ExpandTabs := True;
    SystemFont := False;
  end;
  ACanvas.TextRect(ARect,ARect.Left+3,ARect.Top,AValue, Style);
end;

procedure TPropertyEditor.GetProperties(Proc:TGetPropEditProc);
begin
end;

function TPropertyEditor.GetPropInfo: PPropInfo;
begin
  Result:=FPropList^[0].PropInfo;
end;

function TPropertyEditor.GetInstProp: PInstProp;
begin
  Result:=@FPropList^[0];
end;

function TPropertyEditor.GetEnumValueAt(Index: Integer): string;
begin
  Result:=FPropList^[Index].GetEnum;
end;

function TPropertyEditor.GetPropType: PTypeInfo;
begin
  with FPropList^[0] do begin
    if FieldTypeInfo<>nil then
      Result:=FieldTypeInfo
    else
      Result:=PropInfo^.PropType;
  end;
end;

function TPropertyEditor.GetStrValue: AnsiString;
begin
  Result:=GetStrValueAt(0);
end;

function TPropertyEditor.GetStrValueAt(Index:Integer): AnsiString;
begin
  Result:=FPropList^[Index].GetString;
end;

function TPropertyEditor.GetVarValue: Variant;
begin
  Result:=GetVarValueAt(0);
end;

function TPropertyEditor.GetVarValueAt(Index:Integer): Variant;
begin
  Result:=FPropList^[Index].GetVariant;
end;

function TPropertyEditor.GetWideStrValue: WideString;
begin
  Result:=GetWideStrValueAt(0);
end;

function TPropertyEditor.GetWideStrValueAt(Index: Integer): WideString;
begin
  Result:=FPropList^[Index].GetWideString;
end;

function TPropertyEditor.HasDefaultValue: Boolean;
var
  APropInfo: PPropInfo;
begin
  APropInfo:=FPropList^[0].PropInfo;
  if APropInfo=nil then exit(true);
  Result := APropInfo^.Default<>NoDefaultValue;
end;

function TPropertyEditor.GetUnicodeStrValue: UnicodeString;
begin
  Result:=GetUnicodeStrValueAt(0);
end;

function TPropertyEditor.GetUnicodeStrValueAt(Index: Integer): UnicodeString;
begin
  Result:=FPropList^[Index].GetUnicodeString;
end;

function TPropertyEditor.GetValue:ansistring;
begin
  Result:=oisUnknown;
end;

function TPropertyEditor.GetHint(HintType: TPropEditHint; x, y: integer): string;
var
  TypeHint: String;
begin
  Result := GetName + LineEnding + oisValue + ' ' + GetVisualValue;
  case GetPropType^.Kind of
   tkInteger : TypeHint:=oisInteger;
   tkInt64 : TypeHint:=oisInt64;
   tkBool : TypeHint:=oisBoolean;
   tkEnumeration : TypeHint:=oisEnumeration;
   tkChar, tkWChar : TypeHint:=oisChar;
   tkUnknown : TypeHint:=oisUnknown;
   tkObject : TypeHint:=oisObject;
   tkClass : TypeHint:=oisClass;
   tkQWord : TypeHint:=oisWord;
   tkString, tkLString, tkAString, tkWString : TypeHint:=oisString;
   tkFloat : TypeHint:=oisFloat;
   tkSet : TypeHint:=oisSet;
   tkMethod : TypeHint:=oisMethod;
   tkVariant : TypeHint:=oisVariant;
   tkArray : TypeHint:=oisArray;
   tkRecord : TypeHint:=oisRecord;
   tkInterface : TypeHint:=oisInterface;
  else
    TypeHint:='';
  end;
  if TypeHint<>'' then
    Result:=Result+LineEnding+TypeHint;
end;

function TPropertyEditor.GetDefaultValue: ansistring;
begin
  if not HasDefaultValue then
    raise EPropertyError.Create('No property default available');
  Result:='';
end;

function TPropertyEditor.GetVisualValue: ansistring;
begin
  if AllEqual then
  begin
    Result:=GetValue;
    {$IFDEF LCLCarbon}
    Result:=StringReplace(Result,LineEnding,LineFeedSymbolUTF8,[rfReplaceAll])
    {$ENDIF}
  end
  else
    Result:='';
end;

procedure TPropertyEditor.GetValues(Proc:TGetStrProc);
begin
end;

procedure TPropertyEditor.Initialize;
begin
  with FPropList^[0] do begin
    if Instance<>nil then
    begin
      if PropInfo=nil then
        raise Exception.Create('TPropertyEditor.Initialize 20240902134758 '+dbgsName(Self));
    end else if Field<>nil then begin
      if FieldTypeInfo=nil then
        raise Exception.Create('TPropertyEditor.Initialize 20240902134825 '+dbgsName(Self));
    end else
      raise Exception.Create('TPropertyEditor.Initialize 20240902134831 '+dbgsName(Self));
  end;
end;

procedure TPropertyEditor.Modified(Index: integer);
var
  PropName: ShortString;
begin
  if PropertyHook = nil then exit;
  with FPropList^[Index] do begin
    if PropInfo<>nil then
      PropName:=PropInfo^.Name
    else
      PropName:='';
  end;
  PropertyHook.Modified(Self, PropName);
end;

procedure TPropertyEditor.SetPropEntry(Index:Integer;
  AnInstance:TPersistent; APropInfo:PPropInfo);
begin
  FPropList^[Index]:=Default(TInstProp);
  with FPropList^[Index] do begin
    Instance:=AnInstance;
    PropInfo:=APropInfo;
  end;
end;

procedure TPropertyEditor.SetRecordFieldEntry(Index: Integer; AnInstance: Pointer;
  aName: PShortString; AFieldInfo: PTypeInfo);
begin
  FPropList^[Index]:=Default(TInstProp);
  with FPropList^[Index] do begin
    Field:=AnInstance;
    FieldName:=aName;
    FieldTypeInfo:=AFieldInfo;
  end;
end;

procedure TPropertyEditor.SetFloatValue(const NewValue: Extended);
var
  I: Integer;
begin
  for I:=0 to FPropCount-1 do
    with FPropList^[I] do begin
      SetFloat(NewValue);
      Modified(I);
    end;
end;

procedure TPropertyEditor.SetMethodValue(const NewValue: TMethod);
var
  I: Integer;
begin
  for I:=0 to FPropCount-1 do
    with FPropList^[I] do begin
      SetMethod(NewValue);
      Modified(I);
    end;
end;

procedure TPropertyEditor.SetInt64Value(const NewValue: Int64);
var
  I: Integer;
begin
  for I:=0 to FPropCount-1 do
    with FPropList^[I] do begin
      SetOrd(NewValue);
      Modified(I);
    end;
end;

procedure TPropertyEditor.SetIntfValue(const NewValue: IInterface);
var
  I: Integer;
begin
  for I := 0 to FPropCount - 1 do
    with FPropList^[I] do begin
      SetInterface(NewValue);
      Modified(I);
    end;
end;

procedure TPropertyEditor.SetOrdValue(const NewValue: Longint);
var
  I: Integer;
begin
  for I := 0 to FPropCount - 1 do
    with FPropList^[I] do begin
      SetOrd(NewValue);
      Modified(I);
    end;
end;

procedure TPropertyEditor.SetPtrValue(const NewValue: Pointer);
var
  I: Integer;
begin
  for I := 0 to FPropCount - 1 do
    with FPropList^[I] do begin
      SetPointerValue(NewValue);
      Modified(I);
    end;
end;

procedure TPropertyEditor.SetStrValue(const NewValue: AnsiString);
var
  I: Integer;
begin
  for I:=0 to FPropCount-1 do
    with FPropList^[I] do begin
      SetString(NewValue);
      Modified(I);
    end;
end;

procedure TPropertyEditor.SetWideStrValue(const NewValue: WideString);
var
  I: Integer;
begin
  for I:=0 to FPropCount-1 do
    with FPropList^[I] do begin
      SetWideString(NewValue);
      Modified(I);
    end;
end;

procedure TPropertyEditor.SetUnicodeStrValue(const NewValue: UnicodeString);
var
  I: Integer;
begin
  for I:=0 to FPropCount-1 do
    with FPropList^[I] do begin
      SetUnicodeString(NewValue);
      Modified(I);
    end;
end;

procedure TPropertyEditor.SetVarValue(const NewValue: Variant);
var
  I: Integer;
begin
  for I:=0 to FPropCount-1 do
    with FPropList^[I] do begin
      SetVariant(NewValue);  // todo field
      Modified(I);
    end;
end;

procedure TPropertyEditor.Revert;
var
  I: Integer;
begin
  if PropertyHook<>nil then
    for I:=0 to FPropCount-1 do
      with FPropList^[I] do PropertyHook.Revert(Instance,PropInfo); // todo field
end;

procedure TPropertyEditor.RevertToInherited;
var
  i: Integer;
  AncestorInstProp: TInstProp;
  Changed: Boolean;
  InstProp: TInstProp;
  NewOrdValue, OldOrdValue: Int64;
  OldStr, NewStr: String;
  OldWideStr, NewWideStr: WideString;
  OldUString, NewUString: UnicodeString;
  OldFloat, NewFloat: Extended;
  OldObj, NewObj: TObject;
  OldMethod, NewMethod: TMethod;
  OldInterface, NewInterface: IInterface;
begin
  if PropertyHook=nil then exit;
  Changed:=false;
  try
    for i:=0 to FPropCount-1 do
    begin
      InstProp:=FPropList^[i];
      if not PropertyHook.GetAncestorInstance(InstProp,AncestorInstProp) then
        continue;

      case InstProp.GetKind of
      tkInteger,tkChar,tkEnumeration,tkBool,tkInt64,tkQWord:
        begin
          OldOrdValue:=InstProp.GetOrd;
          NewOrdValue:=AncestorInstProp.GetOrd;
          if OldOrdValue=NewOrdValue then continue;
          Changed:=true;
          InstProp.SetOrd(NewOrdValue);
        end;
      tkSet:
        begin
          OldStr:=InstProp.GetSet(false);
          NewStr:=AncestorInstProp.GetSet(false);
          if OldStr=NewStr then continue;
          Changed:=true;
          InstProp.SetSet(NewStr);
        end;
      tkString,tkLString,tkAString:
        begin
          OldStr:=InstProp.GetString;
          NewStr:=AncestorInstProp.GetString;
          if OldStr=NewStr then continue;
          Changed:=true;
          InstProp.SetString(NewStr);
        end;
      tkWString:
        begin
          OldWideStr:=InstProp.GetWideString;
          NewWideStr:=AncestorInstProp.GetWideString;
          if OldWideStr=NewWideStr then continue;
          Changed:=true;
          InstProp.SetWideString(NewWideStr);
        end;
      tkUString:
        begin
          OldUString:=InstProp.GetUnicodeString;
          NewUString:=AncestorInstProp.GetUnicodeString;
          if OldUString=NewUString then continue;
          Changed:=true;
          InstProp.SetUnicodeString(NewUString);
        end;
      tkFloat:
        begin
          OldFloat:=InstProp.GetFloat;
          NewFloat:=AncestorInstProp.GetFloat;
          if OldFloat=NewFloat then continue;
          Changed:=true;
          InstProp.SetFloat(NewFloat);
        end;
      tkClass:
        begin
          OldObj:=InstProp.GetObject;
          NewObj:=AncestorInstProp.GetObject;
          if OldObj=NewObj then continue;
          Changed:=true;
          InstProp.SetObject(NewObj);
        end;
      tkMethod:
        begin
          OldMethod:=InstProp.GetMethod;
          NewMethod:=AncestorInstProp.GetMethod;
          if SameMethod(OldMethod,NewMethod) then continue;
          Changed:=true;
          InstProp.SetMethod(NewMethod);
        end;
      tkInterface:
        begin
          OldInterface:=InstProp.GetInterface;
          NewInterface:=AncestorInstProp.GetInterface;
          if OldInterface=NewInterface then continue;
          Changed:=true;
          InstProp.SetInterface(NewInterface);
        end;
      else
      end;
    end;
  finally
    if Changed then
      Modified;
  end;
end;

procedure TPropertyEditor.SetValue(const NewValue:ansistring);
begin
end;

function TPropertyEditor.ValueAvailable:Boolean;
var
  I:Integer;
begin
  Result:=True;
  for I:=0 to FPropCount-1 do
  begin
    if (FPropList^[I].Instance is TComponent)
    and (csCheckPropAvail in TComponent(FPropList^[I].Instance).ComponentStyle)
    then begin
      try
        GetValue;
        AllEqual;
      except
        Result:=False;
      end;
      Exit;
    end;
  end;
end;

function TPropertyEditor.GetInt64Value:Int64;
begin
  Result:=GetInt64ValueAt(0);
end;

function TPropertyEditor.GetInt64ValueAt(Index:Integer):Int64;
begin
  Result:=FPropList^[Index].GetOrd;
end;

function TPropertyEditor.GetIntfValue: IInterface;
begin
  Result := GetIntfValueAt(0);
end;

function TPropertyEditor.GetIntfValueAt(Index: Integer): IInterface;
begin
  Result:=FPropList^[Index].GetInterface;
end;

{ these three procedures implement the default render behavior of the
  object inspector's drop down list editor. You don't need to
  override the two measure procedures if the default width or height don't
  need to be changed. }
procedure TPropertyEditor.ListMeasureHeight(const AValue: ansistring;
  Index: Integer; ACanvas: TCanvas; var AHeight: Integer);
begin
  AHeight := ACanvas.TextHeight(AValue);
end;

procedure TPropertyEditor.ListMeasureWidth(const AValue: ansistring;
  Index: Integer; ACanvas: TCanvas; var AWidth: Integer);
begin
  //
end;

procedure TPropertyEditor.ListDrawValue(const AValue: ansistring; Index: Integer;
  ACanvas: TCanvas; const ARect: TRect; AState: TPropEditDrawState);
var
  Style : TTextStyle;
begin
  FillChar(Style{%H-},SizeOf(Style),0);
  With Style do begin
    Alignment := taLeftJustify;
    Layout := tlCenter;
    Opaque := False;
    Clipping := True;
    ShowPrefix := True;
    WordBreak := False;
    SingleLine := True;
    SystemFont := False;
  end;
  ACanvas.TextRect(ARect, ARect.Left+2,ARect.Top,AValue, Style);
end;

{ these three procedures implement the default render behavior of the
  object inspector's property row. You don't need to override the measure
  procedure if the default height don't need to be changed. }
procedure TPropertyEditor.PropMeasureHeight(const NewValue: ansistring;
  ACanvas: TCanvas; var AHeight: Integer);
begin
  //
end;

procedure TPropertyEditor.PropDrawName(ACanvas: TCanvas; const ARect: TRect;
  AState: TPropEditDrawState);
var
  Style : TTextStyle;
begin
  FillChar(Style{%H-},SizeOf(Style),0);
  With Style do begin
    Alignment := taLeftJustify;
    Layout := tlCenter;
    Opaque := False;
    Clipping := True;
    ShowPrefix := False;
    WordBreak := False;
    SingleLine := True;
    ExpandTabs := True;
    SystemFont := False;
  end;
  ACanvas.TextRect(ARect,ARect.Left+2,ARect.Top,GetName,Style);
end;

procedure TPropertyEditor.PropDrawValue(ACanvas: TCanvas; const ARect: TRect;
  AState: TPropEditDrawState);
begin
  DrawValue(GetVisualValue,ACanvas,ARect,AState);
end;

procedure TPropertyEditor.UpdateSubProperties;
begin
  if (OnSubPropertiesChanged<>nil) and SubPropertiesNeedsUpdate then
    OnSubPropertiesChanged(Self);
end;

function TPropertyEditor.SubPropertiesNeedsUpdate: boolean;
begin
  Result:=false;
end;

function TPropertyEditor.ValueIsStreamed: boolean;
begin
  Result := CallStoredFunction;

  if Result and HasDefaultValue then
    Result := GetDefaultValue<>GetVisualValue;
end;

function TPropertyEditor.IsRevertableToInherited: boolean;
begin
  Result:=(paRevertable in GetAttributes) and (GetComponent(0) is TComponent)
    and (csAncestor in TComponent(GetComponent(0)).ComponentState)
    and (PropertyHook<>nil)
    and (GetPropType^.Kind in
      [tkInteger,tkChar,tkEnumeration,tkBool,tkInt64,tkQWord,
       tkSet,
       tkString,tkLString,tkAString,
       tkWString,
       tkUString,
       tkFloat,
       tkClass,
       tkMethod,
       tkInterface]);
end;

function TPropertyEditor.GetVerbCount: Integer;
begin
  Result:=0;
  if HasDefaultValue then
    inc(Result); // show a menu item for default value only if there is default value
  if IsRevertableToInherited then
    inc(Result); // show a menu item for 'Revert to inherited'
end;

function TPropertyEditor.GetVerb(Index: Integer): string;
var
  i: Integer;
begin
  Result := '';
  i:=-1;
  if HasDefaultValue then begin
    inc(i);
    if i=Index then begin
      Result := Format(oisSetToDefault, [GetDefaultValue]);
      exit;
    end;
  end;
  if IsRevertableToInherited then begin
    inc(i);
    if i=Index then begin
      Result := oisRevertToInherited;
      exit;
    end;
  end;
end;

procedure TPropertyEditor.PrepareItem(Index: Integer; const AnItem: TMenuItem);
begin
  // overridden by descendants
end;

procedure TPropertyEditor.ExecuteVerb(Index: Integer);
var
  i: Integer;
begin
  i:=-1;
  if HasDefaultValue then begin
    inc(i);
    if i=Index then begin
      SetValue(GetDefaultValue);
      exit;
    end;
  end;
  if IsRevertableToInherited then begin
    inc(i);
    if i=Index then begin
      RevertToInherited;
      exit;
    end;
  end;
end;

{ TInstProp }

function TInstProp.GetTypeInfo: PTypeInfo;
begin
  if PropInfo<>nil then
    Result:=PropInfo^.PropType
  else
    Result:=FieldTypeInfo;
end;

function TInstProp.GetKind: TTypeKind;
begin
  if PropInfo<>nil then
    Result:=PropInfo^.PropType^.Kind
  else
    Result:=FieldTypeInfo^.Kind;
end;

function TInstProp.GetOrd: int64;
begin
  if Field<>nil then
    Result:=GetOrdField(Field,FieldTypeInfo)
  else
    Result:=GetOrdProp(Instance,PropInfo);
end;

procedure TInstProp.SetOrd(const Value: int64);
begin
  if Field<>nil then
    SetOrdField(Field,FieldTypeInfo,Value)
  else
    SetOrdProp(Instance,PropInfo,Value);
end;

function TInstProp.GetEnum: string;
begin
  if Field<>nil then
    Result:=GetEnumField(Field,FieldTypeInfo)
  else
    Result:=GetEnumProp(Instance,PropInfo);
end;

procedure TInstProp.SetEnum(const Value: string);
begin
  if Field<>nil then
    SetEnumField(Field,FieldTypeInfo,Value)
  else
    SetEnumProp(Instance,PropInfo,Value);
end;

function TInstProp.GetSet(Brackets: boolean): string;
begin
  if Field<>nil then
    Result:=GetSetField(Field,FieldTypeInfo,Brackets)
  else
    Result:=GetSetProp(Instance,PropInfo,Brackets);
end;

procedure TInstProp.SetSet(const Value: string);
begin
  if Field<>nil then
    SetSetField(Field,FieldTypeInfo,Value)
  else
    SetSetProp(Instance,PropInfo,Value);
end;

function TInstProp.GetString: AnsiString;
begin
  if Field<>nil then
    Result:=GetStringField(Field,FieldTypeInfo)
  else
    Result:=GetStrProp(Instance,PropInfo);
end;

procedure TInstProp.SetString(const Value: string);
begin
  if Field<>nil then
    SetStringField(Field,FieldTypeInfo,Value)
  else
    SetStrProp(Instance,PropInfo,Value);
end;

function TInstProp.GetWideString: WideString;
begin
  if Field<>nil then
    Result:=GetWideStringField(Field,FieldTypeInfo)
  else
    Result:=GetWideStrProp(Instance,PropInfo);
end;

procedure TInstProp.SetWideString(const Value: WideString);
begin
  if Field<>nil then
    SetWideStringField(Field,FieldTypeInfo,Value)
  else
    SetWideStrProp(Instance,PropInfo,Value);
end;

function TInstProp.GetUnicodeString: UnicodeString;
begin
  if Field<>nil then
    Result:=GetUnicodeStringField(Field,FieldTypeInfo)
  else
    Result:=GetUnicodeStrProp(Instance,PropInfo);
end;

procedure TInstProp.SetUnicodeString(const Value: UnicodeString);
begin
  if Field<>nil then
    SetUnicodeStringField(Field,FieldTypeInfo,Value)
  else
    SetUnicodeStrProp(Instance,PropInfo,Value);
end;

function TInstProp.GetRawByteString: RawByteString;
begin
  if Field<>nil then
    Result:=GetRawbyteStringField(Field,FieldTypeInfo)
  else
    Result:=GetRawbyteStrProp(Instance,PropInfo);
end;

procedure TInstProp.SetRawByteString(const Value: RawByteString);
begin
  if Field<>nil then
    SetRawbyteStringField(Field,FieldTypeInfo,Value)
  else
    SetRawByteStrProp(Instance,PropInfo,Value);
end;

function TInstProp.GetFloat: Extended;
begin
  if Field<>nil then
    Result:=GetFloatField(Field,FieldTypeInfo)
  else
    Result:=GetFloatProp(Instance,PropInfo);
end;

procedure TInstProp.SetFloat(const Value: Extended);
begin
  if Field<>nil then
    SetFloatField(Field,FieldTypeInfo,Value)
  else
    SetFloatProp(Instance,PropInfo,Value);
end;

function TInstProp.GetObject(MinClass: TClass): TObject;
begin
  if Field<>nil then
    Result:=GetObjectField(Field,FieldTypeInfo,MinClass)
  else
    Result:=GetObjectProp(Instance,PropInfo,MinClass);
end;

procedure TInstProp.SetObject(const Value: TObject);
begin
  if Field<>nil then
    SetObjectField(Field,FieldTypeInfo,Value)
  else
    SetObjectProp(Instance,PropInfo,Value);
end;

function TInstProp.GetPointerValue: Pointer;
begin
  if Field<>nil then
    Result:=GetPointerField(Field,FieldTypeInfo)
  else
    Result:=GetPointerProp(Instance,PropInfo);
end;

procedure TInstProp.SetPointerValue(const Value: Pointer);
begin
  if Field<>nil then
    SetPointerField(Field,FieldTypeInfo,Value)
  else
    SetPointerProp(Instance,PropInfo,Value);
end;

function TInstProp.GetMethod: TMethod;
begin
  if Field<>nil then
    Result:=GetMethodField(Field,FieldTypeInfo)
  else
    Result:=GetMethodProp(Instance,PropInfo);
end;

procedure TInstProp.SetMethod(const Value: TMethod);
begin
  if Field<>nil then
    SetMethodField(Field,FieldTypeInfo,Value)
  else
    LazSetMethodProp(Instance,PropInfo,Value);
end;

function TInstProp.GetInterface: IInterface;
begin
  if Field<>nil then
    Result:=GetInterfaceField(Field,FieldTypeInfo)
  else
    Result:=GetInterfaceProp(Instance,PropInfo);
end;

procedure TInstProp.SetInterface(const Value: IInterface);
begin
  if Field<>nil then
    SetInterfaceField(Field,FieldTypeInfo,Value)
  else
    SetInterfaceProp(Instance,PropInfo,Value);
end;

function TInstProp.GetVariant: Variant;
begin
  if Field<>nil then
    Result:=GetVariantField(Field,FieldTypeInfo)
  else
    Result:=GetVariantProp(Instance,PropInfo);
end;

procedure TInstProp.SetVariant(const Value: Variant);
begin
  if Field<>nil then
    SetVariantField(Field,FieldTypeInfo,Value)
  else
    SetVariantProp(Instance,PropInfo,Value);
end;

{ TOrdinalPropertyEditor }

function TOrdinalPropertyEditor.AllEqual: Boolean;
var
  I: Integer;
  V: Longint;
begin
  Result := False;
  if PropCount > 1 then
  begin
    V := GetOrdValue;
    for I := 1 to PropCount - 1 do
      if GetOrdValueAt(I) <> V then Exit;
  end;
  Result := True;
end;

function TOrdinalPropertyEditor.GetEditLimit: Integer;
begin
  Result := 63;
end;

function TOrdinalPropertyEditor.GetValue: ansistring;
begin
  Result:=OrdValueToVisualValue(GetOrdValue);
end;

function TOrdinalPropertyEditor.GetDefaultValue: ansistring;
begin
  Result:=OrdValueToVisualValue(GetDefaultOrdValue);
end;

function TOrdinalPropertyEditor.OrdValueToVisualValue(OrdValue: longint): string;
begin
  Result:=IntToStr(OrdValue);
end;

{ TIntegerPropertyEditor }

function TIntegerPropertyEditor.OrdValueToVisualValue(OrdValue: longint): string;
begin
  with GetTypeData(GetPropType)^ do begin
    {debugln('TIntegerPropertyEditor.OrdValueToVisualValue ',GetName,' ',dbgs(ord(OrdType)),' ',dbgs(OrdValue));
    case OrdType of
      otSByte : debugln('TIntegerPropertyEditor.OrdValueToVisualValue otSByte ',dbgs(ShortInt(OrdValue)));
      otUByte : debugln('TIntegerPropertyEditor.OrdValueToVisualValue otUByte ',dbgs(Byte(OrdValue)));
      otSWord : debugln('TIntegerPropertyEditor.OrdValueToVisualValue otSWord ',dbgs(SmallInt(OrdValue)));
      otUWord : debugln('TIntegerPropertyEditor.OrdValueToVisualValue otUWord ',dbgs(Word(OrdValue)));
      otULong : debugln('TIntegerPropertyEditor.OrdValueToVisualValue otULong ',dbgs(Cardinal(OrdValue)));
      else debugln('TIntegerPropertyEditor.OrdValueToVisualValue ??? ',dbgs(OrdValue));
    end;}

    case OrdType of
      otSByte : Result:= IntToStr(ShortInt(OrdValue));
      otUByte : Result:= IntToStr(Byte(OrdValue));
      otSWord : Result:= IntToStr(Integer(SmallInt(OrdValue)));// double conversion needed due to compiler bug 3534
      otUWord : Result:= IntToStr(Word(OrdValue));
      otULong : Result:= IntToStr(Cardinal(OrdValue));
      else Result := IntToStr(OrdValue);
    end;
    //debugln('TIntegerPropertyEditor.OrdValueToVisualValue ',Result);
  end;
end;

procedure TIntegerPropertyEditor.SetValue(const NewValue: AnsiString);

  procedure Error(const Args: array of const);
  begin
    raise EPropertyError.CreateResFmt(@SOutOfRange, Args);
  end;

var
  L: Int64;
begin
  L := StrToInt64(NewValue);
  with GetTypeData(GetPropType)^ do
    if OrdType = otULong then
    begin   // unsigned compare and reporting needed
      if (L < Cardinal(MinValue)) or (L > Cardinal(MaxValue)) then begin
        // bump up to Int64 to get past the %d in the format string
        Error([Int64(Cardinal(MinValue)), Int64(Cardinal(MaxValue))]);
        exit;
      end
    end
    else if (L < MinValue) or (L > MaxValue) then begin
      Error([MinValue, MaxValue]);
      exit;
    end;
  SetOrdValue(integer(L));
end;

{ TCharPropertyEditor }

function TCharPropertyEditor.OrdValueToVisualValue(OrdValue: longint): string;
var
  Ch: Char;
begin
  Ch := Chr(OrdValue);
  if Ch in [#33..#127] then
    Result := Ch
  else
    Result:='#'+IntToStr(Ord(Ch));
end;

procedure TCharPropertyEditor.SetValue(const NewValue: ansistring);
var
  L: Longint;
begin
  if Length(NewValue) = 0 then L := 0 else
    if Length(NewValue) = 1 then L := Ord(NewValue[1]) else
      if NewValue[1] = '#' then L := StrToInt(Copy(NewValue, 2, Maxint)) else
      begin
        {raise EPropertyError.CreateRes(@SInvalidPropertyValue)};
        exit;
      end;
  with GetTypeData(GetPropType)^ do
    //Only Chars < #$80 are valid single-byte UTF-8 codepoints,
    //so use this instead of MaxValue (255 for tkChar), since LCL is UTF-8
    if (L < MinValue) or (L > $7F) then begin
      {raise EPropertyError.CreateResFmt(@SOutOfRange, [MinValue, MaxValue])};
      exit;
    end;
  SetOrdValue(L);
end;

{ TEnumPropertyEditor }

function TEnumPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paValueList, paSortList, paRevertable];
end;

function TEnumPropertyEditor.OrdValueToVisualValue(OrdValue: longint): string;
var
  L: Longint;
  TypeData: PTypeData;
begin
  L := OrdValue;
  TypeData := GetTypeData(GetPropType);
  with TypeData^ do
    if (L < MinValue) or (L > MaxValue) then
      L := MaxValue;
  Result := GetEnumName(GetPropType, L);
end;

function TEnumPropertyEditor.GetVisualValue: ansistring;
begin
  if FInvalid then
    Result := oisInvalid
  else
    Result := inherited GetVisualValue;
end;

procedure TEnumPropertyEditor.GetValues(Proc: TGetStrProc);
var
  I: Integer;
  EnumType: PTypeInfo;
  s: String;
begin
  EnumType := GetPropType;
  with GetTypeData(EnumType)^ do
    for I := MinValue to MaxValue do begin
      s := GetEnumName(EnumType, I);
      Proc(s);
    end;
end;

procedure TEnumPropertyEditor.SetValue(const NewValue: ansistring);
var
  I: Integer;
begin
  I := GetEnumValue(GetPropType, NewValue);
  FInvalid := I < 0;
  if not FInvalid then
    SetOrdValue(I);
end;

{ TBoolPropertyEditor  }

function TBoolPropertyEditor.OrdValueToVisualValue(OrdValue: longint): string;
begin
  if OrdValue = 0 then
    Result := 'False'
  else
    Result := 'True';
  if FPropertyHook.GetCheckboxForBoolean then
    Result := '(' + Result + ')';
end;

function TBoolPropertyEditor.GetVisualValue: ansistring;
begin
  Result := inherited GetVisualValue;
  if Result = '' then
    Result := oisMixed;
end;

procedure TBoolPropertyEditor.GetValues(Proc: TGetStrProc);
begin
  Proc('False');
  Proc('True');
end;

procedure TBoolPropertyEditor.SetValue(const NewValue: ansistring);
var
  I: Integer;
begin
  if (CompareText(NewValue, 'False') = 0)
  or (CompareText(NewValue, '(False)') = 0)
  or (CompareText(NewValue, 'F') = 0) then
    I := 0
  else 
  if (CompareText(NewValue, 'True') = 0)
  or (CompareText(NewValue, '(True)') = 0)
  or (CompareText(NewValue, 'T') = 0) then
    I := 1
  else
    I := StrToInt(NewValue);
  SetOrdValue(I);
end;

procedure TBoolPropertyEditor.PropDrawValue(ACanvas: TCanvas; const ARect: TRect;
                                            AState: TPropEditDrawState);
var
  TxtRect: TRect;
begin
  TxtRect := DrawCheckValue(ACanvas, ARect, AState, GetOrdValue<>0);
  if TxtRect.Top <> -100 then
    inherited PropDrawValue(ACanvas, TxtRect, AState);
end;

{ TInt64PropertyEditor }

function TInt64PropertyEditor.AllEqual: Boolean;
var
  I: Integer;
  V: Int64;
begin
  Result := False;
  if PropCount > 1 then
  begin
    V := GetInt64Value;
    for I := 1 to PropCount - 1 do
      if GetInt64ValueAt(I) <> V then Exit;
  end;
  Result := True;
end;

function TInt64PropertyEditor.GetEditLimit: Integer;
begin
  Result := 63;
end;

function TInt64PropertyEditor.GetValue: ansistring;
begin
  Result := IntToStr(GetInt64Value);
end;

procedure TInt64PropertyEditor.SetValue(const NewValue: ansistring);
begin
  SetInt64Value(StrToInt64(NewValue));
end;


{ TQWordPropertyEditor }

function TQWordPropertyEditor.GetValue: ansistring;
begin
  Result := IntToStr(QWord(GetInt64Value));
end;

procedure TQWordPropertyEditor.SetValue(const NewValue: ansistring);
begin
  SetInt64Value(Int64(StrToQWord(NewValue)));
end;

{ TFloatPropertyEditor }

function TFloatPropertyEditor.FloatInRange(AValue: Extended; AType: TFloatType): Boolean;
{$ifndef FPC_HAS_TYPE_DOUBLE}
const MaxDouble = Math.MaxSingle;
{$endif}
{$ifndef FPC_HAS_TYPE_EXTENDED}
const MaxExtended = MaxDouble;
{$endif}
{$ifndef FPC_HAS_TYPE_COMP}
const
  MaxComp =  9223372036854775807;
  MinComp = -9223372036854775808;
{$endif}
begin
  try
    case AType of
      ftSingle:   Result := {$ifndef FPC_HAS_TYPE_DOUBLE}
                            //Extended=Single, so anything out of range of a Single will be +/-Infinity
                            not IsInfinite(AValue);
                            {$else}
                            (AValue <= MaxSingle) and (AValue >= -MaxSingle);
                            {$endif}
      ftDouble:   Result := {$ifndef FPC_HAS_TYPE_EXTENDED}
                            //Extended=Double, so anything out of range of a Double will be +/-Infinity
                            not IsInfinite(AValue);
                            {$else}
                            (AValue <= MaxDouble) and (AValue >= -MaxDouble);
                            {$endif}
      ftExtended: Result := not IsInfinite(AValue);
      ftComp:     Result := (AValue <= MaxComp) and (AValue >= MinComp);
      ftCurr:     Result := (AValue <= MaxCurrency) and (AValue >= MinCurrency);
    end;
  except
    //Currency comparisons can cause Floating Point overflow on Win64 (i.e. compared with +/-MaxDouble, +/-MaxExtended, MaxComp, MinComp, +/-Inf)
    //and on Win32 (i.e with +/-MaxExtended), not tested other platforms
    Result := False;
  end;
end;

function TFloatPropertyEditor.AllEqual: Boolean;
var
  I: Integer;
  V: Extended;
begin
  Result := False;
  if PropCount > 1 then
  begin
    V := GetFloatValue;
    for I := 1 to PropCount - 1 do
      if GetFloatValueAt(I) <> V then Exit;
  end;
  Result := True;
end;

function TFloatPropertyEditor.FormatValue(const AValue: Extended): ansistring;
const
  Precisions: array[TFloatType] of Integer = (7, 15, 19, 19, 19);
var
  FS: TFormatSettings;
begin
  FS := DefaultFormatSettings;
  FS.DecimalSeparator := '.'; //It's Pascal sourcecode representation of a float, not a textual (i18n) one
  Result := FloatToStrF(AValue, ffGeneral,
    Precisions[GetTypeData(GetPropType)^.FloatType], 0, FS);
end;

function TFloatPropertyEditor.GetDefaultValue: ansistring;
begin
  if not HasDefaultValue then
    raise EPropertyError.Create('No property default available');
  Result:=FormatValue(0);
end;

function TFloatPropertyEditor.GetValue: ansistring;
begin
  Result := FormatValue(GetFloatValue);
end;

procedure TFloatPropertyEditor.SetValue(const NewValue: ansistring);
var
  FS: TFormatSettings;
  NewFloat: Extended;
  fType: TFloatType;
begin
  //writeln('TFloatPropertyEditor.SetValue A ',NewValue,'  ',StrToFloat(NewValue));
  FS := DefaultFormatSettings;
  FS.DecimalSeparator := '.'; //after all, this is Pascal, so we expect a period
  if not TryStrToFloat(NewValue, NewFloat, FS) then
    //if this failed, assume the user entered DS from his current locale
    NewFloat := StrToFloat(NewValue, DefaultFormatSettings);
  //conversion succeeded, out of range input gets converted to +/-Inf as well, not only literal user input as '+Inf'
  //so check user input instead of IsInfinite now
  if (Pos('inf',LowerCase(NewValue)) > 0) then
    raise EPropertyError.Create(oisInfinityNotSupported);
  //Check NaN before checking for range, since you cannot compare NaN to anything
  if IsNan(NewFloat) then
    raise EPropertyError.Create(oisNaNNotSupported);
  fType := GetTypeData(GetPropType)^.FloatType;
  if not FloatInRange(NewFloat, fType) then
    raise EPropertyError.Create(oisValueOutOfRange);
  SetFloatValue(NewFloat);
  //writeln('TFloatPropertyEditor.SetValue B ',GetValue);
end;


{ TStringPropertyEditor }

function TStringPropertyEditor.AllEqual: Boolean;
var
  I: Integer;
  V: ansistring;
begin
  Result := False;
  if PropCount > 1 then begin
    V := GetStrValue;
    for I := 1 to PropCount - 1 do
      if GetStrValueAt(I) <> V then Exit;
  end;
  Result := True;
end;

function TStringPropertyEditor.GetEditLimit: Integer;
begin
  if GetPropType^.Kind = tkSString then
    Result := GetTypeData(GetPropType)^.MaxLength
  else
    Result := $0FFF;
end;

function TStringPropertyEditor.GetValue: ansistring;
begin
  Result := GetStrValue;
end;

procedure TStringPropertyEditor.SetValue(const NewValue: ansistring);
begin
  SetStrValue(NewValue);
end;

{ TPasswordStringPropertyEditor }

function TPasswordStringPropertyEditor.GetPassword: string;
begin
  if GetVisualValue<>'' then
    Result:='*****'
  else
    Result:='';
end;

procedure TPasswordStringPropertyEditor.PropDrawValue(ACanvas: TCanvas;
  const ARect: TRect; AState: TPropEditDrawState);
begin
  DrawValue(GetPassword,ACanvas,ARect,AState);
end;

{ TWideStringPropertyEditor }

function TWideStringPropertyEditor.AllEqual: Boolean;
var
  I: Integer;
  V: widestring;
begin
  Result := False;
  if PropCount > 1 then begin
    V := GetWideStrValue;
    for I := 1 to PropCount - 1 do
      if GetWideStrValueAt(I) <> V then Exit;
  end;
  Result := True;
end;

function TWideStringPropertyEditor.GetValue: ansistring;
begin
  Result:=UTF8Encode(GetWideStrValue);
end;

procedure TWideStringPropertyEditor.SetValue(const NewValue: ansistring);
begin
  SetWideStrValue(UTF8Decode(NewValue));
end;

{ TPasswordWideStringPropertyEditor }

function TPasswordWideStringPropertyEditor.GetPassword: WideString;
begin
  if GetVisualValue<>'' then
    Result:='*****'
  else
    Result:='';
end;

procedure TPasswordWideStringPropertyEditor.PropDrawValue(ACanvas: TCanvas;
  const ARect: TRect; AState: TPropEditDrawState);
begin
  DrawValue(UTF8Encode(GetPassword),ACanvas,ARect,AState);
end;

{ TUnicodeStringPropertyEditor }

function TUnicodeStringPropertyEditor.AllEqual: Boolean;
var
  I: Integer;
  V: UnicodeString;
begin
  Result := False;
  if PropCount > 1 then begin
    V := GetUnicodeStrValue;
    for I := 1 to PropCount - 1 do
      if GetUnicodeStrValueAt(I) <> V then Exit;
  end;
  Result := True;
end;

function TUnicodeStringPropertyEditor.GetValue: ansistring;
begin
  Result:=UTF8Encode(GetUnicodeStrValue);
end;

procedure TUnicodeStringPropertyEditor.SetValue(const NewValue: ansistring);
begin
  SetUnicodeStrValue(UTF8Decode(NewValue));
end;

{ TNestedPropertyEditor }

constructor TNestedPropertyEditor.Create(Parent: TPropertyEditor);
begin
  FParentEditor:=Parent;
  FPropertyHook:=Parent.PropertyHook;
  FPropList:=Parent.FPropList;
  FPropCount:=Parent.PropCount;
end;

destructor TNestedPropertyEditor.Destroy;
begin
end;

{ TSetElementPropertyEditor }

constructor TSetElementPropertyEditor.Create(Parent: TPropertyEditor;
 AElement: Integer);
begin
  inherited Create(Parent);
  FElement := AElement;
end;

// The IntegerSet (a set of size of an integer)
// don't know if this is always valid
type
  TIntegerSet = set of 0..SizeOf(Integer) * 8 - 1;

function TSetElementPropertyEditor.AllEqual: Boolean;
var
  I: Integer;
  S: TIntegerSet;
  V: Boolean;
begin
  Result := False;
  if PropCount > 1 then begin
    Integer(S) := GetOrdValue;
    V := FElement in S;
    for I := 1 to PropCount - 1 do begin
      Integer(S) := GetOrdValueAt(I);
      if (FElement in S) <> V then Exit;
    end;
  end;
  Result := True;
end;

function TSetElementPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paValueList, paSortList];
end;

function TSetElementPropertyEditor.GetName: shortstring;
begin
  Result := GetEnumName(GetTypeData(GetPropType)^.CompType, FElement);
end;

function TSetElementPropertyEditor.GetValue: ansistring;
var
  S: TIntegerSet;
begin
  Integer(S) := GetOrdValue;
  Result := BooleanIdents[FElement in S];
  if FPropertyHook.GetCheckboxForBoolean then
    Result := '(' + Result + ')';
end;

function TSetElementPropertyEditor.GetVerbCount: Integer;
begin
  Result:=0;
end;

function TSetElementPropertyEditor.GetVisualValue: ansistring;
begin
  Result := inherited GetVisualValue;
  if Result = '' then
    Result := oisMixed;
end;

procedure TSetElementPropertyEditor.GetValues(Proc: TGetStrProc);
begin
  Proc(BooleanIdents[False]);
  Proc(BooleanIdents[True]);
end;

procedure TSetElementPropertyEditor.SetValue(const NewValue: ansistring);
var
  S: TIntegerSet;
begin
  Integer(S) := GetOrdValue;
  if (CompareText(NewValue, 'True') = 0)
  or (CompareText(NewValue, '(True)') = 0) then
    Include(S, FElement)
  else
    Exclude(S, FElement);
  SetOrdValue(Integer(S));
end;

function TSetElementPropertyEditor.ValueIsStreamed: boolean;
var
  S1, S2: TIntegerSet;
begin
  Result := CallStoredFunction;

  if Result and HasDefaultValue then
  begin
    Integer(S1) := GetOrdValue;
    Integer(S2) := GetDefaultOrdValue;
    Result := (FElement in S1) <> (FElement in S2);
  end;
end;

procedure TSetElementPropertyEditor.PropDrawValue(ACanvas: TCanvas; const ARect: TRect;
                                                  AState: TPropEditDrawState);
var
  S: TIntegerSet;
  TxtRect: TRect;
begin
  Integer(S) := GetOrdValue;
  TxtRect := DrawCheckValue(ACanvas, ARect, AState, FElement in S);
  if TxtRect.Top <> -100 then
    inherited PropDrawValue(ACanvas, TxtRect, AState);
end;

{ TSetPropertyEditor }

function TSetPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paSubProperties, paReadOnly, paRevertable];
end;

function TSetPropertyEditor.GetEditLimit: Integer;
begin
  Result := 0;
end;

procedure TSetPropertyEditor.GetProperties(Proc: TGetPropEditProc);
var
  I: Integer;
  EnumType: PTypeInfo;
begin
  EnumType := GetTypeData(GetPropType)^.CompType;
  with GetTypeData(EnumType)^ do
    for I := MinValue to MaxValue do
      Proc(TSetElementPropertyEditor.Create(Self, I));
end;

procedure TSetPropertyEditor.SetValue(const NewValue: ansistring);
var
  S: TIntegerSet;
  TypeInfo: PTypeInfo;
  I: Integer;
begin
  S := [];
  TypeInfo := GetTypeData(GetPropType)^.CompType;
  for I := 0 to SizeOf(Integer) * 8 - 1 do
    if Pos(GetEnumName(TypeInfo, I), NewValue) > 0 then
      Include(S, I);
  SetOrdValue(Integer(S));
end;

function TSetPropertyEditor.OrdValueToVisualValue(OrdValue: longint): string;
var
  S: TIntegerSet;
  TypeInfo: PTypeInfo;
  I: Integer;
begin
  Integer(S) := OrdValue;
  TypeInfo := GetTypeData(GetPropType)^.CompType;
  Result := '[';
  for I := 0 to SizeOf(Integer) * 8 - 1 do
    if I in S then
    begin
      if Length(Result) <> 1 then Result := Result + ',';
      Result := Result + GetEnumName(TypeInfo, I);
    end;
  Result := Result + ']';
end;

{ TStructurePropertyEditor }

function TStructurePropertyEditor.EditorFilter(const AEditor: TPropertyEditor): Boolean;
begin
  Result := IsInteresting(AEditor, SubPropsTypeFilter, SubPropsNameFilter);
end;

procedure TStructurePropertyEditor.ListSubProps(Prop: TPropertyEditor);
begin
  FSubProps.Add(Prop);
end;

procedure TStructurePropertyEditor.SetSubPropsTypeFilter(const AValue: TTypeKinds);
begin
  if FSubPropsTypeFilter = AValue then exit;
  FSubPropsTypeFilter := AValue;
end;

constructor TStructurePropertyEditor.Create(Hook: TPropertyEditorHook; APropCount: Integer);
begin
  inherited Create(Hook, APropCount);
  FSubPropsTypeFilter := tkAny;
end;

destructor TStructurePropertyEditor.Destroy;
begin
  FreeAndNil(FSubProps);
  inherited Destroy;
end;

function TStructurePropertyEditor.AllEqual: Boolean;
begin
  Result:=true;
end;

{ TListElementPropertyEditor }

constructor TListElementPropertyEditor.Create(Parent: TListPropertyEditor;
  AnIndex: integer);
begin
  inherited Create(Parent);
  FList:=Parent;
  FIndex:=AnIndex;
end;

destructor TListElementPropertyEditor.Destroy;
begin
  inherited Destroy;
end;

function TListElementPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result:=List.GetElementAttributes(Self);
end;

function TListElementPropertyEditor.GetName: shortstring;
begin
  Result:=List.GetElementName(Self);
end;

procedure TListElementPropertyEditor.GetProperties(Proc: TGetPropEditProc);
begin
  List.GetElementProperties(Self,Proc);
end;

function TListElementPropertyEditor.GetValue: ansistring;
begin
  Result:=List.GetElementValue(Self);
end;

procedure TListElementPropertyEditor.GetValues(Proc: TGetStrProc);
begin
  List.GetElementValues(Self,Proc);
end;

procedure TListElementPropertyEditor.SetValue(const NewValue: ansistring);
begin
  List.SetElementValue(Self,NewValue);
end;

{ TListPropertyEditor }

function TListPropertyEditor.GetElementCount: integer;
begin
  if not IsSaving then
    Result:=SavedElements.Count
  else
    Result:=ReadElementCount;
end;

function TListPropertyEditor.GetElement(Index: integer): TPersistent;
var
  ElementCount: integer;
begin
  // do some checks
  if (Index<0) then
    raise Exception.Create('TListPropertyEditor.GetElement Index='+IntToStr(Index));
  ElementCount:=GetElementCount;
  if Index>=ElementCount then
    raise Exception.Create('TListPropertyEditor.GetElement Index='+IntToStr(Index)
      +' Count='+IntToStr(ElementCount));
  // get element
  if not IsSaving then
    Result:=TPersistent(SavedElements[Index])
  else
    Result:=ReadElement(Index);
end;

function TListPropertyEditor.GetElement(Element: TListElementPropertyEditor
  ): TPersistent;
begin
  Result:=GetElement(Element.TheIndex);
end;

function TListPropertyEditor.GetElementPropEditor(Index: integer
  ): TListElementPropertyEditor;
// called by GetProperties to get the element property editors
begin
  if not IsSaving then
    Result:=TListElementPropertyEditor(SavedPropertyEditors[Index])
  else
    Result:=CreateElementPropEditor(Index);
end;

procedure TListPropertyEditor.SaveElements;
begin
  if IsSaving then exit;
  BeginSaveElement;
  FreeElementPropertyEditors;
  DoSaveElements;
  FSubPropertiesChanged:=false;
  EndSaveElement;
end;

function TListPropertyEditor.SubPropertiesNeedsUpdate: boolean;
var i: integer;
begin
  Result:=true;
  if FSubPropertiesChanged then exit;
  FSubPropertiesChanged:=true;
  if SavedList<>GetComponent(0) then exit;
  if ReadElementCount<>SavedElements.Count then exit;
  for i:=0 to SavedElements.Count-1 do
    if TPersistent(SavedElements[i])<>ReadElement(i) then exit;
  Result:=false;
  FSubPropertiesChanged:=false;
end;

function TListPropertyEditor.ReadElementCount: integer;
var
  TheList: TObject;
begin
  TheList := GetObjectValue;
  if TheList is TList then
    Result := TList(TheList).Count
  else
    Result := 0;
end;

function TListPropertyEditor.ReadElement(Index: integer): TPersistent;
var
  obj: TObject;
begin
  obj := TObject(TList(GetObjectValue).Items[Index]);
  if obj is TPersistent then
    Result:=TPersistent(obj)
  else
    raise EInvalidOperation.CreateFmt('List element %d is not a TPersistent descendant', [Index]);
end;

function TListPropertyEditor.CreateElementPropEditor(Index: integer
  ): TListElementPropertyEditor;
begin
  Result:=TListElementPropertyEditor.Create(Self,Index);
end;

procedure TListPropertyEditor.BeginSaveElement;
begin
  inc(FSaveElementLock);
end;

procedure TListPropertyEditor.EndSaveElement;
begin
  dec(FSaveElementLock);
  if FSaveElementLock<0 then
    DebugLn('TListPropertyEditor.EndSaveElement ERROR: FSaveElementLock=',
      IntToStr(FSaveElementLock));
end;

procedure TListPropertyEditor.DoSaveElements;
var
  i, ElementCount: integer;
begin
  SavedList:=GetComponent(0);
  ElementCount:=GetElementCount;
  SavedElements.Count:=ElementCount;
  for i:=0 to ElementCount-1 do
    SavedElements[i]:=GetElement(i);
  SavedPropertyEditors.Count:=ElementCount;
  for i:=0 to ElementCount-1 do
    SavedPropertyEditors[i]:=GetElementPropEditor(i);
end;

procedure TListPropertyEditor.FreeElementPropertyEditors;
var
  i: integer;
begin
  for i:=0 to SavedPropertyEditors.Count-1 do
    TObject(SavedPropertyEditors[i]).Free;
  SavedPropertyEditors.Clear;
end;

function TListPropertyEditor.GetElementAttributes(
  Element: TListElementPropertyEditor
  ): TPropertyAttributes;
begin
  Result:= [paReadOnly];
end;

function TListPropertyEditor.GetElementName(Element: TListElementPropertyEditor
  ): shortstring;
begin
  Result:='';
end;

procedure TListPropertyEditor.GetElementProperties(
  Element: TListElementPropertyEditor; Proc: TGetPropEditProc);
begin

end;

function TListPropertyEditor.GetElementValue(Element: TListElementPropertyEditor
  ): ansistring;
begin
  Result:='';
end;

procedure TListPropertyEditor.GetElementValues(
  Element: TListElementPropertyEditor; Proc: TGetStrProc);
begin

end;

procedure TListPropertyEditor.SetElementValue(
  Element: TListElementPropertyEditor; NewValue: ansistring);
begin

end;

function TListPropertyEditor.IsSaving: boolean;
begin
  Result:=SaveElementLock>0;
end;

constructor TListPropertyEditor.Create(Hook: TPropertyEditorHook;
  APropCount: Integer);
begin
  inherited Create(Hook, APropCount);
  SavedElements:=TList.Create;
  SavedPropertyEditors:=TList.Create;
end;

destructor TListPropertyEditor.Destroy;
begin
  UnregisterListPropertyEditor(Self);
  FreeElementPropertyEditors;
  FreeAndNil(SavedPropertyEditors);
  FreeAndNil(SavedElements);
  inherited Destroy;
end;

function TListPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result:= [paSubProperties, paDynamicSubProps, paReadOnly, paDialog];
end;

procedure TListPropertyEditor.GetProperties(Proc: TGetPropEditProc);
var
  i, ElementCount: integer;
begin
  SaveElements;
  ElementCount:=GetElementCount;
  for i:=0 to ElementCount-1 do
    Proc(GetElementPropEditor(i));
end;

function TListPropertyEditor.GetValue: AnsiString;
var
  ElementCount: integer;
begin
  ElementCount:=GetElementCount;
  if ElementCount<>1 then
    Result:=IntToStr(GetElementCount)+' items'
  else
    Result:='1 item';
end;

procedure TListPropertyEditor.Initialize;
begin
  inherited Initialize;
  RegisterListPropertyEditor(Self);
  SaveElements;
end;


const
  CollectionForm: TCollectionPropertyEditorForm = nil;

//  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

function TCollectionPropertyEditor.ReadElement(Index: integer): TPersistent;
var
  Collection: TCollection;
begin
  Collection:=TCollection(GetObjectValue);
  Result:=Collection.Items[Index];
end;

function TCollectionPropertyEditor.GetElementAttributes(
  Element: TListElementPropertyEditor): TPropertyAttributes;
begin
  Result := [paSubProperties, paReadOnly];
end;

function TCollectionPropertyEditor.GetElementName(
  Element: TListElementPropertyEditor): shortstring;
begin
  Result:=inherited GetElementName(Element);
end;

procedure TCollectionPropertyEditor.GetElementProperties(
  Element: TListElementPropertyEditor; Proc: TGetPropEditProc);
begin
  GetPersistentProperties(GetElement(Element),tkProperties,PropertyHook,Proc,nil);
end;

function TCollectionPropertyEditor.GetElementValue(
  Element: TListElementPropertyEditor): ansistring;
begin
  Result:=IntToStr(TCollectionItem(GetElement(Element)).ID);
end;

procedure TCollectionPropertyEditor.GetElementValues(
  Element: TListElementPropertyEditor; Proc: TGetStrProc);
begin
  inherited GetElementValues(Element, Proc);
end;

procedure TCollectionPropertyEditor.SetElementValue(
  Element: TListElementPropertyEditor; NewValue: ansistring);
begin
  inherited SetElementValue(Element, NewValue);
end;

function TCollectionPropertyEditor.ReadElementCount: integer;
var
  Collection: TObject;
begin
  Collection := GetObjectValue;
  if Collection is TCollection then
    Result := TCollection(Collection).Count
  else
    Result := 0;
end;

function TCollectionPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paDialog, paReadOnly];
end;

class function TCollectionPropertyEditor.ShowCollectionEditor(
  ACollection: TCollection; 
  OwnerPersistent: TPersistent; 
  const PropName: String): TCustomForm;
begin
  if CollectionForm = nil then
    CollectionForm := TCollectionPropertyEditorForm.Create(Application);
  CollectionForm.SetCollection(ACollection, OwnerPersistent, PropName);
  CollectionForm.actAdd.Visible := true;
  CollectionForm.actDel.Visible := true;
  CollectionForm.AddButton.Left := 0;
  CollectionForm.DeleteButton.Left := 1;
  CollectionForm.DividerToolButton.Show;
  CollectionForm.DividerToolButton.Left := CollectionForm.DeleteButton.Left + 1;
  SetPopupModeParentForPropertyEditor(CollectionForm);
  CollectionForm.EnsureVisible;
  CollectionForm.UpdateButtons;
  Result:=CollectionForm;
end;

procedure TCollectionPropertyEditor.Edit;
var
  TheCollection: TCollection;
begin
  TheCollection := TCollection(GetObjectValue);
  if TheCollection = nil then
    raise Exception.Create('Collection=nil');
  ShowCollectionEditor(TheCollection, GetComponent(0), GetName);
end;

{ TDisabledCollectionPropertyEditor }

function TDisabledCollectionPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paDialog, paReadOnly, paDisableSubProperties];
end;


{ TNoAddDeleteCollectionPropertyEditor }

class function TNoAddDeleteCollectionPropertyEditor.ShowCollectionEditor(
  ACollection: TCollection; OwnerPersistent: TPersistent;
  const PropName: String): TCustomForm;
begin
  if CollectionForm = nil then
    CollectionForm := TCollectionPropertyEditorForm.Create(Application);
  CollectionForm.SetCollection(ACollection, OwnerPersistent, PropName);
  CollectionForm.actAdd.Visible := false;
  CollectionForm.actDel.Visible := false;
  CollectionForm.DividerToolButton.Hide;
  SetPopupModeParentForPropertyEditor(CollectionForm);
  CollectionForm.EnsureVisible;
  CollectionForm.UpdateButtons;
  Result := CollectionForm;
end;


{ TClassPropertyEditor }

function TClassPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paSubProperties, paReadOnly];
end;

procedure TClassPropertyEditor.GetProperties(Proc: TGetPropEditProc);
var
  selection: TPersistentSelectionList;
begin
  selection := GetSelections;
  if selection = nil then exit;
  GetPersistentProperties(
    selection, SubPropsTypeFilter + [tkClass], PropertyHook, Proc, @EditorFilter);
  selection.Free;
end;

function TClassPropertyEditor.GetSelections: TPersistentSelectionList;
var
  i: Integer;
  subItem: TPersistent;
begin
  Result := TPersistentSelectionList.Create;
  try
    for i := 0 to PropCount - 1 do begin
      subItem := TPersistent(GetObjectValueAt(i));
      if subItem <> nil then
        Result.Add(subItem);
    end;
  except
    Result.Free;
    raise;
  end;
end;

function TClassPropertyEditor.GetValue: String;
begin
  if FHideClassName then
    Result:=''
  else
    Result:='(' + GetPropType^.Name + ')';
end;

function TClassPropertyEditor.ValueIsStreamed: boolean;
var
  I: Integer;
begin
  Result := inherited ValueIsStreamed;
  if not Result then
    Exit;

  if FSubProps=nil then
  begin
    FSubProps := TObjectList.Create(True);
    GetProperties(@ListSubProps);
  end;

  for I := 0 to FSubProps.Count-1 do
    if TPropertyEditor(FSubProps[I]).ValueIsStreamed then
      Exit(True);
  Result := False;
end;

{ TMethodPropertyEditor }

function TMethodPropertyEditor.AllEqual: Boolean;
var
  I: Integer;
  CurFirstValue, AnotherValue: TMethod;
begin
  Result := False;
  if PropCount > 1 then begin
    CurFirstValue := GetMethodValue;
    for I := 1 to PropCount - 1 do begin
      AnotherValue := GetMethodValueAt(I);
      // Note: compare Code and Data
      if (AnotherValue.Code <> CurFirstValue.Code)
      or (AnotherValue.Data <> CurFirstValue.Data) then
        Exit;
    end;
  end;
  Result := True;
end;

procedure TMethodPropertyEditor.Edit;
{ If the method does not exist in current lookuproot: create it
  Then jump to the source.
  
  For inherited methods this means: A new method is created and a call of
  the ancestor value is added. Then the IDE jumps to the new method body.
}
var
  NewMethodName: String;
  r: TModalResult;
begin
  NewMethodName := GetValue;
  {$IFDEF VerboseMethodPropEdit}
  debugln(['TMethodPropertyEditor.Edit OldValue="',NewMethodName,'" FromLookupRoot=',(IsValidIdent(NewMethodName, True, True) and PropertyHook.MethodFromLookupRoot(GetMethodValue))]);
  DumpStack;
  {$ENDIF}
  if IsValidIdent(NewMethodName)
  and PropertyHook.MethodFromLookupRoot(GetMethodValue) then
  begin
    {$IFDEF VerboseMethodPropEdit}
    debugln(['TMethodPropertyEditor.Edit Show']);
    {$ENDIF}
    PropertyHook.ShowMethod(NewMethodName);
  end else begin
    // the current method is from the another class (e.g. ancestor or frame)
    if IsValidIdent(NewMethodName) then
      r:=QuestionDlg('Override or jump',
        'The event "'+GetName+'" currently points to an inherited method.',
        mtConfirmation,[mrYes,'Create Override',mrOk,'Jump to inherited method',mrCancel],
        0)
    else
      r:=mrYes;
    case r of
    mrYes:
      begin
        // -> add an override with the default name
        NewMethodName := GetFormMethodName;
        {$IFDEF VerboseMethodPropEdit}
        debugln(['TMethodPropertyEditor.Edit NewValue="',NewMethodName,'"']);
        {$ENDIF}
        Assert(IsValidIdent(NewMethodName),'Method name "'+NewMethodName+'" must be an identifier');
        NewMethodName:=PropertyHook.LookupRoot.ClassName+'.'+NewMethodName;
        {$IFDEF VerboseMethodPropEdit}
        debugln(['TMethodPropertyEditor.Edit CreateMethod "',NewMethodName,'"...']);
        {$ENDIF}
        SetMethodValue(PropertyHook.CreateMethod(NewMethodName, GetPropType,
                                                 GetComponent(0), GetPropertyPath(0)));
        {$IFDEF VerboseMethodPropEdit}
        debugln(['TMethodPropertyEditor.Edit CHANGED new method=',GetValue]);
        {$ENDIF}
        PropertyHook.RefreshPropertyValues;
        ShowValue;
      end;
    mrOk:
      begin
        // -> jump to ancestor method
        {$IFDEF VerboseMethodPropEdit}
        debugln(['TMethodPropertyEditor.Edit Jump to ancestor method ',NewMethodName]);
        {$ENDIF}
        PropertyHook.ShowMethod(NewMethodName);
      end;
    end;
  end;
end;

procedure TMethodPropertyEditor.ShowValue;
var
  CurMethodName: String;
begin
  CurMethodName:=GetValue;
  PropertyHook.ShowMethod(CurMethodName);
end;

function TMethodPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paDialog, paValueList, paSortList, paRevertable];
end;

function TMethodPropertyEditor.GetEditLimit: Integer;
begin
  Result := 2*MaxIdentLength+1; // clasname.methodname
end;

function TrimNonAscii(const Txt: String): String;
// ToDo: Find a similar function from FPC libs and use it instead.
var
  I: Integer;
begin
  Result := Txt;
  for I := Length(Result) downto 1 do
    if not (
            (Result[I] in ['a'..'z', 'A'..'Z', '_']) or
            (I > 1) and (Result[I] in ['0'..'9'])
           )
    then
      Delete(Result, I, 1);
end;

function TrimDotsAndBrackets(const Txt: String): String;
var
  I: Integer;
begin
  Result := Txt;
  for I := Length(Result) downto 1 do
    if Result[I] in ['.','[',']'] then
      Delete(Result, I, 1);
end;

function TrimEventName(const aName: shortstring): shortstring;
begin
  Result := aName;
  if (Length(Result) >= 2)
  and (Result[1] in ['O','o']) and (Result[2] in ['N','n'])
  then
    Delete(Result, 1, 2);
end;

function TMethodPropertyEditor.GetTrimmedEventName: shortstring;
begin
  Result := TrimEventName(GetName);
end;

function MethodNameSub(Root: TPersistent): shortstring;
begin
  if Root is TCustomForm then
    Result := 'Form'
  else
  if Root is TDataModule then
    Result := 'DataModule'
  else
  if Root is TFrame then
    Result := 'Frame'
  else
    Result := '';
end;

function TMethodPropertyEditor.GetFormMethodName: shortstring;
// returns the default name for a new method
begin
  Result := '';
  if PropertyHook.LookupRoot=nil then exit;
  if GetComponent(0) = PropertyHook.LookupRoot then begin
    Result := MethodNameSub(PropertyHook.LookupRoot);
    if Result = '' then
      Result := ClassNameToComponentName(PropertyHook.GetRootClassName);
  end
  else
    Result := TrimNonAscii(PropertyHook.GetObjectName(GetComponent(0), FOwnerComponent));
  if Result = '' then
    exit;
  Result := Result + GetTrimmedEventName;
end;

class function TMethodPropertyEditor.GetDefaultMethodName(Root, Component: TComponent;
  const RootClassName, ComponentName, PropName: shortstring): shortstring;
// returns the default name for a new method
begin
  Result := '';
  if Root=nil then exit;
  if Component = Root then begin
    Result := MethodNameSub(Root);
    if Result = '' then
      Result := ClassNameToComponentName(RootClassName);
  end
  else
    Result := TrimDotsAndBrackets(ComponentName);
  if Result <> '' then
    Result := Result + TrimEventName(PropName)
  else
    DebugLn(['TMethodPropertyEditor.GetDefaultMethodName cannot create name - should never happen']);
end;

function TMethodPropertyEditor.GetValue: ansistring;
begin
  if Assigned(PropertyHook) then
    Result:=PropertyHook.GetMethodName(GetMethodValue,GetComponent(0))
  else begin
    Result:='';
    debugln(['TMethodPropertyEditor.GetValue : PropertyHook=Nil Name=',GetName,' Data=',dbgs(GetMethodValue.Data)]);
  end;
end;

procedure TMethodPropertyEditor.GetValues(Proc: TGetStrProc);
begin
  //DebugLn('### TMethodPropertyEditor.GetValues');
  Proc(oisNone);
  PropertyHook.GetCompatibleMethods(GetInstProp, Proc);
end;

procedure TMethodPropertyEditor.SetValue(const NewValue: ansistring);
var
  CreateNewMethodSrc: Boolean;
  CurValue: string;
  NewMethodExists, NewMethodIsCompatible, NewMethodIsPublished,
  NewIdentIsMethod: boolean;
  IsNil: Boolean;
  NewMethod: TMethod;
begin
  CurValue := GetValue;
  if CurValue = NewValue then exit;
  {$IFDEF VerboseMethodPropEdit}
  debugln(['TMethodPropertyEditor.SetValue CurValue="',CurValue,'" NewValue="',NewValue,'"']);
  {$ENDIF}
  IsNil := (NewValue='') or (NewValue=oisNone);
  
  if (not IsNil) and (not IsValidIdent(NewValue)) then
  begin
    MessageDlg(oisIncompatibleIdentifier,
      Format(oisIsNotAValidMethodName,[NewValue]),
      mtError, [mbCancel, mbIgnore], 0);
    exit;
  end;

  NewMethodExists := (not IsNil) and
    PropertyHook.CompatibleMethodExists(NewValue, GetInstProp,
                   NewMethodIsCompatible, NewMethodIsPublished, NewIdentIsMethod);
  {$IFDEF VerboseMethodPropEdit}
  debugln(['TMethodPropertyEditor.SetValue NewValue="',NewValue,'" IsCompatible=',NewMethodIsCompatible,' IsPublished=',NewMethodIsPublished,' IsMethod=',NewIdentIsMethod]);
  {$ENDIF}
  if NewMethodExists then
  begin
    if not NewIdentIsMethod then
    begin
      if MessageDlg(oisIncompatibleIdentifier,
        Format(oisTheIdentifierIsNotAMethodPressCancelToUndoPressIgn,
               [NewValue, LineEnding, LineEnding]),
        mtWarning, [mbCancel, mbIgnore], 0)<>mrIgnore
      then
        exit;
    end;
    if not NewMethodIsPublished then
    begin
      if MessageDlg(oisIncompatibleMethod,
        Format(oisTheMethodIsNotPublishedPressCancelToUndoPressIgnor,
               [NewValue, LineEnding, LineEnding]),
        mtWarning, [mbCancel, mbIgnore], 0)<>mrIgnore
      then
        exit;
    end;
    if not NewMethodIsCompatible then
    begin
      if MessageDlg(oisIncompatibleMethod,
        Format(oisTheMethodIsIncompatibleToThisEventPressCancelToUnd,
               [NewValue, GetName, LineEnding, LineEnding]),
          mtWarning, [mbCancel, mbIgnore], 0)<>mrIgnore
      then
        exit;
    end;
  end;

  if IsNil then
  begin
    // clear
    NewMethod.Data := nil;
    NewMethod.Code := nil;
    {$IFDEF VerboseMethodPropEdit}
    debugln(['TMethodPropertyEditor.SetValue SET to NIL']);
    {$ENDIF}
    SetMethodValue(NewMethod);
  end
  else
  if IsValidIdent(CurValue) and
     not NewMethodExists and
     PropertyHook.MethodFromLookupRoot(GetMethodValue) then
  begin
    // rename the method
    // Note:
    //   All other not selected properties that use this method, contain just
    //   the TMethod record. So, changing the name in the jitform will change
    //   all other event names in all other components automatically.
    {$IFDEF VerboseMethodPropEdit}
    debugln(['TMethodPropertyEditor.SetValue RENAME']);
    {$ENDIF}
    PropertyHook.RenameMethod(CurValue, NewValue)
  end else
  begin
    // change value and create method src if needed
    CreateNewMethodSrc := not NewMethodExists;
    {$IFDEF VerboseMethodPropEdit}
    debugln(['TMethodPropertyEditor.SetValue CHANGE new method=',CreateNewMethodSrc]);
    {$ENDIF}
    SetMethodValue(
       PropertyHook.CreateMethod(NewValue, GetPropType,
                                 GetComponent(0), GetPropertyPath(0)));
    {$IFDEF VerboseMethodPropEdit}
    debugln(['TMethodPropertyEditor.SetValue CHANGED new method=',CreateNewMethodSrc]);
    {$ENDIF}
    if CreateNewMethodSrc then
    begin
      {$IFDEF VerboseMethodPropEdit}
      debugln(['TMethodPropertyEditor.SetValue SHOW "',NewValue,'"']);
      {$ENDIF}
      PropertyHook.ShowMethod(NewValue);
    end;
  end;
  {$IFDEF VerboseMethodPropEdit}
  DebugLn('### TMethodPropertyEditor.SetValue END  NewValue=',GetValue);
  {$ENDIF}
end;

{ TPersistentPropertyEditor }

function TPersistentPropertyEditor.FilterFunc(
  const ATestEditor: TPropertyEditor): Boolean;
begin
  Result := not (paNotNestable in ATestEditor.GetAttributes);
end;

function TPersistentPropertyEditor.GetPersistentReference: TPersistent;
begin
  Result := TPersistent(GetObjectValue);
end;

function TPersistentPropertyEditor.GetSelections: TPersistentSelectionList;
begin
  if (GetPersistentReference <> nil) and AllEqual then
    Result := inherited GetSelections
  else
    Result := nil;
end;

function TPersistentPropertyEditor.CheckNewValue(APersistent: TPersistent): boolean;
begin
  Result:=true;
end;

function TPersistentPropertyEditor.ComponentsAllEqual: Boolean;
// Called from AllEqual of TComponentOneFormPropertyEditor and TComponentPropertyEditor.
var
  I: Integer;
  AComponent: TComponent;
begin
  Result:=False;
  AComponent:=TComponent(GetObjectValue);
  if PropCount > 1 then
    for I := 1 to PropCount - 1 do
      if TComponent(GetObjectValueAt(I)) <> AComponent then
        Exit;
  if (PropertyHook<>nil) and PropertyHook.ComponentPropertyOnlyDesign then
    Result:=(AComponent=nil) or (csDesigning in AComponent.ComponentState)
  else
    Result:=true;
end;

function TPersistentPropertyEditor.AllEqual: Boolean;
var
  I: Integer;
  LInstance: TPersistent;
begin
  Result := False;
  LInstance := TPersistent(GetObjectValue);
  if PropCount > 1 then
    for I := 1 to PropCount - 1 do
      if TPersistent(GetObjectValueAt(I)) <> LInstance then
        Exit;
  Result := True;
end;

procedure TPersistentPropertyEditor.Edit;
var
  Temp: TPersistent;
  Designer: TIDesigner;
  AComponent: TComponent;
begin
  Temp := GetPersistentReference;
  if Temp is TComponent then begin
    AComponent:=TComponent(Temp);
    Designer:=FindRootDesigner(AComponent);
    if (Designer<>nil)
    and (Designer.GetShiftState * [ssCtrl, ssLeft] = [ssCtrl, ssLeft]) then
      Designer.SelectOnlyThisComponent(AComponent)
    else
      inherited Edit;
  end else
    inherited Edit;
end;

function TPersistentPropertyEditor.GetAttributes: TPropertyAttributes;
var
  Info: PPropInfo;
begin
  Result := [paMultiSelect];
  Info:=GetPropInfo;
  if (Info<>nil) and Assigned(Info^.SetProc) then
    Result := Result + [paValueList, paSortList, paRevertable, paVolatileSubProperties]
  else
    Result := Result + [paReadOnly];
  if GReferenceExpandable and (GetPersistentReference <> nil) and AllEqual then
    Result := Result + [paSubProperties];
end;

function TPersistentPropertyEditor.GetEditLimit: Integer;
begin
  Result := MaxIdentLength;
end;

function TPersistentPropertyEditor.GetValue: AnsiString;
var
  Component: TComponent;
  APersistent: TPersistent;
begin
  Result := '';
  APersistent := GetPersistentReference;
  if APersistent is TComponent then begin
    Component := TComponent(APersistent);
    if Assigned(PropertyHook) then
      Result := PropertyHook.GetComponentName(Component)
    else begin
      if Assigned(Component) then
        Result := Component.Name;
    end;
  end else if APersistent <> nil then
    Result := inherited GetValue;
end;

procedure TPersistentPropertyEditor.GetValues(Proc: TGetStrProc);
begin
  Proc(oisNone);
  if Assigned(PropertyHook) then
    PropertyHook.GetComponentNames(GetTypeData(GetPropType), Proc);
end;

procedure TPersistentPropertyEditor.SetValue(const NewValue: ansistring);
var
  Persistent: TPersistent;
begin
  if NewValue=GetValue then exit;
  Persistent := nil;
  if (NewValue <> '') and (NewValue<>oisNone) then begin
    if Assigned(PropertyHook) then begin
      Persistent := PropertyHook.GetComponent(NewValue);
      if not (Persistent is GetTypeData(GetPropType)^.ClassType) then begin
        raise EPropertyError.Create(oisInvalidPropertyValue);
      end;
    end;
  end;
  if GetPersistentReference=Persistent then exit;
  if not CheckNewValue(Persistent) then exit;
  SetPtrValue(Persistent);
  if Assigned(PropertyHook) then begin
    PropertyHook.ObjectReferenceChanged(Self,Persistent);
  end;
end;

{ TComponentOneFormPropertyEditor }

function TComponentOneFormPropertyEditor.AllEqual: Boolean;
begin
  Result:=ComponentsAllEqual;
end;

procedure TComponentOneFormPropertyEditor.GetValues(Proc: TGetStrProc);

  procedure TraverseComponents(Root: TComponent);
  var
    i: integer;
  begin
    for i := 0 to Root.ComponentCount - 1 do
      if (fIgnoreClass=nil) or not (Root.Components[i] is fIgnoreClass) then
        Proc(Root.Components[i].Name);
  end;

begin
  Proc(oisNone);
  if Assigned(PropertyHook) and (PropertyHook.FLookupRoot is TComponent) then
    TraverseComponents(TComponent(PropertyHook.FLookupRoot));
end;

{ TCoolBarControlPropertyEditor }

constructor TCoolBarControlPropertyEditor.Create(Hook: TPropertyEditorHook; APropCount: Integer);
begin
  inherited Create(Hook, APropCount);
  fIgnoreClass := TCustomCoolBar;
end;

{ TComponentPropertyEditor }

function TComponentPropertyEditor.GetComponentReference: TComponent;
begin
  Result := TComponent(GetObjectValue);
end;

function TComponentPropertyEditor.AllEqual: Boolean;
begin
  Result:=ComponentsAllEqual;
end;

{ TInterfacePropertyEditor }

function TInterfacePropertyEditor.AllEqual: Boolean;
var
  I: Integer;
  Component: TComponent;
begin
  Result := False;
  Component := GetComponentReference;
  if PropCount > 1 then
    for I := 1 to PropCount - 1 do
      if GetComponent(GetIntfValueAt(I)) <> Component then
        Exit;
  if (PropertyHook<>nil) and PropertyHook.ComponentPropertyOnlyDesign then
    Result:=(Component=nil) or (csDesigning in Component.ComponentState)
  else
    Result := True;
end;

procedure TInterfacePropertyEditor.Edit;
var
  Temp: TPersistent;
  Designer: TIDesigner;
  AComponent: TComponent;
begin
  Temp := GetComponentReference;
  if Temp is TComponent then begin
    AComponent:=TComponent(Temp);
    Designer:=FindRootDesigner(AComponent);
    if (Designer<>nil)
    and (Designer.GetShiftState * [ssCtrl, ssLeft] = [ssCtrl, ssLeft]) then
      Designer.SelectOnlyThisComponent(AComponent)
    else
      inherited Edit;
  end else
    inherited Edit;
end;

function TInterfacePropertyEditor.GetAttributes: TPropertyAttributes;
var
  Info: PPropInfo;
begin
  Result := [paMultiSelect];
  Info:=GetPropInfo;
  if (Info<>nil) and Assigned(Info^.SetProc) then
    Result := Result + [paValueList, paSortList, paRevertable, paVolatileSubProperties]
  else
    Result := Result + [paReadOnly];
  if GReferenceExpandable and (GetComponentReference <> nil) and AllEqual then
    Result := Result + [paSubProperties];
end;

function TInterfacePropertyEditor.GetComponent(const AInterface: IInterface): TComponent;
var
  ComponentRef: IInterfaceComponentReference;
begin
  Result := nil;
  if not Assigned(AInterface) then
    Exit;
  if not Supports(AInterface, IInterfaceComponentReference, ComponentRef) then
    Exit;
  Result := ComponentRef.GetComponent;
end;

function TInterfacePropertyEditor.GetComponentReference: TComponent;
begin
  Result := GetComponent(GetIntfValue);
end;

function TInterfacePropertyEditor.GetSelections: TPersistentSelectionList;
var
  I: Integer;
  SubItem: TPersistent;
begin
  if AllEqual then
  begin
    Result := TPersistentSelectionList.Create;
    try
      for I := 0 to PropCount - 1 do
      begin
        SubItem := GetComponent(GetIntfValueAt(I));
        if Assigned(SubItem) then
          Result.Add(SubItem);
      end;
    except
      Result.Free;
      raise;
    end;
  end
  else
    Result := nil;
end;

procedure TInterfacePropertyEditor.GetValues(Proc: TGetStrProc);

var
  ID: TGUID;

  procedure TraverseComponents(Root: TComponent);
  var
    i: integer;
  begin
    for i := 0 to Root.ComponentCount - 1 do
      if Supports(Root.Components[i], ID) then
        Proc(Root.Components[i].Name);
  end;

begin
  ID := GetTypeData(GetPropType)^.GUID;
  Proc(oisNone);
  if Assigned(PropertyHook) and (PropertyHook.FLookupRoot is TComponent) then
    TraverseComponents(TComponent(PropertyHook.FLookupRoot));
end;

procedure TInterfacePropertyEditor.SetValue(const NewValue: string);
var
  Intf: IInterface;
  Component: TComponent;
begin
  if NewValue = GetValue then
    Exit;
  if (NewValue = '') or (NewValue = oisNone) then
    Intf := nil
  else
  begin
    if Assigned(PropertyHook) then
    begin
      Component := PropertyHook.GetComponent(NewValue);
      if not Assigned(Component) or not Supports(Component, GetTypeData(GetPropType)^.GUID) then
        raise EPropertyError.Create(oisInvalidPropertyValue);
      Intf := Component;
    end
    else
      Intf := nil;
  end;
  SetIntfValue(Intf);
end;

function TInterfacePropertyEditor.GetValue: AnsiString;
var
  Component: TComponent;
begin
  Result := '';
  Component := GetComponentReference;
  if Assigned(Component) then begin
    if Assigned(PropertyHook) then
      Result := PropertyHook.GetComponentName(Component)
    else
      Result := Component.Name;
  end;
end;

{ TComponentNamePropertyEditor }

function TComponentNamePropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [];
end;

function TComponentNamePropertyEditor.GetEditLimit: Integer;
begin
  Result := MaxIdentLength;
end;

function TComponentNamePropertyEditor.GetValue: ansistring;
begin
  Result:=inherited GetValue;
end;

procedure TComponentNamePropertyEditor.SetValue(const NewValue: ansistring);
begin
  if not IsValidIdent(NewValue) then
    raise Exception.Create(Format(oisComponentNameIsNotAValidIdentifier, [NewValue]));
  inherited SetValue(NewValue);
  PropertyHook.ComponentRenamed(TComponent(GetComponent(0)));
end;

{ TDatePropertyEditor }

function TDatePropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paRevertable];
end;

function TDatePropertyEditor.GetValue: string;
var
  DT: TDateTime;
begin
  DT := TDateTime(GetFloatValue);
  if DT = 0.0 then
    Result := ''
  else
    Result := DateToStr(DT);
end;

procedure TDatePropertyEditor.SetValue(const Value: string);
var
  DT: TDateTime;
begin
  if Value = '' then
    DT := 0.0
  else
    DT := StrToDate(Value);
  SetFloatValue(DT);
end;

{ TTimePropertyEditor }

function TTimePropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paRevertable];
end;

function TTimePropertyEditor.GetValue: string;
var
  DT: TDateTime;
begin
  DT := TDateTime(GetFloatValue);
  if DT = 0.0 then Result := '' else
  Result := TimeToStr(DT);
end;

procedure TTimePropertyEditor.SetValue(const Value: string);
var
  DT: TDateTime;
begin
  if Value = '' then DT := 0.0
  else DT := StrToTime(Value);
  SetFloatValue(DT);
end;

{ TDateTimePropertyEditor }

function TDateTimePropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paRevertable];
end;

function TDateTimePropertyEditor.GetValue: string;
var
  DT: TDateTime;
begin
  DT := TDateTime(GetFloatValue);
  if DT = 0.0 then Result := '' else
  Result := DateTimeToStr(DT);
end;

procedure TDateTimePropertyEditor.SetValue(const Value: string);
var
  DT: TDateTime;
  ok: Boolean;
begin
  if Value = '' then DT := 0.0
  else begin
    ok:=false;
    // first try date+time
    try
      DT := StrToDateTime(Value);
      ok:=true;
    except
    end;
    // then try date without time
    if not ok then
      try
        DT := StrToDate(Value);
        ok:=true;
      except
      end;
    // then try time without date
    if not ok then
      try
        DT := StrToTime(Value);
        ok:=true;
      except
      end;
    // if all fails then raise exception
    if not ok then
      StrToDateTime(Value);
  end;
  SetFloatValue(DT);
end;

const
  VarTypeStr: array[0..16] of record
    VarType: Word;
    Name: String;
  end = (
    (VarType: varempty; Name: 'Unassigned'),
    (VarType: varnull; Name: 'Null'),
    (VarType: varsmallint; Name: 'SmallInt'),
    (VarType: varinteger; Name: 'Integer'),
    (VarType: varsingle; Name: 'Single'),
    (VarType: vardouble; Name: 'Double'),
    (VarType: varcurrency; Name: 'Currency'),
    (VarType: vardate; Name: 'Date'),
    (VarType: varolestr; Name: 'OleStr'),
    (VarType: varboolean; Name: 'Boolean'),
    (VarType: varshortint; Name: 'ShortInt'),
    (VarType: varbyte; Name: 'Byte'),
    (VarType: varword; Name: 'Word'),
    (VarType: varlongword; Name: 'LongWord'),
    (VarType: varint64; Name: 'Int64'),
    (VarType: varqword; Name: 'QWord'),
    (VarType: varstring; Name: 'String')
  );

function GetVarTypeName(AVarType: tvartype): String;
var
  I: Integer;
begin
  Result := '';
  for I := Low(VarTypeStr) to High(VarTypeStr) do
    if VarTypeStr[I].VarType = AVarType then
      Exit(VarTypeStr[I].Name);
end;

function GetVarTypeByName(AName: String): tvartype;
var
  I: Integer;
begin
  Result := varempty;
  for I := Low(VarTypeStr) to High(VarTypeStr) do
    if CompareText(VarTypeStr[I].Name, AName) = 0 then
      Exit(VarTypeStr[I].VarType);
end;

type

  { TVarTypeProperty }

  TVarTypeProperty = class(TNestedProperty)
    function GetName: shortstring; override;
    function GetAttributes: TPropertyAttributes; override;
    procedure GetValues(Proc: TGetStrProc); override;
    function GetValue: ansistring; override;
    procedure SetValue(const NewValue: ansistring); override;
  end;

{ TVarTypeProperty }

function TVarTypeProperty.GetName: shortstring;
begin
  Result := 'Type';
end;

function TVarTypeProperty.GetAttributes: TPropertyAttributes;
begin
  Result := [paValueList];
end;

procedure TVarTypeProperty.GetValues(Proc: TGetStrProc);
var
  I: Integer;
begin
  for I := Low(VarTypeStr) to High(VarTypeStr) do
    Proc(VarTypeStr[I].Name);
end;

function TVarTypeProperty.GetValue: ansistring;
begin
  Result := GetVarTypeName(VarType(GetVarValue));
  if Result = '' then
    Result := 'Unknown'; // Is there resourcestring for that?
end;

procedure TVarTypeProperty.SetValue(const NewValue: ansistring);
var
  V: Variant;
  VT: tvartype;
begin
  V := GetVarValue;
  VT := GetVarTypeByName(NewValue);
  case VT of
    varempty:
      VarClear(V);
    varnull:
      V := Null;
    else
      try
        VarCast(V, V, VT);
      except
        VarClear(V);
      end;
  end;
  SetVarValue(V);
end;

{ TVariantPropertyEditor }

function TVariantPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paSubProperties];
end;

procedure TVariantPropertyEditor.GetProperties(Proc:TGetPropEditProc);
begin
  Proc(TVarTypeProperty.Create(Self));
end;

function TVariantPropertyEditor.GetValue: string;
begin
  if VarType(GetVarValue) <> varnull then
    Result := VarToStrDef(GetVarValue, 'Unknown') // Is there resourcestring for that?
  else
    Result := '(Null)';
end;

procedure TVariantPropertyEditor.SetValue(const Value: string);
var
  V: Variant;
begin
  try
    V := Value;
  except
    V := 0; // Some backup value.
  end;
  SetVarValue(V);
end;


{ TModalResultPropertyEditor }

function TModalResultPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paValueList, paRevertable];
end;

function TModalResultPropertyEditor.OrdValueToVisualValue(OrdValue: longint): string;
var
  CurValue: Longint;
begin
  CurValue := OrdValue;
  case CurValue of
    Low(ModalResultStr)..High(ModalResultStr):
      Result := ModalResultStr[CurValue];
  else
    Result := IntToStr(CurValue);
  end;
end;

procedure TModalResultPropertyEditor.GetValues(Proc: TGetStrProc);
var
  I: Integer;
begin
  for I := Low(ModalResultStr) to High(ModalResultStr) do Proc(ModalResultStr[I]);
end;

procedure TModalResultPropertyEditor.SetValue(const NewValue: ansistring);
var
  I: Integer;
begin
  if NewValue = '' then begin
    SetOrdValue(0);
    Exit;
  end;
  for I := Low(ModalResultStr) to High(ModalResultStr) do
    if CompareText(ModalResultStr[I], NewValue) = 0 then
    begin
      SetOrdValue(I);
      Exit;
    end;
  inherited SetValue(NewValue);
end;

{ TShortCutPropertyEditor }

const
  ShortCuts: array[0..135] of TShortCut = (
    scNone,
    Byte('A') or scCtrl,
    Byte('B') or scCtrl,
    Byte('C') or scCtrl,
    Byte('D') or scCtrl,
    Byte('E') or scCtrl,
    Byte('F') or scCtrl,
    Byte('G') or scCtrl,
    Byte('H') or scCtrl,
    Byte('I') or scCtrl,
    Byte('J') or scCtrl,
    Byte('K') or scCtrl,
    Byte('L') or scCtrl,
    Byte('M') or scCtrl,
    Byte('N') or scCtrl,
    Byte('O') or scCtrl,
    Byte('P') or scCtrl,
    Byte('Q') or scCtrl,
    Byte('R') or scCtrl,
    Byte('S') or scCtrl,
    Byte('T') or scCtrl,
    Byte('U') or scCtrl,
    Byte('V') or scCtrl,
    Byte('W') or scCtrl,
    Byte('X') or scCtrl,
    Byte('Y') or scCtrl,
    Byte('Z') or scCtrl,
    Byte('A') or scMeta,
    Byte('B') or scMeta,
    Byte('C') or scMeta,
    Byte('D') or scMeta,
    Byte('E') or scMeta,
    Byte('F') or scMeta,
    Byte('G') or scMeta,
    Byte('H') or scMeta,
    Byte('I') or scMeta,
    Byte('J') or scMeta,
    Byte('K') or scMeta,
    Byte('L') or scMeta,
    Byte('M') or scMeta,
    Byte('N') or scMeta,
    Byte('O') or scMeta,
    Byte('P') or scMeta,
    Byte('Q') or scMeta,
    Byte('R') or scMeta,
    Byte('S') or scMeta,
    Byte('T') or scMeta,
    Byte('U') or scMeta,
    Byte('V') or scMeta,
    Byte('W') or scMeta,
    Byte('X') or scMeta,
    Byte('Y') or scMeta,
    Byte('Z') or scMeta,
    Byte('A') or scCtrl or scAlt,
    Byte('B') or scCtrl or scAlt,
    Byte('C') or scCtrl or scAlt,
    Byte('D') or scCtrl or scAlt,
    Byte('E') or scCtrl or scAlt,
    Byte('F') or scCtrl or scAlt,
    Byte('G') or scCtrl or scAlt,
    Byte('H') or scCtrl or scAlt,
    Byte('I') or scCtrl or scAlt,
    Byte('J') or scCtrl or scAlt,
    Byte('K') or scCtrl or scAlt,
    Byte('L') or scCtrl or scAlt,
    Byte('M') or scCtrl or scAlt,
    Byte('N') or scCtrl or scAlt,
    Byte('O') or scCtrl or scAlt,
    Byte('P') or scCtrl or scAlt,
    Byte('Q') or scCtrl or scAlt,
    Byte('R') or scCtrl or scAlt,
    Byte('S') or scCtrl or scAlt,
    Byte('T') or scCtrl or scAlt,
    Byte('U') or scCtrl or scAlt,
    Byte('V') or scCtrl or scAlt,
    Byte('W') or scCtrl or scAlt,
    Byte('X') or scCtrl or scAlt,
    Byte('Y') or scCtrl or scAlt,
    Byte('Z') or scCtrl or scAlt,
    VK_F1,
    VK_F2,
    VK_F3,
    VK_F4,
    VK_F5,
    VK_F6,
    VK_F7,
    VK_F8,
    VK_F9,
    VK_F10,
    VK_F11,
    VK_F12,
    VK_F1 or scCtrl,
    VK_F2 or scCtrl,
    VK_F3 or scCtrl,
    VK_F4 or scCtrl,
    VK_F5 or scCtrl,
    VK_F6 or scCtrl,
    VK_F7 or scCtrl,
    VK_F8 or scCtrl,
    VK_F9 or scCtrl,
    VK_F10 or scCtrl,
    VK_F11 or scCtrl,
    VK_F12 or scCtrl,
    VK_F1 or scShift,
    VK_F2 or scShift,
    VK_F3 or scShift,
    VK_F4 or scShift,
    VK_F5 or scShift,
    VK_F6 or scShift,
    VK_F7 or scShift,
    VK_F8 or scShift,
    VK_F9 or scShift,
    VK_F10 or scShift,
    VK_F11 or scShift,
    VK_F12 or scShift,
    VK_F1 or scShift or scCtrl,
    VK_F2 or scShift or scCtrl,
    VK_F3 or scShift or scCtrl,
    VK_F4 or scShift or scCtrl,
    VK_F5 or scShift or scCtrl,
    VK_F6 or scShift or scCtrl,
    VK_F7 or scShift or scCtrl,
    VK_F8 or scShift or scCtrl,
    VK_F9 or scShift or scCtrl,
    VK_F10 or scShift or scCtrl,
    VK_F11 or scShift or scCtrl,
    VK_F12 or scShift or scCtrl,
    VK_INSERT,
    VK_INSERT or scShift,
    VK_INSERT or scCtrl,
    VK_DELETE,
    VK_DELETE or scShift,
    VK_DELETE or scCtrl,
    VK_BACK or scAlt,
    VK_BACK or scShift or scAlt,
    VK_ESCAPE);

procedure TShortCutPropertyEditor.Edit;
var
  Box: TShortCutGrabBox;
  OldValue, NewValue: TShortCut;
  OldKey: Word;
  OldShift: TShiftState;
  Dlg: TForm;
  BtnPanel: TButtonPanel;
begin
  Dlg:=TForm.Create(Application);
  try
    Dlg.BorderIcons:=[biSystemMenu];
    Dlg.Caption:=oisSelectShortCut;
    Dlg.Position:=poScreenCenter;
    Dlg.Constraints.MinWidth:=350;
    Dlg.Constraints.MinHeight:=30;
    Dlg.Width:=350;
    Dlg.Height:=120;

    Box:=TShortCutGrabBox.Create(Dlg);
    Box.BorderSpacing.Around:=6;
    Box.Parent:=Dlg;
    Box.Align:=alClient;
    OldValue := TShortCut(GetOrdValue);
    ShortCutToKey(OldValue,OldKey,OldShift);
    Box.ShiftState:=OldShift;
    Box.Key:=OldKey;

    BtnPanel:=TButtonPanel.Create(Dlg);
    BtnPanel.Parent:=Dlg;
    BtnPanel.Align:=alBottom;
    BtnPanel.ShowButtons:=[pbOk,pbCancel];

    Dlg.AutoSize:=true;
    if Dlg.ShowModal=mrOk then begin
      NewValue:=Menus.ShortCut(Box.Key,Box.ShiftState);
      if OldValue<>NewValue then
        SetOrdValue(NewValue);
    end;
  finally
    Dlg.Free;
  end;
end;

function TShortCutPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paValueList, paRevertable, paDialog];
end;

function TShortCutPropertyEditor.OrdValueToVisualValue(OrdValue: longint): string;
var
  CurValue: TShortCut;
begin
  CurValue := TShortCut(OrdValue);
  if CurValue = scNone then
    Result := oisNone
  else
    Result := ShortCutToText(CurValue);
end;

procedure TShortCutPropertyEditor.GetValues(Proc: TGetStrProc);
var
  I: Integer;
begin
  Proc(oisNone);
  {$IFDEF Darwin}
    for I := 1 to High(ShortCuts) do Proc(ShortCutToText(ShortCuts[I]));
  {$ELSE}
    for I := 1 to 26 do Proc(ShortCutToText(ShortCuts[I]));
    for I := 53 to High(ShortCuts) do Proc(ShortCutToText(ShortCuts[I]));
  {$ENDIF}
end;

procedure TShortCutPropertyEditor.SetValue(const Value: string);
var
  NewValue: TShortCut;
begin
  NewValue := 0;
  if (Value <> '') and (AnsiCompareText(Value, oisNone) <> 0) then
  begin
    NewValue := TextToShortCut(Value);
    if NewValue = 0 then
      raise EPropertyError.Create(oisInvalidPropertyValue);
  end;
  SetOrdValue(NewValue);
end;

{ TTabOrderPropertyEditor }

function TTabOrderPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [];
end;

{ TCaptionPropertyEditor }

function TCaptionPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paAutoUpdate, paRevertable];
end;

{ TMenuItemCaptionEditor }

procedure TMenuItemCaptionEditor.SetValue(const NewValue: ansistring);
var
  Designer: TIDesigner;
  MI: TMenuItem;
  Inst: TPersistent;
begin
  Inst := GetComponent(0);
  if (NewValue = cLineCaption) and (Inst is TMenuItem) then
  begin
    MI := TMenuItem(Inst);
    if AnsiStartsStr('MenuItem', MI.Name) then
    begin
      Designer:=FindRootDesigner(MI);
      if Designer<>nil then
        MI.Name:=Designer.UniqueName('Separator');
    end;
  end;
  SetStrValue(NewValue);
end;

{ TStringsPropertyEditor }

procedure TStringsPropertyEditor.Edit;
var
  TheDialog: TStringsPropEditorDlg;
begin
  TheDialog := CreateDlg(TStrings(GetObjectValue));
  try
    if (TheDialog.ShowModal = mrOK) then 
      SetPtrValue(TheDialog.Memo.Lines);
  finally
    TheDialog.Free;
  end;
end;

function TStringsPropertyEditor.CreateDlg(s: TStrings): TStringsPropEditorDlg;
begin
  Result := TStringsPropEditorDlg.Create(Application);
  Result.Editor := Self;
  Result.Memo.Text := s.Text;
  Result.MemoChange(nil); // force call OnChange event
end;

function TStringsPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paDialog, paRevertable, paReadOnly];
end;

{ TValueListPropertyEditor }

procedure TValueListPropertyEditor.Edit;
var
  TheDialog: TKeyValPropEditorDlg;
begin
  TheDialog := CreateDlg(TStrings(GetObjectValue));
  try
    if (TheDialog.ShowModal = mrOK) then
      SetPtrValue(TheDialog.ValueListEdit.Strings);
  finally
    TheDialog.Free;
  end;
end;

function TValueListPropertyEditor.CreateDlg(s: TStrings): TKeyValPropEditorDlg;
begin
  Result := TKeyValPropEditorDlg.Create(Application);
  Result.Editor := Self;
  Result.ValueListEdit.Strings.Assign(s);
  Result.ValueListEdit.Invalidate;
end;

function TValueListPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paDialog, paRevertable, paReadOnly];
end;

{ TStringMultilinePropertyEditor }

procedure TStringMultilinePropertyEditor.Edit;
var
  TheDialog : TStringsPropEditorDlg;
  AString : string;
  LineEndPos: Integer;
begin
  AString := GetStrValue;
  TheDialog := TStringsPropEditorDlg.Create(nil);
  try
    TheDialog.Editor := Self;
    TheDialog.Memo.Text := AString;
    TheDialog.MemoChange(nil);
    if (TheDialog.ShowModal = mrOK) then
    begin
      AString := TheDialog.Memo.Text;
      LineEndPos := Length(AString) - Length(LineEnding) + 1;
      //erase the last lineending if any
      if Copy(AString, LineEndPos, Length(LineEnding)) = LineEnding then
        Delete(AString, LineEndPos, Length(LineEnding));
      SetStrValue(AString);
    end;
  finally
    TheDialog.Free;
  end;
end;

function TStringMultilinePropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paDialog, paRevertable, paAutoUpdate];
end;

{ TCursorPropertyEditor }

function TCursorPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result := [paMultiSelect, paSortList, paValueList, paRevertable];
end;

function TCursorPropertyEditor.OrdValueToVisualValue(OrdValue: longint): string;
begin
  Result := CursorToString(TCursor(OrdValue));
end;

procedure TCursorPropertyEditor.GetValues(Proc: TGetStrProc);
begin
  GetCursorValues(Proc);
end;

procedure TCursorPropertyEditor.SetValue(const NewValue: ansistring);
var
  CValue: Longint;
begin
  CValue:=0;
  if IdentToCursor(NewValue, CValue) then
    SetOrdValue(CValue)
  else
    inherited SetValue(NewValue);
end;

{ TFileNamePropertyEditor }

function TFileNamePropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result:=[paDialog,paRevertable];
end;

procedure TFileNamePropertyEditor.Edit;
begin
  With CreateFileDialog do
    Try
      Filter:=GetFilter;
      Options:=GetDialogOptions;
      FileName:=GetStrValue;
      InitialDir:=GetInitialDirectory;
      Title:=GetDialogTitle;
      If Execute then
        SetFilename(Filename);
    Finally
      Free;
    end;
end;

function TFileNamePropertyEditor.GetFilter: String;
begin
  Result:=oisAllFiles+' ('+GetAllFilesMask+')|'+GetAllFilesMask;
end;

function TFileNamePropertyEditor.GetDialogOptions: TOpenOptions;
begin
  Result:=DefaultOpenDialogOptions;
end;

function TFileNamePropertyEditor.GetDialogTitle: string;
begin
  Result:=oisSelectAFile;
end;

function TFileNamePropertyEditor.GetInitialDirectory: string;
begin
  Result:='';
end;

procedure TFileNamePropertyEditor.SetFilename(const Filename: string);
begin
  SetStrValue(Filename);
end;

function TFileNamePropertyEditor.CreateFileDialog: TOpenDialog;
begin
  Result:=TOpenDialog.Create(nil);
end;

{ TDirectoryPropertyEditor }

function TDirectoryPropertyEditor.CreateFileDialog: TOpenDialog;
begin
  Result:=TSelectDirectoryDialog.Create(nil);
  Result.Options:=Result.Options+[ofFileMustExist];
end;

{ TURLPropertyEditor }

procedure TURLPropertyEditor.SetFilename(const Filename: string);

  function FilenameToURL(const Filename: string): string;
  var
    i: Integer;
  begin
    Result:=Filename;
    {$push}
    {$warnings off}
    if PathDelim<>'/' then
      for i:=1 to length(Result) do
        if Result[i]=PathDelim then
          Result[i]:='/';
    {$pop}
    if Result<>'' then
      Result:='file://'+Result;
  end;

begin
  inherited SetFilename(FilenameToURL(Filename));
end;

{ TURLDirectoryPropertyEditor }

function TURLDirectoryPropertyEditor.CreateFileDialog: TOpenDialog;
begin
  Result:=TSelectDirectoryDialog.Create(nil);
  Result.Options:=Result.Options+[ofFileMustExist];
end;

{ TFileDlgFilterProperty }

function TFileDlgFilterProperty.GetAttributes: TPropertyAttributes;
begin
  Result:=inherited GetAttributes + [paDialog];
end;

procedure TFileDlgFilterProperty.Edit;
begin
  with TFileFilterPropEditForm.Create(Application) do
  try
    Filter:=GetStrValue;
    if ShowModal=mrOk then begin
      SetStrValue(Filter);
      Modified;
    end;
  finally
    Free;
  end;
end;

{ TSessionPropertiesPropertyEditor }

function TSessionPropertiesPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result:=[paDialog,paRevertable,paReadOnly];
end;

procedure TSessionPropertiesPropertyEditor.Edit;
begin
  With TSelectPropertiesForm.Create(Application) do
    Try
      PropertyComponent:=GetComponent(0) as TComponent;
      SelectedProperties:=GetStrValue;
      Caption:=Format(oisPropertiesOf, [TComponent(GetComponent(0)).Name]);
      If (ShowModal=mrOK) then
        SetStrValue(SelectedProperties);
    Finally
      Free;
    end;
end;

{ TConstraintsPropertyEditor }

function TConstraintsPropertyEditor.GetVerbCount: Integer;
begin
  Result:=2;
end;

function TConstraintsPropertyEditor.GetVerb(Index: Integer): string;
var
  s: String;
  c: TControl;
begin
  case Index of
    0: s := oisSetMaxConstraints;
    1: s := oisSetMinConstraints;
    else s := '';
  end;
  c := GetComponent(0) as TControl;
  Result := Format(s, [c.Height, c.Width]);
end;

procedure TConstraintsPropertyEditor.PrepareItem(Index: Integer;
  const AnItem: TMenuItem);
var
  c: TControl;
begin
  c := GetComponent(0) as TControl;
  case Index of
  0:
    begin
      // set max constraints
      AnItem.Enabled := (c.Constraints.MaxHeight<>c.Height)
                     or (c.Constraints.MaxWidth<>c.Width);
      AnItem.Hint := oisSetMaxConstraintsHint;
    end;
  1:
    begin
      // set min constraints
      AnItem.Enabled := (c.Constraints.MinHeight<>c.Height)
                      or (c.Constraints.MinWidth<>c.Width);
      AnItem.Hint := oisSetMinConstraintsHint;
    end;
  end;
end;

procedure TConstraintsPropertyEditor.ExecuteVerb(Index: Integer);
var
  c: TControl;
begin
  c := GetComponent(0) as TControl;
  case Index of
    0: begin
      c.Constraints.MaxHeight := c.Height;
      c.Constraints.MaxWidth := c.Width;
    end;
    1: begin
      c.Constraints.MinHeight := c.Height;
      c.Constraints.MinWidth := c.Width;
    end;
  end;
end;


//==============================================================================


{ TPropertyEditorHook }

function TPropertyEditorHook.CreateMethod(const aName: ShortString;
  ATypeInfo: PTypeInfo; APersistent: TPersistent; const APropertyPath: string
  ): TMethod;
var
  i: Integer;
  Handler: TPropHookCreateMethod;
begin
  Result.Code := nil;
  Result.Data := nil;
  if IsValidIdent(aName,true,true) and Assigned(ATypeInfo) then
  begin
    i := GetHandlerCount(htCreateMethod);
    while GetNextHandlerIndex(htCreateMethod, i) do
    begin
      Handler := TPropHookCreateMethod(FHandlers[htCreateMethod][i]);
      Result := Handler(aName, ATypeInfo, APersistent, APropertyPath);
      if Assigned(Result.Data) or Assigned(Result.Code) then exit;
    end;
  end;
end;

function TPropertyEditorHook.GetMethodName(const Method: TMethod;
  PropOwner: TObject): String;
var
  i: Integer;
begin
  i:=GetHandlerCount(htGetMethodName);
  if GetNextHandlerIndex(htGetMethodName,i) then begin
    Result:=TPropHookGetMethodName(FHandlers[htGetMethodName][i])(Method,PropOwner,LookupRoot);
  end else begin
    // search the method name with the given code pointer
    if Assigned(Method.Code) then begin
      if Method.Data<>nil then begin
        Result:=TObject(Method.Data).MethodName(Method.Code);
        if Result='' then
          Result:='<Unpublished>';
      end else
        Result:='<No LookupRoot>';
    end else
      Result:='';
  end;
end;

procedure TPropertyEditorHook.GetMethods(TypeData: PTypeData;
  const Proc: TGetStrProc);
var
  i: Integer;
begin
  i:=GetHandlerCount(htGetMethods);
  while GetNextHandlerIndex(htGetMethods,i) do
    TPropHookGetMethods(FHandlers[htGetMethods][i])(TypeData,Proc);
end;

procedure TPropertyEditorHook.GetCompatibleMethods(InstProp: PInstProp;
  const Proc: TGetStrProc);
var
  i: Integer;
begin
  i:=GetHandlerCount(htGetCompatibleMethods);
  while GetNextHandlerIndex(htGetCompatibleMethods,i) do
    TPropHookGetCompatibleMethods(FHandlers[htGetCompatibleMethods][i])(InstProp,Proc);
end;

function TPropertyEditorHook.MethodExists(const aName: String;
  TypeData: PTypeData;
  var MethodIsCompatible, MethodIsPublished, IdentIsMethod: boolean):boolean;
var
  i: Integer;
  Handler: TPropHookMethodExists;
begin
  // check if a published method with given aName exists in LookupRoot
  Result:=IsValidIdent(aName) and Assigned(FLookupRoot);
  if not Result then exit;
  i:=GetHandlerCount(htMethodExists);
  if i>=0 then begin
    while GetNextHandlerIndex(htMethodExists,i) do begin
      Handler:=TPropHookMethodExists(FHandlers[htMethodExists][i]);
      Result:=Handler(aName,TypeData,
                            MethodIsCompatible,MethodIsPublished,IdentIsMethod);
    end;
  end else begin
    Result:=(LookupRoot.MethodAddress(aName)<>nil);
    MethodIsCompatible:=Result;
    MethodIsPublished:=Result;
    IdentIsMethod:=Result;
  end;
end;

function TPropertyEditorHook.CompatibleMethodExists(const aName: String;
  InstProp: PInstProp; out MethodIsCompatible, MethodIsPublished,
  IdentIsMethod: boolean): boolean;
var
  i: Integer;
  Handler: TPropHookCompatibleMethodExists;
begin
  MethodIsCompatible:=false;
  MethodIsPublished:=false;
  IdentIsMethod:=false;
  // check if a published method with given aName exists in LookupRoot
  Result:=IsValidIdent(aName) and Assigned(FLookupRoot);
  if not Result then exit;
  i:=GetHandlerCount(htCompatibleMethodExists);
  if i>=0 then begin
    while GetNextHandlerIndex(htCompatibleMethodExists,i) do begin
      Handler:=TPropHookCompatibleMethodExists(FHandlers[htCompatibleMethodExists][i]);
      Result:=Handler(aName,InstProp,
                            MethodIsCompatible,MethodIsPublished,IdentIsMethod);
    end;
  end else begin
    Result:=(LookupRoot.MethodAddress(aName)<>nil);
    MethodIsCompatible:=Result;
    MethodIsPublished:=Result;
    IdentIsMethod:=Result;
  end;
end;

procedure TPropertyEditorHook.RenameMethod(const CurName, NewName: String);
// rename published method in LookupRoot object and source
var
  i: Integer;
begin
  i:=GetHandlerCount(htRenameMethod);
  while GetNextHandlerIndex(htRenameMethod,i) do
    TPropHookRenameMethod(FHandlers[htRenameMethod][i])(CurName,NewName);
end;

procedure TPropertyEditorHook.ShowMethod(const aName: String);
// jump cursor to published method body
var
  i: Integer;
begin
  i:=GetHandlerCount(htShowMethod);
  while GetNextHandlerIndex(htShowMethod,i) do
    TPropHookShowMethod(FHandlers[htShowMethod][i])(aName);
end;

function TPropertyEditorHook.MethodFromAncestor(const Method: TMethod): boolean;
var
  AncestorClass: TClass;
  i: Integer;
  Handler: TPropHookMethodFromAncestor;
begin
  // check if given Method is not in LookupRoot source,
  // but in one of its ancestors
  i := GetHandlerCount(htMethodFromAncestor);
  if GetNextHandlerIndex(htMethodFromAncestor, i) then
  begin
    Handler := TPropHookMethodFromAncestor(FHandlers[htMethodFromAncestor][i]);
    Result := Handler(Method);
  end
  else
  begin
    Result := Assigned(Method.Data) and Assigned(Method.Code);
    if Result then
    begin
      AncestorClass := TObject(Method.Data).ClassParent;
      Result := Assigned(AncestorClass) and (AncestorClass.MethodName(Method.Code)<>'');
    end;
  end;
end;

function TPropertyEditorHook.MethodFromLookupRoot(const Method: TMethod
  ): boolean;
var
  Root: TPersistent;
  i: Integer;
  Handler: TPropHookMethodFromLookupRoot;
begin
  // check if given Method is in LookupRoot source,
  Root:=LookupRoot;
  if Root=nil then exit(false);
  i := GetHandlerCount(htMethodFromLookupRoot);
  if GetNextHandlerIndex(htMethodFromLookupRoot, i) then
  begin
    Handler := TPropHookMethodFromLookupRoot(FHandlers[htMethodFromLookupRoot][i]);
    Result := Handler(Method);
  end
  else
  begin
    Result := (TObject(Method.Data)=Root) and Assigned(Method.Code)
      and (Root.MethodName(Method.Code)<>'');
  end;
end;

procedure TPropertyEditorHook.ChainCall(const AMethodName, InstanceName,
  InstanceMethod: ShortString; TypeData: PTypeData);
var
  i: Integer;
  Handler: TPropHookChainCall;
begin
  i:=GetHandlerCount(htChainCall);
  while GetNextHandlerIndex(htChainCall,i) do begin
    Handler:=TPropHookChainCall(FHandlers[htChainCall][i]);
    Handler(AMethodName,InstanceName,InstanceMethod,TypeData);
  end;
end;

function TPropertyEditorHook.GetComponent(const ComponentPath: string): TComponent;
var
  i: Integer;
begin
  Result := nil;
  if not Assigned(LookupRoot) then
    Exit;
  i := GetHandlerCount(htGetComponent);
  while GetNextHandlerIndex(htGetComponent, i) and (Result = nil) do
    Result := TPropHookGetComponent(FHandlers[htGetComponent][i])(ComponentPath);
  // Note: TWriter only allows pascal identifiers for names, but in general
  // there is no restriction.
  if (Result = nil) and (LookupRoot is TComponent) then
    Result := TComponent(LookupRoot).FindComponent(ComponentPath);
end;

function TPropertyEditorHook.GetComponentName(AComponent: TComponent): String;
var
  i: Integer;
  CompName, OwnerName: String;
  Handler: TPropHookGetComponentName;
begin
  Result := '';
  if AComponent = nil then
    Exit;
  i := GetHandlerCount(htGetComponentName);
  while GetNextHandlerIndex(htGetComponentName, i) and (Result = '') do
  begin
    Handler := TPropHookGetComponentName(FHandlers[htGetComponentName][i]);
    Result := Handler(AComponent);
  end;
  if Result = '' then
  begin
    CompName := AComponent.Name;
    if (AComponent.Owner<>LookupRoot) and (AComponent.Owner<>nil) then
      OwnerName := AComponent.Owner.Name;
{    if CompName='' then
      DebugLn('TPropertyEditorHook.GetComponentName: AComponent.Name is empty, '+
              'AComponent.Owner.Name="' + OwnerName+'".');
    if OwnerName='' then
      DebugLn('TPropertyEditorHook.GetComponentName: AComponent.Owner.Name is empty.');
}
    Result := CompName;
    if OwnerName<>'' then
    begin
      Result := OwnerName;
      if CompName<>'' then
        Result := Result+'.'+CompName;
    end;
  end;
end;

procedure TPropertyEditorHook.GetComponentNames(TypeData: PTypeData;
  const Proc: TGetStrProc);

  procedure TraverseComponents(Root: TComponent);
  var
    i: integer;
  begin
    for i := 0 to Root.ComponentCount - 1 do
      if (Root.Components[i] is TypeData^.ClassType) then
        Proc(Root.Components[i].Name);
  end;

var
  i: integer;
  Handler: TPropHookGetComponentNames;
begin
  if not Assigned(LookupRoot) then
    Exit;
  i := GetHandlerCount(htGetComponentNames);
  if i > 0 then
  begin
    while GetNextHandlerIndex(htGetComponentNames, i) do
    begin
      Handler := TPropHookGetComponentNames(FHandlers[htGetComponentNames][i]);
      Handler(TypeData, Proc);
    end;
  end
  else if LookupRoot is TComponent then
    // No handler -> only traverse local form/datamodule components
    TraverseComponents(TComponent(LookupRoot));
end;

function TPropertyEditorHook.GetRootClassName: ShortString;
var
  i: Integer;
  Handler: TPropHookGetRootClassName;
begin
  Result := '';
  i := GetHandlerCount(htGetRootClassName);
  while GetNextHandlerIndex(htGetRootClassName, i) and (Result = '') do
  begin
    Handler := TPropHookGetRootClassName(FHandlers[htGetRootClassName][i]);
    Result := Handler();
  end;
  if (Result='') and Assigned(LookupRoot) then
    Result := LookupRoot.ClassName;
end;

function TPropertyEditorHook.GetAncestorInstance(const InstProp: TInstProp; out
  AncestorInstProp: TInstProp): boolean;
var
  i: Integer;
  Handler: TPropHookGetAncestorInstProp;
begin
  Result:=false;
  if (InstProp.Instance=nil) or (InstProp.PropInfo=nil) then exit;
  i := GetHandlerCount(htGetAncestorInstProp);
  while GetNextHandlerIndex(htGetAncestorInstProp, i) and (not Result) do
  begin
    Handler := TPropHookGetAncestorInstProp(FHandlers[htGetAncestorInstProp][i]);
    Result := Handler(InstProp,AncestorInstProp);
  end;
end;

function TPropertyEditorHook.AddClicked(ADesigner: TIDesigner;
  MouseDownComponent: TComponent; Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer; var AComponentClass: TComponentClass; var NewParent: TComponent
  ): boolean;
var
  i: Integer;
  Handler: TPropHookAddClicked;
begin
  i := GetHandlerCount(htAddClicked);
  while GetNextHandlerIndex(htAddClicked, i) do
  begin
    Handler := TPropHookAddClicked(FHandlers[htAddClicked][i]);
    Result := Handler(ADesigner,MouseDownComponent,Button,Shift,X,Y,
                      AComponentClass,NewParent);
    if not Result then exit;
    if AComponentClass=nil then
      exit(false);
  end;
  Result := True;
end;

function TPropertyEditorHook.BeforeAddPersistent(Sender: TObject;
  APersistentClass: TPersistentClass; Parent: TPersistent): boolean;
var
  i: Integer;
  Handler: TPropHookBeforeAddPersistent;
begin
  i := GetHandlerCount(htBeforeAddPersistent);
  while GetNextHandlerIndex(htBeforeAddPersistent, i) do
  begin
    Handler := TPropHookBeforeAddPersistent(FHandlers[htBeforeAddPersistent][i]);
    Result := Handler(Sender,APersistentClass,Parent);
    if not Result then exit;
  end;
  Result := True;
end;

procedure TPropertyEditorHook.ComponentRenamed(AComponent: TComponent);
var
  i: Integer;
begin
  i := GetHandlerCount(htComponentRenamed);
  while GetNextHandlerIndex(htComponentRenamed, i) do
    TPropHookComponentRenamed(FHandlers[htComponentRenamed][i])(AComponent);
end;

procedure TPropertyEditorHook.PersistentAdded(APersistent: TPersistent; Select: boolean);
var
  i: Integer;
begin
  i := GetHandlerCount(htPersistentAdded);
  while GetNextHandlerIndex(htPersistentAdded, i) do
    TPropHookPersistentAdded(FHandlers[htPersistentAdded][i])(APersistent, Select);
end;

procedure TPropertyEditorHook.PersistentDeleting(APersistent: TPersistent);
// call this to tell all IDE parts to remove all references from the APersistent
var
  i: Integer;
begin
  i:=GetHandlerCount(htPersistentDeleting);
  while GetNextHandlerIndex(htPersistentDeleting,i) do
    TPropHookPersistentDel(FHandlers[htPersistentDeleting][i])(APersistent);
end;

procedure TPropertyEditorHook.PersistentDeleted(APersistent: TPersistent);
var
  i: Integer;
begin
  i:=GetHandlerCount(htPersistentDeleted);
  while GetNextHandlerIndex(htPersistentDeleted,i) do
    TPropHookPersistentDel(FHandlers[htPersistentDeleted][i])(APersistent);
end;

procedure TPropertyEditorHook.DeletePersistent(var APersistent: TPersistent);
// Call this to actually free APersistent
// One of the hooks will free it.
var
  i: Integer;
begin
  if APersistent=nil then exit;
  i:=GetHandlerCount(htDeletePersistent);
  if i>0 then begin
    while (APersistent<>nil) and GetNextHandlerIndex(htDeletePersistent,i) do
      TPropHookDeletePersistent(FHandlers[htDeletePersistent][i])(APersistent);
  end else
    FreeThenNil(APersistent);
end;

procedure TPropertyEditorHook.GetSelection(const ASelection: TPersistentSelectionList);
var
  i: Integer;
  Handler: TPropHookGetSelection;
begin
  if ASelection=nil then exit;
  ASelection.Clear;
  i:=GetHandlerCount(htGetSelectedPersistents);
  while GetNextHandlerIndex(htGetSelectedPersistents,i) do begin
    Handler:=TPropHookGetSelection(FHandlers[htGetSelectedPersistents][i]);
    Handler(ASelection);
  end;
end;

procedure TPropertyEditorHook.SetSelection(
  const ASelection: TPersistentSelectionList);
var
  i: Integer;
  Handler: TPropHookSetSelection;
  APersistent: TPersistent;
  NewLookupRoot: TPersistent;
begin
  // update LookupRoot
  NewLookupRoot:=LookupRoot;
  if (ASelection<>nil) and (ASelection.Count>0) then begin
    APersistent:=ASelection[0];
    if APersistent<>nil then
      NewLookupRoot:=GetLookupRootForComponent(APersistent);
  end;
  LookupRoot:=NewLookupRoot;
  // set selection
  if ASelection=nil then exit;
  //debulgn(['TPropertyEditorHook.SetSelection A ASelection.Count=',ASelection.Count]);
  i:=GetHandlerCount(htSetSelectedPersistents);
  while GetNextHandlerIndex(htSetSelectedPersistents,i) do begin
    Handler:=TPropHookSetSelection(FHandlers[htSetSelectedPersistents][i]);
    Handler(ASelection);
  end;
  //debugln(['TPropertyEditorHook.SetSelection END ASelection.Count=',ASelection.Count]);
end;

procedure TPropertyEditorHook.Unselect(const APersistent: TPersistent);
var
  Selection: TPersistentSelectionList;
begin
  Selection := TPersistentSelectionList.Create;
  try
    GetSelection(Selection);
    if Selection.IndexOf(APersistent)>=0 then begin
      Selection.Remove(APersistent);
      SetSelection(Selection);
    end;
  finally
    Selection.Free;
  end;
end;

function TPropertyEditorHook.IsSelected(const APersistent: TPersistent): boolean;
var
  Selection: TPersistentSelectionList;
begin
  Selection := TPersistentSelectionList.Create;
  try
    GetSelection(Selection);
    Result:=Selection.IndexOf(APersistent)>=0;
  finally
    Selection.Free;
  end;
end;

procedure TPropertyEditorHook.SelectOnlyThis(const APersistent: TPersistent);
var
  NewSelection: TPersistentSelectionList;
begin
  NewSelection := TPersistentSelectionList.Create;
  try
    if APersistent<>nil then
      NewSelection.Add(APersistent);
    SetSelection(NewSelection);
  finally
    NewSelection.Free;
  end;
end;

procedure TPropertyEditorHook.AddDependency(const AClass: TClass;
  const AnUnitname: shortstring);
var
  i: Integer;
begin
  i:=GetHandlerCount(htAddDependency);
  while GetNextHandlerIndex(htAddDependency,i) do
    TPropHookAddDependency(FHandlers[htAddDependency][i])(AClass,AnUnitName);
end;

function TPropertyEditorHook.GetObject(const aName: ShortString): TPersistent;
var
  i: Integer;
begin
  Result:=nil;
  i:=GetHandlerCount(htGetObject);
  while GetNextHandlerIndex(htGetObject,i) and (Result=nil) do
    Result:=TPropHookGetObject(FHandlers[htGetObject][i])(aName);
end;

function TPropertyEditorHook.GetObjectName(Instance: TPersistent;
  AOwnerComp: TComponent): String;
var
  i: Integer;
begin
  Result:='';
  i:=GetHandlerCount(htGetObjectName);
  if i>0 then begin
    while GetNextHandlerIndex(htGetObjectName,i) and (Result='') do
      Result:=TPropHookGetObjectName(FHandlers[htGetObject][i])(Instance);
  end else
    if Instance is TComponent then
      Result:=TComponent(Instance).Name
    else if instance is TCollectionItem then 
      Result:=TCollectionItem(Instance).GetNamePath
    else begin
      Assert(Assigned(AOwnerComp),'TPropertyEditorHook.GetObjectName: AOwnerComp not assigned.');
      Result:=AOwnerComp.Name + ClassNameToComponentName(Instance.ClassName);
    end;
end;

procedure TPropertyEditorHook.GetObjectNames(TypeData: PTypeData;
  const Proc: TGetStrProc);
var
  i: Integer;
begin
  i:=GetHandlerCount(htGetObjectNames);
  while GetNextHandlerIndex(htGetObjectNames,i) do
    TPropHookGetObjectNames(FHandlers[htGetObjectNames][i])(TypeData,Proc);
end;

procedure TPropertyEditorHook.ObjectReferenceChanged(Sender: TObject;
  NewObject: TPersistent);
var
  i: Integer;
begin
  i:=GetHandlerCount(htObjectPropertyChanged);
  while GetNextHandlerIndex(htObjectPropertyChanged,i) do
    TPropHookObjectPropertyChanged(FHandlers[htObjectPropertyChanged][i])(
                  Sender,NewObject);
end;

procedure TPropertyEditorHook.Modified(Sender: TObject; PropName: ShortString);
var
  i: Integer;
  AForm: TCustomForm;
  Editor: TPropertyEditor;
  List: TFPList;
  APersistent: TPersistent;
  ARoot: TPersistent;
begin
  i := GetHandlerCount(htModified);
  while GetNextHandlerIndex(htModified,i) do
    TPropHookModified(FHandlers[htModified][i])(Sender);

  if PropName>'' then
  begin
    i := GetHandlerCount(htModifiedWithName);
    while GetNextHandlerIndex(htModifiedWithName,i) do
      TPropHookModifiedWithName(FHandlers[htModifiedWithName][i])(Sender, PropName);
  end;

  if Sender is TPropertyEditor then
  begin
    // mark the designer form of every selected persistent
    Editor := TPropertyEditor(Sender);
    List := TFPList.Create;
    try
      for i := 0 to Editor.PropCount - 1 do 
      begin
        // for every selected persistent ...
        APersistent := Editor.GetComponent(i);
        if APersistent = nil then Continue;
        if List.IndexOf(APersistent) >= 0 then Continue;
        List.Add(APersistent);
        // ... get the lookuproot ...
        ARoot := GetLookupRootForComponent(APersistent);
        if ARoot = nil then Continue;
        if (ARoot <> APersistent) and (List.IndexOf(ARoot) >= 0) then Continue;
        List.Add(ARoot);
        if ARoot is TControl then
          TControl(ARoot).Invalidate;
        // ... get the designer ...
        AForm := GetDesignerForm(ARoot);
        if Assigned(AForm) and Assigned(AForm.Designer) then
          AForm.Designer.Modified; // ... and mark it modified
      end;
    finally
      List.Free;
    end;
  end
  else 
  if Assigned(FLookupRoot) then
  begin
    AForm := GetDesignerForm(FLookupRoot);
    if Assigned(AForm) and Assigned(AForm.Designer) then
      AForm.Designer.Modified;
  end;
end;

procedure TPropertyEditorHook.DesignerMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  i: Integer;
  Handler: TMouseEvent;
begin
  i := GetHandlerCount(htDesignerMouseDown);
  while GetNextHandlerIndex(htDesignerMouseDown, i) do
  begin
    Handler := TMouseEvent(FHandlers[htDesignerMouseDown][i]);
    Handler(Sender, Button,  Shift, X, Y);
  end;
end;

procedure TPropertyEditorHook.DesignerMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  i: Integer;
  Handler: TMouseEvent;
begin
  i := GetHandlerCount(htDesignerMouseUp);
  while GetNextHandlerIndex(htDesignerMouseUp, i) do
  begin
    Handler := TMouseEvent(FHandlers[htDesignerMouseUp][i]);
    Handler(Sender, Button,  Shift, X, Y);
  end;
end;

procedure TPropertyEditorHook.Revert(Instance:TPersistent; PropInfo:PPropInfo);
var
  i: Integer;
begin
  i:=GetHandlerCount(htRevert);
  while GetNextHandlerIndex(htRevert,i) do
    TPropHookRevert(FHandlers[htRevert][i])(Instance,PropInfo);
end;

procedure TPropertyEditorHook.RefreshPropertyValues;
var
  i: Integer;
begin
  i:=GetHandlerCount(htRefreshPropertyValues);
  while GetNextHandlerIndex(htRefreshPropertyValues,i) do
    TPropHookRefreshPropertyValues(FHandlers[htRefreshPropertyValues][i])();
end;

function TPropertyEditorHook.GetCheckboxForBoolean: Boolean;
var
  i: Integer;
begin
  Result:=False;
  i:=GetHandlerCount(htGetCheckboxForBoolean);
  if i > 0 then
    TPropHookGetCheckboxForBoolean(FHandlers[htGetCheckboxForBoolean][0])(Result);
end;

procedure TPropertyEditorHook.RemoveAllHandlersForObject(const HandlerObject: TObject);
var
  HookType: TPropHookType;
begin
  for HookType:=Low(FHandlers) to High(FHandlers) do
    if FHandlers[HookType]<>nil then
      FHandlers[HookType].RemoveAllMethodsOfObject(HandlerObject);
end;

procedure TPropertyEditorHook.AddHandlerChangeLookupRoot(
  const OnChangeLookupRoot: TPropHookChangeLookupRoot);
begin
  AddHandler(htChangeLookupRoot,TMethod(OnChangeLookupRoot));
end;

procedure TPropertyEditorHook.RemoveHandlerChangeLookupRoot(
  const OnChangeLookupRoot: TPropHookChangeLookupRoot);
begin
  RemoveHandler(htChangeLookupRoot,TMethod(OnChangeLookupRoot));
end;

procedure TPropertyEditorHook.AddHandlerCreateMethod(
  const OnCreateMethod: TPropHookCreateMethod);
begin
  AddHandler(htCreateMethod,TMethod(OnCreateMethod));
end;

procedure TPropertyEditorHook.RemoveHandlerCreateMethod(
  const OnCreateMethod: TPropHookCreateMethod);
begin
  RemoveHandler(htCreateMethod,TMethod(OnCreateMethod));
end;

procedure TPropertyEditorHook.AddHandlerGetMethodName(
  const OnGetMethodName: TPropHookGetMethodName);
begin
  AddHandler(htGetMethodName,TMethod(OnGetMethodName));
end;

procedure TPropertyEditorHook.RemoveHandlerGetMethodName(
  const OnGetMethodName: TPropHookGetMethodName);
begin
  RemoveHandler(htGetMethodName,TMethod(OnGetMethodName));
end;

procedure TPropertyEditorHook.AddHandlerGetMethods(
  const OnGetMethods: TPropHookGetMethods);
begin
  AddHandler(htGetMethods,TMethod(OnGetMethods));
end;

procedure TPropertyEditorHook.RemoveHandlerGetMethods(
  const OnGetMethods: TPropHookGetMethods);
begin
  RemoveHandler(htGetMethods,TMethod(OnGetMethods));
end;

procedure TPropertyEditorHook.AddHandlerCompatibleMethodExists(
  const OnMethodExists: TPropHookCompatibleMethodExists);
begin
  AddHandler(htCompatibleMethodExists,TMethod(OnMethodExists));
end;

procedure TPropertyEditorHook.RemoveHandlerCompatibleMethodExists(
  const OnMethodExists: TPropHookCompatibleMethodExists);
begin
  RemoveHandler(htCompatibleMethodExists,TMethod(OnMethodExists));
end;

procedure TPropertyEditorHook.AddHandlerGetCompatibleMethods(
  const OnGetMethods: TPropHookGetCompatibleMethods);
begin
  AddHandler(htGetCompatibleMethods,TMethod(OnGetMethods));
end;

procedure TPropertyEditorHook.RemoveHandlerGetCompatibleMethods(
  const OnGetMethods: TPropHookGetCompatibleMethods);
begin
  RemoveHandler(htGetCompatibleMethods,TMethod(OnGetMethods));
end;

procedure TPropertyEditorHook.AddHandlerMethodExists(
  const OnMethodExists: TPropHookMethodExists);
begin
  AddHandler(htMethodExists,TMethod(OnMethodExists));
end;

procedure TPropertyEditorHook.RemoveHandlerMethodExists(
  const OnMethodExists: TPropHookMethodExists);
begin
  RemoveHandler(htMethodExists,TMethod(OnMethodExists));
end;

procedure TPropertyEditorHook.AddHandlerRenameMethod(
  const OnRenameMethod: TPropHookRenameMethod);
begin
  AddHandler(htRenameMethod,TMethod(OnRenameMethod));
end;

procedure TPropertyEditorHook.RemoveHandlerRenameMethod(
  const OnRenameMethod: TPropHookRenameMethod);
begin
  RemoveHandler(htRenameMethod,TMethod(OnRenameMethod));
end;

procedure TPropertyEditorHook.AddHandlerShowMethod(
  const OnShowMethod: TPropHookShowMethod);
begin
  AddHandler(htShowMethod,TMethod(OnShowMethod));
end;

procedure TPropertyEditorHook.RemoveHandlerShowMethod(
  const OnShowMethod: TPropHookShowMethod);
begin
  RemoveHandler(htShowMethod,TMethod(OnShowMethod));
end;

procedure TPropertyEditorHook.AddHandlerMethodFromAncestor(
  const OnMethodFromAncestor: TPropHookMethodFromAncestor);
begin
  AddHandler(htMethodFromAncestor,TMethod(OnMethodFromAncestor));
end;

procedure TPropertyEditorHook.RemoveHandlerMethodFromAncestor(
  const OnMethodFromAncestor: TPropHookMethodFromAncestor);
begin
  RemoveHandler(htMethodFromAncestor,TMethod(OnMethodFromAncestor));
end;

procedure TPropertyEditorHook.AddHandlerMethodFromLookupRoot(
  const OnMethodFromLookupRoot: TPropHookMethodFromLookupRoot);
begin
  AddHandler(htMethodFromLookupRoot,TMethod(OnMethodFromLookupRoot));
end;

procedure TPropertyEditorHook.RemoveHandlerMethodFromLookupRoot(
  const OnMethodFromLookupRoot: TPropHookMethodFromLookupRoot);
begin
  RemoveHandler(htMethodFromLookupRoot,TMethod(OnMethodFromLookupRoot));
end;

procedure TPropertyEditorHook.AddHandlerChainCall(
  const OnChainCall: TPropHookChainCall);
begin
  AddHandler(htChainCall,TMethod(OnChainCall));
end;

procedure TPropertyEditorHook.RemoveHandlerChainCall(
  const OnChainCall: TPropHookChainCall);
begin
  RemoveHandler(htChainCall,TMethod(OnChainCall));
end;

procedure TPropertyEditorHook.AddHandlerGetComponent(
  const OnGetComponent: TPropHookGetComponent);
begin
  AddHandler(htGetComponent,TMethod(OnGetComponent));
end;

procedure TPropertyEditorHook.RemoveHandlerGetComponent(
  const OnGetComponent: TPropHookGetComponent);
begin
  RemoveHandler(htGetComponent,TMethod(OnGetComponent));
end;

procedure TPropertyEditorHook.AddHandlerGetComponentName(
  const OnGetComponentName: TPropHookGetComponentName);
begin
  AddHandler(htGetComponentName,TMethod(OnGetComponentName));
end;

procedure TPropertyEditorHook.RemoveHandlerGetComponentName(
  const OnGetComponentName: TPropHookGetComponentName);
begin
  RemoveHandler(htGetComponentName,TMethod(OnGetComponentName));
end;

procedure TPropertyEditorHook.AddHandlerGetComponentNames(
  const OnGetComponentNames: TPropHookGetComponentNames);
begin
  AddHandler(htGetComponentNames,TMethod(OnGetComponentNames));
end;

procedure TPropertyEditorHook.RemoveHandlerGetComponentNames(
  const OnGetComponentNames: TPropHookGetComponentNames);
begin
  RemoveHandler(htGetComponentNames,TMethod(OnGetComponentNames));
end;

procedure TPropertyEditorHook.AddHandlerAddClicked(
  const Handler: TPropHookAddClicked);
begin
  AddHandler(htAddClicked,TMethod(Handler));
end;

procedure TPropertyEditorHook.RemoveHandlerAddClicked(
  const Handler: TPropHookAddClicked);
begin
  RemoveHandler(htAddClicked,TMethod(Handler));
end;

procedure TPropertyEditorHook.AddHandlerGetRootClassName(
  const OnGetRootClassName: TPropHookGetRootClassName);
begin
  AddHandler(htGetRootClassName,TMethod(OnGetRootClassName));
end;

procedure TPropertyEditorHook.RemoveHandlerGetRootClassName(
  const OnGetRootClassName: TPropHookGetRootClassName);
begin
  RemoveHandler(htGetRootClassName,TMethod(OnGetRootClassName));
end;

procedure TPropertyEditorHook.AddHandlerGetAncestorInstProp(
  const OnGetAncestorInstProp: TPropHookGetAncestorInstProp);
begin
  AddHandler(htGetAncestorInstProp,TMethod(OnGetAncestorInstProp));
end;

procedure TPropertyEditorHook.RemoveHandlerGetAncestorInstProp(
  const OnGetAncestorInstProp: TPropHookGetAncestorInstProp);
begin
  RemoveHandler(htGetAncestorInstProp,TMethod(OnGetAncestorInstProp));
end;

procedure TPropertyEditorHook.AddHandlerBeforeAddPersistent(
  const OnBeforeAddPersistent: TPropHookBeforeAddPersistent);
begin
  AddHandler(htBeforeAddPersistent,TMethod(OnBeforeAddPersistent));
end;

procedure TPropertyEditorHook.RemoveHandlerBeforeAddPersistent(
  const OnBeforeAddPersistent: TPropHookBeforeAddPersistent);
begin
  RemoveHandler(htBeforeAddPersistent,TMethod(OnBeforeAddPersistent));
end;

procedure TPropertyEditorHook.AddHandlerComponentRenamed(
  const OnComponentRenamed: TPropHookComponentRenamed);
begin
  AddHandler(htComponentRenamed,TMethod(OnComponentRenamed));
end;

procedure TPropertyEditorHook.RemoveHandlerComponentRenamed(
  const OnComponentRenamed: TPropHookComponentRenamed);
begin
  RemoveHandler(htComponentRenamed,TMethod(OnComponentRenamed));
end;

procedure TPropertyEditorHook.AddHandlerPersistentAdded(
  const OnPersistentAdded: TPropHookPersistentAdded);
begin
  AddHandler(htPersistentAdded,TMethod(OnPersistentAdded));
end;

procedure TPropertyEditorHook.RemoveHandlerPersistentAdded(
  const OnPersistentAdded: TPropHookPersistentAdded);
begin
  RemoveHandler(htPersistentAdded,TMethod(OnPersistentAdded));
end;

procedure TPropertyEditorHook.AddHandlerPersistentDeleting(
  const OnPersistentDeleting: TPropHookPersistentDel);
begin
  AddHandler(htPersistentDeleting,TMethod(OnPersistentDeleting));
end;

procedure TPropertyEditorHook.RemoveHandlerPersistentDeleting(
  const OnPersistentDeleting: TPropHookPersistentDel);
begin
  RemoveHandler(htPersistentDeleting,TMethod(OnPersistentDeleting));
end;

procedure TPropertyEditorHook.AddHandlerPersistentDeleted(
  const OnPersistentDeleted: TPropHookPersistentDel);
begin
  AddHandler(htPersistentDeleted,TMethod(OnPersistentDeleted));
end;

procedure TPropertyEditorHook.RemoveHandlerPersistentDeleted(
  const OnPersistentDeleted: TPropHookPersistentDel);
begin
  RemoveHandler(htPersistentDeleted,TMethod(OnPersistentDeleted));
end;

procedure TPropertyEditorHook.AddHandlerDeletePersistent(
  const OnDeletePersistent: TPropHookDeletePersistent);
begin
  AddHandler(htDeletePersistent,TMethod(OnDeletePersistent));
end;

procedure TPropertyEditorHook.RemoveHandlerDeletePersistent(
  const OnDeletePersistent: TPropHookDeletePersistent);
begin
  RemoveHandler(htDeletePersistent,TMethod(OnDeletePersistent));
end;

procedure TPropertyEditorHook.AddHandlerGetSelection(
  const OnGetSelection: TPropHookGetSelection);
begin
  AddHandler(htGetSelectedPersistents,TMethod(OnGetSelection));
end;

procedure TPropertyEditorHook.RemoveHandlerGetSelection(
  const OnGetSelection: TPropHookGetSelection);
begin
  RemoveHandler(htGetSelectedPersistents,TMethod(OnGetSelection));
end;

procedure TPropertyEditorHook.AddHandlerSetSelection(
  const OnSetSelection: TPropHookSetSelection);
begin
  AddHandler(htSetSelectedPersistents,TMethod(OnSetSelection));
end;

procedure TPropertyEditorHook.RemoveHandlerSetSelection(
  const OnSetSelection: TPropHookSetSelection);
begin
  RemoveHandler(htSetSelectedPersistents,TMethod(OnSetSelection));
end;

procedure TPropertyEditorHook.AddHandlerGetObject(const OnGetObject: TPropHookGetObject);
begin
  AddHandler(htGetObject,TMethod(OnGetObject));
end;

procedure TPropertyEditorHook.RemoveHandlerGetObject(
  const OnGetObject: TPropHookGetObject);
begin
  RemoveHandler(htGetObject,TMethod(OnGetObject));
end;

procedure TPropertyEditorHook.AddHandlerGetObjectName(
  const OnGetObjectName: TPropHookGetObjectName);
begin
  AddHandler(htGetObjectName,TMethod(OnGetObjectName));
end;

procedure TPropertyEditorHook.RemoveHandlerGetObjectName(
  const OnGetObjectName: TPropHookGetObjectName);
begin
  RemoveHandler(htGetObjectName,TMethod(OnGetObjectName));
end;

procedure TPropertyEditorHook.AddHandlerGetObjectNames(
  const OnGetObjectNames: TPropHookGetObjectNames);
begin
  AddHandler(htGetObjectNames,TMethod(OnGetObjectNames));
end;

procedure TPropertyEditorHook.RemoveHandlerGetObjectNames(
  const OnGetObjectNames: TPropHookGetObjectNames);
begin
  RemoveHandler(htGetObjectNames,TMethod(OnGetObjectNames));
end;

procedure TPropertyEditorHook.AddHandlerObjectPropertyChanged(
  const OnObjectPropertyChanged: TPropHookObjectPropertyChanged);
begin
  AddHandler(htObjectPropertyChanged,TMethod(OnObjectPropertyChanged));
end;

procedure TPropertyEditorHook.RemoveHandlerObjectPropertyChanged(
  const OnObjectPropertyChanged: TPropHookObjectPropertyChanged);
begin
  RemoveHandler(htObjectPropertyChanged,TMethod(OnObjectPropertyChanged));
end;

procedure TPropertyEditorHook.AddHandlerModified(const OnModified: TPropHookModified);
begin
  AddHandler(htModified,TMethod(OnModified));
end;

procedure TPropertyEditorHook.RemoveHandlerModified(const OnModified: TPropHookModified);
begin
  RemoveHandler(htModified,TMethod(OnModified));
end;

procedure TPropertyEditorHook.AddHandlerModifiedWithName(
  const OnModified: TPropHookModifiedWithName);
begin
  AddHandler(htModifiedWithName,TMethod(OnModified));
end;

procedure TPropertyEditorHook.RemoveHandlerModifiedWithName(
  const OnModified: TPropHookModifiedWithName);
begin
  RemoveHandler(htModifiedWithName,TMethod(OnModified));
end;

procedure TPropertyEditorHook.AddHandlerDesignerMouseDown(
  const OnMouseDown: TMouseEvent);
begin
  AddHandler(htDesignerMouseDown,TMethod(OnMouseDown));
end;

procedure TPropertyEditorHook.AddHandlerDesignerMouseUp(
  const OnMouseUp: TMouseEvent);
begin
  AddHandler(htDesignerMouseUp,TMethod(OnMouseUp));
end;

procedure TPropertyEditorHook.RemoveHandlerDesignerMouseDown(
  const OnMouseDown: TMouseEvent);
begin
  RemoveHandler(htDesignerMouseDown,TMethod(OnMouseDown));
end;

procedure TPropertyEditorHook.RemoveHandlerDesignerMouseUp(
  const OnMouseUp: TMouseEvent);
begin
  RemoveHandler(htDesignerMouseUp,TMethod(OnMouseUp));
end;

procedure TPropertyEditorHook.AddHandlerRevert(const OnRevert: TPropHookRevert);
begin
  AddHandler(htRevert,TMethod(OnRevert));
end;

procedure TPropertyEditorHook.RemoveHandlerRevert(const OnRevert: TPropHookRevert);
begin
  RemoveHandler(htRevert,TMethod(OnRevert));
end;

procedure TPropertyEditorHook.AddHandlerRefreshPropertyValues(
  const OnRefreshPropertyValues: TPropHookRefreshPropertyValues);
begin
  AddHandler(htRefreshPropertyValues,TMethod(OnRefreshPropertyValues));
end;

procedure TPropertyEditorHook.RemoveHandlerRefreshPropertyValues(
  const OnRefreshPropertyValues: TPropHookRefreshPropertyValues);
begin
  RemoveHandler(htRefreshPropertyValues,TMethod(OnRefreshPropertyValues));
end;

procedure TPropertyEditorHook.AddHandlerAddDependency(
  const OnAddDependency: TPropHookAddDependency);
begin
  AddHandler(htAddDependency,TMethod(OnAddDependency));
end;

procedure TPropertyEditorHook.RemoveHandlerAddDependency(
  const OnAddDependency: TPropHookAddDependency);
begin
  RemoveHandler(htAddDependency,TMethod(OnAddDependency));
end;

procedure TPropertyEditorHook.AddHandlerGetCheckboxForBoolean(
  const OnGetCheckboxForBoolean: TPropHookGetCheckboxForBoolean);
begin
  AddHandler(htGetCheckboxForBoolean,TMethod(OnGetCheckboxForBoolean));
end;

procedure TPropertyEditorHook.SetLookupRoot(APersistent: TPersistent);
var
  i: Integer;
begin
  if FLookupRoot=APersistent then exit;
  if FLookupRoot is TComponent then
    RemoveFreeNotification(TComponent(FLookupRoot));
  FLookupRoot:=APersistent;
  if FLookupRoot is TComponent then
    FreeNotification(TComponent(FLookupRoot));
  i:=GetHandlerCount(htChangeLookupRoot);
  while GetNextHandlerIndex(htChangeLookupRoot,i) do
    TPropHookChangeLookupRoot(FHandlers[htChangeLookupRoot][i])();
end;

procedure TPropertyEditorHook.AddHandler(HookType: TPropHookType;
  const Handler: TMethod);
begin
  if Handler.Code=nil then
    RaiseGDBException('TPropertyEditorHook.AddHandler');
  if FHandlers[HookType]=nil then
    FHandlers[HookType]:=TMethodList.Create;
  FHandlers[HookType].Add(Handler);
end;

procedure TPropertyEditorHook.RemoveHandler(HookType: TPropHookType;
  const Handler: TMethod);
begin
  if FHandlers[HookType]<>nil then
    FHandlers[HookType].Remove(Handler);
end;

function TPropertyEditorHook.GetHandlerCount(HookType: TPropHookType): integer;
begin
  if FHandlers[HookType]<>nil then
    Result:=FHandlers[HookType].Count
  else
    Result:=0;
end;

function TPropertyEditorHook.GetNextHandlerIndex(HookType: TPropHookType;
  var i: integer): boolean;
begin
  if FHandlers[HookType]<>nil then
    Result:=FHandlers[HookType].NextDownIndex(i)
  else begin
    i:=-1;
    Result:=false;
  end;
end;

procedure TPropertyEditorHook.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (Operation=opRemove) and (AComponent=FLookupRoot) then
    LookupRoot:=nil;
end;

destructor TPropertyEditorHook.Destroy;
var
  HookType: TPropHookType;
begin
  for HookType:=Low(FHandlers) to high(FHandlers) do
    FreeThenNil(FHandlers[HookType]);
  inherited Destroy;
end;

function KeyStringToVKCode(const s: string): word;
var
  i: PtrInt;
  Data: Pointer;
begin
  Result:=VK_UNKNOWN;
  if KeyStringIsIrregular(s) then begin
    Result:=word(StrToIntDef(copy(s,7,length(s)-8),VK_UNKNOWN));
    exit;
  end;
  if (s<>'none') and (s<>'') then begin
    if VirtualKeyStrings=nil then begin
      VirtualKeyStrings:=TStringHashList.Create(true);
      for i:=1 to 255 do
        VirtualKeyStrings.Add(KeyAndShiftStateToKeyString(word(i),[]), {%H-}Pointer(i));
    end;
  end else
    exit;
  Data:=VirtualKeyStrings.Data[s];
  if Data<>nil then
    Result:=word({%H-}PtrUInt(Data));
end;

function GetClassUnitName(Value: TClass): string;
begin
  if Value=nil then
    Result:=''
  else
    Result:=GetSourceClassUnitName(Value)
end;

procedure CreateComponentEvent(AComponent: TComponent; const EventName: string);
var
  CurDesigner: TIDesigner;
  PropInfo: PPropInfo;
  Hook: TPropertyEditorHook;
  PersistentList: TPersistentSelectionList;
  MethodPropEditor: TMethodPropertyEditor;
begin
  CurDesigner:=FindRootDesigner(AComponent);
  if CurDesigner=nil then exit;
  // search method
  PropInfo:=GetPropInfo(AComponent,EventName);
  //writeln('CreateComponentEvent B ',PropInfo<>nil,' ',PropInfo^.PropType<>nil,' ',PropInfo^.PropType^.Kind=tkMethod,' ',(PropInfo^.GetProc<>nil),' ',(PropInfo^.SetProc<>nil));
  if (PropInfo=nil)
  or (PropInfo^.PropType=nil)
  or (PropInfo^.PropType^.Kind<>tkMethod)
  or (PropInfo^.GetProc=nil)
  or (PropInfo^.SetProc=nil) then
    exit;
  
  MethodPropEditor:=nil;
  PersistentList:=nil;
  try
    PersistentList := TPersistentSelectionList.Create;
    PersistentList.Add(AComponent);
    Hook:=GlobalDesignHook;
    MethodPropEditor := TMethodPropertyEditor.Create(Hook,1);
    MethodPropEditor.SetPropEntry(0, AComponent, PropInfo);
    MethodPropEditor.Initialize;
    MethodPropEditor.Edit;
  finally
    MethodPropEditor.Free;
    PersistentList.Free;
  end;
end;

function ClassNameToComponentName(const AClassName: string): string;
begin
  Result:=AClassName;
  if (length(Result)>2) and (Result[1] in ['T','t'])
  and (not (Result[2] in ['0'..'9'])) then
    System.Delete(Result,1,1);
end;

function ControlAcceptsStreamableChildComponent(aControl: TWinControl;
  aComponentClass: TComponentClass; aLookupRoot: TPersistent): boolean;
{off $DEFINE VerboseAddDesigner}
var
  Parent: TWinControl;
begin
  Result:=false;
  if not (csAcceptsControls in aControl.ControlStyle) then
  begin
    {$IFDEF VerboseAddDesigner}
    debugln(['ControlAcceptsStreamableChildComponent missing csAcceptsControls in ',DbgSName(aControl)]);
    {$ENDIF}
    exit;
  end;

  if aComponentClass.InheritsFrom(TControl)
  and not aControl.CheckChildClassAllowed(aComponentClass, False) then
  begin
    {$IFDEF VerboseAddDesigner}
    debugln(['ControlAcceptsStreamableChildComponent aControl=',DbgSName(aControl),' CheckChildClassAllowed forbids ',DbgSName(aComponentClass)]);
    {$ENDIF}
    exit;
  end;

  // the LookupRoot allows children
  if aControl=aLookupRoot then
    exit(true);

  // TWriter only supports children of LookupRoot and LookupRoot.Components
  if (aControl.Owner <> aLookupRoot) and (aControl <> aLookupRoot) then
  begin
    {$IFDEF VerboseAddDesigner}
    debugln(['ControlAcceptsStreamableChildComponent wrong lookuproot aControl=',DbgSName(aControl),' aLookupRoot=',DbgSName(aLookupRoot),' aControl.Owner=',DbgSName(aControl.Owner)]);
    {$ENDIF}
    exit;
  end;

  // TWriter does not support children on nested components
  // (i.e. csInline , e.g. on a frame) nor any of its components
  Parent:=aControl;
  while (Parent<>nil) and (Parent<>aLookupRoot) do begin
    if csInline in Parent.ComponentState then begin
      {$IFDEF VerboseAddDesigner}
      debugln(['ControlAcceptsStreamableChildComponent aControl=',DbgSName(aControl),' Parent=',DbgSName(Parent),' csInline']);
      {$ENDIF}
      exit;
    end;
    Parent:=Parent.Parent;
  end;

  Result:=true;
end;

function ClassTypeInfo(Value: TClass): PTypeInfo;
begin
  Result := PTypeInfo(Value.ClassInfo);
end;

procedure EditCollection(AComponent: TComponent; ACollection: TCollection; APropName: String);
begin
  TCollectionPropertyEditor.ShowCollectionEditor(ACollection, AComponent, APropName);
end;

procedure EditCollectionNoAddDel(AComponent: TComponent; ACollection: TCollection; APropName: String);
begin
  TNoAddDeleteCollectionPropertyEditor.ShowCollectionEditor(ACollection, AComponent, APropName);
end;

function IsInteresting(AEditor: TPropertyEditor; const AFilter: TTypeKinds;
  const APropNameFilter: String): Boolean;

var
  visited: TFPList;

  // check set element names against AFilter
  function IsPropInSet( const ATypeInfo: PTypeInfo ) : Boolean;
  var
    EnumType: PTypeInfo;
    i: Integer;
  begin
    Result := False;
    if (ATypeInfo^.Kind <> tkSet) then exit;
    // Get TypeInfo of set type.
    EnumType := GetTypeData(ATypeInfo)^.CompType;
    with GetTypeData(EnumType)^ do
      for i := MinValue to MaxValue do
      begin
        Result := MultiWordSearch(APropNameFilter, GetEnumName(EnumType,i));
        if Result then
          Break;
      end;
  end;

  //check if class has property name
  function IsPropInClass( const ATypeInfo: PTypeInfo ) : Boolean;
  var
    propInfo: PPropInfo;
    propList: PPropList;
    i, propCount: Integer;
    quSubclass: TFPList;
    icurClass: Integer = 0;
  begin
    Result := False;
    quSubclass := TFPList.Create;
    quSubclass.Add(ATypeInfo);

    while icurClass < quSubclass.Count do
    begin
      propCount := GetPropList(quSubclass.Items[icurClass], propList);

      for i := 0 to propCount - 1 do
      begin
        propInfo := propList^[i];

        Result := MultiWordSearch(APropNameFilter, propInfo^.Name);

        if Result then break;
        //if encounter a Set check its elements name.
        if (propInfo^.PropType^.Kind = tkSet) then
        begin
          Result := IsPropInSet(propInfo^.PropType);
          if Result then break;
        end;
        //queue subclasses(only once) to check later.
        if (propInfo^.PropType^.Kind = tkClass) then
          if quSubclass.IndexOf(propInfo^.PropType) >= 0 then Continue
          else  quSubclass.Add(propInfo^.PropType);
      end;
      if Assigned(propList) then FreeMem(propList);
      //no need to check subclasses if result is already true.
      if Result then break;
      inc(icurClass);
    end;
    quSubclass.Free;
  end;

  // Add AForceShow to display T****PropertyEditor when subproperties found.
  // and name of class is not the same as filter
  procedure Rec(A: TPropertyEditor; AForceShow: Boolean = False);
  var
    propList: PPropList;
    i: Integer;
    ti: PTypeInfo;
    edClass: TPropertyEditorClass;
    ed: TPropertyEditor;
    obj: TPersistent;
    PropCnt: LongInt;
  begin
    ti := A.GetPropType;
    //DebugLn('IsInteresting: ', ti^.Name);
    Result := ti^.Kind <> tkClass;
    if Result then
    begin
      if (APropNameFilter = '') or AForceShow then
        exit;

      Result := MultiWordSearch(APropNameFilter, A.GetName); // Check single Props

      // Check if check Set has element.
      if (ti^.Kind = tkSet) and (A.ClassType <> TSetElementPropertyEditor) then
        Result := Result or IsPropInSet(A.GetPropType);

      exit;
    end;

    // Subproperties can change if user selects another object =>
    // we must show the property, even if it is not interesting currently.
    //Result := paVolatileSubProperties in A.GetAttributes; // Not really needed?
    //if Result then exit;

    if tkClass in AFilter then
    begin
      // We want classes => any non-trivial editor is immediately interesting.
      Result := A.ClassType <> TClassPropertyEditor;
      if Result then
      begin
        // if no SubProperties check against filter name
        if (APropNameFilter = '') then
          exit;

        Result := MultiWordSearch(APropNameFilter, A.GetName);

        if (paSubProperties in A.GetAttributes) then
          Result := Result or IsPropInClass(A.GetPropType);

        exit;
      end;
    end
    else if A.GetAttributes * [paSubProperties, paVolatileSubProperties] = [] then
      exit;

    obj := TPersistent(A.GetObjectValue);
    // At this stage, there is nothing interesting left in empty objects.
    if obj = nil then exit;

    // Class properties may directly or indirectly refer to the same class,
    // so we must avoid infinite recursion.
    if visited.IndexOf(ti) >= 0 then exit;
    visited.Add(ti);
    // actual published properties can be different since the instance can be inherited
    // so update type info from the instance
    ti := obj.ClassInfo;
    PropCnt := GetPropList(ti, propList);
    try
      for i := 0 to PropCnt - 1 do begin
        if not (propList^[i]^.PropType^.Kind in AFilter + [tkClass]) then continue;
        edClass := GetEditorClass(propList^[i], obj);
        if edClass = nil then continue;
        ed := edClass.Create(AEditor.FPropertyHook, 1);
        try
          ed.SetPropEntry(0, obj, propList^[i]);
          ed.Initialize;
          // filter TClassPropertyEditor name recursively
          Rec(ed, MultiWordSearch(APropNameFilter, A.GetName));
        finally
          ed.Free;
        end;
        if Result then break;
      end;
    finally
      FreeMem(propList);
    end;
    visited.Delete(visited.Count - 1);
  end;

begin
  visited := TFPList.Create;
  try
    //DebugLn('IsInteresting -> ', AEditor.GetPropInfo^.Name, ': ', AEditor.GetPropInfo^.PropType^.Name);
    Rec(AEditor);
    //DebugLn('IsInteresting <- ', BoolToStr(Result, true));
  finally
    visited.Free;
  end;
end;

function GetOrdField(Field: Pointer; FieldInfo: PTypeInfo): Int64;
var
  Signed: Boolean;
  DataSize: Integer;
  OrdType: TOrdType;
begin
  Result:=0;
  Signed := false;
  DataSize := 4;
  case FieldInfo^.Kind of
    tkChar, tkBool:
      DataSize:=1;
    tkWChar:
      DataSize:=2;
    tkSet,
    tkEnumeration,
    tkInteger:
      begin
        OrdType:=GetTypeData(FieldInfo)^.OrdType;
        case OrdType of
          otSByte,otUByte: DataSize := 1;
          otSWord,otUWord: DataSize := 2;
        end;
        Signed := OrdType in [otSByte,otSWord,otSLong];
      end;
    tkInt64 :
      begin
        DataSize:=8;
        Signed:=true;
      end;
    tkQword :
      begin
        DataSize:=8;
        Signed:=false;
      end;
  end;

  if Signed then begin
    case DataSize of
      1: Result:=PShortInt(Field)^;
      2: Result:=PSmallInt(Field)^;
      4: Result:=PLongint(Field)^;
      8: Result:=PInt64(Field)^;
    end;
  end else begin
    case DataSize of
      1: Result:=PByte(Field)^;
      2: Result:=PWord(Field)^;
      4: Result:=PLongint(Field)^;
      8: Result:=PInt64(Field)^;
    end;
  end;
end;

procedure SetOrdField(Field: Pointer; FieldInfo: PTypeInfo; Value: Int64);
var
  DataSize: Integer;
begin
  if FieldInfo^.Kind in [tkInt64,tkQword] then
    DataSize := 8
  else
    DataSize := 4;
  if not (FieldInfo^.Kind in [tkInt64,tkQword,tkClass,tkInterface,tkInterfaceRaw,tkDynArray]) then
    begin
      // cut off unnecessary stuff
      case GetTypeData(FieldInfo)^.OrdType of
        otSWord,otUWord:
          begin
            Value:=Value and $ffff;
            DataSize := 2;
          end;
        otSByte,otUByte:
          begin
            Value:=Value and $ff;
            DataSize := 1;
          end;
       end;
    end;
  case DataSize of
  1: PByte(Field)^:=Byte(Value);
  2: PWord(Field)^:=Word(Value);
  4: PLongint(Field)^:=Longint(Value);
  8: PInt64(Field)^:=Value;
  end;
end;

function GetEnumField(Field: Pointer; FieldInfo: PTypeInfo): String;
begin
  Result:=GetEnumName(FieldInfo, GetOrdField(Field, FieldInfo));
end;

procedure SetEnumField(Field: Pointer; FieldInfo: PTypeInfo; const Value: string);
var
  PV: Integer;
begin
  PV:=GetEnumValue(FieldInfo, Value);
  if (PV<0) then
    raise EPropertyError.CreateFmt(SErrUnknownEnumValue, [Value]);
  SetOrdField(Field, FieldInfo, PV);
end;

function GetSetField(Field: Pointer; FieldInfo: PTypeInfo; Brackets: Boolean): String;
begin
  Result:=SetToString(FieldInfo,GetOrdField(Field,FieldInfo),Brackets);
end;

procedure SetSetField(Field: Pointer; FieldInfo: PTypeInfo; const Value: string);
begin
  SetOrdField(Field,FieldInfo,StringToSet(FieldInfo,Value));
end;

function GetStringField(Field: Pointer; FieldInfo: PTypeInfo): String;
begin
  Result:='';
  case FieldInfo^.Kind of
    tkWString:
      Result:=AnsiString(GetWideStringField(Field,FieldInfo));
    tkUString:
      Result := AnsiString(GetUnicodeStringField(Field,FieldInfo));
    tkSString:
      Result := PShortString(Field)^;
    tkAString:
      Result := PAnsiString(Field)^;
  end;
end;

procedure SetStringField(Field: Pointer; FieldInfo: PTypeInfo; const Value: string);
begin
  case FieldInfo^.Kind of
    tkWString:
      SetWideStringField(Field,FieldInfo,WideString(Value));
    tkUString:
      SetUnicodeStringField(Field,FieldInfo,UnicodeString(Value));
    tkSString:
      PShortString(Field)^:=Value;
    tkAString:
      PAnsiString(Field)^:=Value;
  end;
end;

function GetWideStringField(Field: Pointer; FieldInfo: PTypeInfo): WideString;
begin
  Result:='';
  case FieldInfo^.Kind of
    tkSString,tkAString:
      Result := WideString(GetStringField(Field,FieldInfo));
    tkUString :
      Result := GetUnicodeStringField(Field,FieldInfo);
    tkWString:
      Result := PWideString(Field)^;
  end;
end;

procedure SetWideStringField(Field: Pointer; FieldInfo: PTypeInfo; const Value: WideString);
begin
  case FieldInfo^.Kind of
    tkSString,tkAString:
      SetStringField(Field,FieldInfo,AnsiString(Value));
    tkUString:
       SetUnicodeStringField(Field,FieldInfo,Value);
    tkWString:
      PWideString(Field)^:=Value;
  end;
end;

function GetUnicodeStringField(Field: Pointer; FieldInfo: PTypeInfo): UnicodeString;
begin
  Result:='';
  case FieldInfo^.Kind of
    tkSString,tkAString:
      Result := UnicodeString(GetStringField(Field,FieldInfo));
    tkWString:
      Result := GetWideStringField(Field,FieldInfo);
    tkUString :
      Result := PUnicodeString(Field)^;
  end;
end;

procedure SetUnicodeStringField(Field: Pointer; FieldInfo: PTypeInfo; const Value: UnicodeString
  );
begin
  case FieldInfo^.Kind of
    tkSString,tkAString:
      SetStringField(Field,FieldInfo,AnsiString(Value));
    tkWString:
      SetWideStringField(Field,FieldInfo,Value);
    tkUString:
      PUnicodeString(Field)^:=Value;
  end;
end;

function GetRawbyteStringField(Field: Pointer; FieldInfo: PTypeInfo): RawByteString;
begin
  Result:='';
  case FieldInfo^.Kind of
    tkWString:
      Result := RawByteString(GetWideStringField(Field,FieldInfo));
    tkUString:
      Result := RawByteString(GetUnicodeStringField(Field,FieldInfo));
    tkSString:
      Result := RawByteString(GetStringField(Field,FieldInfo));
    tkAString:
      Result := PAnsiString(Field)^;
  end;
end;

procedure SetRawbyteStringField(Field: Pointer; FieldInfo: PTypeInfo; const Value: RawByteString
  );
begin
  case FieldInfo^.Kind of
    tkWString:
      SetWideStringField(Field,FieldInfo,WideString(Value));
    tkUString:
       SetUnicodeStringField(Field,FieldInfo,UnicodeString(Value));
    tkSString:
      SetStringField(Field,FieldInfo,Value);
    tkAString:
      PAnsiString(Field)^:=Value;
  else
  end;
end;

function GetFloatField(Field: Pointer; FieldInfo: PTypeInfo): Extended;
begin
  Result:=0.0;
  case GetTypeData(FieldInfo)^.FloatType of
  ftSingle:
    Result:=PSingle(Field)^;
  ftDouble:
    Result:=PDouble(Field)^;
  ftExtended:
    Result:=PExtended(Field)^;
  ftComp:
    Result:=PComp(Field)^;
  ftCurr:
    Result:=PCurrency(Field)^;
  end;
end;

procedure SetFloatField(Field: Pointer; FieldInfo: PTypeInfo; const Value: Extended);
begin
  Case GetTypeData(FieldInfo)^.FloatType of
    ftSingle:
      PSingle(Field)^:=Value;
    ftDouble:
      PDouble(Field)^:=Value;
    ftExtended:
      PExtended(Field)^:=Value;
{$ifdef FPC_COMP_IS_INT64}
    ftComp:
      PComp(Field)^:=trunc(Value);
{$else FPC_COMP_IS_INT64}
    ftComp:
      PComp(Field)^:=Comp(Value);
{$endif FPC_COMP_IS_INT64}
    ftCurr:
      PCurrency(Field)^:=Value;
   end;
end;

function GetObjectField(Field: Pointer; FieldInfo: PTypeInfo; MinClass: TClass): TObject;
begin
  Result:=TObject(PPointer(Field)^);
  if (MinClass<>nil) and (Result<>nil) Then
    if not Result.InheritsFrom(MinClass) then
      Result:=nil;
  if FieldInfo=nil then ;
end;

procedure SetObjectField(Field: Pointer; FieldInfo: PTypeInfo; const Value: TObject);
begin
  PPointer(Field)^:=Pointer(Value);
  if FieldInfo=nil then ;
end;

function GetPointerField(Field: Pointer; FieldInfo: PTypeInfo): Pointer;
begin
  Result:=PPointer(Field)^;
  if FieldInfo=nil then ;
end;

procedure SetPointerField(Field: Pointer; FieldInfo: PTypeInfo; const Value: Pointer);
begin
  PPointer(Field)^:=Value;
  if FieldInfo=nil then ;
end;

function GetMethodField(Field: Pointer; FieldInfo: PTypeInfo): TMethod;
begin
  Result:=PMethod(Field)^;
  if FieldInfo=nil then ;
end;

procedure SetMethodField(Field: Pointer; FieldInfo: PTypeInfo; const Value: TMethod);
begin
  PMethod(Field)^:=Value;
  if FieldInfo=nil then ;
end;

function GetInterfaceField(Field: Pointer; FieldInfo: PTypeInfo): IInterface;
begin
  Result:=IInterface(PPointer(Field)^);
  if FieldInfo=nil then ;
end;

procedure SetInterfaceField(Field: Pointer; FieldInfo: PTypeInfo; const Value: IInterface);
begin
  PInterface(Field)^:=Value;
  if FieldInfo=nil then ;
end;

function GetVariantField(Field: Pointer; FieldInfo: PTypeInfo): Variant;
begin
  Result:=PVariant(Field)^;
end;

procedure SetVariantField(Field: Pointer; FieldInfo: PTypeInfo; const Value: Variant);
begin
  PVariant(Field)^:=Value;
end;

Function GetPointerProp(Instance: TObject; PropInfo : PPropInfo): Pointer;

Type
  TGetPointerProcIndex = function (index:longint): Pointer of object;
  TGetPointerProc = function (): Pointer of object;

var
  AMethod : TMethod;

begin
  case (PropInfo^.PropProcs) and 3 of
  ptField:
    Result := PPointer(Pointer(Instance) + LongWord(PropInfo^.GetProc))^;
  ptStatic,
  ptVirtual:
    begin
      if (PropInfo^.PropProcs and 3)=ptStatic then
        AMethod.Code:=PropInfo^.GetProc
      else
        AMethod.Code:=PCodePointer(Pointer(Instance.ClassType)+PtrUInt(PropInfo^.GetProc))^;
      AMethod.Data:=Instance;
      if ((PropInfo^.PropProcs shr 6) and 1)<>0 then
        Result:=TGetPointerProcIndex(AMethod)(PropInfo^.Index)
      else
        Result:=TGetPointerProc(AMethod)();
    end;
  else
    raise EPropertyError.CreateFmt(SErrCannotReadProperty, [PropInfo^.Name]);
  end;
end;

Procedure SetPointerProp(Instance: TObject; PropInfo : PPropInfo;  Value: Pointer);

type
  TSetPointerProcIndex = procedure(index: longint; p: pointer) of object;
  TSetPointerProc = procedure(p: pointer) of object;

var
  AMethod : TMethod;

begin
  case (PropInfo^.PropProcs shr 2) and 3 of
    ptField:
      PPointer(Pointer(Instance) + LongWord(PropInfo^.SetProc))^:=Value;
    ptStatic,
    ptVirtual:
      begin
        if ((PropInfo^.PropProcs shr 2) and 3)=ptStatic then
          AMethod.Code:=PropInfo^.SetProc
        else
          AMethod.Code:=PCodePointer(Pointer(Instance.ClassType)+PtrUInt(PropInfo^.SetProc))^;
        AMethod.Data:=Instance;
        if ((PropInfo^.PropProcs shr 6) and 1)<>0 then
          TSetPointerProcIndex(AMethod)(PropInfo^.Index,Value)
        else
          TSetPointerProc(AMethod)(Value);
      end;
  else
    raise EPropertyError.CreateFmt(SErrCannotWriteToProperty, [PropInfo^.Name]);
  end;
end;

function dbgs(peh: TPropEditHint): string;
begin
  writestr(Result,peh);
end;

{ TNoteBookActiveControlPropertyEditor }

function TNoteBookActiveControlPropertyEditor.CheckNewValue(APersistent: TPersistent): boolean;
var
  AComponent: TPersistent;
  Notebook: TCustomTabControl;
begin
  Result:=true;
  if APersistent=nil then exit;
  AComponent:=GetComponent(0);
  if not (AComponent is TCustomTabControl) then
    raise Exception.Create('invalid instance for this property editor');
  Notebook:=TCustomTabControl(AComponent);
  if Notebook.IndexOf(APersistent)<0 then
    raise Exception.Create('only children are allowed for this property');
end;

function TNoteBookActiveControlPropertyEditor.GetAttributes: TPropertyAttributes;
begin
  Result:=(inherited GetAttributes)-[paMultiSelect];
end;

procedure TNoteBookActiveControlPropertyEditor.GetValues(Proc: TGetStrProc);
var
  AComponent: TPersistent;
  Notebook: TCustomTabControl;
  i: Integer;
begin
  Proc(oisNone);
  AComponent:=GetComponent(0);
  if not (AComponent is TCustomTabControl) then exit;
  Notebook:=TCustomTabControl(AComponent);
  for i:=0 to Notebook.PageCount-1 do
    Proc(Notebook.Page[i].Name);
end;

{ TCustomShortCutGrabBox }

procedure TCustomShortCutGrabBox.SetKey(const AValue: Word);
var
  s: String;
  i: LongInt;
begin
  if FKey=AValue then exit;
  FKey:=AValue;
  s:=KeyAndShiftStateToKeyString(FKey,[]);
  {$IFDEF VerboseKeyboard}
  debugln(['TCustomShortCutGrabBox.SetKey ',Key,' "',s,'"']);
  {$ENDIF}
  i:=KeyComboBox.Items.IndexOf(s);
  if i>=0 then
    KeyComboBox.ItemIndex:=i
  else if KeyStringIsIrregular(s) then begin
    KeyComboBox.Items.Add(s);
    KeyComboBox.ItemIndex:=KeyComboBox.Items.IndexOf(s);
  end else
    KeyComboBox.ItemIndex:=0;
end;

procedure TCustomShortCutGrabBox.GrabButtonClick(Sender: TObject);
begin
  FGrabForm:=TForm.Create(Self);
  FGrabForm.BorderStyle:=bsDialog;
  FGrabForm.KeyPreview:=true;
  FGrabForm.Position:=poScreenCenter;
  FGrabForm.OnKeyDown:=@GrabFormKeyDown;
  FGrabForm.Caption:=oisPressAKey;
  with TLabel.Create(Self) do begin
    Caption:=oisPressAKeyEGCtrlP;
    BorderSpacing.Around:=50;
    Parent:=FGrabForm;
  end;
  FGrabForm.Width:=200;
  FGrabForm.Height:=50;
  FGrabForm.AutoSize:=true;
  FGrabForm.ShowModal;
  // After getting a key, focus the main form's OK button. User can just click Enter.
  if (Key <> VK_UNKNOWN) and Assigned(MainOkButton) then
    MainOkButton.SetFocus;
  FreeAndNil(FGrabForm);
end;

procedure TCustomShortCutGrabBox.ShiftCheckBoxClick(Sender: TObject);
var
  s: TShiftStateEnum;
begin
  for s:=Low(TShiftStateEnum) to High(TShiftStateEnum) do
    if FCheckBoxes[s]=Sender then
      if FCheckBoxes[s].Checked then
        Include(FShiftState,s)
      else
        Exclude(FShiftState,s);
end;

procedure TCustomShortCutGrabBox.GrabFormKeyDown(Sender: TObject;
  var AKey: Word; AShift: TShiftState);
begin
  {$IFDEF VerboseKeyboard}
  DebugLn(['TCustomShortCutGrabBox.GrabFormKeyDown ',AKey,' ',dbgs(AShift)]);
  DumpStack;
  {$ENDIF}
  if not (AKey in [VK_CONTROL, VK_LCONTROL, VK_RCONTROL,
             VK_SHIFT, VK_LSHIFT, VK_RSHIFT,
             VK_MENU, VK_LMENU, VK_RMENU,
             VK_LWIN, VK_RWIN,
             VK_PROCESSKEY,
             VK_MODECHANGE,
             VK_UNKNOWN, VK_UNDEFINED])
  then begin
    if (AKey=VK_ESCAPE) and (AShift=[]) then begin
      Key:=VK_UNKNOWN;
      ShiftState:=[];
    end else begin
      Key:=AKey;
      ShiftState:=AShift;
    end;
    FGrabForm.ModalResult:=mrOk;
  end;
end;

procedure TCustomShortCutGrabBox.KeyComboboxEditingDone(Sender: TObject);
begin
  Key:=KeyStringToVKCode(KeyComboBox.Text);
end;

function TCustomShortCutGrabBox.GetShiftCheckBox(Shift: TShiftStateEnum): TCheckBox;
begin
  Result:=FCheckBoxes[Shift];
end;

function TCustomShortCutGrabBox.GetKey: Word;
begin
  Result:=FKey;
  if (FKey = 0) then
    FShiftState:=[];
end;

procedure TCustomShortCutGrabBox.SetAllowedShifts(const AValue: TShiftState);
begin
  if FAllowedShifts=AValue then exit;
  FAllowedShifts:=AValue;
  ShiftState:=ShiftState*FAllowedShifts;
end;

procedure TCustomShortCutGrabBox.SetShiftButtons(const AValue: TShiftState);
begin
  if FShiftButtons=AValue then exit;
  FShiftButtons:=AValue;
  UpdateShiftButtons;
end;

procedure TCustomShortCutGrabBox.SetShiftState(const AValue: TShiftState);
var
  s: TShiftStateEnum;
begin
  if FShiftState=AValue then exit;
  FShiftState:=AValue;
  for s:=low(TShiftStateEnum) to High(TShiftStateEnum) do
    if FCheckBoxes[s]<>nil then
      FCheckBoxes[s].Checked:=s in FShiftState;
end;

procedure TCustomShortCutGrabBox.Loaded;
begin
  inherited Loaded;
  UpdateShiftButtons;
end;

procedure TCustomShortCutGrabBox.RealSetText(const Value: TCaption);
begin
  // do not allow to set caption
end;

procedure TCustomShortCutGrabBox.UpdateShiftButtons;
var
  s: TShiftStateEnum;
  LastCheckBox: TCheckBox;
begin
  if [csLoading,csDestroying]*ComponentState<>[] then exit;
  LastCheckBox:=nil;
  DisableAlign;
  try
    for s:=low(TShiftStateEnum) to High(TShiftStateEnum) do begin
      if s in FShiftButtons then begin
        if FCheckBoxes[s]=nil then begin
          FCheckBoxes[s]:=TCheckBox.Create(Self);
          with FCheckBoxes[s] do begin
            Name:='CheckBox'+ShiftToStr(s);
            Caption:=ShiftToStr(s);
            AutoSize:=true;
            Checked:=s in FShiftState;
            if LastCheckBox<>nil then
              AnchorToNeighbour(akLeft,6,LastCheckBox)
            else
              AnchorParallel(akLeft,0,Self);
            AnchorParallel(akTop,0,Self);
            AnchorParallel(akBottom,0,Self);
            Parent:=Self;
            OnClick:=@ShiftCheckBoxClick;
          end;
        end;
        LastCheckBox:=FCheckBoxes[s];
      end else begin
        FreeAndNil(FCheckBoxes[s]);
      end;
    end;
    if LastCheckBox<>nil then
      FKeyComboBox.AnchorToNeighbour(akLeft,6,LastCheckBox)
    else
      FKeyComboBox.AnchorParallel(akLeft,0,Self);
  finally
    EnableAlign;
  end;
end;

procedure TCustomShortCutGrabBox.Notification(AComponent: TComponent;
  Operation: TOperation);
var
  s: TShiftStateEnum;
begin
  inherited Notification(AComponent, Operation);
  if Operation=opRemove then begin
    if AComponent=FGrabButton then
      FGrabButton:=nil;
    if AComponent=FKeyComboBox then
      FKeyComboBox:=nil;
    if AComponent=FGrabForm then
      FGrabForm:=nil;
    for s:=Low(TShiftStateEnum) to High(TShiftStateEnum) do
      if FCheckBoxes[s]=AComponent then begin
        FCheckBoxes[s]:=nil;
        Exclude(FShiftButtons,s);
      end;
  end;
end;

function TCustomShortCutGrabBox.ShiftToStr(s: TShiftStateEnum): string;
begin
  case s of
  ssShift: Result:='Shift';
  ssAlt: Result:='Alt';
  ssCtrl: Result:='Ctrl';
  ssMeta: Result:='Meta';
  ssSuper: Result:='Super';
  ssHyper: {$IFDEF Darwin}
           Result:='Cmd';
           {$ELSE}
           Result:='Hyper';
           {$ENDIF}
  ssAltGr: Result:='AltGr';
  ssCaps: Result:='Caps';
  ssNum: Result:='Numlock';
  ssScroll: Result:='Scroll';
  else Result:='Modifier'+IntToStr(ord(s));
  end;
end;

constructor TCustomShortCutGrabBox.Create(TheOwner: TComponent);

  procedure AddKeyToCombobox(i: integer);
  var
    s: String;
  begin
    s := KeyAndShiftStateToKeyString(i, []);
    if not KeyStringIsIrregular(s) then
      FKeyComboBox.Items.Add(s);
  end;

var
  i: Integer;
  ShSt: TShiftStateEnum;
begin
  inherited Create(TheOwner);

  FAllowedShifts:=[ssShift, ssAlt, ssCtrl,
    ssMeta, ssSuper, ssHyper, ssAltGr,
    ssCaps, ssNum, ssScroll];

  FGrabButton:=TButton.Create(Self);
  with FGrabButton do begin
    Name:='GrabButton';
    Caption:=srGrabKey;
    Align:=alRight;
    AutoSize:=true;
    Parent:=Self;
    OnClick:=@GrabButtonClick;
  end;

  FKeyComboBox:=TComboBox.Create(Self);
  with FKeyComboBox do begin
    Name:='FKeyComboBox';
    AutoSize:=true;
    Items.BeginUpdate;
    AddKeyToCombobox(0);
    for i:=VK_BACK to VK_SCROLL do
      AddKeyToCombobox(i);
    for i:=VK_BROWSER_BACK to VK_OEM_CLEAR do
      AddKeyToCombobox(i);
    Items.EndUpdate;
    OnEditingDone:=@KeyComboboxEditingDone;
    Parent:=Self;
    AnchorToNeighbour(akRight,6,FGrabButton);
    AnchorVerticalCenterTo(FGrabButton);
    Constraints.MinWidth:=130;
  end;

  BevelOuter:=bvNone;
  ShiftButtons:=GetDefaultShiftButtons;
  ShiftState:=[];
  Key:=VK_UNKNOWN;
  KeyComboBox.Text:=KeyAndShiftStateToKeyString(Key,[]);

  // Fix TabOrders. The controls were created in "wrong" order.
  i:=FGrabButton.TabOrder;                 // GrabButton was created first.
  for ShSt:=Low(FCheckBoxes) to High(FCheckBoxes) do begin
    if Assigned(FCheckBoxes[ShSt]) then begin
      FCheckBoxes[ShSt].TabOrder:=i;
      Inc(i);
    end;
  end;
  FKeyComboBox.TabOrder:=i;
  FGrabButton.TabOrder:=i+1;
end;

function TCustomShortCutGrabBox.GetDefaultShiftButtons: TShiftState;
begin
  {$IFDEF Darwin}
  Result:=[ssCtrl,ssShift,ssAlt,ssMeta];
  {$ELSE}
  Result:=[ssCtrl,ssShift,ssAlt];
  {$ENDIF}
end;

procedure InitPropEdits;
begin
  // Don't create PropertyClassList and PropertyEditorMapperList lists here.
  // RegisterPropertyEditor and RegisterPropertyEditorMapper create them,
  //  and they are called from many initialization sections in unpredictable order.

  // register the standard property editors
  RegisterPropertyEditor(TypeInfo(AnsiString), TComponent, 'Name', TComponentNamePropertyEditor);
  RegisterPropertyEditor(TypeInfo(TTranslateString), TCustomLabel, 'Caption', TStringMultilinePropertyEditor);
  RegisterPropertyEditor(TypeInfo(TTranslateString), TCustomStaticText, 'Caption', TStringMultilinePropertyEditor);
  RegisterPropertyEditor(TypeInfo(TTranslateString), TCustomCheckBox, 'Caption', TStringMultilinePropertyEditor);
  RegisterPropertyEditor(TypeInfo(TTranslateString), TCustomSpeedButton, 'Caption', TStringMultilinePropertyEditor);
  RegisterPropertyEditor(TypeInfo(TTranslateString), TMenuItem, 'Caption', TMenuItemCaptionEditor);
  RegisterPropertyEditor(TypeInfo(TTranslateString), TComponent, 'Hint', TStringMultilinePropertyEditor);
  RegisterPropertyEditor(TypeInfo(TCaption), TGridColumnTitle, 'Caption', TStringMultilinePropertyEditor);
  RegisterPropertyEditor(TypeInfo(TTabOrder), TControl, 'TabOrder', TTabOrderPropertyEditor);
  RegisterPropertyEditor(TypeInfo(ShortString), nil, '', TCaptionPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TStrings), nil, '', TStringsPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TFileName), nil, '', TFileNamePropertyEditor);
  RegisterPropertyEditor(TypeInfo(AnsiString), nil, 'SessionProperties', TSessionPropertiesPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TModalResult), nil, 'ModalResult', TModalResultPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TShortCut), nil, '', TShortCutPropertyEditor);
  //RegisterPropertyEditor(DummyClassForPropTypes.PTypeInfos('TDate'), nil,'',TDatePropertyEditor);
  //RegisterPropertyEditor(DummyClassForPropTypes.PTypeInfos('TTime'), nil,'',TTimePropertyEditor);
  RegisterPropertyEditor(TypeInfo(TDateTime), nil, '', TDateTimePropertyEditor);
  RegisterPropertyEditor(TypeInfo(TCursor), nil, '', TCursorPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TComponent), nil, '', TComponentPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TComponent), nil, 'ActiveControl', TComponentOneFormPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TControl), TCoolBand, 'Control', TCoolBarControlPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TCollection), nil, '', TCollectionPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TFlowPanelControlList), TFlowPanel, 'ControlList', TNoAddDeleteCollectionPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TControl), TFlowPanelControl, 'Control', THiddenPropertyEditor);
  RegisterPropertyEditor(TypeInfo(AnsiString), TFileDialog, 'Filter', TFileDlgFilterProperty);
  RegisterPropertyEditor(TypeInfo(AnsiString), TFilterComboBox, 'Filter', TFileDlgFilterProperty);
  RegisterPropertyEditor(TypeInfo(AnsiString), TFileNameEdit, 'Filter', TFileDlgFilterProperty);
  RegisterPropertyEditor(TypeInfo(AnsiString), TCustomPropertyStorage, 'Filename', TFileNamePropertyEditor);
  RegisterPropertyEditor(TypeInfo(TStrings), TValueListEditor, 'Strings', TValueListPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TCustomPage), TCustomTabControl, 'ActivePage', TNoteBookActiveControlPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TSizeConstraints), TControl, 'Constraints', TConstraintsPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TStrings), TNoteBook, 'Pages', TPagesPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TTranslateString), TCustomTaskDialog, 'Text', TStringMultilinePropertyEditor);
  RegisterPropertyEditor(TypeInfo(TTranslateString), TCustomTaskDialog, 'ExpandedText', TStringMultilinePropertyEditor);
  RegisterPropertyEditor(TypeInfo(TTranslateString), TCustomTaskDialog, 'FooterText', TStringMultilinePropertyEditor);

  // Property is hidden and editing disabled by HiddenPropertyEditor :
  RegisterPropertyEditor(TypeInfo(TAnchorSide), TControl, 'AnchorSideLeft', THiddenPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TAnchorSide), TControl, 'AnchorSideTop', THiddenPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TAnchorSide), TControl, 'AnchorSideRight', THiddenPropertyEditor);
  RegisterPropertyEditor(TypeInfo(TAnchorSide), TControl, 'AnchorSideBottom', THiddenPropertyEditor);
  RegisterPropertyEditor(TypeInfo(LongInt), TControl, 'ClientWidth', THiddenPropertyEditor);
  RegisterPropertyEditor(TypeInfo(LongInt), TControl, 'ClientHeight', THiddenPropertyEditor);
  RegisterPropertyEditor(TypeInfo(AnsiString), TCustomForm, 'LCLVersion', THiddenPropertyEditor);
  RegisterPropertyEditor(TypeInfo(AnsiString), TCustomFrame, 'LCLVersion', THiddenPropertyEditor);

  // since fpc 2.6.0 WordBool, LongBool and QWordBool only allow 0 and 1
  RegisterPropertyEditor(TypeInfo(WordBool), nil, '', TBoolPropertyEditor);
  RegisterPropertyEditor(TypeInfo(LongBool), nil, '', TBoolPropertyEditor);
  RegisterPropertyEditor(TypeInfo(QWordBool), nil, '', TBoolPropertyEditor);

  RegisterPropertyEditor(TypeInfo(IInterface), nil, '', TInterfacePropertyEditor);
  RegisterPropertyEditor(TypeInfo(Variant), nil, '', TVariantPropertyEditor);
end;

procedure FinalPropEdits;
var
  i: integer;
  pm: PPropertyEditorMapperRec;
  pc: PPropertyClassRec;
  sec: PSelectionEditorClassRec;
begin
  if PropertyEditorMapperList<>nil then begin
    for i:=0 to PropertyEditorMapperList.Count-1 do begin
      pm:=PPropertyEditorMapperRec(PropertyEditorMapperList.Items[i]);
      Dispose(pm);
    end;
    FreeAndNil(PropertyEditorMapperList);
  end;

  if PropertyClassList<>nil then begin
    for i:=0 to PropertyClassList.Count-1 do begin
      pc:=PPropertyClassRec(PropertyClassList[i]);
      Dispose(pc);
    end;
    FreeAndNil(PropertyClassList);
  end;

  if Assigned(SelectionEditorClassList) then begin
    for i:=0 to SelectionEditorClassList.Count-1 do begin
      sec:=PSelectionEditorClassRec(SelectionEditorClassList[i]);
      Dispose(sec);
    end;
    FreeAndNil(SelectionEditorClassList);
  end;

  FreeAndNil(ListPropertyEditors);
  FreeAndNil(VirtualKeyStrings);
end;

initialization
  InitPropEdits;

finalization
  FinalPropEdits;

end.

