diff --git a/components/ideintf/ideintf.lpk b/components/ideintf/ideintf.lpk index 44c91153c8..c8a35d9a8e 100644 --- a/components/ideintf/ideintf.lpk +++ b/components/ideintf/ideintf.lpk @@ -20,7 +20,7 @@ - + @@ -334,6 +334,14 @@ + + + + + + + + diff --git a/components/ideintf/ideintf.pas b/components/ideintf/ideintf.pas index 4a745a0677..53a440a280 100644 --- a/components/ideintf/ideintf.pas +++ b/components/ideintf/ideintf.pas @@ -21,7 +21,7 @@ uses PackageIntf, ProjectIntf, ProjectResourcesIntf, PropEdits, PropEditUtils, SrcEditorIntf, StatusBarPropEdit, StringsPropEditDlg, TextTools, TreeViewPropEdit, UnitResources, ProjPackIntf, DBGridColumnsPropEditForm, - ToolBarIntf, LazarusPackageIntf; + ToolBarIntf, ChangeParentDlg, LazarusPackageIntf; implementation diff --git a/components/ideintf/objectinspector.pp b/components/ideintf/objectinspector.pp index 027a6cd833..08f8b02996 100644 --- a/components/ideintf/objectinspector.pp +++ b/components/ideintf/objectinspector.pp @@ -30,7 +30,7 @@ uses // IMPORTANT: the object inspector is a tool and can be used in other programs // too. Don't put Lazarus IDE specific things here. // RTL / FCL - SysUtils, Types, Classes, TypInfo, FPCanvas, + SysUtils, Types, Classes, TypInfo, math, FPCanvas, // LCL InterfaceBase, LCLType, LCLIntf, Forms, Buttons, Graphics, GraphType, StdCtrls, Controls, ComCtrls, ExtCtrls, Menus, Dialogs, Themes, LMessages, LCLProc, @@ -40,8 +40,9 @@ uses // LazUtils LazConfigStorage, LazLoggerBase, // IdeIntf - ObjInspStrConsts, PropEdits, ListViewPropEdit, ImageListEditor, ComponentTreeView, - ComponentEditors, IDEImagesIntf, IDEHelpIntf, OIFavoriteProperties, PropEditUtils; + IDEImagesIntf, IDEHelpIntf, ObjInspStrConsts, + PropEdits, PropEditUtils, ComponentTreeView, OIFavoriteProperties, + ListViewPropEdit, ImageListEditor, ComponentEditors, ChangeParentDlg; const OIOptionsFileVersion = 3; @@ -712,7 +713,7 @@ type FOnNodeGetImageIndex: TOnOINodeGetImageEvent; procedure CreateTopSplitter; procedure CreateBottomSplitter; - function GetChangeParentCandidates: TFPList; + function GetParentCandidates: TFPList; function GetGridControl(Page: TObjectInspectorPage): TOICustomPropertyGrid; procedure SetComponentEditor(const AValue: TBaseComponentEditor); procedure SetFavorites(const AValue: TOIFavoriteProperties); @@ -776,6 +777,8 @@ type function GetActivePropertyGrid: TOICustomPropertyGrid; function GetActivePropertyRow: TOIPropertyGridRow; function GetCurRowDefaultValue(var DefaultStr: string): Boolean; + function GetHasParentCandidates: Boolean; + procedure ChangeParent; procedure HookRefreshPropertyValues; procedure ActivateGrid(Grid: TOICustomPropertyGrid); procedure FocusGrid(Grid: TOICustomPropertyGrid = nil); @@ -840,9 +843,6 @@ implementation {$R *.lfm} {$R images\ideintf_images.res} -uses - math; - const DefaultOIPageNames: array[TObjectInspectorPage] of shortstring = ( 'PropertyPage', @@ -4108,9 +4108,9 @@ begin AddPopupMenuItem(ChangeClassPopupMenuItem,nil,'ChangeClassPopupMenuItem', oisChangeClass,'Change Class of component', '', @OnChangeClassPopupmenuItemClick,false,true,true); - AddPopupMenuItem(ChangeParentPopupMenuItem,nil,'ChangeParentPopupMenuItem', - oisChangeParent,'Change Parent of component', '', - Nil,false,true,true); + AddPopupMenuItem(ChangeParentPopupMenuItem, nil, 'ChangeParentPopupMenuItem', + oisChangeParent+' ...', 'Change Parent of component', '', + @DoChangeParentItemClick, False, True, True); OptionsSeparatorMenuItem3 := AddSeparatorMenuItem(nil, 'OptionsSeparatorMenuItem3', true); AddPopupMenuItem(ShowComponentTreePopupMenuItem,nil @@ -4500,6 +4500,138 @@ begin end; end; +function TObjectInspectorDlg.GetParentCandidates: TFPList; +var + i, j: Integer; + CurSelected: TPersistent; + Candidate: TWinControl; +begin + Result := TFPList.Create; + if not (FPropertyEditorHook.LookupRoot is TWinControl) then + exit; // only LCL controls are supported at the moment + + // check if any selected control can be moved + i := Selection.Count-1; + while i >= 0 do + begin + if (Selection[i] is TControl) + and (TControl(Selection[i]).Owner = FPropertyEditorHook.LookupRoot) + then + // this one can be moved + break; + dec(i); + end; + if i < 0 then Exit; + + // find possible new parents + for i := 0 to TWinControl(FPropertyEditorHook.LookupRoot).ComponentCount-1 do + begin + Candidate := TWinControl(TWinControl(FPropertyEditorHook.LookupRoot).Components[i]); + if not (Candidate is TWinControl) then continue; + j := Selection.Count-1; + while j >= 0 do + begin + CurSelected := Selection[j]; + if CurSelected is TControl then begin + if CurSelected = Candidate then break; + if (CurSelected is TWinControl) and + (TWinControl(CurSelected) = Candidate.Parent) then + break; + if not ControlAcceptsStreamableChildComponent(Candidate, + TComponentClass(CurSelected.ClassType), FPropertyEditorHook.LookupRoot) + then + break; + end; + dec(j); + end; + if j < 0 then + Result.Add(Candidate); + end; + Result.Add(FPropertyEditorHook.LookupRoot); +end; + +function TObjectInspectorDlg.GetHasParentCandidates: Boolean; +var + Candidates: TFPList=nil; +begin + try + Candidates := GetParentCandidates; + Result := (Candidates.Count>1); // single candidate is current parent + finally + Candidates.Free; + end; +end; + +procedure TObjectInspectorDlg.ChangeParent; +var + i: Integer; + Control: TControl; + NewParentName: String; + NewParent: TPersistent; + NewSelection: TPersistentSelectionList; + Candidates: TFPList = nil; +begin + if (Selection.Count < 1) then Exit; + + try + Candidates := GetParentCandidates; + if not ShowChangeParentDlg(Selection, Candidates, NewParentName) then + Exit; + finally + Candidates.Free; + end; + + if NewParentName = TWinControl(FPropertyEditorHook.LookupRoot).Name then + NewParent := FPropertyEditorHook.LookupRoot + else + NewParent := TWinControl(FPropertyEditorHook.LookupRoot).FindComponent(NewParentName); + + if not (NewParent is TWinControl) then Exit; + + for i := 0 to Selection.Count-1 do + begin + if not (Selection[i] is TControl) then Continue; + Control := TControl(Selection[i]); + if Control.Parent = nil then Continue; + Control.Parent := TWinControl(NewParent); + end; + + // Following code taken from DoZOrderItemClick(); + // Ensure the order of controls in the OI now reflects the new ZOrder + //NewSelection := TPersistentSelectionList.Create; + //try + // NewSelection.ForceUpdate:=True; + // NewSelection.Add(Control.Parent); + // SetSelection(NewSelection); + // + // NewSelection.Clear; + // NewSelection.ForceUpdate:=True; + // NewSelection.Add(Control); + // SetSelection(NewSelection); + //finally + // NewSelection.Free; + //end; + + // Ensure the order of controls in the OI now reflects the new ZOrder + // (this code based on commented above) + NewSelection := TPersistentSelectionList.Create; + try + NewSelection.ForceUpdate:=True; + NewSelection.Add(NewParent); + for i:=0 to Selection.Count-1 do + NewSelection.Add(Selection.Items[i]); + SetSelection(NewSelection); + + NewSelection.ForceUpdate:=True; + NewSelection.Delete(0); + SetSelection(NewSelection); + finally + NewSelection.Free; + end; + + DoModified(Self); +end; + procedure TObjectInspectorDlg.SetSelection(const ASelection: TPersistentSelectionList); var OldInSelection: Boolean; @@ -5316,56 +5448,6 @@ begin end; // --- -function TObjectInspectorDlg.GetChangeParentCandidates: TFPList; -var - i, j: Integer; - CurSelected: TPersistent; - Candidate: TWinControl; -begin - Result := TFPList.Create; - if not (FPropertyEditorHook.LookupRoot is TWinControl) then - exit; // only LCL controls are supported at the moment - - // check if any selected control can be moved - i := Selection.Count-1; - while i >= 0 do - begin - if (Selection[i] is TControl) - and (TControl(Selection[i]).Owner = FPropertyEditorHook.LookupRoot) - then - // this one can be moved - break; - dec(i); - end; - if i < 0 then Exit; - - // find possible new parents - for i := 0 to TWinControl(FPropertyEditorHook.LookupRoot).ComponentCount-1 do - begin - Candidate := TWinControl(TWinControl(FPropertyEditorHook.LookupRoot).Components[i]); - if not (Candidate is TWinControl) then continue; - j := Selection.Count-1; - while j >= 0 do - begin - CurSelected := Selection[j]; - if CurSelected is TControl then begin - if CurSelected = Candidate then break; - if (CurSelected is TWinControl) and - (TWinControl(CurSelected) = Candidate.Parent) then - break; - if not ControlAcceptsStreamableChildComponent(Candidate, - TComponentClass(CurSelected.ClassType), FPropertyEditorHook.LookupRoot) - then - break; - end; - dec(j); - end; - if j < 0 then - Result.Add(Candidate); - end; - Result.Add(FPropertyEditorHook.LookupRoot); -end; - procedure TObjectInspectorDlg.OnMainPopupMenuPopup(Sender: TObject); const PropertyEditorMIPrefix = 'PropertyEditorVerbMenuItem'; @@ -5482,27 +5564,6 @@ var MainPopupMenu.Items.Insert(ZItem.MenuIndex + 1, Item); end; - function AddChangeParentMenuItems: Boolean; - var - Item: TMenuItem; - Candidates: TFPList; - i: Integer; - begin - Candidates := GetChangeParentCandidates; - try - Result := Candidates.Count>0; - ChangeParentPopupmenuItem.Clear; - for i := 0 to Candidates.Count-1 do - begin - Item := NewItem(TWinControl(Candidates[i]).Name, 0, False, True, - @DoChangeParentItemClick, 0, ''); - ChangeParentPopupmenuItem.Add(Item); - end; - finally - Candidates.Free; - end; - end; - var b, AtLeastOneComp, CanChangeClass, HasParentCandidates: Boolean; CurRow: TOIPropertyGridRow; @@ -5540,9 +5601,9 @@ begin // add Z-Order menu if (Selection.Count = 1) and (Selection[0] is TControl) then AddZOrderMenuItems; - // add Change Parent menu + // check existing of Change Parent candidates if AtLeastOneComp then - HasParentCandidates := AddChangeParentMenuItems; + HasParentCandidates := GetHasParentCandidates; end; CutPopupMenuItem.Visible := AtLeastOneComp; CopyPopupMenuItem.Visible := AtLeastOneComp; @@ -5606,45 +5667,9 @@ begin end; procedure TObjectInspectorDlg.DoChangeParentItemClick(Sender: TObject); -var - i: Integer; - Control: TControl; - NewParent: TPersistent; - NewSelection: TPersistentSelectionList; begin - if not (Sender is TMenuItem) or (Selection.Count < 1) then Exit; - if TMenuItem(Sender).Caption = TWinControl(FPropertyEditorHook.LookupRoot).Name then - NewParent := FPropertyEditorHook.LookupRoot - else - NewParent := TWinControl(FPropertyEditorHook.LookupRoot).FindComponent(TMenuItem(Sender).Caption); - - if not (NewParent is TWinControl) then Exit; - - for i := 0 to Selection.Count-1 do - begin - if not (Selection[i] is TControl) then Continue; - Control := TControl(Selection[i]); - if Control.Parent = nil then Continue; - Control.Parent := TWinControl(NewParent); - end; - - // Following code taken from DoZOrderItemClick(); - // Ensure the order of controls in the OI now reflects the new ZOrder - NewSelection := TPersistentSelectionList.Create; - try - NewSelection.ForceUpdate:=True; - NewSelection.Add(Control.Parent); - SetSelection(NewSelection); - - NewSelection.Clear; - NewSelection.ForceUpdate:=True; - NewSelection.Add(Control); - SetSelection(NewSelection); - finally - NewSelection.Free; - end; - - DoModified(Self); + if Selection.Count > 0 then + ChangeParent; end; procedure TObjectInspectorDlg.DoComponentEditorVerbMenuItemClick(Sender: TObject); diff --git a/components/ideintf/objinspstrconsts.pas b/components/ideintf/objinspstrconsts.pas index 8f1078338a..09f97b6bdc 100644 --- a/components/ideintf/objinspstrconsts.pas +++ b/components/ideintf/objinspstrconsts.pas @@ -441,6 +441,13 @@ resourcestring oisChangeParent = 'Change Parent'; lisUnableToFindParserForTool = 'Unable to find parser for tool "%s"'; + // TChangeParentDlg + oisShowClasses = 'Show classes'; + oisSelectedControl = 'Selected control'; + oisSelectedControls = 'Selected controls'; + oisCurrentParent = 'Current parent'; + oisCurrentParents = 'Current parents'; + // Dbgrid Columns editor dceAddFields = 'Add Fields'; dceDeleteAll = 'Delete All'; diff --git a/designer/designer.pp b/designer/designer.pp index aea30b5f00..b7fd8f7ec5 100644 --- a/designer/designer.pp +++ b/designer/designer.pp @@ -45,7 +45,7 @@ uses LazFileUtils, LazFileCache, // IDEIntf IDEDialogs, PropEdits, PropEditUtils, ComponentEditors, MenuIntf, IDEImagesIntf, - FormEditingIntf, ComponentReg, IDECommands, LazIDEIntf, ProjectIntf, + FormEditingIntf, ComponentReg, IDECommands, LazIDEIntf, ProjectIntf, MainIntf, // IDE LazarusIDEStrConsts, EnvironmentOpts, EditorOptions, SourceEditor, // Designer @@ -410,7 +410,7 @@ var DesignerMenuSelectAll: TIDEMenuCommand; DesignerMenuChangeClass: TIDEMenuCommand; - DesignerMenuChangeParent: TIDEMenuSection; + DesignerMenuChangeParent: TIDEMenuCommand; DesignerMenuViewLFM: TIDEMenuCommand; DesignerMenuSaveAsXML: TIDEMenuCommand; DesignerMenuCenterForm: TIDEMenuCommand; @@ -600,10 +600,8 @@ begin DesignerMenuSectionMisc:=RegisterIDEMenuSection(DesignerMenuRoot,'Miscellaneous section'); DesignerMenuChangeClass:=RegisterIDEMenuCommand(DesignerMenuSectionMisc, 'Change class',lisDlgChangeClass); - DesignerMenuChangeParent:=RegisterIDEMenuSection(DesignerMenuSectionMisc, - 'Change parent'); - DesignerMenuChangeParent.ChildrenAsSubMenu:=true; - DesignerMenuChangeParent.Caption:=lisChangeParent; + DesignerMenuChangeParent:=RegisterIDEMenuCommand(DesignerMenuSectionMisc, + 'Change parent',lisChangeParent+' ...'); DesignerMenuViewLFM:=RegisterIDEMenuCommand(DesignerMenuSectionMisc, 'View LFM',lisViewSourceLfm); DesignerMenuSaveAsXML:=RegisterIDEMenuCommand(DesignerMenuSectionMisc, @@ -3264,43 +3262,11 @@ begin end; procedure TDesigner.OnChangeParentMenuClick(Sender: TObject); -var - Item: TIDEMenuCommand; - NewParentName: String; - i: Integer; - CurControl: TControl; - NewParent: TWinControl; begin - if not (Sender is TIDEMenuCommand) then Exit; - Item := TIDEMenuCommand(Sender); - NewParentName := Item.Caption; - if SysUtils.CompareText(LookupRoot.Name, NewParentName) = 0 then - NewParent := TWinControl(LookupRoot) - else - NewParent := TWinControl(LookupRoot.FindComponent(NewParentName)); - if (NewParent=nil) or (not (NewParent is TWinControl)) then Exit; - - Form.DisableAlign; - try - i := ControlSelection.Count - 1; - while (i >= 0) do - begin - if i < ControlSelection.Count then - begin - if ControlSelection[i].IsTControl then - begin - CurControl := TControl(ControlSelection[i].Persistent); - if CurControl.Owner = LookupRoot then - CurControl.Parent := NewParent; - end; - end; - dec(i); - end; - finally - if Form <> nil then - Form.EnableAlign; - ControlSelection.DoChange(True); // request updates since control hierarchi change - end; + Assert(ObjectInspector1.PropertyEditorHook.LookupRoot = LookupRoot, + 'TDesigner.OnChangeParentMenuClick: LookupRoot mismatch.'); + if Assigned(ObjectInspector1) then + ObjectInspector1.ChangeParent; end; procedure TDesigner.OnSnapToGridOptionMenuClick(Sender: TObject); @@ -3886,6 +3852,7 @@ begin DesignerMenuSelectAll.OnClick:=@OnSelectAllMenuClick; DesignerMenuChangeClass.OnClick:=@OnChangeClassMenuClick; + DesignerMenuChangeParent.OnClick:=@OnChangeParentMenuClick; DesignerMenuViewLFM.OnClick:=@OnViewLFMMenuClick; DesignerMenuSaveAsXML.OnClick:=@OnSaveAsXMLMenuClick; DesignerMenuCenterForm.OnClick:=@OnCenterFormMenuClick; @@ -3908,84 +3875,6 @@ var SelectionVisible: Boolean; SrcFile: TLazProjectFile; UnitIsVirtual, DesignerCanCopy: Boolean; - - function GetChangeParentCandidates: TFPList; - var - i,j: Integer; - CurSelected: TSelectedControl; - Candidate: TWinControl; - begin - Result:=TFPList.Create; - if ControlSelection.Count=0 then exit; - if LookupRootIsSelected then - exit; // if the LookupRoot is selected, do not show "change parent" - if not (LookupRoot is TWinControl) then - exit; // only LCL controls are supported at the moment - - // check if any selected control can be moved - i:=ControlSelection.Count-1; - while i>=0 do - begin - CurSelected:=ControlSelection[i]; - if CurSelected.IsTControl - and (TControl(CurSelected.Persistent).Owner=LookupRoot) - then - // this one can be moved - break; - dec(i); - end; - if i<0 then exit; - - // find possible new parents - for i := 0 to LookupRoot.ComponentCount - 1 do - begin - Candidate:=TWinControl(LookupRoot.Components[i]); - if not (Candidate is TWinControl) then continue; - - j:=ControlSelection.Count-1; - while j>=0 do - begin - CurSelected:=ControlSelection[j]; - if CurSelected.IsTControl then begin - if CurSelected.Persistent=Candidate then break; - if CurSelected.IsTWinControl and - TWinControl(CurSelected.Persistent).IsParentOf(Candidate) then - break; - if not ControlAcceptsStreamableChildComponent(Candidate, - TComponentClass(CurSelected.ClassType),LookupRoot) - then - break; - end; - dec(j); - end; - if j<0 then - Result.Add(Candidate); - end; - Result.Add(LookupRoot); - end; - - procedure UpdateChangeParentMenu; - var - Candidates: TFPList; - i: Integer; - Item: TIDEMenuItem; - begin - Candidates:=GetChangeParentCandidates; - try - DesignerMenuChangeParent.Visible:=Candidates.Count>0; - DesignerMenuChangeParent.Clear; - for i:=0 to Candidates.Count-1 do - begin - Item:=TIDEMenuCommand.Create(DesignerMenuChangeParent.Name+'_'+IntToStr(i)); - DesignerMenuChangeParent.AddLast(Item); - Item.Caption:=TWinControl(Candidates[i]).Name; - Item.OnClick:=@OnChangeParentMenuClick; - end; - finally - Candidates.Free; - end; - end; - begin SrcFile:=LazarusIDE.GetProjectFileWithDesigner(Self); ControlSelIsNotEmpty:=(ControlSelection.Count>0) @@ -4025,8 +3914,8 @@ begin DesignerMenuChangeClass.Enabled := CompsAreSelected and (ControlSelection.Count = 1); // Disable ViewLFM menu item for virtual units. There is no form file yet. DesignerMenuViewLFM.Enabled := not UnitIsVirtual; - UpdateChangeParentMenu; - + DesignerMenuChangeParent.Enabled := Assigned(ObjectInspector1) + and ObjectInspector1.GetHasParentCandidates; DesignerMenuSnapToGridOption.Checked := EnvironmentOptions.SnapToGrid; DesignerMenuSnapToGuideLinesOption.Checked := EnvironmentOptions.SnapToGuideLines; end;