{*************************************************************************** * * * This source is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This code is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * A copy of the GNU General Public License is available on the World * * Wide Web at . You can also * * obtain it by writing to the Free Software Foundation, * * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. * * * *************************************************************************** Author: Howard Page-Clark, Juha Manninen and other contributors } unit MenuEditorForm; {$mode objfpc}{$H+} interface uses // FCL Classes, SysUtils, Types, typinfo, math, // LazUtils LazTracer, // LCL Controls, StdCtrls, ExtCtrls, Forms, Graphics, Buttons, Menus, ButtonPanel, ImgList, Themes, LCLintf, LCLType, // IdeIntf FormEditingIntf, PropEdits, ObjectInspector, IDEImagesIntf, // IDE LazarusIDEStrConsts, MenuDesignerBase, MenuShortcuts; type { TMenuDesignerForm } TMenuDesignerForm = class(TForm) AddItemAboveButton: TSpeedButton; AddItemBelowButton: TSpeedButton; AddSeparatorAboveButton: TSpeedButton; AddSeparatorBelowButton: TSpeedButton; AddSubMenuButton: TSpeedButton; ButtonsGroupBox: TGroupBox; CaptionedItemsCountLabel: TLabel; DeepestNestingLevelLabel: TLabel; DeleteItemButton: TSpeedButton; GroupIndexLabel: TLabel; HelpButton: TBitBtn; IconCountLabel: TLabel; LeftPanel: TPanel; MoveItemDownButton: TSpeedButton; MoveItemUpButton: TSpeedButton; PopupAssignmentsCountLabel: TLabel; RadioGroupsLabel: TLabel; ShortcutItemsCountLabel: TLabel; StatisticsGroupBox: TGroupBox; RadioItemGroupBox: TGroupBox; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormHide(Sender: TObject); procedure FormShow(Sender: TObject); procedure HelpButtonClick(Sender: TObject); strict private FDesigner: TMenuDesignerBase; FEditedMenu: TMenu; FAcceleratorMenuItemsCount: integer; FCaptionedItemsCount: integer; FDeepestNestingLevel: integer; FAddingItem: Boolean; FGUIEnabled: boolean; FIconsCount: integer; FUpdateCount: integer; FPopupAssignments: TStringList; FPopupAssignmentsListBox: TListBox; function GetItemCounts(out aCaptionedItemCount, aShortcutItemCount, anIconCount, anAccelCount: integer): integer; function GetPopupAssignmentCount: integer; function GetSelectedMenuComponent(const aSelection: TPersistentSelectionList; out isTMenu: boolean; out isTMenuItem: boolean): TPersistent; procedure DisableGUI; procedure EnableGUI(selectedIsNil: boolean); procedure HidePopupAssignmentsInfo; procedure InitializeStatisticVars; procedure LoadFixedButtonGlyphs; procedure DesignerSetSelection(const ASelection: TPersistentSelectionList); procedure ProcessForPopup(aControl: TControl); procedure SetupPopupAssignmentsDisplay; protected procedure UTF8KeyPress(var UTF8Key: TUTF8Char); override; public constructor Create(aDesigner: TMenuDesignerBase); reintroduce; destructor Destroy; override; procedure LoadVariableButtonGlyphs(isInMenubar: boolean); procedure SetMenu(aMenu: TMenu; aMenuItem: TMenuItem); procedure ShowPopupAssignmentsInfo; procedure BeginUpdate; procedure EndUpdate; function IsUpdate: Boolean; procedure UpdateStatistics; procedure UpdateSubmenuGroupBox(selMI: TMenuItem; selBox: TShadowBoxBase); procedure UpdateItemInfo(aMenu: TMenu; aMenuItem: TMenuItem; aShadowBox: TShadowBoxBase; aPropEditHook: TPropertyEditorHook); //property EditedMenu: TMenu read FEditedMenu; //property AcceleratorMenuItemsCount: integer read FAcceleratorMenuItemsCount; property AddingItem: Boolean read FAddingItem write FAddingItem; end; TRadioIconGroup = class; TRadioIconState = (risUp, risDown, risPressed, risUncheckedHot, risCheckedHot); { TRadioIcon } TRadioIcon = class(TGraphicControl) strict private FBGlyph: TButtonGlyph; FOnChange: TNotifyEvent; FRIGroup: TRadioIconGroup; FRIState: TRadioIconState; function GetChecked: Boolean; procedure SetChecked(aValue: Boolean); protected procedure DoChange; procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override; procedure MouseEnter; override; procedure MouseLeave; override; procedure Paint; override; public constructor CreateWithGlyph(aRIGroup: TRadioIconGroup; anImgIndex: integer); destructor Destroy; override; property Checked: Boolean read GetChecked write SetChecked; property OnChange: TNotifyEvent read FOnChange write FOnChange; end; { TRadioIconGroup } TRadioIconGroup = class(TScrollBox) strict private FItemIndex: integer; FOnSelectItem: TNotifyEvent; FRIArray: array of TRadioIcon; procedure CreateRadioItems; procedure RIOnChange(Sender: TObject); procedure DoSelectItem; protected FImageList: TCustomImageList; FedSize: TSize; FedUnchecked, FedChecked, FedPressed, FedUncheckedHot, FedCheckedHot: TThemedElementDetails; FGlyphPt: TPoint; FSpacing: integer; FRadioHeight, FRadioWidth: integer; FRadioRect: TRect; procedure SetParent(NewParent: TWinControl); override; public constructor CreateWithImageList(AOwner: TComponent; anImgList: TCustomImageList); procedure RefreshLayout; property ItemIndex: integer read FItemIndex; property OnSelectItem: TNotifyEvent read FOnSelectItem write FOnSelectItem; end; { TdlgChooseIcon } TdlgChooseIcon = class(TForm) private FButtonPanel: TButtonPanel; FRadioIconGroup: TRadioIconGroup; procedure dlgChooseIconResize(Sender: TObject); function GetImageIndex: integer; procedure RIGClick(Sender: TObject); public constructor Create(TheOwner: TComponent); override; procedure SetRadioIconGroup(anImageList: TCustomImageList); property ImageIndex: integer read GetImageIndex; end; var OnForwardKeyToObjectInspector: TOnForwardKeyToOI; function GetNestingLevelDepth(aMenu: TMenu): integer; function ChooseIconFromImageListDlg(anImageList: TCustomImageList): integer; implementation {$R *.lfm} function GetNestingLevelDepth(aMenu: TMenu): integer; procedure CheckLevel(aMI: TMenuItem; aLevel: integer); var j: integer; begin if (aMI.Count > 0) then begin if (Succ(aLevel) > Result) then Result:=Succ(aLevel); for j:=0 to aMI.Count-1 do CheckLevel(aMI.Items[j], Succ(aLevel)); end; end; var i: integer; begin Result:=0; for i:=0 to aMenu.Items.Count-1 do CheckLevel(aMenu.Items[i], 0); end; function ChooseIconFromImageListDlg(anImageList: TCustomImageList): integer; var dlg: TdlgChooseIcon; begin if (anImageList = nil) or (anImageList.Count = 0) then Exit(-1); if (anImageList.Count = 1) then Exit(0); dlg := TdlgChooseIcon.Create(nil); try dlg.SetRadioIconGroup(anImageList); if (dlg.ShowModal = mrOK) then Result := dlg.ImageIndex else Result := -1; finally dlg.Free; end; end; { TMenuDesignerForm } constructor TMenuDesignerForm.Create(aDesigner: TMenuDesignerBase); begin Inherited Create(Nil); // LazarusIDE.OwningComponent FDesigner := aDesigner; end; destructor TMenuDesignerForm.Destroy; begin inherited Destroy; end; procedure TMenuDesignerForm.FormCreate(Sender: TObject); begin Name:='MenuDesignerWindow'; Caption:=lisMenuEditorMenuEditor; ButtonsGroupBox.Caption:=lisMenuEditorMenuItemActions; RadioItemGroupBox.Caption:=lisMenuEditorRadioItem; FGUIEnabled:=False; LoadFixedButtonGlyphs; LoadVariableButtonGlyphs(True); KeyPreview:=True; InitializeStatisticVars; SetupPopupAssignmentsDisplay; end; procedure TMenuDesignerForm.FormDestroy(Sender: TObject); begin FreeAndNil(FPopupAssignments); end; procedure TMenuDesignerForm.FormShow(Sender: TObject); begin GlobalDesignHook.AddHandlerSetSelection(@DesignerSetSelection); end; procedure TMenuDesignerForm.FormHide(Sender: TObject); begin FDesigner.FreeShadowMenu; GlobalDesignHook.RemoveHandlerSetSelection(@DesignerSetSelection); end; procedure TMenuDesignerForm.HelpButtonClick(Sender: TObject); const helpPath = 'http://wiki.lazarus.freepascal.org/IDE_Window:_Menu_Editor'; begin //LazarusHelp.ShowHelpForIDEControl(Self); OpenURL(helpPath); end; procedure TMenuDesignerForm.DesignerSetSelection(const ASelection: TPersistentSelectionList); var mnu: TMenu; mi, tmp: TMenuItem; isTMenu, isTMenuItem: boolean; persist: TPersistent; begin if FUpdateCount > 0 then Exit; // This event will be executed after all updates, look at EndUpdate //debugln(['TMenuDesignerForm.OnDesignerSetSelection: ']); //ASelection.WriteDebugReport; persist:=GetSelectedMenuComponent(ASelection, isTMenu, isTMenuItem); //debugln(['TMenuDesignerForm.OnDesignerSetSelection isTMenu=',isTMenu,' isTMenuItem=',isTMenuItem,' persist=',DbgSName(persist)]); if (persist <> nil) then begin if isTMenu then SetMenu(TMenu(persist), nil) else if isTMenuItem then begin mi:=TMenuItem(persist); tmp:=mi; while (tmp.Parent <> nil) do tmp:=tmp.Parent; mnu:=tmp.Menu; if (mnu = nil) then mnu:=mi.GetParentMenu; if (mnu = FEditedMenu) and (FDesigner.ShadowMenu <> nil) then FDesigner.ShadowMenu.SetSelectedMenuItem(mi, True, False) else if (mnu <> nil) then SetMenu(mnu, mi); end; end else if not AddingItem then SetMenu(nil, nil); end; procedure TMenuDesignerForm.ShowPopupAssignmentsInfo; var count: integer; begin if FEditedMenu is TPopupMenu then begin count:=GetPopupAssignmentCount; PopupAssignmentsCountLabel.Enabled:=True; if (count > 0) then PopupAssignmentsCountLabel.BorderSpacing.Bottom:=0 else PopupAssignmentsCountLabel.BorderSpacing.Bottom:=Double_Margin; if (count= -1) then PopupAssignmentsCountLabel.Caption:=Format(lisMenuEditorPopupAssignmentsS,[lisMenuEditorNA]) else PopupAssignmentsCountLabel.Caption:=Format(lisMenuEditorPopupAssignmentsS, [IntToStr(count)]); if (count > 0) then begin FPopupAssignmentsListBox.Items.Assign(FPopupAssignments); FPopupAssignmentsListBox.Visible:=True; end else FPopupAssignmentsListBox.Visible:=False; end; end; procedure TMenuDesignerForm.HidePopupAssignmentsInfo; begin if FEditedMenu is TMainMenu then begin PopupAssignmentsCountLabel.Caption:=Format(lisMenuEditorPopupAssignmentsS,[lisMenuEditorNA]); PopupAssignmentsCountLabel.Enabled:=False; FPopupAssignmentsListBox.Visible:=False; end; end; procedure TMenuDesignerForm.SetupPopupAssignmentsDisplay; begin FPopupAssignmentsListBox:=TListBox.Create(Self); with FPopupAssignmentsListBox do begin Name:='FPopupAssignmentsListBox'; Color:=clBtnFace; BorderSpacing.Top:=2; BorderSpacing.Left:=3*Margin; BorderSpacing.Right:=Margin; BorderSpacing.Bottom:=Margin; Anchors:=[akTop, akLeft, akRight]; AnchorSideLeft.Control:=StatisticsGroupBox; AnchorSideTop.Control:=PopupAssignmentsCountLabel; AnchorSideTop.Side:=asrBottom; AnchorSideRight.Control:=StatisticsGroupBox; AnchorSideRight.Side:=asrBottom; ParentFont:=False; TabStop:=False; BorderStyle:=bsNone; ExtendedSelect:=False; Height:=45; Parent:=StatisticsGroupBox; Visible:=False; end; end; procedure TMenuDesignerForm.UTF8KeyPress(var UTF8Key: TUTF8Char); begin if ((Length(UTF8Key) = 1) and (Ord(UTF8Key[1]) < 32)) or (UTF8Key = sLineBreak) then // pass only printable characters Exit; if UTF8Key<>'' then begin if Assigned(OnForwardKeyToObjectInspector) then OnForwardKeyToObjectInspector(Self, UTF8Key); UTF8Key := ''; end; end; function TMenuDesignerForm.GetItemCounts(out aCaptionedItemCount, aShortcutItemCount, anIconCount, anAccelCount: integer): integer; var imgCount: integer; procedure ProcessItems(aMI: TMenuItem); var i: integer; sc: TShortCut; begin Inc(Result); if not aMI.IsLine and (aMI.Caption <> '') then begin Inc(aCaptionedItemCount); if HasAccelerator(aMI.Caption, sc) then Inc(anAccelCount); end; if (aMI.ShortCut <> 0) or (aMI.ShortCutKey2 <> 0) then Inc(aShortcutItemCount); if (imgCount > 0) and (aMI.ImageIndex > -1) and (aMI.ImageIndex < imgCount) then Inc(anIconCount) else if aMI.HasBitmap and not aMI.Bitmap.Empty then Inc(anIconCount); for i:=0 to aMI.Count-1 do ProcessItems(aMI.Items[i]); // Recursive call for sub-menus. end; var i: integer; begin Result:=0; if (FEditedMenu = nil) then Exit; aCaptionedItemCount:=0; aShortcutItemCount:=0; anIconCount:=0; anAccelCount:=0; if (FEditedMenu.Images <> nil) and (FEditedMenu.Images.Count > 0) then imgCount:=FEditedMenu.Images.Count else imgCount:=0; for i:=0 to FEditedMenu.Items.Count-1 do ProcessItems(FEditedMenu.Items[i]); end; function TMenuDesignerForm.GetSelectedMenuComponent(const aSelection: TPersistentSelectionList; out isTMenu: boolean; out isTMenuItem: boolean): TPersistent; begin if (aSelection.Count = 1) then begin if (aSelection.Items[0] is TMenu) then begin isTMenu:=True; isTMenuItem:=False; Result:=aSelection.Items[0]; end else if (aSelection.Items[0] is TMenuItem) then begin isTMenu:=False; isTMenuItem:=True; Result:=aSelection.Items[0]; end else begin isTMenu:=False; isTMenuItem:=False; Result:=nil; end; end else Result:=nil; end; procedure TMenuDesignerForm.ProcessForPopup(aControl: TControl); var wc: TWinControl; j:integer; begin if (aControl.PopupMenu = FEditedMenu) and (aControl.Name <> '') then FPopupAssignments.Add(aControl.Name); if (aControl is TWinControl) then begin wc:=TWinControl(aControl); for j:=0 to wc.ControlCount-1 do ProcessForPopup(wc.Controls[j]); // Recursive call end; end; function TMenuDesignerForm.GetPopupAssignmentCount: integer; var lookupRoot: TPersistent; begin lookupRoot:=GlobalDesignHook.LookupRoot; if (FEditedMenu is TMainMenu) or (lookupRoot is TDataModule) then Exit(-1) else begin FreeAndNil(FPopupAssignments); FPopupAssignments:=TStringList.Create; ProcessForPopup(lookupRoot as TControl); Result:=FPopupAssignments.Count; end end; procedure TMenuDesignerForm.LoadVariableButtonGlyphs(isInMenubar: boolean); begin if isInMenubar then begin IDEImages.AssignImage(MoveItemUpButton, 'arrow_left'); IDEImages.AssignImage(MoveItemDownButton, 'arrow_right'); IDEImages.AssignImage(AddItemAboveButton, 'add_item_left', 24); IDEImages.AssignImage(AddItemBelowButton, 'add_item_right', 24); IDEImages.AssignImage(AddSubMenuButton, 'add_submenu_below', 24); end else begin IDEImages.AssignImage(MoveItemUpButton, 'arrow_up'); IDEImages.AssignImage(MoveItemDownButton, 'arrow_down'); IDEImages.AssignImage(AddItemAboveButton, 'add_item_above', 24); IDEImages.AssignImage(AddItemBelowButton, 'add_item_below', 24); IDEImages.AssignImage(AddSubMenuButton, 'add_submenu_right', 24); end; UpdateSubmenuGroupBox(nil, nil); FDesigner.VariableGlyphsInMenuBar:=isInMenubar; end; procedure TMenuDesignerForm.LoadFixedButtonGlyphs; begin IDEImages.AssignImage(DeleteItemButton, 'laz_delete'); IDEImages.AssignImage(AddSeparatorAboveButton, 'add_sep_above', 24); IDEImages.AssignImage(AddSeparatorBelowButton, 'add_sep_below', 24); HelpButton.Hint:=lisMenuEditorGetHelpToUseThisEditor; end; procedure TMenuDesignerForm.EnableGUI(selectedIsNil: boolean); var isPopupMenu: boolean; begin if not FGUIEnabled then begin StatisticsGroupBox.Font.Style:=[fsBold]; StatisticsGroupBox.Caption:=FEditedMenu.Name; StatisticsGroupBox.Enabled:=True; ButtonsGroupBox.Enabled:=not selectedIsNil; if selectedIsNil then Caption:=Format(lisMenuEditorEditingSSNoMenuItemSelected, [TComponent(GlobalDesignHook.LookupRoot).Name, FEditedMenu.Name]); isPopupMenu:=(FEditedMenu is TPopupMenu); LoadVariableButtonGlyphs(not isPopupMenu); if isPopupMenu then ShowPopupAssignmentsInfo else HidePopupAssignmentsInfo; FGUIEnabled:=True; end; end; procedure TMenuDesignerForm.InitializeStatisticVars; begin FDesigner.Shortcuts.ResetMenuItemsCount; FIconsCount := -1; FDeepestNestingLevel := -1; FCaptionedItemsCount := -1; end; procedure TMenuDesignerForm.DisableGUI; begin if FGUIEnabled then begin StatisticsGroupBox.Font.Style:=[]; StatisticsGroupBox.Caption:=lisMenuEditorNoMenuSelected; CaptionedItemsCountLabel.Caption:=Format(lisMenuEditorCaptionedItemsS,[lisMenuEditorNA]); ShortcutItemsCountLabel.Caption:=Format(lisMenuEditorShortcutItemsS,[lisMenuEditorNA]); IconCountLabel.Caption:=Format(lisMenuEditorItemsWithIconS, [lisMenuEditorNA]); DeepestNestingLevelLabel.Caption:=Format(lisMenuEditorDeepestNestedMenuLevelS, [lisMenuEditorNA]); PopupAssignmentsCountLabel.Caption:=Format(lisMenuEditorPopupAssignmentsS,[lisMenuEditorNA]); StatisticsGroupBox.Enabled:=False; UpdateSubmenuGroupBox(nil, nil); ButtonsGroupBox.Enabled:=False; FPopupAssignmentsListBox.Visible:=False; FGUIEnabled:=False; InitializeStatisticVars; Caption:=Format('%s - %s',[lisMenuEditorMenuEditor, lisMenuEditorNoMenuSelected]); end; end; procedure TMenuDesignerForm.SetMenu(aMenu: TMenu; aMenuItem: TMenuItem); var selection: TMenuItem; begin if (aMenu = nil) then begin DisableGUI; FDesigner.FreeShadowMenu; FEditedMenu:=nil; end else begin if (aMenu = FEditedMenu) and (FDesigner.ShadowMenu <> nil) then FDesigner.ShadowMenu.SetSelectedMenuItem(aMenuItem, True, False) else begin selection := nil; if aMenu = FEditedMenu then begin if (FDesigner.ShadowMenu = nil) and (FEditedMenu.Items.Count > 0) then selection := FEditedMenu.Items[0]; end else begin FDesigner.FreeShadowMenu; FEditedMenu := aMenu; selection := aMenuItem; end; FGUIEnabled := False; EnableGUI(selection = nil); UpdateStatistics; FDesigner.CreateShadowMenu(FEditedMenu, selection, Width-LeftPanel.Width, Height); end; end; end; procedure TMenuDesignerForm.BeginUpdate; begin Inc(FUpdateCount); end; procedure TMenuDesignerForm.EndUpdate; var OI: TObjectInspectorDlg; begin if FUpdateCount<=0 then RaiseGDBException(''); Dec(FUpdateCount); if FUpdateCount = 0 then begin OI := FormEditingHook.GetCurrentObjectInspector; if Assigned(OI) then DesignerSetSelection(OI.Selection); end; end; function TMenuDesignerForm.IsUpdate: Boolean; begin Result := FUpdateCount > 0; end; procedure TMenuDesignerForm.UpdateStatistics; var captions, shrtcuts, icons, accels, tmp: integer; s: String; begin if not SameText(StatisticsGroupBox.Caption, FEditedMenu.Name) then StatisticsGroupBox.Caption:=FEditedMenu.Name; FDesigner.TotalMenuItemsCount:=GetItemCounts(captions, shrtcuts, icons, accels); if (FCaptionedItemsCount <> captions) then begin FCaptionedItemsCount:=captions; CaptionedItemsCountLabel.Caption:= Format(lisMenuEditorCaptionedItemsS, [IntToStr(captions)]); end; s:=FDesigner.Shortcuts.Statistics(shrtcuts); if s <> '' then ShortcutItemsCountLabel.Caption := s; if (FIconsCount <> icons) then begin FIconsCount:=icons; IconCountLabel.Caption:= Format(lisMenuEditorItemsWithIconS, [IntToStr(FIconsCount)]); end; if (FAcceleratorMenuItemsCount <> accels) then FAcceleratorMenuItemsCount:=accels; tmp:=GetNestingLevelDepth(FEditedMenu); if (FDeepestNestingLevel <> tmp) then begin DeepestNestingLevelLabel.Caption:= Format(lisMenuEditorDeepestNestedMenuLevelS, [IntToStr(tmp)]); FDeepestNestingLevel:=tmp; end; StatisticsGroupBox.Invalidate; end; function JoinToString(aGroups: TByteArray): String; var i: Integer; begin Result:=''; for i:=0 to Length(aGroups)-1 do begin if i>0 then Result:=Result+', '; Result:=Result+IntToStr(aGroups[i]); end; end; procedure TMenuDesignerForm.UpdateSubmenuGroupBox(selMI: TMenuItem; selBox: TShadowBoxBase); var Groups: TByteArray; begin if Assigned(selMI) then selBox.LastRIValue:=selMI.RadioItem; RadioItemGroupBox.Visible:=Assigned(selMI) and selMI.RadioItem; if RadioItemGroupBox.Visible then begin GroupIndexLabel.Caption:=Format(lisMenuEditorGroupIndexD, [selMI.GroupIndex]); Groups:=selBox.RadioGroupValues; RadioGroupsLabel.Visible:=Length(Groups)>1; if RadioGroupsLabel.Visible then RadioGroupsLabel.Caption:=Format(lisMenuEditorGroupIndexValuesS, [JoinToString(Groups)]); RadioItemGroupBox.Visible:=True; end; end; procedure TMenuDesignerForm.UpdateItemInfo(aMenu: TMenu; aMenuItem: TMenuItem; aShadowBox: TShadowBoxBase; aPropEditHook: TPropertyEditorHook); var s: string; method: TMethod; begin if aMenuItem = nil then begin Caption:=Format(lisMenuEditorEditingSSNoMenuitemSelected, [aMenu.Owner.Name, aMenu.Name]); ButtonsGroupBox.Enabled:=False; UpdateSubmenuGroupBox(nil, nil); end else begin method:=GetMethodProp(aMenuItem, 'OnClick'); s:=aPropEditHook.GetMethodName(method, aMenuItem); if s = '' then s:=lisMenuEditorIsNotAssigned; Caption:=Format(lisMenuEditorSSSOnClickS, [aMenu.Owner.Name, aMenu.Name, aMenuItem.Name, s]); ButtonsGroupBox.Enabled:=True; UpdateSubmenuGroupBox(aMenuItem, aShadowBox); end; end; { TRadioIcon } constructor TRadioIcon.CreateWithGlyph(aRIGroup: TRadioIconGroup; anImgIndex: integer); begin Assert(anImgIndex > -1, 'TRadioIcon.CreateWithGlyph: param not > -1'); inherited Create(aRIGroup); FRIGroup:=aRIGroup; FBGlyph:=TButtonGlyph.Create; FBGlyph.IsDesigning:=False; FBGlyph.ShowMode:=gsmAlways; FBGlyph.OnChange:=nil; FBGlyph.CacheSetImageList(FRIGroup.FImageList); FBGlyph.CacheSetImageIndex(0, anImgIndex); Tag:=anImgIndex; SetInitialBounds(0, 0, FRIGroup.FRadioWidth, FRIGroup.FRadioHeight); ControlStyle:=ControlStyle + [csCaptureMouse]-[csSetCaption, csClickEvents, csOpaque]; FRIState:=risUp; Color:=clBtnFace; end; destructor TRadioIcon.Destroy; begin FreeAndNil(FBGlyph); inherited Destroy; end; function TRadioIcon.GetChecked: Boolean; begin Result:=FRIState in [risDown, risPressed, risCheckedHot]; end; procedure TRadioIcon.SetChecked(aValue: Boolean); begin case aValue of True: if (FRIState <> risDown) then begin // set to True FRIState:=risDown; Invalidate; end; False: if (FRIState <> risUp) then begin // set to False FRIState:=risUp; Invalidate; end; end; end; procedure TRadioIcon.DoChange; begin if Assigned(FOnChange) then FOnChange(Self); end; procedure TRadioIcon.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin inherited MouseDown(Button, Shift, X, Y); if (Button = mbLeft) and (FRIState in [risUncheckedHot, risUp]) then begin FRIState:=risPressed; Invalidate; DoChange; end; end; procedure TRadioIcon.MouseEnter; begin inherited MouseEnter; case FRIState of risUp: FRIState:=risUncheckedHot; risDown: FRIState:=risCheckedHot; end; Invalidate; end; procedure TRadioIcon.MouseLeave; begin case FRIState of risPressed, risCheckedHot: FRIState:=risDown; risUncheckedHot: FRIState:=risUp; end; Invalidate; inherited MouseLeave; end; procedure TRadioIcon.Paint; var ted: TThemedElementDetails; begin if (Canvas.Brush.Color <> Color) then Canvas.Brush.Color:=Color; Canvas.FillRect(ClientRect); case FRIState of risUp: ted:=FRIGroup.FedUnchecked; risDown: ted:=FRIGroup.FedChecked; risPressed: ted:=FRIGroup.FedPressed; risUncheckedHot: ted:=FRIGroup.FedUncheckedHot; risCheckedHot: ted:=FRIGroup.FedCheckedHot; end; ThemeServices.DrawElement(Canvas.Handle, ted, FRIGroup.FRadioRect); FBGlyph.Draw(Canvas, ClientRect, FRIGroup.FGlyphPt, bsUp, False, 0, Font.PixelsPerInch, GetCanvasScaleFactor); inherited Paint; end; { TRadioIconGroup } constructor TRadioIconGroup.CreateWithImageList(AOwner: TComponent; anImgList: TCustomImageList); var topOffset: integer; begin Assert(AOwner<>nil,'TRadioIconGroup.CreateWithImageList: AOwner is nil'); Assert(anImgList<>nil,'TRadioIconGroup.CreateWithImageList:anImgList is nil'); inherited Create(AOwner); FImageList:=anImgList; FedUnChecked:=ThemeServices.GetElementDetails(tbRadioButtonUncheckedNormal); FedChecked:=ThemeServices.GetElementDetails(tbRadioButtonCheckedNormal); FedPressed:=ThemeServices.GetElementDetails(tbRadioButtonCheckedPressed); FedUncheckedHot:=ThemeServices.GetElementDetails(tbRadioButtonUncheckedHot); FedCheckedHot:=ThemeServices.GetElementDetails(tbRadioButtonCheckedHot); FedSize:=ThemeServices.GetDetailSizeForPPI(FedUnChecked, Font.PixelsPerInch); FRadioHeight:=Max(FedSize.cy, anImgList.Height); topOffset:=(FRadioHeight - FedSize.cy) div 2; FRadioRect:=Rect(0, topOffset, FedSize.cx, topOffset+FedSize.cy); FSpacing:=5; FRadioWidth:=FedSize.cx + FSpacing + anImgList.Width; FGlyphPt:=Point(FedSize.cx+FSpacing, 0); FItemIndex:= -1; HorzScrollBar.Visible := False; CreateRadioItems; end; procedure TRadioIconGroup.RefreshLayout; var i: Integer; ColCount: Integer; RadioIconWidth: Integer; RadioIconHeight: Integer; begin RadioIconWidth := FRIArray[0].Width + ScaleX(20, 96); RadioIconHeight := FRIArray[0].Height + ScaleX(20, 96); ColCount := Max(ClientWidth div RadioIconWidth, 1); for i := 0 to High(FRIArray) do begin FRIArray[i].Left := ScaleX(10, 96) + (i mod ColCount) * RadioIconWidth; FRIArray[i].Top := ScaleY(10, 96) + (i div ColCount) * RadioIconHeight; end; end; procedure TRadioIconGroup.CreateRadioItems; var i: integer; begin SetLength(FRIArray, FImageList.Count); for i:=Low(FRIArray) to High(FRIArray) do begin FRIArray[i]:=TRadioIcon.CreateWithGlyph(Self, i); FRIArray[i].OnChange:=@RIOnChange; end; end; procedure TRadioIconGroup.RIOnChange(Sender: TObject); var aRi: TRadioIcon; i: integer; begin if not (Sender is TRadioIcon) then Exit; aRi:=TRadioIcon(Sender); FItemIndex:=aRi.Tag; DoSelectItem; if aRi.Checked then begin for i:=Low(FRIArray) to High(FRIArray) do if (i <> FItemIndex) then FRIArray[i].Checked:=False; end; end; procedure TRadioIconGroup.DoSelectItem; begin if Assigned(FOnSelectItem) then FOnSelectItem(Self); end; procedure TRadioIconGroup.SetParent(NewParent: TWinControl); var i: Integer; begin inherited SetParent(NewParent); if (NewParent <> nil) then begin RefreshLayout; for i:=Low(FRIArray) to High(FRIArray) do FRIArray[i].SetParent(Self); end; end; { TdlgChooseIcon } constructor TdlgChooseIcon.Create(TheOwner: TComponent); begin inherited CreateNew(TheOwner); Position:=poScreenCenter; BorderStyle:=bsSizeable; Width:=250; Height:=250; FButtonPanel:=TButtonPanel.Create(Self); FButtonPanel.ShowButtons:=[pbOK, pbCancel]; FButtonPanel.OKButton.Name:='OKButton'; FButtonPanel.OKButton.DefaultCaption:=True; FButtonPanel.OKButton.Enabled:=False; FButtonPanel.CancelButton.Name:='CancelButton'; FButtonPanel.CancelButton.DefaultCaption:=True; FButtonPanel.Parent:=Self; OnResize := @dlgChooseIconResize; end; function TdlgChooseIcon.GetImageIndex: integer; begin Result:=FRadioIconGroup.ItemIndex; end; procedure TdlgChooseIcon.dlgChooseIconResize(Sender: TObject); begin if Assigned(FRadioIconGroup) then FRadioIconGroup.RefreshLayout; end; procedure TdlgChooseIcon.RIGClick(Sender: TObject); begin FButtonPanel.OKButton.Enabled:=True; FButtonPanel.OKButton.SetFocus; end; procedure TdlgChooseIcon.SetRadioIconGroup(anImageList: TCustomImageList); begin FRadioIconGroup:=TRadioIconGroup.CreateWithImageList(Self, anImageList); with FRadioIconGroup do begin Align:=alClient; BorderSpacing.Top:=FButtonPanel.BorderSpacing.Around; BorderSpacing.Left:=FButtonPanel.BorderSpacing.Around; BorderSpacing.Right:=FButtonPanel.BorderSpacing.Around; TabOrder:=0; OnSelectItem:=@RIGClick; Parent:=Self; end; Caption:=Format(lisMenuEditorPickAnIconFromS, [anImageList.Name]); end; end.