/usr/src/castle-game-engine-4.1.1/window/windows/castlewindow_winapi_menu.inc is in castle-game-engine-src 4.1.1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | {
Copyright 2004-2013 Michalis Kamburelis.
This file is part of "Castle Game Engine".
"Castle Game Engine" is free software; see the file COPYING.txt,
included in this distribution, for details about the copyright.
"Castle Game Engine" is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
----------------------------------------------------------------------------
}
{ @abstract(Helper for CastleWindow implementation of menu-related things
under WinAPI.)
It converts menu structure from classes in castlewindowmenu.inc to WinAPI menu
(HMenu handle).
Let me elaborate here a little about how menu shortcuts are handled
when CastleWindow is implemented on top of WinAPI:
@orderedList(
@item(
1st thing to realize is that WinAPI is stupid and AFAIK you simply
can't explicitly say to WinAPI : this menu item's key shortcut
is xxx.
Instead you have to do two things:
@orderedList(
@item(
When creating menu item, specify it's caption as
"Real caption" + CharTab + "textual description of menu's key shortcut")
@item(
Create accelerator table (e.g. with CreateAcceleratorTable)
that specifies associations "key shortcuts" -> "command numbers to
be returned by WM_COMMAND".
Then you have to use that accelerator table in your message loop
using TranslateAccelerators. This will actually translate WM_KEYDOWN
messages to WM_COMMAND.)
)
What's stupid about this?
It's stupid because there actually need not be any connection
between what "textual description of menu's key shortcut"
you gave in 1 and what actual key shortcuts you associate
with which commands in 2.
You can easily specify none or inccorrect textual key descriptions for
some menu items. Moreover you can create "textual descriptions"
in non-standard way, e.g. some programs name Ctrl + O shortcut
as 'Ctrl+o' and some as 'Ctrl+O'.)
@item(
OK, so what I'm doing with it?
Well, in this particular case stupidity of WinAPI means that I have
less work. That's because I already implemented mechanism to
create textual key descriptions in TMenuItem.KeyString
and I already created mechanism to translate key downs to
appropriately translate key presses to menu items.
I had to do this because my CastleWindow unit must
work when implemented on top of Xlib, where I don't have
a concept of a "menu item with key shortcut" available.
So what I'm doing?
I'm doing 1.1 in this unit using my TMenuItem.KeyString, and
I'm ignoring 1.2 (i.e. I'm doing equivalent things myself in CastleWindow unit,
not using any WinAPI accelerator tables).)
)
}
{ We use ParentAllowsEnabled trick to take into account main Menu.Enabled
state. WinAPI doesn't allow the menu bar or popup to be just disabled,
so instead we disable all direct children of main Manu
if Menu.Enabled = false.
In other words, when main menu creates it's children, it passes
ParentAllowsEnabled = main Menu.Enabled.
When other menu creates it's children, it passes
ParentAllowsEnabled = @true. }
function MakeWinapiMenuCore(Menu: TMenu;
MenuBar: boolean; ParentAllowsEnabled: boolean): HMenu;
function SMnemonicsToWin(const S: string): string;
var
SPos, ResultPos: Integer;
begin
{ I'm utlizing here the fact that Result for sure will be
shorter or equal to S }
SetLength(Result, Length(S));
ResultPos := 1;
SPos := 1;
while SPos <= Length(S) do
begin
if S[SPos] = '_' then
begin
if (SPos < Length(S)) and (S[SPos + 1] = '_') then
begin
{ '__' replace with '_' }
Result[ResultPos] := '_';
Inc(ResultPos);
Inc(SPos, 2);
end else
begin
{ '_' (not starting '__') replace with '&' }
Result[ResultPos] := '&';
Inc(ResultPos);
Inc(SPos);
end;
end else
begin
Result[ResultPos] := S[SPos];
Inc(ResultPos);
Inc(SPos);
end;
end;
SetLength(Result, ResultPos - 1);
end;
function EnabledFlag(Enabled: boolean): UInt;
begin
if Enabled then
Result := MF_ENABLED else
Result := MF_GRAYED;
end;
procedure AppendGLMenu(Menu: TMenu; ParentAllowsEnabled: boolean);
begin
{ I'm casting MakeWinapiMenu result (:HMenu)
to UINT to avoid range check errors }
OSCheck( AppendMenu(Result,
MF_STRING or MF_POPUP or EnabledFlag(Menu.Enabled and ParentAllowsEnabled),
UINT(MakeWinapiMenuCore(Menu, false, true)),
PChar(SMnemonicsToWin(Menu.Caption))) );
end;
procedure AppendGLMenuItem(MenuItem: TMenuItem; ParentAllowsEnabled: boolean);
var
Flags: UInt;
KeyStr, S: string;
begin
Flags := MF_STRING or EnabledFlag(MenuItem.Enabled and ParentAllowsEnabled);
{ If I understand docs properly, MF_UNCHECKED is actually meaningless
here as I don't use any customized bitmaps for menu check marks.
But I wrote it for consistency. }
if MenuItem is TMenuItemChecked then
begin
if TMenuItemChecked(MenuItem).Checked then
Flags := Flags or MF_CHECKED else
Flags := Flags or MF_UNCHECKED;
end;
{ Don't use here MenuItem.CaptionWithKey.
Instead use #9 to delimit key names.
This way our key shortcuts will be drawn nicely. }
S := SMnemonicsToWin(MenuItem.Caption);
if MenuItem.KeyString(KeyStr) then
S := S + #9 + KeyStr;
OSCheck( AppendMenu(Result, Flags, MenuItem.SmallId, PChar(S)) );
if (MenuItem is TMenuItemRadio) and TMenuItemRadio(MenuItem).Checked then
OSCheck( CheckMenuRadioItem(Result,
MenuItem.SmallId, MenuItem.SmallId, MenuItem.SmallId, MF_BYCOMMAND) );
end;
procedure AppendGLMenuSeparator;
begin
OSCheck( AppendMenu(Result, MF_SEPARATOR, 0, nil) );
end;
var
M: TMenuEntry;
i: Integer;
begin
if MenuBar then
begin
Result := CreateMenu;
OSCheck( Result <> 0, 'CreateMenu');
end else
begin
Result := CreatePopupMenu;
OSCheck( Result <> 0, 'CreatePopupMenu');
end;
for i := 0 to Menu.Count - 1 do
begin
M := Menu[i];
if M is TMenuItem then
AppendGLMenuItem(TMenuItem(M), ParentAllowsEnabled) else
if M is TMenuSeparator then
begin
if not MenuBar then AppendGLMenuSeparator;
end else
if M is TMenu then
AppendGLMenu(TMenu(M), ParentAllowsEnabled) else
raise EInternalError.Create('Not implemented TMenuEntry subclass');
end;
end;
{ Convert Menu structure to a WinAPI menu. Resulting menu is menu bar
(obtained with CreateMenu) if MenuBar = true, else it's popup menu
(obtained with CreatePopupMenu).
Each TMenuItem will be added with identifier taken from it's SmallId.
If MenuBar then TMenuSeparator entries in Menu.Entries[] are ignored
(WinAPI toplevel menu bar cannot have a separator, that's quite sensible
actually). Of course separators at lower depths are not ignored. }
function MakeWinapiMenu(Menu: TMenu; MenuBar: boolean): HMenu;
begin
Result := MakeWinapiMenuCore(Menu, MenuBar, Menu.Enabled);
end;
|