diff --git a/designer/componenttreeview.pas b/designer/componenttreeview.pas index 7464458706..b2e1711f34 100644 --- a/designer/componenttreeview.pas +++ b/designer/componenttreeview.pas @@ -14,14 +14,18 @@ Abstract: TComponentTreeView is a component to show the child components of a - TComponent. TControls can be shown in a hierachic view. + TComponent. TControls are shown in a hierachic view. It supports - multi selecting components - editing the creation order - editing the TControl.Parent hierachy - For example of the usage, see the object inspector. + For an usage example, see the object inspector. ToDo: + - pass selection to form editor + - icons + - pass keys to form editor + - drag&drop: change parent and position } unit ComponentTreeView; @@ -37,17 +41,20 @@ type TComponentTreeView = class(TCustomTreeView) private - FComponentList: TComponentSelectionList; + FComponentList: TBackupComponentList; FPropertyEditorHook: TPropertyEditorHook; + function GetSelections: TComponentSelectionList; procedure SetPropertyEditorHook(const AValue: TPropertyEditorHook); procedure SetSelections(const NewSelections: TComponentSelectionList); + protected + procedure DoSelectionChanged; override; public constructor Create(TheOwner: TComponent); override; destructor Destroy; override; procedure RebuildComponentNodes; virtual; function CreateNodeCaption(AComponent: TComponent): string; virtual; public - property Selections: TComponentSelectionList read FComponentList + property Selections: TComponentSelectionList read GetSelections write SetSelections; property PropertyEditorHook: TPropertyEditorHook read FPropertyEditorHook write SetPropertyEditorHook; @@ -60,10 +67,51 @@ implementation procedure TComponentTreeView.SetSelections( const NewSelections: TComponentSelectionList); begin - FComponentList.Assign(NewSelections); + if (PropertyEditorHook=nil) then begin + if (FComponentList.LookupRoot=nil) then + exit; + FComponentList.Clear; + end else begin + if FComponentList.IsEqual(PropertyEditorHook.LookupRoot,NewSelections) then + exit; + end; + FComponentList.LookupRoot:=PropertyEditorHook.LookupRoot; + FComponentList.Selection.Assign(NewSelections); RebuildComponentNodes; end; +procedure TComponentTreeView.DoSelectionChanged; +var + ANode: TTreeNode; + AComponent: TComponent; + NewSelection: TComponentSelectionList; +begin + NewSelection:=TComponentSelectionList.Create; + try + if (PropertyEditorHook<>nil) + and (PropertyEditorHook.LookupRoot<>nil) + and (not (csDestroying in ComponentState)) then begin + ANode:=GetFirstMultiSelected; + while ANode<>nil do begin + AComponent:=TComponent(ANode.Data); + if AComponent=nil then + RaiseGDBException('TComponentTreeView.DoSelectionChanged ANode.Data=nil'); + if ((AComponent.Owner=nil) + and (AComponent=PropertyEditorHook.LookupRoot)) + or (AComponent.Owner=PropertyEditorHook.LookupRoot) + then + NewSelection.Add(AComponent); + ANode:=ANode.GetNextMultiSelected; + end; + end; + if NewSelection.IsEqual(FComponentList.Selection) then exit; + FComponentList.Selection.Assign(NewSelection); + finally + NewSelection.Free; + end; + inherited DoSelectionChanged; +end; + procedure TComponentTreeView.SetPropertyEditorHook( const AValue: TPropertyEditorHook); begin @@ -72,10 +120,15 @@ begin RebuildComponentNodes; end; +function TComponentTreeView.GetSelections: TComponentSelectionList; +begin + Result:=FComponentList.Selection; +end; + constructor TComponentTreeView.Create(TheOwner: TComponent); begin inherited Create(TheOwner); - FComponentList:=TComponentSelectionList.Create; + FComponentList:=TBackupComponentList.Create; Options:=Options+[tvoAllowMultiselect,tvoAutoItemHeight,tvoKeepCollapsedNodes]; end; @@ -86,6 +139,26 @@ begin end; procedure TComponentTreeView.RebuildComponentNodes; + + procedure AddChildControls(AControl: TWinControl; ANode: TTreeNode); + var + i: Integer; + CurControl: TControl; + NewNode: TTreeNode; + begin + if AControl=nil then exit; + for i:=0 to AControl.ControlCount-1 do begin + CurControl:=AControl.Controls[i]; + if CurControl.Owner<>AControl.Owner then continue; + NewNode:=Items.AddChild(ANode,CreateNodeCaption(CurControl)); + NewNode.Data:=CurControl; + NewNode.ImageIndex:=-1; + NewNode.MultiSelected:=Selections.IndexOf(CurControl)>=0; + if CurControl is TWinControl then + AddChildControls(TWinControl(CurControl),NewNode); + end; + end; + var OldExpanded: TTreeNodeExpandedState; NewNode: TTreeNode; @@ -93,6 +166,7 @@ var i: Integer; AComponent: TComponent; RootNode: TTreeNode; + AControl: TControl; begin BeginUpdate; // save old expanded state and clear @@ -103,15 +177,25 @@ begin if RootComponent<>nil then begin // first add the lookup root RootNode:=Items.Add(nil,CreateNodeCaption(RootComponent)); + RootNode.Data:=RootComponent; RootNode.ImageIndex:=-1; - RootNode.Selected:=Selections.IndexOf(RootComponent)>=0; + RootNode.MultiSelected:=Selections.IndexOf(RootComponent)>=0; - // add components in creation order + // add components in creation order and TControl.Parent relationship for i:=0 to RootComponent.ComponentCount-1 do begin AComponent:=RootComponent.Components[i]; + if AComponent is TControl then begin + AControl:=TControl(AComponent); + if (AControl.Parent<>nil) and (AControl.Parent<>RootComponent) then + // child controls will be added recursively, not here + continue; + end; NewNode:=Items.AddChild(RootNode,CreateNodeCaption(AComponent)); + NewNode.Data:=AComponent; NewNode.ImageIndex:=-1; - NewNode.Selected:=Selections.IndexOf(AComponent)>=0; + NewNode.MultiSelected:=Selections.IndexOf(AComponent)>=0; + if AComponent is TWinControl then + AddChildControls(TWinControl(AComponent),NewNode); end; RootNode.Expand(true); diff --git a/designer/controlselection.pp b/designer/controlselection.pp index 3cf433e708..95f7a2774c 100644 --- a/designer/controlselection.pp +++ b/designer/controlselection.pp @@ -38,7 +38,7 @@ interface uses Classes, LCLLinux, LCLType, Controls, Forms, GraphType, Graphics, SysUtils, - EnvironmentOpts, DesignerProcs, Menus; + Menus, EnvironmentOpts, PropEdits, DesignerProcs; type TControlSelection = class; @@ -348,11 +348,12 @@ type function IndexOf(AComponent:TComponent):integer; function Add(AComponent: TComponent):integer; - function AssignComponent(AComponent:TComponent): boolean; procedure Remove(AComponent: TComponent); procedure Delete(Index:integer); procedure Clear; - procedure Assign(AControlSelection:TControlSelection); + function AssignComponent(AComponent:TComponent): boolean; + procedure Assign(AControlSelection: TControlSelection); + procedure AssignSelection(ASelection: TComponentSelectionList); function IsSelected(AComponent: TComponent): Boolean; function IsOnlySelected(AComponent: TComponent): Boolean; procedure SaveBounds; @@ -1818,6 +1819,25 @@ begin DoChange; end; +procedure TControlSelection.AssignSelection(ASelection: TComponentSelectionList + ); +var i:integer; +begin + if (cssNotSavingBounds in FStates) then exit; + Include(FStates,cssNotSavingBounds); + BeginUpdate; + Clear; + FControls.Capacity:=ASelection.Count; + for i:=0 to ASelection.Count-1 do + Add(ASelection[i]); + SetCustomForm; + UpdateBounds; + Exclude(FStates,cssNotSavingBounds); + SaveBounds; + EndUpdate; + DoChange; +end; + function TControlSelection.IsSelected(AComponent: TComponent): Boolean; begin Result:=(IndexOf(AComponent)>=0); diff --git a/designer/objectinspector.pp b/designer/objectinspector.pp index 774af31947..6656affeb0 100644 --- a/designer/objectinspector.pp +++ b/designer/objectinspector.pp @@ -22,6 +22,11 @@ ToDo: - backgroundcolor=clNone + - pair splitter + - default values for property editors + - set to default value + - Define Init values + - Set to init value } unit ObjectInspector; @@ -303,8 +308,6 @@ type TOnAddAvailableComponent = procedure(AComponent:TComponent; var Allowed:boolean) of object; - TOnSelectComponentInOI = procedure(AComponent:TComponent) of object; - TOIFlag = ( oifRebuildPropListsNeeded ); @@ -324,6 +327,7 @@ type ShowOptionsPopupMenuItem: TMenuItem; ComponentTree: TComponentTreeView; procedure AvailComboBoxCloseUp(Sender: TObject); + procedure ComponentTreeSelectionChanged(Sender: TObject); procedure OnBackgroundColPopupMenuItemClick(Sender :TObject); procedure OnShowHintPopupMenuItemClick(Sender :TObject); procedure OnShowOptionsPopupMenuItemClick(Sender :TObject); @@ -337,7 +341,7 @@ type FOnShowOptions: TNotifyEvent; FPropertyEditorHook:TPropertyEditorHook; FOnAddAvailableComponent:TOnAddAvailableComponent; - FOnSelectComponentInOI:TOnSelectComponentInOI; + FOnSelectComponentsInOI:TNotifyEvent; FOnModified: TNotifyEvent; FShowComponentTree: boolean; FUpdateLock: integer; @@ -372,8 +376,8 @@ type read FComponentList write SetSelections; property OnAddAvailComponent: TOnAddAvailableComponent read FOnAddAvailableComponent write FOnAddAvailableComponent; - property OnSelectComponentInOI: TOnSelectComponentInOI - read FOnSelectComponentInOI write FOnSelectComponentInOI; + property OnSelectComponentsInOI: TNotifyEvent + read FOnSelectComponentsInOI write FOnSelectComponentsInOI; property PropertyEditorHook: TPropertyEditorHook read FPropertyEditorHook write SetPropertyEditorHook; property OnModified: TNotifyEvent read FOnModified write FOnModified; @@ -2201,13 +2205,14 @@ begin Visible:=not FShowComponentTree; end; - // Component Tree + // Component Tree at top (filled with available components) ComponentTree:=TComponentTreeView.Create(Self); with ComponentTree do begin Name:='ComponentTree'; Height:=ComponentTreeHeight; Parent:=Self; Align:=alTop; + OnSelectionChanged:=@ComponentTreeSelectionChanged; Visible:=FShowComponentTree; end; @@ -2453,8 +2458,8 @@ var NewComponent,Root:TComponent; FComponentList.Clear; FComponentList.Add(c); RefreshSelections; - if Assigned(FOnSelectComponentInOI) then - FOnSelectComponentInOI(c); + if Assigned(FOnSelectComponentsInOI) then + FOnSelectComponentsInOI(Self); end; // AvailComboBoxChange @@ -2477,6 +2482,15 @@ begin end; end; +procedure TObjectInspector.ComponentTreeSelectionChanged(Sender: TObject); +begin + if (PropertyEditorHook=nil) or (PropertyEditorHook.LookupRoot=nil) then exit; + FComponentList.Assign(ComponentTree.Selections); + RefreshSelections; + if Assigned(FOnSelectComponentsInOI) then + FOnSelectComponentsInOI(Self); +end; + procedure TObjectInspector.OnBackgroundColPopupMenuItemClick(Sender :TObject); var ColorDialog:TColorDialog; begin diff --git a/designer/propedits.pp b/designer/propedits.pp index f51abd0187..2dcbe2775e 100644 --- a/designer/propedits.pp +++ b/designer/propedits.pp @@ -882,6 +882,30 @@ type procedure Assign(SourceSelectionList: TComponentSelectionList); property Items[Index: integer]: TComponent read GetItems write SetItems; default; end; + + TBackupComponentList = class + private + FComponentList: TList; + FLookupRoot: TComponent; + FSelection: TComponentSelectionList; + function GetComponents(Index: integer): TComponent; + procedure SetComponents(Index: integer; const AValue: TComponent); + procedure SetLookupRoot(const AValue: TComponent); + procedure SetSelection(const AValue: TComponentSelectionList); + protected + public + constructor Create; + destructor Destroy; override; + function IndexOf(AComponent: TComponent): integer; + procedure Clear; + function ComponentCount: integer; + function IsEqual(ALookupRoot: TComponent; + ASelection: TComponentSelectionList): boolean; + public + property LookupRoot: TComponent read FLookupRoot write SetLookupRoot; + property Components[Index: integer]: TComponent read GetComponents write SetComponents; + property Selection: TComponentSelectionList read FSelection write SetSelection; + end; //============================================================================== { @@ -5015,6 +5039,82 @@ begin end; +{ TBackupComponentList } + +function TBackupComponentList.GetComponents(Index: integer): TComponent; +begin + Result:=TComponent(FComponentList[Index]); +end; + +procedure TBackupComponentList.SetComponents(Index: integer; + const AValue: TComponent); +begin + FComponentList[Index]:=AValue; +end; + +procedure TBackupComponentList.SetLookupRoot(const AValue: TComponent); +var + i: Integer; +begin + FLookupRoot:=AValue; + FComponentList.Clear; + if FLookupRoot<>nil then + for i:=0 to FLookupRoot.ComponentCount-1 do + FComponentList.Add(FLookupRoot.Components[i]); + FSelection.Clear; +end; + +procedure TBackupComponentList.SetSelection( + const AValue: TComponentSelectionList); +begin + if FSelection=AValue then exit; + FSelection.Assign(AValue); +end; + +constructor TBackupComponentList.Create; +begin + FSelection:=TComponentSelectionList.Create; + FComponentList:=TList.Create; +end; + +destructor TBackupComponentList.Destroy; +begin + FreeAndNil(FSelection); + FreeAndNil(FComponentList); + inherited Destroy; +end; + +function TBackupComponentList.IndexOf(AComponent: TComponent): integer; +begin + Result:=FComponentList.IndexOf(AComponent); +end; + +procedure TBackupComponentList.Clear; +begin + LookupRoot:=nil; +end; + +function TBackupComponentList.ComponentCount: integer; +begin + Result:=FComponentList.Count; +end; + +function TBackupComponentList.IsEqual(ALookupRoot: TComponent; + ASelection: TComponentSelectionList): boolean; +var + i: Integer; +begin + Result:=false; + if ALookupRoot<>LookupRoot then exit; + if not FSelection.IsEqual(ASelection) then exit; + if ALookupRoot<>nil then begin + if ComponentCount<>ALookupRoot.ComponentCount then exit; + for i:=0 to FComponentList.Count-1 do + if TComponent(FComponentList[i])<>ALookupRoot.Components[i] then exit; + end; + Result:=true; +end; + initialization InitPropEdits; diff --git a/ide/main.pp b/ide/main.pp index 5ed66b5e5c..9ed3ec41c1 100644 --- a/ide/main.pp +++ b/ide/main.pp @@ -271,7 +271,7 @@ type procedure OnSrcNotebookViewJumpHistory(Sender : TObject); // ObjectInspector + PropertyEditorHook events - procedure OIOnSelectComponent(AComponent:TComponent); + procedure OIOnSelectComponents(Sender: TObject); procedure OIOnShowOptions(AComponent:TComponent); procedure OnPropHookGetMethods(TypeData:PTypeData; Proc:TGetStringProc); function OnPropHookMethodExists(const AMethodName:ShortString; @@ -930,11 +930,9 @@ begin Application.CreateForm(TLazFindReplaceDialog, FindReplaceDlg); end; -procedure TMainIDE.OIOnSelectComponent(AComponent:TComponent); +procedure TMainIDE.OIOnSelectComponents(Sender: TObject); begin - TheControlSelection.AssignComponent(AComponent); - if AComponent.Owner is TControl then - TControl(AComponent.Owner).Invalidate; + TheControlSelection.AssignSelection(ObjectInspector1.Selections); end; procedure TMainIDE.OIOnShowOptions(AComponent: TComponent); @@ -1147,7 +1145,7 @@ procedure TMainIDE.SetupObjectInspector; begin ObjectInspector1 := TObjectInspector.Create(Self); - ObjectInspector1.OnSelectComponentInOI:=@OIOnSelectComponent; + ObjectInspector1.OnSelectComponentsInOI:=@OIOnSelectComponents; ObjectInspector1.OnShowOptions:=@OIOnShowOptions; GlobalDesignHook:=TPropertyEditorHook.Create; @@ -9750,6 +9748,9 @@ end. { ============================================================================= $Log$ + Revision 1.643 2003/08/22 18:10:39 mattias + implemented selections in component tree + Revision 1.642 2003/08/20 15:06:57 mattias implemented Build+Run File diff --git a/lcl/comctrls.pp b/lcl/comctrls.pp index 98dcaf616a..3773a8ac49 100644 --- a/lcl/comctrls.pp +++ b/lcl/comctrls.pp @@ -1519,7 +1519,8 @@ type tvsWaitForDragging, tvsDblClicked, tvsTripleClicked, - tvsQuadClicked + tvsQuadClicked, + tvsSelectionChanged ); TTreeViewStates = set of TTreeViewState; @@ -1602,6 +1603,8 @@ type FScrolledTop: integer; // vertical scrolled pixels (hidden pixels at top) FSelectedColor: TColor; FSelectedNode: TTreeNode; + fSelectionChangeEventLock: integer; + fSeparatorColor: TColor; FSortType: TSortType; FStateChangeLink: TChangeLink; FStateImages: TCustomImageList; @@ -1610,7 +1613,6 @@ type FTreeLineColor: TColor; FTreeNodes: TTreeNodes; FUpdateCount: integer; - fSeparatorColor: TColor; //FWideText: WideString; procedure CanvasChanged(Sender: TObject); //procedure CMColorChanged(var Message: TMessage); message CM_COLORCHANGED; @@ -1688,6 +1690,7 @@ type procedure WMSize(var Msg: TLMSize); message LM_SIZE; //procedure WMContextMenu(var Message: TLMContextMenu); message LM_CONTEXTMENU; //procedure CMSysColorChange(var Message: TMessage); message CM_SYSCOLORCHANGE; + procedure InternalSelectionChanged; protected FChangeTimer: TTimer; //procedure Edit(const Item: TTVItem); dynamic; @@ -1744,6 +1747,7 @@ type procedure UpdateDefaultItemHeight; virtual; procedure WndProc(var Message: TLMessage); override; procedure UpdateInsertMark(X,Y: integer); virtual; + procedure DoSelectionChanged; virtual; protected property AutoExpand: Boolean read GetAutoExpand write SetAutoExpand default False; property BorderStyle: TBorderStyle @@ -1814,6 +1818,9 @@ type procedure SaveToFile(const FileName: string); procedure SaveToStream(Stream: TStream); procedure WriteDebugReport(const Prefix: string; AllNodes: boolean); + procedure LockSelectionChangeEvent; + procedure UnlockSelectionChangeEvent; + function GetFirstMultiSelected: TTreeNode; public property BackgroundColor: TColor read FBackgroundColor write SetBackgroundColor; @@ -2030,6 +2037,9 @@ end. { ============================================================================= $Log$ + Revision 1.84 2003/08/22 18:10:39 mattias + implemented selections in component tree + Revision 1.83 2003/08/22 07:58:38 mattias started componenttree diff --git a/lcl/include/treeview.inc b/lcl/include/treeview.inc index 0288ae1eaa..fd849c54d7 100644 --- a/lcl/include/treeview.inc +++ b/lcl/include/treeview.inc @@ -950,6 +950,7 @@ begin Exclude(FStates,nsMultiSelected); if TreeNodes<>nil then UnbindFromMultiSelected; end; + if TreeView<>nil then TreeView.InternalSelectionChanged; Update; end; @@ -1290,6 +1291,7 @@ var begin if (TreeView<>nil) and (not (tvoAllowMultiselect in TreeView.Options)) then exit; + if (TreeView<>nil) then TreeView.LockSelectionChangeEvent; FirstNode:=GetPrevSibling; while (FirstNode<>nil) and (not FirstNode.MultiSelected) do FirstNode:=FirstNode.GetPrevSibling; @@ -1304,6 +1306,7 @@ begin if ANode=LastNode then break; ANode:=ANode.GetNextSibling; end; + if (TreeView<>nil) then TreeView.UnlockSelectionChangeEvent; end; procedure TTreeNode.MakeVisible; @@ -1742,12 +1745,14 @@ procedure TTreeNodes.ClearMultiSelection; var ANode, OldNode: TTreeNode; begin + if Owner<>nil then Owner.LockSelectionChangeEvent; ANode:=FFirstMultiSelected; while ANode<>nil do begin OldNode:=ANode; ANode:=ANode.GetNextMultiSelected; OldNode.MultiSelected:=false; end; + if Owner<>nil then Owner.UnlockSelectionChangeEvent; end; function TTreeNodes.IsMultiSelection: boolean; @@ -2776,13 +2781,13 @@ end;} procedure TCustomTreeView.BeginUpdate; begin inc(FUpdateCount); + LockSelectionChangeEvent; end; procedure TCustomTreeView.EndUpdate; begin -// if FUpdateCount<=0 then -// writeln('TCustomTreeView.EndUpdate UpdateCount=',FUpdateCount); - if FUpdateCount<=0 then exit; + UnlockSelectionChangeEvent; + if FUpdateCount<=0 then RaiseGDBException('TCustomTreeView.EndUpdate'); dec(FUpdateCount); if FUpdateCount=0 then begin // ToDo: only refresh if something changed @@ -3618,8 +3623,7 @@ begin Value.Selected := True; Value.MakeVisible; end; - if Assigned(OnSelectionChanged) then OnSelectionChanged(Self); - Invalidate; + InternalSelectionChanged; end; function TCustomTreeView.GetShowButtons: boolean; @@ -4026,6 +4030,11 @@ begin SetInsertMark(nil,tvimNone); end; +procedure TCustomTreeView.DoSelectionChanged; +begin + if Assigned(OnSelectionChanged) then OnSelectionChanged(Self); +end; + function TCustomTreeView.IsInsertMarkVisible: boolean; begin Result:=(FInsertMarkType<>tvimNone) and (FInsertMarkNode<>nil) @@ -4628,8 +4637,10 @@ begin CursorNode.MultiSelected:=not CursorNode.MultiSelected; end else begin if (Selected<>CursorNode) or Items.IsMultiSelection then begin + LockSelectionChangeEvent; Items.ClearMultiSelection; CursorNode.MultiSelected:=true; + UnlockSelectionChangeEvent; end; end; end; @@ -4936,6 +4947,16 @@ begin inherited; end; +procedure TCustomTreeView.InternalSelectionChanged; +begin + if fSelectionChangeEventLock>0 then begin + Include(fStates,tvsSelectionChanged); + end else begin + Exclude(fStates,tvsSelectionChanged); + DoSelectionChanged; + end; +end; + { CustomDraw support } procedure TCustomTreeView.CanvasChanged(Sender: TObject); @@ -5054,6 +5075,26 @@ begin end; end; +procedure TCustomTreeView.LockSelectionChangeEvent; +begin + inc(fSelectionChangeEventLock); +end; + +procedure TCustomTreeView.UnlockSelectionChangeEvent; +begin + dec(fSelectionChangeEventLock); + if fSelectionChangeEventLock<0 then + RaiseGDBException('TCustomTreeView.UnlockSelectionChangeEvent'); + if (fSelectionChangeEventLock=0) + and (tvsSelectionChanged in fStates) then + InternalSelectionChanged; +end; + +function TCustomTreeView.GetFirstMultiSelected: TTreeNode; +begin + Result:=Items.FFirstMultiSelected; +end; + procedure TCustomTreeView.SetSeparatorColor(const AValue: TColor); begin if fSeparatorColor=AValue then exit;