2 {*****************************************************************************}
4 { Tnt Delphi Unicode Controls }
5 { http://www.tntware.com/delphicontrols/unicode/ }
8 { Copyright (c) 2002-2007, Troy Wolbrink (troy.wolbrink@tntware.com) }
10 {*****************************************************************************}
14 {$INCLUDE TntCompilers.inc}
19 Windows
, Classes
, Menus
, Graphics
, Messages
;
23 TTntMenuItem
= class(TMenuItem
{TNT-ALLOW TMenuItem})
25 FIgnoreMenuChanged
: Boolean;
29 function GetCaption
: WideString
;
30 procedure SetInheritedCaption(const Value
: AnsiString
);
31 procedure SetCaption(const Value
: WideString
);
32 function IsCaptionStored
: Boolean;
33 procedure UpdateMenuString(ParentMenu
: TMenu
);
34 function GetAlignmentDrawStyle
: Word;
35 function MeasureItemTextWidth(ACanvas
: TCanvas
; const Text: WideString
): Integer;
36 function GetHint
: WideString
;
37 procedure SetInheritedHint(const Value
: AnsiString
);
38 procedure SetHint(const Value
: WideString
);
39 function IsHintStored
: Boolean;
41 procedure DefineProperties(Filer
: TFiler
); override;
42 function GetActionLinkClass
: TMenuActionLinkClass
; override;
43 procedure ActionChange(Sender
: TObject
; CheckDefaults
: Boolean); override;
44 procedure MenuChanged(Rebuild
: Boolean); override;
45 procedure AdvancedDrawItem(ACanvas
: TCanvas
; ARect
: TRect
;
46 State
: TOwnerDrawState
; TopLevel
: Boolean); override;
47 procedure DoDrawText(ACanvas
: TCanvas
; const ACaption
: WideString
;
48 var Rect
: TRect
; Selected
: Boolean; Flags
: Integer);
49 procedure MeasureItem(ACanvas
: TCanvas
; var Width
, Height
: Integer); override;
51 procedure InitiateAction
; override;
52 procedure Loaded
; override;
53 function Find(ACaption
: WideString
): TMenuItem
{TNT-ALLOW TMenuItem};
55 property Caption
: WideString read GetCaption write SetCaption stored IsCaptionStored
;
56 property Hint
: WideString read GetHint write SetHint stored IsHintStored
;
60 TTntMainMenu
= class(TMainMenu
{TNT-ALLOW TMainMenu})
62 procedure DoChange(Source
: TMenuItem
{TNT-ALLOW TMenuItem}; Rebuild
: Boolean); override;
64 {$IFDEF COMPILER_9_UP}
65 function CreateMenuItem
: TMenuItem
{TNT-ALLOW TMenuItem}; override;
70 TTntPopupMenu
= class(TPopupMenu
{TNT-ALLOW TPopupMenu})
72 procedure DoChange(Source
: TMenuItem
{TNT-ALLOW TMenuItem}; Rebuild
: Boolean); override;
74 constructor Create(AOwner
: TComponent
); override;
75 {$IFDEF COMPILER_9_UP}
76 function CreateMenuItem
: TMenuItem
{TNT-ALLOW TMenuItem}; override;
78 destructor Destroy
; override;
79 procedure Popup(X
, Y
: Integer); override;
83 function WideNewSubMenu(const ACaption
: WideString
; hCtx
: THelpContext
;
84 const AName
: TComponentName
; const Items
: array of TTntMenuItem
;
85 AEnabled
: Boolean): TTntMenuItem
;
87 function WideNewItem(const ACaption
: WideString
; AShortCut
: TShortCut
;
88 AChecked
, AEnabled
: Boolean; AOnClick
: TNotifyEvent
; hCtx
: THelpContext
;
89 const AName
: TComponentName
): TTntMenuItem
;
91 function MessageToShortCut(Msg
: TWMKeyDown
): TShortCut
;
93 {TNT-WARN ShortCutToText}
94 function WideShortCutToText(WordShortCut
: Word): WideString
;
95 {TNT-WARN TextToShortCut}
96 function WideTextToShortCut(Text: WideString
): TShortCut
;
98 function WideGetHotkey(const Text: WideString
): WideString
;
99 {TNT-WARN StripHotkey}
100 function WideStripHotkey(const Text: WideString
): WideString
;
101 {TNT-WARN AnsiSameCaption}
102 function WideSameCaption(const Text1
, Text2
: WideString
): Boolean;
104 function WideGetMenuItemCaption(MenuItem
: TMenuItem
{TNT-ALLOW TMenuItem}): WideString
;
105 function WideGetMenuItemHint(MenuItem
: TMenuItem
{TNT-ALLOW TMenuItem}): WideString
;
107 procedure NoOwnerDrawTopLevelItems(Menu
: TMainMenu
{TNT-ALLOW TMainMenu});
109 procedure FixMenuBiDiProblem(Menu
: TMenu
);
111 function MenuItemHasBitmap(MenuItem
: TMenuItem
{TNT-ALLOW TMenuItem}): Boolean;
114 TTntPopupList
= class(TPopupList
)
116 SavedPopupList
: TPopupList
;
118 procedure WndProc(var Message: TMessage
); override;
122 TntPopupList
: TTntPopupList
;
127 Forms
, SysUtils
, Consts
, ActnList
, ImgList
, TntControls
, TntGraphics
,
128 TntActnList
, TntClasses
, TntForms
, TntSysUtils
, TntWindows
;
130 function WideNewSubMenu(const ACaption
: WideString
; hCtx
: THelpContext
;
131 const AName
: TComponentName
; const Items
: array of TTntMenuItem
;
132 AEnabled
: Boolean): TTntMenuItem
;
136 Result
:= TTntMenuItem
.Create(nil);
137 for I
:= Low(Items
) to High(Items
) do
138 Result
.Add(Items
[I
]);
139 Result
.Caption
:= ACaption
;
140 Result
.HelpContext
:= hCtx
;
141 Result
.Name
:= AName
;
142 Result
.Enabled
:= AEnabled
;
145 function WideNewItem(const ACaption
: WideString
; AShortCut
: TShortCut
;
146 AChecked
, AEnabled
: Boolean; AOnClick
: TNotifyEvent
; hCtx
: THelpContext
;
147 const AName
: TComponentName
): TTntMenuItem
;
149 Result
:= TTntMenuItem
.Create(nil);
153 ShortCut
:= AShortCut
;
162 function MessageToShortCut(Msg
: TWMKeyDown
): TShortCut
;
164 ShiftState
: TShiftState
;
166 ShiftState
:= Forms
.KeyDataToShiftState(TWMKeyDown(Msg
).KeyData
);
167 Result
:= Menus
.ShortCut(TWMKeyDown(Msg
).CharCode
, ShiftState
);
170 function WideGetSpecialName(WordShortCut
: Word): WideString
;
173 KeyName
: array[0..255] of WideChar
;
175 Assert(Win32PlatformIsUnicode
);
177 ScanCode
:= MapVirtualKeyW(WordRec(WordShortCut
).Lo
, 0) shl 16;
178 if ScanCode
<> 0 then
180 GetKeyNameTextW(ScanCode
, KeyName
, SizeOf(KeyName
));
185 function WideGetKeyboardChar(Key
: Word): WideChar
;
187 LatinNumChar
: WideChar
;
189 Assert(Win32PlatformIsUnicode
);
190 Result
:= WideChar(MapVirtualKeyW(Key
, 2));
191 if (Key
in [$30..$39]) then
193 // Check to see if "0" - "9" can be used if all that differs is shift state
194 LatinNumChar
:= WideChar(Key
- $30 + Ord('0'));
195 if (Result
<> LatinNumChar
)
196 and (Byte(Key
) = WordRec(VkKeyScanW(LatinNumChar
)).Lo
) then // .Hi would be the shift state
197 Result
:= LatinNumChar
;
201 function WideShortCutToText(WordShortCut
: Word): WideString
;
205 if (not Win32PlatformIsUnicode
)
206 or (WordRec(WordShortCut
).Lo
in [$08..$09 {BKSP, TAB}, $0D {ENTER}, $1B {ESC}, $20..$28 {Misc Nav},
207 $2D..$2E {INS, DEL}, $70..$87 {F1 - F24}])
209 Result
:= ShortCutToText
{TNT-ALLOW ShortCutToText}(WordShortCut
)
211 case WordRec(WordShortCut
).Lo
of
212 $30..$39: Name
:= WideGetKeyboardChar(WordRec(WordShortCut
).Lo
); {1-9,0}
213 $41..$5A: Name
:= WideGetKeyboardChar(WordRec(WordShortCut
).Lo
); {A-Z}
214 $60..$69: Name
:= WideGetKeyboardChar(WordRec(WordShortCut
).Lo
); {numpad 1-9,0}
216 Name
:= WideGetSpecialName(WordShortCut
);
221 if WordShortCut
and scShift
<> 0 then Result
:= Result
+ SmkcShift
;
222 if WordShortCut
and scCtrl
<> 0 then Result
:= Result
+ SmkcCtrl
;
223 if WordShortCut
and scAlt
<> 0 then Result
:= Result
+ SmkcAlt
;
224 Result
:= Result
+ Name
;
230 { This function is *very* slow. Use sparingly. Return 0 if no VK code was
233 function WideTextToShortCut(Text: WideString
): TShortCut
;
235 { If the front of Text is equal to Front then remove the matching piece
236 from Text and return True, otherwise return False }
238 function CompareFront(var Text: WideString
; const Front
: WideString
): Boolean;
240 Result
:= (Pos(Front
, Text) = 1);
242 Delete(Text, 1, Length(Front
));
253 if CompareFront(Text, SmkcShift
) then Shift
:= Shift
or scShift
254 else if CompareFront(Text, '^') then Shift
:= Shift
or scCtrl
255 else if CompareFront(Text, SmkcCtrl
) then Shift
:= Shift
or scCtrl
256 else if CompareFront(Text, SmkcAlt
) then Shift
:= Shift
or scAlt
259 if Text = '' then Exit
;
260 for Key
:= $08 to $255 do { Copy range from table in ShortCutToText }
261 if WideSameText(Text, WideShortCutToText(Key
)) then
263 Result
:= Key
or Shift
;
268 function WideGetHotkeyPos(const Text: WideString
): Integer;
277 if (Text[I
] = cHotkeyPrefix
) and (L
- I
>= 1) then
280 if Text[I
] <> cHotkeyPrefix
then
281 Result
:= I
; // this might not be the last
287 function WideGetHotkey(const Text: WideString
): WideString
;
291 I
:= WideGetHotkeyPos(Text);
298 function WideStripHotkey(const Text: WideString
): WideString
;
304 while I
<= Length(Result
) do
306 if Result
[I
] = cHotkeyPrefix
then
308 and ((I
> 1) and (Length(Result
) - I
>= 2)
309 and (Result
[I
- 1] = '(') and (Result
[I
+ 2] = ')')) then begin
310 Delete(Result
, I
- 1, 4);
313 Delete(Result
, I
, 1);
318 function WideSameCaption(const Text1
, Text2
: WideString
): Boolean;
320 Result
:= WideSameText(WideStripHotkey(Text1
), WideStripHotkey(Text2
));
323 function WideSameCaptionStr(const Text1
, Text2
: WideString
): Boolean;
325 Result
:= WideSameStr(WideStripHotkey(Text1
), WideStripHotkey(Text2
));
328 function WideGetMenuItemCaption(MenuItem
: TMenuItem
{TNT-ALLOW TMenuItem}): WideString
;
330 if MenuItem
is TTntMenuItem
then
331 Result
:= TTntMenuItem(MenuItem
).Caption
333 Result
:= MenuItem
.Caption
;
336 function WideGetMenuItemHint(MenuItem
: TMenuItem
{TNT-ALLOW TMenuItem}): WideString
;
338 if MenuItem
is TTntMenuItem
then
339 Result
:= TTntMenuItem(MenuItem
).Hint
341 Result
:= MenuItem
.Hint
;
344 procedure NoOwnerDrawTopLevelItems(Menu
: TMainMenu
{TNT-ALLOW TMainMenu});
345 {If top-level items are created as owner-drawn, they will not appear as raised
346 buttons when the mouse hovers over them. The VCL will often create top-level
347 items as owner-drawn even when they don't need to be (owner-drawn state can be
348 set on an item-by-item basis). This routine turns off the owner-drawn flag for
349 top-level items if it appears unnecessary}
351 function ItemHasValidImage(Item
: TMenuItem
{TNT-ALLOW TMenuItem}): boolean;
353 Images
: TCustomImageList
;
355 Assert(Item
<> nil, 'TNT Internal Error: ItemHasValidImage: item = nil');
356 Images
:= Item
.GetImageList
;
357 Result
:= (Assigned(Images
) and (Item
.ImageIndex
>= 0) and (Item
.ImageIndex
< Images
.Count
))
358 or (MenuItemHasBitmap(Item
) and (not Item
.Bitmap
.Empty
))
364 Info
: TMenuItemInfoA
;
365 Item
: TMenuItem
{TNT-ALLOW TMenuItem};
368 if Assigned(Menu
) then begin
369 Win98Plus
:= (Win32MajorVersion
> 4)
370 or((Win32MajorVersion
= 4) and (Win32MinorVersion
> 0));
371 if not Win98Plus
then
372 Exit
; {exit if Windows 95 or NT 4.0}
374 Info
.cbSize
:= sizeof(Info
);
375 for i
:= 0 to GetMenuItemCount(HM
) - 1 do begin
376 Info
.fMask
:= MIIM_FTYPE
or MIIM_ID
;
377 if not GetMenuItemInfo(HM
, i
, true, Info
) then
379 if Info
.fType
and MFT_OWNERDRAW
<> 0 then begin
380 Item
:= Menu
.FindItem(Info
.wID
, fkCommand
);
381 if not Assigned(Item
) then
383 if Assigned(Item
.OnDrawItem
)
384 or Assigned(Item
.OnAdvancedDrawItem
)
385 or ItemHasValidImage(Item
) then
387 Info
.fMask
:= MIIM_FTYPE
or MIIM_STRING
;
388 Info
.fType
:= (Info
.fType
and not MFT_OWNERDRAW
) or MFT_STRING
;
389 if Win32PlatformIsUnicode
and (Item
is TTntMenuItem
) then begin
391 TMenuItemInfoW(Info
).dwTypeData
:= PWideChar(TTntMenuItem(Item
).Caption
);
392 SetMenuItemInfoW(HM
, i
, true, TMenuItemInfoW(Info
));
395 Info
.dwTypeData
:= PAnsiChar(Item
.Caption
);
396 SetMenuItemInfoA(HM
, i
, true, Info
);
403 { TTntMenuItem's utility procs }
405 procedure SyncHotKeyPosition(const Source
: WideString
; var Dest
: WideString
);
408 FarEastHotString
: WideString
;
410 if (AnsiString(Source
) <> AnsiString(Dest
))
411 and WideSameCaptionStr(AnsiString(Source
), AnsiString(Dest
)) then begin
412 // when reduced to ansi, the only difference is hot key positions
413 Dest
:= WideStripHotkey(Dest
);
415 while I
<= Length(Source
) do
417 if Source
[I
] = cHotkeyPrefix
then begin
419 and ((I
> 1) and (Length(Source
) - I
>= 2)
420 and (Source
[I
- 1] = '(') and (Source
[I
+ 2] = ')')) then begin
421 FarEastHotString
:= Copy(Source
, I
- 1, 4);
423 Insert(FarEastHotString
, Dest
, I
);
426 Insert(cHotkeyPrefix
, Dest
, I
);
433 if AnsiString(Source
) <> AnsiString(Dest
) then
434 raise ETntInternalError
.CreateFmt('Internal Error: SyncHotKeyPosition Failed ("%s" <> "%s").',
435 [AnsiString(Source
), AnsiString(Dest
)]);
439 procedure UpdateMenuItems(Items
: TMenuItem
{TNT-ALLOW TMenuItem}; ParentMenu
: TMenu
);
443 if (Items
.ComponentState
* [csReading
, csDestroying
] = []) then begin
444 for i
:= Items
.Count
- 1 downto 0 do
445 UpdateMenuItems(Items
[i
], ParentMenu
);
446 if Items
is TTntMenuItem
then
447 TTntMenuItem(Items
).UpdateMenuString(ParentMenu
);
451 procedure FixMenuBiDiProblem(Menu
: TMenu
);
455 // TMenu sometimes sets bidi on first visible item which can convert caption to ansi
456 if (SysLocale
.MiddleEast
)
458 and (Menu
.Items
.Count
> 0) then
460 for i
:= 0 to Menu
.Items
.Count
- 1 do begin
461 if Menu
.Items
[i
].Visible
then begin
462 if (Menu
.Items
[i
] is TTntMenuItem
) then
463 (Menu
.Items
[i
] as TTntMenuItem
).UpdateMenuString(Menu
);
464 break
; // found first visible menu item!
471 {$IFDEF COMPILER_6} // verified against VCL source in Delphi 6 and BCB 6
473 THackMenuItem
= class(TComponent
)
475 FxxxxCaption
: Ansistring
;
477 FxxxxChecked
: Boolean;
478 FxxxxEnabled
: Boolean;
479 FxxxxDefault
: Boolean;
480 FxxxxAutoHotkeys
: TMenuItemAutoFlag
;
481 FxxxxAutoLineReduction
: TMenuItemAutoFlag
;
482 FxxxxRadioItem
: Boolean;
483 FxxxxVisible
: Boolean;
484 FxxxxGroupIndex
: Byte;
485 FxxxxImageIndex
: TImageIndex
;
486 FxxxxActionLink
: TMenuActionLink
{TNT-ALLOW TMenuActionLink};
487 FxxxxBreak
: TMenuBreak
;
490 FxxxxHelpContext
: THelpContext
;
491 FxxxxHint
: AnsiString
;
493 FxxxxShortCut
: TShortCut
;
494 FxxxxParent
: TMenuItem
{TNT-ALLOW TMenuItem};
495 FMerged
: TMenuItem
{TNT-ALLOW TMenuItem};
496 FMergedWith
: TMenuItem
{TNT-ALLOW TMenuItem};
499 {$IFDEF DELPHI_7} // verified against VCL source in Delphi 7
501 THackMenuItem
= class(TComponent
)
503 FxxxxCaption
: AnsiString
;
505 FxxxxChecked
: Boolean;
506 FxxxxEnabled
: Boolean;
507 FxxxxDefault
: Boolean;
508 FxxxxAutoHotkeys
: TMenuItemAutoFlag
;
509 FxxxxAutoLineReduction
: TMenuItemAutoFlag
;
510 FxxxxRadioItem
: Boolean;
511 FxxxxVisible
: Boolean;
512 FxxxxGroupIndex
: Byte;
513 FxxxxImageIndex
: TImageIndex
;
514 FxxxxActionLink
: TMenuActionLink
{TNT-ALLOW TMenuActionLink};
515 FxxxxBreak
: TMenuBreak
;
518 FxxxxHelpContext
: THelpContext
;
519 FxxxxHint
: AnsiString
;
521 FxxxxShortCut
: TShortCut
;
522 FxxxxParent
: TMenuItem
{TNT-ALLOW TMenuItem};
523 FMerged
: TMenuItem
{TNT-ALLOW TMenuItem};
524 FMergedWith
: TMenuItem
{TNT-ALLOW TMenuItem};
527 {$IFDEF DELPHI_9} // verified against VCL source in Delphi 9
529 THackMenuItem
= class(TComponent
)
531 FxxxxCaption
: AnsiString
;
533 FxxxxChecked
: Boolean;
534 FxxxxEnabled
: Boolean;
535 FxxxxDefault
: Boolean;
536 FxxxxAutoHotkeys
: TMenuItemAutoFlag
;
537 FxxxxAutoLineReduction
: TMenuItemAutoFlag
;
538 FxxxxRadioItem
: Boolean;
539 FxxxxVisible
: Boolean;
540 FxxxxGroupIndex
: Byte;
541 FxxxxImageIndex
: TImageIndex
;
542 FxxxxActionLink
: TMenuActionLink
{TNT-ALLOW TMenuActionLink};
543 FxxxxBreak
: TMenuBreak
;
546 FxxxxHelpContext
: THelpContext
;
547 FxxxxHint
: AnsiString
;
549 FxxxxShortCut
: TShortCut
;
550 FxxxxParent
: TMenuItem
{TNT-ALLOW TMenuItem};
551 FMerged
: TMenuItem
{TNT-ALLOW TMenuItem};
552 FMergedWith
: TMenuItem
{TNT-ALLOW TMenuItem};
555 {$IFDEF DELPHI_10} // verified against VCL source in Delphi 10
557 THackMenuItem
= class(TComponent
)
559 FxxxxCaption
: AnsiString
;
561 FxxxxChecked
: Boolean;
562 FxxxxEnabled
: Boolean;
563 FxxxxDefault
: Boolean;
564 FxxxxAutoHotkeys
: TMenuItemAutoFlag
;
565 FxxxxAutoLineReduction
: TMenuItemAutoFlag
;
566 FxxxxRadioItem
: Boolean;
567 FxxxxVisible
: Boolean;
568 FxxxxGroupIndex
: Byte;
569 FxxxxImageIndex
: TImageIndex
;
570 FxxxxActionLink
: TMenuActionLink
{TNT-ALLOW TMenuActionLink};
571 FxxxxBreak
: TMenuBreak
;
574 FxxxxHelpContext
: THelpContext
;
575 FxxxxHint
: AnsiString
;
577 FxxxxShortCut
: TShortCut
;
578 FxxxxParent
: TMenuItem
{TNT-ALLOW TMenuItem};
579 FMerged
: TMenuItem
{TNT-ALLOW TMenuItem};
580 FMergedWith
: TMenuItem
{TNT-ALLOW TMenuItem};
584 function MenuItemHasBitmap(MenuItem
: TMenuItem
{TNT-ALLOW TMenuItem}): Boolean;
586 Result
:= Assigned(THackMenuItem(MenuItem
).FBitmap
);
591 procedure TTntMenuItem
.DefineProperties(Filer
: TFiler
);
594 TntPersistent_AfterInherited_DefineProperties(Filer
, Self
);
597 type TAccessActionlink
= class(TActionLink
);
599 procedure TTntMenuItem
.InitiateAction
;
601 if GetKeyboardLayout(0) <> FKeyboardLayout
then
606 function TTntMenuItem
.IsCaptionStored
: Boolean;
608 Result
:= (ActionLink
= nil) or (not TAccessActionlink(ActionLink
).IsCaptionLinked
);
611 procedure TTntMenuItem
.SetInheritedCaption(const Value
: AnsiString
);
613 inherited Caption
:= Value
;
616 function TTntMenuItem
.GetCaption
: WideString
;
618 if (AnsiString(FCaption
) <> inherited Caption
)
619 and WideSameCaptionStr(AnsiString(FCaption
), inherited Caption
) then
621 // only difference is hotkey position, update caption with new hotkey position
622 SyncHotKeyPosition(inherited Caption
, FCaption
);
624 Result
:= GetSyncedWideString(FCaption
, (inherited Caption
));
627 procedure TTntMenuItem
.SetCaption(const Value
: WideString
);
629 GetCaption
; // auto adjust for hot key changes
630 SetSyncedWideString(Value
, FCaption
, (inherited Caption
), SetInheritedCaption
);
633 function TTntMenuItem
.GetHint
: WideString
;
635 Result
:= GetSyncedWideString(FHint
, inherited Hint
);
638 procedure TTntMenuItem
.SetInheritedHint(const Value
: AnsiString
);
640 inherited Hint
:= Value
;
643 procedure TTntMenuItem
.SetHint(const Value
: WideString
);
645 SetSyncedWideString(Value
, FHint
, inherited Hint
, SetInheritedHint
);
648 function TTntMenuItem
.IsHintStored
: Boolean;
650 Result
:= (ActionLink
= nil) or not TAccessActionlink(ActionLink
).IsHintLinked
;
653 procedure TTntMenuItem
.Loaded
;
656 UpdateMenuString(GetParentMenu
);
659 procedure TTntMenuItem
.MenuChanged(Rebuild
: Boolean);
661 if (not FIgnoreMenuChanged
) then begin
663 UpdateMenuItems(Self
, GetParentMenu
);
664 FixMenuBiDiProblem(GetParentMenu
);
668 procedure TTntMenuItem
.UpdateMenuString(ParentMenu
: TMenu
);
670 ParentHandle
: THandle
;
672 function NativeMenuTypeIsString
: Boolean;
674 MenuItemInfo
: TMenuItemInfoW
;
675 Buffer
: array[0..79] of WideChar
;
677 MenuItemInfo
.cbSize
:= 44; // Required for Windows NT 4.0
678 MenuItemInfo
.fMask
:= MIIM_TYPE
;
679 MenuItemInfo
.dwTypeData
:= Buffer
; // ??
680 MenuItemInfo
.cch
:= Length(Buffer
); // ??
681 Result
:= GetMenuItemInfoW(ParentHandle
, Command
, False, MenuItemInfo
)
682 and ((MenuItemInfo
.fType
and (MFT_BITMAP
or MFT_SEPARATOR
or MFT_OWNERDRAW
)) = 0)
685 function NativeMenuString
: WideString
;
689 Assert(Win32PlatformIsUnicode
);
690 Len
:= GetMenuStringW(ParentHandle
, Command
, nil, 0, MF_BYCOMMAND
);
694 SetLength(Result
, Len
+ 1);
695 Len
:= GetMenuStringW(ParentHandle
, Command
, PWideChar(Result
), Len
+ 1, MF_BYCOMMAND
);
696 SetLength(Result
, Len
);
700 procedure SetMenuString(const Value
: WideString
);
702 MenuItemInfo
: TMenuItemInfoW
;
703 Buffer
: array[0..79] of WideChar
;
705 MenuItemInfo
.cbSize
:= 44; // Required for Windows NT 4.0
706 MenuItemInfo
.fMask
:= MIIM_TYPE
;
707 MenuItemInfo
.dwTypeData
:= Buffer
; // ??
708 MenuItemInfo
.cch
:= Length(Buffer
); // ??
709 if GetMenuItemInfoW(ParentHandle
, Command
, False, MenuItemInfo
)
710 and ((MenuItemInfo
.fType
and (MFT_BITMAP
or MFT_SEPARATOR
or MFT_OWNERDRAW
)) = 0) then
712 MenuItemInfo
.dwTypeData
:= PWideChar(Value
);
713 MenuItemInfo
.cch
:= Length(Value
);
714 Win32Check(SetMenuItemInfoW(ParentHandle
, Command
, False, MenuItemInfo
));
718 function SameEvent(A
, B
: TMenuMeasureItemEvent
): Boolean;
724 MenuCaption
: WideString
;
726 FKeyboardLayout
:= GetKeyboardLayout(0);
729 else if (THackMenuItem(Self
.Parent
).FMergedWith
<> nil) then
730 ParentHandle
:= THackMenuItem(Self
.Parent
).FMergedWith
.Handle
732 ParentHandle
:= Parent
.Handle
;
734 if (Win32PlatformIsUnicode
)
735 and (Parent
<> nil) and (ParentMenu
<> nil)
736 and (ComponentState
* [csReading
, csDestroying
] = [])
738 and (NativeMenuTypeIsString
) then begin
739 MenuCaption
:= Caption
;
741 and ((ShortCut
<> scNone
)
742 and ((Parent
= nil) or (Parent
.Parent
<> nil) or not (Parent
.Owner
is TMainMenu
{TNT-ALLOW TMainMenu}))) then
743 MenuCaption
:= MenuCaption
+ #9 + WideShortCutToText(ShortCut
);
744 if (NativeMenuString
<> MenuCaption
) then
746 SetMenuString(MenuCaption
);
747 if ((Parent
= ParentMenu
.Items
) or (THackMenuItem(Self
.Parent
).FMergedWith
<> nil))
748 and (ParentMenu
is TMainMenu
{TNT-ALLOW TMainMenu})
749 and (ParentMenu
.WindowHandle
<> 0) then
750 DrawMenuBar(ParentMenu
.WindowHandle
) {top level menu bar items}
755 function TTntMenuItem
.GetAlignmentDrawStyle
: Word;
757 Alignments
: array[TPopupAlignment
] of Word = (DT_LEFT
, DT_RIGHT
, DT_CENTER
);
760 Alignment
: TPopupAlignment
;
762 ParentMenu
:= GetParentMenu
;
763 if ParentMenu
is TMenu
then
765 else if ParentMenu
is TPopupMenu
{TNT-ALLOW TPopupMenu} then
766 Alignment
:= TPopupMenu
{TNT-ALLOW TPopupMenu}(ParentMenu
).Alignment
769 Result
:= Alignments
[Alignment
];
772 procedure TTntMenuItem
.AdvancedDrawItem(ACanvas
: TCanvas
; ARect
: TRect
;
773 State
: TOwnerDrawState
; TopLevel
: Boolean);
775 procedure DrawMenuText(BiDi
: Boolean);
777 ImageList
: TCustomImageList
;
778 DrawImage
, DrawGlyph
: Boolean;
779 GlyphRect
, SaveRect
: TRect
;
785 ImageList
:= GetImageList
;
786 Selected
:= odSelected
in State
;
787 Win98Plus
:= (Win32MajorVersion
> 4) or
788 ((Win32MajorVersion
= 4) and (Win32MinorVersion
> 0));
789 Win2K
:= (Win32MajorVersion
> 4) and (Win32Platform
= VER_PLATFORM_WIN32_NT
);
792 GlyphRect
.Left
:= ARect
.Left
+ 1;
793 DrawImage
:= (ImageList
<> nil) and ((ImageIndex
> -1) and
794 (ImageIndex
< ImageList
.Count
) or Checked
and ((not MenuItemHasBitmap(Self
)) or
796 if DrawImage
or MenuItemHasBitmap(Self
) and not Bitmap
.Empty
then
800 GlyphRect
.Right
:= GlyphRect
.Left
+ ImageList
.Width
802 { Need to add BitmapWidth/Height properties for TMenuItem if we're to
803 support them. Right now let's hardcode them to 16x16. }
804 GlyphRect
.Right
:= GlyphRect
.Left
+ 16;
806 { Draw background pattern brush if selected }
809 Inc(GlyphRect
.Right
);
811 Brush
.Bitmap
:= AllocPatternBitmap(clBtnFace
, clBtnHighlight
);
815 Dec(GlyphRect
.Right
);
817 if (ImageList
<> nil) and (not TopLevel
) then
818 GlyphRect
.Right
:= GlyphRect
.Left
+ ImageList
.Width
820 GlyphRect
.Right
:= GlyphRect
.Left
;
824 SaveRect
:= GlyphRect
;
825 GlyphRect
.Left
:= ARect
.Right
- (SaveRect
.Right
- ARect
.Left
);
826 GlyphRect
.Right
:= ARect
.Right
- (SaveRect
.Left
- ARect
.Left
);
828 with GlyphRect
do begin
832 if Selected
then begin
833 if DrawGlyph
then begin
835 ARect
.Right
:= GlyphRect
.Left
- 1
837 ARect
.Left
:= GlyphRect
.Right
+ 1;
839 if not (Win98Plus
and TopLevel
) then
840 Brush
.Color
:= clHighlight
;
842 if TopLevel
and Win98Plus
and (not Selected
)
843 {$IFDEF COMPILER_7_UP}
844 and (not Win32PlatformIsXP
)
847 OffsetRect(ARect
, 0, -1);
848 if not (Selected
and DrawGlyph
) then begin
850 ARect
.Right
:= GlyphRect
.Left
- 1
852 ARect
.Left
:= GlyphRect
.Right
+ 1;
856 DrawStyle
:= DT_EXPANDTABS
or DT_SINGLELINE
or GetAlignmentDrawStyle
;
857 if Win2K
and (odNoAccel
in State
) then
858 DrawStyle
:= DrawStyle
or DT_HIDEPREFIX
;
859 { Calculate vertical layout }
861 if odDefault
in State
then
862 Font
.Style
:= [fsBold
];
863 DoDrawText(ACanvas
, Caption
, ARect
, Selected
, DrawStyle
or DT_CALCRECT
or DT_NOCLIP
);
865 { the DT_CALCRECT does not take into account alignment }
866 ARect
.Left
:= SaveRect
.Left
;
867 ARect
.Right
:= SaveRect
.Right
;
869 OffsetRect(ARect
, 0, ((SaveRect
.Bottom
- SaveRect
.Top
) - (ARect
.Bottom
- ARect
.Top
)) div 2);
870 if TopLevel
and Selected
and Win98Plus
871 {$IFDEF COMPILER_7_UP}
872 and (not Win32PlatformIsXP
)
875 OffsetRect(ARect
, 1, 0);
876 DoDrawText(ACanvas
, Caption
, ARect
, Selected
, DrawStyle
);
877 if (ShortCut
<> scNone
) and not TopLevel
then
881 ARect
.Right
:= ARect
.Left
+ WideCanvasTextWidth(ACanvas
, WideShortCutToText(ShortCut
));
883 ARect
.Left
:= ARect
.Right
;
884 ARect
.Right
:= SaveRect
.Right
- 10;
886 DoDrawText(ACanvas
, WideShortCutToText(ShortCut
), ARect
, Selected
, DT_RIGHT
);
893 SaveCaption
: WideString
;
894 SaveShortCut
: TShortCut
;
896 ParentMenu
:= GetParentMenu
;
897 if (not Win32PlatformIsUnicode
)
899 or ( (ParentMenu
<> nil) and (ParentMenu
.OwnerDraw
or (GetImageList
<> nil))
900 and (Assigned(OnAdvancedDrawItem
) or Assigned(OnDrawItem
)) ) then
903 SaveCaption
:= Caption
;
904 SaveShortCut
:= ShortCut
;
906 FIgnoreMenuChanged
:= True;
911 FIgnoreMenuChanged
:= False;
915 FIgnoreMenuChanged
:= True;
917 Caption
:= SaveCaption
;
918 ShortCut
:= SaveShortcut
;
920 FIgnoreMenuChanged
:= False;
923 DrawMenuText((ParentMenu
<> nil) and (ParentMenu
.IsRightToLeft
))
927 procedure TTntMenuItem
.DoDrawText(ACanvas
: TCanvas
; const ACaption
: WideString
;
928 var Rect
: TRect
; Selected
: Boolean; Flags
: Longint);
933 if (not Win32PlatformIsUnicode
)
935 inherited DoDrawText(ACanvas
, ACaption
, Rect
, Selected
, Flags
)
937 ParentMenu
:= GetParentMenu
;
938 if (ParentMenu
<> nil) and (ParentMenu
.IsRightToLeft
) then
940 if Flags
and DT_LEFT
= DT_LEFT
then
941 Flags
:= Flags
and (not DT_LEFT
) or DT_RIGHT
942 else if Flags
and DT_RIGHT
= DT_RIGHT
then
943 Flags
:= Flags
and (not DT_RIGHT
) or DT_LEFT
;
944 Flags
:= Flags
or DT_RTLREADING
;
947 if (Flags
and DT_CALCRECT
<> 0) and ((Text = '') or
948 (Text[1] = cHotkeyPrefix
) and (Text[2] = #0)) then Text := Text + ' ';
951 Brush
.Style
:= bsClear
;
953 Font
.Style
:= Font
.Style
+ [fsBold
];
958 OffsetRect(Rect
, 1, 1);
959 Font
.Color
:= clBtnHighlight
;
960 Tnt_DrawTextW(Handle
, PWideChar(Text), Length(Text), Rect
, Flags
);
961 OffsetRect(Rect
, -1, -1);
963 if Selected
and (ColorToRGB(clHighlight
) = ColorToRGB(clBtnShadow
)) then
964 Font
.Color
:= clBtnHighlight
else
965 Font
.Color
:= clBtnShadow
;
967 Tnt_DrawTextW(Handle
, PWideChar(Text), Length(Text), Rect
, Flags
);
972 function TTntMenuItem
.MeasureItemTextWidth(ACanvas
: TCanvas
; const Text: WideString
): Integer;
976 FillChar(R
, SizeOf(R
), 0);
977 DoDrawText(ACanvas
, Text, R
, False,
978 GetAlignmentDrawStyle
or DT_EXPANDTABS
or DT_SINGLELINE
or DT_NOCLIP
or DT_CALCRECT
);
979 Result
:= R
.Right
- R
.Left
;
982 procedure TTntMenuItem
.MeasureItem(ACanvas
: TCanvas
; var Width
, Height
: Integer);
984 SaveMeasureItemEvent
: TMenuMeasureItemEvent
;
986 if (not Win32PlatformIsUnicode
)
987 or (Self
.IsLine
) then
990 SaveMeasureItemEvent
:= inherited OnMeasureItem
;
992 inherited OnMeasureItem
:= nil;
994 Inc(Width
, MeasureItemTextWidth(ACanvas
, Caption
));
995 Dec(Width
, MeasureItemTextWidth(ACanvas
, inherited Caption
));
996 if ShortCut
<> scNone
then begin
997 Inc(Width
, MeasureItemTextWidth(ACanvas
, WideShortCutToText(ShortCut
)));
998 Dec(Width
, MeasureItemTextWidth(ACanvas
, ShortCutToText
{TNT-ALLOW ShortCutToText}(ShortCut
)));
1001 inherited OnMeasureItem
:= SaveMeasureItemEvent
;
1003 if Assigned(OnMeasureItem
) then OnMeasureItem(Self
, ACanvas
, Width
, Height
);
1007 function TTntMenuItem
.Find(ACaption
: WideString
): TMenuItem
{TNT-ALLOW TMenuItem};
1012 ACaption
:= WideStripHotkey(ACaption
);
1013 for I
:= 0 to Count
- 1 do
1014 if WideSameText(ACaption
, WideStripHotkey(WideGetMenuItemCaption(Items
[I
]))) then
1021 function TTntMenuItem
.GetActionLinkClass
: TMenuActionLinkClass
;
1023 Result
:= TTntMenuActionLink
;
1026 procedure TTntMenuItem
.ActionChange(Sender
: TObject
; CheckDefaults
: Boolean);
1028 if (Sender
is TCustomAction
{TNT-ALLOW TCustomAction}) and Supports(Sender
, ITntAction
) then begin
1029 if not CheckDefaults
or (Caption
= '') then
1030 Caption
:= TntAction_GetCaption(TCustomAction
{TNT-ALLOW TCustomAction}(Sender
));
1031 if not CheckDefaults
or (Hint
= '') then
1032 Hint
:= TntAction_GetHint(TCustomAction
{TNT-ALLOW TCustomAction}(Sender
));
1039 {$IFDEF COMPILER_9_UP}
1040 function TTntMainMenu
.CreateMenuItem
: TMenuItem
{TNT-ALLOW TMenuItem};
1042 Result
:= TTntMenuItem
.Create(Self
);
1046 procedure TTntMainMenu
.DoChange(Source
: TMenuItem
{TNT-ALLOW TMenuItem}; Rebuild
: Boolean);
1049 UpdateMenuItems(Items
, Self
);
1050 if (THackMenuItem(Items
).FMerged
<> nil) then begin
1051 UpdateMenuItems(THackMenuItem(Items
).FMerged
, Self
);
1057 constructor TTntPopupMenu
.Create(AOwner
: TComponent
);
1060 PopupList
.Remove(Self
);
1061 if TntPopupList
<> nil then
1062 TntPopupList
.Add(Self
);
1065 {$IFDEF COMPILER_9_UP}
1066 function TTntPopupMenu
.CreateMenuItem
: TMenuItem
{TNT-ALLOW TMenuItem};
1068 Result
:= TTntMenuItem
.Create(Self
);
1072 destructor TTntPopupMenu
.Destroy
;
1074 if TntPopupList
<> nil then
1075 TntPopupList
.Remove(Self
);
1076 PopupList
.Add(Self
);
1080 procedure TTntPopupMenu
.DoChange(Source
: TMenuItem
{TNT-ALLOW TMenuItem}; Rebuild
: Boolean);
1083 UpdateMenuItems(Items
, Self
);
1086 procedure TTntPopupMenu
.Popup(X
, Y
: Integer);
1088 Menus
.PopupList
:= TntPopupList
;
1092 Menus
.PopupList
:= TntPopupList
.SavedPopupList
;
1098 procedure TTntPopupList
.WndProc(var Message: TMessage
);
1101 MenuItem
: TMenuItem
{TNT-ALLOW TMenuItem};
1102 FindKind
: TFindItemKind
;
1107 Menus
.PopupList
:= SavedPopupList
;
1108 for i
:= 0 to Count
- 1 do
1109 FixMenuBiDiProblem(Items
[i
]);
1112 with TWMMenuSelect(Message) do
1114 FindKind
:= fkCommand
;
1115 if MenuFlag
and MF_POPUP
<> 0 then FindKind
:= fkHandle
;
1116 for I
:= 0 to Count
- 1 do
1118 if FindKind
= fkHandle
then
1121 Item
:= Integer(GetSubMenu(Menu
, IDItem
)) else
1126 MenuItem
:= TPopupMenu
{TNT-ALLOW TPopupMenu}(Items
[I
]).FindItem(Item
, FindKind
);
1127 if MenuItem
<> nil then
1129 TntApplication
.Hint
:= WideGetLongHint(WideGetMenuItemHint(MenuItem
));
1133 TntApplication
.Hint
:= '';
1140 TntPopupList
:= TTntPopupList
.Create
;
1141 TntPopupList
.SavedPopupList
:= Menus
.PopupList
;
1144 FreeAndNil(TntPopupList
);