unit CollectionPropEditForm; {$mode objfpc}{$H+} interface uses Classes, SysUtils, // LCL LCLType, LCLProc, Forms, Controls, Dialogs, ComCtrls, StdCtrls, ActnList, // IdeIntf ObjInspStrConsts, PropEditUtils, IDEImagesIntf, IDEWindowIntf; type { TCollectionPropertyEditorForm } TCollectionPropertyEditorForm = class(TForm) actAdd: TAction; actDel: TAction; actMoveUp: TAction; actMoveDown: TAction; ActionList1: TActionList; CollectionListBox: TListBox; ToolBar1: TToolBar; AddButton: TToolButton; DeleteButton: TToolButton; DividerToolButton: TToolButton; MoveUpButton: TToolButton; MoveDownButton: TToolButton; procedure actAddExecute(Sender: TObject); procedure actDelExecute(Sender: TObject); procedure actMoveUpDownExecute(Sender: TObject); procedure CollectionListBoxClick(Sender: TObject); procedure FormClose(Sender: TObject; var CloseAction: TCloseAction); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private FCollection: TCollection; FOwnerComponent: TPersistent; FOwnerPersistent: TPersistent; FPropertyName: String; procedure FillCollectionListBox; procedure ClearSelectionInObjectInspector; procedure SelectInObjectInspector(ForceUpdate: Boolean); procedure Modified; protected procedure UpdateCaption; procedure UpdateButtons; procedure PersistentAdded({%H-}APersistent: TPersistent; {%H-}Select: boolean); procedure ComponentRenamed(AComponent: TComponent); procedure PersistentDeleting(APersistent: TPersistent); procedure RefreshPropertyValues; public procedure SetCollection(NewCollection: TCollection; NewOwnerPersistent: TPersistent; const NewPropName: String); public property Collection: TCollection read FCollection; property OwnerComponent: TPersistent read FOwnerComponent; property OwnerPersistent: TPersistent read FOwnerPersistent; property PropertyName: String read FPropertyName; end; implementation {$R *.lfm} uses PropEdits; type TPersistentAccess = class(TPersistent) end; procedure TCollectionPropertyEditorForm.FormCreate(Sender: TObject); begin ToolBar1.Images := IDEImages.Images_16; actAdd.Caption := oiColEditAdd; actDel.Caption := oiColEditDelete; actMoveUp.Caption := oiColEditUp; actMoveDown.Caption := oiColEditDown; actAdd.ImageIndex := IDEImages.LoadImage('laz_add'); actDel.ImageIndex := IDEImages.LoadImage('laz_delete'); actMoveUp.ImageIndex := IDEImages.LoadImage('arrow_up'); actMoveDown.ImageIndex := IDEImages.LoadImage('arrow_down'); actMoveUp.ShortCut := scCtrl or VK_UP; actMoveDown.ShortCut := scCtrl or VK_DOWN; actAdd.Hint := oiColEditAdd; actDel.Hint := oiColEditDelete; actMoveUp.Hint := oiColEditUp; actMoveDown.Hint := oiColEditDown; IDEDialogLayoutList.ApplyLayout(Self); end; procedure TCollectionPropertyEditorForm.FormClose(Sender: TObject; var CloseAction: TCloseAction); begin IDEDialogLayoutList.SaveLayout(Self); end; procedure TCollectionPropertyEditorForm.FormDestroy(Sender: TObject); begin if GlobalDesignHook <> nil then GlobalDesignHook.RemoveAllHandlersForObject(Self); end; procedure TCollectionPropertyEditorForm.CollectionListBoxClick(Sender: TObject); begin UpdateButtons; UpdateCaption; SelectInObjectInspector(False); end; procedure TCollectionPropertyEditorForm.actAddExecute(Sender: TObject); begin if Collection = nil then Exit; Collection.Add; FillCollectionListBox; if CollectionListBox.Items.Count > 0 then CollectionListBox.ItemIndex := CollectionListBox.Items.Count - 1; SelectInObjectInspector(True); UpdateButtons; UpdateCaption; Modified; end; procedure TCollectionPropertyEditorForm.actDelExecute(Sender: TObject); var I : Integer; NewItemIndex: Integer; begin if Collection = nil then Exit; I := CollectionListBox.ItemIndex; if (I >= 0) and (I < Collection.Count) then begin if MessageDlg(oisConfirmDelete, Format(oisDeleteItem, [Collection.Items[I].DisplayName]), mtConfirmation, [mbYes, mbNo], 0) = mrYes then begin // select other item, or unselect NewItemIndex := I + 1; while (NewItemIndex < CollectionListBox.Items.Count) and (CollectionListBox.Selected[NewItemIndex]) do Inc(NewItemIndex); if NewItemIndex = CollectionListBox.Items.Count then begin NewItemIndex := 0; while (NewItemIndex < Pred(I)) and not (CollectionListBox.Selected[NewItemIndex]) do Inc(NewItemIndex); if NewItemIndex = I then NewItemIndex := -1; end; CollectionListBox.ItemIndex := -1; if NewItemIndex > I then Dec(NewItemIndex); //debugln('TCollectionPropertyEditorForm.DeleteClick A NewItemIndex=',dbgs(NewItemIndex),' ItemIndex=',dbgs(CollectionListBox.ItemIndex),' CollectionListBox.Items.Count=',dbgs(CollectionListBox.Items.Count),' Collection.Count=',dbgs(Collection.Count)); // unselect all items in OI (collections can act strange on delete) ClearSelectionInObjectInspector; // now delete Collection.Items[I].Free; // update listbox after whatever happened FillCollectionListBox; // set NewItemIndex if NewItemIndex < CollectionListBox.Items.Count then begin CollectionListBox.ItemIndex := NewItemIndex; SelectInObjectInspector(False); end; //debugln('TCollectionPropertyEditorForm.DeleteClick B'); Modified; end; end; UpdateButtons; UpdateCaption; end; procedure TCollectionPropertyEditorForm.actMoveUpDownExecute(Sender: TObject); var I, Direction: Integer; begin if Collection = nil then Exit; I := CollectionListBox.ItemIndex; if TComponent(Sender).Name = 'actMoveUp' then begin Direction := -1; Assert(I > 0, 'TCollectionPropertyEditorForm.actMoveUpDownExecute: wrong index.'); end else begin Direction := 1; Assert(I < Collection.Count-1, 'TCollectionPropertyEditorForm.actMoveUpDownExecute: wrong index.'); end; Collection.Items[I].Index := I + Direction; CollectionListBox.Items.Move(I + Direction, I); CollectionListBox.ItemIndex := I + Direction; FillCollectionListBox; SelectInObjectInspector(False); Modified; end; procedure TCollectionPropertyEditorForm.UpdateCaption; var NewCaption: String; begin //I think to match Delphi this should be formatted like //"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 CollectionListBox.ItemIndex > -1 then NewCaption := NewCaption + '[' + IntToStr(CollectionListBox.ItemIndex) + ']'; Caption := NewCaption; end; procedure TCollectionPropertyEditorForm.UpdateButtons; var I: Integer; begin I := CollectionListBox.ItemIndex; actAdd.Enabled := Collection <> nil; actDel.Enabled := I > -1; actMoveUp.Enabled := I > 0; actMoveDown.Enabled := (I >= 0) and (I < CollectionListBox.Items.Count - 1); end; procedure TCollectionPropertyEditorForm.PersistentAdded(APersistent: TPersistent; Select: boolean); begin //DebugLn('*** TCollectionPropertyEditorForm.PersistentAdded called ***'); FillCollectionListBox; end; procedure TCollectionPropertyEditorForm.ComponentRenamed(AComponent: TComponent); begin //DebugLn('*** TCollectionPropertyEditorForm.ComponentRenamed called ***'); if AComponent = OwnerPersistent then UpdateCaption; end; procedure TCollectionPropertyEditorForm.PersistentDeleting(APersistent: TPersistent); begin // For some reason this is called only when the whole collection is deleted, // for example when changing to another project. Thus clear the whole collection. DebugLn(['TCollectionPropertyEditorForm.PersistentDeleting: APersistent=', APersistent, ', OwnerPersistent=', OwnerPersistent, ', OwnerComponent=', OwnerComponent]); SetCollection(nil, nil, ''); Hide; UpdateButtons; UpdateCaption; end; procedure TCollectionPropertyEditorForm.RefreshPropertyValues; begin FillCollectionListBox; //DebugLn('*** TCollectionPropertyEditorForm.RefreshPropertyValues called ***'); end; procedure TCollectionPropertyEditorForm.FillCollectionListBox; var I: Integer; CurItem: String; Cnt: Integer; begin CollectionListBox.Items.BeginUpdate; try if Collection <> nil then Cnt := Collection.Count else Cnt := 0; // add or replace list items for I := 0 to Cnt - 1 do begin CurItem := IntToStr(I) + ' - ' + Collection.Items[I].DisplayName; if I >= CollectionListBox.Items.Count then CollectionListBox.Items.Add(CurItem) else CollectionListBox.Items[I] := CurItem; end; // delete unneeded list items if Cnt > 0 then begin while CollectionListBox.Items.Count > Cnt do CollectionListBox.Items.Delete(CollectionListBox.Items.Count - 1); end else CollectionListBox.Items.Clear; finally CollectionListBox.Items.EndUpdate; UpdateButtons; UpdateCaption; end; end; procedure TCollectionPropertyEditorForm.ClearSelectionInObjectInspector; var NewSelection: TPersistentSelectionList; begin if GlobalDesignHook = nil then Exit; // select in OI NewSelection := TPersistentSelectionList.Create; NewSelection.ForceUpdate := True; try GlobalDesignHook.SetSelection(NewSelection); GlobalDesignHook.LookupRoot := GetLookupRootForComponent(OwnerPersistent); finally NewSelection.Free; end; end; procedure TCollectionPropertyEditorForm.SelectInObjectInspector(ForceUpdate: Boolean); var I: Integer; NewSelection: TPersistentSelectionList; begin if (Collection = nil) or (GlobalDesignHook = nil) then Exit; // select in OI NewSelection := TPersistentSelectionList.Create; NewSelection.ForceUpdate := ForceUpdate; try for I := 0 to CollectionListBox.Items.Count - 1 do if CollectionListBox.Selected[I] then NewSelection.Add(Collection.Items[I]); GlobalDesignHook.SetSelection(NewSelection); GlobalDesignHook.LookupRoot := GetLookupRootForComponent(OwnerPersistent); finally NewSelection.Free; end; end; procedure TCollectionPropertyEditorForm.SetCollection(NewCollection: TCollection; NewOwnerPersistent: TPersistent; const NewPropName: String); begin if (FCollection = NewCollection) and (FOwnerPersistent = NewOwnerPersistent) and (FPropertyName = NewPropName) then Exit; FCollection := NewCollection; FOwnerPersistent := NewOwnerPersistent; FPropertyName := NewPropName; //find the component that owns the collection FOwnerComponent := NewOwnerPersistent; while FOwnerComponent <> nil do begin if FOwnerComponent is TComponent then break; FOwnerComponent := TPersistentAccess(FOwnerComponent).GetOwner; end; //debugln('TCollectionPropertyEditorForm.SetCollection A Collection=',dbgsName(FCollection),' OwnerPersistent=',dbgsName(OwnerPersistent),' PropName=',PropertyName); if GlobalDesignHook <> nil then begin GlobalDesignHook.RemoveAllHandlersForObject(Self); if FOwnerPersistent <> nil then begin GlobalDesignHook.AddHandlerPersistentAdded(@PersistentAdded); GlobalDesignHook.AddHandlerComponentRenamed(@ComponentRenamed); GlobalDesignHook.AddHandlerPersistentDeleting(@PersistentDeleting); GlobalDesignHook.AddHandlerRefreshPropertyValues(@RefreshPropertyValues); end; end; FillCollectionListBox; UpdateCaption; end; procedure TCollectionPropertyEditorForm.Modified; begin //debugln(['TCollectionPropertyEditorForm.Modified FOwnerPersistent=',DbgSName(FOwnerPersistent),' FCollection=',DbgSName(FCollection),' GlobalDesignHook.LookupRoot=',DbgSName(GlobalDesignHook.LookupRoot)]); if GlobalDesignHook <> nil then GlobalDesignHook.Modified(Self); end; end.