Rxmenus.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:58k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. {*******************************************************}
  2. {                                                       }
  3. {         Delphi VCL Extensions (RX)                    }
  4. {                                                       }
  5. {         Copyright (c) 1997, 1998 Master-Bank          }
  6. {                                                       }
  7. {*******************************************************}
  8. unit RxMenus;
  9. {$I RX.INC}
  10. {$S-,W-,R-}
  11. interface
  12. uses {$IFDEF WIN32} Windows, {$ELSE} WinTypes, WinProcs, {$ENDIF} SysUtils,
  13.   Classes, Controls, Messages, Graphics, {$IFDEF RX_D4} ImgList, {$ENDIF}
  14.   Menus, RxHook;
  15. type
  16.   TRxMenuStyle = (msStandard, msOwnerDraw {$IFDEF WIN32}, msBtnLowered,
  17.     msBtnRaised {$ENDIF});
  18.   TMenuOwnerDrawState = set of (mdSelected, mdGrayed, mdDisabled, mdChecked,
  19.     mdFocused {$IFDEF WIN32}, mdDefault {$ENDIF});
  20.   TDrawMenuItemEvent = procedure(Sender: TMenu; Item: TMenuItem; Rect: TRect;
  21.     State: TMenuOwnerDrawState) of object;
  22.   TMeasureMenuItemEvent = procedure(Sender: TMenu; Item: TMenuItem; var Width,
  23.     Height: Integer) of object;
  24.   TDrawMarginEvent = procedure(Sender: TMenu; Rect: TRect) of object;
  25.   TItemParamsEvent = procedure(Sender: TMenu; Item: TMenuItem;
  26.     State: TMenuOwnerDrawState; AFont: TFont; var Color: TColor;
  27.     var Graphic: TGraphic; var NumGlyphs: Integer) of object;
  28. {$IFDEF WIN32}
  29.   TItemImageEvent = procedure(Sender: TMenu; Item: TMenuItem;
  30.     State: TMenuOwnerDrawState; var ImageIndex: Integer) of object;
  31. {$ENDIF}
  32. { TRxMainMenu }
  33.   TRxMainMenu = class(TMainMenu)
  34.   private
  35.     FStyle: TRxMenuStyle;
  36.     FCanvas: TCanvas;
  37.     FHook: TRxWindowHook;
  38.     FShowCheckMarks: Boolean;
  39.     FMinTextOffset: Cardinal;
  40.     FCursor: TCursor;
  41.     FOnDrawItem: TDrawMenuItemEvent;
  42.     FOnMeasureItem: TMeasureMenuItemEvent;
  43.     FOnGetItemParams: TItemParamsEvent;
  44. {$IFDEF WIN32}
  45.     FImages: TImageList;
  46.     FImageChangeLink: TChangeLink;
  47.     FOnGetImageIndex: TItemImageEvent;
  48.     procedure SetImages(Value: TImageList);
  49.     procedure ImageListChange(Sender: TObject);
  50. {$ENDIF}
  51.     procedure SetStyle(Value: TRxMenuStyle);
  52.     function FindForm: TWinControl;
  53.     procedure WndMessage(Sender: TObject; var AMsg: TMessage;
  54.       var Handled: Boolean);
  55.     procedure CMMenuChanged(var Message: TMessage); message CM_MENUCHANGED;
  56.     procedure WMDrawItem(var Message: TWMDrawItem); message WM_DRAWITEM;
  57.     procedure WMMeasureItem(var Message: TWMMeasureItem); message WM_MEASUREITEM;
  58.     procedure WMMenuSelect(var Message: TWMMenuSelect); message WM_MENUSELECT;
  59.   protected
  60.     procedure Loaded; override;
  61. {$IFDEF WIN32}
  62.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  63.     procedure GetImageIndex(Item: TMenuItem; State: TMenuOwnerDrawState;
  64.       var ImageIndex: Integer); dynamic;
  65. {$ENDIF}
  66.     procedure DrawItem(Item: TMenuItem; Rect: TRect;
  67.       State: TMenuOwnerDrawState); virtual;
  68.     procedure GetItemParams(Item: TMenuItem; State: TMenuOwnerDrawState;
  69.       AFont: TFont; var Color: TColor; var Graphic: TGraphic;
  70.       var NumGlyphs: Integer); dynamic;
  71.     procedure MeasureItem(Item: TMenuItem; var Width, Height: Integer); dynamic;
  72.     procedure RefreshMenu(AOwnerDraw: Boolean); virtual;
  73.     function IsOwnerDrawMenu: Boolean;
  74.   public
  75.     constructor Create(AOwner: TComponent); override;
  76.     destructor Destroy; override;
  77.     procedure Refresh;
  78.     procedure DefaultDrawItem(Item: TMenuItem; Rect: TRect;
  79.       State: TMenuOwnerDrawState);
  80.     property Canvas: TCanvas read FCanvas;
  81.   published
  82.     property Cursor: TCursor read FCursor write FCursor default crDefault;
  83.     property MinTextOffset: Cardinal read FMinTextOffset write FMinTextOffset default 0;
  84.     property Style: TRxMenuStyle read FStyle write SetStyle default msStandard;
  85.     property ShowCheckMarks: Boolean read FShowCheckMarks write FShowCheckMarks default True;
  86. {$IFDEF RX_D4}
  87.     property OwnerDraw stored False;
  88. {$ENDIF}
  89. {$IFDEF WIN32}
  90.     property Images: TImageList read FImages write SetImages;
  91.     property OnGetImageIndex: TItemImageEvent read FOnGetImageIndex write FOnGetImageIndex;
  92. {$ENDIF}
  93.     property OnDrawItem: TDrawMenuItemEvent read FOnDrawItem write FOnDrawItem;
  94.     property OnGetItemParams: TItemParamsEvent read FOnGetItemParams write FOnGetItemParams;
  95.     property OnMeasureItem: TMeasureMenuItemEvent read FOnMeasureItem write FOnMeasureItem;
  96.   end;
  97. { TRxPopupMenu }
  98.   TRxPopupMenu = class(TPopupMenu)
  99.   private
  100.     FStyle: TRxMenuStyle;
  101.     FCanvas: TCanvas;
  102.     FShowCheckMarks: Boolean;
  103.     FMinTextOffset: Cardinal;
  104.     FLeftMargin: Cardinal;
  105.     FCursor: TCursor;
  106.     FOnDrawItem: TDrawMenuItemEvent;
  107.     FOnMeasureItem: TMeasureMenuItemEvent;
  108.     FOnDrawMargin: TDrawMarginEvent;
  109.     FOnGetItemParams: TItemParamsEvent;
  110. {$IFDEF RX_D4}
  111.     FPopupPoint: TPoint;
  112.     FParentBiDiMode: Boolean;
  113. {$ENDIF}
  114. {$IFDEF WIN32}
  115.     FImages: TImageList;
  116.     FImageChangeLink: TChangeLink;
  117.     FOnGetImageIndex: TItemImageEvent;
  118.     procedure SetImages(Value: TImageList);
  119.     procedure ImageListChange(Sender: TObject);
  120. {$ENDIF}
  121.     procedure SetStyle(Value: TRxMenuStyle);
  122.     procedure WndMessage(Sender: TObject; var AMsg: TMessage;
  123.       var Handled: Boolean);
  124.     procedure WMDrawItem(var Message: TWMDrawItem); message WM_DRAWITEM;
  125.     procedure WMMeasureItem(var Message: TWMMeasureItem); message WM_MEASUREITEM;
  126. {$IFDEF RX_D4}
  127.     procedure SetBiDiModeFromPopupControl;
  128. {$ENDIF}
  129.   protected
  130.     procedure Loaded; override;
  131. {$IFDEF RX_D4}
  132.     function UseRightToLeftAlignment: Boolean;
  133. {$ENDIF}
  134. {$IFDEF WIN32}
  135.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  136.     procedure GetImageIndex(Item: TMenuItem; State: TMenuOwnerDrawState;
  137.       var ImageIndex: Integer); dynamic;
  138. {$ENDIF}
  139.     procedure DrawItem(Item: TMenuItem; Rect: TRect;
  140.       State: TMenuOwnerDrawState); virtual;
  141.     procedure DrawMargin(ARect: TRect); virtual;
  142.     procedure GetItemParams(Item: TMenuItem; State: TMenuOwnerDrawState;
  143.       AFont: TFont; var Color: TColor; var Graphic: TGraphic;
  144.       var NumGlyphs: Integer); dynamic;
  145.     procedure MeasureItem(Item: TMenuItem; var Width, Height: Integer); dynamic;
  146.     procedure RefreshMenu(AOwnerDraw: Boolean); virtual;
  147.     function IsOwnerDrawMenu: Boolean;
  148.   public
  149.     constructor Create(AOwner: TComponent); override;
  150.     destructor Destroy; override;
  151.     procedure Refresh;
  152.     procedure Popup(X, Y: Integer); override;
  153.     procedure DefaultDrawItem(Item: TMenuItem; Rect: TRect;
  154.       State: TMenuOwnerDrawState);
  155.     procedure DefaultDrawMargin(ARect: TRect; StartColor, EndColor: TColor);
  156.     property Canvas: TCanvas read FCanvas;
  157.   published
  158.     property Cursor: TCursor read FCursor write FCursor default crDefault;
  159.     property LeftMargin: Cardinal read FLeftMargin write FLeftMargin default 0;
  160.     property MinTextOffset: Cardinal read FMinTextOffset write FMinTextOffset default 0;
  161.     property Style: TRxMenuStyle read FStyle write SetStyle default msStandard;
  162.     property ShowCheckMarks: Boolean read FShowCheckMarks write FShowCheckMarks default True;
  163. {$IFDEF RX_D4}
  164.     property OwnerDraw stored False;
  165. {$ENDIF}
  166. {$IFDEF WIN32}
  167.     property Images: TImageList read FImages write SetImages;
  168.     property OnGetImageIndex: TItemImageEvent read FOnGetImageIndex write FOnGetImageIndex;
  169. {$ENDIF}
  170.     property OnDrawItem: TDrawMenuItemEvent read FOnDrawItem write FOnDrawItem;
  171.     property OnDrawMargin: TDrawMarginEvent read FOnDrawMargin write FOnDrawMargin;
  172.     property OnGetItemParams: TItemParamsEvent read FOnGetItemParams write FOnGetItemParams;
  173.     property OnMeasureItem: TMeasureMenuItemEvent read FOnMeasureItem write FOnMeasureItem;
  174.   end;
  175. { Utility routines }
  176. procedure SetDefaultMenuFont(AFont: TFont);
  177. function IsItemPopup(Item: TMenuItem): Boolean;
  178. implementation
  179. uses {$IFDEF WIN32} CommCtrl, {$ENDIF} Forms, ExtCtrls, Consts, RxConst,
  180.   MaxMin, VclUtils, ClipIcon, rxStrUtils;
  181. const
  182.   DefMarginColor: TColor = clBlue;
  183.   AddWidth = 2;
  184.   AddHeight = 4;
  185.   Tab = #9#9;
  186.   Separator = '-';
  187. type
  188.   TBtnStyle = (bsNone, bsLowered, bsRaised, bsOffice);
  189. function BtnStyle(MenuStyle: TRxMenuStyle): TBtnStyle;
  190. begin
  191. {$IFDEF WIN32}
  192.   case MenuStyle of
  193.     msBtnLowered: Result := bsLowered;
  194.     msBtnRaised: Result := bsRaised;
  195.     else Result := bsNone;
  196.   end;
  197. {$ELSE}
  198.   Result := bsNone;
  199. {$ENDIF}
  200. end;
  201. function IsItemPopup(Item: TMenuItem): Boolean;
  202. begin
  203.   Result := (Item.Parent = nil) or (Item.Parent.Parent <> nil) or
  204.     not (Item.Parent.Owner is TMainMenu);
  205. end;
  206. {$IFNDEF WIN32}
  207. const
  208.   { return codes for WM_MENUCHAR (not defined in Delphi 1.0) }
  209.   MNC_IGNORE = 0;
  210.   MNC_CLOSE = 1;
  211.   MNC_EXECUTE = 2;
  212.   MNC_SELECT = 3;
  213. {$ENDIF}
  214. {$IFNDEF RX_D4}
  215. procedure ProcessMenuChar(AMenu: TMenu; var Message: TWMMenuChar);
  216. var
  217.   C, I, First, Hilite, Next: Integer;
  218.   State: Word;
  219.   function IsAccelChar(Menu: HMENU; State: Word; I: Integer; C: Char): Boolean;
  220.   var
  221.     Item: TMenuItem;
  222.     Id: Cardinal;
  223.   begin
  224.     Item := nil;
  225.     if State and MF_POPUP <> 0 then begin
  226.       Menu := GetSubMenu(Menu, I);
  227.       Item := AMenu.FindItem(Menu, fkHandle);
  228.     end
  229.     else begin
  230.       Id := GetMenuItemID(Menu, I);
  231.       if Id <> {$IFDEF WIN32} $FFFFFFFF {$ELSE} $FFFF {$ENDIF} then
  232.         Item := AMenu.FindItem(Id, fkCommand);
  233.     end;
  234.     if Item <> nil then Result := IsAccel(Ord(C), Item.Caption)
  235.     else Result := False;
  236.   end;
  237.   function IsInitialChar(Menu: HMENU; State: Word; I: Integer; C: Char): Boolean;
  238.   var
  239.     Item: TMenuItem;
  240.   begin
  241.     if State and MF_POPUP <> 0 then begin
  242.       Menu := GetSubMenu(Menu, I);
  243.       Item := AMenu.FindItem(Menu, fkHandle);
  244.     end
  245.     else begin
  246.       Item := AMenu.FindItem(Menu, fkHandle);
  247.       if Item <> nil then Item := Item.Items[I];
  248.     end;
  249.     if (Item <> nil) and (Item.Caption <> '') then
  250.       Result := AnsiCompareText(Item.Caption[1], C) = 0
  251.     else Result := False;
  252.   end;
  253. begin
  254.   with Message do begin
  255.     Result := MNC_IGNORE; { No item found: beep }
  256.     First := -1;
  257.     Hilite := -1;
  258.     Next := -1;
  259.     C := GetMenuItemCount(Menu);
  260.     for I := 0 to C - 1 do begin
  261.       State := GetMenuState(Menu, I, MF_BYPOSITION);
  262.       if IsAccelChar(Menu, State, I, User) then begin
  263.         if State and MF_DISABLED <> 0 then begin
  264.           { Close the menu if this is the only disabled item to choose from.
  265.             Otherwise, ignore the item. }
  266.           if First < 0 then First := -2;
  267.           Continue;
  268.         end;
  269.         if First < 0 then begin
  270.           First := I;
  271.           Result := MNC_EXECUTE;
  272.         end
  273.         else Result := MNC_SELECT;
  274.         if State and MF_HILITE <> 0 then Hilite := I
  275.         else if Hilite >= 0 then Next := I;
  276.       end;
  277.     end;
  278.     { We found a single disabled item. End the selection. }
  279.     if First < -1 then begin
  280.       Result := MNC_CLOSE shl 16;
  281.       Exit;
  282.     end;
  283.     { If we can't find accelerators, then look for initial letters }
  284.     if First < 0 then
  285.       for I := 0 to C - 1 do begin
  286.         State := GetMenuState(Menu, I, MF_BYPOSITION);
  287.         if IsInitialChar(Menu, State, I, User) then begin
  288.           if State and MF_DISABLED <> 0 then begin
  289.             Result := MNC_CLOSE shl 16;
  290.             Exit;
  291.           end;
  292.           if First < 0 then begin
  293.             First := I;
  294.             Result := MNC_EXECUTE;
  295.           end
  296.           else Result := MNC_SELECT;
  297.           if State and MF_HILITE <> 0 then Hilite := I
  298.           else if Hilite >= 0 then Next := I;
  299.         end;
  300.       end;
  301.     if (Result = MNC_EXECUTE) then Result := Result shl 16 or First
  302.     else if Result = MNC_SELECT then begin
  303.       if Next < 0 then Next := First;
  304.       Result := Result shl 16 or Next;
  305.     end;
  306.   end;
  307. end;
  308. {$ENDIF RX_D4}
  309. procedure MenuWndMessage(Menu: TMenu; var AMsg: TMessage; var Handled: Boolean);
  310. var
  311.   Message: TMessage;
  312.   Item: Pointer;
  313. begin
  314.   with AMsg do
  315.     case Msg of
  316.       WM_MEASUREITEM:
  317.         if (TWMMeasureItem(AMsg).MeasureItemStruct^.CtlType = ODT_MENU) then
  318.         begin
  319.           Item := Menu.FindItem(TWMMeasureItem(AMsg).MeasureItemStruct^.itemID, fkCommand);
  320.           if Item <> nil then begin
  321.             Message := AMsg;
  322.             TWMMeasureItem(Message).MeasureItemStruct^.ItemData := Longint(Item);
  323.             Menu.Dispatch(Message);
  324.             Result := 1;
  325.             Handled := True;
  326.           end;
  327.         end;
  328.       WM_DRAWITEM:
  329.         if (TWMDrawItem(AMsg).DrawItemStruct^.CtlType = ODT_MENU) then
  330.         begin
  331.           Item := Menu.FindItem(TWMDrawItem(AMsg).DrawItemStruct^.itemID, fkCommand);
  332.           if Item <> nil then begin
  333.             Message := AMsg;
  334.             TWMDrawItem(Message).DrawItemStruct^.ItemData := Longint(Item);
  335.             Menu.Dispatch(Message);
  336.             Result := 1;
  337.             Handled := True;
  338.           end;
  339.         end;
  340.       WM_MENUSELECT: Menu.Dispatch(AMsg);
  341.       CM_MENUCHANGED: Menu.Dispatch(AMsg);
  342.       WM_MENUCHAR:
  343.         begin
  344. {$IFDEF RX_D4}
  345.           Menu.ProcessMenuChar(TWMMenuChar(AMsg));
  346. {$ELSE}
  347.           ProcessMenuChar(Menu, TWMMenuChar(AMsg));
  348. {$ENDIF}
  349.         end;
  350.     end;
  351. end;
  352. {$IFNDEF RX_D4}
  353. procedure RefreshMenuItem(MenuItem: TMenuItem; OwnerDraw: Boolean);
  354. const
  355.   Breaks: array[TMenuBreak] of Longint = (0, MF_MENUBREAK, MF_MENUBARBREAK);
  356.   Checks: array[Boolean] of LongInt = (MF_UNCHECKED, MF_CHECKED);
  357.   Enables: array[Boolean] of LongInt = (MF_DISABLED or MF_GRAYED, MF_ENABLED);
  358.   Separators: array[Boolean] of LongInt = (MF_STRING, MF_SEPARATOR);
  359. {$IFDEF WIN32}
  360.   IBreaks: array[TMenuBreak] of DWORD = (MFT_STRING, MFT_MENUBREAK, MFT_MENUBARBREAK);
  361.   IRadios: array[Boolean] of DWORD = (MFT_STRING, MFT_RADIOCHECK);
  362.   ISeparators: array[Boolean] of DWORD = (MFT_STRING, MFT_SEPARATOR);
  363.   IOwnerDraw: array[Boolean] of DWORD = (MFT_STRING, MFT_OWNERDRAW);
  364. {$ENDIF}
  365. var
  366. {$IFDEF WIN32}
  367.   MenuItemInfo: TMenuItemInfo;
  368. {$ENDIF}
  369.   CCaption: array[0..255] of Char;
  370.   NewFlags: Integer;
  371.   ItemID, I, C: Integer;
  372.   MenuHandle: THandle;
  373.   Item: TMenuItem;
  374. {$IFDEF WIN32}
  375.   procedure PrepareItemInfo;
  376.   begin
  377.     FillChar(MenuItemInfo, SizeOf(TMenuItemInfo), 0);
  378.     with MenuItemInfo do begin
  379.       cbSize := SizeOf(TMenuItemInfo);
  380.       fMask := MIIM_CHECKMARKS or MIIM_DATA or MIIM_ID or MIIM_STATE or
  381.         MIIM_SUBMENU or MIIM_TYPE;
  382.       cch := SizeOf(CCaption) - 1;
  383.     end;
  384.   end;
  385. {$ENDIF}
  386. begin
  387.   if (MenuItem <> nil) then begin
  388.     StrPCopy(CCaption, MenuItem.Caption);
  389.     NewFlags := Breaks[MenuItem.Break] or Checks[MenuItem.Checked] or
  390.       Enables[MenuItem.Enabled] or Separators[MenuItem.Caption = Separator] or
  391.       MF_BYCOMMAND;
  392.     ItemID := MenuItem.Command;
  393.     if MenuItem.Count > 0 then begin
  394.       NewFlags := NewFlags or MF_POPUP;
  395.       ItemID := MenuItem.Handle;
  396.     end
  397.     else begin
  398.       if (MenuItem.ShortCut <> scNone) and ((MenuItem.Parent = nil) or
  399.         (MenuItem.Parent.Parent <> nil) or
  400.         not (MenuItem.Parent.Owner is TMainMenu)) then
  401.           StrPCopy(StrECopy(StrEnd(CCaption), Tab),
  402.             ShortCutToText(MenuItem.ShortCut));
  403.     end;
  404.     Item := MenuItem;
  405.     while Item.Parent <> nil do Item := Item.Parent;
  406.     if (Item.Owner <> nil) and (Item.Owner is TMenu) then
  407.       MenuHandle := TMenu(Item.Owner).Handle
  408.     else
  409.       MenuHandle := Item.Handle;
  410. {$IFDEF WIN32}
  411.     if Lo(GetVersion) >= 4 then begin
  412.       FillChar(MenuItemInfo, SizeOf(TMenuItemInfo), 0);
  413.       MenuItemInfo.cbSize := SizeOf(TMenuItemInfo);
  414.       if MenuItem.Count > 0 then begin
  415.         MenuItemInfo.fMask := MIIM_DATA or MIIM_TYPE;
  416.         with MenuItem do
  417.           MenuItemInfo.fType := IRadios[RadioItem] or IBreaks[Break] or
  418.             ISeparators[Caption = Separator] or IOwnerDraw[OwnerDraw];
  419.         MenuItemInfo.dwTypeData := CCaption;
  420.         SetMenuItemInfo(MenuHandle, MenuItem.Command, False, MenuItemInfo);
  421.       end
  422.       else begin
  423.         C := GetMenuItemCount(MenuHandle);
  424.         ItemID := -1;
  425.         for I := 0 to C - 1 do begin
  426.           PrepareItemInfo;
  427.           MenuItemInfo.dwTypeData := CCaption;
  428.           GetMenuItemInfo(MenuHandle, I, True, MenuItemInfo);
  429.           if MenuItemInfo.wID = MenuItem.Command then begin
  430.             ItemID := I;
  431.             Break;
  432.           end;
  433.         end;
  434.         if (ItemID < 0) and (MenuItem.Parent <> nil) then begin
  435.           MenuHandle := MenuItem.Parent.Handle;
  436.           C := GetMenuItemCount(MenuHandle);
  437.           for I := 0 to C - 1 do begin
  438.             PrepareItemInfo;
  439.             MenuItemInfo.dwTypeData := CCaption;
  440.             GetMenuItemInfo(MenuHandle, I, True, MenuItemInfo);
  441.             if MenuItemInfo.wID = MenuItem.Command then begin
  442.               ItemID := I;
  443.               Break;
  444.             end;
  445.           end;
  446.         end;
  447.         if ItemID < 0 then Exit;
  448.         with MenuItem do
  449.           MenuItemInfo.fType := IRadios[RadioItem] or IBreaks[Break] or
  450.             ISeparators[Caption = Separator] or IOwnerDraw[OwnerDraw];
  451.         MenuItemInfo.dwTypeData := CCaption;
  452.         DeleteMenu(MenuHandle, MenuItem.Command, MF_BYCOMMAND);
  453.         InsertMenuItem(MenuHandle, ItemID, True, MenuItemInfo);
  454.       end;
  455.     end
  456.     else
  457. {$ENDIF WIN32}
  458.     begin
  459.       if OwnerDraw then begin
  460.         ModifyMenu(MenuHandle, MenuItem.Command, NewFlags or MF_OWNERDRAW and
  461.           not MF_STRING, ItemID, PChar(MenuItem));
  462.       end
  463.       else begin
  464.         ModifyMenu(MenuHandle, MenuItem.Command, NewFlags, ItemID, CCaption);
  465.       end;
  466.     end;
  467.     for I := 0 to MenuItem.Count - 1 do
  468.       RefreshMenuItem(MenuItem.Items[I], OwnerDraw);
  469.   end;
  470. end;
  471. {$ENDIF RX_D4}
  472. procedure SetDefaultMenuFont(AFont: TFont);
  473. {$IFDEF WIN32}
  474. var
  475.   NCMetrics: TNonCLientMetrics;
  476. {$ENDIF}
  477. begin
  478. {$IFDEF WIN32}
  479.   if NewStyleControls then begin
  480.     NCMetrics.cbSize := SizeOf(TNonCLientMetrics);
  481.     if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NCMetrics, 0) then
  482.     begin
  483.       AFont.Handle := CreateFontIndirect(NCMetrics.lfMenuFont);
  484.       Exit;
  485.     end;
  486.   end;
  487. {$ENDIF}
  488.   with AFont do begin
  489.     if NewStyleControls then Name := 'MS Sans Serif'
  490.     else Name := 'System';
  491.     Size := 8;
  492.     Color := clMenuText;
  493.     Style := [];
  494.   end;
  495.   AFont.Color := clMenuText;
  496. end;
  497. function GetDefItemHeight: Integer;
  498. begin
  499.   Result := GetSystemMetrics(SM_CYMENU);
  500.   if NewStyleControls then Dec(Result, 2);
  501. end;
  502. function GetMarginOffset: Integer;
  503. begin
  504.   Result := Round(LoWord(GetMenuCheckMarkDimensions) * 0.3);
  505. end;
  506. procedure MenuLine(Canvas: TCanvas; C: TColor; X1, Y1, X2, Y2: Integer);
  507. begin
  508.   with Canvas do begin
  509.     Pen.Color := C;
  510.     MoveTo(X1, Y1);
  511.     LineTo(X2, Y2);
  512.   end;
  513. end;
  514. procedure DrawDisabledBitmap(Canvas: TCanvas; X, Y: Integer; Bitmap: TBitmap;
  515.   State: TMenuOwnerDrawState);
  516. const
  517.   ROP_DSPDxax = $00E20746;
  518. var
  519.   Bmp: TBitmap;
  520.   GrayColor, SaveColor: TColor;
  521.   IsHighlight: Boolean;
  522. begin
  523.   if (mdSelected in State) then GrayColor := clGrayText
  524.   else GrayColor := clBtnShadow;
  525.   IsHighlight := NewStyleControls and ((not (mdSelected in State)) or
  526.     (GetNearestColor(Canvas.Handle, ColorToRGB(clGrayText)) =
  527.     GetNearestColor(Canvas.Handle, ColorToRGB(clHighlight))));
  528.   if Bitmap.Monochrome then begin
  529.     SaveColor := Canvas.Brush.Color;
  530.     try
  531.       if IsHighlight then begin
  532.         Canvas.Brush.Color := clBtnHighlight;
  533.         SetTextColor(Canvas.Handle, clWhite);
  534.         SetBkColor(Canvas.Handle, clBlack);
  535.         BitBlt(Canvas.Handle, X + 1, Y + 1, Bitmap.Width, Bitmap.Height,
  536.           Bitmap.Canvas.Handle, 0, 0, ROP_DSPDxax);
  537.       end;
  538.       Canvas.Brush.Color := GrayColor;
  539.       SetTextColor(Canvas.Handle, clWhite);
  540.       SetBkColor(Canvas.Handle, clBlack);
  541.       BitBlt(Canvas.Handle, X, Y, Bitmap.Width, Bitmap.Height,
  542.         Bitmap.Canvas.Handle, 0, 0, ROP_DSPDxax);
  543.     finally
  544.       Canvas.Brush.Color := SaveColor;
  545.     end;
  546.   end
  547.   else begin
  548.     Bmp := CreateDisabledBitmapEx(Bitmap, clBlack, clMenu,
  549.       clBtnHighlight, GrayColor, IsHighlight);
  550.     try
  551.       DrawBitmapTransparent(Canvas, X, Y, Bmp, clMenu);
  552.     finally
  553.       Bmp.Free;
  554.     end;
  555.   end;
  556. end;
  557. procedure DrawMenuBitmap(Canvas: TCanvas; X, Y: Integer; Bitmap: TBitmap;
  558.   IsColor: Boolean; State: TMenuOwnerDrawState);
  559. begin
  560.   if (mdDisabled in State) then
  561.     DrawDisabledBitmap(Canvas, X, Y, Bitmap, State)
  562.   else begin
  563.     if Bitmap.Monochrome and not IsColor then
  564.       BitBlt(Canvas.Handle, X, Y, Bitmap.Width, Bitmap.Height,
  565.         Bitmap.Canvas.Handle, 0, 0, SRCCOPY)
  566.     else
  567.       DrawBitmapTransparent(Canvas, X, Y, Bitmap, Bitmap.TransparentColor
  568.         and not PaletteMask);
  569.   end;
  570. end;
  571. procedure DrawMenuItem(AMenu: TMenu; Item: TMenuItem; Glyph: TGraphic;
  572.   NumGlyphs: Integer; Canvas: TCanvas; ShowCheck: Boolean; Buttons: TBtnStyle;
  573.   Rect: TRect; MinOffset: {$IFDEF RX_D4} Integer {$ELSE} Cardinal {$ENDIF};
  574.   State: TMenuOwnerDrawState {$IFDEF WIN32}; Images: TImageList;
  575.   ImageIndex: Integer {$ENDIF});
  576. var
  577.   Left, LineTop, MaxWidth, I, W: Integer;
  578.   CheckSize: Longint;
  579.   BtnRect: TRect;
  580.   IsPopup, DrawHighlight, DrawLowered: Boolean;
  581.   GrayColor: TColor;
  582.   Bmp: TBitmap;
  583. {$IFDEF WIN32}
  584.   Ico: HIcon;
  585.   H: Integer;
  586. {$ENDIF}
  587. {$IFDEF RX_D4}
  588.   ParentMenu: TMenu;
  589. {$ENDIF}
  590.   procedure MenuTextOut(X, Y: Integer; const Text: string; Flags: Longint);
  591.   var
  592.     R: TRect;
  593.   begin
  594.     if Length(Text) = 0 then Exit;
  595. {$IFDEF RX_D4}
  596.     if (ParentMenu <> nil) and (ParentMenu.IsRightToLeft) then begin
  597.       if Flags and DT_LEFT = DT_LEFT then
  598.         Flags := Flags and (not DT_LEFT) or DT_RIGHT
  599.       else if Flags and DT_RIGHT = DT_RIGHT then
  600.         Flags := Flags and (not DT_RIGHT) or DT_LEFT;
  601.       Flags := Flags or DT_RTLREADING;
  602.     end;
  603. {$ENDIF}
  604.     R := Rect; R.Left := X; R.Top := Y;
  605.     if (mdDisabled in State) then begin
  606.       if DrawHighlight then begin
  607.         Canvas.Font.Color := clBtnHighlight;
  608.         OffsetRect(R, 1, 1);
  609.         DrawText(Canvas.Handle, @Text[1], Length(Text), R, Flags);
  610.         OffsetRect(R, -1, -1);
  611.       end;
  612.       Canvas.Font.Color := GrayColor;
  613.     end;
  614.     DrawText(Canvas.Handle, @Text[1], Length(Text), R, Flags)
  615.   end;
  616.   procedure DrawCheckImage(X, Y: Integer);
  617.   begin
  618.     Bmp := TBitmap.Create;
  619.     try
  620. {$IFDEF WIN32}
  621.       with Bmp do begin
  622.         Width := LoWord(CheckSize);
  623.         Height := HiWord(CheckSize);
  624.       end;
  625.       if Item.RadioItem then begin
  626.         with Bmp do begin
  627.           DrawFrameControl(Canvas.Handle, Bounds(0, 0, Width, Height),
  628.             DFC_MENU, DFCS_MENUBULLET);
  629.           Monochrome := True;
  630.         end;
  631.       end
  632.       else begin
  633.         with Bmp do begin
  634.           DrawFrameControl(Canvas.Handle, Bounds(0, 0, Width, Height),
  635.             DFC_MENU, DFCS_MENUCHECK);
  636.           Monochrome := True;
  637.         end;
  638.       end;
  639. {$ELSE}
  640.       Bmp.Handle := LoadBitmap(0, PChar(32760));
  641. {$ENDIF}
  642.       DrawMenuBitmap(Canvas, X, Y, Bmp, DrawLowered, State);
  643.     finally
  644.       Bmp.Free;
  645.     end;
  646.   end;
  647.   procedure DrawGlyphCheck(ARect: TRect);
  648.   var
  649.     SaveColor: TColor;
  650.     Bmp: TBitmap;
  651.   begin
  652.     InflateRect(ARect, 0, -1);
  653.     SaveColor := Canvas.Brush.Color;
  654.     try
  655.       if not (mdSelected in State) then
  656. {$IFDEF RX_D4}
  657.         Bmp := AllocPatternBitmap(clMenu, clBtnHighlight)
  658. {$ELSE}
  659.         Bmp := CreateTwoColorsBrushPattern(clMenu, clBtnHighlight)
  660. {$ENDIF}
  661.       else Bmp := nil;
  662.       try
  663.         if Bmp <> nil then Canvas.Brush.Bitmap := Bmp
  664.         else Canvas.Brush.Color := clMenu;
  665.         Canvas.FillRect(ARect);
  666.       finally
  667.         Canvas.Brush.Bitmap := nil;
  668. {$IFNDEF RX_D4}
  669.         Bmp.Free;
  670. {$ENDIF}
  671.       end;
  672.     finally
  673.       Canvas.Brush.Color := SaveColor;
  674.     end;
  675.     Frame3D(Canvas, ARect, GrayColor, clBtnHighlight, 1);
  676.   end;
  677. {$IFDEF WIN32}
  678.   function UseImages: Boolean;
  679.   begin
  680.     Result := Assigned(Images) and (ImageIndex >= 0) and
  681.       (ImageIndex < Images.Count) and Images.HandleAllocated;
  682.   end;
  683. {$ENDIF}
  684. begin
  685.   IsPopup := IsItemPopup(Item);
  686.   
  687.   DrawLowered := Item.Checked and IsPopup and not (ShowCheck or
  688.     (Buttons in [bsLowered, bsRaised]));
  689.   DrawHighlight := NewStyleControls and (not (mdSelected in State) or
  690.     (Buttons in [bsLowered, bsRaised]) or (not IsPopup and
  691.     (Buttons = bsOffice)) or
  692.     (GetNearestColor(Canvas.Handle, ColorToRGB(clGrayText)) =
  693.     GetNearestColor(Canvas.Handle, ColorToRGB(clHighlight))));
  694.   if (mdSelected in State) and not (Buttons in [bsLowered, bsRaised]) then
  695.     GrayColor := clGrayText
  696.   else GrayColor := clBtnShadow;
  697.   if IsPopup then begin
  698.     if ShowCheck then
  699.       CheckSize := GetMenuCheckMarkDimensions
  700.     else
  701.       CheckSize := 2;
  702.     Left := 2 * GetMarginOffset + LoWord(CheckSize);
  703.   end
  704.   else begin
  705.     MinOffset := 0;
  706.     CheckSize := 0;
  707.     Left := GetMarginOffset + 2;
  708.   end;
  709.   if (Buttons <> bsNone) and (mdSelected in State) then begin
  710.     case Buttons of
  711.       bsLowered: Frame3D(Canvas, Rect, clBtnShadow, clBtnHighlight, 1);
  712.       bsRaised: Frame3D(Canvas, Rect, clBtnHighlight, clBtnShadow, 1);
  713.       bsOffice:
  714.         if not IsPopup then
  715.           Frame3D(Canvas, Rect, clBtnShadow, clBtnHighlight, 1);
  716.     end;
  717.   end;
  718.   if Assigned(Item) then begin
  719. {$IFDEF RX_D4}
  720.     ParentMenu := Item.GetParentMenu;
  721. {$ENDIF}
  722.     if Item.Checked and ShowCheck and IsPopup then begin
  723.       DrawCheckImage(Rect.Left + (Left - LoWord(CheckSize)) div 2,
  724.         (Rect.Bottom + Rect.Top - HiWord(CheckSize)) div 2);
  725.     end;
  726. {$IFDEF WIN32}
  727.     if Assigned(Images) and IsPopup then
  728.       MinOffset := Max(MinOffset, Images.Width + AddWidth);
  729. {$ENDIF}
  730.     if not ShowCheck and (Assigned(Glyph) or (MinOffset > 0)) then
  731.       if Buttons = bsOffice then Left := 1
  732.       else Left := GetMarginOffset;
  733. {$IFDEF WIN32}
  734.     if UseImages then begin
  735.       W := Images.Width + AddWidth;
  736.       if W < Integer(MinOffset) then W := MinOffset;
  737.       BtnRect := Bounds(Rect.Left + Left - 1, Rect.Top, W + 2,
  738.         Rect.Bottom - Rect.Top);
  739.       if DrawLowered then DrawGlyphCheck(BtnRect)
  740.       else if (mdSelected in State) and IsPopup and (Buttons = bsOffice) and
  741.         not ShowCheck then
  742.       begin
  743.         Frame3D(Canvas, BtnRect, clBtnHighlight, GrayColor, 1);
  744.       end;
  745.       if (mdDisabled in State) then
  746.         ImageListDrawDisabled(Images, Canvas, Rect.Left + Left +
  747.           (W - Images.Width) div 2, (Rect.Bottom + Rect.Top -
  748.           Images.Height) div 2, ImageIndex, clBtnHighlight, GrayColor,
  749.           DrawHighlight)
  750.       else ImageList_Draw(Images.Handle, ImageIndex, Canvas.Handle,
  751.         Rect.Left + Left + (W - Images.Width) div 2, (Rect.Bottom +
  752.         Rect.Top - Images.Height) div 2, ILD_NORMAL);
  753.       Inc(Left, W + GetMarginOffset);
  754.     end else
  755. {$ENDIF}
  756.     if Assigned(Glyph) and not Glyph.Empty and (Item.Caption <> Separator) then
  757.     begin
  758.       W := Glyph.Width;
  759.       if (Glyph is TBitmap) and (NumGlyphs in [2..5]) then
  760.         W := W div NumGlyphs;
  761.       W := Max(W + AddWidth, MinOffset);
  762. {$IFDEF WIN32}
  763.       if not (Glyph is TIcon) then
  764. {$ENDIF}
  765.       begin
  766.         BtnRect := Bounds(Rect.Left + Left - 1, Rect.Top, W + 2,
  767.           Rect.Bottom - Rect.Top);
  768.         if DrawLowered then DrawGlyphCheck(BtnRect)
  769.         else if (mdSelected in State) and IsPopup and (Buttons = bsOffice) and
  770.           not ShowCheck then
  771.         begin
  772.           Frame3D(Canvas, BtnRect, clBtnHighlight, GrayColor, 1);
  773.         end;
  774.       end;
  775.       if Glyph is TBitmap then begin
  776.         if (NumGlyphs in [2..5]) then begin
  777.           I := 0;
  778.           if (mdDisabled in State) then I := 1
  779.           else if (mdChecked in State) then I := 3
  780.           else if (mdSelected in State) then I := 2;
  781.           if I > NumGlyphs - 1 then I := 0;
  782.           Bmp := TBitmap.Create;
  783.           try
  784.             AssignBitmapCell(Glyph, Bmp, NumGlyphs, 1, I);
  785.             DrawMenuBitmap(Canvas, Rect.Left + Left + (W - Bmp.Width) div 2,
  786.               (Rect.Bottom + Rect.Top - Bmp.Height) div 2, Bmp, DrawLowered,
  787.               State - [mdDisabled]);
  788.           finally
  789.             Bmp.Free;
  790.           end;
  791.         end
  792.         else DrawMenuBitmap(Canvas, Rect.Left + Left + (W - Glyph.Width) div 2,
  793.           (Rect.Bottom + Rect.Top - Glyph.Height) div 2, TBitmap(Glyph),
  794.           DrawLowered, State);
  795.         Inc(Left, W + GetMarginOffset);
  796.       end
  797. {$IFDEF WIN32}
  798.       else if Glyph is TIcon then begin
  799.         Ico := CreateRealSizeIcon(TIcon(Glyph));
  800.         try
  801.           GetIconSize(Ico, W, H);
  802.           I := Max(W + AddWidth, MinOffset);
  803.           BtnRect := Bounds(Rect.Left + Left - 1, Rect.Top, I + 2,
  804.             Rect.Bottom - Rect.Top);
  805.           if DrawLowered then DrawGlyphCheck(BtnRect)
  806.           else if (mdSelected in State) and IsPopup and (Buttons = bsOffice) and
  807.             not ShowCheck then
  808.           begin
  809.             Frame3D(Canvas, BtnRect, clBtnHighlight, GrayColor, 1);
  810.           end;
  811.           DrawIconEx(Canvas.Handle, Rect.Left + Left + (I - W) div 2,
  812.             (Rect.Top + Rect.Bottom - H) div 2, Ico, W, H, 0, 0, DI_NORMAL);
  813.           Inc(Left, I + GetMarginOffset);
  814.         finally
  815.           DestroyIcon(Ico);
  816.         end;
  817.       end
  818. {$ENDIF}
  819.       else begin
  820.         Canvas.Draw(Rect.Left + Left + (W - Glyph.Width) div 2,
  821.           (Rect.Bottom + Rect.Top - Glyph.Height) div 2, Glyph);
  822.         Inc(Left, W + GetMarginOffset);
  823.       end;
  824.     end
  825.     else if (MinOffset > 0) then begin
  826.       BtnRect := Bounds(Rect.Left + Left - 1, Rect.Top, MinOffset + 2,
  827.         Rect.Bottom - Rect.Top);
  828.       if DrawLowered then begin
  829.         DrawGlyphCheck(BtnRect);
  830.         CheckSize := GetMenuCheckMarkDimensions;
  831.         DrawCheckImage(BtnRect.Left + 2 + (MinOffset - LoWord(CheckSize)) div 2,
  832.           (Rect.Bottom + Rect.Top - HiWord(CheckSize)) div 2 + 1);
  833.       end
  834.       else if (mdSelected in State) and IsPopup and (Buttons = bsOffice) and
  835.         not ShowCheck then
  836.       begin
  837.         Frame3D(Canvas, BtnRect, clBtnHighlight, GrayColor, 1);
  838.       end;
  839.       Inc(Left, MinOffset + GetMarginOffset);
  840.     end;
  841.     if Item.Caption = Separator then begin
  842.       LineTop := (Rect.Top + Rect.Bottom) div 2 - 1;
  843.       if NewStyleControls then begin
  844.         Canvas.Pen.Width := 1;
  845.         MenuLine(Canvas, clBtnShadow, Rect.Left, LineTop, Rect.Right, LineTop);
  846.         MenuLine(Canvas, clBtnHighlight, Rect.Left, LineTop + 1, Rect.Right, LineTop + 1);
  847.       end
  848.       else begin
  849.         Canvas.Pen.Width := 2;
  850.         MenuLine(Canvas, clMenuText, Rect.Left, LineTop + 1, Rect.Right, LineTop + 1);
  851.       end;
  852.     end
  853.     else begin
  854.       MaxWidth := Canvas.TextWidth(DelChars(Item.Caption, '&') + Tab);
  855.       if (Item.Parent <> nil) and (Item.ShortCut <> scNone) then begin
  856.         for I := 0 to Item.Parent.Count - 1 do
  857.           MaxWidth := Max(Canvas.TextWidth(DelChars(Item.Parent.Items[I].Caption,
  858.             '&') + Tab), MaxWidth);
  859.       end;
  860.       Canvas.Brush.Style := bsClear;
  861.       LineTop := (Rect.Bottom + Rect.Top - Canvas.TextHeight('Ay')) div 2;
  862.       MenuTextOut(Rect.Left + Left, LineTop, Item.Caption, DT_EXPANDTABS or
  863.         DT_LEFT or DT_SINGLELINE);
  864.       if (Item.ShortCut <> scNone) and (Item.Count = 0) and IsPopup then begin
  865.         MenuTextOut(Rect.Left + Left + MaxWidth, LineTop,
  866.           ShortCutToText(Item.ShortCut), DT_EXPANDTABS or DT_LEFT or
  867.           DT_SINGLELINE);
  868.       end;
  869.     end;
  870.   end;
  871. end;
  872. procedure MenuMeasureItem(AMenu: TMenu; Item: TMenuItem; Canvas: TCanvas;
  873.   ShowCheck: Boolean; Glyph: TGraphic; NumGlyphs: Integer; var ItemWidth,
  874.   ItemHeight: Integer; MinOffset: Cardinal {$IFDEF WIN32}; Images: TImageList;
  875.   ImageIndex: Integer {$ENDIF});
  876. var
  877.   IsPopup: Boolean;
  878.   W, H: Integer;
  879. {$IFDEF WIN32}
  880.   Ico: HIcon;
  881. {$ENDIF}
  882.   function GetTextWidth(Item: TMenuItem): Integer;
  883.   var
  884.     I, MaxW: Integer;
  885.   begin
  886.     if IsPopup then begin
  887.       Result := Canvas.TextWidth(DelChars(Item.Caption, '&') + Tab);
  888.       MaxW := Canvas.TextWidth(ShortCutToText(Item.ShortCut) + ' ');
  889.       if (Item.Parent <> nil) and (Item.ShortCut <> scNone) then begin
  890.         for I := 0 to Item.Parent.Count - 1 do
  891.           with Item.Parent.Items[I] do begin
  892.             Result := Max(Result, Canvas.TextWidth(DelChars(Caption, '&') + Tab));
  893.             MaxW := Max(MaxW, Canvas.TextWidth(ShortCutToText(ShortCut) + ' '));
  894.           end;
  895.       end;
  896.       Result := Result + MaxW;
  897.       if Item.Count > 0 then Inc(Result, Canvas.TextWidth(Tab));
  898.     end
  899.     else Result := Canvas.TextWidth(DelChars(Item.Caption, '&'));
  900.   end;
  901. begin
  902.   IsPopup := IsItemPopup(Item);
  903.   ItemHeight := GetDefItemHeight;
  904.   if IsPopup then begin
  905.     ItemWidth := GetMarginOffset * 2;
  906. {$IFDEF WIN32}
  907.     if Assigned(Images) then
  908.       MinOffset := Max(MinOffset, Images.Width + AddWidth);
  909. {$ENDIF}
  910.   end
  911.   else begin
  912.     ItemWidth := 0;
  913.     MinOffset := 0;
  914.   end;
  915.   Inc(ItemWidth, GetTextWidth(Item));
  916.   if IsPopup and ShowCheck then
  917.     Inc(ItemWidth, LoWord(GetMenuCheckMarkDimensions));
  918.   if Item.Caption = Separator then begin
  919.     ItemHeight := Max(Canvas.TextHeight(Separator) div 2, 9);
  920.   end
  921.   else begin
  922.     ItemHeight := Max(ItemHeight, Canvas.TextHeight(Item.Caption));
  923. {$IFDEF WIN32}
  924.     if Assigned(Images) and (IsPopup or ((ImageIndex >= 0) and
  925.       (ImageIndex < Images.Count))) then
  926.     begin
  927.       Inc(ItemWidth, Max(Images.Width + AddWidth, MinOffset));
  928.       if not IsPopup then Inc(ItemWidth, GetMarginOffset);
  929.       if (ImageIndex >= 0) and (ImageIndex < Images.Count) then
  930.         ItemHeight := Max(ItemHeight, Images.Height + AddHeight);
  931.     end else
  932. {$ENDIF}
  933.     if Assigned(Glyph) and not Glyph.Empty then begin
  934.       W := Glyph.Width;
  935.       if (Glyph is TBitmap) and (NumGlyphs in [2..5]) then
  936.         W := W div NumGlyphs;
  937.       H := Glyph.Height;
  938. {$IFDEF WIN32}
  939.       if Glyph is TIcon then begin
  940.         Ico := CreateRealSizeIcon(TIcon(Glyph));
  941.         try
  942.           GetIconSize(Ico, W, H);
  943.         finally
  944.           DestroyIcon(Ico);
  945.         end;
  946.       end;
  947. {$ENDIF}
  948.       W := Max(W + AddWidth, MinOffset);
  949.       Inc(ItemWidth, W);
  950.       if not IsPopup then Inc(ItemWidth, GetMarginOffset);
  951.       ItemHeight := Max(ItemHeight, H + AddHeight);
  952.     end
  953.     else if MinOffset > 0 then begin
  954.       Inc(ItemWidth, MinOffset);
  955.       if not IsPopup then Inc(ItemWidth, GetMarginOffset);
  956.     end;
  957.   end;
  958. end;
  959. { TRxMainMenu }
  960. constructor TRxMainMenu.Create(AOwner: TComponent);
  961. begin
  962.   inherited Create(AOwner);
  963.   FCanvas := TControlCanvas.Create;
  964.   FShowCheckMarks := True;
  965.   FHook := TRxWindowHook.Create(Self);
  966.   FHook.AfterMessage := WndMessage;
  967. {$IFDEF WIN32}
  968.   FImageChangeLink := TChangeLink.Create;
  969.   FImageChangeLink.OnChange := ImageListChange;
  970. {$ENDIF}
  971. end;
  972. destructor TRxMainMenu.Destroy;
  973. begin
  974. {$IFDEF WIN32}
  975.   FImageChangeLink.Free;
  976. {$ENDIF}
  977.   SetStyle(msStandard);
  978.   FHook.Free;
  979.   FCanvas.Free;
  980.   inherited Destroy;
  981. end;
  982. procedure TRxMainMenu.Loaded;
  983. begin
  984.   inherited Loaded;
  985.   if IsOwnerDrawMenu then RefreshMenu(True);
  986. end;
  987. function TRxMainMenu.IsOwnerDrawMenu: Boolean;
  988. begin
  989.   Result := (FStyle <> msStandard)
  990.     {$IFDEF WIN32} or (Assigned(FImages) and (FImages.Count > 0)) {$ENDIF};
  991. end;
  992. {$IFDEF WIN32}
  993. procedure TRxMainMenu.Notification(AComponent: TComponent; Operation: TOperation);
  994. begin
  995.   inherited Notification(AComponent, Operation);
  996.   if Operation = opRemove then begin
  997.     if AComponent = FImages then SetImages(nil);
  998.   end;
  999. end;
  1000. procedure TRxMainMenu.ImageListChange(Sender: TObject);
  1001. begin
  1002.   if Sender = FImages then RefreshMenu(IsOwnerDrawMenu);
  1003. end;
  1004. procedure TRxMainMenu.SetImages(Value: TImageList);
  1005. var
  1006.   OldOwnerDraw: Boolean;
  1007. begin
  1008.   OldOwnerDraw := IsOwnerDrawMenu;
  1009.   if FImages <> nil then FImages.UnregisterChanges(FImageChangeLink);
  1010.   FImages := Value;
  1011.   if Value <> nil then begin
  1012.     FImages.RegisterChanges(FImageChangeLink);
  1013.     FImages.FreeNotification(Self);
  1014.   end;
  1015.   if IsOwnerDrawMenu then FHook.WinControl := FindForm
  1016.   else FHook.WinControl := nil;
  1017.   if IsOwnerDrawMenu <> OldOwnerDraw then RefreshMenu(not OldOwnerDraw);
  1018. end;
  1019. {$ENDIF}
  1020. procedure TRxMainMenu.SetStyle(Value: TRxMenuStyle);
  1021. begin
  1022.   if FStyle <> Value then begin
  1023.     FStyle := Value;
  1024.     if IsOwnerDrawMenu then FHook.WinControl := FindForm
  1025.     else FHook.WinControl := nil;
  1026.     RefreshMenu(IsOwnerDrawMenu);
  1027.   end;
  1028. end;
  1029. function TRxMainMenu.FindForm: TWinControl;
  1030. begin
  1031.   Result := FindControl(WindowHandle);
  1032.   if (Result = nil) and (Owner is TWinControl) then
  1033.     Result := TWinControl(Owner);
  1034. end;
  1035. procedure TRxMainMenu.Refresh;
  1036. begin
  1037.   RefreshMenu(IsOwnerDrawMenu);
  1038. end;
  1039. procedure TRxMainMenu.RefreshMenu(AOwnerDraw: Boolean);
  1040. {$IFDEF RX_D4}
  1041. begin
  1042.   Self.OwnerDraw := AOwnerDraw and (FHook.WinControl <> nil) and
  1043.     not (csDesigning in ComponentState);
  1044. {$ELSE}
  1045. var
  1046.   I: Integer;
  1047. begin
  1048.   if AOwnerDraw and (FHook.WinControl = nil) then Exit;
  1049.   if not (csDesigning in ComponentState) then
  1050.     for I := 0 to Items.Count - 1 do
  1051.       RefreshMenuItem(Items[I], AOwnerDraw);
  1052. {$ENDIF}
  1053. end;
  1054. procedure TRxMainMenu.DefaultDrawItem(Item: TMenuItem; Rect: TRect;
  1055.   State: TMenuOwnerDrawState);
  1056. var
  1057.   Graphic: TGraphic;
  1058.   BackColor: TColor;
  1059.   NumGlyphs {$IFDEF WIN32}, ImageIndex {$ENDIF}: Integer;
  1060. begin
  1061.   if Canvas.Handle <> 0 then begin
  1062.     Graphic := nil;
  1063.     BackColor := Canvas.Brush.Color;
  1064.     NumGlyphs := 1;
  1065.     GetItemParams(Item, State, Canvas.Font, BackColor, Graphic, NumGlyphs);
  1066. {$IFDEF WIN32}
  1067. {$IFDEF RX_D4}
  1068.     ImageIndex := Item.ImageIndex;
  1069. {$ELSE}
  1070.     ImageIndex := -1;
  1071. {$ENDIF}
  1072.     GetImageIndex(Item, State, ImageIndex);
  1073. {$ENDIF}
  1074.     DrawMenuItem(Self, Item, Graphic, NumGlyphs, Canvas, FShowCheckMarks,
  1075.       BtnStyle(Style), Rect, FMinTextOffset, State
  1076.       {$IFDEF WIN32}, FImages, ImageIndex {$ENDIF});
  1077.   end;
  1078. end;
  1079. procedure TRxMainMenu.DrawItem(Item: TMenuItem; Rect: TRect;
  1080.   State: TMenuOwnerDrawState);
  1081. var
  1082.   Graphic: TGraphic;
  1083.   BackColor: TColor;
  1084.   NumGlyphs {$IFDEF WIN32}, ImageIndex {$ENDIF}: Integer;
  1085. begin
  1086.   if Canvas.Handle <> 0 then begin
  1087.     Graphic := nil;
  1088.     BackColor := Canvas.Brush.Color;
  1089.     NumGlyphs := 1;
  1090.     GetItemParams(Item, State, Canvas.Font, BackColor, Graphic, NumGlyphs);
  1091.     if BackColor <> clNone then begin
  1092.       Canvas.Brush.Color := BackColor;
  1093.       Canvas.FillRect(Rect);
  1094.     end;
  1095.     if Assigned(FOnDrawItem) then FOnDrawItem(Self, Item, Rect, State)
  1096.     else begin
  1097. {$IFDEF WIN32}
  1098. {$IFDEF RX_D4}
  1099.       ImageIndex := Item.ImageIndex;
  1100. {$ELSE}
  1101.       ImageIndex := -1;
  1102. {$ENDIF}
  1103.       GetImageIndex(Item, State, ImageIndex);
  1104. {$ENDIF}
  1105.       DrawMenuItem(Self, Item, Graphic, NumGlyphs, Canvas, FShowCheckMarks,
  1106.         BtnStyle(Style), Rect, FMinTextOffset, State
  1107.         {$IFDEF WIN32}, FImages, ImageIndex {$ENDIF});
  1108.     end;
  1109.   end;
  1110. end;
  1111. procedure TRxMainMenu.MeasureItem(Item: TMenuItem; var Width, Height: Integer);
  1112. begin
  1113.   if Assigned(FOnMeasureItem) then FOnMeasureItem(Self, Item, Width, Height)
  1114. end;
  1115. procedure TRxMainMenu.WndMessage(Sender: TObject; var AMsg: TMessage;
  1116.   var Handled: Boolean);
  1117. begin
  1118.   if IsOwnerDrawMenu then MenuWndMessage(Self, AMsg, Handled);
  1119. end;
  1120. procedure TRxMainMenu.GetItemParams(Item: TMenuItem; State: TMenuOwnerDrawState;
  1121.   AFont: TFont; var Color: TColor; var Graphic: TGraphic; var NumGlyphs: Integer);
  1122. begin
  1123.   if Assigned(FOnGetItemParams) then
  1124.     FOnGetItemParams(Self, Item, State, AFont, Color, Graphic, NumGlyphs);
  1125.   if (Item <> nil) and (Item.Caption = Separator) then Graphic := nil;
  1126. end;
  1127. {$IFDEF WIN32}
  1128. procedure TRxMainMenu.GetImageIndex(Item: TMenuItem; State: TMenuOwnerDrawState;
  1129.   var ImageIndex: Integer);
  1130. begin
  1131.   if Assigned(FImages) and (Item <> nil) and (Item.Caption <> Separator) and
  1132.     Assigned(FOnGetImageIndex) then
  1133.     FOnGetImageIndex(Self, Item, State, ImageIndex);
  1134. end;
  1135. {$ENDIF}
  1136. procedure TRxMainMenu.CMMenuChanged(var Message: TMessage);
  1137. begin
  1138. {$IFNDEF RX_D4}
  1139.   if IsOwnerDrawMenu then RefreshMenu(True);
  1140. {$ENDIF}
  1141. end;
  1142. procedure TRxMainMenu.WMDrawItem(var Message: TWMDrawItem);
  1143. var
  1144.   State: TMenuOwnerDrawState;
  1145.   SaveIndex: Integer;
  1146.   Item: TMenuItem;
  1147. begin
  1148.   with Message.DrawItemStruct^ do begin
  1149. {$IFDEF WIN32}
  1150.     State := TMenuOwnerDrawState(WordRec(LongRec(itemState).Lo).Lo);
  1151. {$ELSE}
  1152.     State := TMenuOwnerDrawState(WordRec(itemState).Lo);
  1153. {$ENDIF}
  1154.     {if (mdDisabled in State) then State := State - [mdSelected];}
  1155.     Item := TMenuItem(Pointer(itemData));
  1156.     if Assigned(Item) and
  1157.       (FindItem(Item.Command, fkCommand) = Item) then
  1158.     begin
  1159.       SaveIndex := SaveDC(hDC);
  1160.       try
  1161.         FCanvas.Handle := hDC;
  1162.         SetDefaultMenuFont(FCanvas.Font);
  1163.         FCanvas.Font.Color := clMenuText;
  1164.         FCanvas.Brush.Color := clMenu;
  1165. {$IFDEF WIN32}
  1166.         if mdDefault in State then
  1167.           FCanvas.Font.Style := FCanvas.Font.Style + [fsBold];
  1168. {$ENDIF}
  1169.         if (mdSelected in State) {$IFDEF WIN32} and not
  1170.           (Style in [msBtnLowered, msBtnRaised]) {$ENDIF} then
  1171.         begin
  1172.           FCanvas.Brush.Color := clHighlight;
  1173.           FCanvas.Font.Color := clHighlightText;
  1174.         end;
  1175.         with rcItem do
  1176.           IntersectClipRect(FCanvas.Handle, Left, Top, Right, Bottom);
  1177.         DrawItem(Item, rcItem, State);
  1178.         FCanvas.Handle := 0;
  1179.       finally
  1180.         RestoreDC(hDC, SaveIndex);
  1181.       end;
  1182.     end;
  1183.   end;
  1184. end;
  1185. procedure TRxMainMenu.WMMeasureItem(var Message: TWMMeasureItem);
  1186. var
  1187.   Item: TMenuItem;
  1188.   Graphic: TGraphic;
  1189.   BackColor: TColor;
  1190.   DC: HDC;
  1191.   NumGlyphs {$IFDEF WIN32}, ImageIndex {$ENDIF}: Integer;
  1192. begin
  1193.   with Message.MeasureItemStruct^ do begin
  1194.     Item := TMenuItem(Pointer(itemData));
  1195.     if Assigned(Item) and (FindItem(Item.Command, fkCommand) = Item) then
  1196.     begin
  1197.       DC := GetDC(0);
  1198.       try
  1199.         FCanvas.Handle := DC;
  1200.         SetDefaultMenuFont(FCanvas.Font);
  1201. {$IFDEF WIN32}
  1202.         if Item.Default then
  1203.           FCanvas.Font.Style := FCanvas.Font.Style + [fsBold];
  1204. {$ENDIF}
  1205.         Graphic := nil;
  1206.         BackColor := FCanvas.Brush.Color;
  1207.         NumGlyphs := 1;
  1208.         GetItemParams(Item, [], FCanvas.Font, BackColor, Graphic, NumGlyphs);
  1209. {$IFDEF WIN32}
  1210. {$IFDEF RX_D4}
  1211.         ImageIndex := Item.ImageIndex;
  1212. {$ELSE}
  1213.         ImageIndex := -1;
  1214. {$ENDIF}
  1215.         GetImageIndex(Item, [], ImageIndex);
  1216. {$ENDIF}
  1217.         MenuMeasureItem(Self, Item, FCanvas, FShowCheckMarks, Graphic,
  1218.           NumGlyphs, Integer(itemWidth), Integer(itemHeight), FMinTextOffset
  1219.           {$IFDEF WIN32}, FImages, ImageIndex {$ENDIF});
  1220.         MeasureItem(Item, Integer(itemWidth), Integer(itemHeight));
  1221.       finally
  1222.         FCanvas.Handle := 0;
  1223.         ReleaseDC(0, DC);
  1224.       end;
  1225.     end;
  1226.   end;
  1227. end;
  1228. procedure TRxMainMenu.WMMenuSelect(var Message: TWMMenuSelect);
  1229. var
  1230.   MenuItem: TMenuItem;
  1231.   FindKind: TFindItemKind;
  1232.   MenuID: Integer;
  1233. begin
  1234.   if FCursor <> crDefault then
  1235.     with Message do begin
  1236.       FindKind := fkCommand;
  1237.       if MenuFlag and MF_POPUP <> 0 then begin
  1238.         FindKind := fkHandle;
  1239.         MenuId := GetSubMenu(Menu, IDItem);
  1240.       end
  1241.       else MenuId := IDItem;
  1242.       MenuItem := FindItem(MenuId, FindKind);
  1243.       if (MenuItem <> nil) and (IsItemPopup(MenuItem) or (MenuItem.Count = 0))
  1244.         and (MenuFlag and MF_HILITE <> 0) then
  1245.         SetCursor(Screen.Cursors[FCursor])
  1246.       else SetCursor(Screen.Cursors[crDefault]);
  1247.     end;
  1248. end;
  1249. { TPopupList }
  1250. type
  1251.   TPopupList = class(TList)
  1252.   private
  1253. {$IFNDEF WIN32}
  1254.     FMenuHelp: THelpContext;
  1255. {$ENDIF}
  1256.     procedure WndProc(var Message: TMessage);
  1257.   public
  1258.     Window: HWND;
  1259.     procedure Add(Popup: TPopupMenu);
  1260.     procedure Remove(Popup: TPopupMenu);
  1261.   end;
  1262. const
  1263.   PopupList: TPopupList = nil;
  1264. procedure TPopupList.WndProc(var Message: TMessage);
  1265. var
  1266.   I: Integer;
  1267.   MenuItem: TMenuItem;
  1268.   FindKind: TFindItemKind;
  1269.   ContextID: Integer;
  1270.   Handled: Boolean;
  1271. begin
  1272.   try
  1273.     case Message.Msg of
  1274.       WM_MEASUREITEM, WM_DRAWITEM:
  1275.         for I := 0 to Count - 1 do begin
  1276.           Handled := False;
  1277.           TRxPopupMenu(Items[I]).WndMessage(nil, Message, Handled);
  1278.           if Handled then Exit;
  1279.         end;
  1280.       WM_COMMAND:
  1281.         for I := 0 to Count - 1 do
  1282.           if TRxPopupMenu(Items[I]).DispatchCommand(Message.wParam) then Exit;
  1283.       WM_INITMENUPOPUP:
  1284.         for I := 0 to Count - 1 do
  1285.           with TWMInitMenuPopup(Message) do
  1286.             if TRxPopupMenu(Items[I]).DispatchPopup(MenuPopup) then Exit;
  1287.       WM_MENUSELECT:
  1288.         with TWMMenuSelect(Message) do begin
  1289.           FindKind := fkCommand;
  1290.           if MenuFlag and MF_POPUP <> 0 then begin
  1291.             FindKind := fkHandle;
  1292.             ContextId := GetSubMenu(Menu, IDItem);
  1293.           end
  1294.           else ContextId := IDItem;
  1295.           for I := 0 to Count - 1 do begin
  1296.             MenuItem := TRxPopupMenu(Items[I]).FindItem(ContextId, FindKind);
  1297.             if MenuItem <> nil then begin
  1298. {$IFNDEF WIN32}
  1299.               FMenuHelp := MenuItem.HelpContext;
  1300. {$ENDIF}
  1301.               Application.Hint := MenuItem.Hint;
  1302.               with TRxPopupMenu(Items[I]) do
  1303.                 if FCursor <> crDefault then begin
  1304.                   if (MenuFlag and MF_HILITE <> 0) then
  1305.                     SetCursor(Screen.Cursors[FCursor])
  1306.                   else SetCursor(Screen.Cursors[crDefault]);
  1307.                 end;
  1308.               Exit;
  1309.             end;
  1310.           end;
  1311. {$IFNDEF WIN32}
  1312.           FMenuHelp := 0;
  1313. {$ENDIF}
  1314.           Application.Hint := '';
  1315.         end;
  1316.       WM_MENUCHAR:
  1317.         for I := 0 to Count - 1 do
  1318.           with TRxPopupMenu(Items[I]) do
  1319.             if (Handle = HMenu(Message.LParam)) or
  1320.               (FindItem(Message.LParam, fkHandle) <> nil) then
  1321.             begin
  1322. {$IFDEF RX_D4}
  1323.               ProcessMenuChar(TWMMenuChar(Message));
  1324. {$ELSE}
  1325.               ProcessMenuChar(TRxPopupMenu(Items[I]), TWMMenuChar(Message));
  1326. {$ENDIF}
  1327.               Exit;
  1328.             end;
  1329. {$IFDEF WIN32}
  1330.       WM_HELP:
  1331.         with PHelpInfo(Message.LParam)^ do begin
  1332.           for I := 0 to Count - 1 do
  1333.             if TRxPopupMenu(Items[I]).Handle = hItemHandle then begin
  1334.               ContextID := TMenu(Items[I]).GetHelpContext(iCtrlID, True);
  1335.               if ContextID = 0 then
  1336.                 ContextID := TMenu(Items[I]).GetHelpContext(hItemHandle, False);
  1337.               if Screen.ActiveForm = nil then Exit;
  1338.               if (biHelp in Screen.ActiveForm.BorderIcons) then
  1339.                 Application.HelpCommand(HELP_CONTEXTPOPUP, ContextID)
  1340.               else
  1341.                 Application.HelpContext(ContextID);
  1342.               Exit;
  1343.             end;
  1344.         end;
  1345. {$ELSE}
  1346.       WM_ENTERIDLE:
  1347.         if (TWMEnterIdle(Message).Source = MSGF_MENU) and
  1348.           (GetKeyState(VK_F1) < 0) and (FMenuHelp <> 0) then
  1349.         begin
  1350.           Application.HelpContext(FMenuHelp);
  1351.           FMenuHelp := 0;
  1352.           Exit;
  1353.         end;
  1354. {$ENDIF WIN32}
  1355.     end;
  1356.     with Message do Result := DefWindowProc(Window, Msg, wParam, lParam);
  1357.   except
  1358.     Application.HandleException(Self);
  1359.   end;
  1360. end;
  1361. procedure TPopupList.Add(Popup: TPopupMenu);
  1362. begin
  1363.   if Count = 0 then Window := AllocateHWnd(WndProc);
  1364.   inherited Add(Popup);
  1365. end;
  1366. procedure TPopupList.Remove(Popup: TPopupMenu);
  1367. begin
  1368.   inherited Remove(Popup);
  1369.   if Count = 0 then DeallocateHWnd(Window);
  1370. end;
  1371. { TRxPopupMenu }
  1372. constructor TRxPopupMenu.Create(AOwner: TComponent);
  1373. begin
  1374.   inherited Create(AOwner);
  1375.   if PopupList = nil then
  1376.     PopupList := TPopupList.Create;
  1377.   FShowCheckMarks := True;
  1378.   FCanvas := TControlCanvas.Create;
  1379.   FCursor := crDefault;
  1380.   PopupList.Add(Self);
  1381. {$IFDEF WIN32}
  1382.   FImageChangeLink := TChangeLink.Create;
  1383.   FImageChangeLink.OnChange := ImageListChange;
  1384. {$ENDIF}
  1385. {$IFDEF RX_D4}
  1386.   FPopupPoint := Point(-1, -1);
  1387. {$ENDIF}
  1388. end;
  1389. destructor TRxPopupMenu.Destroy;
  1390. begin
  1391. {$IFDEF WIN32}
  1392.   FImageChangeLink.Free;
  1393. {$ENDIF}
  1394.   SetStyle(msStandard);
  1395.   PopupList.Remove(Self);
  1396.   FCanvas.Free;
  1397.   inherited Destroy;
  1398. end;
  1399. procedure TRxPopupMenu.Loaded;
  1400. begin
  1401.   inherited Loaded;
  1402.   if IsOwnerDrawMenu then RefreshMenu(True);
  1403. end;
  1404. {$IFDEF WIN32}
  1405. procedure TRxPopupMenu.Notification(AComponent: TComponent; Operation: TOperation);
  1406. begin
  1407.   inherited Notification(AComponent, Operation);
  1408.   if Operation = opRemove then begin
  1409.     if AComponent = FImages then SetImages(nil);
  1410.   end;
  1411. end;
  1412. procedure TRxPopupMenu.ImageListChange(Sender: TObject);
  1413. begin
  1414.   if Sender = FImages then RefreshMenu(IsOwnerDrawMenu);
  1415. end;
  1416. procedure TRxPopupMenu.SetImages(Value: TImageList);
  1417. var
  1418.   OldOwnerDraw: Boolean;
  1419. begin
  1420.   OldOwnerDraw := IsOwnerDrawMenu;
  1421.   if FImages <> nil then FImages.UnregisterChanges(FImageChangeLink);
  1422.   FImages := Value;
  1423.   if Value <> nil then begin
  1424.     FImages.RegisterChanges(FImageChangeLink);
  1425.     FImages.FreeNotification(Self);
  1426.   end;
  1427.   if IsOwnerDrawMenu <> OldOwnerDraw then RefreshMenu(not OldOwnerDraw);
  1428. end;
  1429. {$ENDIF}
  1430. {$IFDEF RX_D4}
  1431. function FindPopupControl(const Pos: TPoint): TControl;
  1432. var
  1433.   Window: TWinControl;
  1434. begin
  1435.   Result := nil;
  1436.   Window := FindVCLWindow(Pos);
  1437.   if Window <> nil then begin
  1438.     Result := Window.ControlAtPos(Pos, False);
  1439.     if Result = nil then Result := Window;
  1440.   end;
  1441. end;
  1442. procedure TRxPopupMenu.SetBiDiModeFromPopupControl;
  1443. var
  1444.   AControl: TControl;
  1445. begin
  1446.   if not SysLocale.MiddleEast then Exit;
  1447.   if FParentBiDiMode then begin
  1448.     AControl := FindPopupControl(FPopupPoint);
  1449.     if AControl <> nil then
  1450.       BiDiMode := AControl.BiDiMode
  1451.     else
  1452.       BiDiMode := Application.BiDiMode;
  1453.   end;
  1454. end;
  1455. function TRxPopupMenu.UseRightToLeftAlignment: Boolean;
  1456. var
  1457.   AControl: TControl;
  1458. begin
  1459.   Result := False;
  1460.   if not SysLocale.MiddleEast then Exit;
  1461.   if FParentBiDiMode then begin
  1462.     AControl := FindPopupControl(FPopupPoint);
  1463.     if AControl <> nil then
  1464.       Result := AControl.UseRightToLeftAlignment
  1465.     else
  1466.       Result := Application.UseRightToLeftAlignment;
  1467.   end
  1468.   else Result := (BiDiMode = bdRightToLeft);
  1469. end;
  1470. {$ENDIF RX_D4}
  1471. procedure TRxPopupMenu.Popup(X, Y: Integer);
  1472. const
  1473. {$IFDEF RX_D4}
  1474.   Flags: array[Boolean, TPopupAlignment] of Word =
  1475.     ((TPM_LEFTALIGN, TPM_RIGHTALIGN, TPM_CENTERALIGN),
  1476.      (TPM_RIGHTALIGN, TPM_LEFTALIGN, TPM_CENTERALIGN));
  1477.   Buttons: array[TTrackButton] of Word = (TPM_RIGHTBUTTON, TPM_LEFTBUTTON);
  1478. {$ELSE}
  1479.   Flags: array[TPopupAlignment] of Word = (TPM_LEFTALIGN, TPM_RIGHTALIGN,
  1480.     TPM_CENTERALIGN);
  1481. {$ENDIF}
  1482. var
  1483.   FOnPopup: TNotifyEvent;
  1484. begin
  1485. {$IFDEF RX_D4}
  1486.   FPopupPoint := Point(X, Y);
  1487.   FParentBiDiMode := ParentBiDiMode;
  1488.   try
  1489.     SetBiDiModeFromPopupControl;
  1490. {$ENDIF}
  1491.     FOnPopup := OnPopup;
  1492.     if Assigned(FOnPopup) then FOnPopup(Self);
  1493.     if IsOwnerDrawMenu then RefreshMenu(True);
  1494. {$IFNDEF WIN32}
  1495.     PopupList.FMenuHelp := HelpContext;
  1496. {$ENDIF}
  1497. {$IFDEF RX_D4}
  1498.     AdjustBiDiBehavior;
  1499.     TrackPopupMenu(Items.Handle,
  1500.       Flags[UseRightToLeftAlignment, Alignment] or Buttons[TrackButton], X, Y,
  1501.       0 { reserved }, PopupList.Window, nil);
  1502.   finally
  1503.     ParentBiDiMode := FParentBiDiMode;
  1504.   end;
  1505. {$ELSE}
  1506.   TrackPopupMenu(Items.Handle, Flags[Alignment] or TPM_RIGHTBUTTON, X, Y,
  1507.     0 { reserved }, PopupList.Window, nil);
  1508. {$ENDIF}
  1509. end;
  1510. procedure TRxPopupMenu.Refresh;
  1511. begin
  1512.   RefreshMenu(IsOwnerDrawMenu);
  1513. end;
  1514. function TRxPopupMenu.IsOwnerDrawMenu: Boolean;
  1515. begin
  1516.   Result := (FStyle <> msStandard)
  1517.     {$IFDEF WIN32} or (Assigned(FImages) and (FImages.Count > 0)) {$ENDIF};
  1518. end;
  1519. procedure TRxPopupMenu.RefreshMenu(AOwnerDraw: Boolean);
  1520. {$IFDEF RX_D4}
  1521. begin
  1522.   Self.OwnerDraw := AOwnerDraw and not (csDesigning in ComponentState);
  1523. {$ELSE}
  1524. var
  1525.   I: Integer;
  1526. begin
  1527.   if not (csDesigning in ComponentState) then
  1528.     for I := 0 to Items.Count - 1 do
  1529.       RefreshMenuItem(Items[I], AOwnerDraw);
  1530. {$ENDIF}
  1531. end;
  1532. procedure TRxPopupMenu.SetStyle(Value: TRxMenuStyle);
  1533. begin
  1534.   if FStyle <> Value then begin
  1535.     FStyle := Value;
  1536.     RefreshMenu(IsOwnerDrawMenu);
  1537.   end;
  1538. end;
  1539. procedure TRxPopupMenu.DefaultDrawItem(Item: TMenuItem; Rect: TRect;
  1540.   State: TMenuOwnerDrawState);
  1541. var
  1542.   Graphic: TGraphic;
  1543.   BackColor: TColor;
  1544.   NumGlyphs {$IFDEF WIN32}, ImageIndex {$ENDIF}: Integer;
  1545. begin
  1546.   if Canvas.Handle <> 0 then begin
  1547.     Graphic := nil;
  1548.     BackColor := Canvas.Brush.Color;
  1549.     NumGlyphs := 1;
  1550.     GetItemParams(Item, State, Canvas.Font, BackColor, Graphic, NumGlyphs);
  1551. {$IFDEF WIN32}
  1552. {$IFDEF RX_D4}
  1553.     ImageIndex := Item.ImageIndex;
  1554. {$ELSE}
  1555.     ImageIndex := -1;
  1556. {$ENDIF}
  1557.     GetImageIndex(Item, State, ImageIndex);
  1558. {$ENDIF}
  1559.     DrawMenuItem(Self, Item, Graphic, NumGlyphs, Canvas, FShowCheckMarks,
  1560.       BtnStyle(Style), Rect, FMinTextOffset, State
  1561.       {$IFDEF WIN32}, FImages, ImageIndex {$ENDIF});
  1562.   end;
  1563. end;
  1564. procedure TRxPopupMenu.DrawItem(Item: TMenuItem; Rect: TRect;
  1565.   State: TMenuOwnerDrawState);
  1566. var
  1567.   Graphic: TGraphic;
  1568.   BackColor: TColor;
  1569.   NumGlyphs {$IFDEF WIN32}, ImageIndex {$ENDIF}: Integer;
  1570. begin
  1571.   if Canvas.Handle <> 0 then begin
  1572.     Graphic := nil;
  1573.     BackColor := Canvas.Brush.Color;
  1574.     NumGlyphs := 1;
  1575.     GetItemParams(Item, State, Canvas.Font, BackColor, Graphic, NumGlyphs);
  1576.     if BackColor <> clNone then begin
  1577.       Canvas.Brush.Color := BackColor;
  1578.       Canvas.FillRect(Rect);
  1579.     end;
  1580.     if Assigned(FOnDrawItem) then FOnDrawItem(Self, Item, Rect, State)
  1581.     else begin
  1582. {$IFDEF WIN32}
  1583. {$IFDEF RX_D4}
  1584.       ImageIndex := Item.ImageIndex;
  1585. {$ELSE}
  1586.       ImageIndex := -1;
  1587. {$ENDIF}
  1588.       GetImageIndex(Item, State, ImageIndex);
  1589. {$ENDIF}
  1590.       DrawMenuItem(Self, Item, Graphic, NumGlyphs, Canvas, FShowCheckMarks,
  1591.         BtnStyle(Style), Rect, FMinTextOffset, State
  1592.         {$IFDEF WIN32}, FImages, ImageIndex {$ENDIF});
  1593.     end;
  1594.   end;
  1595. end;
  1596. procedure TRxPopupMenu.MeasureItem(Item: TMenuItem; var Width, Height: Integer);
  1597. begin
  1598.   if Assigned(FOnMeasureItem) then FOnMeasureItem(Self, Item, Width, Height)
  1599. end;
  1600. procedure TRxPopupMenu.WndMessage(Sender: TObject; var AMsg: TMessage;
  1601.   var Handled: Boolean);
  1602. begin
  1603.   if IsOwnerDrawMenu then MenuWndMessage(Self, AMsg, Handled);
  1604. end;
  1605. procedure TRxPopupMenu.GetItemParams(Item: TMenuItem; State: TMenuOwnerDrawState;
  1606.   AFont: TFont; var Color: TColor; var Graphic: TGraphic; var NumGlyphs: Integer);
  1607. begin
  1608.   if Assigned(FOnGetItemParams) then
  1609.     FOnGetItemParams(Self, Item, State, AFont, Color, Graphic, NumGlyphs);
  1610.   if (Item <> nil) and (Item.Caption = Separator) then Graphic := nil;
  1611. end;
  1612. {$IFDEF WIN32}
  1613. procedure TRxPopupMenu.GetImageIndex(Item: TMenuItem; State: TMenuOwnerDrawState;
  1614.   var ImageIndex: Integer);
  1615. begin
  1616.   if Assigned(FImages) and (Item <> nil) and (Item.Caption <> Separator) and
  1617.     Assigned(FOnGetImageIndex) then
  1618.     FOnGetImageIndex(Self, Item, State, ImageIndex);
  1619. end;
  1620. {$ENDIF}
  1621. procedure TRxPopupMenu.DefaultDrawMargin(ARect: TRect; StartColor,
  1622.   EndColor: TColor);
  1623. var
  1624.   R: Integer;
  1625. begin
  1626.   with ARect do begin
  1627.     if NewStyleControls then R := Right - 3
  1628.     else R := Right;
  1629.     GradientFillRect(Canvas, Rect(Left, Top, R, Bottom), StartColor,
  1630.       EndColor, fdTopToBottom, 32);
  1631.     if NewStyleControls then begin
  1632.       MenuLine(Canvas, clBtnShadow, Right - 2, Top, Right - 2, Bottom);
  1633.       MenuLine(Canvas, clBtnHighlight, Right - 1, Top, Right - 1, Bottom);
  1634.     end;
  1635.   end;
  1636. end;
  1637. procedure TRxPopupMenu.DrawMargin(ARect: TRect);
  1638. begin
  1639.   if Assigned(FOnDrawMargin) then FOnDrawMargin(Self, ARect)
  1640.   else begin
  1641.     DefaultDrawMargin(ARect, DefMarginColor, RGB(
  1642.       GetRValue(DefMarginColor) div 4,
  1643.       GetGValue(DefMarginColor) div 4,
  1644.       GetBValue(DefMarginColor) div 4));
  1645.   end;
  1646. end;
  1647. procedure TRxPopupMenu.WMDrawItem(var Message: TWMDrawItem);
  1648. var
  1649.   State: TMenuOwnerDrawState;
  1650.   SaveIndex: Integer;
  1651.   Item: TMenuItem;
  1652.   MarginRect: TRect;
  1653. begin
  1654.   with Message.DrawItemStruct^ do begin
  1655. {$IFDEF WIN32}
  1656.     State := TMenuOwnerDrawState(WordRec(LongRec(itemState).Lo).Lo);
  1657. {$ELSE}
  1658.     State := TMenuOwnerDrawState(WordRec(itemState).Lo);
  1659. {$ENDIF}
  1660.     Item := TMenuItem(Pointer(itemData));
  1661.     if Assigned(Item) and
  1662.       (FindItem(Item.Command, fkCommand) = Item) then
  1663.     begin
  1664.       SaveIndex := SaveDC(hDC);
  1665.       try
  1666.         FCanvas.Handle := hDC;
  1667.         if (Item.Parent = Self.Items) and (FLeftMargin > 0) then
  1668.           if (itemAction = ODA_DRAWENTIRE) then begin
  1669.             MarginRect := FCanvas.ClipRect;
  1670.             MarginRect.Left := 0;
  1671.             MarginRect.Right := FLeftMargin;
  1672.             DrawMargin(MarginRect);
  1673.           end;
  1674.         SetDefaultMenuFont(FCanvas.Font);
  1675.         FCanvas.Font.Color := clMenuText;
  1676.         FCanvas.Brush.Color := clMenu;
  1677. {$IFDEF WIN32}
  1678.         if mdDefault in State then
  1679.           FCanvas.Font.Style := FCanvas.Font.Style + [fsBold];
  1680. {$ENDIF}
  1681.         if (mdSelected in State) {$IFDEF WIN32} and
  1682.           not (Style in [msBtnLowered, msBtnRaised]) {$ENDIF} then
  1683.         begin
  1684.           FCanvas.Brush.Color := clHighlight;
  1685.           FCanvas.Font.Color := clHighlightText;
  1686.         end;
  1687.         if (Item.Parent = Self.Items) then
  1688.           Inc(rcItem.Left, LeftMargin + 1);
  1689.         with rcItem do
  1690.           IntersectClipRect(FCanvas.Handle, Left, Top, Right, Bottom);
  1691.         DrawItem(Item, rcItem, State);
  1692.         FCanvas.Handle := 0;
  1693.       finally
  1694.         RestoreDC(hDC, SaveIndex);
  1695.       end;
  1696.     end;
  1697.   end;
  1698. end;
  1699. procedure TRxPopupMenu.WMMeasureItem(var Message: TWMMeasureItem);
  1700. var
  1701.   Item: TMenuItem;
  1702.   Graphic: TGraphic;
  1703.   BackColor: TColor;
  1704.   NumGlyphs {$IFDEF WIN32}, ImageIndex {$ENDIF}: Integer;
  1705. begin
  1706.   with Message.MeasureItemStruct^ do begin
  1707.     Item := TMenuItem(Pointer(itemData));
  1708.     if Assigned(Item) and (FindItem(Item.Command, fkCommand) = Item) then
  1709.     begin
  1710.       FCanvas.Handle := GetDC(0);
  1711.       try
  1712.         SetDefaultMenuFont(FCanvas.Font);
  1713. {$IFDEF WIN32}
  1714.         if Item.Default then
  1715.           FCanvas.Font.Style := FCanvas.Font.Style + [fsBold];
  1716. {$ENDIF}
  1717.         Graphic := nil;
  1718.         BackColor := Canvas.Brush.Color;
  1719.         NumGlyphs := 1;
  1720.         GetItemParams(Item, [], FCanvas.Font, BackColor, Graphic, NumGlyphs);
  1721. {$IFDEF WIN32}
  1722. {$IFDEF RX_D4}
  1723.         ImageIndex := Item.ImageIndex;
  1724. {$ELSE}
  1725.         ImageIndex := -1;
  1726. {$ENDIF}
  1727.         GetImageIndex(Item, [], ImageIndex);
  1728. {$ENDIF}
  1729.         MenuMeasureItem(Self, Item, FCanvas, FShowCheckMarks, Graphic,
  1730.           NumGlyphs, Integer(itemWidth), Integer(itemHeight), FMinTextOffset
  1731.           {$IFDEF WIN32}, FImages, ImageIndex {$ENDIF});
  1732.         MeasureItem(Item, Integer(itemWidth), Integer(itemHeight));
  1733.         if (Item.Parent = Self.Items) then
  1734.           Inc(itemWidth, LeftMargin + 1);
  1735.       finally
  1736.         ReleaseDC(0, FCanvas.Handle);
  1737.         FCanvas.Handle := 0;
  1738.       end;
  1739.     end;
  1740.   end;
  1741. end;
  1742. {$IFNDEF WIN32}
  1743. procedure FreePopupList; far;
  1744. begin
  1745.   if PopupList <> nil then begin
  1746.     PopupList.Free;
  1747.     PopupList := nil;
  1748.   end;
  1749. end;
  1750. {$ENDIF}
  1751. initialization
  1752.   PopupList := nil;
  1753. {$IFDEF WIN32}
  1754. finalization
  1755.   if PopupList <> nil then PopupList.Free;
  1756. {$ELSE}
  1757.   AddExitProc(FreePopupList);
  1758. {$ENDIF}
  1759. end.