/designer/menueditorform.pas

http://github.com/graemeg/lazarus · Pascal · 1007 lines · 881 code · 90 blank · 36 comment · 99 complexity · 6225c33ad49339f1ceea7e1f8a4a972e MD5 · raw file

  1. {***************************************************************************
  2. * *
  3. * This source is free software; you can redistribute it and/or modify *
  4. * it under the terms of the GNU General Public License as published by *
  5. * the Free Software Foundation; either version 2 of the License, or *
  6. * (at your option) any later version. *
  7. * *
  8. * This code is distributed in the hope that it will be useful, but *
  9. * WITHOUT ANY WARRANTY; without even the implied warranty of *
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
  11. * General Public License for more details. *
  12. * *
  13. * A copy of the GNU General Public License is available on the World *
  14. * Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
  15. * obtain it by writing to the Free Software Foundation, *
  16. * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
  17. * *
  18. ***************************************************************************
  19. Author: Howard Page-Clark }
  20. unit MenuEditorForm;
  21. {$mode objfpc}{$H+}
  22. interface
  23. uses
  24. // FCL + LCL
  25. Classes, SysUtils, Types, typinfo,
  26. Controls, StdCtrls, ExtCtrls, Forms, Graphics, Buttons, Menus, ButtonPanel,
  27. ImgList, Themes, LCLintf, LCLProc,
  28. // IdeIntf
  29. FormEditingIntf, PropEdits,
  30. // IDE
  31. LazarusIDEStrConsts, MenuDesignerBase, MenuShortcuts;
  32. type
  33. { TMenuDesignerForm }
  34. TMenuDesignerForm = class(TForm)
  35. AddItemAboveButton: TSpeedButton;
  36. AddItemBelowButton: TSpeedButton;
  37. AddSeparatorAboveButton: TSpeedButton;
  38. AddSeparatorBelowButton: TSpeedButton;
  39. AddSubMenuButton: TSpeedButton;
  40. ButtonsGroupBox: TGroupBox;
  41. CaptionedItemsCountLabel: TLabel;
  42. DeepestNestingLevelLabel: TLabel;
  43. DeleteItemButton: TSpeedButton;
  44. GroupIndexLabel: TLabel;
  45. HelpButton: TBitBtn;
  46. IconCountLabel: TLabel;
  47. LeftPanel: TPanel;
  48. MoveItemDownButton: TSpeedButton;
  49. MoveItemUpButton: TSpeedButton;
  50. PopupAssignmentsCountLabel: TLabel;
  51. RadioGroupsLabel: TLabel;
  52. ShortcutItemsCountLabel: TLabel;
  53. StatisticsGroupBox: TGroupBox;
  54. SubmenuGroupBox: TGroupBox;
  55. procedure FormCreate(Sender: TObject);
  56. procedure FormDestroy(Sender: TObject);
  57. procedure HelpButtonClick(Sender: TObject);
  58. strict private
  59. FDesigner: TMenuDesignerBase;
  60. FEditedMenu: TMenu;
  61. FAcceleratorMenuItemsCount: integer;
  62. FCaptionedItemsCount: integer;
  63. FDeepestNestingLevel: integer;
  64. FAddingItem: Boolean;
  65. FGUIEnabled: boolean;
  66. FIconsCount: integer;
  67. FUpdateCount: integer;
  68. FPopupAssignments: TStringList;
  69. FPopupAssignmentsListBox: TListBox;
  70. function GetItemCounts(out aCaptionedItemCount, aShortcutItemCount,
  71. anIconCount, anAccelCount: integer): integer;
  72. function GetPopupAssignmentCount: integer;
  73. function GetSelectedMenuComponent(const aSelection: TPersistentSelectionList;
  74. out isTMenu: boolean; out isTMenuItem: boolean): TPersistent;
  75. procedure DisableGUI;
  76. procedure EnableGUI(selectedIsNil: boolean);
  77. procedure HidePopupAssignmentsInfo;
  78. procedure InitializeStatisticVars;
  79. procedure LoadFixedButtonGlyphs;
  80. procedure OnDesignerSetSelection(const ASelection: TPersistentSelectionList);
  81. procedure ProcessForPopup(aControl: TControl);
  82. procedure SetupPopupAssignmentsDisplay;
  83. public
  84. constructor Create(aDesigner: TMenuDesignerBase); reintroduce;
  85. destructor Destroy; override;
  86. procedure LoadVariableButtonGlyphs(isInMenubar: boolean);
  87. procedure SetMenu(aMenu: TMenu; aMenuItem: TMenuItem);
  88. procedure ShowPopupAssignmentsInfo;
  89. procedure BeginUpdate;
  90. procedure EndUpdate;
  91. function IsUpdate: Boolean;
  92. procedure UpdateStatistics;
  93. procedure UpdateSubmenuGroupBox(selMI: TMenuItem; selBox: TShadowBoxBase; boxIsRoot:boolean);
  94. procedure UpdateItemInfo(aMenu: TMenu; aMenuItem: TMenuItem;
  95. aShadowBox: TShadowBoxBase; aPropEditHook: TPropertyEditorHook);
  96. //property EditedMenu: TMenu read FEditedMenu;
  97. //property AcceleratorMenuItemsCount: integer read FAcceleratorMenuItemsCount;
  98. property AddingItem: Boolean read FAddingItem write FAddingItem;
  99. end;
  100. TRadioIconGroup = class;
  101. TRadioIconState = (risUp, risDown, risPressed, risUncheckedHot, risCheckedHot);
  102. { TRadioIcon }
  103. TRadioIcon = class(TGraphicControl)
  104. strict private
  105. FBGlyph: TButtonGlyph;
  106. FOnChange: TNotifyEvent;
  107. FRIGroup: TRadioIconGroup;
  108. FRIState: TRadioIconState;
  109. function GetChecked: Boolean;
  110. procedure SetChecked(aValue: Boolean);
  111. protected
  112. procedure DoChange;
  113. procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
  114. procedure MouseEnter; override;
  115. procedure MouseLeave; override;
  116. procedure Paint; override;
  117. public
  118. constructor CreateWithGlyph(aRIGroup: TRadioIconGroup; anImgIndex: integer);
  119. destructor Destroy; override;
  120. property Checked: Boolean read GetChecked write SetChecked;
  121. property OnChange: TNotifyEvent read FOnChange write FOnChange;
  122. end;
  123. { TRadioIconGroup }
  124. TRadioIconGroup = class(TScrollBox)
  125. strict private
  126. FItemIndex: integer;
  127. FOnSelectItem: TNotifyEvent;
  128. FRIArray: array of TRadioIcon;
  129. procedure CreateRadioItems;
  130. procedure ApplyLayout;
  131. procedure RIOnChange(Sender: TObject);
  132. procedure DoSelectItem;
  133. protected
  134. FImageList: TCustomImageList;
  135. FedSize: TSize;
  136. FedUnchecked, FedChecked, FedPressed, FedUncheckedHot, FedCheckedHot: TThemedElementDetails;
  137. FGlyphPt: TPoint;
  138. FSpacing: integer;
  139. FRadioHeight, FRadioWidth: integer;
  140. FRadioRect: TRect;
  141. procedure SetParent(NewParent: TWinControl); override;
  142. public
  143. constructor CreateWithImageList(AOwner: TComponent; anImgList: TCustomImageList);
  144. property ItemIndex: integer read FItemIndex;
  145. property OnSelectItem: TNotifyEvent read FOnSelectItem write FOnSelectItem;
  146. end;
  147. { TdlgChooseIcon }
  148. TdlgChooseIcon = class(TForm)
  149. private
  150. FButtonPanel: TButtonPanel;
  151. FRadioIconGroup: TRadioIconGroup;
  152. function GetImageIndex: integer;
  153. procedure RIGClick(Sender: TObject);
  154. public
  155. constructor Create(TheOwner: TComponent); override;
  156. procedure SetRadioIconGroup(anImageList: TCustomImageList);
  157. property ImageIndex: integer read GetImageIndex;
  158. end;
  159. function GetNestingLevelDepth(aMenu: TMenu): integer;
  160. function ChooseIconFromImageListDlg(anImageList: TCustomImageList): integer;
  161. implementation
  162. {$R *.lfm}
  163. function GetNestingLevelDepth(aMenu: TMenu): integer;
  164. procedure CheckLevel(aMI: TMenuItem; aLevel: integer);
  165. var
  166. j: integer;
  167. begin
  168. if (aMI.Count > 0) then begin
  169. if (Succ(aLevel) > Result) then
  170. Result:=Succ(aLevel);
  171. for j:=0 to aMI.Count-1 do
  172. CheckLevel(aMI.Items[j], Succ(aLevel));
  173. end;
  174. end;
  175. var
  176. i: integer;
  177. begin
  178. Result:=0;
  179. for i:=0 to aMenu.Items.Count-1 do
  180. CheckLevel(aMenu.Items[i], 0);
  181. end;
  182. function ChooseIconFromImageListDlg(anImageList: TCustomImageList): integer;
  183. var
  184. dlg: TdlgChooseIcon;
  185. begin
  186. if (anImageList = nil) or (anImageList.Count = 0) then
  187. Exit(-1);
  188. if (anImageList.Count = 1) then
  189. Exit(0);
  190. dlg := TdlgChooseIcon.Create(nil);
  191. try
  192. dlg.SetRadioIconGroup(anImageList);
  193. if (dlg.ShowModal = mrOK) then
  194. Result := dlg.ImageIndex
  195. else
  196. Result := -1;
  197. finally
  198. dlg.Free;
  199. end;
  200. end;
  201. { TMenuDesignerForm }
  202. constructor TMenuDesignerForm.Create(aDesigner: TMenuDesignerBase);
  203. begin
  204. Inherited Create(Nil); // LazarusIDE.OwningComponent
  205. FDesigner := aDesigner;
  206. end;
  207. destructor TMenuDesignerForm.Destroy;
  208. begin
  209. inherited Destroy;
  210. end;
  211. procedure TMenuDesignerForm.FormCreate(Sender: TObject);
  212. begin
  213. Name:='MenuDesignerWindow';
  214. Caption:=lisMenuEditorMenuEditor;
  215. ButtonsGroupBox.Caption:=lisMenuEditorMenuItemActions;
  216. FGUIEnabled:=False;
  217. LoadFixedButtonGlyphs;
  218. LoadVariableButtonGlyphs(True);
  219. KeyPreview:=True;
  220. GlobalDesignHook.AddHandlerSetSelection(@OnDesignerSetSelection);
  221. InitializeStatisticVars;
  222. SetupPopupAssignmentsDisplay;
  223. end;
  224. procedure TMenuDesignerForm.FormDestroy(Sender: TObject);
  225. begin
  226. FreeAndNil(FPopupAssignments);
  227. end;
  228. procedure TMenuDesignerForm.HelpButtonClick(Sender: TObject);
  229. const
  230. helpPath = 'http://wiki.lazarus.freepascal.org/IDE_Window:_Menu_Editor';
  231. begin
  232. //LazarusHelp.ShowHelpForIDEControl(Self);
  233. OpenURL(helpPath);
  234. end;
  235. procedure TMenuDesignerForm.OnDesignerSetSelection(const ASelection: TPersistentSelectionList);
  236. var
  237. mnu: TMenu;
  238. mi, tmp: TMenuItem;
  239. isTMenu, isTMenuItem: boolean;
  240. persist: TPersistent;
  241. begin
  242. if FUpdateCount > 0 then
  243. Exit; // This event will be executed after all updates, look at EndUpdate
  244. persist:=GetSelectedMenuComponent(ASelection, isTMenu, isTMenuItem);
  245. if (persist <> nil) then
  246. begin
  247. if isTMenu then
  248. SetMenu(TMenu(persist), nil)
  249. else if isTMenuItem then begin
  250. mi:=TMenuItem(persist);
  251. tmp:=mi;
  252. while (tmp.Parent <> nil) do
  253. tmp:=tmp.Parent;
  254. mnu:=tmp.Menu;
  255. if (mnu = nil) then
  256. mnu:=mi.GetParentMenu;
  257. if (mnu = FEditedMenu) and (FDesigner.ShadowMenu <> nil) then
  258. FDesigner.ShadowMenu.SetSelectedMenuItem(mi, True, False)
  259. else if (mnu <> nil) then
  260. SetMenu(mnu, mi);
  261. end;
  262. end
  263. else if not AddingItem then
  264. SetMenu(nil, nil);
  265. end;
  266. procedure TMenuDesignerForm.ShowPopupAssignmentsInfo;
  267. var
  268. count: integer;
  269. begin
  270. if (FEditedMenu <> nil) and (FEditedMenu is TPopupMenu) then begin
  271. count:=GetPopupAssignmentCount;
  272. PopupAssignmentsCountLabel.Enabled:=True;
  273. if (count > 0) then
  274. PopupAssignmentsCountLabel.BorderSpacing.Bottom:=0
  275. else
  276. PopupAssignmentsCountLabel.BorderSpacing.Bottom:=Double_Margin;
  277. if (count= -1) then
  278. PopupAssignmentsCountLabel.Caption:=Format(lisMenuEditorPopupAssignmentsS,[lisMenuEditorNA])
  279. else
  280. PopupAssignmentsCountLabel.Caption:=Format(lisMenuEditorPopupAssignmentsS, [IntToStr(count)]);
  281. if (count > 0) then begin
  282. FPopupAssignmentsListBox.Items.Assign(FPopupAssignments);
  283. FPopupAssignmentsListBox.Visible:=True;
  284. end
  285. else
  286. FPopupAssignmentsListBox.Visible:=False;
  287. end;
  288. end;
  289. procedure TMenuDesignerForm.HidePopupAssignmentsInfo;
  290. begin
  291. if (FEditedMenu <> nil) and (FEditedMenu is TMainMenu) then begin
  292. PopupAssignmentsCountLabel.Caption:=Format(lisMenuEditorPopupAssignmentsS,[lisMenuEditorNA]);
  293. PopupAssignmentsCountLabel.Enabled:=False;
  294. FPopupAssignmentsListBox.Visible:=False;
  295. end;
  296. end;
  297. procedure TMenuDesignerForm.SetupPopupAssignmentsDisplay;
  298. begin
  299. FPopupAssignmentsListBox:=TListBox.Create(Self);
  300. with FPopupAssignmentsListBox do begin
  301. Name:='FPopupAssignmentsListBox';
  302. Color:=clBtnFace;
  303. BorderSpacing.Top:=2;
  304. BorderSpacing.Left:=3*Margin;
  305. BorderSpacing.Right:=Margin;
  306. BorderSpacing.Bottom:=Margin;
  307. Anchors:=[akTop, akLeft, akRight];
  308. AnchorSideLeft.Control:=StatisticsGroupBox;
  309. AnchorSideTop.Control:=PopupAssignmentsCountLabel;
  310. AnchorSideTop.Side:=asrBottom;
  311. AnchorSideRight.Control:=StatisticsGroupBox;
  312. AnchorSideRight.Side:=asrBottom;
  313. ParentFont:=False;
  314. TabStop:=False;
  315. BorderStyle:=bsNone;
  316. ExtendedSelect:=False;
  317. Height:=45;
  318. Parent:=StatisticsGroupBox;
  319. Visible:=False;
  320. end;
  321. end;
  322. function TMenuDesignerForm.GetItemCounts(out aCaptionedItemCount,
  323. aShortcutItemCount, anIconCount, anAccelCount: integer): integer;
  324. var
  325. imgCount: integer;
  326. procedure ProcessItems(aMI: TMenuItem);
  327. var
  328. i: integer;
  329. sc: TShortCut;
  330. begin
  331. Inc(Result);
  332. if not aMI.IsLine and (aMI.Caption <> '') then begin
  333. Inc(aCaptionedItemCount);
  334. if HasAccelerator(aMI.Caption, sc) then
  335. Inc(anAccelCount);
  336. end;
  337. if (aMI.ShortCut <> 0) or (aMI.ShortCutKey2 <> 0) then
  338. Inc(aShortcutItemCount);
  339. if (imgCount > 0) and (aMI.ImageIndex > -1) and (aMI.ImageIndex < imgCount) then
  340. Inc(anIconCount)
  341. else if aMI.HasBitmap and not aMI.Bitmap.Empty then
  342. Inc(anIconCount);
  343. for i:=0 to aMI.Count-1 do
  344. ProcessItems(aMI.Items[i]); // Recursive call for sub-menus.
  345. end;
  346. var
  347. i: integer;
  348. begin
  349. if (FEditedMenu = nil) then
  350. Exit;
  351. aCaptionedItemCount:=0;
  352. aShortcutItemCount:=0;
  353. anIconCount:=0;
  354. anAccelCount:=0;
  355. if (FEditedMenu.Images <> nil) and (FEditedMenu.Images.Count > 0) then
  356. imgCount:=FEditedMenu.Images.Count
  357. else
  358. imgCount:=0;
  359. Result:=0;
  360. for i:=0 to FEditedMenu.Items.Count-1 do
  361. ProcessItems(FEditedMenu.Items[i]);
  362. end;
  363. function TMenuDesignerForm.GetSelectedMenuComponent(const aSelection: TPersistentSelectionList;
  364. out isTMenu: boolean; out isTMenuItem: boolean): TPersistent;
  365. begin
  366. if (aSelection.Count = 1) then begin
  367. if (aSelection.Items[0] is TMenu) then
  368. begin
  369. isTMenu:=True;
  370. isTMenuItem:=False;
  371. Result:=aSelection.Items[0];
  372. end
  373. else
  374. if (aSelection.Items[0] is TMenuItem) then
  375. begin
  376. isTMenu:=False;
  377. isTMenuItem:=True;
  378. Result:=aSelection.Items[0];
  379. end
  380. else begin
  381. isTMenu:=False;
  382. isTMenuItem:=False;
  383. Result:=nil;
  384. end;
  385. end
  386. else
  387. Result:=nil;
  388. end;
  389. procedure TMenuDesignerForm.ProcessForPopup(aControl: TControl);
  390. var
  391. wc: TWinControl;
  392. j:integer;
  393. begin
  394. if (aControl.PopupMenu = FEditedMenu) and (aControl.Name <> '') then
  395. FPopupAssignments.Add(aControl.Name);
  396. if (aControl is TWinControl) then begin
  397. wc:=TWinControl(aControl);
  398. for j:=0 to wc.ControlCount-1 do
  399. ProcessForPopup(wc.Controls[j]); // Recursive call
  400. end;
  401. end;
  402. function TMenuDesignerForm.GetPopupAssignmentCount: integer;
  403. var
  404. lookupRoot: TPersistent;
  405. begin
  406. lookupRoot:=GlobalDesignHook.LookupRoot;
  407. if (FEditedMenu is TMainMenu) or (lookupRoot is TDataModule) then
  408. Exit(-1)
  409. else begin
  410. FreeAndNil(FPopupAssignments);
  411. FPopupAssignments:=TStringList.Create;
  412. ProcessForPopup(lookupRoot as TControl);
  413. Result:=FPopupAssignments.Count;
  414. end
  415. end;
  416. procedure TMenuDesignerForm.LoadVariableButtonGlyphs(isInMenubar: boolean);
  417. begin
  418. if isInMenubar then
  419. begin
  420. MoveItemUpButton.LoadGlyphFromResourceName(HINSTANCE,'arrow_left');
  421. MoveItemDownButton.LoadGlyphFromResourceName(HINSTANCE,'arrow_right');
  422. AddItemAboveButton.LoadGlyphFromResourceName(HINSTANCE,'add_item_left');
  423. AddItemBelowButton.LoadGlyphFromResourceName(HINSTANCE,'add_item_right');
  424. AddSubMenuButton.LoadGlyphFromResourceName(HINSTANCE,'add_submenu_below');
  425. end else
  426. begin
  427. MoveItemUpButton.LoadGlyphFromResourceName(HINSTANCE,'arrow_up');
  428. MoveItemDownButton.LoadGlyphFromResourceName(HINSTANCE,'arrow_down');
  429. AddItemAboveButton.LoadGlyphFromResourceName(HINSTANCE,'add_item_above');
  430. AddItemBelowButton.LoadGlyphFromResourceName(HINSTANCE,'add_item_below');
  431. AddSubMenuButton.LoadGlyphFromResourceName(HINSTANCE,'add_submenu_right');
  432. end;
  433. UpdateSubmenuGroupBox(nil, nil, False);
  434. FDesigner.VariableGlyphsInMenuBar:=isInMenubar;
  435. end;
  436. procedure TMenuDesignerForm.LoadFixedButtonGlyphs;
  437. begin
  438. DeleteItemButton.LoadGlyphFromResourceName(HINSTANCE,'laz_delete');
  439. AddSeparatorAboveButton.LoadGlyphFromResourceName(HINSTANCE,'add_sep_above');
  440. AddSeparatorBelowButton.LoadGlyphFromResourceName(HINSTANCE,'add_sep_below');
  441. HelpButton.Hint:=lisMenuEditorGetHelpToUseThisEditor;
  442. end;
  443. procedure TMenuDesignerForm.EnableGUI(selectedIsNil: boolean);
  444. var
  445. isPopupMenu: boolean;
  446. begin
  447. if not FGUIEnabled then
  448. begin
  449. StatisticsGroupBox.Font.Style:=[fsBold];
  450. StatisticsGroupBox.Caption:=FEditedMenu.Name;
  451. StatisticsGroupBox.Enabled:=True;
  452. ButtonsGroupBox.Enabled:=not selectedIsNil;
  453. if selectedIsNil then
  454. Caption:=Format(lisMenuEditorEditingSSNoMenuItemSelected,
  455. [TComponent(GlobalDesignHook.LookupRoot).Name, FEditedMenu.Name]);
  456. isPopupMenu:=(FEditedMenu is TPopupMenu);
  457. LoadVariableButtonGlyphs(not isPopupMenu);
  458. if isPopupMenu then
  459. ShowPopupAssignmentsInfo
  460. else HidePopupAssignmentsInfo;
  461. FGUIEnabled:=True;
  462. end;
  463. end;
  464. procedure TMenuDesignerForm.InitializeStatisticVars;
  465. begin
  466. FDesigner.Shortcuts.ResetMenuItemsCount;
  467. FIconsCount := -1;
  468. FDeepestNestingLevel := -1;
  469. FCaptionedItemsCount := -1;
  470. end;
  471. procedure TMenuDesignerForm.DisableGUI;
  472. begin
  473. if FGUIEnabled then begin
  474. StatisticsGroupBox.Font.Style:=[];
  475. StatisticsGroupBox.Caption:=lisMenuEditorNoMenuSelected;
  476. CaptionedItemsCountLabel.Caption:=Format(lisMenuEditorCaptionedItemsS,[lisMenuEditorNA]);
  477. ShortcutItemsCountLabel.Caption:=Format(lisMenuEditorShortcutItemsS,[lisMenuEditorNA]);
  478. IconCountLabel.Caption:=Format(lisMenuEditorItemsWithIconS, [lisMenuEditorNA]);
  479. DeepestNestingLevelLabel.Caption:=Format(lisMenuEditorDeepestNestedMenuLevelS, [lisMenuEditorNA]);
  480. PopupAssignmentsCountLabel.Caption:=Format(lisMenuEditorPopupAssignmentsS,[lisMenuEditorNA]);
  481. StatisticsGroupBox.Enabled:=False;
  482. UpdateSubmenuGroupBox(nil, nil, False);
  483. ButtonsGroupBox.Enabled:=False;
  484. FPopupAssignmentsListBox.Visible:=False;
  485. FGUIEnabled:=False;
  486. InitializeStatisticVars;
  487. Caption:=Format('%s - %s',[lisMenuEditorMenuEditor, lisMenuEditorNoMenuSelected]);
  488. end;
  489. end;
  490. procedure TMenuDesignerForm.SetMenu(aMenu: TMenu; aMenuItem: TMenuItem);
  491. var
  492. selection: TMenuItem;
  493. begin
  494. if (aMenu = nil) then
  495. begin
  496. DisableGUI;
  497. FDesigner.FreeShadowMenu;
  498. FEditedMenu:=nil;
  499. end
  500. else begin
  501. if (aMenu = FEditedMenu) and (FDesigner.ShadowMenu <> nil) then
  502. FDesigner.ShadowMenu.SetSelectedMenuItem(aMenuItem, True, False)
  503. else begin
  504. if (aMenu = FEditedMenu) and (FDesigner.ShadowMenu = nil) then
  505. begin
  506. if (FEditedMenu.Items.Count > 0) then
  507. selection := FEditedMenu.Items[0]
  508. else
  509. selection := nil;
  510. end
  511. else if (aMenu <> FEditedMenu) then
  512. begin
  513. FDesigner.ShadowMenu.Free;
  514. FDesigner.ShadowMenu := Nil;
  515. FEditedMenu := aMenu;
  516. selection := aMenuItem;
  517. end;
  518. FGUIEnabled := False;
  519. EnableGUI(selection = nil);
  520. UpdateStatistics;
  521. FDesigner.CreateShadowMenu(FEditedMenu, selection, Width-LeftPanel.Width, Height);
  522. end;
  523. end;
  524. end;
  525. procedure TMenuDesignerForm.BeginUpdate;
  526. begin
  527. Inc(FUpdateCount);
  528. end;
  529. procedure TMenuDesignerForm.EndUpdate;
  530. begin
  531. if FUpdateCount<=0 then
  532. RaiseGDBException('');
  533. Dec(FUpdateCount);
  534. if FUpdateCount = 0 then
  535. OnDesignerSetSelection(FormEditingHook.GetCurrentObjectInspector.Selection);
  536. end;
  537. function TMenuDesignerForm.IsUpdate: Boolean;
  538. begin
  539. Result := FUpdateCount > 0;
  540. end;
  541. procedure TMenuDesignerForm.UpdateStatistics;
  542. var
  543. captions, shrtcuts, icons, accels, tmp: integer;
  544. s: String;
  545. begin
  546. if not SameText(StatisticsGroupBox.Caption, FEditedMenu.Name) then
  547. StatisticsGroupBox.Caption:=FEditedMenu.Name;
  548. FDesigner.TotalMenuItemsCount:=GetItemCounts(captions, shrtcuts, icons, accels);
  549. if (FCaptionedItemsCount <> captions) then begin
  550. FCaptionedItemsCount:=captions;
  551. CaptionedItemsCountLabel.Caption:=
  552. Format(lisMenuEditorCaptionedItemsS, [IntToStr(captions)]);
  553. end;
  554. s:=FDesigner.Shortcuts.Statistics(shrtcuts);
  555. if s <> '' then
  556. ShortcutItemsCountLabel.Caption := s;
  557. if (FIconsCount <> icons) then begin
  558. FIconsCount:=icons;
  559. IconCountLabel.Caption:=
  560. Format(lisMenuEditorItemsWithIconS, [IntToStr(FIconsCount)]);
  561. end;
  562. if (FAcceleratorMenuItemsCount <> accels) then
  563. FAcceleratorMenuItemsCount:=accels;
  564. tmp:=GetNestingLevelDepth(FEditedMenu);
  565. if (FDeepestNestingLevel <> tmp) then begin
  566. DeepestNestingLevelLabel.Caption:=
  567. Format(lisMenuEditorDeepestNestedMenuLevelS, [IntToStr(tmp)]);
  568. FDeepestNestingLevel:=tmp;
  569. end;
  570. StatisticsGroupBox.Invalidate;
  571. end;
  572. procedure TMenuDesignerForm.UpdateSubmenuGroupBox(selMI: TMenuItem;
  573. selBox: TShadowBoxBase; boxIsRoot: boolean);
  574. begin
  575. if SubmenuGroupBox = nil then
  576. Exit;
  577. if (selMI = nil) then begin
  578. SubmenuGroupBox.Caption:=lisMenuEditorNoMenuSelected;
  579. RadioGroupsLabel.Caption:='';
  580. GroupIndexLabel.Caption:='';
  581. end
  582. else begin
  583. selBox.LastRIValue:=selMI.RadioItem;
  584. if boxIsRoot then
  585. SubmenuGroupBox.Caption:=lisMenuEditorRootMenu
  586. else SubmenuGroupBox.Caption:=Format(lisMenuEditorSSubmenu,[selBox.ParentMenuItem.Name]);
  587. if selMI.RadioItem then begin
  588. GroupIndexLabel.Caption:=Format(lisMenuEditorSGroupIndexD,
  589. [selMI.Name, selMI.GroupIndex]);
  590. GroupIndexLabel.Enabled:=True;
  591. end
  592. else begin
  593. GroupIndexLabel.Caption:=Format(lisMenuEditorSIsNotARadioitem,
  594. [selMI.Name]);
  595. GroupIndexLabel.Enabled:=False;
  596. end;
  597. if selBox.HasRadioItems then begin
  598. RadioGroupsLabel.Caption:=Format(lisMenuEditorGroupIndexValueSS,
  599. [selBox.RadioGroupsString]);
  600. RadioGroupsLabel.Enabled:=True;
  601. end
  602. else begin
  603. RadioGroupsLabel.Caption:=lisMenuEditorNoRadioitemsInThisMenu;
  604. RadioGroupsLabel.Enabled:=False;
  605. RadioGroupsLabel.Invalidate; //for some reason this seems necessary
  606. end;
  607. end;
  608. end;
  609. procedure TMenuDesignerForm.UpdateItemInfo(aMenu: TMenu; aMenuItem: TMenuItem;
  610. aShadowBox: TShadowBoxBase; aPropEditHook: TPropertyEditorHook);
  611. var
  612. s: string;
  613. method: TMethod;
  614. begin
  615. if aMenuItem = nil then
  616. begin
  617. Caption:=Format(lisMenuEditorEditingSSNoMenuitemSelected,
  618. [aMenu.Owner.Name, aMenu.Name]);
  619. ButtonsGroupBox.Enabled:=False;
  620. UpdateSubmenuGroupBox(nil, nil, False);
  621. end
  622. else begin
  623. method:=GetMethodProp(aMenuItem, 'OnClick');
  624. s:=aPropEditHook.GetMethodName(method, aMenuItem);
  625. if s = '' then
  626. s:=lisMenuEditorIsNotAssigned;
  627. Caption:=Format(lisMenuEditorSSSOnClickS,
  628. [aMenu.Owner.Name, aMenu.Name, aMenuItem.Name, s]);
  629. ButtonsGroupBox.Enabled:=True;
  630. UpdateSubmenuGroupBox(aMenuItem, aShadowBox, aShadowBox.Level=0);
  631. end;
  632. end;
  633. { TRadioIcon }
  634. constructor TRadioIcon.CreateWithGlyph(aRIGroup: TRadioIconGroup;
  635. anImgIndex: integer);
  636. begin
  637. Assert(anImgIndex > -1, 'TRadioIcon.CreateWithGlyph: param not > -1');
  638. inherited Create(aRIGroup);
  639. FRIGroup:=aRIGroup;
  640. FBGlyph:=TButtonGlyph.Create;
  641. FBGlyph.IsDesigning:=False;
  642. FBGlyph.ShowMode:=gsmAlways;
  643. FBGlyph.OnChange:=nil;
  644. FBGlyph.CacheSetImageList(FRIGroup.FImageList);
  645. FBGlyph.CacheSetImageIndex(0, anImgIndex);
  646. Tag:=anImgIndex;
  647. SetInitialBounds(0, 0, FRIGroup.FRadioWidth, FRIGroup.FRadioHeight);
  648. ControlStyle:=ControlStyle + [csCaptureMouse]-[csSetCaption, csClickEvents, csOpaque];
  649. FRIState:=risUp;
  650. Color:=clBtnFace;
  651. end;
  652. destructor TRadioIcon.Destroy;
  653. begin
  654. FreeAndNil(FBGlyph);
  655. inherited Destroy;
  656. end;
  657. function TRadioIcon.GetChecked: Boolean;
  658. begin
  659. Result:=FRIState in [risDown, risPressed, risCheckedHot];
  660. end;
  661. procedure TRadioIcon.SetChecked(aValue: Boolean);
  662. begin
  663. case aValue of
  664. True: if (FRIState <> risDown) then begin // set to True
  665. FRIState:=risDown;
  666. Repaint;
  667. end;
  668. False: if (FRIState <> risUp) then begin // set to False
  669. FRIState:=risUp;
  670. Repaint;
  671. end;
  672. end;
  673. end;
  674. procedure TRadioIcon.DoChange;
  675. begin
  676. if Assigned(FOnChange) then
  677. FOnChange(Self);
  678. end;
  679. procedure TRadioIcon.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  680. begin
  681. inherited MouseDown(Button, Shift, X, Y);
  682. if (Button = mbLeft) and (FRIState in [risUncheckedHot, risUp]) then begin
  683. FRIState:=risPressed;
  684. Repaint;
  685. DoChange;
  686. end;
  687. end;
  688. procedure TRadioIcon.MouseEnter;
  689. begin
  690. inherited MouseEnter;
  691. case FRIState of
  692. risUp: FRIState:=risUncheckedHot;
  693. risDown: FRIState:=risCheckedHot;
  694. end;
  695. Repaint;
  696. end;
  697. procedure TRadioIcon.MouseLeave;
  698. begin
  699. case FRIState of
  700. risPressed, risCheckedHot: FRIState:=risDown;
  701. risUncheckedHot: FRIState:=risUp;
  702. end;
  703. Repaint;
  704. inherited MouseLeave;
  705. end;
  706. procedure TRadioIcon.Paint;
  707. var
  708. ted: TThemedElementDetails;
  709. begin
  710. if (Canvas.Brush.Color <> Color) then
  711. Canvas.Brush.Color:=Color;
  712. Canvas.FillRect(ClientRect);
  713. case FRIState of
  714. risUp: ted:=FRIGroup.FedUnchecked;
  715. risDown: ted:=FRIGroup.FedChecked;
  716. risPressed: ted:=FRIGroup.FedPressed;
  717. risUncheckedHot: ted:=FRIGroup.FedUncheckedHot;
  718. risCheckedHot: ted:=FRIGroup.FedCheckedHot;
  719. end;
  720. ThemeServices.DrawElement(Canvas.Handle, ted, FRIGroup.FRadioRect);
  721. FBGlyph.Draw(Canvas, ClientRect, FRIGroup.FGlyphPt, bsUp, False, 0);
  722. inherited Paint;
  723. end;
  724. { TRadioIconGroup }
  725. constructor TRadioIconGroup.CreateWithImageList(AOwner: TComponent;
  726. anImgList: TCustomImageList);
  727. var
  728. topOffset: integer;
  729. begin
  730. Assert(AOwner<>nil,'TRadioIconGroup.CreateWithImageList: AOwner is nil');
  731. Assert(anImgList<>nil,'TRadioIconGroup.CreateWithImageList:anImgList is nil');
  732. inherited Create(AOwner);
  733. FImageList:=anImgList;
  734. FedUnChecked:=ThemeServices.GetElementDetails(tbRadioButtonUncheckedNormal);
  735. FedChecked:=ThemeServices.GetElementDetails(tbRadioButtonCheckedNormal);
  736. FedPressed:=ThemeServices.GetElementDetails(tbRadioButtonCheckedPressed);
  737. FedUncheckedHot:=ThemeServices.GetElementDetails(tbRadioButtonUncheckedHot);
  738. FedCheckedHot:=ThemeServices.GetElementDetails(tbRadioButtonCheckedHot);
  739. FedSize:=ThemeServices.GetDetailSize(FedUnChecked);
  740. FRadioHeight:=FedSize.cy;
  741. if (anImgList.Height > FRadioHeight) then
  742. FRadioHeight:=anImgList.Height;
  743. topOffset:=(FRadioHeight - FedSize.cy) div 2;
  744. FRadioRect:=Rect(0, topOffset, FedSize.cx, topOffset+FedSize.cy);
  745. FSpacing:=5;
  746. FRadioWidth:=FedSize.cx + FSpacing + anImgList.Width;
  747. FGlyphPt:=Point(FedSize.cx+FSpacing, 0);
  748. FItemIndex:= -1;
  749. CreateRadioItems;
  750. end;
  751. procedure TRadioIconGroup.CreateRadioItems;
  752. var
  753. i: integer;
  754. begin
  755. SetLength(FRIArray, FImageList.Count);
  756. for i:=Low(FRIArray) to High(FRIArray) do
  757. begin
  758. FRIArray[i]:=TRadioIcon.CreateWithGlyph(Self, i);
  759. FRIArray[i].OnChange:=@RIOnChange;
  760. end;
  761. end;
  762. procedure TRadioIconGroup.ApplyLayout;
  763. var
  764. unitArea, hSpace, sepn, count, cols, rows, lastRowCount, space, h, num, denom: integer;
  765. procedure CalcSepn;
  766. begin
  767. rows:=count div cols;
  768. if (cols*rows < count) or (rows < 2) then
  769. Inc(rows);
  770. lastRowCount:=count mod cols;
  771. if (lastRowCount = 0) then
  772. lastRowCount:=cols;
  773. num:=space + hSpace*FRIArray[0].Height - lastRowCount*unitArea;
  774. denom:=Pred(rows)*hSpace + FRIArray[0].Height*Pred(cols)*Pred(rows);
  775. Assert(denom > 0,'TRadioIconGroup.ApplyLayout: divisor is zero');
  776. sepn:=trunc(num/denom);
  777. repeat
  778. Dec(sepn);
  779. h:=cols*FRIArray[0].Width + Pred(cols)*sepn;
  780. until (h < hSpace) or (sepn <= Margin);
  781. end;
  782. const
  783. BPanelVertDim = 46;
  784. var
  785. areaToFill, hBorderAndMargins, vSpace, vSepn, oldCols,
  786. i, v, gap, hInc, vInc, maxIdx, vBorderAndMargins: integer;
  787. lft: integer = Margin;
  788. tp: integer = Margin;
  789. r: integer = 1;
  790. c: integer = 1;
  791. begin
  792. hBorderAndMargins:=integer(BorderSpacing.Left)+integer(BorderSpacing.Right)+integer(BorderSpacing.Around*2) + Double_Margin;
  793. hSpace:=Parent.ClientWidth - hBorderAndMargins;
  794. vBorderAndMargins:=integer(BorderSpacing.Top)+integer(BorderSpacing.Bottom)+integer(BorderSpacing.Around*2) + Double_Margin;
  795. vSpace:=Parent.ClientHeight - vBorderAndMargins - BPanelVertDim;
  796. areaToFill:=hSpace*vSpace;
  797. unitArea:=FRIArray[0].Width*FRIArray[0].Height;
  798. count:=Length(FRIArray);
  799. space:=areaToFill - count*unitArea;
  800. cols:=trunc(sqrt(count)); // assume area is roughly square
  801. if (cols = 0) then
  802. Inc(cols);
  803. oldCols:=cols;
  804. CalcSepn;
  805. gap:=hSpace - h;
  806. if (gap > 0) and (gap > FRIArray[0].Width) then
  807. begin
  808. Inc(cols);
  809. CalcSepn;
  810. end;
  811. if (sepn <= Margin) then
  812. begin
  813. cols:=oldcols;
  814. CalcSepn;
  815. end;
  816. vSepn:=sepn;
  817. v:=rows*FRIArray[0].Height + Pred(rows)*vSepn;
  818. if (v > vSpace) then
  819. repeat
  820. Dec(vSepn);
  821. v:=rows*FRIArray[0].Height + Pred(rows)*vSepn;
  822. until (v < vSpace) or (vSepn <= Margin);
  823. hInc:=FRIArray[0].Width + sepn;
  824. vInc:=FRIArray[0].Height + vSepn;
  825. maxIdx:=High(FRIArray);
  826. for i:=Low(FRIArray) to maxIdx do
  827. begin
  828. FRIArray[i].Left:=lft;
  829. FRIArray[i].Top:=tp;
  830. Inc(c);
  831. Inc(lft, hInc);
  832. if (c > cols) and (i < maxIdx) then
  833. begin
  834. c:=1;
  835. lft:=Margin;
  836. Inc(r);
  837. Inc(tp, vInc);
  838. end;
  839. end;
  840. Assert(r <= rows,'TRadioIconGroup.ApplyLayout: error in calculation of space needed');
  841. end;
  842. procedure TRadioIconGroup.RIOnChange(Sender: TObject);
  843. var
  844. aRi: TRadioIcon;
  845. i: integer;
  846. begin
  847. if not (Sender is TRadioIcon) then
  848. Exit;
  849. aRi:=TRadioIcon(Sender);
  850. FItemIndex:=aRi.Tag;
  851. DoSelectItem;
  852. if aRi.Checked then
  853. begin
  854. for i:=Low(FRIArray) to High(FRIArray) do
  855. if (i <> FItemIndex) then
  856. FRIArray[i].Checked:=False;
  857. end;
  858. end;
  859. procedure TRadioIconGroup.DoSelectItem;
  860. begin
  861. if Assigned(FOnSelectItem) then
  862. FOnSelectItem(Self);
  863. end;
  864. procedure TRadioIconGroup.SetParent(NewParent: TWinControl);
  865. var
  866. i: Integer;
  867. begin
  868. inherited SetParent(NewParent);
  869. if (NewParent <> nil) then
  870. begin
  871. ApplyLayout;
  872. for i:=Low(FRIArray) to High(FRIArray) do
  873. FRIArray[i].SetParent(Self);
  874. end;
  875. end;
  876. { TdlgChooseIcon }
  877. constructor TdlgChooseIcon.Create(TheOwner: TComponent);
  878. begin
  879. inherited CreateNew(TheOwner);
  880. Position:=poScreenCenter;
  881. BorderStyle:=bsDialog;
  882. Width:=250;
  883. Height:=250;
  884. FButtonPanel:=TButtonPanel.Create(Self);
  885. FButtonPanel.ShowButtons:=[pbOK, pbCancel];
  886. FButtonPanel.OKButton.Name:='OKButton';
  887. FButtonPanel.OKButton.DefaultCaption:=True;
  888. FButtonPanel.OKButton.Enabled:=False;
  889. FButtonPanel.CancelButton.Name:='CancelButton';
  890. FButtonPanel.CancelButton.DefaultCaption:=True;
  891. FButtonPanel.Parent:=Self;
  892. end;
  893. function TdlgChooseIcon.GetImageIndex: integer;
  894. begin
  895. Result:=FRadioIconGroup.ItemIndex;
  896. end;
  897. procedure TdlgChooseIcon.RIGClick(Sender: TObject);
  898. begin
  899. FButtonPanel.OKButton.Enabled:=True;
  900. FButtonPanel.OKButton.SetFocus;
  901. end;
  902. procedure TdlgChooseIcon.SetRadioIconGroup(anImageList: TCustomImageList);
  903. begin
  904. FRadioIconGroup:=TRadioIconGroup.CreateWithImageList(Self, anImageList);
  905. with FRadioIconGroup do begin
  906. Align:=alClient;
  907. BorderSpacing.Top:=FButtonPanel.BorderSpacing.Around;
  908. BorderSpacing.Left:=FButtonPanel.BorderSpacing.Around;
  909. BorderSpacing.Right:=FButtonPanel.BorderSpacing.Around;
  910. TabOrder:=0;
  911. OnSelectItem:=@RIGClick;
  912. Parent:=Self;
  913. end;
  914. Caption:=Format(lisMenuEditorPickAnIconFromS, [anImageList.Name]);
  915. end;
  916. end.