1 // Windows/Menu.cpp
2
3 #include "StdAfx.h"
4
5 #ifndef _UNICODE
6 #include "../Common/StringConvert.h"
7 #endif
8 #include "Menu.h"
9
10 #ifndef _UNICODE
11 extern bool g_IsNT;
12 #endif
13
14 namespace NWindows {
15
16 /*
17 structures
18 MENUITEMINFOA
19 MENUITEMINFOW
20 contain additional member:
21 #if (WINVER >= 0x0500)
22 HBITMAP hbmpItem;
23 #endif
24 If we compile the source code with (WINVER >= 0x0500), some functions
25 will not work at NT4, if cbSize is set as sizeof(MENUITEMINFO).
26 So we use size of old version of structure in some conditions.
27 Win98 probably supports full structure including hbmpItem.
28
29 We have 2 ways to get/set string in menu item:
30 win95/NT4: we must use MIIM_TYPE only.
31 MIIM_TYPE : Retrieves or sets the fType and dwTypeData members.
32 win98/win2000: there are new flags that can be used instead of MIIM_TYPE:
33 MIIM_FTYPE : Retrieves or sets the fType member.
34 MIIM_STRING : Retrieves or sets the dwTypeData member.
35
36 Windows versions probably support MIIM_TYPE flag, if we set MENUITEMINFO::cbSize
37 as sizeof of old (small) MENUITEMINFO that doesn't include (hbmpItem) field.
38 But do all Windows versions support old MIIM_TYPE flag, if we use
39 MENUITEMINFO::cbSize as sizeof of new (big) MENUITEMINFO including (hbmpItem) field ?
40 win10 probably supports any combination of small/big (cbSize) and old/new MIIM_TYPE/MIIM_STRING.
41 */
42
43 #if defined(UNDER_CE) || defined(_WIN64) || (WINVER < 0x0500)
44 #ifndef _UNICODE
45 #define my_compatib_MENUITEMINFOA_size sizeof(MENUITEMINFOA)
46 #endif
47 #define my_compatib_MENUITEMINFOW_size sizeof(MENUITEMINFOW)
48 #else
49 #define MY_STRUCT_SIZE_BEFORE(structname, member) ((UINT)(UINT_PTR)((LPBYTE)(&((structname*)0)->member) - (LPBYTE)(structname*)0))
50 #ifndef _UNICODE
51 #define my_compatib_MENUITEMINFOA_size MY_STRUCT_SIZE_BEFORE(MENUITEMINFOA, hbmpItem)
52 #endif
53 #define my_compatib_MENUITEMINFOW_size MY_STRUCT_SIZE_BEFORE(MENUITEMINFOW, hbmpItem)
54 #if defined(__clang__) && __clang_major__ >= 13
55 // error : performing pointer subtraction with a null pointer may have undefined behavior
56 #pragma GCC diagnostic ignored "-Wnull-pointer-subtraction"
57 #endif
58 #endif
59
60
61 #define COPY_MENUITEM_field(d, s, name) \
62 d.name = s.name;
63
64 #define COPY_MENUITEM_fields(d, s) \
65 COPY_MENUITEM_field(d, s, fMask) \
66 COPY_MENUITEM_field(d, s, fType) \
67 COPY_MENUITEM_field(d, s, fState) \
68 COPY_MENUITEM_field(d, s, wID) \
69 COPY_MENUITEM_field(d, s, hSubMenu) \
70 COPY_MENUITEM_field(d, s, hbmpChecked) \
71 COPY_MENUITEM_field(d, s, hbmpUnchecked) \
72 COPY_MENUITEM_field(d, s, dwItemData) \
73
ConvertItemToSysForm(const CMenuItem & item,MENUITEMINFOW & si)74 static void ConvertItemToSysForm(const CMenuItem &item, MENUITEMINFOW &si)
75 {
76 ZeroMemory(&si, sizeof(si));
77 si.cbSize = my_compatib_MENUITEMINFOW_size; // sizeof(si);
78 COPY_MENUITEM_fields(si, item)
79 }
80
81 #ifndef _UNICODE
ConvertItemToSysForm(const CMenuItem & item,MENUITEMINFOA & si)82 static void ConvertItemToSysForm(const CMenuItem &item, MENUITEMINFOA &si)
83 {
84 ZeroMemory(&si, sizeof(si));
85 si.cbSize = my_compatib_MENUITEMINFOA_size; // sizeof(si);
86 COPY_MENUITEM_fields(si, item)
87 }
88 #endif
89
ConvertItemToMyForm(const MENUITEMINFOW & si,CMenuItem & item)90 static void ConvertItemToMyForm(const MENUITEMINFOW &si, CMenuItem &item)
91 {
92 COPY_MENUITEM_fields(item, si)
93 }
94
95 #ifndef _UNICODE
ConvertItemToMyForm(const MENUITEMINFOA & si,CMenuItem & item)96 static void ConvertItemToMyForm(const MENUITEMINFOA &si, CMenuItem &item)
97 {
98 COPY_MENUITEM_fields(item, si)
99 }
100 #endif
101
102
GetItem(UINT itemIndex,bool byPosition,CMenuItem & item) const103 bool CMenu::GetItem(UINT itemIndex, bool byPosition, CMenuItem &item) const
104 {
105 item.StringValue.Empty();
106 const unsigned kMaxSize = 512;
107 #ifndef _UNICODE
108 if (!g_IsNT)
109 {
110 MENUITEMINFOA si;
111 ConvertItemToSysForm(item, si);
112 const bool isString = item.IsString();
113 unsigned bufSize = kMaxSize;
114 AString a;
115 if (isString)
116 {
117 si.cch = bufSize;
118 si.dwTypeData = a.GetBuf(bufSize);
119 }
120 bool res = GetItemInfo(itemIndex, byPosition, &si);
121 if (isString)
122 a.ReleaseBuf_CalcLen(bufSize);
123 if (!res)
124 return false;
125 {
126 if (isString && si.cch >= bufSize - 1)
127 {
128 si.dwTypeData = NULL;
129 res = GetItemInfo(itemIndex, byPosition, &si);
130 if (!res)
131 return false;
132 si.cch++;
133 bufSize = si.cch;
134 si.dwTypeData = a.GetBuf(bufSize);
135 res = GetItemInfo(itemIndex, byPosition, &si);
136 a.ReleaseBuf_CalcLen(bufSize);
137 if (!res)
138 return false;
139 }
140 ConvertItemToMyForm(si, item);
141 if (isString)
142 item.StringValue = GetUnicodeString(a);
143 return true;
144 }
145 }
146 else
147 #endif
148 {
149 wchar_t s[kMaxSize + 1];
150 s[0] = 0;
151 MENUITEMINFOW si;
152 ConvertItemToSysForm(item, si);
153 const bool isString = item.IsString();
154 unsigned bufSize = kMaxSize;
155 if (isString)
156 {
157 si.cch = bufSize;
158 si.dwTypeData = s;
159 }
160 bool res = GetItemInfo(itemIndex, byPosition, &si);
161 if (!res)
162 return false;
163 if (isString)
164 {
165 s[Z7_ARRAY_SIZE(s) - 1] = 0;
166 item.StringValue = s;
167 if (si.cch >= bufSize - 1)
168 {
169 si.dwTypeData = NULL;
170 res = GetItemInfo(itemIndex, byPosition, &si);
171 if (!res)
172 return false;
173 si.cch++;
174 bufSize = si.cch;
175 si.dwTypeData = item.StringValue.GetBuf(bufSize);
176 res = GetItemInfo(itemIndex, byPosition, &si);
177 item.StringValue.ReleaseBuf_CalcLen(bufSize);
178 if (!res)
179 return false;
180 }
181 // if (item.StringValue.Len() != si.cch) throw 123; // for debug
182 }
183 ConvertItemToMyForm(si, item);
184 return true;
185 }
186 }
187
188
SetItem(UINT itemIndex,bool byPosition,const CMenuItem & item)189 bool CMenu::SetItem(UINT itemIndex, bool byPosition, const CMenuItem &item)
190 {
191 #ifndef _UNICODE
192 if (!g_IsNT)
193 {
194 MENUITEMINFOA si;
195 ConvertItemToSysForm(item, si);
196 AString s;
197 if (item.IsString())
198 {
199 s = GetSystemString(item.StringValue);
200 si.dwTypeData = s.Ptr_non_const();
201 }
202 return SetItemInfo(itemIndex, byPosition, &si);
203 }
204 else
205 #endif
206 {
207 MENUITEMINFOW si;
208 ConvertItemToSysForm(item, si);
209 if (item.IsString())
210 si.dwTypeData = item.StringValue.Ptr_non_const();
211 return SetItemInfo(itemIndex, byPosition, &si);
212 }
213 }
214
215
InsertItem(UINT itemIndex,bool byPosition,const CMenuItem & item)216 bool CMenu::InsertItem(UINT itemIndex, bool byPosition, const CMenuItem &item)
217 {
218 #ifndef _UNICODE
219 if (!g_IsNT)
220 {
221 MENUITEMINFOA si;
222 ConvertItemToSysForm(item, si);
223 AString s;
224 if (item.IsString())
225 {
226 s = GetSystemString(item.StringValue);
227 si.dwTypeData = s.Ptr_non_const();
228 }
229 return InsertItem(itemIndex, byPosition, &si);
230 }
231 else
232 #endif
233 {
234 MENUITEMINFOW si;
235 ConvertItemToSysForm(item, si);
236 if (item.IsString())
237 si.dwTypeData = item.StringValue.Ptr_non_const();
238 #ifdef UNDER_CE
239 UINT flags = (item.fType & MFT_SEPARATOR) ? MF_SEPARATOR : MF_STRING;
240 UINT_PTR id = item.wID;
241 if ((item.fMask & MIIM_SUBMENU) != 0)
242 {
243 flags |= MF_POPUP;
244 id = (UINT_PTR)item.hSubMenu;
245 }
246 if (!Insert(itemIndex, flags | (byPosition ? MF_BYPOSITION : MF_BYCOMMAND), id, item.StringValue))
247 return false;
248 return SetItemInfo(itemIndex, byPosition, &si);
249 #else
250 return InsertItem(itemIndex, byPosition, &si);
251 #endif
252 }
253 }
254
255 #ifndef _UNICODE
AppendItem(UINT flags,UINT_PTR newItemID,LPCWSTR newItem)256 bool CMenu::AppendItem(UINT flags, UINT_PTR newItemID, LPCWSTR newItem)
257 {
258 if (g_IsNT)
259 return BOOLToBool(::AppendMenuW(_menu, flags, newItemID, newItem));
260 else
261 return AppendItem(flags, newItemID, GetSystemString(newItem));
262 }
263 #endif
264
265 }
266