mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-09 08:29:06 +02:00
Designer, Menu Editor: Implement drag'n'drop for moving items. Issue #8733, patch from Mike Thompson.
git-svn-id: trunk@46280 -
This commit is contained in:
parent
aee3f9b771
commit
a23a0688fb
@ -148,6 +148,9 @@ type
|
||||
procedure MenuItemMouseDown(Sender: TObject; Button: TMouseButton;
|
||||
Shift: TShiftState; X, Y: Integer);
|
||||
procedure MenuItemDblClick(Sender: TObject);
|
||||
procedure MenuItemDragDrop(Sender, Source: TObject; X, Y: Integer);
|
||||
procedure MenuItemDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState;
|
||||
var Accept: Boolean);
|
||||
procedure AddNewItemBeforeClick(Sender: TObject);
|
||||
procedure AddNewItemAfterClick(Sender: TObject);
|
||||
procedure AddSubMenuClick(Sender: TObject);
|
||||
@ -169,6 +172,7 @@ type
|
||||
function MoveDown(DMenuItem: TDesignerMenuItem; Ident: string): Integer;
|
||||
function DeleteItem(DMenuItem: TDesignerMenuItem): Integer;
|
||||
function ChangeCaption(DMenuItem: TDesignerMenuItem; const newcaption: string): Integer;
|
||||
function MoveToNewLocation(DMenuItem, DDestMenuItem: TDesignerMenuItem; TheAction: Integer): Boolean;
|
||||
procedure InsertFromTemplate(Item,Ident: string);
|
||||
procedure SaveAsTemplate(Item,Ident: string);
|
||||
procedure ReplaceInTemplate(old_Item, new_Item: string);
|
||||
@ -264,7 +268,7 @@ begin
|
||||
end;
|
||||
|
||||
//
|
||||
constructor TDesignerMainMenu.CreateWithMenu(AOwner: TComponent; aMenu: TMenu);
|
||||
constructor TDesignerMainMenu.CreateWithMenu(AOwner: TComponent; AMenu: TMenu);
|
||||
var
|
||||
PopupMenuItem: TMenuItem;
|
||||
begin
|
||||
@ -393,6 +397,8 @@ begin
|
||||
DMenuItem.SelfPanel.Caption:='';
|
||||
DMenuItem.SelfPanel.Height:=DESIGNER_MENU_ITEM_HEIGHT;
|
||||
DMenuItem.SelfPanel.OnMouseDown:=@MenuItemMouseDown;
|
||||
DMenuItem.SelfPanel.OnDragOver:=@MenuItemDragOver;
|
||||
DMenuItem.SelfPanel.OnDragDrop:=@MenuItemDragDrop;
|
||||
DMenuItem.SelfPanel.OnDblClick:=@MenuItemDblClick;
|
||||
DMenuItem.SelfPanel.PopupMenu := FDesignerPopupMenu;
|
||||
|
||||
@ -403,6 +409,8 @@ begin
|
||||
DMenuItem.CaptionLabel.Top:=2;
|
||||
DMenuItem.CaptionLabel.Height:=DESIGNER_MENU_ITEM_HEIGHT - 4;
|
||||
DMenuItem.CaptionLabel.OnMouseDown:=@MenuItemMouseDown;
|
||||
DMenuItem.CaptionLabel.OnDragOver:=@MenuItemDragOver;
|
||||
DMenuItem.CaptionLabel.OnDragDrop:=@MenuItemDragDrop;
|
||||
DMenuItem.CaptionLabel.OnDblClick:=@MenuItemDblClick;
|
||||
|
||||
DMenuItem.SubMenuArrow:=TArrow.Create(self);
|
||||
@ -414,6 +422,8 @@ begin
|
||||
DMenuItem.SubMenuArrow.ShadowType:=stout;
|
||||
DMenuItem.SubMenuArrow.Visible:=false;
|
||||
DMenuItem.SubMenuArrow.OnMouseDown:=@MenuItemMouseDown;
|
||||
DMenuItem.SubMenuArrow.OnDragOver:=@MenuItemDragOver;
|
||||
DMenuItem.SubMenuArrow.OnDragDrop:=@MenuItemDragDrop;
|
||||
DMenuItem.SubMenuArrow.OnDblClick:=@MenuItemDblClick;
|
||||
|
||||
DesignerMenuItemIdent:=DesignerMenuItemIdent + 1;
|
||||
@ -818,6 +828,9 @@ begin
|
||||
UpdateMenu(fMenu.Items, GetDesignerMenuItem(Root, SelectedDesignerMenuItem), 1, 9);
|
||||
|
||||
RealignDesigner;
|
||||
|
||||
If Button=mbLeft Then
|
||||
TWinControl(Sender).BeginDrag(False, 1);
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -826,6 +839,327 @@ begin
|
||||
HandleOnClickEventClick(Sender);
|
||||
end;
|
||||
|
||||
procedure TDesignerMainMenu.MenuItemDragDrop(Sender, Source: TObject; X, Y: Integer);
|
||||
|
||||
function InnerIsChildOf(AMenu, AChild: TDesignerMenuItem): Boolean;
|
||||
var
|
||||
MenuItem: TDesignerMenuItem;
|
||||
begin
|
||||
result := false;
|
||||
MenuItem := AMenu;
|
||||
|
||||
while assigned(MenuItem) and not result do
|
||||
begin
|
||||
result := (MenuItem=AChild);
|
||||
|
||||
if not result then
|
||||
begin
|
||||
if assigned(MenuItem.SubMenu) then
|
||||
result := InnerIsChildOf(MenuItem.SubMenu, AChild);
|
||||
|
||||
if not result then
|
||||
MenuItem := MenuItem.NextItem;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function IsChildOf(AMenu, AChild: TDesignerMenuItem): Boolean;
|
||||
begin
|
||||
result := false;
|
||||
if assigned(AMenu.SubMenu) then
|
||||
begin
|
||||
result := (AMenu.SubMenu=AChild);
|
||||
|
||||
if not result then
|
||||
result := InnerIsChildOf(AMenu.SubMenu, AChild);
|
||||
end;
|
||||
end;
|
||||
|
||||
var
|
||||
DestDesignerItem: TDesignerMenuItem;
|
||||
SelectedDesignerItem: TDesignerMenuItem;
|
||||
bMoved: Boolean;
|
||||
begin
|
||||
SelectedDesignerItem := GetDesignerMenuItem(Root, SelectedDesignerMenuItem);
|
||||
if not assigned(SelectedDesignerItem) then
|
||||
exit;
|
||||
|
||||
if (Sender is TPanel) then
|
||||
DestDesignerItem := SearchItemByPanel(Root, TPanel(Sender))
|
||||
else
|
||||
if (Sender is TLabel) then
|
||||
DestDesignerItem := SearchItemByPanel(Root, TPanel(TLabel(Sender).Parent))
|
||||
else
|
||||
if (Sender is TArrow) then
|
||||
DestDesignerItem := SearchItemByPanel(Root, TPanel(TArrow(Sender).Parent))
|
||||
else
|
||||
DestDesignerItem := nil;
|
||||
|
||||
if not assigned(DestDesignerItem) then
|
||||
exit;
|
||||
|
||||
if IsChildOf(SelectedDesignerItem, DestDesignerItem) then
|
||||
exit;
|
||||
|
||||
bMoved := false;
|
||||
if (Sender is TArrow) then
|
||||
bMoved := MoveToNewLocation(SelectedDesignerItem, DestDesignerItem, 2)
|
||||
else if (DestDesignerItem.Level=1) and (fMenu is TMainMenu) then
|
||||
begin
|
||||
// we're moving the menu sideways amoung the root menu items
|
||||
if (x<10) then
|
||||
bMoved := MoveToNewLocation(SelectedDesignerItem, DestDesignerItem, 0)
|
||||
else if (x>TWinControl(Sender).Width-10) then
|
||||
bMoved := MoveToNewLocation(SelectedDesignerItem, DestDesignerItem, 1)
|
||||
else if (DestDesignerItem.SubMenu<>SelectedDesignerItem) then
|
||||
bMoved := MoveToNewLocation(SelectedDesignerItem, DestDesignerItem, 2);
|
||||
end
|
||||
else
|
||||
begin
|
||||
// We're moving the menu up or down
|
||||
If (y<5) then
|
||||
bMoved := MoveToNewLocation(SelectedDesignerItem, DestDesignerItem, 0)
|
||||
else if (y>TWinControl(Sender).Height-5) then
|
||||
bMoved := MoveToNewLocation(SelectedDesignerItem, DestDesignerItem, 1)
|
||||
else if (DestDesignerItem.SubMenu<>SelectedDesignerItem) then
|
||||
bMoved := MoveToNewLocation(SelectedDesignerItem, DestDesignerItem, 2);
|
||||
end;
|
||||
|
||||
if bMoved then
|
||||
begin
|
||||
SetCoordinates(POSITION_LEFT, POSITION_TOP, 0, Root);
|
||||
|
||||
// destroy all existing panels
|
||||
ChangeMenuItem(Root, 2, Root.ID);
|
||||
|
||||
// rebuild internal data state
|
||||
InitIndexSequence;
|
||||
SelectedDesignerMenuItem := SelectedDesignerItem.ID;
|
||||
ChangeMenuItem(Root, 1, SelectedDesignerMenuItem);
|
||||
CreateIndexSequence(Root, SelectedDesignerMenuItem, 1);
|
||||
|
||||
// Ensure the OI is updated and SelectedDesignerItem selected
|
||||
// (seems to be only possible by toggling selected)
|
||||
GetDesigner.SelectOnlyThisComponent(DestDesignerItem.RealMenuItem);
|
||||
GetDesigner.SelectOnlyThisComponent(SelectedDesignerItem.RealMenuItem);
|
||||
|
||||
RealignDesigner;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TDesignerMainMenu.MoveToNewLocation(DMenuItem, DDestMenuItem: TDesignerMenuItem; TheAction: Integer): Boolean;
|
||||
|
||||
function FindParentDesignerMenuItem(DSubMenuItem: TDesignerMenuItem): TDesignerMenuItem;
|
||||
var
|
||||
TempMI: TDesignerMenuItem;
|
||||
begin
|
||||
if DSubMenuItem.Level=1 then
|
||||
result := nil
|
||||
else
|
||||
begin
|
||||
// find the first menu in this submenu
|
||||
TempMI := DSubMenuItem;
|
||||
while assigned(TempMI.PrevItem) do
|
||||
TempMI := TempMI.PrevItem;
|
||||
|
||||
if assigned(TempMI.ParentMenu) then
|
||||
result := TempMI.ParentMenu
|
||||
else
|
||||
result := nil;
|
||||
end;
|
||||
end;
|
||||
|
||||
// Find the whole submenu that ATemp belongs to and recalculate the .indexs
|
||||
procedure RecalcIndexes(DSubMenuItem: TDesignerMenuItem);
|
||||
var
|
||||
i : Integer;
|
||||
TempMI: TDesignerMenuItem;
|
||||
s: String;
|
||||
begin
|
||||
if not assigned(DSubMenuItem) then
|
||||
exit;
|
||||
|
||||
TempMI := FindParentDesignerMenuItem(DSubMenuItem);
|
||||
If assigned(TempMI) Then
|
||||
s := TempMI.Caption
|
||||
Else
|
||||
s := 'Root';
|
||||
|
||||
if assigned(TempMI) then
|
||||
TempMI := TempMI.SubMenu
|
||||
else
|
||||
TempMI := fRoot;
|
||||
|
||||
// now reindex the submenu
|
||||
i := 0;
|
||||
while assigned(TempMI) do
|
||||
begin
|
||||
TempMI.Index:=i;
|
||||
i:=i+1;
|
||||
TempMI := TempMI.NextItem;
|
||||
end;
|
||||
end;
|
||||
|
||||
var
|
||||
TempMI: TDesignerMenuItem;
|
||||
DOrigParent: TDesignerMenuItem;
|
||||
DOrigIndex: Integer;
|
||||
|
||||
begin
|
||||
result := false;
|
||||
if (not assigned(DDestMenuItem)) or (not assigned(DMenuItem)) then
|
||||
exit;
|
||||
|
||||
if not TheAction in [0..2] then
|
||||
exit;
|
||||
|
||||
DOrigParent := FindParentDesignerMenuItem(DMenuItem);
|
||||
DOrigIndex := DMenuItem.Index;
|
||||
|
||||
// First: unlink DMenuItem from it's current position
|
||||
|
||||
// Was this the root node?
|
||||
if not assigned(DMenuItem.ParentMenu) and not assigned(DMenuItem.PrevItem) then
|
||||
begin
|
||||
fRoot := DMenuItem.NextItem;
|
||||
|
||||
if assigned(fRoot) then
|
||||
begin
|
||||
fRoot.PrevItem := nil;
|
||||
DMenuItem.NextItem := nil;
|
||||
RecalcIndexes(fRoot);
|
||||
end;
|
||||
end;
|
||||
|
||||
// is this the first node in a submenu?
|
||||
if assigned(DMenuItem.ParentMenu) then
|
||||
begin
|
||||
TempMI := DMenuItem.NextItem;
|
||||
|
||||
if assigned(TempMI) then
|
||||
begin
|
||||
TempMI.ParentMenu := DMenuItem.ParentMenu;
|
||||
TempMI.ParentMenu.SubMenu := TempMI;
|
||||
|
||||
RecalcIndexes(TempMI);
|
||||
end
|
||||
else
|
||||
begin
|
||||
DMenuItem.ParentMenu.SubMenu := nil;
|
||||
DMenuItem.ParentMenu.SubMenuPanel.Visible := false;
|
||||
end;
|
||||
|
||||
DMenuItem.ParentMenu := nil;
|
||||
end;
|
||||
|
||||
// unlink from siblings
|
||||
if assigned(DMenuItem.PrevItem) or assigned(DMenuItem.NextItem) then
|
||||
begin
|
||||
TempMI := nil;
|
||||
if assigned(DMenuItem.PrevItem) then
|
||||
begin
|
||||
TempMI := DMenuItem.PrevItem;
|
||||
TempMI.NextItem := DMenuItem.NextItem;
|
||||
end;
|
||||
|
||||
if assigned(DMenuItem.NextItem) then
|
||||
begin
|
||||
DMenuItem.NextItem.PrevItem := TempMI;
|
||||
TempMI := DMenuItem.NextItem;
|
||||
end;
|
||||
|
||||
DMenuItem.NextItem := nil;
|
||||
DMenuItem.PrevItem := nil;
|
||||
|
||||
RecalcIndexes(TempMI);
|
||||
end;
|
||||
|
||||
// Now that DMenuItem is unlinked, it needs to be linked into new position
|
||||
case TheAction of
|
||||
0: // In front of DDestMenuItem.
|
||||
begin
|
||||
if assigned(DDestMenuItem.ParentMenu) then
|
||||
begin
|
||||
DMenuItem.ParentMenu := DDestMenuItem.ParentMenu;
|
||||
DMenuItem.ParentMenu.SubMenu := DMenuItem;
|
||||
DDestMenuItem.ParentMenu := nil;
|
||||
end;
|
||||
|
||||
if assigned(DDestMenuItem.PrevItem) then
|
||||
begin
|
||||
TempMI := DDestMenuItem.PrevItem;
|
||||
TempMI.NextItem := DMenuItem;
|
||||
DMenuItem.PrevItem := TempMI;
|
||||
end;
|
||||
|
||||
DDestMenuItem.PrevItem := DMenuItem;
|
||||
DMenuItem.NextItem := DDestMenuItem;
|
||||
DMenuItem.Level := DDestMenuItem.Level;
|
||||
|
||||
if not assigned(DMenuItem.ParentMenu) and not assigned(DMenuItem.PrevItem) then
|
||||
fRoot := DMenuItem;
|
||||
|
||||
RecalcIndexes(DMenuItem);
|
||||
result := true;
|
||||
end;
|
||||
1: // Behind DDestMenuItem
|
||||
begin
|
||||
if assigned(DDestMenuItem.NextItem) then
|
||||
begin
|
||||
TempMI := DDestMenuItem.NextItem;
|
||||
TempMI.PrevItem := DMenuItem;
|
||||
DMenuItem.NextItem := TempMI;
|
||||
end;
|
||||
|
||||
DDestMenuItem.NextItem := DMenuItem;
|
||||
DMenuItem.PrevItem := DDestMenuItem;
|
||||
DMenuItem.Level := DDestMenuItem.Level;
|
||||
|
||||
RecalcIndexes(DDestMenuItem);
|
||||
result := true;
|
||||
end;
|
||||
2: // Insert as first submenu item of DDestMenuItem
|
||||
begin
|
||||
if assigned(DDestMenuItem.SubMenu) then
|
||||
begin
|
||||
TempMI := DDestMenuItem.SubMenu;
|
||||
TempMI.ParentMenu := nil;
|
||||
TempMI.PrevItem := DMenuItem;
|
||||
DMenuItem.NextItem := TempMI;
|
||||
end;
|
||||
|
||||
DDestMenuItem.SubMenu := DMenuItem;
|
||||
DMenuItem.ParentMenu := DDestMenuItem;
|
||||
DMenuItem.Level := DDestMenuItem.Level + 1;
|
||||
|
||||
RecalcIndexes(DMenuItem);
|
||||
result := true;
|
||||
end;
|
||||
end;
|
||||
|
||||
if result then
|
||||
begin
|
||||
// unlink the RealMenuItem
|
||||
if assigned(DOrigParent) then
|
||||
DOrigParent.RealMenuItem.Delete(DOrigIndex)
|
||||
else
|
||||
fMenu.Items.Delete(DOrigIndex);
|
||||
|
||||
// relink the RealMenuItem
|
||||
TempMI := FindParentDesignerMenuItem(DMenuItem);
|
||||
if assigned(TempMI) then
|
||||
TempMI.RealMenuItem.Insert(DMenuItem.Index, DMenuItem.RealMenuItem)
|
||||
else
|
||||
fMenu.Items.Insert(DMenuItem.Index, DMenuItem.RealMenuItem);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TDesignerMainMenu.MenuItemDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState;
|
||||
var Accept: Boolean);
|
||||
begin
|
||||
Accept := (Sender<>Source) and ((Source is TPanel) or (Source is TLabel) or (Source is TArrow));
|
||||
end;
|
||||
|
||||
// -------------------------------------------------------------//
|
||||
// New Item (before) has been selected from context menu -------//
|
||||
// -------------------------------------------------------------//
|
||||
@ -898,6 +1232,8 @@ end;
|
||||
// Move Up has been selected from context menu --------//
|
||||
// ----------------------------------------------------//
|
||||
procedure TDesignerMainMenu.MoveUpClick(Sender: TObject);
|
||||
var
|
||||
TempMI : TDesignerMenuItem;
|
||||
begin
|
||||
if (MoveUp(Root, SelectedDesignerMenuItem) > 0) then
|
||||
begin
|
||||
@ -906,7 +1242,12 @@ begin
|
||||
InitIndexSequence;
|
||||
CreateIndexSequence(Root, SelectedDesignerMenuItem, 1);
|
||||
|
||||
UpdateMenu(fMenu.Items, GetDesignerMenuItem(Root, SelectedDesignerMenuItem), 1, 4);
|
||||
TempMI := GetDesignerMenuItem(Root, SelectedDesignerMenuItem);
|
||||
UpdateMenu(fMenu.Items, TempMI, 1, 4);
|
||||
|
||||
// Ensure OI is updated and correct item select
|
||||
GetDesigner.SelectOnlyThisComponent(TempMI.NextItem.RealMenuItem);
|
||||
GetDesigner.SelectOnlyThisComponent(TempMI.RealMenuItem);
|
||||
|
||||
RealignDesigner;
|
||||
end;
|
||||
@ -1464,6 +1805,8 @@ end;
|
||||
// Move Down has been selected from context menu --------//
|
||||
// ------------------------------------------------------//
|
||||
procedure TDesignerMainMenu.MoveDownClick(Sender: TObject);
|
||||
var
|
||||
TempMI: TDesignerMenuItem;
|
||||
begin
|
||||
if (MoveDown(Root, SelectedDesignerMenuItem) > 0) then
|
||||
begin
|
||||
@ -1472,7 +1815,12 @@ begin
|
||||
InitIndexSequence;
|
||||
CreateIndexSequence(Root, SelectedDesignerMenuItem,1);
|
||||
|
||||
UpdateMenu(fMenu.Items, GetDesignerMenuItem(Root, SelectedDesignerMenuItem), 1, 5);
|
||||
TempMI := GetDesignerMenuItem(Root, SelectedDesignerMenuItem);
|
||||
UpdateMenu(fMenu.Items, TempMI, 1, 5);
|
||||
|
||||
// Ensure OI is updated and correct item select
|
||||
GetDesigner.SelectOnlyThisComponent(TempMI.PrevItem.RealMenuItem);
|
||||
GetDesigner.SelectOnlyThisComponent(TempMI.RealMenuItem);
|
||||
|
||||
RealignDesigner;
|
||||
end;
|
||||
@ -1872,7 +2220,7 @@ begin
|
||||
index_sequence[i]:=-1;
|
||||
end;
|
||||
|
||||
function TDEsignerMainMenu.CreateIndexSequence(DMenuItem: TDesignerMenuItem;
|
||||
function TDesignerMainMenu.CreateIndexSequence(DMenuItem: TDesignerMenuItem;
|
||||
Ident: string; Ind: Integer): Boolean;
|
||||
begin
|
||||
Result:=false;
|
||||
|
Loading…
Reference in New Issue
Block a user