Designer, IdeIntf: Improve "Change Parent" feature, use a dialog. Issue #29868, patch from FTurtle.

git-svn-id: trunk@52193 -
This commit is contained in:
juha 2016-04-14 10:02:14 +00:00
parent 2355da2c35
commit 47e2727bb5
5 changed files with 174 additions and 245 deletions

View File

@ -20,7 +20,7 @@
<Description Value="IDEIntf - the interface units for the Lazarus IDE"/>
<License Value="Modified LPGL2"/>
<Version Major="1"/>
<Files Count="78">
<Files Count="80">
<Item1>
<Filename Value="actionseditor.pas"/>
<UnitName Value="ActionsEditor"/>
@ -334,6 +334,14 @@
<Filename Value="toolbarintf.pas"/>
<UnitName Value="ToolBarIntf"/>
</Item78>
<Item79>
<Filename Value="changeparentdlg.pas"/>
<UnitName Value="ChangeParentDlg"/>
</Item79>
<Item80>
<Filename Value="changeparentdlg.lfm"/>
<Type Value="LFM"/>
</Item80>
</Files>
<LazDoc Paths="docs"/>
<i18n>

View File

@ -21,7 +21,7 @@ uses
PackageIntf, ProjectIntf, ProjectResourcesIntf, PropEdits, PropEditUtils,
SrcEditorIntf, StatusBarPropEdit, StringsPropEditDlg, TextTools,
TreeViewPropEdit, UnitResources, ProjPackIntf, DBGridColumnsPropEditForm,
ToolBarIntf, LazarusPackageIntf;
ToolBarIntf, ChangeParentDlg, LazarusPackageIntf;
implementation

View File

@ -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);

View File

@ -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';

View File

@ -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;