{***************************************************************************
 *                                                                         *
 *   This source is free software; you can redistribute it and/or modify   *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This code is distributed in the hope that it will be useful, but      *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 *   General Public License for more details.                              *
 *                                                                         *
 *   A copy of the GNU General Public License is available on the World    *
 *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
 *   obtain it by writing to the Free Software Foundation,                 *
 *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
 *                                                                         *
 ***************************************************************************

  Author: Howard Page-Clark, Juha Manninen and other contributors }

unit MenuEditor;

{$mode objfpc}{$H+}

interface

uses
  // FCL
  Classes, SysUtils, Types, TypInfo, StrUtils,
  // LCL
  ActnList, Controls, Dialogs, StdCtrls, ExtCtrls, Menus,
  Forms, Graphics, ImgList, Themes, LCLType, LCLIntf, LCLProc,
  // LazUtils
  LazLoggerBase, LazTracer,
  // IdeIntf
  FormEditingIntf, IDEWindowIntf, IDEImagesIntf, ComponentEditors, IDEDialogs,
  IdeIntfStrConsts, PropEdits,
  // IDE
  LazarusIDEStrConsts, LazIDEIntf, MenuDesignerBase, MenuEditorForm, MenuShortcutDisplay,
  MenuTemplates, MenuResolveConflicts;

type

  TShadowMenu = class;
  TShadowBox = class;

  { TFake }

  TFake = class(TCustomControl)
  private
    FShadowMenu: TShadowMenu;
    FMinWidth: integer;
  protected
    function GetShouldBeVisible: boolean; virtual; abstract;
    procedure SetVisibilitySizeAndPosition; virtual; abstract;
    procedure TextChanged; override;
    procedure Paint; override;
    class function GetControlClassDefaultSize: TSize; override;
  public
    constructor Create(anOwner: TShadowMenu); reintroduce;
    procedure Refresh;
    property ShouldBeVisible: boolean read GetShouldBeVisible;
  end;

  TAddSiblingFake = class(TFake)
  protected
    function GetShouldBeVisible: boolean; override;
    procedure SetVisibilitySizeAndPosition; override;
  end;

  TAddSubmenuFake = class(TFake)
  protected
    function GetShouldBeVisible: boolean; override;
    procedure SetVisibilitySizeAndPosition; override;
  end;

  TAddFirstFake = class(TFake)
  protected
    function GetShouldBeVisible: boolean; override;
    procedure SetVisibilitySizeAndPosition; override;
  end;

  TMenuDesigner = class;

  { TShadowItem }

  TShadowItem = class(TShadowItemBase)
  strict private
    FBottomFake: TFake;
    FParentBox: TShadowBox;
    FRightFake: TFake;
    FShadowMenu: TShadowMenu;
    FShowingBottomFake: boolean;
    FShowingRightFake: boolean;
    function GetBitmapLeftTop: TPoint;
    function GetBottomFake: TFake;
    function GetIconTopLeft: TPoint;
    function GetIsInMenuBar: boolean;
    function GetIsMainMenu: boolean;
    function GetLevel: integer;
    function GetRightFake: TFake;
    function GetShortcutWidth: integer;
    function GetShowingBottomFake: boolean;
    function GetShowingRightFake: boolean;
    function GetSubImagesIconTopLeft: TPoint;
    procedure RecursiveHideChildren(aMI: TMenuItem);
  private
    function HasChildBox(out aChildBox: TShadowBoxBase): boolean;
    procedure HideChainFromRoot;
    procedure HideChildren;
    procedure ShowChainToRoot;
    procedure ShowChildBox;
  protected
    procedure DblClick; override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure Paint; override;
  public
    constructor CreateWithBoxAndItem(aSMenu: TShadowMenu; aParentBox: TShadowBox;
      aRealItem: TMenuItem);
    function GetWidth: integer; override;
    procedure Invalidate; override;
  public
    property BottomFake: TFake read GetBottomFake write FBottomFake;
    property IsInMenuBar: boolean read GetIsInMenuBar;
    property IsMainMenu: boolean read GetIsMainMenu;
    property Level: integer read GetLevel;
    property ParentBox: TShadowBox read FParentBox;
    property RightFake: TFake read GetRightFake write FRightFake;
    property ShowingBottomFake: boolean read GetShowingBottomFake write FShowingBottomFake;
    property ShowingRightFake: boolean read GetShowingRightFake write FShowingRightFake;
  end;

  { TShadowBox }

  TShadowBox = class(TShadowBoxBase)
  strict private
    FShadowMenu: TShadowMenu;
    FUpdating: boolean;
    procedure BeginUpdate;
    procedure EndUpdate;
    procedure ShowAllUnSelected;
  private
    procedure AddItemAndShadow(existingSI: TShadowItem; addBefore: boolean;
      isSeparator: boolean=False);
    procedure LocateShadows;
    procedure RemoveAllSeparators;
    procedure SelectPrevious(aSI: TShadowItem);
    procedure SelectSuccessor(aSI: TShadowItem);
    property Updating: boolean read FUpdating;
  protected
    function GetIsMainMenu: boolean; override;
    function GetIsMenuBar: boolean; override;
    procedure Paint; override;
  public
    constructor CreateWithParentBox(aSMenu: TShadowMenu; aParentBox: TShadowBox;
      aParentItem: TMenuItem);
    procedure SetUnCheckedAllExcept(aMI: TMenuItem);
  end;

  TPopEnum = {%region}
    (popItemMoveBefore, popItemMoveAfter,
     popSeparators_,
       popAddSeparatorBefore, popAddSeparatorAfter, popRemoveAllSeparators,
     popItemDelete, popItemAddBefore, popItemAddAfter, popItemAddSubMenu,
     popItemSep,
     popAddImgListIcon, popItemAddOnClick,
     popItemOISep,
     popShortcuts_,
       popListShortcuts, popListShortcutsAccelerators, popResolveShortcutConflicts,
     popTemplates_,
       popSaveAsTemplate, popAddFromTemplate, popDeleteTemplate);{%endregion}

  { TShadowMenu }

  TShadowMenu = class(TShadowMenuBase)
  strict private
    FActionList: TActionList;
    FAddImgListIconAction: TAction;
    FAddItemFake: TFake;
    FAddFirstItemFake: TFake;
    FAddSubmenuFake: TFake;
    FInitialising: boolean;
    FInitialSelectedMenuItem: TMenuItem;
    FItemsPopupMenu: TPopupMenu;
    FRootBox: TShadowBox;
    procedure DeleteBox(aMI: TMenuItem);
    procedure DeleteItm(anItem: TMenuItem);
    function GetActionForEnum(anEnum: TPopEnum): TAction;
    function GetMaxVisibleBoxDims(aSB: TShadowBox): TPoint;
    function GetMaxVisibleFakeDims: TPoint;
    function GetMenuBarCumWidthForItemIndex(anIndex: integer): integer;
    function GetParentItemHeightInBox(aParentItem: TMenuItem): integer;
    function GetSelectedShadowBox: TShadowBox;
    function GetSelectedShadowItem: TShadowItem;
    procedure AddManyItems(aPrimaries, aDepth: integer);
    procedure AddSubMenuTo(anExistingSI: TShadowItem);
    procedure ConnectSpeedButtonOnClickMethods;
    procedure CreateShadowBoxesAndItems;
    procedure DeleteChildlessShadowAndItem(anExistingSI: TShadowItem);
    procedure DeleteShadowAndItemAndChildren(anExistingSI: TShadowItem);
    procedure OnDesignerModified(Sender: TObject);
    procedure OnObjectPropertyChanged(Sender: TObject; NewObject: TPersistent);
    procedure OnDesignerRefreshPropertyValues;
    procedure RecursiveCreateShadows(aParentBox: TShadowBox; aMI: TMenuItem);
    procedure SetupPopupMenu;
    procedure UpdateButtonGlyphs(isInBar: boolean);
    // user actions
    procedure AddFromTemplate(Sender: TObject);
    procedure AddImageListIcon(Sender: TObject);
    procedure AddItemAfter(Sender: TObject);
    procedure AddItemBefore(Sender: TObject);
    procedure AddSeparatorAbove(Sender: TObject);
    procedure AddSeparatorBelow(Sender: TObject);
    procedure AddSubMenu(Sender: TObject);
    procedure AddFirstMenu(Sender: TObject);
    procedure DeleteTemplate(Sender: TObject);
    procedure ListShortcuts(Sender: TObject);
    procedure ListShortcutsAndAccelerators(Sender: TObject);
    procedure MoveItemAfter(Sender: TObject);
    procedure MoveItemBefore(Sender: TObject);
    procedure RemoveAllSeparators(Sender: TObject);
    procedure ResolveShortcutConflicts(Sender: TObject);
    procedure SaveAsTemplate(Sender: TObject);
  private
    FDesigner: TMenuDesigner;
    function GetMenuBarIconWidth(aMI: TMenuItem): integer;
    function OnClickIsAssigned(aMI: TMenuItem): boolean;
    procedure AddOnClick(Sender: TObject);
    procedure DeleteItem(Sender: TObject);
    function GetBoxWithParentItem(aParentMI: TMenuItem): TShadowBoxBase;
    procedure HideFakes;
    procedure RemoveEmptyBox(aSB: TShadowBox);
    procedure SetSelectedShadow(const prevSelectedItem, curSelectedItem: TMenuItem; viaDesigner: boolean);
    procedure UpdateActionsEnabledness;
  private
    property AddItemFake: TFake read FAddItemFake;
    property AddSubmenuFake: TFake read FAddSubmenuFake;
    property ItemsPopupMenu: TPopupMenu read FItemsPopupMenu;
    property RootBox: TShadowBox read FRootBox;
  protected
    procedure Paint; override;
    procedure SetParent(NewParent: TWinControl); override;
  public
    constructor Create(aDesigner: TMenuDesigner; aForm: TForm; aMenu: TMenu;
      aSelect: TMenuItem; aWidth, aHeight: integer); reintroduce;
    destructor Destroy; override;
    procedure HideBoxesAboveLevel(aLevel: integer);
    procedure RefreshFakes; override;
    procedure SetSelectedMenuItem(aMI: TMenuItem;
      viaDesigner, prevWasDeleted: boolean); override;
    procedure UpdateBoxLocationsAndSizes; override;
    procedure UpdateSelectedItemInfo;
  public
    property SelectedShadowBox: TShadowBox read GetSelectedShadowBox;
    property SelectedShadowItem: TShadowItem read GetSelectedShadowItem;
  end;

  { TMenuDesigner }

  TMenuDesigner = class(TMenuDesignerBase)
  private
    FGui: TMenuDesignerForm;
  public
    constructor Create;
    destructor Destroy; override;
    procedure CreateShadowMenu(aMenu: TMenu; aSelect: TMenuItem;
      aWidth, aHeight: integer); override;
  end;

  { TMenuComponentEditor - the default component editor for TMenu }

  TMainMenuComponentEditor = class(TComponentEditor)
  public
    procedure Edit; override;
    function GetVerbCount: Integer; override;
    function GetVerb(Index: Integer): string; override;
    procedure ExecuteVerb(Index: Integer); override;
  end;

procedure ShowMenuEditor(aMenu: TMenu);
function MenuDesigner: TMenuDesigner;


implementation

const
  Shortcut_Offset = 23;
  Popup_Origin: TPoint = (x:15; y:15);

var
  ShadowItemID: integer = 0;
  ShadowBoxID: integer = 0;

  MenuDesignerSingleton: TMenuDesigner = nil;

procedure ShowMenuEditor(aMenu: TMenu);
begin
  Assert(Assigned(aMenu), 'ShowMenuEditor: aMenu parameter is nil');
  MenuDesigner.FGui.SetMenu(aMenu, nil);
  SetPopupModeParentForPropertyEditor(MenuDesigner.FGui);
  MenuDesigner.FGui.ShowOnTop;
end;

function MenuDesigner: TMenuDesigner; // refer always to a single instance
begin
  if (MenuDesignerSingleton = nil) then
    MenuDesignerSingleton:=TMenuDesigner.Create;
  Result:=MenuDesignerSingleton;
end;

// utility functions
{
function ItemStateToStr(aState: TShadowItemDisplayState): string;
begin
  Result:=GetEnumName(TypeInfo(TShadowItemDisplayState), Ord(aState));
end;
}
function GetPreviousNonSepItem(aMI: TMenuItem): TMenuItem;
var
  idx: integer;
begin
  Result:=nil;
  idx:=aMI.MenuIndex;
  if (idx = 0) then
    Exit
  else repeat
    idx:=Pred(idx);
    Result:=aMI.Parent.Items[idx];
  until not Result.IsLine or (idx = 0);
  if Result.IsLine then
    Result:=nil;
end;

function GetPreviousItem(aMI: TMenuItem): TMenuItem;
var
  idx: integer;
begin
  idx:=aMI.MenuIndex;
  if (idx = 0) then
    Exit(nil)
  else
    Result:=aMI.Parent.Items[Pred(idx)];
end;

function GetNextItem(aMI: TMenuItem): TMenuItem;
var
  idx: integer;
begin
  idx:=aMI.MenuIndex;
  if (idx = Pred(aMI.Parent.Count)) then
    Exit(nil)
  else
    Result:=aMI.Parent.Items[Succ(idx)];
end;

function GetNextNonSepItem(aMI: TMenuItem): TMenuItem;
var
  idx, maxIdx: integer;
begin
  Result:=nil;
  idx:=aMI.MenuIndex;
  maxIdx:=Pred(aMI.Parent.Count);
  if (idx = maxIdx) then
    Exit
  else repeat
    idx:=Succ(idx);
    Result:=aMI.Parent.Items[idx];
  until not Result.IsLine or (idx = maxIdx);
  if Result.IsLine then
    Result:=nil;
end;

function PreviousItemIsSeparator(aMI: TMenuItem): boolean;
var
  idx: integer;
begin
  if (aMI = nil) then
    Exit(False);
  idx:=aMI.MenuIndex;
  Result:=(idx > 0) and aMI.Parent.Items[Pred(idx)].IsLine;
end;

function NextItemIsSeparator(aMI: TMenuItem): boolean;
var
  idx: integer;
begin
  if (aMI = nil) then
    Exit(False);
  idx:=aMI.MenuIndex;
  Result:=(idx < Pred(aMI.Parent.Count)) and aMI.Parent.Items[Succ(idx)].IsLine;
end;

function GetChildSeparatorCount(aMI: TMenuItem): integer;
var
  i: integer;
begin
  Result:=0;
  for i:=0 to aMI.Count-1 do
    if aMI.Items[i].IsLine then
      Inc(Result);
end;

function AIsDescendantOfB(miA, miB: TMenuItem): boolean;
var
  tmp: TMenuItem;
begin
  if (miA = nil) or (miB = nil) then
    Exit(False);
  tmp:=miA.Parent;
  repeat
    if (tmp = miB) then
      Exit(True);
    tmp:=tmp.Parent;
  until (tmp = nil);
  Result:=False;
end;

function LevelZeroAndNoGrandchildren(aMI: TMenuItem): boolean;
var
  i: integer;
begin
  Result:=(aMI.Parent <> nil) and (aMI.Parent.Parent = nil);
  if Result then
    for i:=0 to aMI.Count-1 do
      if (aMI.Items[i].Count > 0) then
        Exit(False);
end;

function SortByItemMenuIndex(const Item1, Item2: TShadowItemBase): Integer;
var
  i1, i2: integer;
begin
  i1:=Item1.RealItem.MenuIndex;
  i2:=Item2.RealItem.MenuIndex;
  if (i1 > i2) then
    Result:=1
  else if (i2 > i1) then
    Result:= -1
  else
    Result:=0;
end;

function SortByBoxLevel(const Item1, Item2: TShadowBoxBase): Integer;
var
  lvl1, lvl2: integer;
begin
  lvl1:=Item1.Level;
  lvl2:=Item2.Level;
  if (lvl1 > lvl2) then
    Result:=1
  else if (lvl1 < lvl2) then
    Result:= -1
  else
    Result:=0;
end;

{ TAddFirstFake }

function TAddFirstFake.GetShouldBeVisible: boolean;
begin
  Result:=(FShadowMenu.FMenu<>nil) and (FShadowMenu.FMenu.Items.Count=0);
end;

procedure TAddFirstFake.SetVisibilitySizeAndPosition;
begin
  if ShouldBeVisible then begin
    SetBounds(Left, Top, FMinWidth, DropDown_Height);
    Show;
  end
  else begin
    Hide;
  end;
end;

{ TAddSubmenuFake }

function TAddSubmenuFake.GetShouldBeVisible: boolean;
var
  item: TMenuItem;
begin
  item:=FShadowMenu.SelectedMenuItem;
  if (item = nil) then
    Exit(False)
  else
    Result:=not item.IsLine and (item.Count = 0);
end;

procedure TAddSubmenuFake.SetVisibilitySizeAndPosition;
var
  selShadow: TShadowItem;
  selMI: TMenuItem;
  w: integer;
begin
  selMI:=FShadowMenu.SelectedMenuItem;
  if (selMI=nil) then
    Exit;
  selShadow:=TShadowItem(FShadowMenu.GetShadowForMenuItem(selMI));
  if selShadow=nil then Exit;
  if not ShouldBeVisible then begin
    if selMI.IsInMenuBar then
      selShadow.BottomFake:=nil
    else
      selShadow.RightFake:=nil;
    Hide;
  end
  else begin
    w:=FMinWidth;
    if selMI.IsInMenuBar then begin
      if (selShadow.Width > w) then
        w:=selShadow.Width;
      SetBounds(selShadow.Left, MenuBar_Height + 1, w, DropDown_Height);
      selShadow.ShowingBottomFake:=True;
      selShadow.BottomFake:=Self;
      selShadow.ShowingRightFake:=False;
    end
    else begin
      SetBounds(selShadow.ParentBox.Left + selShadow.BoundsRect.Right + 1,
                selShadow.ParentBox.Top + selShadow.Top, w, DropDown_Height);
      selShadow.ShowingRightFake:=True;
      selShadow.RightFake:=Self;
      selShadow.ShowingBottomFake:=False;
    end;
    Show;
  end;
end;

{ TAddSiblingFake }

function TAddSiblingFake.GetShouldBeVisible: boolean;
var
  item: TMenuItem;
begin
  item:=FShadowMenu.SelectedMenuItem;
  if (item = nil) then
    Exit(False)
  else
    Result:=(item.MenuIndex = Pred(item.Parent.Count));
end;

procedure TAddSiblingFake.SetVisibilitySizeAndPosition;
var
  selShadow: TShadowItem;
  selMI: TMenuItem;
  w: integer;
begin
  selMI:=FShadowMenu.SelectedMenuItem;
  if (selMI=nil) then
    Exit;
  selShadow:=TShadowItem(FShadowMenu.GetShadowForMenuItem(selMI));
  if selShadow=nil then Exit;
  if not ShouldBeVisible then begin
    if selMI.IsInMenuBar then
      selShadow.RightFake:=nil
    else
      selShadow.BottomFake:=nil;
    Hide;
  end
  else begin
    if selMI.IsInMenuBar then begin
      SetBounds(selShadow.Left + selShadow.Width + 1, 0, FMinWidth, MenuBar_Height);
      selShadow.ShowingRightFake:=True;
      selShadow.RightFake:=Self;
      selShadow.ShowingBottomFake:=False;
    end
    else begin
      w:=selShadow.ParentBox.Width - Gutter_X - 1;
      if (FMinWidth > w) then
        w:=FMinWidth;
      SetBounds(selShadow.ParentBox.Left + selShadow.Left + Gutter_X,
                selShadow.ParentBox.Top + selShadow.ParentBox.Height + 1,
                w, DropDown_Height);
      selShadow.ShowingBottomFake:=True;
      selShadow.BottomFake:=Self;
      selShadow.ShowingRightFake:=False;
    end;
    Invalidate;
    Show;
  end;
end;

{ TFake }

constructor TFake.Create(anOwner: TShadowMenu);
begin
  inherited Create(anOwner);
  FShadowMenu:=anOwner;
  with GetControlClassDefaultSize do
    SetInitialBounds(0, 0, cx, cy);
  BorderStyle:=bsNone;
  Visible:=False;
  Canvas.Pen.Color:=clBtnText;
  Canvas.Pen.Style:=psDot;
  Canvas.Font.Color:=clBtnText;
  Canvas.Brush.Color:=clBtnFace;
  Parent:=anOwner;
end;

class function TFake.GetControlClassDefaultSize: TSize;
begin
  Result.cx:=100;
  Result.cy:=DropDown_Height;
end;

procedure TFake.Paint;
var
  r: TRect;
  TextSize: TSize;
  TextPoint, AddBmpPoint: TPoint;
  AddBmp: TImageIndex;
  IL: TLCLGlyphs;
  Res: TScaledImageListResolution;
begin
  r:=ClientRect;
  Canvas.FillRect(r);
  Canvas.RoundRect(r, 3, 3);
  IL:=IDEImages.Images_16;
  AddBmp:=IL.GetImageIndex('laz_add');
  Res:=IL.ResolutionForControl[0, Self];
  TextSize:=Canvas.TextExtent(Caption);
  TextPoint.y:=(r.Bottom - r.Top - TextSize.cy) div 2;
  if (TextPoint.y < 1) then
    TextPoint.y:=1;
  TextPoint.x:=(r.Right - r.Left - TextSize.cx + Res.Width) div 2;
  Canvas.TextRect(r, TextPoint.x, TextPoint.y, Caption);

  AddBmpPoint.x:=(TextPoint.x - Res.Width) div 2;
  AddBmpPoint.y:=(r.Bottom - r.Top - Res.Height) div 2;
  Res.Draw(Canvas, AddBmpPoint.x, AddBmpPoint.y, AddBmp);
end;

procedure TFake.Refresh;
begin
  SetVisibilitySizeAndPosition;
end;

procedure TFake.TextChanged;
begin
  inherited TextChanged;
  FMinWidth:=FShadowMenu.GetStringWidth(Caption, False) +
             Double_MenuBar_Text_Offset +
             Add_Icon_Width;
end;

{ TShadowMenu }

procedure TShadowMenu.AddItemAfter(Sender: TObject);
var
  si: TShadowItem;
begin
  si:=SelectedShadowItem;
  if (si <> nil) then
    si.ParentBox.AddItemAndShadow(si, False);
end;

procedure TShadowMenu.AddItemBefore(Sender: TObject);
var
  si: TShadowItem;
begin
  si:=SelectedShadowItem;
  if (si <> nil) then
    si.ParentBox.AddItemAndShadow(si, True);
end;

procedure TShadowMenu.AddOnClick(Sender: TObject);
var
  CompEditor: TDefaultComponentEditor;
begin
  if (FSelectedMenuItem <> nil) then begin
    FDesigner.FGui.BeginUpdate;
    CompEditor:=nil;
    try
      CompEditor:=TDefaultComponentEditor.Create(FSelectedMenuItem, FEditorDesigner);
      CompEditor.Edit;
      UpdateSelectedItemInfo;
    finally
      CompEditor.Free;
      FDesigner.FGui.EndUpdate;
    end;
  end;
end;

procedure TShadowMenu.AddSubMenu(Sender: TObject);
var
  si: TShadowItem;
begin
  si:=SelectedShadowItem;
  if (si <> nil) then begin
    HideFakes;
    AddSubMenuTo(si);
  end;
end;

procedure TShadowMenu.DeleteItem(Sender: TObject);
var
  si: TShadowItem;
begin
  if (FDesigner.TotalMenuItemsCount > 0) then
  begin
    if (Sender is TShadowItem) then
      DeleteChildlessShadowAndItem(TShadowItem(Sender))
    else begin
      si:=SelectedShadowItem;
      if (si <> nil) then
        DeleteChildlessShadowAndItem(si);
    end;
  end;
end;

procedure TShadowMenu.AddSeparatorAbove(Sender: TObject);
var
  selected: TShadowItem;
begin
  if (FSelectedMenuItem <> nil) then begin
    selected:=SelectedShadowItem;
    selected.ParentBox.AddItemAndShadow(selected, True, True);
  end;
end;

procedure TShadowMenu.AddSeparatorBelow(Sender: TObject);
var
  selected: TShadowItem;
begin
  if (FSelectedMenuItem <> nil) then begin
    selected:=SelectedShadowItem;
    selected.ParentBox.AddItemAndShadow(selected, False, True);
  end;
end;

procedure TShadowMenu.MoveItemAfter(Sender: TObject);
var
  nextI, parentI: TMenuItem;
  currIdx: integer;
  selected: TShadowItem;
begin
  if (FSelectedMenuItem <> nil) then begin
    nextI:=GetNextItem(FSelectedMenuItem);
    parentI:=FSelectedMenuItem.Parent;
    selected:=SelectedShadowItem;
    if (nextI <> nil) and (parentI <> nil) then
      begin
        HideFakes;
        HideBoxesAboveLevel(selected.Level);
        currIdx:=FSelectedMenuItem.MenuIndex;
        parentI.Remove(nextI);
        parentI.Remove(FSelectedMenuItem);
        parentI.Insert(currIdx, nextI);
        parentI.Insert(Succ(currIdx), FSelectedMenuItem);
        FEditorDesigner.PropertyEditorHook.RefreshPropertyValues;
        FEditorDesigner.PropertyEditorHook.Modified(FMenu);
        selected.ParentBox.LocateShadows;
        UpdateBoxLocationsAndSizes;
        selected.ShowChildBox;
        RefreshFakes;
        UpdateActionsEnabledness;
      end;
  end;
end;

procedure TShadowMenu.MoveItemBefore(Sender: TObject);
var
  previousI, parentI: TMenuItem;
  currIdx: integer;
  selected: TShadowItem;
begin
  if (FSelectedMenuItem <> nil) then begin
    previousI:=GetPreviousItem(FSelectedMenuItem);
    parentI:=FSelectedMenuItem.Parent;
    selected:=SelectedShadowItem;
    if (previousI <> nil) and (parentI <> nil) then
      begin
        HideFakes;
        HideBoxesAboveLevel(selected.Level);
        currIdx:=FSelectedMenuItem.MenuIndex;
        parentI.Remove(previousI);
        parentI.Remove(FSelectedMenuItem);
        parentI.Insert(Pred(currIdx), FSelectedMenuItem);
        parentI.Insert(currIdx, previousI);
        FEditorDesigner.PropertyEditorHook.RefreshPropertyValues;
        FEditorDesigner.PropertyEditorHook.Modified(FMenu);
        selected.ParentBox.LocateShadows;
        UpdateBoxLocationsAndSizes;
        selected.ShowChildBox;
        RefreshFakes;
        UpdateActionsEnabledness;
      end;
  end;
end;

procedure TShadowMenu.RemoveAllSeparators(Sender: TObject);
begin
  if (FSelectedMenuItem <> nil) then
    SelectedShadowItem.ParentBox.RemoveAllSeparators;
end;

procedure TShadowMenu.ConnectSpeedButtonOnClickMethods;
begin
  with FDesigner.FGui do begin
    AddSeparatorAboveButton.OnClick:=@AddSeparatorAbove;
    AddSeparatorBelowButton.OnClick:=@AddSeparatorBelow;
    MoveItemUpButton.OnClick:=@MoveItemBefore;
    MoveItemDownButton.OnClick:=@MoveItemAfter;
    DeleteItemButton.OnClick:=@DeleteItem;
    AddItemAboveButton.OnClick:=@AddItemBefore;
    AddItemBelowButton.OnClick:=@AddItemAfter;
    AddSubMenuButton.OnClick:=@AddSubMenu;
  end;
end;

procedure TShadowMenu.RecursiveCreateShadows(aParentBox: TShadowBox; aMI: TMenuItem);
var
  i: integer;
  sb: TShadowBox;
begin
  TShadowItem.CreateWithBoxAndItem(Self, aParentBox, aMI);
  if aMI.Count = 0 then Exit;
  sb:=TShadowBox.CreateWithParentBox(Self, aParentBox, aMI);
  for i:=0 to aMI.Count-1 do
    RecursiveCreateShadows(sb, aMI.Items[i]);
end;

procedure TShadowMenu.CreateShadowBoxesAndItems;
var
  i: integer;
begin
  if FMenu.Items.Count = 0 then Exit;
  FRootBox:=TShadowBox.CreateWithParentBox(Self, nil, FMenu.Items);
  for i:=0 to FMenu.Items.Count-1 do
    RecursiveCreateShadows(FRootBox, FMenu.Items[i]);
end;

procedure TShadowMenu.DeleteChildlessShadowAndItem(anExistingSI: TShadowItem);
var
  nearestMI, mi: TMenuItem;
  box: TShadowBox;
begin
  FDesigner.FGui.BeginUpdate;
  try
    mi:=anExistingSI.RealItem;
    if (mi.Count > 0) then
      DeleteShadowAndItemAndChildren(anExistingSI)
    else begin
      HideFakes;
      if (mi = FSelectedMenuItem) then
        FSelectedMenuItem:=nil;
      nearestMI:=GetNextNonSepItem(mi);
      if (nearestMI = nil) then
        nearestMI:=GetPreviousNonSepItem(mi);
      if (nearestMI = nil) then
      begin
        if mi.Parent<>FMenu.Items then
          nearestMI:=mi.Parent;
      end;
      box:=anExistingSI.ParentBox;
      box.ParentMenuItem.Remove(mi);
      anExistingSI.RealItem:=nil;
      box.ShadowList.Remove(anExistingSI);
      anExistingSI.Parent:=nil;
      Application.ReleaseComponent(anExistingSI);
      FEditorDesigner.PropertyEditorHook.Modified(mi);
      FEditorDesigner.PropertyEditorHook.DeletePersistent(TPersistent(mi));
      FEditorDesigner.Modified;

      if (box.ShadowList.Count = 0) then
      begin
        FBoxList.Remove(box);
        box.Parent:=nil;
        if box=FRootBox then
          FRootBox:=nil;
        Application.ReleaseComponent(box);
        box:=nil;
      end;
      if Assigned(box) then
        box.LocateShadows;
      UpdateBoxLocationsAndSizes;
      SetSelectedMenuItem(nearestMI, False, True);
      FDesigner.FGui.UpdateStatistics;
    end;
  finally
    FDesigner.FGui.EndUpdate;
  end;
end;

procedure TShadowMenu.DeleteBox(aMI: TMenuItem);
var
  i: integer;
  sb: TShadowBoxBase;
  si: TShadowItemBase;
begin
  sb:=GetParentBoxForMenuItem(aMI);
  sb.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TShadowMenu.DeleteBox'){$ENDIF};
  for i:=aMI.Count-1 downto 0 do
    DeleteBox(aMI.Items[i]);
  Assert(sb<>nil,'TShadowMenu.DeleteBox: internal error');
  sb.Hide;
  sb.ShadowList.Remove(GetShadowForMenuItem(aMI));
  if (sb.ShadowList.Count = 0) then
  begin
    FBoxList.Remove(sb);
    sb.Parent:=nil;
    si:=GetShadowForMenuItem(sb.ParentMenuItem);
    if Assigned(si) then
      si.Invalidate;
    Application.ReleaseComponent(sb);
  end;
end;

procedure TShadowMenu.DeleteItm(anItem: TMenuItem);
var
  i: integer;
begin
  for i:=anItem.Count-1 downto 0 do
    DeleteItm(anItem.Items[i]);
  anItem.Parent.Remove(anItem);
  GlobalDesignHook.DeletePersistent(TPersistent(anItem));
  GlobalDesignHook.Modified(anItem);
end;

procedure TShadowMenu.DeleteShadowAndItemAndChildren(anExistingSI: TShadowItem);
var
  firstBoxToDelete: TShadowBoxBase;
  mi: TMenuItem;
  i: integer;
begin
  if IDEQuestionDialogAb(
       lisDelete,
       lisMenuEditorDeleteThisItemAndItsSubitems,
       mtWarning, [mrYes, mrNo], False) = mrYes then
  begin
    firstBoxToDelete:=GetBoxWithParentItem(anExistingSI.RealItem);
    Assert(firstBoxToDelete<>nil,'TShadowMenu.DeleteShadowAndItemAndChildren: no children');
    // Delete boxes recursively
    mi:=firstBoxToDelete.ParentMenuItem;
    Assert(mi<>nil,'TShadowMenu,DeleteShadowAndItemAndChildren: RecursiveBoxDelete internal error');
    for i:=mi.Count-1 downto 0 do
      DeleteBox(mi.Items[i]);
    // Delete children recursively
    mi:=anExistingSI.RealItem;
    for i:=mi.Count-1 downto 0 do
      DeleteItm(mi.Items[i]);
    DeleteChildlessShadowAndItem(anExistingSI);
  end;
end;

function TShadowMenu.GetSelectedShadowItem: TShadowItem;
begin
  Result:=TShadowItem(GetShadowForMenuItem(FSelectedMenuItem));
end;

function TShadowMenu.GetMenuBarIconWidth(aMI: TMenuItem): integer;
begin
  Result:=0;
  if aMI.IsInMenuBar then begin
    if aMI.HasIcon and (aMI.ImageIndex > -1) and
       (FMenu.Images <> nil) then
         Inc(Result, FMenu.Images.Width)
    else if (aMI.Bitmap <> nil) and not aMI.Bitmap.Empty then
      Inc(Result, aMI.Bitmap.Width);
    if (Result > 24) then
      Result:=24;
  end;
end;

procedure TShadowMenu.AddManyItems(aPrimaries, aDepth: integer);
var
  p, d: integer;
  mi, mi2: TMenuItem;
  sb: TShadowBox;

  function NewMenuItem(aParentMI: TMenuItem): TMenuItem;
  begin
    Result:=TMenuItem.Create(FLookupRoot);
    Result.Name:=FEditorDesigner.CreateUniqueComponentName('TMenuItem');
    Result.Caption:=Result.Name;
    if (aParentMI = nil) then
      FMenu.Items.Add(Result)
    else aParentMI.Add(Result);
    FEditorDesigner.PropertyEditorHook.PersistentAdded(Result, False);
    FEditorDesigner.PropertyEditorHook.Modified(Result);
  end;

begin
  if not IsMainMenu then
    begin
      for p:=1 to aPrimaries do
        TShadowItem.CreateWithBoxAndItem(Self, FRootBox, NewMenuItem(nil));
      UpdateBoxLocationsAndSizes;
    end
  else
    begin
      for p:=0 to aPrimaries-1 do
        begin
          if (p = 0) then
            mi:=FMenu.Items[0]
          else
            begin
              mi:=NewMenuItem(nil);
              TShadowItem.CreateWithBoxAndItem(Self, FRootBox, mi);
            end;
          sb:=TShadowBox.CreateWithParentBox(Self, FRootBox, mi);
          for d:=1 to aDepth do
            begin
              mi2:=NewMenuItem(mi);
              TShadowItem.CreateWithBoxAndItem(Self, sb, mi2);
            end;
        end;
      UpdateBoxLocationsAndSizes;
      HideBoxesAboveLevel(0);
    end;
  SetSelectedMenuItem(FMenu.Items[0], False, False);
  SelectedShadowItem.ShowChildBox;
  FDesigner.FGui.UpdateStatistics;
end;

function TShadowMenu.GetBoxWithParentItem(aParentMI: TMenuItem): TShadowBoxBase;
var
  sb: TShadowBoxBase;
begin
  Assert(aParentMI<>nil,'TShadowMenu.GetBoxWithParentItem: parent item is nil');
  for sb in FBoxList do
    if (sb.ParentMenuItem = aParentMI) then
      Exit(sb);
  Result:=nil;
end;

function TShadowMenu.GetMaxVisibleBoxDims(aSB: TShadowBox): TPoint;
begin
  Result:=Point(0,0);
  if (aSB = nil) or not aSB.Visible then
    Exit
  else Result:=Point(aSB.BoundsRect.Right, aSB.BoundsRect.Bottom);
end;

function TShadowMenu.GetMaxVisibleFakeDims: TPoint;
begin
  Result:=Point(0, 0);
  if FAddItemFake.Visible then
    Result:=Point(FAddItemFake.BoundsRect.Right, FAddItemFake.BoundsRect.Bottom);
  if FAddSubMenuFake.Visible then begin
    if (FAddSubmenuFake.BoundsRect.Right > Result.x) then
      Result.x:=FAddSubmenuFake.BoundsRect.Right;
    if (FAddSubmenuFake.BoundsRect.Bottom > Result.y) then
      Result.y:=FAddSubmenuFake.BoundsRect.Bottom;
  end;
end;

function TShadowMenu.GetSelectedShadowBox: TShadowBox;
var
  sel: TShadowItem;
begin
  sel:=SelectedShadowItem;
  if (sel = nil) then
    Result:=nil
  else
    Result:=sel.ParentBox;
end;

procedure TShadowMenu.AddSubMenuTo(anExistingSI: TShadowItem);
var
  newMI: TMenuItem;
  box: TShadowBox;
begin
  if (anExistingSI.RealItem.Count <> 0) then
    Exit;
  newMI:=TMenuItem.Create(FLookupRoot);
  newMI.Name:=FEditorDesigner.CreateUniqueComponentName(newMI.ClassName);
  newMI.Caption:=newMI.Name;
  anExistingSI.RealItem.Add(newMI);
  GlobalDesignHook.PersistentAdded(newMI, False);
  GlobalDesignHook.Modified(newMI);
  box:=TShadowBox.CreateWithParentBox(Self, anExistingSI.ParentBox, anExistingSI.RealItem);
  TShadowItem.CreateWithBoxAndItem(Self, box, newMI);
  UpdateBoxLocationsAndSizes;
  SetSelectedMenuItem(newMI, False, False);
  FDesigner.FGui.UpdateStatistics;
end;

procedure TShadowMenu.SetupPopupMenu;
var
  pe: TPopEnum;
  ac: TAction;
  primaryItem, mi: TMenuItem;

  procedure NewPopItem(const aCaption: string; anOnClick: TNotifyEvent;
                       aShortcut: TShortCut=0); //aShortCut2: String='');
  begin
    ac:=TAction.Create(Self);
    with ac do begin
      ac.ActionList:=FActionList;
      ac.DisableIfNoHandler:=False;
      Tag:=PtrInt(pe);
      Caption:=aCaption;
      OnExecute:=anOnClick;
      ShortCut:=aShortcut;
      //if aShortCut2 <> '' then                 Does not work.
      //  SecondaryShortCuts.Add(aShortCut2);
    end;
    mi:=TMenuItem.Create(Self);
    FItemsPopupMenu.Items.Add(mi);
    mi.Action:=ac;
  end;

  procedure NewPopPrimary(const aCaption: string);
  begin
    ac:=TAction.Create(Self);
    with ac do begin
      ActionList:=FActionList;
      DisableIfNoHandler:=False;
      Tag:=PtrInt(pe);
      Caption:=aCaption;
    end;
    mi:=TMenuItem.Create(Self);
    FItemsPopupMenu.Items.Add(mi);
    mi.Action:=ac;
    primaryItem:=mi;
  end;

  procedure NewPopSub(const aPrimary: TMenuItem; const aCaption: string;
                      anOnClick: TNotifyEvent; aShortcut: TShortCut=0);
  begin
    ac:=TAction.Create(Self);
    with ac do begin
      ActionList:=FActionList;
      DisableIfNoHandler:=False;
      Tag:=PtrInt(pe);
      Caption:=aCaption;
      OnExecute:=anOnClick;
      ShortCut:=aShortcut;
    end;
    mi:=TMenuItem.Create(Self);
    aPrimary.Add(mi);
    mi.Action:=ac;
  end;

  procedure NewSeparatorAction;
  begin
    FItemsPopupMenu.Items.AddSeparator;
    ac:=TAction.Create(Self);
    ac.ActionList:=FActionList;
    ac.Tag:=PtrInt(pe);
    ac.Name:=GetEnumName(TypeInfo(TPopEnum), PtrInt(pe));
  end;

begin
  for pe in TPopEnum do
    with FDesigner.FGui do
    case pe of
      popItemAddOnClick:
        NewPopItem(lisMenuEditorAddOnClickHandler, @AddOnClick);
      popItemAddBefore: begin
        NewPopItem('', @AddItemBefore, KeyToShortCut(VK_INSERT,[]));
        AddItemAboveButton.Action:=ac;
      end;
      popItemAddAfter: begin
        NewPopItem('', @AddItemAfter);
        AddItemBelowButton.Action:=ac;
      end;
      popItemAddSubMenu: begin
        NewPopItem('', @AddSubMenu,KeyToShortCut(VK_INSERT,[ssCtrl]));
        AddSubMenuButton.Action:=ac;
      end;
      popItemDelete: begin
        NewPopItem(lisMenuEditorDeleteItem, @DeleteItem, KeyToShortCut(VK_DELETE, []));
        DeleteItemButton.Action:=ac;
      end;
      popItemOISep:
        NewSeparatorAction;
      popItemMoveBefore: begin
        NewPopItem('', @MoveItemBefore, KeyToShortCut(VK_UP,[ssCtrl]));
        MoveItemUpButton.Action:=ac;
      end;
      popItemMoveAfter: begin
        NewPopItem('', @MoveItemAfter, KeyToShortCut(VK_DOWN,[ssCtrl]));
        MoveItemDownButton.Action:=ac;
      end;
      popAddImgListIcon: begin
        NewPopItem('', @AddImageListIcon);
        FAddImgListIconAction:=ac;
      end;
      popItemSep:
        NewSeparatorAction;
      popSeparators_:
        NewPopPrimary(lisMenuEditorSeParators);
      popAddSeparatorBefore: begin
        NewPopSub(primaryItem, lisMenuEditorAddSeparatorBefore, @AddSeparatorAbove);
        AddSeparatorAboveButton.Action:=ac;
        ac.Hint:=lisAddANewSeparatorAboveSelectedItem;
      end;
      popAddSeparatorAfter: begin
        NewPopSub(primaryItem, lisMenuEditorAddSeparatorAfter, @AddSeparatorBelow);
        AddSeparatorBelowButton.Action:=ac;
        ac.Hint:=lisAddANewSeparatorBelowSelectedItem;
      end;
      popRemoveAllSeparators:
        NewPopSub(primaryItem, lisMenuEditorRemoveAllSeparators, @RemoveAllSeparators);
      popShortcuts_:
        NewPopPrimary(lisMenuEditorShortcUts2);
      popListShortcuts:
        NewPopSub(primaryItem, '', @ListShortcuts);
      popListShortcutsAccelerators:
        NewPopSub(primaryItem, '', @ListShortcutsAndAccelerators);
      popResolveShortcutConflicts:
        NewPopSub(primaryItem, lisMenuEditorResolveShortcutConflicts, @ResolveshortcutConflicts);
      popTemplates_:
        NewPopPrimary(lisMenuEditorTemplates);
      popSaveAsTemplate:
        NewPopSub(primaryItem, lisMenuEditorSaveMenuAsATemplate, @SaveAsTemplate);
      popAddFromTemplate:
        NewPopSub(primaryItem, lisMenuEditorAddFromTemplate, @AddFromTemplate);
      popDeleteTemplate:
        NewPopSub(primaryItem, lisMenuEditorDeleteMenuTemplate, @DeleteTemplate);
    end; // case
end;

function TShadowMenu.GetMenuBarCumWidthForItemIndex(anIndex: integer): integer;
var
  w: integer;
  mi: TMenuItem;
begin
  Result:=0;
  if anIndex <> 0 then
    repeat
      mi:=FMenu.Items[Pred(anIndex)];
      w:=GetStringWidth(mi.Caption, mi.Default) +
         Double_MenuBar_Text_Offset + GetMenuBarIconWidth(mi);
      Inc(Result, w);
      Dec(anIndex)
    until (anIndex <= 0);
end;

function TShadowMenu.GetParentItemHeightInBox(aParentItem: TMenuItem): integer;

  function HeightOfItem(anIndex: integer): integer;
  begin
    if aParentItem.Parent.Items[anIndex].IsLine then
      Result:=Separator_Height
    else
      Result:=DropDown_Height;
  end;

var
  idx: integer = 0;
begin
  Result:=1;
  repeat
    if (idx < aParentItem.MenuIndex) then
      Inc(Result, HeightOfItem(idx));
    Inc(idx);
  until (idx >= aParentItem.MenuIndex);
end;

procedure TShadowMenu.UpdateBoxLocationsAndSizes;
var
  sb: TShadowBoxBase;
  si: TShadowItemBase;
  lft, w, idx: integer;
  pt: TPoint;
begin
  FBoxList.Sort(@SortByBoxLevel);
  for sb in FBoxList do begin
    if sb.IsMenuBar then
      begin
        sb.Align:=alTop;
        sb.Height:=MenuBar_Height;
        lft:=0;
        for si in sb.ShadowList do begin
          w:=si.GetWidth;
          si.SetBounds(lft, 0, w, MenuBar_Height);
          Inc(lft, w);
        end;
      end
    else if IsMainMenu and (sb.Level = 1) then
      begin
        pt:=sb.GetInnerDims;
        idx:=sb.ParentMenuItem.MenuIndex;
        lft:=GetMenuBarCumWidthForItemIndex(idx);
        sb.SetBounds(lft, MenuBar_Height+1, pt.x+2, pt.y+2);
      end
    else begin
      pt:=sb.GetInnerDims;
      if (sb.Level = 0) then
        sb.SetBounds(Popup_Origin.x, Popup_Origin.y, pt.x+2, pt.y+2)
      else sb.SetBounds(sb.ParentBox.Left+sb.ParentBox.Width,
                        sb.ParentBox.Top+GetParentItemHeightInBox(sb.ParentMenuItem),
                        pt.x+2, pt.y+2);
    end;
  end;
  RefreshFakes;
end;

procedure TShadowMenu.RemoveEmptyBox(aSB: TShadowBox);
var
  miToSelect: TMenuItem;
begin
  if (aSB.ShadowList.Count = 0) then begin
    miToSelect:=aSB.ParentMenuItem;
    FBoxList.Remove(aSB);
    aSB.Parent:=nil;
    Application.ReleaseComponent(aSB);
    UpdateBoxLocationsAndSizes;
    SetSelectedMenuItem(miToSelect, False, True);
  end;
end;

procedure TShadowMenu.HideFakes;
begin
  FAddSubmenuFake.Hide;
  FAddItemFake.Hide;
  FAddFirstItemFake.Hide;
end;

procedure TShadowMenu.RefreshFakes;
begin
  // MG: Dont: Application.ProcessMessages; this might free components and trigger events
  FAddItemFake.Refresh;
  FAddSubmenuFake.Refresh;
  FAddFirstItemFake.Refresh;
end;

procedure TShadowMenu.UpdateButtonGlyphs(isInBar: boolean);
begin
  if (FSelectedMenuItem <> nil) and (isInBar <> FDesigner.VariableGlyphsInMenuBar) then
    FDesigner.FGui.LoadVariableButtonGlyphs(isInBar);
end;

procedure TShadowMenu.AddFromTemplate(Sender: TObject);
var
  newItem: TMenuItem;
  sb: TShadowBox;
  i: integer;
begin
  if (FSelectedMenuItem <> nil) and (FSelectedMenuItem.Parent.Parent = nil) then
  begin
    HideFakes;
    newItem:=InsertMenuTemplateDlg(FMenu);
    if (newItem <> nil) then
    begin
      FMenu.Items.Add(newItem);
      FLookupRoot.InsertComponent(newItem);
      newItem.Name:=FEditorDesigner.CreateUniqueComponentName(newItem.ClassName);
      FEditorDesigner.PropertyEditorHook.PersistentAdded(TPersistent(newItem), False);
      FEditorDesigner.Modified;
      TShadowItem.CreateWithBoxAndItem(Self, FRootBox, newItem);
      if (newItem.Count > 0) then begin
        sb:=TShadowBox.CreateWithParentBox(Self, FRootBox, newItem);
        for i:=0 to newItem.Count-1 do
        begin
          FLookupRoot.InsertComponent(newItem.Items[i]);
          newItem.Items[i].Name:=FEditorDesigner.CreateUniqueComponentName(newItem.Items[i].ClassName);
          FEditorDesigner.PropertyEditorHook.PersistentAdded(TPersistent(newItem.Items[i]), False);
          FEditorDesigner.Modified;
          TShadowItem.CreateWithBoxAndItem(Self, sb, newItem.Items[i]);
        end;
      end;
      UpdateBoxLocationsAndSizes;
      SetSelectedMenuItem(newItem, False, False);
    end;
  end;
end;

procedure TShadowMenu.AddImageListIcon(Sender: TObject);
var
  idx: integer;
  selected: TShadowItem;
begin
  if FSelectedMenuItem = nil then Exit;
  idx := -1;
  selected:=SelectedShadowItem;
  if (FMenu.Images <> nil) then
    idx := ChooseIconFromImageListDlg(FMenu.Images)
  else if (selected.Level > 0)
  and (FSelectedMenuItem.Parent.SubMenuImages <> nil) then
    idx := ChooseIconFromImageListDlg(FSelectedMenuItem.Parent.SubMenuImages);
  if idx = -1 then Exit;
  FSelectedMenuItem.ImageIndex := idx;
  selected.Invalidate;
  UpdateActionsEnabledness;
  FEditorDesigner.PropertyEditorHook.RefreshPropertyValues;
  FEditorDesigner.Modified;
end;

procedure TShadowMenu.DeleteTemplate(Sender: TObject);
begin
  if SavedTemplatesExist and DeleteMenuTemplateDlg then begin
    FDesigner.UpdateTemplatesCount;
    UpdateActionsEnabledness;
  end;
end;

procedure TShadowMenu.ListShortcuts(Sender: TObject);
begin
  ListShortCutDlg(FDesigner.Shortcuts, True, Self, FMenu);
end;

procedure TShadowMenu.ListShortcutsAndAccelerators(Sender: TObject);
begin
  ListShortCutDlg(FDesigner.Shortcuts, False, Self, Nil);
end;

procedure TShadowMenu.ResolveShortcutConflicts(Sender: TObject);
var
  dlg: TResolveConflictsDlg;
begin
  dlg:=TResolveConflictsDlg.Create(FDesigner.Shortcuts, Self);
  try
    if dlg.ShowModal <> mrCancel then
      UpdateActionsEnabledness;
  finally
    dlg.Free;
  end;
end;

procedure TShadowMenu.SaveAsTemplate(Sender: TObject);
var
  dlg: TMenuTemplateDialog;
begin
  if (FSelectedMenuItem <> nil) and LevelZeroAndNoGrandchildren(FSelectedMenuItem) then
  begin
    //SaveMenuTemplateDlg(FSelectedMenuItem);
    dlg:=TMenuTemplateDialog.CreateWithMode(FMenu, dmSave);
    try
      dlg.MenuToSave:=FSelectedMenuItem;
      dlg.ShowModal;
    finally
      dlg.Free;
    end;
    FDesigner.UpdateTemplatesCount;
    UpdateActionsEnabledness;
  end;
end;

procedure TShadowMenu.OnObjectPropertyChanged(Sender: TObject; NewObject: TPersistent);
var
  propertyEditor: TPropertyEditor absolute Sender;
  i: Integer;
  persistent: TPersistent;
  mi: TMenuItem absolute persistent;
begin
  if not (Sender is TPropertyEditor) or (NewObject = nil) then
    Exit;
  if (NewObject is TAction) then
    for i:=0 to propertyEditor.PropCount-1 do begin
      persistent:=propertyEditor.GetComponent(i);
      if (persistent is TMenuItem) then begin
        if GetShadowForMenuItem(mi) <> nil then
        begin
          UpdateBoxLocationsAndSizes;
          RefreshFakes;
          if (FSelectedMenuItem <> nil) then
            SelectedShadowItem.Invalidate;
        end;
      end;
    end;
  if (NewObject is TImageList) and (NewObject = FMenu.Images) then
    UpdateActionsEnabledness;
end;

procedure TShadowMenu.OnDesignerModified(Sender: TObject);
var
  i: integer;
  persistent: TPersistent;
  mi: TMenuItem absolute persistent;
  refreshNeeded: boolean = False;
begin
  if FDesigner.FGui.IsUpdate then
    Exit;

  if (Sender is TPropertyEditor) then begin
    for i:=0 to TPropertyEditor(Sender).PropCount-1 do begin
      persistent:=TPropertyEditor(Sender).GetComponent(i);
      if (persistent is TMenuItem) then begin
        if GetShadowForMenuItem(mi) <> nil then
          refreshNeeded:=True;
      end;
    end;
    if refreshNeeded then begin
      UpdateBoxLocationsAndSizes;
      if ((mi.Action <> nil) and (TAction(mi.Action).ShortCut <> 0)) or
         (mi.ShortCut <> 0) or (mi.ShortCutKey2 <> 0) then
           FDesigner.Shortcuts.UpdateShortcutList(True);
      if (FSelectedMenuItem <> nil) then begin
        SelectedShadowItem.Invalidate;
      end;
      FDesigner.FGui.UpdateStatistics;
    end;
  end;
end;

procedure TShadowMenu.OnDesignerRefreshPropertyValues;
var
  comp: TComponent;
  mi: TMenuItem absolute comp;
  selBox: TShadowBox;
begin
  if FSelectedMenuItem = nil then
    Exit;
  comp:=GlobalDesignHook.GetComponent(FSelectedMenuItem.Name);
  if comp is TMenuItem then
  begin
    selBox:=SelectedShadowBox;
    if (selBox.LastRIValue <> mi.RadioItem) then
      FDesigner.FGui.UpdateSubmenuGroupBox(FSelectedMenuItem, selBox);
  end;
end;

function TShadowMenu.OnClickIsAssigned(aMI: TMenuItem): boolean;
begin
  if (aMI = nil) then
    Exit(False);
  Result:=(FEditorDesigner.PropertyEditorHook.GetMethodName(GetMethodProp(aMI, 'OnClick'), aMI) <> '');
end;

procedure TShadowMenu.Paint;
begin
  if FInitialising then
    Exit;
end;

procedure TShadowMenu.SetParent(NewParent: TWinControl);
begin
  inherited SetParent(NewParent);
  if (NewParent <> nil) and not (csDestroying in ComponentState) then
  begin
    Align:=alNone;
    CreateShadowBoxesAndItems;
    UpdateBoxLocationsAndSizes;
    HideBoxesAboveLevel(0);
    // MG: Dont: Application.ProcessMessages; this might free components and trigger events
    FInitialising:=True;
    if (FInitialSelectedMenuItem <> nil) then begin
      SetSelectedMenuItem(FInitialSelectedMenuItem, True, False);
      UpdateActionsEnabledness;
    end;
  end;
end;

procedure TShadowMenu.SetSelectedMenuItem(aMI: TMenuItem;
  viaDesigner, prevWasDeleted: boolean);
var
  prevSelectedMenuItem: TMenuItem;
  prevSelectedShadow: TShadowItem;
begin
  if (aMI = nil) then
  begin
    if prevWasDeleted then
      SetSelectedShadow(nil, nil, False)
    else
      SetSelectedShadow(FSelectedMenuItem, nil, False);
    FSelectedMenuItem:=nil;
    RefreshFakes;
    Exit;
  end;
  if (FSelectedMenuItem <> aMI) then
  begin
    if (FSelectedMenuItem = nil) or prevWasDeleted then begin
      prevSelectedMenuItem:=nil;
      prevSelectedShadow:=nil;
    end
    else begin
      prevSelectedMenuItem:=FSelectedMenuItem;
      prevSelectedShadow:=TShadowItem(GetShadowForMenuItem(prevSelectedMenuItem));
    end;
    if (prevSelectedShadow <> nil) then begin
      if prevSelectedMenuItem.Enabled then
        prevSelectedShadow.ShowNormal
      else
        prevSelectedShadow.ShowDisabled;
      if not AIsDescendantOfB(aMI, prevSelectedMenuItem) then
        prevSelectedShadow.HideChildren;
    end;
    FSelectedMenuItem:=aMI;
    SetSelectedShadow(prevSelectedMenuItem, FSelectedMenuItem, viaDesigner);
  end;
end;

procedure TShadowMenu.SetSelectedShadow(const prevSelectedItem,
  curSelectedItem: TMenuItem; viaDesigner: boolean);
var
  selectedShadow, prevShadow: TShadowItem;
begin
  selectedShadow:=TShadowItem(GetShadowForMenuItem(curSelectedItem));
  if selectedShadow=nil then
  begin
    HideFakes;
    if (FSelectedMenuItem <> nil) then
    begin
      SelectedShadowItem.ShowNormal;
      FSelectedMenuItem:=nil;
    end;
    UpdateSelectedItemInfo;
    if not viaDesigner and (FMenu<>nil) then
      FEditorDesigner.SelectOnlyThisComponent(FMenu);
  end else
  begin
    if (prevSelectedItem <> nil) then
    begin
      prevShadow:=TShadowItem(GetShadowForMenuItem(prevSelectedItem));
      if (prevShadow <> nil)
      and (selectedShadow.ParentBox.ParentMenuItem <> prevSelectedItem)
      and (prevShadow.ParentBox <> selectedShadow.ParentBox)
      then
        prevShadow.HideChainFromRoot;
    end;
    UpdateButtonGlyphs(FSelectedMenuItem.IsInMenuBar);
    selectedShadow.ShowChainToRoot;
    selectedShadow.ShowSelected;
    HideBoxesAboveLevel(selectedShadow.Level);
    selectedShadow.ShowChildBox;

    UpdateSelectedItemInfo;
    if not viaDesigner then begin
      //debugln(['TShadowMenu.SetSelectedShadow ',DbgSName(curSelectedItem)]);
      FEditorDesigner.SelectOnlyThisComponent(curSelectedItem);
    end;

    if not FDesigner.FGui.Visible then
      FDesigner.FGui.ShowOnTop;
    if selectedShadow<>nil then
      selectedShadow.SetFocus;
    UpdateActionsEnabledness;
    RefreshFakes;
  end;
end;

function TShadowMenu.GetActionForEnum(anEnum: TPopEnum): TAction;
var
  i: integer;
begin
  for i:=0 to FActionList.ActionCount do
    if TAction(FActionList.Actions[i]).Tag = PtrInt(anEnum) then
      Exit(TAction(FActionList.Actions[i]));
  Result:=nil;
end;

procedure TShadowMenu.UpdateActionsEnabledness;
var
  ac, ac1, ac2, ac3: TAction;
  pe: TPopEnum;
  isInBar, isFirst, isSeparator, isLast, prevIsSeparator, nextIsSeparator,
    levelZero, levelZeroOr1, primarySCEnabled: boolean;
begin
  if (FSelectedMenuItem = nil) then
    Exit;
  isInBar:=FSelectedMenuItem.IsInMenuBar;
  isFirst:=(FSelectedMenuItem.MenuIndex = 0);
  isLast:=(FSelectedMenuItem.MenuIndex = Pred(FSelectedMenuItem.Parent.Count));
  isSeparator:=FSelectedMenuItem.IsLine;
  prevIsSeparator:=PreviousItemIsSeparator(FSelectedMenuItem);
  nextIsSeparator:=NextItemIsSeparator(FSelectedMenuItem);
  levelZero:=(FSelectedMenuItem.Parent <> nil) and (FSelectedMenuItem.Parent.Parent = nil);
  levelZeroOr1:=LevelZeroAndNoGrandchildren(FSelectedMenuItem);
  primarySCEnabled:=not isInBar and (FSelectedMenuItem.Parent.Count > 1);

  for pe in TPopEnum do
  begin
    ac:=GetActionForEnum(pe);
    case pe of
      popItemAddOnClick: ac.Enabled:=not OnClickIsAssigned(FSelectedMenuItem);
      popItemAddBefore:
        if isInBar then begin
          ac.Caption:=lisMenuEditorAddNewItemBefore;
          ac.Hint:=lisMenuEditorAddANewItemBeforeSelectedItem;
        end
        else begin
          ac.Caption:=lisMenuEditorAddNewItemAbove;
          ac.Hint:=lisMenuEditorAddANewItemAboveSelectedItem;
        end;
      popItemAddAfter:
        if isInBar then begin
          ac.Caption:=lisMenuEditorAddNeWItemAfter;
          ac.Hint:=lisMenuEditorAddANewItemAfterSelectedItem;
        end
        else begin
          ac.Caption:=lisMenuEditorAddNeWItemBelow;
          ac.Hint:=lisMenuEditorAddANewItemBelowSelectedItem;
        end;
      popItemAddSubMenu: begin
        ac.Enabled:=(FSelectedMenuItem.Count = 0) and not FSelectedMenuItem.IsLine;
        if isInBar then begin
          ac.Caption:=lisMenuEditorAddSubmenuBelow;
          ac.Hint:=lisMenuEditorAddASubmenuBelowSelectedItem;
        end
        else begin
          ac.Caption:=lisMenuEditorAddSubmenuRight;
          ac.Hint:=lisMenuEditorAddASubmenuAtTheRightOfSelectedItem;
        end;
      end;
      popItemDelete: ac.Enabled:=(FMenu.Items.Count > 0);
      //popItemOISep
      popItemMoveBefore: begin
        ac.Enabled:=not isFirst;
        if isInBar then begin
          ac.Caption:=lisMenuEditorMoveItemLeft;
          ac.Hint:=lisMenuEditorMoveSelectedItemToTheLeft;
        end
        else begin
          ac.Caption:=lisMenuEditorMoveItemUp;
          ac.Hint:=lisMenuEditorMoveSelectedItemUp; end;
      end;
      popItemMoveAfter: begin
        ac.Enabled:=not isLast;
        if isInBar then begin
          ac.Caption:=lisMenuEditorMoVeItemRight;
          ac.Hint:=lisMenuEditorMoveSelectedItemToTheRight;
        end
        else begin
          ac.Caption:=lisMenuEditorMoVeItemDown;
          ac.Hint:=lisMenuEditorMoveSelectedItemDown;
        end;
      end;
      popAddImgListIcon: begin
        ac.Enabled:=(FMenu.Images <> nil) and (FMenu.Images.Count > 0);
        if ac.Enabled then begin
          if (FSelectedMenuItem.ImageIndex < 0) then
            ac.Caption:=Format(lisMenuEditorAddIconFromS + ' ...',
                               [FMenu.Images.Name])
          else ac.Caption:=lisMenuEditorChangeImagelistIcon;
          if (FMenu.Images.Count = 1) and (FSelectedMenuItem.ImageIndex = 0) then
            ac.Enabled:=False;
        end
        else
          ac.Caption:=lisMenuEditorAddImagelistIcon;
      end;
      //popItemSep
      popSeparators_: ac.Enabled:=primarySCEnabled;
      popAddSeparatorBefore:
        ac.Enabled:=primarySCEnabled and not isSeparator and not isFirst and not prevIsSeparator;
      popAddSeparatorAfter:
        ac.Enabled:=not isInBar and not isSeparator and not nextIsSeparator;
      popRemoveAllSeparators:
        ac.Enabled:=primarySCEnabled and (GetChildSeparatorCount(FSelectedMenuItem.Parent) > 0);
      //popShortcuts_
      popListShortcuts: begin
        ac.Enabled:=(FDesigner.Shortcuts.ShortcutMenuItemsCount > 0);
        ac.Caption:=Format(lisMenuEditorListShortcutsForS, [FMenu.Name]);
      end;
      popListShortcutsAccelerators: begin
        ac.Enabled:=(FDesigner.Shortcuts.ShortcutList.AcceleratorsInContainerCount > 0);
        ac.Caption:=Format(lisMenuEditorListShortcutsAndAccelerators,[FLookupRoot.Name]);
      end;
      popResolveShortcutConflicts: ac.Enabled:=
        (FDesigner.Shortcuts.ShortcutList.InitialDuplicates.Count > 0);
      popTemplates_:          ac.Enabled:=levelZero or FDesigner.TemplatesSaved;
      popSaveAsTemplate:      ac.Enabled:=levelZeroOr1;
      popAddFromTemplate:     ac.Enabled:=levelZero;
      popDeleteTemplate:      ac.Enabled:=FDesigner.TemplatesSaved;
    end; // case
  end; // for
  ac:=GetActionForEnum(popShortcuts_);
  ac1:=GetActionForEnum(popListShortcuts);
  ac2:=GetActionForEnum(popListShortcutsAccelerators);
  ac3:=GetActionForEnum(popResolveShortcutConflicts);
  ac.Enabled:=ac1.Enabled or ac2.Enabled or ac3.Enabled;
end;

constructor TShadowMenu.Create(aDesigner: TMenuDesigner; aForm: TForm;
  aMenu: TMenu; aSelect: TMenuItem; aWidth, aHeight: integer);
begin
  Assert(aMenu<>nil,'TShadowMenu.Create: TMenu parameter is nil');
  inherited Create(nil, aMenu);
  InitMenuBaseSizes;
  FDesigner := aDesigner;
  FMainCanvas := aForm.Canvas;
  FInitialSelectedMenuItem := aSelect;
  SetInitialBounds(0, 0, aWidth, aHeight);
  Name := 'ShadowMenu';
  DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TShadowMenu.Create'){$ENDIF};
  try
    FItemsPopupMenu := TPopupMenu.Create(Self);
    FItemsPopupMenu.Name := 'ItemsPopupMenu';
    FActionList := TActionList.Create(Self);
    SetupPopupMenu;
    FAddItemFake := TAddSiblingFake.Create(Self);
    FAddItemFake.OnClick := @AddItemAfter;
    FAddItemFake.Caption := lisMenuEditorAddMenuItem;
    FAddItemFake.Name := 'AddItemFake';
    FAddSubmenuFake := TAddSubmenuFake.Create(Self);
    FAddSubmenuFake.OnClick := @AddSubMenu;
    FAddSubmenuFake.Caption := lisMenuEditorAddSubmenu;
    FAddSubmenuFake.Name := 'AddSubmenuFake';
    FAddFirstItemFake := TAddFirstFake.Create(Self);
    FAddFirstItemFake.OnClick := @AddFirstMenu;
    FAddFirstItemFake.Caption := lisMenuEditorAddMenuItem;
    FAddFirstItemFake.Name := 'AddFirstItemFake';
    FAddFirstItemFake.Left := Popup_Origin.x;
    FAddFirstItemFake.Top := Popup_Origin.y;
    ConnectSpeedButtonOnClickMethods;
    GlobalDesignHook.AddHandlerObjectPropertyChanged(@OnObjectPropertyChanged);
    GlobalDesignHook.AddHandlerModified(@OnDesignerModified);
    GlobalDesignHook.AddHandlerRefreshPropertyValues(@OnDesignerRefreshPropertyValues);
    Color := clBtnFace;
    BorderStyle := bsNone;
    // Parent must be set before the Align property.
    // Otherwise ShadowMenu goes on top of ButtonsGroupBox which is Top aligned.
    Parent := aForm;
    AutoSize := False;
    Align := alClient;
  finally
    EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TShadowMenu.Create'){$ENDIF};
  end;
end;

destructor TShadowMenu.Destroy;
begin
  Parent := nil;
  if Assigned(LazarusIDE) and not LazarusIDE.IDEIsClosing then
  begin
    GlobalDesignHook.RemoveHandlerRefreshPropertyValues(@OnDesignerRefreshPropertyValues);
    GlobalDesignHook.RemoveHandlerModified(@OnDesignerModified);
    GlobalDesignHook.RemoveHandlerObjectPropertyChanged(@OnObjectPropertyChanged);
  end;
  inherited Destroy;
end;

procedure TShadowMenu.AddFirstMenu(Sender: TObject);
var
  newMI: TMenuItem;
  box: TShadowBox;
begin
  newMI:=TMenuItem.Create(FLookupRoot);
  newMI.Name:=FEditorDesigner.CreateUniqueComponentName(newMI.ClassName);
  newMI.Caption:=newMI.Name;
  FMenu.Items.Add(newMI);
  GlobalDesignHook.PersistentAdded(newMI, False);
  GlobalDesignHook.Modified(newMI);
  box:=TShadowBox.CreateWithParentBox(Self, nil, FMenu.Items);
  FRootBox:=box;
  TShadowItem.CreateWithBoxAndItem(Self, box, newMI);
  UpdateBoxLocationsAndSizes;
  SetSelectedMenuItem(newMI, False, False);
  FDesigner.FGui.UpdateStatistics;
end;

procedure TShadowMenu.HideBoxesAboveLevel(aLevel: integer);
var
  sb: TShadowBoxBase;
begin
  for sb in FBoxList do
    if sb.Level > aLevel then
      sb.Hide;
end;

procedure TShadowMenu.UpdateSelectedItemInfo;
begin
  FDesigner.FGui.UpdateItemInfo(FMenu, FSelectedMenuItem, SelectedShadowBox,
                                FEditorDesigner.PropertyEditorHook);
end;

{ TShadowBox }

procedure TShadowBox.BeginUpdate;
begin
  FUpdating:=True;
end;

procedure TShadowBox.EndUpdate;
begin
  FUpdating:=False;
end;

procedure TShadowBox.ShowAllUnSelected;
var
  si: TShadowItemBase;
begin
  for si in FShadowList do
    si.ShowNormal;
end;

function TShadowBox.GetIsMainMenu: boolean;
begin
  Result:=FShadowMenu.IsMainMenu;
end;

function TShadowBox.GetIsMenuBar: boolean;
begin
  Result:=(FLevel = 0) and IsMainMenu;
end;

procedure TShadowBox.Paint;
var
  r: TRect;
  dets: TThemedElementDetails;
begin
  r:=ClientRect;
  BeginUpdate;
    if IsMenuBar then begin
      dets:=ThemeServices.GetElementDetails(tmBarBackgroundActive);
      ThemeServices.DrawElement(Canvas.Handle, dets, r);
    end
    else begin
      Canvas.FillRect(r);
      Canvas.Frame(r);
    end;
  EndUpdate;
end;

procedure TShadowBox.SelectPrevious(aSI: TShadowItem);
var
  prevMI: TMenuItem;
begin
  prevMI:=GetPreviousNonSepItem(aSI.RealItem);
  if (prevMI <> nil) then
    FShadowMenu.SetSelectedMenuItem(prevMI, False, False);
end;

procedure TShadowBox.SelectSuccessor(aSI: TShadowItem);
var
  nextMI: TMenuItem;
begin
  nextMI:=GetNextNonSepItem(aSI.RealItem);
  if (nextMI <> nil) then
    FShadowMenu.SetSelectedMenuItem(nextMI, False, False);
end;

procedure TShadowBox.AddItemAndShadow(existingSI: TShadowItem;
  addBefore: boolean; isSeparator: boolean);
var
  idx: integer;
  newMI: TMenuItem;
  nm: string;
begin
  FShadowMenu.HideFakes;
  idx:=existingSI.RealItem.MenuIndex;
  if not addBefore then
    Inc(idx);
  newMI:=TMenuItem.Create(FShadowMenu.LookupRoot);
  if isSeparator then begin
    nm:='Separator';
    newMI.Caption:=cLineCaption;
  end
  else begin
    nm:=newMI.ClassName;
    newMI.Caption:=newMI.Name;
  end;
  newMI.Name:=FShadowMenu.FEditorDesigner.CreateUniqueComponentName(nm);
  existingSI.RealItem.Parent.Insert(idx, newMI);
  TShadowItem.CreateWithBoxAndItem(FShadowMenu, existingSI.ParentBox, newMI);
  FShadowMenu.UpdateBoxLocationsAndSizes;
  FShadowMenu.FDesigner.FGui.AddingItem := True;
  GlobalDesignHook.PersistentAdded(newMI, not isSeparator);
  GlobalDesignHook.Modified(newMI);
  FShadowMenu.FDesigner.FGui.AddingItem := False;
  FShadowMenu.SetSelectedMenuItem(newMI, False, False);
  if not isSeparator then
    FShadowMenu.FDesigner.FGui.UpdateStatistics;
  FShadowMenu.UpdateActionsEnabledness;
end;

procedure TShadowBox.RemoveAllSeparators;
var
  mi, nearestMI: TMenuItem;
  i, sepCount: integer;
  si: TShadowItemBase;
begin
  if (IsMainMenu and (Self = FShadowMenu.RootBox)) then
    Exit;
  sepCount:=GetChildSeparatorCount(FParentMenuItem);
  if (sepCount > 0) then begin
    FShadowMenu.HideFakes;
    ShowAllUnSelected;
    nearestMI:=FShadowMenu.SelectedMenuItem;
    if assigned(nearestMI) and nearestMI.IsLine then begin
      nearestMI:=GetNextNonSepItem(FShadowMenu.SelectedMenuItem);
      if (nearestMI = nil) then
        nearestMI:=GetPreviousNonSepItem(FShadowMenu.SelectedMenuItem);
    end
    else
      FShadowMenu.SelectedMenuItem := nil;
    if (nearestMI = nil) then
      nearestMI:=FParentMenuItem;
    for i:=ParentMenuItem.Count-1 downto 0 do
    begin
      mi:=ParentMenuItem.Items[i];
      if mi.IsLine then
      begin
        si:=FShadowMenu.GetShadowForMenuItem(mi);
        Assert(si<>nil,'TShadowBox.RemoveAllSeparators: shadow for separator is nil');
        FShadowList.Remove(si);
        Application.ReleaseComponent(si);
        ParentMenuItem.Remove(mi);
        FShadowMenu.FEditorDesigner.PropertyEditorHook.DeletePersistent(TPersistent(mi));
      end;
    end;
    if (ShadowList.Count = 0) then
      FShadowMenu.RemoveEmptyBox(Self)
    else begin
      FShadowMenu.UpdateBoxLocationsAndSizes;
      FShadowMenu.SetSelectedMenuItem(nearestMI, False, True);
      LocateShadows;
    end;
  end;
end;

procedure TShadowBox.LocateShadows;
var
  si: TShadowItemBase;
  len, t, w, h: integer;
begin
  if (ShadowList.Count = 0) then
    Exit;
  FShadowList.Sort(@SortByItemMenuIndex);
  DisableAutoSizing;
  if IsMenuBar then begin
    len:=0;
    for si in FShadowList do begin
      w:=si.GetWidth;
      si.SetBounds(len, 0, w, MenuBar_Height);
      Inc(len, w);
    end;
  end
  else begin
    w:=GetInnerDims.x;
    t:=1;
    for si in FShadowList do begin
      h:=si.GetHeight;
      si.SetBounds(1, t, w, h);
      Inc(t, h);
    end;
  end;
  EnableAutoSizing;
end;

constructor TShadowBox.CreateWithParentBox(aSMenu: TShadowMenu;
  aParentBox: TShadowBox; aParentItem: TMenuItem);
begin
  inherited Create(aSMenu, aParentItem);
  Name := 'ShadowBox' + IntToStr(ShadowBoxID);
  Inc(ShadowBoxID);
  FShadowMenu := aSMenu;
  FParentBox := aParentBox;
  if (FParentBox = nil) then
    FLevel:=0
  else
    FLevel := aParentBox.Level + 1;
  Canvas.Pen.Color := clLtGray;
  Canvas.Brush.Color := clBtnFace;
  FShadowMenu.BoxList.Add(Self);
  Parent := FShadowMenu;
end;

procedure TShadowBox.SetUnCheckedAllExcept(aMI: TMenuItem);
var
  i: integer;
begin
  if (aMI = nil) or (FShadowMenu.GetParentBoxForMenuItem(aMI) <> Self) or
     (FParentMenuItem = nil) then
    Exit;
  for i:=0 to Pred(FParentMenuItem.Count) do
    begin
      if (FParentMenuItem.Items[i] = aMI) then
        Continue;
      if FParentMenuItem.Items[i].RadioItem and
         (FParentMenuItem.Items[i].GroupIndex = aMI.GroupIndex) then
        begin
          FParentMenuItem.Items[i].Checked:=False;
          FShadowMenu.FEditorDesigner.PropertyEditorHook.RefreshPropertyValues;
          FShadowMenu.GetShadowForMenuItem(FParentMenuItem.Items[i]).Invalidate;
        end;
    end;
end;

{ TShadowItem }

function TShadowItem.GetWidth: integer;
var
  w: integer;
begin
  w:=FShadowMenu.GetStringWidth(FRealItem.Caption, FRealItem.Default);
  if FRealItem.IsInMenuBar then
    Result:=w + Double_MenuBar_Text_Offset + FShadowMenu.GetMenuBarIconWidth(FRealItem)
  else
    Result:=w + Double_DropDown_Text_Offset + GetShortcutWidth + Add_Icon_Width;
end;

procedure TShadowItem.Invalidate;
var
  OldHeight, NewHeight: Integer;
begin
  OldHeight := Height;
  NewHeight := GetHeight;
  if OldHeight <> NewHeight then
  begin
    Height := NewHeight;
    FParentBox.LocateShadows;
  end;
  inherited Invalidate;
end;

function TShadowItem.HasChildBox(out aChildBox: TShadowBoxBase): boolean;
begin
  aChildBox:=nil;
  Result:=(FRealItem.Count > 0);
  if Result then begin
    aChildBox:=FShadowMenu.GetBoxWithParentItem(FRealItem);
    Assert(aChildBox<>nil,'TShadowItem.HasChildBox: children exist but not the container for them');
  end;
end;

procedure TShadowItem.RecursiveHideChildren(aMI: TMenuItem);
var
  container: TShadowBoxBase;
  firstChild: TMenuItem;
begin
  container:=FShadowMenu.GetParentBoxForMenuItem(aMI);
  Assert(container<>nil,'TShadowItem.HideChildren: missing parent box for '+aMI.Caption);
  container.Hide;
  if (aMI.Count > 0) then begin
    firstChild:=aMI.Items[0];
    Assert(firstChild<>nil,'TShadowItem.HideChildren: missing child');
    RecursiveHideChildren(firstChild);
  end;
end;

procedure TShadowItem.HideChildren;
var
  child: TMenuItem;
begin
  if (FRealItem.Count > 0) then begin
    child:=FRealItem.Items[0];
    Assert(child<>nil,'TShadowItem.HideChildren: missing child');
    RecursiveHideChildren(child);
  end;
end;

procedure TShadowItem.DblClick;
begin
  inherited DblClick;
  FShadowMenu.AddOnClick(nil);
end;

function TShadowItem.GetIsInMenuBar: boolean;
begin
  Result:=FRealItem.IsInMenuBar;
end;

function TShadowItem.GetIsMainMenu: boolean;
begin
  Result:=FShadowMenu.IsMainMenu;
end;

function TShadowItem.GetLevel: integer;
begin
  Result:=FParentBox.Level;
end;

function TShadowItem.GetBottomFake: TFake;
begin
  Result:=nil;
  if (FShadowMenu.SelectedShadowItem = Self) then
    case FRealItem.IsInMenuBar of
      False: if (FShadowMenu.AddItemFake.Visible) then
               Result:=FBottomFake;
      True: if (FShadowMenu.AddSubMenuFake.Visible) then
              Result:=FBottomFake;
    end;
end;

function TShadowItem.GetRightFake: TFake;
begin
  Result:=nil;
  if (FShadowMenu.SelectedShadowItem = Self) then
    case FRealItem.IsInMenuBar of
      False: if (FShadowMenu.AddSubMenuFake.Visible) then
               Result:=FRightFake;
      True: if FShadowMenu.AddItemFake.Visible then
              Result:=FRightFake;
    end;
end;

function TShadowItem.GetShortcutWidth: integer;
var
  hasSC, hasSC2: boolean;
begin
  Result:=0;
  if FRealItem.IsInMenuBar then
    Exit;
  hasSC:=(FRealItem.ShortCut <> 0);
  if hasSC then
    Inc(Result, FShadowMenu.GetStringWidth(ShortCutToText(FRealItem.ShortCut),
                                           FRealItem.Default));
  hasSC2:=(FRealItem.ShortCutKey2 <> 0);
  if hasSC2 then
    Inc(Result, FShadowMenu.GetStringWidth(ShortCutToText(FRealItem.ShortCutKey2),
                                           FRealItem.Default));
  if (hasSC or hasSC2) then
    Inc(Result, Shortcut_Offset);
  if (hasSC and hasSC2) then
    Inc(Result, FShadowMenu.GetStringWidth(', ', False));
end;

function TShadowItem.GetShowingBottomFake: boolean;
begin
  Result:=(BottomFake <> nil) and BottomFake.Visible;
end;

function TShadowItem.GetShowingRightFake: boolean;
begin
  Result:=(RightFake <> nil) and RightFake.Visible;
end;

function TShadowItem.GetIconTopLeft: TPoint;
begin
  Result:=Point(1, 1);
  if (FShadowMenu.FMenu.Images.Height < ClientHeight) then
    Result.y:=(ClientHeight - FShadowMenu.FMenu.Images.Height) div 2;
  if (FShadowMenu.FMenu.Images.Width < Gutter_X) then
    Result.x:=(Gutter_X - FShadowMenu.FMenu.Images.Width) div 2;
end;

function TShadowItem.GetBitmapLeftTop: TPoint;
begin
  Result:=Point(1, 1);
  if (FRealItem.Bitmap.Height < ClientHeight) then
    Result.y:=(ClientHeight - FRealItem.Bitmap.Height) div 2;
  if (FRealItem.Bitmap.Width < Gutter_X) then
    Result.x:=(Gutter_X - FRealItem.Bitmap.Width) div 2;
end;

function TShadowItem.GetSubImagesIconTopLeft: TPoint;
begin
  Result:=Point(1, 1);
  if (FRealItem.Parent.SubMenuImages.Height < ClientHeight) then
    Result.y:=(ClientHeight - FRealItem.Parent.SubMenuImages.Height) div 2;
  if (FRealItem.Parent.SubMenuImages.Width < Gutter_X) then
    Result.x:=(Gutter_X - FRealItem.Parent.SubMenuImages.Width) div 2;
end;

procedure TShadowItem.Paint;
var
  r, gutterR: TRect;
  textFlags: integer = DT_VCENTER or DT_SINGLELINE or DT_EXPANDTABS or DT_CENTER;
  tStyle: TTextStyle;
  s: string;

  procedure DrawMenuBarItem;
  var
    oldFontStyle: TFontStyles;
    oldFontColor: TColor;
    x, y: integer;
    sz: TSize;
    pt: TPoint;
    dets: TThemedElementDetails;
  begin
    if (FState = dsSelected) then begin
      Canvas.Brush.Color:=clHighlight;
      Canvas.FillRect(r);
      sz:=Canvas.TextExtent(s);
      y:=(r.Bottom - r.Top - sz.cy) div 2;
      x:=(r.Right - r.Left - sz.cx) div 2;
      if FRealItem.HasIcon and (FRealItem.ImageIndex > -1) and (FShadowMenu.FMenu.Images <> nil) then begin
        pt:=GetIconTopLeft;
        FShadowMenu.FMenu.Images.DrawForControl(Canvas, 0, pt.y, FRealItem.ImageIndex, FShadowMenu.FMenu.ImagesWidth, Self);
        Inc(x, MenuBar_Text_Offset);
      end
      else if (FRealItem.Bitmap <> nil) and not FRealItem.Bitmap.Empty then begin
        pt:=GetBitmapLeftTop;
        Canvas.Draw(0, pt.y, RealItem.Bitmap);
        Inc(x, MenuBar_Text_Offset);
      end;
      oldFontStyle:=Canvas.Font.Style;
      if FRealItem.Default then
        Canvas.Font.Style:=[fsBold]
      else Canvas.Font.Style:=[];
      oldFontColor:=Canvas.Font.Color;
      Canvas.Font.Color:=clHighlightText;
      Canvas.TextRect(r, x, y, s, tStyle);
      Canvas.Font.Color:=oldFontColor;
      Canvas.Font.Style:=oldFontStyle;
    end
    else begin
      InflateRect(r, 1, 0); // hack needed only on Windows?
      case FState of
        dsNormal:   dets:=ThemeServices.GetElementDetails(tmBarBackgroundActive);
        dsSelected: dets:=ThemeServices.GetElementDetails(tmBarItemPushed);
        dsDisabled: dets:=ThemeServices.GetElementDetails(tmBarItemDisabled);
      end;
      ThemeServices.DrawElement(Canvas.Handle, dets, r);
      if FRealItem.HasIcon and (FRealItem.ImageIndex > -1) and (FShadowMenu.FMenu.Images <> nil) then
        ThemeServices.DrawIcon(Canvas, dets, Point(0,0), FShadowMenu.FMenu.Images, FRealItem.ImageIndex, 0, Self)
      else if (FRealItem.Bitmap <> nil) and not FRealItem.Bitmap.Empty then begin
        pt:=GetBitmapLeftTop;
        Canvas.Draw(pt.x, pt.y, RealItem.Bitmap);
      end;
      r.Left:=FShadowMenu.GetMenuBarIconWidth(FRealItem);
      if FRealItem.Default then begin
        oldFontStyle:=Canvas.Font.Style;
        Canvas.Font.Style:=[fsBold];
      end;
      ThemeServices.DrawText(Canvas, dets, FRealItem.Caption, r, textFlags, 0);
      if (FState = dsDisabled) then begin // perhaps this display hack is only needed on Windows?
        Canvas.Pen.Color:=clBtnShadow;
        Canvas.Line(0, MenuBar_Height-1, ClientWidth, MenuBar_Height-1);
      end;
      if FRealItem.Default then
        Canvas.Font.Style:=oldFontStyle;
    end;
  end;

  procedure DrawBackgroundAndGutter;
  begin
    case FState of
      dsNormal, dsDisabled: Canvas.Brush.Color:=clBtnFace;
      dsSelected: Canvas.Brush.Color:=clHighlight;
    end;
    if FRealItem.IsLine and (FState = dsSelected) then
      Canvas.FillRect(r.Left, r.Top+2, r.Right, r.Bottom+2)
    else
      Canvas.FillRect(r);
    gutterR:=Rect(Gutter_X, 0, Gutter_X+1, ClientHeight);
    LCLIntf.DrawEdge(Canvas.Handle, gutterR, EDGE_ETCHED, BF_LEFT);
  end;

  procedure DrawCheckMarkIcon;
  var
    pt: TPoint;
    dets: TThemedElementDetails;
  begin
    if FRealItem.Checked then begin
      gutterR:=r;
      gutterR.Right:=Gutter_X;
      if FRealItem.RadioItem then       // radioItem
        case FState of
          dsNormal: begin
  	      dets:=ThemeServices.GetElementDetails(tmPopupCheckBackgroundNormal);
  	      ThemeServices.DrawElement(Canvas.Handle, dets, gutterR);
  	      dets:=ThemeServices.GetElementDetails(tmPopupBulletNormal);
  	      ThemeServices.DrawElement(Canvas.Handle, dets, gutterR);
  	    end;
          dsSelected: begin
              dets:=ThemeServices.GetElementDetails(tmPopupItemHot);
              ThemeServices.DrawElement(Canvas.Handle, dets, gutterR);
              dets:=ThemeServices.GetElementDetails(tmPopupBulletNormal);
              ThemeServices.DrawElement(Canvas.Handle, dets, gutterR);
            end;
          dsDisabled: begin
              dets:=ThemeServices.GetElementDetails(tmPopupCheckBackgroundDisabled);
  	      ThemeServices.DrawElement(Canvas.Handle, dets, gutterR);
  	      dets:=ThemeServices.GetElementDetails(tmPopupBulletDisabled);
  	      ThemeServices.DrawElement(Canvas.Handle, dets, gutterR);
            end;
        end
      else begin                              // checkmark
  	dets:=ThemeServices.GetElementDetails(tmPopupCheckBackgroundNormal);
  	ThemeServices.DrawElement(Canvas.Handle, dets, gutterR);
  	dets:=ThemeServices.GetElementDetails(tmPopupCheckMarkNormal);
  	ThemeServices.DrawElement(Canvas.Handle, dets, gutterR);
      end;
    end
    else                                     // not checked
      if FRealItem.HasIcon and (FRealItem.GlyphShowMode<>gsmNever) and
        (FRealItem.ImageIndex > -1) and (FShadowMenu.FMenu.Images <> nil) and
        (FRealItem.ImageIndex < FShadowMenu.FMenu.Images.Count) then
          ThemeServices.DrawIcon(Canvas, dets, GetIconTopLeft,
                                 FShadowMenu.FMenu.Images, FRealItem.ImageIndex, 0, Self)
      else
        if (FRealItem.ImageIndex > -1) and (FParentBox.Level > 0) and
          (FRealItem.Parent.SubMenuImages <> nil) and
          (FRealItem.ImageIndex < FRealItem.Parent.SubMenuImages.Count) then
            ThemeServices.DrawIcon(Canvas, dets, GetSubImagesIconTopLeft,
                                   RealItem.Parent.SubMenuImages, RealItem.ImageIndex, 0, Self)
        else if FRealItem.HasBitmap and not FRealItem.Bitmap.Empty then begin
  	  pt:=GetBitmapLeftTop;
  	  Canvas.Draw(pt.x, pt.y, RealItem.Bitmap);
        end;
  end;

  procedure DrawText;
  var
    oldFontColor: TColor;
    oldFontStyle: TFontStyles;
    s1, s2: string;
    sc1, sc2: boolean;
    x, y: integer;
  begin
    Canvas.Brush.Style:=bsClear;
    if FRealItem.RightJustify then
      textFlags:=textFlags or DT_RIGHT
    else
      textFlags:=textFlags or DT_LEFT;
    r.Left:=DropDown_Text_Offset;
    oldFontStyle:=Canvas.Font.Style;
    if FRealItem.Default then
      Canvas.Font.Style:=[fsBold]
    else Canvas.Font.Style:=[];
    x:=DropDown_Text_Offset;
    y:=(Height-Canvas.TextHeight(s)) div 2;
    case FState of
      dsNormal: Canvas.TextRect(r, x, y, s, tStyle);
      dsSelected: begin
          OldFontColor:=Canvas.Font.Color;
          Canvas.Font.Color:=clHighlightText;
          Canvas.TextRect(r, x, y, s, tStyle);
          Canvas.Font.Color:=oldFontColor;
        end;
      dsDisabled: begin
          OldFontColor:=Canvas.Font.Color;
          Canvas.Font.Color:=clBtnShadow;
          Canvas.TextRect(r, x, y, s, tStyle);
          Canvas.Font.Color:=OldFontColor;
        end;
    end;

    sc1:=(FRealItem.ShortCut <> 0);
    if sc1 then
      s1:=ShortCutToText(FRealItem.Shortcut);
    sc2:=(FRealItem.ShortCutKey2 <> 0);
    if sc2 then
      s2:=ShortCutToText(FRealItem.ShortCutKey2);
    if sc1 or sc2 then    //#todo allow for rightjustify?
    begin
      if sc1 and not sc2 then
        s:=s1
      else if sc2 and not sc1 then
        s:=s2
      else
        s:=s1 + ', ' + s2;
      x:=r.Right - Canvas.TextWidth(s) - DropDown_Height;
      case FState of
        dsNormal: Canvas.TextRect(r, x, y, s, tStyle);
        dsSelected: begin
            OldFontColor:=Canvas.Font.Color;
            Canvas.Font.Color:=clHighlightText;
            Canvas.TextRect(r, x, y, s, tStyle);
            Canvas.Font.Color:=oldFontColor;
          end;
        dsDisabled: begin
            OldFontColor:=Canvas.Font.Color;
            Canvas.Font.Color:=clBtnShadow;
            Canvas.TextRect(r, x, y, s, tStyle);
            Canvas.Font.Color:=OldFontColor;
          end;
      end;
    end;
    Canvas.Font.Style:=oldFontStyle;
  end;

  procedure DrawChevron;
  var
    pts: array of TPoint;
    oldBrushColor, oldPenColor: TColor;
  begin
    { ToDo: This should be done by theme services
            but it must be implemented for different widgetsets first.
    dets:=ThemeServices.GetElementDetails(tmPopupSubmenuNormal);
    ThemeServices.DrawElement(Canvas.Handle, dets, r);
    }
    r.Right:=ClientWidth;
    r.Left:=r.Right - MenuBar_Height;
    SetLength(pts{%H-}, 4);
    pts[0]:=Point(r.Left, ScaleY(9, 96));
    pts[1]:=Point(r.Left + ScaleX(4, 96), ScaleY(12, 96));
    pts[2]:=Point(r.Left, ScaleY(15, 96));
    pts[3]:=pts[0];
    oldBrushColor:=Canvas.Brush.Color;
    oldPenColor:=Canvas.Pen.Color;
    if (FState = dsSelected) then begin
      Canvas.Pen.Color:=clHighlightText;
      Canvas.Brush.Color:=clHighlightText;
    end
    else begin
      Canvas.Brush.Color:=clBlack;
      Canvas.Pen.Color:=clBlack;
    end;
    Canvas.Polygon(pts);
    Canvas.Brush.Color:=oldBrushColor;
    Canvas.Pen.Color:=oldPenColor;
  end;

var
  alygn: TAlignment;
begin
  if FParentBox.Updating then Exit;
  r:=ClientRect;
  if FRealItem.RightJustify then
    alygn:=taRightJustify
  else
    alygn:=taLeftJustify;
  if (FRealItem.Caption = '') then
    s:=FRealItem.Name
  else
    s:=FRealItem.Caption;
  FillChar(tStyle{%H-}, SizeOf(tStyle), 0);
  with tStyle do begin
    Alignment:=BidiFlipAlignment(alygn, UseRightToLeftAlignment);
    Layout:=tlCenter;
    SingleLine:=True;
    Clipping:=True;
    ShowPrefix:=True;
    RightToLeft:=UseRightToLeftReading;
    ExpandTabs:=True;
  end;
  if FRealItem.IsInMenuBar then
    DrawMenuBarItem
  else begin
    DrawBackgroundAndGutter;
    if FRealItem.IsLine then begin
      gutterR:=Rect(Gutter_X, Separator_Centre, ClientWidth, Separator_Centre);
      LCLIntf.DrawEdge(Canvas.Handle, gutterR, EDGE_ETCHED, BF_TOP);
      Exit;
    end;
    if (FRealItem.Checked or FRealItem.HasIcon) then
      DrawCheckMarkIcon;
    DrawText;
    if (FRealItem.Count > 0) then
      DrawChevron;
  end;
end;

procedure TShadowItem.KeyDown(var Key: Word; Shift: TShiftState);
begin
  if (Shift = []) then
    case Key of
      VK_LEFT: begin
        if IsInMenuBar then
          FParentBox.SelectPrevious(Self)
        else if (IsMainMenu and (Level > 1)) or (not IsMainMenu and (level > 0)) then
          FShadowMenu.SetSelectedMenuItem(FParentBox.ParentMenuItem, False, False);
        Key:=0;
      end;
      VK_RIGHT: begin
        if IsInMenuBar then
          FParentBox.SelectSuccessor(Self)
        else if (FRealItem.Count > 0) then begin
          ShowChildBox;
          FShadowMenu.SetSelectedMenuItem(FRealItem.Items[0], False, False);
        end;
        Key:=0;
      end;
      VK_DOWN: begin
        if IsInMenuBar and (FRealItem.Count > 0) then begin
          ShowChildBox;
          FShadowMenu.SetSelectedMenuItem(FRealItem.Items[0], False, False);
        end
        else FParentBox.SelectSuccessor(Self);
        Key:=0;
      end;
      VK_UP: begin
        if (FRealItem.MenuIndex = 0) and FParentBox.ParentMenuItem.IsInMenuBar then
          FShadowMenu.SetSelectedMenuItem(FParentBox.ParentMenuItem, False, False)
        else if not IsInMenuBar then
          FParentBox.SelectPrevious(Self);
        Key:=0;
      end;
      VK_DELETE: begin
        Key:=0;
        FShadowMenu.DeleteItem(Self);
      end;
      else inherited KeyDown(Key, Shift);
    end // case
  else inherited KeyDown(Key, Shift);
end;

procedure TShadowItem.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if Button = mbLeft then begin
    FRealItem.Click;
    FShadowMenu.FEditorDesigner.PropertyEditorHook.RefreshPropertyValues;
  end;
  if (FState = dsSelected) then
    SetFocus
  else
    FShadowMenu.SetSelectedMenuItem(FRealItem, False, False);
  inherited MouseDown(Button, Shift, X, Y);
end;

procedure TShadowItem.ShowChainToRoot;
var
  sb: TShadowBoxBase;
begin
  sb:=FParentBox;
  while (sb <> FShadowMenu.RootBox) do begin
    sb.Show;
    sb:=sb.ParentBox;
  end;
end;

procedure TShadowItem.HideChainFromRoot;
var
  sb: TShadowBoxBase;
begin
  sb:=FParentBox;
  while (sb <> FShadowMenu.RootBox) do begin
    sb.Hide;
    sb:=sb.ParentBox;
  end;
end;

procedure TShadowItem.ShowChildBox;
var
  sb: TShadowBoxBase;
begin
  if HasChildBox(sb) then
    sb.Show;
end;

constructor TShadowItem.CreateWithBoxAndItem(aSMenu: TShadowMenu;
  aParentBox: TShadowBox; aRealItem: TMenuItem);
begin
  inherited Create(aParentBox, aRealItem);
  Name:='ShadowItem' + IntToStr(ShadowItemID);
  Inc(ShadowItemID);
  FShadowMenu:=aSMenu;
  FParentBox:=aParentBox;
  FParentBox.ShadowList.Add(Self);
  Canvas.Brush.Color:=clBtnFace;
  SetInitialBounds(0, 0, GetWidth, GetHeight);
  if FRealItem.Enabled then
    FState:=dsNormal
  else
    FState:=dsDisabled;
  TabStop:=False;
  TabOrder:= -1;
  PopupMenu:=FShadowMenu.ItemsPopupMenu;
  Parent:=FParentBox;
  FParentBox.LocateShadows;
end;

{ TMenuDesigner }

constructor TMenuDesigner.Create;
begin
  inherited Create;
  FGui:=TMenuDesignerForm.Create(Self);
end;

destructor TMenuDesigner.Destroy;
begin
  FreeAndNil(FGui);
  inherited Destroy;
end;

procedure TMenuDesigner.CreateShadowMenu(aMenu: TMenu; aSelect: TMenuItem;
  aWidth, aHeight: integer);
begin
  FShadowMenu := TShadowMenu.Create(Self, FGui, aMenu, aSelect, aWidth, aHeight);
end;

{ TMainMenuComponentEditor}

procedure TMainMenuComponentEditor.Edit;
begin
  ShowMenuEditor(Component as TMenu);
end;

function TMainMenuComponentEditor.GetVerbCount: Integer;
begin
  Result:=1;
end;

function TMainMenuComponentEditor.GetVerb(Index: Integer): string;
begin
  case Index of
    0: Result:=lisMenuEditorMenuEditor + ' ...';
    else Result:='';
  end;
end;

procedure TMainMenuComponentEditor.ExecuteVerb(Index: Integer);
begin
  if (Index = 0) then
    Edit;
end;


initialization
  RegisterComponentEditor(TMenu, TMainMenuComponentEditor);

finalization
  FreeAndNil(MenuDesignerSingleton);

end.

