{ PropertyEditor Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. } unit SynPropertyEditObjectList; {$mode objfpc}{$H+} interface uses Classes, SysUtils, LCLProc, SynEdit, SynGutterBase, SynEditMiscClasses, SynEditMouseCmds, SynEditKeyCmds, SynDesignStringConstants, PropEdits, PropEditUtils, Forms, StdCtrls, ComCtrls, Dialogs, ComponentEditors, ObjInspStrConsts, Controls, IDEImagesIntf, typinfo, FormEditingIntf; type { TSynEdOptionsPropertyEditor } TSynEdOptionsPropertyEditor = class(TSetPropertyEditor) public procedure GetProperties(Proc: TGetPropEditProc); override; end; { TSynPropertEditClassList } TSynPropertyEditObjectList = class(TListPropertyEditor) protected function ReadElementCount: integer; override; function ReadElement(Index: integer): TPersistent; override; class function ClassList: TStringList; virtual; abstract; public function GetAttributes: TPropertyAttributes; override; procedure Edit; override; class function ShowClassListEditor(AClassList: TSynObjectList; OwnerPersistent: TPersistent; const PropName: String): TCustomForm; end; { TSynPropertyEditGutterPartList } TSynPropertyEditGutterPartList = class(TSynPropertyEditObjectList) protected class function ClassList: TStringList; override; end; { TSynObjectPartListPropertyEditorForm } TSynObjectPartListPropertyEditorForm = class(TForm) SynObjectPartsListBox: TListBox; ToolBar1: TToolBar; AddButton: TToolButton; ClassComboBox: TComboBox; DeleteButton: TToolButton; ToolButton3: TToolButton; MoveUpButton: TToolButton; MoveDownButton: TToolButton; procedure AddButtonClick(Sender: TObject); procedure SynObjectPartsListBoxClick(Sender: TObject); procedure DeleteButtonClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure MoveDownButtonClick(Sender: TObject); procedure MoveUpButtonClick(Sender: TObject); private FSynObjectPartList: TSynObjectList; FOwnerPersistent: TPersistent; FPropertyName: String; FClassesList: TStringList; protected procedure UpdateCaption; procedure UpdateButtons; procedure ComponentRenamed(AComponent: TComponent); procedure PersistentDeleting(APersistent: TPersistent); procedure RefreshPropertyValues; public procedure FillSynObjectPartsListBox; procedure SelectInObjectInspector(UnselectAll: Boolean); procedure SetSynObjectPartList(NewSynObjectPartList: TSynObjectList; NewClassesList: TStringList; NewOwnerPersistent: TPersistent; const NewPropName: String); procedure Modified; public property SynObjectPartList: TSynObjectList read FSynObjectPartList; property OwnerPersistent: TPersistent read FOwnerPersistent; property PropertyName: String read FPropertyName; end; { TSynKeyCommandPropertyEditor } TSynKeyCommandPropertyEditor = 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; { TSynMouseCommandPropertyEditor } TSynMouseCommandPropertyEditor = 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; { TSynEditComponentEditor } TSynEditComponentEditor = class(TDefaultComponentEditor) public function GetVerbCount: Integer; override; function GetVerb(Index: Integer): string; override; procedure ExecuteVerb(Index: Integer); override; end; procedure RegisterGutterPartClass(AClass: TSynGutterPartBaseClass; AName: String); implementation {$R *.lfm} const SynObjectPartListForm: TSynObjectPartListPropertyEditorForm = nil; KnownSynGutterPartClasses: TStringList = nil; procedure RegisterGutterPartClass(AClass: TSynGutterPartBaseClass; AName: String); begin if KnownSynGutterPartClasses = nil then KnownSynGutterPartClasses := TStringList.Create; KnownSynGutterPartClasses.AddObject(AName, TObject(Pointer(AClass))); end; { TSynEditComponentEditor } function TSynEditComponentEditor.GetVerbCount: Integer; begin Result := inherited GetVerbCount; Result := Result + 1; end; function TSynEditComponentEditor.GetVerb(Index: Integer): string; begin case Index - (inherited GetVerbCount) of 0: Result := syndsResetMouseActions; else Result := inherited GetVerb(Index); end; end; procedure TSynEditComponentEditor.ExecuteVerb(Index: Integer); var Hook: TPropertyEditorHook; begin case Index - (inherited GetVerbCount) of 0: begin if (Component <> nil) and (Component is TCustomSynEdit) then TCustomSynEdit(Component).ResetMouseActions; Modified; GetHook(Hook); if Assigned(Hook) then Hook.Modified(Self); end; else inherited ExecuteVerb(Index); end; end; { TSynEdOptionsPropertyEditor } procedure TSynEdOptionsPropertyEditor.GetProperties(Proc: TGetPropEditProc); var I: Integer; begin with GetTypeData(GetTypeData(GetPropType)^.CompType)^ do for I := MinValue to MaxValue do if not(TSynEditorOption(I) in SYNEDIT_UNIMPLEMENTED_OPTIONS) then Proc(TSetElementPropertyEditor.Create(Self, I)); end; { TSynKeyCommandPropertyEditor } function TSynKeyCommandPropertyEditor.GetAttributes: TPropertyAttributes; begin Result := [paMultiSelect, paValueList, paRevertable]; if GetDefaultOrdValue <> NoDefaultValue then Result := Result + [paHasDefaultValue]; end; function TSynKeyCommandPropertyEditor.OrdValueToVisualValue(OrdValue: longint): string; begin Result:=''; if not EditorCommandToIdent(OrdValue, Result) then Result := inherited OrdValueToVisualValue(OrdValue); end; procedure TSynKeyCommandPropertyEditor.GetValues(Proc: TGetStrProc); var CValue: Integer; begin CValue:=0; if not IdentToSynMouseCmd(GetVisualValue, CValue) then Proc(GetVisualValue); GetEditorCommandValues(Proc); end; procedure TSynKeyCommandPropertyEditor.SetValue(const NewValue: ansistring); var CValue: Integer; begin CValue:=0; if IdentToEditorCommand(NewValue, CValue) then SetOrdValue(CValue) else inherited SetValue(NewValue); end; { TSynMouseCommandPropertyEditor } function TSynMouseCommandPropertyEditor.GetAttributes: TPropertyAttributes; begin Result := [paMultiSelect, paValueList, paRevertable]; if GetDefaultOrdValue <> NoDefaultValue then Result := Result + [paHasDefaultValue]; end; function TSynMouseCommandPropertyEditor.OrdValueToVisualValue(OrdValue: longint): string; begin Result:=''; if not SynMouseCmdToIdent(OrdValue, Result) then Result := inherited OrdValueToVisualValue(OrdValue); end; procedure TSynMouseCommandPropertyEditor.GetValues(Proc: TGetStrProc); var CValue: Integer; CName: String; i: TSynEditorMouseCommand; begin CValue:=0; CName:=''; if not IdentToSynMouseCmd(GetVisualValue, CValue) then Proc(GetVisualValue); for i := 0 to emcMax do if SynMouseCmdToIdent(i, CName) then Proc(CName); end; procedure TSynMouseCommandPropertyEditor.SetValue(const NewValue: ansistring); var CValue: Integer; begin CValue:=0; if IdentToSynMouseCmd(NewValue, CValue) then SetOrdValue(CValue) else inherited SetValue(NewValue); end; { TSynObjectPartListPropertyEditorForm } procedure TSynObjectPartListPropertyEditorForm.AddButtonClick(Sender: TObject); var i: Integer; NewPart: TSynObjectListItem; begin if (SynObjectPartList = nil) or (FClassesList = nil) then Exit; i := ClassComboBox.ItemIndex; if (i < 0) or (i >= FClassesList.Count) then exit; NewPart := TSynObjectListItemClass(Pointer(FClassesList.Objects[i])).Create(SynObjectPartList); NewPart.Name := FormEditingHook.CreateUniqueComponentName(NewPart.ClassName, SynObjectPartList); FillSynObjectPartsListBox; if SynObjectPartsListBox.Items.Count > 0 then SynObjectPartsListBox.ItemIndex := SynObjectPartsListBox.Items.Count - 1; SelectInObjectInspector(False); UpdateButtons; UpdateCaption; Modified; end; procedure TSynObjectPartListPropertyEditorForm.SynObjectPartsListBoxClick(Sender: TObject); begin UpdateButtons; UpdateCaption; SelectInObjectInspector(False); end; procedure TSynObjectPartListPropertyEditorForm.DeleteButtonClick(Sender: TObject); var I : Integer; NewItemIndex: Integer; begin if SynObjectPartList = nil then Exit; I := SynObjectPartsListBox.ItemIndex; if (I >= 0) and (I < SynObjectPartList.Count) then begin if MessageDlg(oisConfirmDelete, Format(oisDeleteItem, [SynObjectPartList.BaseItems[I].DisplayName]), mtConfirmation, [mbYes, mbNo], 0) = mrYes then begin // select other item, or unselect NewItemIndex := I + 1; while (NewItemIndex < SynObjectPartsListBox.Items.Count) and (SynObjectPartsListBox.Selected[NewItemIndex]) do Inc(NewItemIndex); if NewItemIndex = SynObjectPartsListBox.Items.Count then begin NewItemIndex := 0; while (NewItemIndex < Pred(I)) and not (SynObjectPartsListBox.Selected[NewItemIndex]) do Inc(NewItemIndex); if NewItemIndex = I then NewItemIndex := -1; end; SynObjectPartsListBox.ItemIndex := -1; if NewItemIndex > I then Dec(NewItemIndex); // unselect all items in OI SelectInObjectInspector(True); // now delete SynObjectPartList.BaseItems[I].Free; // update listbox after whatever happened FillSynObjectPartsListBox; // set NewItemIndex if NewItemIndex < SynObjectPartsListBox.Items.Count then begin SynObjectPartsListBox.ItemIndex := NewItemIndex; SelectInObjectInspector(False); end; Modified; end; end; UpdateButtons; UpdateCaption; end; procedure TSynObjectPartListPropertyEditorForm.FormCreate(Sender: TObject); begin ToolBar1.Images := IDEImages.Images_16; AddButton.Caption := oiColEditAdd; DeleteButton.Caption := oiColEditDelete; MoveUpButton.Caption := oiColEditUp; MoveDownButton.Caption := oiColEditDown; AddButton.ImageIndex := IDEImages.LoadImage(16, 'laz_add'); DeleteButton.ImageIndex := IDEImages.LoadImage(16, 'laz_delete'); MoveUpButton.ImageIndex := IDEImages.LoadImage(16, 'arrow_up'); MoveDownButton.ImageIndex := IDEImages.LoadImage(16, 'arrow_down'); end; procedure TSynObjectPartListPropertyEditorForm.FormDestroy(Sender: TObject); begin if GlobalDesignHook <> nil then GlobalDesignHook.RemoveAllHandlersForObject(Self); end; procedure TSynObjectPartListPropertyEditorForm.MoveDownButtonClick(Sender: TObject); var I: Integer; begin if SynObjectPartList = nil then Exit; I := SynObjectPartsListBox.ItemIndex; if I >= SynObjectPartList.Count - 1 then Exit; SynObjectPartList.BaseItems[I].Index := I + 1; SynObjectPartsListBox.ItemIndex := I + 1; FillSynObjectPartsListBox; SelectInObjectInspector(False); Modified; end; procedure TSynObjectPartListPropertyEditorForm.MoveUpButtonClick(Sender: TObject); var I: Integer; begin if SynObjectPartList = nil then Exit; I := SynObjectPartsListBox.ItemIndex; if I < 0 then Exit; SynObjectPartList.BaseItems[I].Index := I - 1; SynObjectPartsListBox.ItemIndex := I - 1; FillSynObjectPartsListBox; SelectInObjectInspector(False); Modified; end; procedure TSynObjectPartListPropertyEditorForm.UpdateCaption; var NewCaption: String; begin //"Editing ComponentName.PropertyName[Index]" if OwnerPersistent is TComponent then NewCaption := TComponent(OwnerPersistent).Name else if OwnerPersistent <> nil then NewCaption := OwnerPersistent.GetNamePath else NewCaption := ''; if NewCaption <> '' then NewCaption := NewCaption + '.'; NewCaption := oiColEditEditing + ' ' + NewCaption + PropertyName; if SynObjectPartsListBox.ItemIndex > -1 then NewCaption := NewCaption + '[' + IntToStr(SynObjectPartsListBox.ItemIndex) + ']'; Caption := NewCaption; end; procedure TSynObjectPartListPropertyEditorForm.UpdateButtons; var I: Integer; begin if SynObjectPartList = nil then begin AddButton.Enabled := False; DeleteButton.Enabled := False; MoveUpButton.Enabled := False; MoveDownButton.Enabled := False; exit; end; I := SynObjectPartsListBox.ItemIndex; AddButton.Enabled := True; DeleteButton.Enabled := I > -1; MoveUpButton.Enabled := I > 0; MoveDownButton.Enabled := (I >= 0) and (I < SynObjectPartList.Count - 1); end; procedure TSynObjectPartListPropertyEditorForm.ComponentRenamed(AComponent: TComponent); begin if AComponent = OwnerPersistent then UpdateCaption; end; procedure TSynObjectPartListPropertyEditorForm.PersistentDeleting(APersistent: TPersistent); var OldSynObjectPartList: TSynObjectList; I: Integer; begin debugln(['TSynObjectPartListPropertyEditorForm.PersistentDeleting ']); if APersistent = OwnerPersistent then begin OldSynObjectPartList := SynObjectPartList; SetSynObjectPartList(nil, nil, nil, ''); GlobalDesignHook.Unselect(OldSynObjectPartList); for I := 0 to OldSynObjectPartList.Count - 1 do GlobalDesignHook.Unselect(OldSynObjectPartList.BaseItems[I]); if GlobalDesignHook.LookupRoot = OldSynObjectPartList then GlobalDesignHook.LookupRoot := nil; Hide; end; end; procedure TSynObjectPartListPropertyEditorForm.RefreshPropertyValues; begin FillSynObjectPartsListBox; Modified; end; procedure TSynObjectPartListPropertyEditorForm.FillSynObjectPartsListBox; var I: Integer; CurItem: String; Cnt: Integer; begin SynObjectPartsListBox.Items.BeginUpdate; try if SynObjectPartList <> nil then Cnt := SynObjectPartList.Count else Cnt := 0; // add or replace list items for I := 0 to Cnt - 1 do begin CurItem := IntToStr(I) + ' - ' + SynObjectPartList.BaseItems[I].DisplayName; if I >= SynObjectPartsListBox.Items.Count then SynObjectPartsListBox.Items.Add(CurItem) else SynObjectPartsListBox.Items[I] := CurItem; end; // delete unneeded list items if Cnt > 0 then begin while SynObjectPartsListBox.Items.Count > Cnt do begin SynObjectPartsListBox.Items.Delete(SynObjectPartsListBox.Items.Count - 1); end; end else begin SynObjectPartsListBox.Items.Clear; end; finally SynObjectPartsListBox.Items.EndUpdate; UpdateButtons; UpdateCaption; end; end; procedure TSynObjectPartListPropertyEditorForm.SelectInObjectInspector(UnselectAll: Boolean); var I: Integer; NewSelection: TPersistentSelectionList; begin if SynObjectPartList = nil then Exit; // select in OI NewSelection := TPersistentSelectionList.Create; try if not UnselectAll then begin for I := 0 to SynObjectPartsListBox.Items.Count - 1 do if SynObjectPartsListBox.Selected[I] then NewSelection.Add(SynObjectPartList.BaseItems[I]); end; GlobalDesignHook.SetSelection(NewSelection); GlobalDesignHook.LookupRoot := GetLookupRootForComponent(OwnerPersistent); finally NewSelection.Free; end; end; procedure TSynObjectPartListPropertyEditorForm.SetSynObjectPartList (NewSynObjectPartList: TSynObjectList; NewClassesList: TStringList; NewOwnerPersistent: TPersistent; const NewPropName: String); begin if (FSynObjectPartList = NewSynObjectPartList) and (FClassesList = NewClassesList) and (FOwnerPersistent = NewOwnerPersistent) and (FPropertyName = NewPropName) then Exit; FSynObjectPartList := NewSynObjectPartList; FClassesList := NewClassesList; if assigned(FClassesList) then ClassComboBox.Items.Assign(FClassesList) else ClassComboBox.Clear; if ClassComboBox.Items.Count >0 then ClassComboBox.ItemIndex := 0; // Can not use NewOwnerPersistent since it points to the SynGutter if NewSynObjectPartList <> nil then FOwnerPersistent := NewSynObjectPartList.Owner // The SynEdit else FOwnerPersistent := nil; FPropertyName := NewPropName; //debugln('TSynObjectPartListPropertyEditorForm.SetSynObjectPartList A SynObjectPartList=',dbgsName(FSynObjectPartList),' OwnerPersistent=',dbgsName(OwnerPersistent),' PropName=',PropertyName); if GlobalDesignHook <> nil then begin if FOwnerPersistent <> nil then begin GlobalDesignHook.AddHandlerComponentRenamed(@ComponentRenamed); GlobalDesignHook.AddHandlerPersistentDeleting(@PersistentDeleting); GlobalDesignHook.AddHandlerRefreshPropertyValues(@RefreshPropertyValues); end else begin GlobalDesignHook.RemoveAllHandlersForObject(Self); end; end; FillSynObjectPartsListBox; UpdateCaption; end; procedure TSynObjectPartListPropertyEditorForm.Modified; begin GlobalDesignHook.Modified(Self); //SelectInObjectInspector(False); end; { TSynPropertyEditObjectList } function TSynPropertyEditObjectList.ReadElementCount: integer; var SynObjectList: TObject; begin SynObjectList := GetObjectValue; if (SynObjectList <> nil) and (SynObjectList is TSynObjectList) then Result := TSynObjectList(SynObjectList).Count else Result:=0; end; function TSynPropertyEditObjectList.ReadElement(Index: integer): TPersistent; var SynObjectList: TSynObjectList; begin SynObjectList := TSynObjectList(GetObjectValue); Result := SynObjectList.BaseItems[Index]; end; function TSynPropertyEditObjectList.GetAttributes: TPropertyAttributes; begin Result := [paDialog, paReadOnly]; end; procedure TSynPropertyEditObjectList.Edit; var TheSynObjectList: TSynObjectList; begin TheSynObjectList := TSynObjectList(GetObjectValue); if TheSynObjectList = nil then raise Exception.Create('ObjectPartList=nil'); ShowClassListEditor(TheSynObjectList, GetComponent(0), GetName); end; class function TSynPropertyEditObjectList.ShowClassListEditor(AClassList: TSynObjectList; OwnerPersistent: TPersistent; const PropName: String): TCustomForm; begin if SynObjectPartListForm = nil then SynObjectPartListForm := TSynObjectPartListPropertyEditorForm.Create(Application); SynObjectPartListForm.SetSynObjectPartList(AClassList, ClassList, OwnerPersistent, PropName); SynObjectPartListForm.EnsureVisible; Result := SynObjectPartListForm; end; { TSynPropertyEditGutterPartList } class function TSynPropertyEditGutterPartList.ClassList: TStringList; begin Result := KnownSynGutterPartClasses; end; initialization if KnownSynGutterPartClasses = nil then KnownSynGutterPartClasses := TStringList.Create; finalization FreeAndNil(KnownSynGutterPartClasses); end.