1unit MenuEditor; 2 3{$mode objfpc}{$H+} 4 5interface 6 7uses 8 // FCL 9 Classes, SysUtils, Types, typinfo, strutils, 10 // LazUtils 11 LazLogger, 12 // LCL 13 ActnList, Controls, Dialogs, StdCtrls, ExtCtrls, Menus, 14 Forms, Graphics, ImgList, Themes, LCLType, LCLIntf, LCLProc, 15 // IdeIntf 16 FormEditingIntf, IDEWindowIntf, IDEImagesIntf, ComponentEditors, IDEDialogs, 17 PropEdits, 18 // IDE 19 LazarusIDEStrConsts, LazIDEIntf, MenuDesignerBase, MenuEditorForm, MenuShortcutDisplay, 20 MenuTemplates, MenuResolveConflicts; 21 22type 23 24 TShadowMenu = class; 25 TShadowBox = class; 26 27 { TFake } 28 29 TFake = class(TCustomControl) 30 private 31 FShadowMenu: TShadowMenu; 32 FMinWidth: integer; 33 protected 34 function GetShouldBeVisible: boolean; virtual; abstract; 35 procedure SetVisibilitySizeAndPosition; virtual; abstract; 36 procedure TextChanged; override; 37 procedure Paint; override; 38 class function GetControlClassDefaultSize: TSize; override; 39 public 40 constructor Create(anOwner: TShadowMenu); reintroduce; 41 procedure Refresh; 42 property ShouldBeVisible: boolean read GetShouldBeVisible; 43 end; 44 45 TAddSiblingFake = class(TFake) 46 protected 47 function GetShouldBeVisible: boolean; override; 48 procedure SetVisibilitySizeAndPosition; override; 49 end; 50 51 TAddSubmenuFake = class(TFake) 52 protected 53 function GetShouldBeVisible: boolean; override; 54 procedure SetVisibilitySizeAndPosition; override; 55 end; 56 57 TAddFirstFake = class(TFake) 58 protected 59 function GetShouldBeVisible: boolean; override; 60 procedure SetVisibilitySizeAndPosition; override; 61 end; 62 63 TMenuDesigner = class; 64 65 { TShadowItem } 66 67 TShadowItem = class(TShadowItemBase) 68 strict private 69 FBottomFake: TFake; 70 FParentBox: TShadowBox; 71 FRightFake: TFake; 72 FShadowMenu: TShadowMenu; 73 FShowingBottomFake: boolean; 74 FShowingRightFake: boolean; 75 function GetBitmapLeftTop: TPoint; 76 function GetBottomFake: TFake; 77 function GetIconTopLeft: TPoint; 78 function GetIsInMenuBar: boolean; 79 function GetIsMainMenu: boolean; 80 function GetLevel: integer; 81 function GetRightFake: TFake; 82 function GetShortcutWidth: integer; 83 function GetShowingBottomFake: boolean; 84 function GetShowingRightFake: boolean; 85 function GetSubImagesIconTopLeft: TPoint; 86 procedure RecursiveHideChildren(aMI: TMenuItem); 87 private 88 function HasChildBox(out aChildBox: TShadowBoxBase): boolean; 89 procedure HideChainFromRoot; 90 procedure HideChildren; 91 procedure ShowChainToRoot; 92 procedure ShowChildBox; 93 protected 94 procedure DblClick; override; 95 procedure KeyDown(var Key: Word; Shift: TShiftState); override; 96 procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; 97 procedure Paint; override; 98 public 99 constructor CreateWithBoxAndItem(aSMenu: TShadowMenu; aParentBox: TShadowBox; 100 aRealItem: TMenuItem); 101 function GetWidth: integer; override; 102 procedure Invalidate; override; 103 public 104 property BottomFake: TFake read GetBottomFake write FBottomFake; 105 property IsInMenuBar: boolean read GetIsInMenuBar; 106 property IsMainMenu: boolean read GetIsMainMenu; 107 property Level: integer read GetLevel; 108 property ParentBox: TShadowBox read FParentBox; 109 property RightFake: TFake read GetRightFake write FRightFake; 110 property ShowingBottomFake: boolean read GetShowingBottomFake write FShowingBottomFake; 111 property ShowingRightFake: boolean read GetShowingRightFake write FShowingRightFake; 112 end; 113 114 { TShadowBox } 115 116 TShadowBox = class(TShadowBoxBase) 117 strict private 118 FShadowMenu: TShadowMenu; 119 FUpdating: boolean; 120 procedure BeginUpdate; 121 procedure EndUpdate; 122 procedure ShowAllUnSelected; 123 private 124 procedure AddItemAndShadow(existingSI: TShadowItem; addBefore: boolean; 125 isSeparator: boolean=False); 126 procedure LocateShadows; 127 procedure RemoveAllSeparators; 128 procedure SelectPrevious(aSI: TShadowItem); 129 procedure SelectSuccessor(aSI: TShadowItem); 130 property Updating: boolean read FUpdating; 131 protected 132 function GetIsMainMenu: boolean; override; 133 function GetIsMenuBar: boolean; override; 134 procedure Paint; override; 135 public 136 constructor CreateWithParentBox(aSMenu: TShadowMenu; aParentBox: TShadowBox; 137 aParentItem: TMenuItem); 138 procedure SetUnCheckedAllExcept(aMI: TMenuItem); 139 end; 140 141 TPopEnum = {%region} 142 (popItemMoveBefore, popItemMoveAfter, 143 popSeparators_, 144 popAddSeparatorBefore, popAddSeparatorAfter, popRemoveAllSeparators, 145 popItemDelete, popItemAddBefore, popItemAddAfter, popItemAddSubMenu, 146 popItemSep, 147 popAddImgListIcon, popItemAddOnClick, popItemEditCaption, 148 popItemOISep, 149 popShortcuts_, 150 popListShortcuts, popListShortcutsAccelerators, popResolveShortcutConflicts, 151 popTemplates_, 152 popSaveAsTemplate, popAddFromTemplate, popDeleteTemplate);{%endregion} 153 154 { TShadowMenu } 155 156 TShadowMenu = class(TShadowMenuBase) 157 strict private 158 FActionList: TActionList; 159 FAddImgListIconAction: TAction; 160 FAddItemFake: TFake; 161 FAddFirstItemFake: TFake; 162 FAddSubmenuFake: TFake; 163 FInitialising: boolean; 164 FInitialSelectedMenuItem: TMenuItem; 165 FItemsPopupMenu: TPopupMenu; 166 FRootBox: TShadowBox; 167 FInPlaceEditor: TEdit; 168 FEditedMenuItem: TMenuItem; 169 procedure DeleteBox(aMI: TMenuItem); 170 procedure DeleteItm(anItem: TMenuItem); 171 function GetActionForEnum(anEnum: TPopEnum): TAction; 172 function GetMaxVisibleBoxDims(aSB: TShadowBox): TPoint; 173 function GetMaxVisibleFakeDims: TPoint; 174 function GetMenuBarCumWidthForItemIndex(anIndex: integer): integer; 175 function GetParentItemHeightInBox(aParentItem: TMenuItem): integer; 176 function GetSelectedShadowBox: TShadowBox; 177 function GetSelectedShadowItem: TShadowItem; 178 procedure AddManyItems(aPrimaries, aDepth: integer); 179 procedure AddSubMenuTo(anExistingSI: TShadowItem); 180 procedure ConnectSpeedButtonOnClickMethods; 181 procedure CreateShadowBoxesAndItems; 182 procedure DeleteChildlessShadowAndItem(anExistingSI: TShadowItem); 183 procedure DeleteShadowAndItemAndChildren(anExistingSI: TShadowItem); 184 procedure InPlaceEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); 185 procedure OnDesignerModified(Sender: TObject); 186 procedure OnObjectPropertyChanged(Sender: TObject; NewObject: TPersistent); 187 procedure OnDesignerRefreshPropertyValues; 188 procedure RecursiveCreateShadows(aParentBox: TShadowBox; aMI: TMenuItem); 189 procedure SetupPopupMenu; 190 procedure StopEditingCaption; 191 procedure UpdateButtonGlyphs(isInBar: boolean); 192 // user actions 193 procedure AddFromTemplate(Sender: TObject); 194 procedure AddImageListIcon(Sender: TObject); 195 procedure AddItemAfter(Sender: TObject); 196 procedure AddItemBefore(Sender: TObject); 197 procedure AddSeparatorAbove(Sender: TObject); 198 procedure AddSeparatorBelow(Sender: TObject); 199 procedure AddSubMenu(Sender: TObject); 200 procedure AddFirstMenu(Sender: TObject); 201 procedure DeleteTemplate(Sender: TObject); 202 procedure EditCaption(Sender: TObject); 203 procedure ListShortcuts(Sender: TObject); 204 procedure ListShortcutsAndAccelerators(Sender: TObject); 205 procedure MoveItemAfter(Sender: TObject); 206 procedure MoveItemBefore(Sender: TObject); 207 procedure RemoveAllSeparators(Sender: TObject); 208 procedure ResolveShortcutConflicts(Sender: TObject); 209 procedure SaveAsTemplate(Sender: TObject); 210 private 211 FDesigner: TMenuDesigner; 212 function GetMenuBarIconWidth(aMI: TMenuItem): integer; 213 function OnClickIsAssigned(aMI: TMenuItem): boolean; 214 procedure AddOnClick(Sender: TObject); 215 procedure DeleteItem(Sender: TObject); 216 function GetBoxWithParentItem(aParentMI: TMenuItem): TShadowBoxBase; 217 procedure HideFakes; 218 procedure RemoveEmptyBox(aSB: TShadowBox); 219 procedure SetSelectedShadow(const prevSelectedItem, curSelectedItem: TMenuItem; viaDesigner: boolean); 220 procedure UpdateActionsEnabledness; 221 private 222 property AddItemFake: TFake read FAddItemFake; 223 property AddSubmenuFake: TFake read FAddSubmenuFake; 224 property ItemsPopupMenu: TPopupMenu read FItemsPopupMenu; 225 property RootBox: TShadowBox read FRootBox; 226 protected 227 procedure Paint; override; 228 procedure SetParent(NewParent: TWinControl); override; 229 public 230 constructor Create(aDesigner: TMenuDesigner; aForm: TForm; aMenu: TMenu; 231 aSelect: TMenuItem; aWidth, aHeight: integer); reintroduce; 232 destructor Destroy; override; 233 procedure HideBoxesAboveLevel(aLevel: integer); 234 procedure RefreshFakes; override; 235 procedure SetSelectedMenuItem(aMI: TMenuItem; 236 viaDesigner, prevWasDeleted: boolean); override; 237 procedure UpdateBoxLocationsAndSizes; override; 238 procedure UpdateSelectedItemInfo; 239 public 240 property SelectedShadowBox: TShadowBox read GetSelectedShadowBox; 241 property SelectedShadowItem: TShadowItem read GetSelectedShadowItem; 242 end; 243 244 { TMenuDesigner } 245 246 TMenuDesigner = class(TMenuDesignerBase) 247 private 248 FGui: TMenuDesignerForm; 249 public 250 constructor Create; 251 destructor Destroy; override; 252 procedure CreateShadowMenu(aMenu: TMenu; aSelect: TMenuItem; 253 aWidth, aHeight: integer); override; 254 end; 255 256 { TMenuComponentEditor - the default component editor for TMenu } 257 258 TMainMenuComponentEditor = class(TComponentEditor) 259 public 260 procedure Edit; override; 261 function GetVerbCount: Integer; override; 262 function GetVerb(Index: Integer): string; override; 263 procedure ExecuteVerb(Index: Integer); override; 264 end; 265 266procedure ShowMenuEditor(aMenu: TMenu); 267function MenuDesigner: TMenuDesigner; 268 269 270implementation 271 272const 273 Shortcut_Offset = 23; 274 Popup_Origin: TPoint = (x:15; y:15); 275 276var 277 ShadowItemID: integer = 0; 278 ShadowBoxID: integer = 0; 279 280 MenuDesignerSingleton: TMenuDesigner = nil; 281 282procedure ShowMenuEditor(aMenu: TMenu); 283begin 284 if (aMenu = nil) then 285 RaiseGDBException(lisMenuEditorShowMenuEditorTMenuParameterIsNil); 286 MenuDesigner.FGui.SetMenu(aMenu, nil); 287 SetPopupModeParentForPropertyEditor(MenuDesigner.FGui); 288 MenuDesigner.FGui.ShowOnTop; 289end; 290 291function MenuDesigner: TMenuDesigner; // refer always to a single instance 292begin 293 if (MenuDesignerSingleton = nil) then 294 MenuDesignerSingleton:=TMenuDesigner.Create; 295 Result:=MenuDesignerSingleton; 296end; 297 298// utility functions 299{ 300function ItemStateToStr(aState: TShadowItemDisplayState): string; 301begin 302 Result:=GetEnumName(TypeInfo(TShadowItemDisplayState), Ord(aState)); 303end; 304} 305function GetPreviousNonSepItem(aMI: TMenuItem): TMenuItem; 306var 307 idx: integer; 308begin 309 Result:=nil; 310 idx:=aMI.MenuIndex; 311 if (idx = 0) then 312 Exit 313 else repeat 314 idx:=Pred(idx); 315 Result:=aMI.Parent.Items[idx]; 316 until not Result.IsLine or (idx = 0); 317 if Result.IsLine then 318 Result:=nil; 319end; 320 321function GetPreviousItem(aMI: TMenuItem): TMenuItem; 322var 323 idx: integer; 324begin 325 idx:=aMI.MenuIndex; 326 if (idx = 0) then 327 Exit(nil) 328 else 329 Result:=aMI.Parent.Items[Pred(idx)]; 330end; 331 332function GetNextItem(aMI: TMenuItem): TMenuItem; 333var 334 idx: integer; 335begin 336 idx:=aMI.MenuIndex; 337 if (idx = Pred(aMI.Parent.Count)) then 338 Exit(nil) 339 else 340 Result:=aMI.Parent.Items[Succ(idx)]; 341end; 342 343function GetNextNonSepItem(aMI: TMenuItem): TMenuItem; 344var 345 idx, maxIdx: integer; 346begin 347 Result:=nil; 348 idx:=aMI.MenuIndex; 349 maxIdx:=Pred(aMI.Parent.Count); 350 if (idx = maxIdx) then 351 Exit 352 else repeat 353 idx:=Succ(idx); 354 Result:=aMI.Parent.Items[idx]; 355 until not Result.IsLine or (idx = maxIdx); 356 if Result.IsLine then 357 Result:=nil; 358end; 359 360function PreviousItemIsSeparator(aMI: TMenuItem): boolean; 361var 362 idx: integer; 363begin 364 if (aMI = nil) then 365 Exit(False); 366 idx:=aMI.MenuIndex; 367 Result:=(idx > 0) and aMI.Parent.Items[Pred(idx)].IsLine; 368end; 369 370function NextItemIsSeparator(aMI: TMenuItem): boolean; 371var 372 idx: integer; 373begin 374 if (aMI = nil) then 375 Exit(False); 376 idx:=aMI.MenuIndex; 377 Result:=(idx < Pred(aMI.Parent.Count)) and aMI.Parent.Items[Succ(idx)].IsLine; 378end; 379 380function GetChildSeparatorCount(aMI: TMenuItem): integer; 381var 382 i: integer; 383begin 384 Result:=0; 385 for i:=0 to aMI.Count-1 do 386 if aMI.Items[i].IsLine then 387 Inc(Result); 388end; 389 390function AIsDescendantOfB(miA, miB: TMenuItem): boolean; 391var 392 tmp: TMenuItem; 393begin 394 if (miA = nil) or (miB = nil) then 395 Exit(False); 396 tmp:=miA.Parent; 397 repeat 398 if (tmp = miB) then 399 Exit(True); 400 tmp:=tmp.Parent; 401 until (tmp = nil); 402 Result:=False; 403end; 404 405function LevelZeroAndNoGrandchildren(aMI: TMenuItem): boolean; 406var 407 i: integer; 408begin 409 Result:=(aMI.Parent <> nil) and (aMI.Parent.Parent = nil); 410 if Result then 411 for i:=0 to aMI.Count-1 do 412 if (aMI.Items[i].Count > 0) then 413 Exit(False); 414end; 415 416function SortByItemMenuIndex(const Item1, Item2: TShadowItemBase): Integer; 417var 418 i1, i2: integer; 419begin 420 i1:=Item1.RealItem.MenuIndex; 421 i2:=Item2.RealItem.MenuIndex; 422 if (i1 > i2) then 423 Result:=1 424 else if (i2 > i1) then 425 Result:= -1 426 else 427 Result:=0; 428end; 429 430function SortByBoxLevel(const Item1, Item2: TShadowBoxBase): Integer; 431var 432 lvl1, lvl2: integer; 433begin 434 lvl1:=Item1.Level; 435 lvl2:=Item2.Level; 436 if (lvl1 > lvl2) then 437 Result:=1 438 else if (lvl1 < lvl2) then 439 Result:= -1 440 else 441 Result:=0; 442end; 443 444{ TAddFirstFake } 445 446function TAddFirstFake.GetShouldBeVisible: boolean; 447begin 448 Result:=(FShadowMenu.FMenu<>nil) and (FShadowMenu.FMenu.Items.Count=0); 449end; 450 451procedure TAddFirstFake.SetVisibilitySizeAndPosition; 452begin 453 if ShouldBeVisible then begin 454 SetBounds(Left, Top, FMinWidth, DropDown_Height); 455 Show; 456 end 457 else begin 458 Hide; 459 end; 460end; 461 462{ TAddSubmenuFake } 463 464function TAddSubmenuFake.GetShouldBeVisible: boolean; 465var 466 item: TMenuItem; 467begin 468 item:=FShadowMenu.SelectedMenuItem; 469 if (item = nil) then 470 Exit(False) 471 else 472 Result:=not item.IsLine and (item.Count = 0); 473end; 474 475procedure TAddSubmenuFake.SetVisibilitySizeAndPosition; 476var 477 selShadow: TShadowItem; 478 selMI: TMenuItem; 479 w: integer; 480begin 481 selMI:=FShadowMenu.SelectedMenuItem; 482 if (selMI=nil) then 483 Exit; 484 selShadow:=TShadowItem(FShadowMenu.GetShadowForMenuItem(selMI)); 485 if selShadow=nil then Exit; 486 if not ShouldBeVisible then begin 487 if selMI.IsInMenuBar then 488 selShadow.BottomFake:=nil 489 else 490 selShadow.RightFake:=nil; 491 Hide; 492 end 493 else begin 494 w:=FMinWidth; 495 if selMI.IsInMenuBar then begin 496 if (selShadow.Width > w) then 497 w:=selShadow.Width; 498 SetBounds(selShadow.Left, MenuBar_Height + 1, w, DropDown_Height); 499 selShadow.ShowingBottomFake:=True; 500 selShadow.BottomFake:=Self; 501 selShadow.ShowingRightFake:=False; 502 end 503 else begin 504 SetBounds(selShadow.ParentBox.Left + selShadow.BoundsRect.Right + 1, 505 selShadow.ParentBox.Top + selShadow.Top, w, DropDown_Height); 506 selShadow.ShowingRightFake:=True; 507 selShadow.RightFake:=Self; 508 selShadow.ShowingBottomFake:=False; 509 end; 510 Show; 511 end; 512end; 513 514{ TAddSiblingFake } 515 516function TAddSiblingFake.GetShouldBeVisible: boolean; 517var 518 item: TMenuItem; 519begin 520 item:=FShadowMenu.SelectedMenuItem; 521 if (item = nil) then 522 Exit(False) 523 else 524 Result:=(item.MenuIndex = Pred(item.Parent.Count)); 525end; 526 527procedure TAddSiblingFake.SetVisibilitySizeAndPosition; 528var 529 selShadow: TShadowItem; 530 selMI: TMenuItem; 531 w: integer; 532begin 533 selMI:=FShadowMenu.SelectedMenuItem; 534 if (selMI=nil) then 535 Exit; 536 selShadow:=TShadowItem(FShadowMenu.GetShadowForMenuItem(selMI)); 537 if selShadow=nil then Exit; 538 if not ShouldBeVisible then begin 539 if selMI.IsInMenuBar then 540 selShadow.RightFake:=nil 541 else 542 selShadow.BottomFake:=nil; 543 Hide; 544 end 545 else begin 546 if selMI.IsInMenuBar then begin 547 SetBounds(selShadow.Left + selShadow.Width + 1, 0, FMinWidth, MenuBar_Height); 548 selShadow.ShowingRightFake:=True; 549 selShadow.RightFake:=Self; 550 selShadow.ShowingBottomFake:=False; 551 end 552 else begin 553 w:=selShadow.ParentBox.Width - Gutter_X - 1; 554 if (FMinWidth > w) then 555 w:=FMinWidth; 556 SetBounds(selShadow.ParentBox.Left + selShadow.Left + Gutter_X, 557 selShadow.ParentBox.Top + selShadow.ParentBox.Height + 1, 558 w, DropDown_Height); 559 selShadow.ShowingBottomFake:=True; 560 selShadow.BottomFake:=Self; 561 selShadow.ShowingRightFake:=False; 562 end; 563 Invalidate; 564 Show; 565 end; 566end; 567 568{ TFake } 569 570constructor TFake.Create(anOwner: TShadowMenu); 571begin 572 inherited Create(anOwner); 573 FShadowMenu:=anOwner; 574 with GetControlClassDefaultSize do 575 SetInitialBounds(0, 0, cx, cy); 576 BorderStyle:=bsNone; 577 Visible:=False; 578 Canvas.Pen.Color:=clBtnText; 579 Canvas.Pen.Style:=psDot; 580 Canvas.Font.Color:=clBtnText; 581 Canvas.Brush.Color:=clBtnFace; 582 Parent:=anOwner; 583end; 584 585class function TFake.GetControlClassDefaultSize: TSize; 586begin 587 Result.cx:=100; 588 Result.cy:=DropDown_Height; 589end; 590 591procedure TFake.Paint; 592var 593 r: TRect; 594 TextSize: TSize; 595 TextPoint, AddBmpPoint: TPoint; 596 AddBmp: TImageIndex; 597 IL: TLCLGlyphs; 598 Res: TScaledImageListResolution; 599begin 600 r:=ClientRect; 601 Canvas.FillRect(r); 602 Canvas.RoundRect(r, 3, 3); 603 IL:=IDEImages.Images_16; 604 AddBmp:=IL.GetImageIndex('laz_add'); 605 Res:=IL.ResolutionForControl[0, Self]; 606 TextSize:=Canvas.TextExtent(Caption); 607 TextPoint.y:=(r.Bottom - r.Top - TextSize.cy) div 2; 608 if (TextPoint.y < 1) then 609 TextPoint.y:=1; 610 TextPoint.x:=(r.Right - r.Left - TextSize.cx + Res.Width) div 2; 611 Canvas.TextRect(r, TextPoint.x, TextPoint.y, Caption); 612 613 AddBmpPoint.x:=(TextPoint.x - Res.Width) div 2; 614 AddBmpPoint.y:=(r.Bottom - r.Top - Res.Height) div 2; 615 Res.Draw(Canvas, AddBmpPoint.x, AddBmpPoint.y, AddBmp); 616end; 617 618procedure TFake.Refresh; 619begin 620 SetVisibilitySizeAndPosition; 621end; 622 623procedure TFake.TextChanged; 624begin 625 inherited TextChanged; 626 FMinWidth:=FShadowMenu.GetStringWidth(Caption, False) + 627 Double_MenuBar_Text_Offset + 628 Add_Icon_Width; 629end; 630 631{ TShadowMenu } 632 633procedure TShadowMenu.AddItemAfter(Sender: TObject); 634var 635 si: TShadowItem; 636begin 637 si:=SelectedShadowItem; 638 if (si <> nil) then 639 si.ParentBox.AddItemAndShadow(si, False); 640end; 641 642procedure TShadowMenu.AddItemBefore(Sender: TObject); 643var 644 si: TShadowItem; 645begin 646 si:=SelectedShadowItem; 647 if (si <> nil) then 648 si.ParentBox.AddItemAndShadow(si, True); 649end; 650 651procedure TShadowMenu.AddOnClick(Sender: TObject); 652var 653 CompEditor: TDefaultComponentEditor; 654begin 655 if (FSelectedMenuItem <> nil) then begin 656 FDesigner.FGui.BeginUpdate; 657 CompEditor:=nil; 658 try 659 CompEditor:=TDefaultComponentEditor.Create(FSelectedMenuItem, FEditorDesigner); 660 CompEditor.Edit; 661 UpdateSelectedItemInfo; 662 finally 663 CompEditor.Free; 664 FDesigner.FGui.EndUpdate; 665 end; 666 end; 667end; 668 669procedure TShadowMenu.AddSubMenu(Sender: TObject); 670var 671 si: TShadowItem; 672begin 673 si:=SelectedShadowItem; 674 if (si <> nil) then begin 675 HideFakes; 676 AddSubMenuTo(si); 677 end; 678end; 679 680procedure TShadowMenu.DeleteItem(Sender: TObject); 681var 682 si: TShadowItem; 683begin 684 if (FDesigner.TotalMenuItemsCount > 0) then 685 begin 686 if (Sender is TShadowItem) then 687 DeleteChildlessShadowAndItem(TShadowItem(Sender)) 688 else begin 689 si:=SelectedShadowItem; 690 if (si <> nil) then 691 DeleteChildlessShadowAndItem(si); 692 end; 693 end; 694end; 695 696procedure TShadowMenu.EditCaption(Sender: TObject); 697var 698 SelShadow: TShadowItem; 699begin 700 SelShadow := SelectedShadowItem; 701 if (SelShadow <> nil) then begin 702 HideFakes; 703 FEditedMenuItem := FSelectedMenuItem; 704 FInPlaceEditor.Parent := SelShadow; 705 // ToDo: Calculate Left and Width properly. 706 FInPlaceEditor.Left := 24; 707 FInPlaceEditor.Width := SelShadow.Width - 24; 708 FInPlaceEditor.Text := FEditedMenuItem.Caption; 709 FInPlaceEditor.Visible := True; 710 FInPlaceEditor.SetFocus; 711 end; 712end; 713 714procedure TShadowMenu.StopEditingCaption; 715var 716 EditedShadow: TShadowItem; 717 s: TCaption; 718begin 719 if not FInPlaceEditor.Visible then Exit; 720 Assert(Assigned(FEditedMenuItem), 'TShadowMenu.StopEditingCaption: FEditedMenuItem = Nil'); 721 EditedShadow := TShadowItem(GetShadowForMenuItem(FEditedMenuItem)); 722 s := FInPlaceEditor.Text; 723 if s <> '' then 724 begin 725 FEditedMenuItem.Caption:=s; 726 if (s = cLineCaption) and AnsiStartsStr('MenuItem', FEditedMenuItem.Name) then 727 FEditedMenuItem.Name:=FEditorDesigner.CreateUniqueComponentName('N'); 728 GlobalDesignHook.RefreshPropertyValues; 729 GlobalDesignHook.Modified(FEditedMenuItem); 730 EditedShadow.Invalidate; 731 end; 732 EditedShadow.SetFocus; 733 FInPlaceEditor.Text := ''; 734 FInPlaceEditor.Visible := False; 735 FInPlaceEditor.Parent := Nil; 736 FEditedMenuItem := Nil; 737 RefreshFakes; 738end; 739 740procedure TShadowMenu.InPlaceEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); 741begin 742 case Key of 743 VK_ESCAPE: begin Key:=0; FInPlaceEditor.Text := ''; StopEditingCaption; end; 744 VK_RETURN: begin Key:=0; StopEditingCaption; end; 745 else inherited KeyDown(Key, Shift); 746 end; 747end; 748 749procedure TShadowMenu.AddSeparatorAbove(Sender: TObject); 750var 751 selected: TShadowItem; 752begin 753 if (FSelectedMenuItem <> nil) then begin 754 selected:=SelectedShadowItem; 755 selected.ParentBox.AddItemAndShadow(selected, True, True); 756 end; 757end; 758 759procedure TShadowMenu.AddSeparatorBelow(Sender: TObject); 760var 761 selected: TShadowItem; 762begin 763 if (FSelectedMenuItem <> nil) then begin 764 selected:=SelectedShadowItem; 765 selected.ParentBox.AddItemAndShadow(selected, False, True); 766 end; 767end; 768 769procedure TShadowMenu.MoveItemAfter(Sender: TObject); 770var 771 nextI, parentI: TMenuItem; 772 currIdx: integer; 773 selected: TShadowItem; 774begin 775 if (FSelectedMenuItem <> nil) then begin 776 nextI:=GetNextItem(FSelectedMenuItem); 777 parentI:=FSelectedMenuItem.Parent; 778 selected:=SelectedShadowItem; 779 if (nextI <> nil) and (parentI <> nil) then 780 begin 781 HideFakes; 782 HideBoxesAboveLevel(selected.Level); 783 currIdx:=FSelectedMenuItem.MenuIndex; 784 parentI.Remove(nextI); 785 parentI.Remove(FSelectedMenuItem); 786 parentI.Insert(currIdx, nextI); 787 parentI.Insert(Succ(currIdx), FSelectedMenuItem); 788 FEditorDesigner.PropertyEditorHook.RefreshPropertyValues; 789 FEditorDesigner.PropertyEditorHook.Modified(FMenu); 790 selected.ParentBox.LocateShadows; 791 UpdateBoxLocationsAndSizes; 792 selected.ShowChildBox; 793 RefreshFakes; 794 UpdateActionsEnabledness; 795 end; 796 end; 797end; 798 799procedure TShadowMenu.MoveItemBefore(Sender: TObject); 800var 801 previousI, parentI: TMenuItem; 802 currIdx: integer; 803 selected: TShadowItem; 804begin 805 if (FSelectedMenuItem <> nil) then begin 806 previousI:=GetPreviousItem(FSelectedMenuItem); 807 parentI:=FSelectedMenuItem.Parent; 808 selected:=SelectedShadowItem; 809 if (previousI <> nil) and (parentI <> nil) then 810 begin 811 HideFakes; 812 HideBoxesAboveLevel(selected.Level); 813 currIdx:=FSelectedMenuItem.MenuIndex; 814 parentI.Remove(previousI); 815 parentI.Remove(FSelectedMenuItem); 816 parentI.Insert(Pred(currIdx), FSelectedMenuItem); 817 parentI.Insert(currIdx, previousI); 818 FEditorDesigner.PropertyEditorHook.RefreshPropertyValues; 819 FEditorDesigner.PropertyEditorHook.Modified(FMenu); 820 selected.ParentBox.LocateShadows; 821 UpdateBoxLocationsAndSizes; 822 selected.ShowChildBox; 823 RefreshFakes; 824 UpdateActionsEnabledness; 825 end; 826 end; 827end; 828 829procedure TShadowMenu.RemoveAllSeparators(Sender: TObject); 830begin 831 if (FSelectedMenuItem <> nil) then 832 SelectedShadowItem.ParentBox.RemoveAllSeparators; 833end; 834 835procedure TShadowMenu.ConnectSpeedButtonOnClickMethods; 836begin 837 with FDesigner.FGui do begin 838 AddSeparatorAboveButton.OnClick:=@AddSeparatorAbove; 839 AddSeparatorBelowButton.OnClick:=@AddSeparatorBelow; 840 MoveItemUpButton.OnClick:=@MoveItemBefore; 841 MoveItemDownButton.OnClick:=@MoveItemAfter; 842 DeleteItemButton.OnClick:=@DeleteItem; 843 AddItemAboveButton.OnClick:=@AddItemBefore; 844 AddItemBelowButton.OnClick:=@AddItemAfter; 845 AddSubMenuButton.OnClick:=@AddSubMenu; 846 end; 847end; 848 849procedure TShadowMenu.RecursiveCreateShadows(aParentBox: TShadowBox; aMI: TMenuItem); 850var 851 j: integer; 852 sb: TShadowBox; 853begin 854 TShadowItem.CreateWithBoxAndItem(Self, aParentBox, aMI); 855 if (aMI.Count > 0) then 856 begin 857 sb:=TShadowBox.CreateWithParentBox(Self, aParentBox, aMI); 858 for j:=0 to aMI.Count-1 do 859 RecursiveCreateShadows(sb, aMI.Items[j]); 860 end; 861end; 862 863procedure TShadowMenu.CreateShadowBoxesAndItems; 864var 865 i: integer; 866begin 867 if (FMenu.Items.Count > 0) then 868 begin 869 FRootBox:=TShadowBox.CreateWithParentBox(Self, nil, FMenu.Items); 870 for i:=0 to FMenu.Items.Count-1 do begin 871 if IsMainMenu and FMenu.Items[i].IsLine then 872 RaiseGDBException(lisMenuEditorSomeWidgetsetsDoNotAllowSeparatorsInTheMainMenubar); 873 RecursiveCreateShadows(FRootBox, FMenu.Items[i]); 874 end; 875 end; 876end; 877 878procedure TShadowMenu.DeleteChildlessShadowAndItem(anExistingSI: TShadowItem); 879var 880 nearestMI, mi: TMenuItem; 881 box: TShadowBox; 882begin 883 StopEditingCaption; 884 FDesigner.FGui.BeginUpdate; 885 try 886 mi:=anExistingSI.RealItem; 887 if (mi.Count > 0) then 888 DeleteShadowAndItemAndChildren(anExistingSI) 889 else begin 890 HideFakes; 891 if (mi = FSelectedMenuItem) then 892 FSelectedMenuItem:=nil; 893 nearestMI:=GetNextNonSepItem(mi); 894 if (nearestMI = nil) then 895 nearestMI:=GetPreviousNonSepItem(mi); 896 if (nearestMI = nil) then 897 begin 898 if mi.Parent<>FMenu.Items then 899 nearestMI:=mi.Parent; 900 end; 901 box:=anExistingSI.ParentBox; 902 box.ParentMenuItem.Remove(mi); 903 anExistingSI.RealItem:=nil; 904 box.ShadowList.Remove(anExistingSI); 905 anExistingSI.Parent:=nil; 906 Application.ReleaseComponent(anExistingSI); 907 FEditorDesigner.PropertyEditorHook.Modified(mi); 908 FEditorDesigner.PropertyEditorHook.DeletePersistent(TPersistent(mi)); 909 FEditorDesigner.Modified; 910 911 if (box.ShadowList.Count = 0) then 912 begin 913 FBoxList.Remove(box); 914 box.Parent:=nil; 915 if box=FRootBox then 916 FRootBox:=nil; 917 Application.ReleaseComponent(box); 918 box:=nil; 919 end; 920 if Assigned(box) then 921 box.LocateShadows; 922 UpdateBoxLocationsAndSizes; 923 SetSelectedMenuItem(nearestMI, False, True); 924 FDesigner.FGui.UpdateStatistics; 925 end; 926 finally 927 FDesigner.FGui.EndUpdate; 928 end; 929end; 930 931procedure TShadowMenu.DeleteBox(aMI: TMenuItem); 932var 933 i: integer; 934 sb: TShadowBoxBase; 935 si: TShadowItemBase; 936begin 937 sb:=GetParentBoxForMenuItem(aMI); 938 sb.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TShadowMenu.DeleteBox'){$ENDIF}; 939 for i:=aMI.Count-1 downto 0 do 940 DeleteBox(aMI.Items[i]); 941 Assert(sb<>nil,'TShadowMenu.DeleteBox: internal error'); 942 sb.Hide; 943 sb.ShadowList.Remove(GetShadowForMenuItem(aMI)); 944 if (sb.ShadowList.Count = 0) then 945 begin 946 FBoxList.Remove(sb); 947 sb.Parent:=nil; 948 si:=GetShadowForMenuItem(sb.ParentMenuItem); 949 if Assigned(si) then 950 si.Invalidate; 951 Application.ReleaseComponent(sb); 952 end; 953end; 954 955procedure TShadowMenu.DeleteItm(anItem: TMenuItem); 956var 957 i: integer; 958begin 959 for i:=anItem.Count-1 downto 0 do 960 DeleteItm(anItem.Items[i]); 961 anItem.Parent.Remove(anItem); 962 GlobalDesignHook.DeletePersistent(TPersistent(anItem)); 963 GlobalDesignHook.Modified(anItem); 964end; 965 966procedure TShadowMenu.DeleteShadowAndItemAndChildren(anExistingSI: TShadowItem); 967var 968 firstBoxToDelete: TShadowBoxBase; 969 mi: TMenuItem; 970 i: integer; 971begin 972 if IDEQuestionDialogAb( 973 lisDelete, 974 lisMenuEditorDeleteThisItemAndItsSubitems, 975 mtWarning, [mrYes, mrNo], False) = mrYes then 976 begin 977 firstBoxToDelete:=GetBoxWithParentItem(anExistingSI.RealItem); 978 Assert(firstBoxToDelete<>nil,'TShadowMenu.DeleteShadowAndItemAndChildren: no children'); 979 // Delete boxes recursively 980 mi:=firstBoxToDelete.ParentMenuItem; 981 Assert(mi<>nil,'TShadowMenu,DeleteShadowAndItemAndChildren: RecursiveBoxDelete internal error'); 982 for i:=mi.Count-1 downto 0 do 983 DeleteBox(mi.Items[i]); 984 // Delete children recursively 985 mi:=anExistingSI.RealItem; 986 for i:=mi.Count-1 downto 0 do 987 DeleteItm(mi.Items[i]); 988 DeleteChildlessShadowAndItem(anExistingSI); 989 end; 990end; 991 992function TShadowMenu.GetSelectedShadowItem: TShadowItem; 993begin 994 Result:=TShadowItem(GetShadowForMenuItem(FSelectedMenuItem)); 995end; 996 997function TShadowMenu.GetMenuBarIconWidth(aMI: TMenuItem): integer; 998begin 999 Result:=0; 1000 if aMI.IsInMenuBar then begin 1001 if aMI.HasIcon and (aMI.ImageIndex > -1) and 1002 (FMenu.Images <> nil) then 1003 Inc(Result, FMenu.Images.Width) 1004 else if (aMI.Bitmap <> nil) and not aMI.Bitmap.Empty then 1005 Inc(Result, aMI.Bitmap.Width); 1006 if (Result > 24) then 1007 Result:=24; 1008 end; 1009end; 1010 1011procedure TShadowMenu.AddManyItems(aPrimaries, aDepth: integer); 1012var 1013 p, d: integer; 1014 mi, mi2: TMenuItem; 1015 sb: TShadowBox; 1016 1017 function NewMenuItem(aParentMI: TMenuItem): TMenuItem; 1018 begin 1019 Result:=TMenuItem.Create(FLookupRoot); 1020 Result.Name:=FEditorDesigner.CreateUniqueComponentName('TMenuItem'); 1021 Result.Caption:=Result.Name; 1022 if (aParentMI = nil) then 1023 FMenu.Items.Add(Result) 1024 else aParentMI.Add(Result); 1025 FEditorDesigner.PropertyEditorHook.PersistentAdded(Result, False); 1026 FEditorDesigner.PropertyEditorHook.Modified(Result); 1027 end; 1028 1029begin 1030 if not IsMainMenu then 1031 begin 1032 for p:=1 to aPrimaries do 1033 TShadowItem.CreateWithBoxAndItem(Self, FRootBox, NewMenuItem(nil)); 1034 UpdateBoxLocationsAndSizes; 1035 end 1036 else 1037 begin 1038 for p:=0 to aPrimaries-1 do 1039 begin 1040 if (p = 0) then 1041 mi:=FMenu.Items[0] 1042 else 1043 begin 1044 mi:=NewMenuItem(nil); 1045 TShadowItem.CreateWithBoxAndItem(Self, FRootBox, mi); 1046 end; 1047 sb:=TShadowBox.CreateWithParentBox(Self, FRootBox, mi); 1048 for d:=1 to aDepth do 1049 begin 1050 mi2:=NewMenuItem(mi); 1051 TShadowItem.CreateWithBoxAndItem(Self, sb, mi2); 1052 end; 1053 end; 1054 UpdateBoxLocationsAndSizes; 1055 HideBoxesAboveLevel(0); 1056 end; 1057 SetSelectedMenuItem(FMenu.Items[0], False, False); 1058 SelectedShadowItem.ShowChildBox; 1059 FDesigner.FGui.UpdateStatistics; 1060end; 1061 1062function TShadowMenu.GetBoxWithParentItem(aParentMI: TMenuItem): TShadowBoxBase; 1063var 1064 sb: TShadowBoxBase; 1065begin 1066 Assert(aParentMI<>nil,'TShadowMenu.GetBoxWithParentItem: parent item is nil'); 1067 for sb in FBoxList do 1068 if (sb.ParentMenuItem = aParentMI) then 1069 Exit(sb); 1070 Result:=nil; 1071end; 1072 1073function TShadowMenu.GetMaxVisibleBoxDims(aSB: TShadowBox): TPoint; 1074begin 1075 Result:=Point(0,0); 1076 if (aSB = nil) or not aSB.Visible then 1077 Exit 1078 else Result:=Point(aSB.BoundsRect.Right, aSB.BoundsRect.Bottom); 1079end; 1080 1081function TShadowMenu.GetMaxVisibleFakeDims: TPoint; 1082begin 1083 Result:=Point(0, 0); 1084 if FAddItemFake.Visible then 1085 Result:=Point(FAddItemFake.BoundsRect.Right, FAddItemFake.BoundsRect.Bottom); 1086 if FAddSubMenuFake.Visible then begin 1087 if (FAddSubmenuFake.BoundsRect.Right > Result.x) then 1088 Result.x:=FAddSubmenuFake.BoundsRect.Right; 1089 if (FAddSubmenuFake.BoundsRect.Bottom > Result.y) then 1090 Result.y:=FAddSubmenuFake.BoundsRect.Bottom; 1091 end; 1092end; 1093 1094function TShadowMenu.GetSelectedShadowBox: TShadowBox; 1095var 1096 sel: TShadowItem; 1097begin 1098 sel:=SelectedShadowItem; 1099 if (sel = nil) then 1100 Result:=nil 1101 else 1102 Result:=sel.ParentBox; 1103end; 1104 1105procedure TShadowMenu.AddSubMenuTo(anExistingSI: TShadowItem); 1106var 1107 newMI: TMenuItem; 1108 box: TShadowBox; 1109begin 1110 if (anExistingSI.RealItem.Count <> 0) then 1111 Exit; 1112 newMI:=TMenuItem.Create(FLookupRoot); 1113 newMI.Name:=FEditorDesigner.CreateUniqueComponentName(newMI.ClassName); 1114 newMI.Caption:=newMI.Name; 1115 anExistingSI.RealItem.Add(newMI); 1116 GlobalDesignHook.PersistentAdded(newMI, False); 1117 GlobalDesignHook.Modified(newMI); 1118 box:=TShadowBox.CreateWithParentBox(Self, anExistingSI.ParentBox, anExistingSI.RealItem); 1119 TShadowItem.CreateWithBoxAndItem(Self, box, newMI); 1120 UpdateBoxLocationsAndSizes; 1121 SetSelectedMenuItem(newMI, False, False); 1122 FDesigner.FGui.UpdateStatistics; 1123end; 1124 1125procedure TShadowMenu.SetupPopupMenu; 1126var 1127 pe: TPopEnum; 1128 ac: TAction; 1129 primaryItem, mi: TMenuItem; 1130 1131 procedure NewPopItem(const aCaption: string; anOnClick: TNotifyEvent; 1132 aShortcut: TShortCut=0); //aShortCut2: String=''); 1133 begin 1134 ac:=TAction.Create(Self); 1135 with ac do begin 1136 ac.ActionList:=FActionList; 1137 ac.DisableIfNoHandler:=False; 1138 Tag:=PtrInt(pe); 1139 Caption:=aCaption; 1140 OnExecute:=anOnClick; 1141 ShortCut:=aShortcut; 1142 //if aShortCut2 <> '' then Does not work. 1143 // SecondaryShortCuts.Add(aShortCut2); 1144 end; 1145 mi:=TMenuItem.Create(Self); 1146 FItemsPopupMenu.Items.Add(mi); 1147 mi.Action:=ac; 1148 end; 1149 1150 procedure NewPopPrimary(const aCaption: string); 1151 begin 1152 ac:=TAction.Create(Self); 1153 with ac do begin 1154 ActionList:=FActionList; 1155 DisableIfNoHandler:=False; 1156 Tag:=PtrInt(pe); 1157 Caption:=aCaption; 1158 end; 1159 mi:=TMenuItem.Create(Self); 1160 FItemsPopupMenu.Items.Add(mi); 1161 mi.Action:=ac; 1162 primaryItem:=mi; 1163 end; 1164 1165 procedure NewPopSub(const aPrimary: TMenuItem; const aCaption: string; 1166 anOnClick: TNotifyEvent; aShortcut: TShortCut=0); 1167 begin 1168 ac:=TAction.Create(Self); 1169 with ac do begin 1170 ActionList:=FActionList; 1171 DisableIfNoHandler:=False; 1172 Tag:=PtrInt(pe); 1173 Caption:=aCaption; 1174 OnExecute:=anOnClick; 1175 ShortCut:=aShortcut; 1176 end; 1177 mi:=TMenuItem.Create(Self); 1178 aPrimary.Add(mi); 1179 mi.Action:=ac; 1180 end; 1181 1182 procedure NewSeparatorAction; 1183 begin 1184 FItemsPopupMenu.Items.AddSeparator; 1185 ac:=TAction.Create(Self); 1186 ac.ActionList:=FActionList; 1187 ac.Tag:=PtrInt(pe); 1188 ac.Name:=GetEnumName(TypeInfo(TPopEnum), PtrInt(pe)); 1189 end; 1190 1191begin 1192 for pe in TPopEnum do 1193 with FDesigner.FGui do 1194 case pe of 1195 popItemAddOnClick: 1196 NewPopItem(lisMenuEditorAddOnClickHandler, @AddOnClick); 1197 popItemAddBefore: begin 1198 NewPopItem('', @AddItemBefore, KeyToShortCut(VK_INSERT,[])); 1199 AddItemAboveButton.Action:=ac; 1200 end; 1201 popItemAddAfter: begin 1202 NewPopItem('', @AddItemAfter); 1203 AddItemBelowButton.Action:=ac; 1204 end; 1205 popItemAddSubMenu: begin 1206 NewPopItem('', @AddSubMenu,KeyToShortCut(VK_INSERT,[ssCtrl])); 1207 AddSubMenuButton.Action:=ac; 1208 end; 1209 popItemDelete: begin 1210 NewPopItem(lisMenuEditorDeleteItem, @DeleteItem, KeyToShortCut(VK_DELETE, [])); 1211 DeleteItemButton.Action:=ac; 1212 end; 1213 popItemOISep: 1214 NewSeparatorAction; 1215 popItemEditCaption: 1216 NewPopItem(lisMenuEditorEditCaption, @EditCaption, KeyToShortCut(VK_RETURN, [])); 1217 popItemMoveBefore: begin 1218 NewPopItem('', @MoveItemBefore, KeyToShortCut(VK_UP,[ssCtrl])); 1219 MoveItemUpButton.Action:=ac; 1220 end; 1221 popItemMoveAfter: begin 1222 NewPopItem('', @MoveItemAfter, KeyToShortCut(VK_DOWN,[ssCtrl])); 1223 MoveItemDownButton.Action:=ac; 1224 end; 1225 popAddImgListIcon: begin 1226 NewPopItem('', @AddImageListIcon); 1227 FAddImgListIconAction:=ac; 1228 end; 1229 popItemSep: 1230 NewSeparatorAction; 1231 popSeparators_: 1232 NewPopPrimary(lisMenuEditorSeParators); 1233 popAddSeparatorBefore: begin 1234 NewPopSub(primaryItem, lisMenuEditorAddSeparatorBefore, @AddSeparatorAbove); 1235 AddSeparatorAboveButton.Action:=ac; 1236 ac.Hint:=lisAddANewSeparatorAboveSelectedItem; 1237 end; 1238 popAddSeparatorAfter: begin 1239 NewPopSub(primaryItem, lisMenuEditorAddSeparatorAfter, @AddSeparatorBelow); 1240 AddSeparatorBelowButton.Action:=ac; 1241 ac.Hint:=lisAddANewSeparatorBelowSelectedItem; 1242 end; 1243 popRemoveAllSeparators: 1244 NewPopSub(primaryItem, lisMenuEditorRemoveAllSeparators, @RemoveAllSeparators); 1245 popShortcuts_: 1246 NewPopPrimary(lisMenuEditorShortcUts2); 1247 popListShortcuts: 1248 NewPopSub(primaryItem, '', @ListShortcuts); 1249 popListShortcutsAccelerators: 1250 NewPopSub(primaryItem, '', @ListShortcutsAndAccelerators); 1251 popResolveShortcutConflicts: 1252 NewPopSub(primaryItem, lisMenuEditorResolveShortcutConflicts, @ResolveshortcutConflicts); 1253 popTemplates_: 1254 NewPopPrimary(lisMenuEditorTemplates); 1255 popSaveAsTemplate: 1256 NewPopSub(primaryItem, lisMenuEditorSaveMenuAsATemplate, @SaveAsTemplate); 1257 popAddFromTemplate: 1258 NewPopSub(primaryItem, lisMenuEditorAddFromTemplate, @AddFromTemplate); 1259 popDeleteTemplate: 1260 NewPopSub(primaryItem, lisMenuEditorDeleteMenuTemplate, @DeleteTemplate); 1261 end; // case 1262end; 1263 1264function TShadowMenu.GetMenuBarCumWidthForItemIndex(anIndex: integer): integer; 1265var 1266 w: integer; 1267 mi: TMenuItem; 1268begin 1269 Result:=0; 1270 if anIndex <> 0 then 1271 repeat 1272 mi:=FMenu.Items[Pred(anIndex)]; 1273 w:=GetStringWidth(mi.Caption, mi.Default) + 1274 Double_MenuBar_Text_Offset + GetMenuBarIconWidth(mi); 1275 Inc(Result, w); 1276 Dec(anIndex) 1277 until (anIndex <= 0); 1278end; 1279 1280function TShadowMenu.GetParentItemHeightInBox(aParentItem: TMenuItem): integer; 1281 1282 function HeightOfItem(anIndex: integer): integer; 1283 begin 1284 if aParentItem.Parent.Items[anIndex].IsLine then 1285 Result:=Separator_Height 1286 else 1287 Result:=DropDown_Height; 1288 end; 1289 1290var 1291 idx: integer = 0; 1292begin 1293 Result:=1; 1294 repeat 1295 if (idx < aParentItem.MenuIndex) then 1296 Inc(Result, HeightOfItem(idx)); 1297 Inc(idx); 1298 until (idx >= aParentItem.MenuIndex); 1299end; 1300 1301procedure TShadowMenu.UpdateBoxLocationsAndSizes; 1302var 1303 sb: TShadowBoxBase; 1304 si: TShadowItemBase; 1305 lft, w, idx: integer; 1306 pt: TPoint; 1307begin 1308 FBoxList.Sort(@SortByBoxLevel); 1309 for sb in FBoxList do begin 1310 if sb.IsMenuBar then 1311 begin 1312 sb.Align:=alTop; 1313 sb.Height:=MenuBar_Height; 1314 lft:=0; 1315 for si in sb.ShadowList do begin 1316 w:=si.GetWidth; 1317 si.SetBounds(lft, 0, w, MenuBar_Height); 1318 Inc(lft, w); 1319 end; 1320 end 1321 else if IsMainMenu and (sb.Level = 1) then 1322 begin 1323 pt:=sb.GetInnerDims; 1324 idx:=sb.ParentMenuItem.MenuIndex; 1325 lft:=GetMenuBarCumWidthForItemIndex(idx); 1326 sb.SetBounds(lft, MenuBar_Height+1, pt.x+2, pt.y+2); 1327 end 1328 else begin 1329 pt:=sb.GetInnerDims; 1330 if (sb.Level = 0) then 1331 sb.SetBounds(Popup_Origin.x, Popup_Origin.y, pt.x+2, pt.y+2) 1332 else sb.SetBounds(sb.ParentBox.Left+sb.ParentBox.Width, 1333 sb.ParentBox.Top+GetParentItemHeightInBox(sb.ParentMenuItem), 1334 pt.x+2, pt.y+2); 1335 end; 1336 end; 1337 RefreshFakes; 1338end; 1339 1340procedure TShadowMenu.RemoveEmptyBox(aSB: TShadowBox); 1341var 1342 miToSelect: TMenuItem; 1343begin 1344 if (aSB.ShadowList.Count = 0) then begin 1345 miToSelect:=aSB.ParentMenuItem; 1346 FBoxList.Remove(aSB); 1347 aSB.Parent:=nil; 1348 Application.ReleaseComponent(aSB); 1349 UpdateBoxLocationsAndSizes; 1350 SetSelectedMenuItem(miToSelect, False, True); 1351 end; 1352end; 1353 1354procedure TShadowMenu.HideFakes; 1355begin 1356 FAddSubmenuFake.Hide; 1357 FAddItemFake.Hide; 1358 FAddFirstItemFake.Hide; 1359end; 1360 1361procedure TShadowMenu.RefreshFakes; 1362begin 1363 // MG: Dont: Application.ProcessMessages; this might free components and trigger events 1364 FAddItemFake.Refresh; 1365 FAddSubmenuFake.Refresh; 1366 FAddFirstItemFake.Refresh; 1367end; 1368 1369procedure TShadowMenu.UpdateButtonGlyphs(isInBar: boolean); 1370begin 1371 if (FSelectedMenuItem <> nil) and (isInBar <> FDesigner.VariableGlyphsInMenuBar) then 1372 FDesigner.FGui.LoadVariableButtonGlyphs(isInBar); 1373end; 1374 1375procedure TShadowMenu.AddFromTemplate(Sender: TObject); 1376var 1377 newItem: TMenuItem; 1378 sb: TShadowBox; 1379 i: integer; 1380begin 1381 if (FSelectedMenuItem <> nil) and (FSelectedMenuItem.Parent.Parent = nil) then 1382 begin 1383 HideFakes; 1384 newItem:=InsertMenuTemplateDlg(FMenu); 1385 if (newItem <> nil) then 1386 begin 1387 FMenu.Items.Add(newItem); 1388 FLookupRoot.InsertComponent(newItem); 1389 newItem.Name:=FEditorDesigner.CreateUniqueComponentName(newItem.ClassName); 1390 FEditorDesigner.PropertyEditorHook.PersistentAdded(TPersistent(newItem), False); 1391 FEditorDesigner.Modified; 1392 TShadowItem.CreateWithBoxAndItem(Self, FRootBox, newItem); 1393 if (newItem.Count > 0) then begin 1394 sb:=TShadowBox.CreateWithParentBox(Self, FRootBox, newItem); 1395 for i:=0 to newItem.Count-1 do 1396 begin 1397 FLookupRoot.InsertComponent(newItem.Items[i]); 1398 newItem.Items[i].Name:=FEditorDesigner.CreateUniqueComponentName(newItem.Items[i].ClassName); 1399 FEditorDesigner.PropertyEditorHook.PersistentAdded(TPersistent(newItem.Items[i]), False); 1400 FEditorDesigner.Modified; 1401 TShadowItem.CreateWithBoxAndItem(Self, sb, newItem.Items[i]); 1402 end; 1403 end; 1404 UpdateBoxLocationsAndSizes; 1405 SetSelectedMenuItem(newItem, False, False); 1406 end; 1407 end; 1408end; 1409 1410procedure TShadowMenu.AddImageListIcon(Sender: TObject); 1411var 1412 idx: integer; 1413 selected: TShadowItem; 1414begin 1415 if FSelectedMenuItem = nil then Exit; 1416 idx := -1; 1417 selected:=SelectedShadowItem; 1418 if (FMenu.Images <> nil) then 1419 idx := ChooseIconFromImageListDlg(FMenu.Images) 1420 else if (selected.Level > 0) 1421 and (FSelectedMenuItem.Parent.SubMenuImages <> nil) then 1422 idx := ChooseIconFromImageListDlg(FSelectedMenuItem.Parent.SubMenuImages); 1423 if idx = -1 then Exit; 1424 FSelectedMenuItem.ImageIndex := idx; 1425 selected.Invalidate; 1426 UpdateActionsEnabledness; 1427 FEditorDesigner.PropertyEditorHook.RefreshPropertyValues; 1428 FEditorDesigner.Modified; 1429end; 1430 1431procedure TShadowMenu.DeleteTemplate(Sender: TObject); 1432begin 1433 if SavedTemplatesExist and DeleteMenuTemplateDlg then begin 1434 FDesigner.UpdateTemplatesCount; 1435 UpdateActionsEnabledness; 1436 end; 1437end; 1438 1439procedure TShadowMenu.ListShortcuts(Sender: TObject); 1440begin 1441 ListShortCutDlg(FDesigner.Shortcuts, True, Self, FMenu); 1442end; 1443 1444procedure TShadowMenu.ListShortcutsAndAccelerators(Sender: TObject); 1445begin 1446 ListShortCutDlg(FDesigner.Shortcuts, False, Self, Nil); 1447end; 1448 1449procedure TShadowMenu.ResolveShortcutConflicts(Sender: TObject); 1450var 1451 dlg: TResolveConflictsDlg; 1452begin 1453 dlg:=TResolveConflictsDlg.Create(FDesigner.Shortcuts, Self); 1454 try 1455 if dlg.ShowModal <> mrCancel then 1456 UpdateActionsEnabledness; 1457 finally 1458 dlg.Free; 1459 end; 1460end; 1461 1462procedure TShadowMenu.SaveAsTemplate(Sender: TObject); 1463var 1464 dlg: TMenuTemplateDialog; 1465begin 1466 if (FSelectedMenuItem <> nil) and LevelZeroAndNoGrandchildren(FSelectedMenuItem) then 1467 begin 1468 //SaveMenuTemplateDlg(FSelectedMenuItem); 1469 dlg:=TMenuTemplateDialog.CreateWithMode(FMenu, dmSave); 1470 try 1471 dlg.MenuToSave:=FSelectedMenuItem; 1472 dlg.ShowModal; 1473 finally 1474 dlg.Free; 1475 end; 1476 FDesigner.UpdateTemplatesCount; 1477 UpdateActionsEnabledness; 1478 end; 1479end; 1480 1481procedure TShadowMenu.OnObjectPropertyChanged(Sender: TObject; NewObject: TPersistent); 1482var 1483 propertyEditor: TPropertyEditor absolute Sender; 1484 i: Integer; 1485 persistent: TPersistent; 1486 mi: TMenuItem absolute persistent; 1487begin 1488 if not (Sender is TPropertyEditor) or (NewObject = nil) then 1489 Exit; 1490 if (NewObject is TAction) then 1491 for i:=0 to propertyEditor.PropCount-1 do begin 1492 persistent:=propertyEditor.GetComponent(i); 1493 if (persistent is TMenuItem) then begin 1494 if GetShadowForMenuItem(mi) <> nil then 1495 begin 1496 UpdateBoxLocationsAndSizes; 1497 RefreshFakes; 1498 if (FSelectedMenuItem <> nil) then 1499 SelectedShadowItem.Invalidate; 1500 end; 1501 end; 1502 end; 1503 if (NewObject is TImageList) and (NewObject = FMenu.Images) then 1504 UpdateActionsEnabledness; 1505end; 1506 1507procedure TShadowMenu.OnDesignerModified(Sender: TObject); 1508var 1509 i: integer; 1510 persistent: TPersistent; 1511 mi: TMenuItem absolute persistent; 1512 refreshNeeded: boolean = False; 1513begin 1514 if FDesigner.FGui.IsUpdate then 1515 Exit; 1516 1517 if (Sender is TPropertyEditor) then begin 1518 for i:=0 to TPropertyEditor(Sender).PropCount-1 do begin 1519 persistent:=TPropertyEditor(Sender).GetComponent(i); 1520 if (persistent is TMenuItem) then begin 1521 if GetShadowForMenuItem(mi) <> nil then 1522 refreshNeeded:=True; 1523 end; 1524 end; 1525 if refreshNeeded then begin 1526 UpdateBoxLocationsAndSizes; 1527 if ((mi.Action <> nil) and (TAction(mi.Action).ShortCut <> 0)) or 1528 (mi.ShortCut <> 0) or (mi.ShortCutKey2 <> 0) then 1529 FDesigner.Shortcuts.UpdateShortcutList(True); 1530 if (FSelectedMenuItem <> nil) then begin 1531 SelectedShadowItem.Invalidate; 1532 end; 1533 FDesigner.FGui.UpdateStatistics; 1534 end; 1535 end; 1536end; 1537 1538procedure TShadowMenu.OnDesignerRefreshPropertyValues; 1539var 1540 comp: TComponent; 1541 mi: TMenuItem absolute comp; 1542 selBox: TShadowBox; 1543begin 1544 if FSelectedMenuItem = nil then 1545 Exit; 1546 comp:=GlobalDesignHook.GetComponent(FSelectedMenuItem.Name); 1547 if comp is TMenuItem then 1548 begin 1549 selBox:=SelectedShadowBox; 1550 if (selBox.LastRIValue <> mi.RadioItem) then 1551 FDesigner.FGui.UpdateSubmenuGroupBox(FSelectedMenuItem, selBox); 1552 end; 1553end; 1554 1555function TShadowMenu.OnClickIsAssigned(aMI: TMenuItem): boolean; 1556begin 1557 if (aMI = nil) then 1558 Exit(False); 1559 Result:=(FEditorDesigner.PropertyEditorHook.GetMethodName(GetMethodProp(aMI, 'OnClick'), aMI) <> ''); 1560end; 1561 1562procedure TShadowMenu.Paint; 1563begin 1564 if FInitialising then 1565 Exit; 1566end; 1567 1568procedure TShadowMenu.SetParent(NewParent: TWinControl); 1569begin 1570 inherited SetParent(NewParent); 1571 if (NewParent <> nil) and not (csDestroying in ComponentState) then 1572 begin 1573 Align:=alNone; 1574 CreateShadowBoxesAndItems; 1575 UpdateBoxLocationsAndSizes; 1576 HideBoxesAboveLevel(0); 1577 // MG: Dont: Application.ProcessMessages; this might free components and trigger events 1578 FInitialising:=True; 1579 if (FInitialSelectedMenuItem <> nil) then begin 1580 SetSelectedMenuItem(FInitialSelectedMenuItem, True, False); 1581 UpdateActionsEnabledness; 1582 end; 1583 end; 1584end; 1585 1586procedure TShadowMenu.SetSelectedMenuItem(aMI: TMenuItem; 1587 viaDesigner, prevWasDeleted: boolean); 1588var 1589 prevSelectedMenuItem: TMenuItem; 1590 prevSelectedShadow: TShadowItem; 1591begin 1592 if (aMI = nil) then 1593 begin 1594 if prevWasDeleted then 1595 SetSelectedShadow(nil, nil, False) 1596 else 1597 SetSelectedShadow(FSelectedMenuItem, nil, False); 1598 FSelectedMenuItem:=nil; 1599 RefreshFakes; 1600 Exit; 1601 end; 1602 if (FSelectedMenuItem <> aMI) then 1603 begin 1604 if (FSelectedMenuItem = nil) or prevWasDeleted then begin 1605 prevSelectedMenuItem:=nil; 1606 prevSelectedShadow:=nil; 1607 end 1608 else begin 1609 prevSelectedMenuItem:=FSelectedMenuItem; 1610 prevSelectedShadow:=TShadowItem(GetShadowForMenuItem(prevSelectedMenuItem)); 1611 end; 1612 if (prevSelectedShadow <> nil) then begin 1613 if prevSelectedMenuItem.Enabled then 1614 prevSelectedShadow.ShowNormal 1615 else 1616 prevSelectedShadow.ShowDisabled; 1617 if not AIsDescendantOfB(aMI, prevSelectedMenuItem) then 1618 prevSelectedShadow.HideChildren; 1619 end; 1620 FSelectedMenuItem:=aMI; 1621 SetSelectedShadow(prevSelectedMenuItem, FSelectedMenuItem, viaDesigner); 1622 end; 1623end; 1624 1625procedure TShadowMenu.SetSelectedShadow(const prevSelectedItem, 1626 curSelectedItem: TMenuItem; viaDesigner: boolean); 1627var 1628 selectedShadow, prevShadow: TShadowItem; 1629begin 1630 selectedShadow:=TShadowItem(GetShadowForMenuItem(curSelectedItem)); 1631 if selectedShadow=nil then 1632 begin 1633 HideFakes; 1634 if (FSelectedMenuItem <> nil) then 1635 begin 1636 SelectedShadowItem.ShowNormal; 1637 FSelectedMenuItem:=nil; 1638 end; 1639 UpdateSelectedItemInfo; 1640 if not viaDesigner and (FMenu<>nil) then 1641 FEditorDesigner.SelectOnlyThisComponent(FMenu); 1642 end else 1643 begin 1644 if (prevSelectedItem <> nil) then 1645 begin 1646 StopEditingCaption; 1647 prevShadow:=TShadowItem(GetShadowForMenuItem(prevSelectedItem)); 1648 if (prevShadow <> nil) 1649 and (selectedShadow.ParentBox.ParentMenuItem <> prevSelectedItem) 1650 and (prevShadow.ParentBox <> selectedShadow.ParentBox) 1651 then 1652 prevShadow.HideChainFromRoot; 1653 end; 1654 UpdateButtonGlyphs(FSelectedMenuItem.IsInMenuBar); 1655 selectedShadow.ShowChainToRoot; 1656 selectedShadow.ShowSelected; 1657 HideBoxesAboveLevel(selectedShadow.Level); 1658 selectedShadow.ShowChildBox; 1659 1660 UpdateSelectedItemInfo; 1661 if not viaDesigner then begin 1662 //debugln(['TShadowMenu.SetSelectedShadow ',DbgSName(curSelectedItem)]); 1663 FEditorDesigner.SelectOnlyThisComponent(curSelectedItem); 1664 end; 1665 1666 if not FDesigner.FGui.Visible then 1667 FDesigner.FGui.ShowOnTop; 1668 if selectedShadow<>nil then 1669 selectedShadow.SetFocus; 1670 UpdateActionsEnabledness; 1671 RefreshFakes; 1672 end; 1673end; 1674 1675function TShadowMenu.GetActionForEnum(anEnum: TPopEnum): TAction; 1676var 1677 i: integer; 1678begin 1679 for i:=0 to FActionList.ActionCount do 1680 if TAction(FActionList.Actions[i]).Tag = PtrInt(anEnum) then 1681 Exit(TAction(FActionList.Actions[i])); 1682 Result:=nil; 1683end; 1684 1685procedure TShadowMenu.UpdateActionsEnabledness; 1686var 1687 ac, ac1, ac2, ac3: TAction; 1688 pe: TPopEnum; 1689 isInBar, isFirst, isSeparator, isLast, prevIsSeparator, nextIsSeparator, 1690 levelZero, levelZeroOr1, primarySCEnabled: boolean; 1691begin 1692 if (FSelectedMenuItem = nil) then 1693 Exit; 1694 isInBar:=FSelectedMenuItem.IsInMenuBar; 1695 isFirst:=(FSelectedMenuItem.MenuIndex = 0); 1696 isLast:=(FSelectedMenuItem.MenuIndex = Pred(FSelectedMenuItem.Parent.Count)); 1697 isSeparator:=FSelectedMenuItem.IsLine; 1698 prevIsSeparator:=PreviousItemIsSeparator(FSelectedMenuItem); 1699 nextIsSeparator:=NextItemIsSeparator(FSelectedMenuItem); 1700 levelZero:=(FSelectedMenuItem.Parent <> nil) and (FSelectedMenuItem.Parent.Parent = nil); 1701 levelZeroOr1:=LevelZeroAndNoGrandchildren(FSelectedMenuItem); 1702 primarySCEnabled:=not isInBar and (FSelectedMenuItem.Parent.Count > 1); 1703 1704 for pe in TPopEnum do 1705 begin 1706 ac:=GetActionForEnum(pe); 1707 case pe of 1708 popItemAddOnClick: ac.Enabled:=not OnClickIsAssigned(FSelectedMenuItem); 1709 popItemAddBefore: 1710 if isInBar then begin 1711 ac.Caption:=lisMenuEditorAddNewItemBefore; 1712 ac.Hint:=lisMenuEditorAddANewItemBeforeSelectedItem; 1713 end 1714 else begin 1715 ac.Caption:=lisMenuEditorAddNewItemAbove; 1716 ac.Hint:=lisMenuEditorAddANewItemAboveSelectedItem; 1717 end; 1718 popItemAddAfter: 1719 if isInBar then begin 1720 ac.Caption:=lisMenuEditorAddNeWItemAfter; 1721 ac.Hint:=lisMenuEditorAddANewItemAfterSelectedItem; 1722 end 1723 else begin 1724 ac.Caption:=lisMenuEditorAddNeWItemBelow; 1725 ac.Hint:=lisMenuEditorAddANewItemBelowSelectedItem; 1726 end; 1727 popItemAddSubMenu: begin 1728 ac.Enabled:=(FSelectedMenuItem.Count = 0) and not FSelectedMenuItem.IsLine; 1729 if isInBar then begin 1730 ac.Caption:=lisMenuEditorAddSubmenuBelow; 1731 ac.Hint:=lisMenuEditorAddASubmenuBelowSelectedItem; 1732 end 1733 else begin 1734 ac.Caption:=lisMenuEditorAddSubmenuRight; 1735 ac.Hint:=lisMenuEditorAddASubmenuAtTheRightOfSelectedItem; 1736 end; 1737 end; 1738 popItemDelete: ac.Enabled:=(FMenu.Items.Count > 0); 1739 //popItemOISep 1740 //popItemEditCaption 1741 popItemMoveBefore: begin 1742 ac.Enabled:=not isFirst; 1743 if isInBar then begin 1744 ac.Caption:=lisMenuEditorMoveItemLeft; 1745 ac.Hint:=lisMenuEditorMoveSelectedItemToTheLeft; 1746 end 1747 else begin 1748 ac.Caption:=lisMenuEditorMoveItemUp; 1749 ac.Hint:=lisMenuEditorMoveSelectedItemUp; end; 1750 end; 1751 popItemMoveAfter: begin 1752 ac.Enabled:=not isLast; 1753 if isInBar then begin 1754 ac.Caption:=lisMenuEditorMoVeItemRight; 1755 ac.Hint:=lisMenuEditorMoveSelectedItemToTheRight; 1756 end 1757 else begin 1758 ac.Caption:=lisMenuEditorMoVeItemDown; 1759 ac.Hint:=lisMenuEditorMoveSelectedItemDown; 1760 end; 1761 end; 1762 popAddImgListIcon: begin 1763 ac.Enabled:=(FMenu.Images <> nil) and (FMenu.Images.Count > 0); 1764 if ac.Enabled then begin 1765 if (FSelectedMenuItem.ImageIndex < 0) then 1766 ac.Caption:=Format(lisMenuEditorAddIconFromS + ' ...', 1767 [FMenu.Images.Name]) 1768 else ac.Caption:=lisMenuEditorChangeImagelistIcon; 1769 if (FMenu.Images.Count = 1) and (FSelectedMenuItem.ImageIndex = 0) then 1770 ac.Enabled:=False; 1771 end 1772 else 1773 ac.Caption:=lisMenuEditorAddImagelistIcon; 1774 end; 1775 //popItemSep 1776 popSeparators_: ac.Enabled:=primarySCEnabled; 1777 popAddSeparatorBefore: 1778 ac.Enabled:=primarySCEnabled and not isSeparator and not isFirst and not prevIsSeparator; 1779 popAddSeparatorAfter: 1780 ac.Enabled:=not isInBar and not isSeparator and not nextIsSeparator; 1781 popRemoveAllSeparators: 1782 ac.Enabled:=primarySCEnabled and (GetChildSeparatorCount(FSelectedMenuItem.Parent) > 0); 1783 //popShortcuts_ 1784 popListShortcuts: begin 1785 ac.Enabled:=(FDesigner.Shortcuts.ShortcutMenuItemsCount > 0); 1786 ac.Caption:=Format(lisMenuEditorListShortcutsForS, [FMenu.Name]); 1787 end; 1788 popListShortcutsAccelerators: begin 1789 ac.Enabled:=(FDesigner.Shortcuts.ShortcutList.AcceleratorsInContainerCount > 0); 1790 ac.Caption:=Format(lisMenuEditorListShortcutsAndAccelerators,[FLookupRoot.Name]); 1791 end; 1792 popResolveShortcutConflicts: ac.Enabled:= 1793 (FDesigner.Shortcuts.ShortcutList.InitialDuplicates.Count > 0); 1794 popTemplates_: ac.Enabled:=levelZero or FDesigner.TemplatesSaved; 1795 popSaveAsTemplate: ac.Enabled:=levelZeroOr1; 1796 popAddFromTemplate: ac.Enabled:=levelZero; 1797 popDeleteTemplate: ac.Enabled:=FDesigner.TemplatesSaved; 1798 end; // case 1799 end; // for 1800 ac:=GetActionForEnum(popShortcuts_); 1801 ac1:=GetActionForEnum(popListShortcuts); 1802 ac2:=GetActionForEnum(popListShortcutsAccelerators); 1803 ac3:=GetActionForEnum(popResolveShortcutConflicts); 1804 ac.Enabled:=ac1.Enabled or ac2.Enabled or ac3.Enabled; 1805end; 1806 1807constructor TShadowMenu.Create(aDesigner: TMenuDesigner; aForm: TForm; 1808 aMenu: TMenu; aSelect: TMenuItem; aWidth, aHeight: integer); 1809begin 1810 Assert(aMenu<>nil,'TShadowMenu.Create: TMenu parameter is nil'); 1811 inherited Create(nil, aMenu); 1812 InitMenuBaseSizes; 1813 FDesigner := aDesigner; 1814 FMainCanvas := aForm.Canvas; 1815 FInitialSelectedMenuItem := aSelect; 1816 SetInitialBounds(0, 0, aWidth, aHeight); 1817 Name := 'ShadowMenu'; 1818 DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TShadowMenu.Create'){$ENDIF}; 1819 try 1820 FItemsPopupMenu := TPopupMenu.Create(Self); 1821 FItemsPopupMenu.Name := 'ItemsPopupMenu'; 1822 FActionList := TActionList.Create(Self); 1823 SetupPopupMenu; 1824 FAddItemFake := TAddSiblingFake.Create(Self); 1825 FAddItemFake.OnClick := @AddItemAfter; 1826 FAddItemFake.Caption := lisMenuEditorAddMenuItem; 1827 FAddItemFake.Name := 'AddItemFake'; 1828 FAddSubmenuFake := TAddSubmenuFake.Create(Self); 1829 FAddSubmenuFake.OnClick := @AddSubMenu; 1830 FAddSubmenuFake.Caption := lisMenuEditorAddSubmenu; 1831 FAddSubmenuFake.Name := 'AddSubmenuFake'; 1832 FAddFirstItemFake := TAddFirstFake.Create(Self); 1833 FAddFirstItemFake.OnClick := @AddFirstMenu; 1834 FAddFirstItemFake.Caption := lisMenuEditorAddMenuItem; 1835 FAddFirstItemFake.Name := 'AddFirstItemFake'; 1836 FAddFirstItemFake.Left := Popup_Origin.x; 1837 FAddFirstItemFake.Top := Popup_Origin.y; 1838 FInPlaceEditor := TEdit.Create(Self); 1839 FInPlaceEditor.OnKeyDown := @InPlaceEditKeyDown; 1840 FInPlaceEditor.Visible := False; 1841 ConnectSpeedButtonOnClickMethods; 1842 GlobalDesignHook.AddHandlerObjectPropertyChanged(@OnObjectPropertyChanged); 1843 GlobalDesignHook.AddHandlerModified(@OnDesignerModified); 1844 GlobalDesignHook.AddHandlerRefreshPropertyValues(@OnDesignerRefreshPropertyValues); 1845 Color := clBtnFace; 1846 BorderStyle := bsNone; 1847 // Parent must be set before the Align property. 1848 // Otherwise ShadowMenu goes on top of ButtonsGroupBox which is Top aligned. 1849 Parent := aForm; 1850 AutoSize := False; 1851 Align := alClient; 1852 finally 1853 EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TShadowMenu.Create'){$ENDIF}; 1854 end; 1855end; 1856 1857destructor TShadowMenu.Destroy; 1858begin 1859 Parent := nil; 1860 if Assigned(LazarusIDE) and not LazarusIDE.IDEIsClosing then 1861 begin 1862 GlobalDesignHook.RemoveHandlerRefreshPropertyValues(@OnDesignerRefreshPropertyValues); 1863 GlobalDesignHook.RemoveHandlerModified(@OnDesignerModified); 1864 GlobalDesignHook.RemoveHandlerObjectPropertyChanged(@OnObjectPropertyChanged); 1865 end; 1866 inherited Destroy; 1867end; 1868 1869procedure TShadowMenu.AddFirstMenu(Sender: TObject); 1870var 1871 newMI: TMenuItem; 1872 box: TShadowBox; 1873begin 1874 newMI:=TMenuItem.Create(FLookupRoot); 1875 newMI.Name:=FEditorDesigner.CreateUniqueComponentName(newMI.ClassName); 1876 newMI.Caption:=newMI.Name; 1877 FMenu.Items.Add(newMI); 1878 GlobalDesignHook.PersistentAdded(newMI, False); 1879 GlobalDesignHook.Modified(newMI); 1880 box:=TShadowBox.CreateWithParentBox(Self, nil, FMenu.Items); 1881 FRootBox:=box; 1882 TShadowItem.CreateWithBoxAndItem(Self, box, newMI); 1883 UpdateBoxLocationsAndSizes; 1884 SetSelectedMenuItem(newMI, False, False); 1885 FDesigner.FGui.UpdateStatistics; 1886end; 1887 1888procedure TShadowMenu.HideBoxesAboveLevel(aLevel: integer); 1889var 1890 sb: TShadowBoxBase; 1891begin 1892 for sb in FBoxList do 1893 if sb.Level > aLevel then 1894 sb.Hide; 1895end; 1896 1897procedure TShadowMenu.UpdateSelectedItemInfo; 1898begin 1899 FDesigner.FGui.UpdateItemInfo(FMenu, FSelectedMenuItem, SelectedShadowBox, 1900 FEditorDesigner.PropertyEditorHook); 1901end; 1902 1903{ TShadowBox } 1904 1905procedure TShadowBox.BeginUpdate; 1906begin 1907 FUpdating:=True; 1908end; 1909 1910procedure TShadowBox.EndUpdate; 1911begin 1912 FUpdating:=False; 1913end; 1914 1915procedure TShadowBox.ShowAllUnSelected; 1916var 1917 si: TShadowItemBase; 1918begin 1919 for si in FShadowList do 1920 si.ShowNormal; 1921end; 1922 1923function TShadowBox.GetIsMainMenu: boolean; 1924begin 1925 Result:=FShadowMenu.IsMainMenu; 1926end; 1927 1928function TShadowBox.GetIsMenuBar: boolean; 1929begin 1930 Result:=(FLevel = 0) and IsMainMenu; 1931end; 1932 1933procedure TShadowBox.Paint; 1934var 1935 r: TRect; 1936 dets: TThemedElementDetails; 1937begin 1938 r:=ClientRect; 1939 BeginUpdate; 1940 if IsMenuBar then begin 1941 dets:=ThemeServices.GetElementDetails(tmBarBackgroundActive); 1942 ThemeServices.DrawElement(Canvas.Handle, dets, r); 1943 end 1944 else begin 1945 Canvas.FillRect(r); 1946 Canvas.Frame(r); 1947 end; 1948 EndUpdate; 1949end; 1950 1951procedure TShadowBox.SelectPrevious(aSI: TShadowItem); 1952var 1953 prevMI: TMenuItem; 1954begin 1955 prevMI:=GetPreviousNonSepItem(aSI.RealItem); 1956 if (prevMI <> nil) then 1957 FShadowMenu.SetSelectedMenuItem(prevMI, False, False); 1958end; 1959 1960procedure TShadowBox.SelectSuccessor(aSI: TShadowItem); 1961var 1962 nextMI: TMenuItem; 1963begin 1964 nextMI:=GetNextNonSepItem(aSI.RealItem); 1965 if (nextMI <> nil) then 1966 FShadowMenu.SetSelectedMenuItem(nextMI, False, False); 1967end; 1968 1969procedure TShadowBox.AddItemAndShadow(existingSI: TShadowItem; 1970 addBefore: boolean; isSeparator: boolean); 1971var 1972 idx: integer; 1973 newMI: TMenuItem; 1974begin 1975 FShadowMenu.HideFakes; 1976 idx:=existingSI.RealItem.MenuIndex; 1977 if not addBefore then 1978 Inc(idx); 1979 newMI:=TMenuItem.Create(FShadowMenu.LookupRoot); 1980 newMI.Name:=FShadowMenu.FEditorDesigner.CreateUniqueComponentName(newMI.ClassName); 1981 if isSeparator then 1982 newMI.Caption:=cLineCaption 1983 else 1984 newMI.Caption:=newMI.Name; 1985 existingSI.RealItem.Parent.Insert(idx, newMI); 1986 TShadowItem.CreateWithBoxAndItem(FShadowMenu, existingSI.ParentBox, newMI); 1987 FShadowMenu.UpdateBoxLocationsAndSizes; 1988 FShadowMenu.FDesigner.FGui.AddingItem := True; 1989 GlobalDesignHook.PersistentAdded(newMI, not isSeparator); 1990 GlobalDesignHook.Modified(newMI); 1991 FShadowMenu.FDesigner.FGui.AddingItem := False; 1992 FShadowMenu.SetSelectedMenuItem(newMI, False, False); 1993 if not isSeparator then 1994 FShadowMenu.FDesigner.FGui.UpdateStatistics; 1995 FShadowMenu.UpdateActionsEnabledness; 1996end; 1997 1998procedure TShadowBox.RemoveAllSeparators; 1999var 2000 mi, nearestMI: TMenuItem; 2001 i, sepCount: integer; 2002 si: TShadowItemBase; 2003begin 2004 if (IsMainMenu and (Self = FShadowMenu.RootBox)) then 2005 Exit; 2006 sepCount:=GetChildSeparatorCount(FParentMenuItem); 2007 if (sepCount > 0) then begin 2008 FShadowMenu.HideFakes; 2009 ShowAllUnSelected; 2010 nearestMI:=FShadowMenu.SelectedMenuItem; 2011 if assigned(nearestMI) and nearestMI.IsLine then begin 2012 nearestMI:=GetNextNonSepItem(FShadowMenu.SelectedMenuItem); 2013 if (nearestMI = nil) then 2014 nearestMI:=GetPreviousNonSepItem(FShadowMenu.SelectedMenuItem); 2015 end 2016 else 2017 FShadowMenu.SelectedMenuItem := nil; 2018 if (nearestMI = nil) then 2019 nearestMI:=FParentMenuItem; 2020 for i:=ParentMenuItem.Count-1 downto 0 do 2021 begin 2022 mi:=ParentMenuItem.Items[i]; 2023 if mi.IsLine then 2024 begin 2025 si:=FShadowMenu.GetShadowForMenuItem(mi); 2026 Assert(si<>nil,'TShadowBox.RemoveAllSeparators: shadow for separator is nil'); 2027 FShadowList.Remove(si); 2028 Application.ReleaseComponent(si); 2029 ParentMenuItem.Remove(mi); 2030 FShadowMenu.FEditorDesigner.PropertyEditorHook.DeletePersistent(TPersistent(mi)); 2031 end; 2032 end; 2033 if (ShadowList.Count = 0) then 2034 FShadowMenu.RemoveEmptyBox(Self) 2035 else begin 2036 FShadowMenu.UpdateBoxLocationsAndSizes; 2037 FShadowMenu.SetSelectedMenuItem(nearestMI, False, True); 2038 LocateShadows; 2039 end; 2040 end; 2041end; 2042 2043procedure TShadowBox.LocateShadows; 2044var 2045 si: TShadowItemBase; 2046 len, t, w, h: integer; 2047begin 2048 if (ShadowList.Count = 0) then 2049 Exit; 2050 FShadowList.Sort(@SortByItemMenuIndex); 2051 DisableAutoSizing; 2052 if IsMenuBar then begin 2053 len:=0; 2054 for si in FShadowList do begin 2055 w:=si.GetWidth; 2056 si.SetBounds(len, 0, w, MenuBar_Height); 2057 Inc(len, w); 2058 end; 2059 end 2060 else begin 2061 w:=GetInnerDims.x; 2062 t:=1; 2063 for si in FShadowList do begin 2064 h:=si.GetHeight; 2065 si.SetBounds(1, t, w, h); 2066 Inc(t, h); 2067 end; 2068 end; 2069 EnableAutoSizing; 2070end; 2071 2072constructor TShadowBox.CreateWithParentBox(aSMenu: TShadowMenu; 2073 aParentBox: TShadowBox; aParentItem: TMenuItem); 2074begin 2075 inherited Create(aSMenu, aParentItem); 2076 Name := 'ShadowBox' + IntToStr(ShadowBoxID); 2077 Inc(ShadowBoxID); 2078 FShadowMenu := aSMenu; 2079 FParentBox := aParentBox; 2080 if (FParentBox = nil) then 2081 FLevel:=0 2082 else 2083 FLevel := aParentBox.Level + 1; 2084 Canvas.Pen.Color := clLtGray; 2085 Canvas.Brush.Color := clBtnFace; 2086 FShadowMenu.BoxList.Add(Self); 2087 Parent := FShadowMenu; 2088end; 2089 2090procedure TShadowBox.SetUnCheckedAllExcept(aMI: TMenuItem); 2091var 2092 i: integer; 2093begin 2094 if (aMI = nil) or (FShadowMenu.GetParentBoxForMenuItem(aMI) <> Self) or 2095 (FParentMenuItem = nil) then 2096 Exit; 2097 for i:=0 to Pred(FParentMenuItem.Count) do 2098 begin 2099 if (FParentMenuItem.Items[i] = aMI) then 2100 Continue; 2101 if FParentMenuItem.Items[i].RadioItem and 2102 (FParentMenuItem.Items[i].GroupIndex = aMI.GroupIndex) then 2103 begin 2104 FParentMenuItem.Items[i].Checked:=False; 2105 FShadowMenu.FEditorDesigner.PropertyEditorHook.RefreshPropertyValues; 2106 FShadowMenu.GetShadowForMenuItem(FParentMenuItem.Items[i]).Invalidate; 2107 end; 2108 end; 2109end; 2110 2111{ TShadowItem } 2112 2113function TShadowItem.GetWidth: integer; 2114var 2115 w: integer; 2116begin 2117 w:=FShadowMenu.GetStringWidth(FRealItem.Caption, FRealItem.Default); 2118 if FRealItem.IsInMenuBar then 2119 Result:=w + Double_MenuBar_Text_Offset + FShadowMenu.GetMenuBarIconWidth(FRealItem) 2120 else 2121 Result:=w + Double_DropDown_Text_Offset + GetShortcutWidth + Add_Icon_Width; 2122end; 2123 2124procedure TShadowItem.Invalidate; 2125var 2126 OldHeight, NewHeight: Integer; 2127begin 2128 OldHeight := Height; 2129 NewHeight := GetHeight; 2130 if OldHeight <> NewHeight then 2131 begin 2132 Height := NewHeight; 2133 FParentBox.LocateShadows; 2134 end; 2135 inherited Invalidate; 2136end; 2137 2138function TShadowItem.HasChildBox(out aChildBox: TShadowBoxBase): boolean; 2139begin 2140 aChildBox:=nil; 2141 Result:=(FRealItem.Count > 0); 2142 if Result then begin 2143 aChildBox:=FShadowMenu.GetBoxWithParentItem(FRealItem); 2144 Assert(aChildBox<>nil,'TShadowItem.HasChildBox: children exist but not the container for them'); 2145 end; 2146end; 2147 2148procedure TShadowItem.RecursiveHideChildren(aMI: TMenuItem); 2149var 2150 container: TShadowBoxBase; 2151 firstChild: TMenuItem; 2152begin 2153 container:=FShadowMenu.GetParentBoxForMenuItem(aMI); 2154 Assert(container<>nil,'TShadowItem.HideChildren: missing parent box for '+aMI.Caption); 2155 container.Hide; 2156 if (aMI.Count > 0) then begin 2157 firstChild:=aMI.Items[0]; 2158 Assert(firstChild<>nil,'TShadowItem.HideChildren: missing child'); 2159 RecursiveHideChildren(firstChild); 2160 end; 2161end; 2162 2163procedure TShadowItem.HideChildren; 2164var 2165 child: TMenuItem; 2166begin 2167 if (FRealItem.Count > 0) then begin 2168 child:=FRealItem.Items[0]; 2169 Assert(child<>nil,'TShadowItem.HideChildren: missing child'); 2170 RecursiveHideChildren(child); 2171 end; 2172end; 2173 2174procedure TShadowItem.DblClick; 2175begin 2176 inherited DblClick; 2177 FShadowMenu.AddOnClick(nil); 2178end; 2179 2180function TShadowItem.GetIsInMenuBar: boolean; 2181begin 2182 Result:=FRealItem.IsInMenuBar; 2183end; 2184 2185function TShadowItem.GetIsMainMenu: boolean; 2186begin 2187 Result:=FShadowMenu.IsMainMenu; 2188end; 2189 2190function TShadowItem.GetLevel: integer; 2191begin 2192 Result:=FParentBox.Level; 2193end; 2194 2195function TShadowItem.GetBottomFake: TFake; 2196begin 2197 Result:=nil; 2198 if (FShadowMenu.SelectedShadowItem = Self) then 2199 case FRealItem.IsInMenuBar of 2200 False: if (FShadowMenu.AddItemFake.Visible) then 2201 Result:=FBottomFake; 2202 True: if (FShadowMenu.AddSubMenuFake.Visible) then 2203 Result:=FBottomFake; 2204 end; 2205end; 2206 2207function TShadowItem.GetRightFake: TFake; 2208begin 2209 Result:=nil; 2210 if (FShadowMenu.SelectedShadowItem = Self) then 2211 case FRealItem.IsInMenuBar of 2212 False: if (FShadowMenu.AddSubMenuFake.Visible) then 2213 Result:=FRightFake; 2214 True: if FShadowMenu.AddItemFake.Visible then 2215 Result:=FRightFake; 2216 end; 2217end; 2218 2219function TShadowItem.GetShortcutWidth: integer; 2220var 2221 hasSC, hasSC2: boolean; 2222begin 2223 Result:=0; 2224 if FRealItem.IsInMenuBar then 2225 Exit; 2226 hasSC:=(FRealItem.ShortCut <> 0); 2227 if hasSC then 2228 Inc(Result, FShadowMenu.GetStringWidth(ShortCutToText(FRealItem.ShortCut), 2229 FRealItem.Default)); 2230 hasSC2:=(FRealItem.ShortCutKey2 <> 0); 2231 if hasSC2 then 2232 Inc(Result, FShadowMenu.GetStringWidth(ShortCutToText(FRealItem.ShortCutKey2), 2233 FRealItem.Default)); 2234 if (hasSC or hasSC2) then 2235 Inc(Result, Shortcut_Offset); 2236 if (hasSC and hasSC2) then 2237 Inc(Result, FShadowMenu.GetStringWidth(', ', False)); 2238end; 2239 2240function TShadowItem.GetShowingBottomFake: boolean; 2241begin 2242 Result:=(BottomFake <> nil) and BottomFake.Visible; 2243end; 2244 2245function TShadowItem.GetShowingRightFake: boolean; 2246begin 2247 Result:=(RightFake <> nil) and RightFake.Visible; 2248end; 2249 2250function TShadowItem.GetIconTopLeft: TPoint; 2251begin 2252 Result:=Point(1, 1); 2253 if (FShadowMenu.FMenu.Images.Height < ClientHeight) then 2254 Result.y:=(ClientHeight - FShadowMenu.FMenu.Images.Height) div 2; 2255 if (FShadowMenu.FMenu.Images.Width < Gutter_X) then 2256 Result.x:=(Gutter_X - FShadowMenu.FMenu.Images.Width) div 2; 2257end; 2258 2259function TShadowItem.GetBitmapLeftTop: TPoint; 2260begin 2261 Result:=Point(1, 1); 2262 if (FRealItem.Bitmap.Height < ClientHeight) then 2263 Result.y:=(ClientHeight - FRealItem.Bitmap.Height) div 2; 2264 if (FRealItem.Bitmap.Width < Gutter_X) then 2265 Result.x:=(Gutter_X - FRealItem.Bitmap.Width) div 2; 2266end; 2267 2268function TShadowItem.GetSubImagesIconTopLeft: TPoint; 2269begin 2270 Result:=Point(1, 1); 2271 if (FRealItem.Parent.SubMenuImages.Height < ClientHeight) then 2272 Result.y:=(ClientHeight - FRealItem.Parent.SubMenuImages.Height) div 2; 2273 if (FRealItem.Parent.SubMenuImages.Width < Gutter_X) then 2274 Result.x:=(Gutter_X - FRealItem.Parent.SubMenuImages.Width) div 2; 2275end; 2276 2277procedure TShadowItem.Paint; 2278var 2279 r, gutterR: TRect; 2280 textFlags: integer = DT_VCENTER or DT_SINGLELINE or DT_EXPANDTABS or DT_CENTER; 2281 tStyle: TTextStyle; 2282 s: string; 2283 2284 procedure DrawMenuBarItem; 2285 var 2286 oldFontStyle: TFontStyles; 2287 oldFontColor: TColor; 2288 x, y: integer; 2289 sz: TSize; 2290 pt: TPoint; 2291 dets: TThemedElementDetails; 2292 begin 2293 if (FState = dsSelected) then begin 2294 Canvas.Brush.Color:=clHighlight; 2295 Canvas.FillRect(r); 2296 sz:=Canvas.TextExtent(s); 2297 y:=(r.Bottom - r.Top - sz.cy) div 2; 2298 x:=(r.Right - r.Left - sz.cx) div 2; 2299 if FRealItem.HasIcon and (FRealItem.ImageIndex > -1) and (FShadowMenu.FMenu.Images <> nil) then begin 2300 pt:=GetIconTopLeft; 2301 FShadowMenu.FMenu.Images.DrawForControl(Canvas, 0, pt.y, FRealItem.ImageIndex, FShadowMenu.FMenu.ImagesWidth, Self); 2302 Inc(x, MenuBar_Text_Offset); 2303 end 2304 else if (FRealItem.Bitmap <> nil) and not FRealItem.Bitmap.Empty then begin 2305 pt:=GetBitmapLeftTop; 2306 Canvas.Draw(0, pt.y, RealItem.Bitmap); 2307 Inc(x, MenuBar_Text_Offset); 2308 end; 2309 oldFontStyle:=Canvas.Font.Style; 2310 if FRealItem.Default then 2311 Canvas.Font.Style:=[fsBold] 2312 else Canvas.Font.Style:=[]; 2313 oldFontColor:=Canvas.Font.Color; 2314 Canvas.Font.Color:=clHighlightText; 2315 Canvas.TextRect(r, x, y, s, tStyle); 2316 Canvas.Font.Color:=oldFontColor; 2317 Canvas.Font.Style:=oldFontStyle; 2318 end 2319 else begin 2320 InflateRect(r, 1, 0); // hack needed only on Windows? 2321 case FState of 2322 dsNormal: dets:=ThemeServices.GetElementDetails(tmBarBackgroundActive); 2323 dsSelected: dets:=ThemeServices.GetElementDetails(tmBarItemPushed); 2324 dsDisabled: dets:=ThemeServices.GetElementDetails(tmBarItemDisabled); 2325 end; 2326 ThemeServices.DrawElement(Canvas.Handle, dets, r); 2327 if FRealItem.HasIcon and (FRealItem.ImageIndex > -1) and (FShadowMenu.FMenu.Images <> nil) then 2328 ThemeServices.DrawIcon(Canvas, dets, Point(0,0), FShadowMenu.FMenu.Images, FRealItem.ImageIndex, 0, Self) 2329 else if (FRealItem.Bitmap <> nil) and not FRealItem.Bitmap.Empty then begin 2330 pt:=GetBitmapLeftTop; 2331 Canvas.Draw(pt.x, pt.y, RealItem.Bitmap); 2332 end; 2333 r.Left:=FShadowMenu.GetMenuBarIconWidth(FRealItem); 2334 if FRealItem.Default then begin 2335 oldFontStyle:=Canvas.Font.Style; 2336 Canvas.Font.Style:=[fsBold]; 2337 end; 2338 ThemeServices.DrawText(Canvas, dets, FRealItem.Caption, r, textFlags, 0); 2339 if (FState = dsDisabled) then begin // perhaps this display hack is only needed on Windows? 2340 Canvas.Pen.Color:=clBtnShadow; 2341 Canvas.Line(0, MenuBar_Height-1, ClientWidth, MenuBar_Height-1); 2342 end; 2343 if FRealItem.Default then 2344 Canvas.Font.Style:=oldFontStyle; 2345 end; 2346 end; 2347 2348 procedure DrawBackgroundAndGutter; 2349 begin 2350 case FState of 2351 dsNormal, dsDisabled: Canvas.Brush.Color:=clBtnFace; 2352 dsSelected: Canvas.Brush.Color:=clHighlight; 2353 end; 2354 if FRealItem.IsLine and (FState = dsSelected) then 2355 Canvas.FillRect(r.Left, r.Top+2, r.Right, r.Bottom+2) 2356 else 2357 Canvas.FillRect(r); 2358 gutterR:=Rect(Gutter_X, 0, Gutter_X+1, ClientHeight); 2359 LCLIntf.DrawEdge(Canvas.Handle, gutterR, EDGE_ETCHED, BF_LEFT); 2360 end; 2361 2362 procedure DrawCheckMarkIcon; 2363 var 2364 pt: TPoint; 2365 dets: TThemedElementDetails; 2366 begin 2367 if FRealItem.Checked then begin 2368 gutterR:=r; 2369 gutterR.Right:=Gutter_X; 2370 if FRealItem.RadioItem then // radioItem 2371 case FState of 2372 dsNormal: begin 2373 dets:=ThemeServices.GetElementDetails(tmPopupCheckBackgroundNormal); 2374 ThemeServices.DrawElement(Canvas.Handle, dets, gutterR); 2375 dets:=ThemeServices.GetElementDetails(tmPopupBulletNormal); 2376 ThemeServices.DrawElement(Canvas.Handle, dets, gutterR); 2377 end; 2378 dsSelected: begin 2379 dets:=ThemeServices.GetElementDetails(tmPopupItemHot); 2380 ThemeServices.DrawElement(Canvas.Handle, dets, gutterR); 2381 dets:=ThemeServices.GetElementDetails(tmPopupBulletNormal); 2382 ThemeServices.DrawElement(Canvas.Handle, dets, gutterR); 2383 end; 2384 dsDisabled: begin 2385 dets:=ThemeServices.GetElementDetails(tmPopupCheckBackgroundDisabled); 2386 ThemeServices.DrawElement(Canvas.Handle, dets, gutterR); 2387 dets:=ThemeServices.GetElementDetails(tmPopupBulletDisabled); 2388 ThemeServices.DrawElement(Canvas.Handle, dets, gutterR); 2389 end; 2390 end 2391 else begin // checkmark 2392 dets:=ThemeServices.GetElementDetails(tmPopupCheckBackgroundNormal); 2393 ThemeServices.DrawElement(Canvas.Handle, dets, gutterR); 2394 dets:=ThemeServices.GetElementDetails(tmPopupCheckMarkNormal); 2395 ThemeServices.DrawElement(Canvas.Handle, dets, gutterR); 2396 end; 2397 end 2398 else // not checked 2399 if FRealItem.HasIcon and (FRealItem.GlyphShowMode<>gsmNever) and 2400 (FRealItem.ImageIndex > -1) and (FShadowMenu.FMenu.Images <> nil) and 2401 (FRealItem.ImageIndex < FShadowMenu.FMenu.Images.Count) then 2402 ThemeServices.DrawIcon(Canvas, dets, GetIconTopLeft, 2403 FShadowMenu.FMenu.Images, FRealItem.ImageIndex, 0, Self) 2404 else 2405 if (FRealItem.ImageIndex > -1) and (FParentBox.Level > 0) and 2406 (FRealItem.Parent.SubMenuImages <> nil) and 2407 (FRealItem.ImageIndex < FRealItem.Parent.SubMenuImages.Count) then 2408 ThemeServices.DrawIcon(Canvas, dets, GetSubImagesIconTopLeft, 2409 RealItem.Parent.SubMenuImages, RealItem.ImageIndex, 0, Self) 2410 else if FRealItem.HasBitmap and not FRealItem.Bitmap.Empty then begin 2411 pt:=GetBitmapLeftTop; 2412 Canvas.Draw(pt.x, pt.y, RealItem.Bitmap); 2413 end; 2414 end; 2415 2416 procedure DrawText; 2417 var 2418 oldFontColor: TColor; 2419 oldFontStyle: TFontStyles; 2420 s1, s2: string; 2421 sc1, sc2: boolean; 2422 x, y: integer; 2423 begin 2424 Canvas.Brush.Style:=bsClear; 2425 if FRealItem.RightJustify then 2426 textFlags:=textFlags or DT_RIGHT 2427 else 2428 textFlags:=textFlags or DT_LEFT; 2429 r.Left:=DropDown_Text_Offset; 2430 oldFontStyle:=Canvas.Font.Style; 2431 if FRealItem.Default then 2432 Canvas.Font.Style:=[fsBold] 2433 else Canvas.Font.Style:=[]; 2434 x:=DropDown_Text_Offset; 2435 y:=(Height-Canvas.TextHeight(s)) div 2; 2436 case FState of 2437 dsNormal: Canvas.TextRect(r, x, y, s, tStyle); 2438 dsSelected: begin 2439 OldFontColor:=Canvas.Font.Color; 2440 Canvas.Font.Color:=clHighlightText; 2441 Canvas.TextRect(r, x, y, s, tStyle); 2442 Canvas.Font.Color:=oldFontColor; 2443 end; 2444 dsDisabled: begin 2445 OldFontColor:=Canvas.Font.Color; 2446 Canvas.Font.Color:=clBtnShadow; 2447 Canvas.TextRect(r, x, y, s, tStyle); 2448 Canvas.Font.Color:=OldFontColor; 2449 end; 2450 end; 2451 2452 sc1:=(FRealItem.ShortCut <> 0); 2453 if sc1 then 2454 s1:=ShortCutToText(FRealItem.Shortcut); 2455 sc2:=(FRealItem.ShortCutKey2 <> 0); 2456 if sc2 then 2457 s2:=ShortCutToText(FRealItem.ShortCutKey2); 2458 if sc1 or sc2 then //#todo allow for rightjustify? 2459 begin 2460 if sc1 and not sc2 then 2461 s:=s1 2462 else if sc2 and not sc1 then 2463 s:=s2 2464 else 2465 s:=s1 + ', ' + s2; 2466 x:=r.Right - Canvas.TextWidth(s) - DropDown_Height; 2467 case FState of 2468 dsNormal: Canvas.TextRect(r, x, y, s, tStyle); 2469 dsSelected: begin 2470 OldFontColor:=Canvas.Font.Color; 2471 Canvas.Font.Color:=clHighlightText; 2472 Canvas.TextRect(r, x, y, s, tStyle); 2473 Canvas.Font.Color:=oldFontColor; 2474 end; 2475 dsDisabled: begin 2476 OldFontColor:=Canvas.Font.Color; 2477 Canvas.Font.Color:=clBtnShadow; 2478 Canvas.TextRect(r, x, y, s, tStyle); 2479 Canvas.Font.Color:=OldFontColor; 2480 end; 2481 end; 2482 end; 2483 Canvas.Font.Style:=oldFontStyle; 2484 end; 2485 2486 procedure DrawChevron; 2487 var 2488 pts: array of TPoint; 2489 oldBrushColor, oldPenColor: TColor; 2490 begin 2491 { ToDo: This should be done by theme services 2492 but it must be implemented for different widgetsets first. 2493 dets:=ThemeServices.GetElementDetails(tmPopupSubmenuNormal); 2494 ThemeServices.DrawElement(Canvas.Handle, dets, r); 2495 } 2496 r.Right:=ClientWidth; 2497 r.Left:=r.Right - MenuBar_Height; 2498 SetLength(pts, 4); 2499 pts[0]:=Point(r.Left, ScaleY(9, 96)); 2500 pts[1]:=Point(r.Left + ScaleX(4, 96), ScaleY(12, 96)); 2501 pts[2]:=Point(r.Left, ScaleY(15, 96)); 2502 pts[3]:=pts[0]; 2503 oldBrushColor:=Canvas.Brush.Color; 2504 oldPenColor:=Canvas.Pen.Color; 2505 if (FState = dsSelected) then begin 2506 Canvas.Pen.Color:=clHighlightText; 2507 Canvas.Brush.Color:=clHighlightText; 2508 end 2509 else begin 2510 Canvas.Brush.Color:=clBlack; 2511 Canvas.Pen.Color:=clBlack; 2512 end; 2513 Canvas.Polygon(pts); 2514 Canvas.Brush.Color:=oldBrushColor; 2515 Canvas.Pen.Color:=oldPenColor; 2516 end; 2517 2518var 2519 alygn: TAlignment; 2520begin 2521 if FParentBox.Updating then Exit; 2522 r:=ClientRect; 2523 if FRealItem.RightJustify then 2524 alygn:=taRightJustify 2525 else 2526 alygn:=taLeftJustify; 2527 if (FRealItem.Caption = '') then 2528 s:=FRealItem.Name 2529 else 2530 s:=FRealItem.Caption; 2531 FillChar(tStyle{%H-}, SizeOf(tStyle), 0); 2532 with tStyle do begin 2533 Alignment:=BidiFlipAlignment(alygn, UseRightToLeftAlignment); 2534 Layout:=tlCenter; 2535 SingleLine:=True; 2536 Clipping:=True; 2537 ShowPrefix:=True; 2538 RightToLeft:=UseRightToLeftReading; 2539 ExpandTabs:=True; 2540 end; 2541 if FRealItem.IsInMenuBar then 2542 DrawMenuBarItem 2543 else begin 2544 DrawBackgroundAndGutter; 2545 if FRealItem.IsLine then begin 2546 gutterR:=Rect(Gutter_X, Separator_Centre, ClientWidth, Separator_Centre); 2547 LCLIntf.DrawEdge(Canvas.Handle, gutterR, EDGE_ETCHED, BF_TOP); 2548 Exit; 2549 end; 2550 if (FRealItem.Checked or FRealItem.HasIcon) then 2551 DrawCheckMarkIcon; 2552 DrawText; 2553 if (FRealItem.Count > 0) then 2554 DrawChevron; 2555 end; 2556end; 2557 2558procedure TShadowItem.KeyDown(var Key: Word; Shift: TShiftState); 2559begin 2560 if (Shift = []) then 2561 case Key of 2562 VK_LEFT: begin 2563 if IsInMenuBar then 2564 FParentBox.SelectPrevious(Self) 2565 else if (IsMainMenu and (Level > 1)) or (not IsMainMenu and (level > 0)) then 2566 FShadowMenu.SetSelectedMenuItem(FParentBox.ParentMenuItem, False, False); 2567 Key:=0; 2568 end; 2569 VK_RIGHT: begin 2570 if IsInMenuBar then 2571 FParentBox.SelectSuccessor(Self) 2572 else if (FRealItem.Count > 0) then begin 2573 ShowChildBox; 2574 FShadowMenu.SetSelectedMenuItem(FRealItem.Items[0], False, False); 2575 end; 2576 Key:=0; 2577 end; 2578 VK_DOWN: begin 2579 if IsInMenuBar and (FRealItem.Count > 0) then begin 2580 ShowChildBox; 2581 FShadowMenu.SetSelectedMenuItem(FRealItem.Items[0], False, False); 2582 end 2583 else FParentBox.SelectSuccessor(Self); 2584 Key:=0; 2585 end; 2586 VK_UP: begin 2587 if (FRealItem.MenuIndex = 0) and FParentBox.ParentMenuItem.IsInMenuBar then 2588 FShadowMenu.SetSelectedMenuItem(FParentBox.ParentMenuItem, False, False) 2589 else if not IsInMenuBar then 2590 FParentBox.SelectPrevious(Self); 2591 Key:=0; 2592 end; 2593 VK_DELETE: begin 2594 Key:=0; 2595 FShadowMenu.DeleteItem(Self); 2596 end; 2597 else inherited KeyDown(Key, Shift); 2598 end // case 2599 else inherited KeyDown(Key, Shift); 2600end; 2601 2602procedure TShadowItem.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); 2603begin 2604 if Button = mbLeft then begin 2605 FRealItem.Click; 2606 FShadowMenu.FEditorDesigner.PropertyEditorHook.RefreshPropertyValues; 2607 end; 2608 if (FState = dsSelected) then 2609 SetFocus 2610 else 2611 FShadowMenu.SetSelectedMenuItem(FRealItem, False, False); 2612 inherited MouseDown(Button, Shift, X, Y); 2613end; 2614 2615procedure TShadowItem.ShowChainToRoot; 2616var 2617 sb: TShadowBoxBase; 2618begin 2619 sb:=FParentBox; 2620 while (sb <> FShadowMenu.RootBox) do begin 2621 sb.Show; 2622 sb:=sb.ParentBox; 2623 end; 2624end; 2625 2626procedure TShadowItem.HideChainFromRoot; 2627var 2628 sb: TShadowBoxBase; 2629begin 2630 sb:=FParentBox; 2631 while (sb <> FShadowMenu.RootBox) do begin 2632 sb.Hide; 2633 sb:=sb.ParentBox; 2634 end; 2635end; 2636 2637procedure TShadowItem.ShowChildBox; 2638var 2639 sb: TShadowBoxBase; 2640begin 2641 if HasChildBox(sb) then 2642 sb.Show; 2643end; 2644 2645constructor TShadowItem.CreateWithBoxAndItem(aSMenu: TShadowMenu; 2646 aParentBox: TShadowBox; aRealItem: TMenuItem); 2647begin 2648 inherited Create(aParentBox, aRealItem); 2649 Name:='ShadowItem' + IntToStr(ShadowItemID); 2650 Inc(ShadowItemID); 2651 FShadowMenu:=aSMenu; 2652 FParentBox:=aParentBox; 2653 FParentBox.ShadowList.Add(Self); 2654 Canvas.Brush.Color:=clBtnFace; 2655 SetInitialBounds(0, 0, GetWidth, GetHeight); 2656 if FRealItem.Enabled then 2657 FState:=dsNormal 2658 else 2659 FState:=dsDisabled; 2660 TabStop:=False; 2661 TabOrder:= -1; 2662 PopupMenu:=FShadowMenu.ItemsPopupMenu; 2663 Parent:=FParentBox; 2664 FParentBox.LocateShadows; 2665end; 2666 2667{ TMenuDesigner } 2668 2669constructor TMenuDesigner.Create; 2670begin 2671 inherited Create; 2672 FGui:=TMenuDesignerForm.Create(Self); 2673end; 2674 2675destructor TMenuDesigner.Destroy; 2676begin 2677 FreeAndNil(FGui); 2678 inherited Destroy; 2679end; 2680 2681procedure TMenuDesigner.CreateShadowMenu(aMenu: TMenu; aSelect: TMenuItem; 2682 aWidth, aHeight: integer); 2683begin 2684 FShadowMenu := TShadowMenu.Create(Self, FGui, aMenu, aSelect, aWidth, aHeight); 2685end; 2686 2687{ TMainMenuComponentEditor} 2688 2689procedure TMainMenuComponentEditor.Edit; 2690begin 2691 ShowMenuEditor(Component as TMenu); 2692end; 2693 2694function TMainMenuComponentEditor.GetVerbCount: Integer; 2695begin 2696 Result:=1; 2697end; 2698 2699function TMainMenuComponentEditor.GetVerb(Index: Integer): string; 2700begin 2701 case Index of 2702 0: Result:=lisMenuEditorMenuEditor + ' ...'; 2703 else Result:=''; 2704 end; 2705end; 2706 2707procedure TMainMenuComponentEditor.ExecuteVerb(Index: Integer); 2708begin 2709 if (Index = 0) then 2710 Edit; 2711end; 2712 2713 2714initialization 2715 RegisterComponentEditor(TMenu, TMainMenuComponentEditor); 2716 2717finalization 2718 FreeAndNil(MenuDesignerSingleton); 2719 2720end. 2721 2722