Sync with trunk r43000
[reactos.git] / rostests / winetests / user32 / menu.c
1 /*
2 * Unit tests for menus
3 *
4 * Copyright 2005 Robert Shearman
5 * Copyright 2007 Dmitry Timoshkov
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #define _WIN32_WINNT 0x0501
23
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <assert.h>
28
29 #define OEMRESOURCE /* For OBM_MNARROW */
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wingdi.h"
34 #include "winuser.h"
35
36 #include "wine/test.h"
37
38 static ATOM atomMenuCheckClass;
39
40 static BOOL (WINAPI *pGetMenuInfo)(HMENU,LPCMENUINFO);
41 static UINT (WINAPI *pSendInput)(UINT, INPUT*, size_t);
42 static BOOL (WINAPI *pSetMenuInfo)(HMENU,LPCMENUINFO);
43
44 static void init_function_pointers(void)
45 {
46 HMODULE hdll = GetModuleHandleA("user32");
47
48 #define GET_PROC(func) \
49 p ## func = (void*)GetProcAddress(hdll, #func); \
50 if(!p ## func) \
51 trace("GetProcAddress(%s) failed\n", #func);
52
53 GET_PROC(GetMenuInfo)
54 GET_PROC(SendInput)
55 GET_PROC(SetMenuInfo)
56
57 #undef GET_PROC
58 }
59
60 static LRESULT WINAPI menu_check_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
61 {
62 switch (msg)
63 {
64 case WM_ENTERMENULOOP:
65 /* mark window as having entered menu loop */
66 SetWindowLongPtr(hwnd, GWLP_USERDATA, TRUE);
67 /* exit menu modal loop
68 * ( A SendMessage does not work on NT3.51 here ) */
69 return PostMessage(hwnd, WM_CANCELMODE, 0, 0);
70 }
71 return DefWindowProc(hwnd, msg, wparam, lparam);
72 }
73
74 /* The MSVC headers ignore our NONAMELESSUNION requests so we have to define
75 * our own type */
76 typedef struct
77 {
78 DWORD type;
79 union
80 {
81 MOUSEINPUT mi;
82 KEYBDINPUT ki;
83 HARDWAREINPUT hi;
84 } u;
85 } TEST_INPUT;
86
87 /* globals to communicate between test and wndproc */
88
89 static BOOL bMenuVisible;
90 static HMENU hMenus[4];
91
92 #define MOD_SIZE 10
93 #define MOD_NRMENUS 8
94
95 /* menu texts with their sizes */
96 static struct {
97 LPCSTR text;
98 SIZE size; /* size of text up to any \t */
99 SIZE sc_size; /* size of the short-cut */
100 } MOD_txtsizes[] = {
101 { "Pinot &Noir" },
102 { "&Merlot\bF4" },
103 { "Shira&z\tAlt+S" },
104 { "" },
105 { NULL }
106 };
107
108 static unsigned int MOD_maxid;
109 static RECT MOD_rc[MOD_NRMENUS];
110 static int MOD_avec, MOD_hic;
111 static int MOD_odheight;
112 static SIZE MODsizes[MOD_NRMENUS]= { {MOD_SIZE, MOD_SIZE},{MOD_SIZE, MOD_SIZE},
113 {MOD_SIZE, MOD_SIZE},{MOD_SIZE, MOD_SIZE}};
114 static int MOD_GotDrawItemMsg = FALSE;
115 /* wndproc used by test_menu_ownerdraw() */
116 static LRESULT WINAPI menu_ownerdraw_wnd_proc(HWND hwnd, UINT msg,
117 WPARAM wparam, LPARAM lparam)
118 {
119 switch (msg)
120 {
121 case WM_MEASUREITEM:
122 {
123 MEASUREITEMSTRUCT* pmis = (MEASUREITEMSTRUCT*)lparam;
124 if( winetest_debug)
125 trace("WM_MEASUREITEM received data %lx size %dx%d\n",
126 pmis->itemData, pmis->itemWidth, pmis->itemHeight);
127 MOD_odheight = pmis->itemHeight;
128 pmis->itemWidth = MODsizes[pmis->itemData].cx;
129 pmis->itemHeight = MODsizes[pmis->itemData].cy;
130 return TRUE;
131 }
132 case WM_DRAWITEM:
133 {
134 DRAWITEMSTRUCT * pdis;
135 TEXTMETRIC tm;
136 HPEN oldpen;
137 char chrs[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
138 SIZE sz;
139 int i;
140 pdis = (DRAWITEMSTRUCT *) lparam;
141 if( winetest_debug) {
142 RECT rc;
143 GetMenuItemRect( hwnd, (HMENU)pdis->hwndItem, pdis->itemData ,&rc);
144 trace("WM_DRAWITEM received hwnd %p hmenu %p itemdata %ld item %d rc %d,%d-%d,%d itemrc: %d,%d-%d,%d\n",
145 hwnd, (HMENU)pdis->hwndItem, pdis->itemData,
146 pdis->itemID, pdis->rcItem.left, pdis->rcItem.top,
147 pdis->rcItem.right,pdis->rcItem.bottom,
148 rc.left,rc.top,rc.right,rc.bottom);
149 oldpen=SelectObject( pdis->hDC, GetStockObject(
150 pdis->itemState & ODS_SELECTED ? WHITE_PEN :BLACK_PEN));
151 Rectangle( pdis->hDC, pdis->rcItem.left,pdis->rcItem.top,
152 pdis->rcItem.right,pdis->rcItem.bottom );
153 SelectObject( pdis->hDC, oldpen);
154 }
155 /* calculate widths of some menu texts */
156 if( ! MOD_txtsizes[0].size.cx)
157 for(i = 0; MOD_txtsizes[i].text; i++) {
158 char buf[100], *p;
159 RECT rc={0,0,0,0};
160 strcpy( buf, MOD_txtsizes[i].text);
161 if( ( p = strchr( buf, '\t'))) {
162 *p = '\0';
163 DrawText( pdis->hDC, p + 1, -1, &rc,
164 DT_SINGLELINE|DT_CALCRECT);
165 MOD_txtsizes[i].sc_size.cx= rc.right - rc.left;
166 MOD_txtsizes[i].sc_size.cy= rc.bottom - rc.top;
167 }
168 DrawText( pdis->hDC, buf, -1, &rc,
169 DT_SINGLELINE|DT_CALCRECT);
170 MOD_txtsizes[i].size.cx= rc.right - rc.left;
171 MOD_txtsizes[i].size.cy= rc.bottom - rc.top;
172 }
173
174 if( pdis->itemData > MOD_maxid) return TRUE;
175 /* store the rectangl */
176 MOD_rc[pdis->itemData] = pdis->rcItem;
177 /* calculate average character width */
178 GetTextExtentPoint( pdis->hDC, chrs, 52, &sz );
179 MOD_avec = (sz.cx + 26)/52;
180 GetTextMetrics( pdis->hDC, &tm);
181 MOD_hic = tm.tmHeight;
182 MOD_GotDrawItemMsg = TRUE;
183 return TRUE;
184 }
185 case WM_ENTERIDLE:
186 {
187 PostMessage(hwnd, WM_CANCELMODE, 0, 0);
188 return TRUE;
189 }
190
191 }
192 return DefWindowProc(hwnd, msg, wparam, lparam);
193 }
194
195 static void register_menu_check_class(void)
196 {
197 WNDCLASS wc =
198 {
199 0,
200 menu_check_wnd_proc,
201 0,
202 0,
203 GetModuleHandle(NULL),
204 NULL,
205 LoadCursor(NULL, IDC_ARROW),
206 (HBRUSH)(COLOR_BTNFACE+1),
207 NULL,
208 TEXT("WineMenuCheck"),
209 };
210
211 atomMenuCheckClass = RegisterClass(&wc);
212 }
213
214 /* demonstrates that windows locks the menu object so that it is still valid
215 * even after a client calls DestroyMenu on it */
216 static void test_menu_locked_by_window(void)
217 {
218 BOOL ret;
219 HMENU hmenu;
220 HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
221 WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200,
222 NULL, NULL, NULL, NULL);
223 ok(hwnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
224 hmenu = CreateMenu();
225 ok(hmenu != NULL, "CreateMenu failed with error %d\n", GetLastError());
226 ret = InsertMenu(hmenu, 0, MF_STRING, 0, TEXT("&Test"));
227 ok(ret, "InsertMenu failed with error %d\n", GetLastError());
228 ret = SetMenu(hwnd, hmenu);
229 ok(ret, "SetMenu failed with error %d\n", GetLastError());
230 ret = DestroyMenu(hmenu);
231 ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
232
233 ret = DrawMenuBar(hwnd);
234 todo_wine {
235 ok(ret, "DrawMenuBar failed with error %d\n", GetLastError());
236 }
237 ret = IsMenu(GetMenu(hwnd));
238 ok(!ret, "Menu handle should have been destroyed\n");
239
240 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
241 /* did we process the WM_INITMENU message? */
242 ret = GetWindowLongPtr(hwnd, GWLP_USERDATA);
243 todo_wine {
244 ok(ret, "WM_INITMENU should have been sent\n");
245 }
246
247 DestroyWindow(hwnd);
248 }
249
250 static void test_menu_ownerdraw(void)
251 {
252 int i,j,k;
253 BOOL ret;
254 HMENU hmenu;
255 LONG leftcol;
256 HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
257 WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200,
258 NULL, NULL, NULL, NULL);
259 ok(hwnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
260 if( !hwnd) return;
261 SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG)menu_ownerdraw_wnd_proc);
262 hmenu = CreatePopupMenu();
263 ok(hmenu != NULL, "CreateMenu failed with error %d\n", GetLastError());
264 if( !hmenu) { DestroyWindow(hwnd);return;}
265 k=0;
266 for( j=0;j<2;j++) /* create columns */
267 for(i=0;i<2;i++) { /* create rows */
268 ret = AppendMenu( hmenu, MF_OWNERDRAW |
269 (i==0 ? MF_MENUBREAK : 0), k, (LPCTSTR) k);
270 k++;
271 ok( ret, "AppendMenu failed for %d\n", k-1);
272 }
273 MOD_maxid = k-1;
274 assert( k <= sizeof(MOD_rc)/sizeof(RECT));
275 /* display the menu */
276 ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
277
278 /* columns have a 4 pixel gap between them */
279 ok( MOD_rc[0].right + 4 == MOD_rc[2].left,
280 "item rectangles are not separated by 4 pixels space\n");
281 /* height should be what the MEASUREITEM message has returned */
282 ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE,
283 "menu item has wrong height: %d should be %d\n",
284 MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE);
285 /* no gaps between the rows */
286 ok( MOD_rc[0].bottom - MOD_rc[1].top == 0,
287 "There should not be a space between the rows, gap is %d\n",
288 MOD_rc[0].bottom - MOD_rc[1].top);
289 /* test the correct value of the item height that was sent
290 * by the WM_MEASUREITEM message */
291 ok( MOD_odheight == HIWORD( GetDialogBaseUnits()) || /* WinNT,2k,XP */
292 MOD_odheight == MOD_hic, /* Win95,98,ME */
293 "Wrong height field in MEASUREITEMSTRUCT, expected %d or %d actual %d\n",
294 HIWORD( GetDialogBaseUnits()), MOD_hic, MOD_odheight);
295 /* test what MF_MENUBREAK did at the first position. Also show
296 * that an MF_SEPARATOR is ignored in the height calculation. */
297 leftcol= MOD_rc[0].left;
298 ModifyMenu( hmenu, 0, MF_BYCOMMAND| MF_OWNERDRAW| MF_SEPARATOR, 0, 0);
299 /* display the menu */
300 ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
301 /* left should be 4 pixels less now */
302 ok( leftcol == MOD_rc[0].left + 4,
303 "columns should be 4 pixels to the left (actual %d).\n",
304 leftcol - MOD_rc[0].left);
305 /* test width */
306 ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE,
307 "width of owner drawn menu item is wrong. Got %d expected %d\n",
308 MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE);
309 /* and height */
310 ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE,
311 "Height is incorrect. Got %d expected %d\n",
312 MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE);
313
314 /* test width/height of an ownerdraw menu bar as well */
315 ret = DestroyMenu(hmenu);
316 ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
317 hmenu = CreateMenu();
318 ok(hmenu != NULL, "CreateMenu failed with error %d\n", GetLastError());
319 if( !hmenu) { DestroyWindow(hwnd);return;}
320 MOD_maxid=1;
321 for(i=0;i<2;i++) {
322 ret = AppendMenu( hmenu, MF_OWNERDRAW , i, 0);
323 ok( ret, "AppendMenu failed for %d\n", i);
324 }
325 ret = SetMenu( hwnd, hmenu);
326 UpdateWindow( hwnd); /* hack for wine to draw the window + menu */
327 ok(ret, "SetMenu failed with error %d\n", GetLastError());
328 /* test width */
329 ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE,
330 "width of owner drawn menu item is wrong. Got %d expected %d\n",
331 MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE);
332 /* test hight */
333 ok( MOD_rc[0].bottom - MOD_rc[0].top == GetSystemMetrics( SM_CYMENU) - 1,
334 "Height of owner drawn menu item is wrong. Got %d expected %d\n",
335 MOD_rc[0].bottom - MOD_rc[0].top, GetSystemMetrics( SM_CYMENU) - 1);
336
337 /* clean up */
338 ret = DestroyMenu(hmenu);
339 ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
340 DestroyWindow(hwnd);
341 }
342
343 /* helper for test_menu_bmp_and_string() */
344 static void test_mbs_help( int ispop, int hassub, int mnuopt,
345 HWND hwnd, int arrowwidth, int count, HBITMAP hbmp,
346 SIZE bmpsize, LPCSTR text, SIZE size, SIZE sc_size)
347 {
348 BOOL ret;
349 HMENU hmenu, submenu;
350 MENUITEMINFO mii={ sizeof( MENUITEMINFO )};
351 MENUINFO mi;
352 RECT rc;
353 CHAR text_copy[16];
354 int hastab, expect;
355 int failed = 0;
356
357 MOD_GotDrawItemMsg = FALSE;
358 mii.fMask = MIIM_FTYPE | MIIM_DATA | MIIM_STATE;
359 mii.fType = 0;
360 mii.fState = MF_CHECKED;
361 mii.dwItemData =0;
362 MODsizes[0] = bmpsize;
363 hastab = 0;
364 if( text ) {
365 char *p;
366 mii.fMask |= MIIM_STRING;
367 strcpy(text_copy, text);
368 mii.dwTypeData = text_copy; /* structure member declared non-const */
369 if( ( p = strchr( text, '\t'))) {
370 hastab = *(p + 1) ? 2 : 1;
371 }
372 }
373 /* tabs don't make sense in menubars */
374 if(hastab && !ispop) return;
375 if( hbmp) {
376 mii.fMask |= MIIM_BITMAP;
377 mii.hbmpItem = hbmp;
378 }
379 submenu = CreateMenu();
380 ok( submenu != 0, "CreateMenu failed with error %d\n", GetLastError());
381 if( ispop)
382 hmenu = CreatePopupMenu();
383 else
384 hmenu = CreateMenu();
385 ok( hmenu != 0, "Create{Popup}Menu failed with error %d\n", GetLastError());
386 if( hassub) {
387 mii.fMask |= MIIM_SUBMENU;
388 mii.hSubMenu = submenu;
389 }
390 if( mnuopt) {
391 mi.cbSize = sizeof(mi);
392 mi.fMask = MIM_STYLE;
393 pGetMenuInfo( hmenu, &mi);
394 mi.dwStyle |= mnuopt == 1 ? MNS_NOCHECK : MNS_CHECKORBMP;
395 ret = pSetMenuInfo( hmenu, &mi);
396 ok( ret, "SetMenuInfo failed with error %d\n", GetLastError());
397 }
398 ret = InsertMenuItem( hmenu, 0, FALSE, &mii);
399 ok( ret, "InsertMenuItem failed with error %d\n", GetLastError());
400 failed = !ret;
401 if( winetest_debug) {
402 HDC hdc=GetDC(hwnd);
403 RECT rc = {100, 50, 400, 70};
404 char buf[100];
405
406 sprintf( buf,"%d text \"%s\" mnuopt %d", count, text ? text: "(nil)", mnuopt);
407 FillRect( hdc, &rc, (HBRUSH) COLOR_WINDOW);
408 TextOut( hdc, 100, 50, buf, strlen( buf));
409 ReleaseDC( hwnd, hdc);
410 }
411 if(ispop)
412 ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
413 else {
414 ret = SetMenu( hwnd, hmenu);
415 ok(ret, "SetMenu failed with error %d\n", GetLastError());
416 DrawMenuBar( hwnd);
417 }
418 ret = GetMenuItemRect( hwnd, hmenu, 0, &rc);
419 /* check menu width */
420 if( ispop)
421 expect = ( text || hbmp ?
422 4 + (mnuopt != 1 ? GetSystemMetrics(SM_CXMENUCHECK) : 0)
423 : 0) +
424 arrowwidth + MOD_avec + (hbmp ? bmpsize.cx + 2 : 0) +
425 (text && hastab ? /* TAB space */
426 MOD_avec + ( hastab==2 ? sc_size.cx : 0) : 0) +
427 (text ? 2 + (text[0] ? size.cx :0): 0) ;
428 else
429 expect = !(text || hbmp) ? 0 :
430 ( hbmp ? (text ? 2:0) + bmpsize.cx : 0 ) +
431 (text ? 2 * MOD_avec + (text[0] ? size.cx :0): 0) ;
432 ok( rc.right - rc.left == expect,
433 "menu width wrong, got %d expected %d\n", rc.right - rc.left, expect);
434 failed = failed || !(rc.right - rc.left == expect);
435 /* check menu height */
436 if( ispop)
437 expect = max( ( !(text || hbmp) ? GetSystemMetrics( SM_CYMENUSIZE)/2 : 0),
438 max( (text ? max( 2 + size.cy, MOD_hic + 4) : 0),
439 (hbmp ? bmpsize.cy + 2 : 0)));
440 else
441 expect = ( !(text || hbmp) ? GetSystemMetrics( SM_CYMENUSIZE)/2 :
442 max( GetSystemMetrics( SM_CYMENU) - 1, (hbmp ? bmpsize.cy : 0)));
443 ok( rc.bottom - rc.top == expect,
444 "menu height wrong, got %d expected %d (%d)\n",
445 rc.bottom - rc.top, expect, GetSystemMetrics( SM_CYMENU));
446 failed = failed || !(rc.bottom - rc.top == expect);
447 if( hbmp == HBMMENU_CALLBACK && MOD_GotDrawItemMsg) {
448 /* check the position of the bitmap */
449 /* horizontal */
450 expect = ispop ? (4 + ( mnuopt ? 0 : GetSystemMetrics(SM_CXMENUCHECK)))
451 : 3;
452 ok( expect == MOD_rc[0].left,
453 "bitmap left is %d expected %d\n", MOD_rc[0].left, expect);
454 failed = failed || !(expect == MOD_rc[0].left);
455 /* vertical */
456 expect = (rc.bottom - rc.top - MOD_rc[0].bottom + MOD_rc[0].top) / 2;
457 ok( expect == MOD_rc[0].top,
458 "bitmap top is %d expected %d\n", MOD_rc[0].top, expect);
459 failed = failed || !(expect == MOD_rc[0].top);
460 }
461 /* if there was a failure, report details */
462 if( failed) {
463 trace("*** count %d text \"%s\" bitmap %p bmsize %d,%d textsize %d+%d,%d mnuopt %d hastab %d\n",
464 count, text ? text: "(nil)", hbmp, bmpsize.cx, bmpsize.cy,
465 size.cx, size.cy, sc_size.cx, mnuopt, hastab);
466 trace(" check %d,%d arrow %d avechar %d\n",
467 GetSystemMetrics(SM_CXMENUCHECK ),
468 GetSystemMetrics(SM_CYMENUCHECK ),arrowwidth, MOD_avec);
469 if( hbmp == HBMMENU_CALLBACK)
470 trace( " rc %d,%d-%d,%d bmp.rc %d,%d-%d,%d\n",
471 rc.left, rc.top, rc.top, rc.bottom, MOD_rc[0].left,
472 MOD_rc[0].top,MOD_rc[0].right, MOD_rc[0].bottom);
473 }
474 /* clean up */
475 ret = DestroyMenu(submenu);
476 ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
477 ret = DestroyMenu(hmenu);
478 ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
479 }
480
481
482 static void test_menu_bmp_and_string(void)
483 {
484 BYTE bmfill[300];
485 HBITMAP hbm_arrow;
486 BITMAP bm;
487 INT arrowwidth;
488 HWND hwnd;
489 int count, szidx, txtidx, bmpidx, hassub, mnuopt, ispop;
490
491 if( !pGetMenuInfo)
492 {
493 skip("GetMenuInfo is not available\n");
494 return;
495 }
496
497 memset( bmfill, 0xcc, sizeof( bmfill));
498 hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
499 WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200,
500 NULL, NULL, NULL, NULL);
501 hbm_arrow=LoadBitmap( 0, (CHAR*)OBM_MNARROW);
502 GetObject( hbm_arrow, sizeof(bm), &bm);
503 arrowwidth = bm.bmWidth;
504
505 ok(hwnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
506 if( !hwnd) return;
507 SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG)menu_ownerdraw_wnd_proc);
508
509 if( winetest_debug)
510 trace(" check %d,%d arrow %d avechar %d\n",
511 GetSystemMetrics(SM_CXMENUCHECK ),
512 GetSystemMetrics(SM_CYMENUCHECK ),arrowwidth, MOD_avec);
513 count = 0;
514 MOD_maxid = 0;
515 for( ispop=1; ispop >= 0; ispop--){
516 static SIZE bmsizes[]= {
517 {10,10},{38,38},{1,30},{55,5}};
518 for( szidx=0; szidx < sizeof( bmsizes) / sizeof( SIZE); szidx++) {
519 HBITMAP hbm = CreateBitmap( bmsizes[szidx].cx, bmsizes[szidx].cy,1,1,bmfill);
520 HBITMAP bitmaps[] = { HBMMENU_CALLBACK, hbm, NULL };
521 ok( (int)hbm, "CreateBitmap failed err %d\n", GetLastError());
522 for( txtidx = 0; txtidx < sizeof(MOD_txtsizes)/sizeof(MOD_txtsizes[0]); txtidx++) {
523 for( hassub = 0; hassub < 2 ; hassub++) { /* add submenu item */
524 for( mnuopt = 0; mnuopt < 3 ; mnuopt++){ /* test MNS_NOCHECK/MNS_CHECKORBMP */
525 for( bmpidx = 0; bmpidx <sizeof(bitmaps)/sizeof(HBITMAP); bmpidx++) {
526 /* no need to test NULL bitmaps of several sizes */
527 if( !bitmaps[bmpidx] && szidx > 0) continue;
528 if( !ispop && hassub) continue;
529 test_mbs_help( ispop, hassub, mnuopt,
530 hwnd, arrowwidth, ++count,
531 bitmaps[bmpidx],
532 bmsizes[szidx],
533 MOD_txtsizes[txtidx].text,
534 MOD_txtsizes[txtidx].size,
535 MOD_txtsizes[txtidx].sc_size);
536 }
537 }
538 }
539 }
540 DeleteObject( hbm);
541 }
542 }
543 /* clean up */
544 DestroyWindow(hwnd);
545 }
546
547 static void test_menu_add_string( void )
548 {
549 HMENU hmenu;
550 MENUITEMINFO info;
551 BOOL rc;
552 int ret;
553
554 char string[0x80];
555 char string2[0x80];
556
557 char strback[0x80];
558 WCHAR strbackW[0x80];
559 static CHAR blah[] = "blah";
560 static const WCHAR expectedString[] = {'D','u','m','m','y',' ','s','t','r','i','n','g', 0};
561
562 hmenu = CreateMenu();
563
564 memset( &info, 0, sizeof info );
565 info.cbSize = sizeof info;
566 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_ID;
567 info.dwTypeData = blah;
568 info.cch = 6;
569 info.dwItemData = 0;
570 info.wID = 1;
571 info.fState = 0;
572 InsertMenuItem(hmenu, 0, TRUE, &info );
573
574 memset( &info, 0, sizeof info );
575 info.cbSize = sizeof info;
576 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_DATA | MIIM_ID;
577 info.dwTypeData = string;
578 info.cch = sizeof string;
579 string[0] = 0;
580 GetMenuItemInfo( hmenu, 0, TRUE, &info );
581
582 ok( !strcmp( string, "blah" ), "menu item name differed\n");
583
584 /* Test combination of ownerdraw and strings with GetMenuItemString(A/W) */
585 strcpy(string, "Dummy string");
586 memset(&info, 0x00, sizeof(info));
587 info.cbSize= sizeof(MENUITEMINFO);
588 info.fMask= MIIM_FTYPE | MIIM_STRING; /* Set OwnerDraw + typeData */
589 info.fType= MFT_OWNERDRAW;
590 info.dwTypeData= string;
591 rc = InsertMenuItem( hmenu, 0, TRUE, &info );
592 ok (rc, "InsertMenuItem failed\n");
593
594 strcpy(string,"Garbage");
595 ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
596 ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
597
598 SetLastError(0xdeadbeef);
599 ret = GetMenuStringW( hmenu, 0, (WCHAR *)strbackW, 99, MF_BYPOSITION);
600 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
601 skip("GetMenuStringW is not implemented\n");
602 else
603 {
604 ok (ret, "GetMenuStringW on ownerdraw entry failed\n");
605 ok (!lstrcmpW( strbackW, expectedString ), "Menu text from Unicode version incorrect\n");
606 }
607
608 /* Just change ftype to string and see what text is stored */
609 memset(&info, 0x00, sizeof(info));
610 info.cbSize= sizeof(MENUITEMINFO);
611 info.fMask= MIIM_FTYPE; /* Set string type */
612 info.fType= MFT_STRING;
613 info.dwTypeData= (char *)0xdeadbeef;
614 rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
615 ok (rc, "SetMenuItemInfo failed\n");
616
617 /* Did we keep the old dwTypeData? */
618 ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
619 ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
620
621 /* Ensure change to bitmap type fails */
622 memset(&info, 0x00, sizeof(info));
623 info.cbSize= sizeof(MENUITEMINFO);
624 info.fMask= MIIM_FTYPE; /* Set as bitmap type */
625 info.fType= MFT_BITMAP;
626 info.dwTypeData= (char *)0xdeadbee2;
627 rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
628 ok (!rc, "SetMenuItemInfo unexpectedly worked\n");
629
630 /* Just change ftype back and ensure data hasn't been freed */
631 info.fType= MFT_OWNERDRAW; /* Set as ownerdraw type */
632 info.dwTypeData= (char *)0xdeadbee3;
633 rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
634 ok (rc, "SetMenuItemInfo failed\n");
635
636 /* Did we keep the old dwTypeData? */
637 ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
638 ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
639
640 /* Just change string value (not type) */
641 memset(&info, 0x00, sizeof(info));
642 info.cbSize= sizeof(MENUITEMINFO);
643 info.fMask= MIIM_STRING; /* Set typeData */
644 strcpy(string2, "string2");
645 info.dwTypeData= string2;
646 rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
647 ok (rc, "SetMenuItemInfo failed\n");
648
649 ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
650 ok (!strcmp( strback, "string2" ), "Menu text from Ansi version incorrect\n");
651
652 /* crashes with wine 0.9.5 */
653 memset(&info, 0x00, sizeof(info));
654 info.cbSize= sizeof(MENUITEMINFO);
655 info.fMask= MIIM_FTYPE | MIIM_STRING; /* Set OwnerDraw + typeData */
656 info.fType= MFT_OWNERDRAW;
657 rc = InsertMenuItem( hmenu, 0, TRUE, &info );
658 ok (rc, "InsertMenuItem failed\n");
659 ok (!GetMenuString( hmenu, 0, NULL, 0, MF_BYPOSITION),
660 "GetMenuString on ownerdraw entry succeeded.\n");
661 SetLastError(0xdeadbeef);
662 ret = GetMenuStringW( hmenu, 0, NULL, 0, MF_BYPOSITION);
663 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
664 skip("GetMenuStringW is not implemented\n");
665 else
666 ok (!ret, "GetMenuStringW on ownerdraw entry succeeded.\n");
667
668 DestroyMenu( hmenu );
669 }
670
671 /* define building blocks for the menu item info tests */
672 static int strncmpW( const WCHAR *str1, const WCHAR *str2, int n )
673 {
674 if (n <= 0) return 0;
675 while ((--n > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; }
676 return *str1 - *str2;
677 }
678
679 static WCHAR *strcpyW( WCHAR *dst, const WCHAR *src )
680 {
681 WCHAR *p = dst;
682 while ((*p++ = *src++));
683 return dst;
684 }
685
686
687 #define DMIINFF( i, e, field)\
688 ok((int)((i)->field)==(int)((e)->field) || (int)((i)->field)==(0xffff & (int)((e)->field)), \
689 "%s got 0x%x expected 0x%x\n", #field, (int)((i)->field), (int)((e)->field));
690
691 #define DUMPMIINF(s,i,e)\
692 {\
693 DMIINFF( i, e, fMask)\
694 DMIINFF( i, e, fType)\
695 DMIINFF( i, e, fState)\
696 DMIINFF( i, e, wID)\
697 DMIINFF( i, e, hSubMenu)\
698 DMIINFF( i, e, hbmpChecked)\
699 DMIINFF( i, e, hbmpUnchecked)\
700 DMIINFF( i, e, dwItemData)\
701 DMIINFF( i, e, dwTypeData)\
702 DMIINFF( i, e, cch)\
703 if( s==sizeof(MENUITEMINFOA)) DMIINFF( i, e, hbmpItem)\
704 }
705
706 /* insert menu item */
707 #define TMII_INSMI( a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,n1,\
708 eret1)\
709 {\
710 MENUITEMINFOA info1=a1 b1,c1,d1,e1,f1,(void*)g1,(void*)h1,(void*)i1,j1,(void*)k1,l1,(void*)m1 n1;\
711 HMENU hmenu = CreateMenu();\
712 BOOL ret, stop = FALSE;\
713 SetLastError( 0xdeadbeef);\
714 if(ansi)strcpy( string, init);\
715 else strcpyW( (WCHAR*)string, (WCHAR*)init);\
716 if( ansi) ret = InsertMenuItemA(hmenu, 0, TRUE, &info1 );\
717 else ret = InsertMenuItemW(hmenu, 0, TRUE, (MENUITEMINFOW*)&info1 );\
718 if( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)\
719 {\
720 skip("InsertMenuItem%s not implemented\n", ansi ? "A" : "W");\
721 break;\
722 }\
723 if( !(eret1)) { ok( (eret1)==ret,"InsertMenuItem should have failed.\n");\
724 stop = TRUE;\
725 } else ok( (eret1)==ret,"InsertMenuItem failed, err %d\n",GetLastError());\
726
727
728 /* GetMenuItemInfo + GetMenuString */
729 #define TMII_GMII( a2,b2,c2,d2,e2,f2,g2,h2,i2,j2,k2,l2,m2,n2,\
730 a3,b3,c3,d3,e3,f3,g3,h3,i3,j3,k3,l3,m3,n3,\
731 expname, eret2, eret3)\
732 {\
733 MENUITEMINFOA info2A=a2 b2,c2,d2,e2,f2,(void*)g2,(void*)h2,(void*)i2,j2,(void*)k2,l2,(void*)m2 n2;\
734 MENUITEMINFOA einfoA=a3 b3,c3,d3,e3,f3,(void*)g3,(void*)h3,(void*)i3,j3,(void*)k3,l3,(void*)m3 n3;\
735 MENUITEMINFOA *info2 = &info2A;\
736 MENUITEMINFOA *einfo = &einfoA;\
737 MENUITEMINFOW *info2W = (MENUITEMINFOW *)&info2A;\
738 if( !stop) {\
739 SetLastError( 0xdeadbeef);\
740 ret = ansi ? GetMenuItemInfoA( hmenu, 0, TRUE, info2 ) :\
741 GetMenuItemInfoW( hmenu, 0, TRUE, info2W );\
742 if( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)\
743 {\
744 skip("GetMenuItemInfo%s not implemented\n", ansi ? "A" : "W");\
745 break;\
746 }\
747 if( !(eret2)) ok( (eret2)==ret,"GetMenuItemInfo should have failed.\n");\
748 else { \
749 ok( (eret2)==ret,"GetMenuItemInfo failed, err %d\n",GetLastError());\
750 ret = memcmp( info2, einfo, sizeof einfoA);\
751 /* ok( ret==0, "Got wrong menu item info data\n");*/\
752 if( ret) DUMPMIINF(info2A.cbSize, &info2A, &einfoA)\
753 if( einfo->dwTypeData == string) {\
754 if(ansi) ok( !strncmp( expname, info2->dwTypeData, einfo->cch ), "menu item name differed \"%s\"\n",\
755 einfo->dwTypeData ? einfo->dwTypeData: "");\
756 else ok( !strncmpW( (WCHAR*)expname, (WCHAR*)info2->dwTypeData, einfo->cch ), "menu item name differed \"%s\"\n",\
757 einfo->dwTypeData ? einfo->dwTypeData: "");\
758 ret = ansi ? GetMenuStringA( hmenu, 0, string, 80, MF_BYPOSITION) :\
759 GetMenuStringW( hmenu, 0, string, 80, MF_BYPOSITION);\
760 if( (eret3)){\
761 ok( ret, "GetMenuString failed, err %d\n",GetLastError());\
762 }else\
763 ok( !ret, "GetMenuString should have failed\n");\
764 }\
765 }\
766 }\
767 }
768
769 #define TMII_DONE \
770 RemoveMenu(hmenu, 0, TRUE );\
771 DestroyMenu( hmenu );\
772 DestroyMenu( submenu );\
773 submenu = CreateMenu();\
774 }
775 /* modify menu */
776 #define TMII_MODM( flags, id, data, eret )\
777 if( !stop) {\
778 SetLastError( 0xdeadbeef);\
779 if(ansi)ret = ModifyMenuA( hmenu, 0, flags, (UINT_PTR)id, (char*)data);\
780 else ret = ModifyMenuW( hmenu, 0, flags, (UINT_PTR)id, (WCHAR*)data);\
781 if( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)\
782 {\
783 skip("ModifyMenu%s not implemented\n", ansi ? "A" : "W");\
784 break;\
785 }\
786 if( !(eret)) ok( (eret)==ret,"ModifyMenuA should have failed.\n");\
787 else ok( (eret)==ret,"ModifyMenuA failed, err %d\n",GetLastError());\
788 }
789
790 /* SetMenuItemInfo */
791 #define TMII_SMII( a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,n1,\
792 eret1)\
793 if( !stop) {\
794 MENUITEMINFOA info1=a1 b1,c1,d1,e1,f1,(void*)g1,(void*)h1,(void*)i1,j1,(void*)k1,l1,(void*)m1 n1;\
795 SetLastError( 0xdeadbeef);\
796 if(ansi)strcpy( string, init);\
797 else strcpyW( (WCHAR*)string, (WCHAR*)init);\
798 if( ansi) ret = SetMenuItemInfoA(hmenu, 0, TRUE, &info1 );\
799 else ret = SetMenuItemInfoW(hmenu, 0, TRUE, (MENUITEMINFOW*)&info1 );\
800 if( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)\
801 {\
802 skip("SetMenuItemInfo%s not implemented\n", ansi ? "A" : "W");\
803 break;\
804 }\
805 if( !(eret1)) { ok( (eret1)==ret,"InsertMenuItem should have failed.\n");\
806 stop = TRUE;\
807 } else ok( (eret1)==ret,"InsertMenuItem failed, err %d\n",GetLastError());\
808 }
809
810
811
812 #define OK 1
813 #define ER 0
814
815
816 static void test_menu_iteminfo( void )
817 {
818 int S=sizeof( MENUITEMINFOA);
819 int ansi = TRUE;
820 char txtA[]="wine";
821 char initA[]="XYZ";
822 char emptyA[]="";
823 WCHAR txtW[]={'W','i','n','e',0};
824 WCHAR initW[]={'X','Y','Z',0};
825 WCHAR emptyW[]={0};
826 void *txt, *init, *empty, *string;
827 HBITMAP hbm = CreateBitmap(1,1,1,1,NULL);
828 char stringA[0x80];
829 HMENU submenu=CreateMenu();
830
831 do {
832 if( ansi) {txt=txtA;init=initA;empty=emptyA;string=stringA;}
833 else {txt=txtW;init=initW;empty=emptyW;string=stringA;}
834 trace( "%s string %p hbm %p txt %p\n", ansi ? "ANSI tests: " : "Unicode tests:", string, hbm, txt);
835 /* test all combinations of MFT_STRING, MFT_OWNERDRAW and MFT_BITMAP */
836 /* (since MFT_STRING is zero, there are four of them) */
837 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, 0, 0, 0, 0, 0, 0, txt, 0, 0, }, OK)
838 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
839 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
840 txt, OK, OK )
841 TMII_DONE
842 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
843 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
844 {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, 0, 0, 0, },
845 empty, OK, ER )
846 TMII_DONE
847 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, 6, -1, }, OK)
848 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
849 {, S, MIIM_TYPE, MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
850 empty, OK, ER )
851 TMII_DONE
852 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, hbm, 6, -1, }, OK)
853 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
854 {, S, MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
855 empty, OK, ER )
856 TMII_DONE
857 /* not enough space for name*/
858 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
859 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, NULL, 0, -9, },
860 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, NULL, 4, 0, },
861 empty, OK, OK )
862 TMII_DONE
863 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
864 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 5, -9, },
865 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
866 txt, OK, OK )
867 TMII_DONE
868 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
869 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 4, -9, },
870 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 3, 0, },
871 txt, OK, OK )
872 TMII_DONE
873 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
874 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, NULL, 0, -9, },
875 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 0, 0, },
876 empty, OK, ER )
877 TMII_DONE
878 /* cannot combine MIIM_TYPE with some other flags */
879 TMII_INSMI( {, S, MIIM_TYPE|MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, ER)
880 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
881 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
882 empty, OK, OK )
883 TMII_DONE
884 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
885 TMII_GMII ( {, S, MIIM_TYPE|MIIM_STRING, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
886 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
887 empty, ER, OK )
888 TMII_DONE
889 TMII_INSMI( {, S, MIIM_TYPE|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, ER)
890 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
891 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
892 empty, OK, OK )
893 TMII_DONE
894 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
895 TMII_GMII ( {, S, MIIM_TYPE|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
896 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
897 empty, ER, OK )
898 TMII_DONE
899 TMII_INSMI( {, S, MIIM_TYPE|MIIM_BITMAP, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, 6, hbm, }, ER)
900 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
901 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
902 empty, OK, OK )
903 TMII_DONE
904 /* but succeeds with some others */
905 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
906 TMII_GMII ( {, S, MIIM_TYPE|MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
907 {, S, MIIM_TYPE|MIIM_SUBMENU, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
908 txt, OK, OK )
909 TMII_DONE
910 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
911 TMII_GMII ( {, S, MIIM_TYPE|MIIM_STATE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
912 {, S, MIIM_TYPE|MIIM_STATE, MFT_STRING, 0, -9, 0, -9, -9, -9, string, 4, 0, },
913 txt, OK, OK )
914 TMII_DONE
915 TMII_INSMI( {, S, MIIM_TYPE|MIIM_ID, MFT_STRING, -1, 888, -1, -1, -1, -1, txt, 6, -1, }, OK)
916 TMII_GMII ( {, S, MIIM_TYPE|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
917 {, S, MIIM_TYPE|MIIM_ID, MFT_STRING, -9, 888, 0, -9, -9, -9, string, 4, 0, },
918 txt, OK, OK )
919 TMII_DONE
920 TMII_INSMI( {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING, -1, -1, -1, -1, -1, 999, txt, 6, -1, }, OK)
921 TMII_GMII ( {, S, MIIM_TYPE|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
922 {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING, -9, -9, 0, -9, -9, 999, string, 4, 0, },
923 txt, OK, OK )
924 TMII_DONE
925 /* to be continued */
926 /* set text with MIIM_TYPE and retrieve with MIIM_STRING */
927 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
928 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
929 {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, -9, },
930 txt, OK, OK )
931 TMII_DONE
932 /* set text with MIIM_TYPE and retrieve with MIIM_STRING; MFT_OWNERDRAW causes an empty string */
933 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
934 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
935 {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 0, -9, },
936 empty, OK, ER )
937 TMII_DONE
938 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
939 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
940 {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 0, -9, },
941 empty, OK, ER )
942 TMII_DONE
943 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
944 TMII_GMII ( {, S, MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
945 {, S, MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 80, -9, },
946 init, OK, ER )
947 TMII_DONE
948 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
949 TMII_GMII ( {, S, 0, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
950 {, S, 0, -9, -9, -9, 0, -9, -9, -9, string, 80, -9, },
951 init, OK, OK )
952 TMII_DONE
953 /* contrary to MIIM_TYPE,you can set the text for an owner draw menu */
954 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
955 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
956 {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 4, -9, },
957 txt, OK, OK )
958 TMII_DONE
959 /* same but retrieve with MIIM_TYPE */
960 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
961 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
962 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
963 txt, OK, OK )
964 TMII_DONE
965 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
966 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
967 {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 0, -9, },
968 empty, OK, ER )
969 TMII_DONE
970 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
971 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
972 {, S, MIIM_STRING|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 0, -9, },
973 empty, OK, ER )
974 TMII_DONE
975
976 /* How is that with bitmaps? */
977 TMII_INSMI( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
978 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
979 {, S, MIIM_TYPE, MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
980 empty, OK, ER )
981 TMII_DONE
982 TMII_INSMI( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
983 TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
984 {, S, MIIM_BITMAP|MIIM_FTYPE, 0, -9, -9, 0, -9, -9, -9, string, 80, hbm, },
985 init, OK, ER )
986 TMII_DONE
987 /* MIIM_BITMAP does not like MFT_BITMAP */
988 TMII_INSMI( {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, ER)
989 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
990 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
991 init, OK, OK )
992 TMII_DONE
993 /* no problem with OWNERDRAWN */
994 TMII_INSMI( {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
995 TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
996 {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 80, hbm, },
997 init, OK, ER )
998 TMII_DONE
999 /* setting MFT_BITMAP with MFT_FTYPE fails anyway */
1000 TMII_INSMI( {, S, MIIM_FTYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, }, ER)
1001 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
1002 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
1003 empty, OK, OK )
1004 TMII_DONE
1005
1006 /* menu with submenu */
1007 TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, -1, -1, -1, txt, 0, -1, }, OK)
1008 TMII_GMII ( {, S, MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1009 {, S, MIIM_SUBMENU, -9, -9, -9, submenu, -9, -9, -9, string, 80, -9, },
1010 init, OK, ER )
1011 TMII_DONE
1012 TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, -1, -1, -1, empty, 0, -1, }, OK)
1013 TMII_GMII ( {, S, MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1014 {, S, MIIM_SUBMENU, -9, -9, -9, submenu, -9, -9, -9, string, 80, -9, },
1015 init, OK, ER )
1016 TMII_DONE
1017 /* menu with submenu, without MIIM_SUBMENU the submenufield is cleared */
1018 TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, -1, -1, -1, txt, 0, -1, }, OK)
1019 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1020 {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 0, -9, },
1021 empty, OK, ER )
1022 TMII_GMII ( {, S, MIIM_SUBMENU|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1023 {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, submenu, -9, -9, -9, string, 80, -9, },
1024 empty, OK, ER )
1025 TMII_DONE
1026 /* menu with invalid submenu */
1027 TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, 999, -1, -1, -1, txt, 0, -1, }, ER)
1028 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
1029 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
1030 init, OK, ER )
1031 TMII_DONE
1032 /* Separator */
1033 TMII_INSMI( {, S, MIIM_TYPE, MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, txt, 0, 0, }, OK)
1034 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1035 {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, 0, 0, 0, },
1036 empty, OK, ER )
1037 TMII_DONE
1038 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP|MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, hbm, 6, -1, }, OK)
1039 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1040 {, S, MIIM_TYPE, MFT_BITMAP|MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
1041 empty, OK, ER )
1042 TMII_DONE
1043 /* SEPARATOR and STRING go well together */
1044 /* BITMAP and STRING go well together */
1045 TMII_INSMI( {, S, MIIM_STRING|MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1046 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1047 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, hbm, },
1048 txt, OK, OK )
1049 TMII_DONE
1050 /* BITMAP, SEPARATOR and STRING go well together */
1051 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1052 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1053 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 4, hbm, },
1054 txt, OK, OK )
1055 TMII_DONE
1056 /* last two tests, but use MIIM_TYPE to retrieve info */
1057 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
1058 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1059 {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1060 txt, OK, OK )
1061 TMII_DONE
1062 TMII_INSMI( {, S, MIIM_STRING|MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1063 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1064 {, S, MIIM_TYPE, MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
1065 txt, OK, OK )
1066 TMII_DONE
1067 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1068 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1069 {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
1070 txt, OK, OK )
1071 TMII_DONE
1072 /* same three with MFT_OWNERDRAW */
1073 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
1074 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1075 {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1076 txt, OK, OK )
1077 TMII_DONE
1078 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1079 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1080 {, S, MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
1081 txt, OK, OK )
1082 TMII_DONE
1083 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1084 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1085 {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_BITMAP|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
1086 txt, OK, OK )
1087 TMII_DONE
1088
1089 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE|MIIM_ID, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
1090 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1091 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1092 txt, OK, OK )
1093 TMII_DONE
1094 /* test with modifymenu: string is preserved after setting OWNERDRAW */
1095 TMII_INSMI( {, S, MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
1096 TMII_MODM( MFT_OWNERDRAW, -1, 787, OK)
1097 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1098 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_DATA, MFT_OWNERDRAW, -9, -9, 0, -9, -9, 787, string, 4, -9, },
1099 txt, OK, OK )
1100 TMII_DONE
1101 /* same with bitmap: now the text is cleared */
1102 TMII_INSMI( {, S, MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
1103 TMII_MODM( MFT_BITMAP, 545, hbm, OK)
1104 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1105 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_BITMAP, -9, 545, 0, -9, -9, -9, string, 0, hbm, },
1106 empty, OK, ER )
1107 TMII_DONE
1108 /* start with bitmap: now setting text clears it (though he flag is raised) */
1109 TMII_INSMI( {, S, MIIM_BITMAP, MFT_STRING, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1110 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1111 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_STRING, -9, 0, 0, -9, -9, -9, string, 0, hbm, },
1112 empty, OK, ER )
1113 TMII_MODM( MFT_STRING, 545, txt, OK)
1114 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1115 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_STRING, -9, 545, 0, -9, -9, -9, string, 4, 0, },
1116 txt, OK, OK )
1117 TMII_DONE
1118 /*repeat with text NULL */
1119 TMII_INSMI( {, S, MIIM_BITMAP, MFT_STRING, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1120 TMII_MODM( MFT_STRING, 545, NULL, OK)
1121 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1122 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_SEPARATOR, -9, 545, 0, -9, -9, -9, string, 0, 0, },
1123 empty, OK, ER )
1124 TMII_DONE
1125 /* repeat with text "" */
1126 TMII_INSMI( {, S, MIIM_BITMAP, -1 , -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1127 TMII_MODM( MFT_STRING, 545, empty, OK)
1128 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1129 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_STRING, -9, 545, 0, -9, -9, -9, string, 0, 0, },
1130 empty, OK, ER )
1131 TMII_DONE
1132 /* start with bitmap: set ownerdraw */
1133 TMII_INSMI( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1134 TMII_MODM( MFT_OWNERDRAW, -1, 232, OK)
1135 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1136 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_DATA, MFT_OWNERDRAW, -9, -9, 0, -9, -9, 232, string, 0, hbm, },
1137 empty, OK, ER )
1138 TMII_DONE
1139 /* ask nothing */
1140 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1141 TMII_GMII ( {, S, 0, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1142 {, S, 0, -9, -9, -9, 0, -9, -9, -9, string, 80, -9, },
1143 init, OK, OK )
1144 TMII_DONE
1145 /* some tests with small cbSize: the hbmpItem is to be ignored */
1146 TMII_INSMI( {, S - 4, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1147 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1148 {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, NULL, 0, NULL, },
1149 empty, OK, ER )
1150 TMII_DONE
1151 TMII_INSMI( {, S - 4, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1152 TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1153 {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 80, NULL, },
1154 init, OK, ER )
1155 TMII_DONE
1156 TMII_INSMI( {, S - 4, MIIM_STRING|MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1157 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1158 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, NULL, },
1159 txt, OK, OK )
1160 TMII_DONE
1161 TMII_INSMI( {, S - 4, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1162 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1163 {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1164 txt, OK, OK )
1165 TMII_DONE
1166 TMII_INSMI( {, S - 4, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1167 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1168 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1169 txt, OK, OK )
1170 TMII_DONE
1171 TMII_INSMI( {, S - 4, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1172 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1173 {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1174 txt, OK, OK )
1175 TMII_DONE
1176 /* MIIM_TYPE by itself does not get/set the dwItemData for OwnerDrawn menus */
1177 TMII_INSMI( {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, 343, txt, 0, -1, }, OK)
1178 TMII_GMII ( {, S, MIIM_TYPE|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1179 {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, 343, 0, 0, 0, },
1180 empty, OK, ER )
1181 TMII_DONE
1182 TMII_INSMI( {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, 343, txt, 0, -1, }, OK)
1183 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1184 {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, 0, 0, 0, },
1185 empty, OK, ER )
1186 TMII_DONE
1187 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, 343, txt, 0, -1, }, OK)
1188 TMII_GMII ( {, S, MIIM_TYPE|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1189 {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, 0, 0, 0, 0, },
1190 empty, OK, ER )
1191 TMII_DONE
1192 /* set a string menu to ownerdraw with MIIM_TYPE */
1193 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -2, -2, -2, -2, -2, -2, txt, -2, -2, }, OK)
1194 TMII_SMII( {, S, MIIM_TYPE, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, -1, -1, -1, }, OK)
1195 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1196 {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 4, -9, },
1197 txt, OK, OK )
1198 TMII_DONE
1199 /* test with modifymenu add submenu */
1200 TMII_INSMI( {, S, MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
1201 TMII_MODM( MF_POPUP, submenu, txt, OK)
1202 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1203 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_SUBMENU, MFT_STRING, -9, -9, submenu, -9, -9, -9, string, 4, -9, },
1204 txt, OK, OK )
1205 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1206 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
1207 txt, OK, OK )
1208 TMII_DONE
1209 /* MFT_SEPARATOR bit is kept when the text is added */
1210 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
1211 TMII_SMII( {, S, MIIM_STRING, -1, -1, -1, -1, -1, -1, -1, txt, -1, -1, }, OK)
1212 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1213 {, S, MIIM_STRING|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 4, -9, },
1214 txt, OK, OK )
1215 TMII_DONE
1216 /* MFT_SEPARATOR bit is kept when bitmap is added */
1217 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
1218 TMII_SMII( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1219 TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1220 {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 80, hbm, },
1221 init, OK, ER )
1222 TMII_DONE
1223 /* Bitmaps inserted with MIIM_TYPE and MFT_BITMAP:
1224 Only the low word of the dwTypeData is used.
1225 Use a magic bitmap here (Word 95 uses this to create its MDI menu buttons) */
1226 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP | MFT_RIGHTJUSTIFY, -1, -1, -1, -1, -1, -1, MAKELONG(HBMMENU_MBAR_CLOSE, 0x1234), -1, -1, }, OK)
1227 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1228 {, S, MIIM_TYPE, MFT_BITMAP | MFT_RIGHTJUSTIFY, -9, -9, 0, -9, -9, -9, HBMMENU_MBAR_CLOSE, 0, HBMMENU_MBAR_CLOSE, },
1229 empty, OK, OK )
1230 TMII_DONE
1231 /* Type flags */
1232 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP | MFT_MENUBARBREAK | MFT_RADIOCHECK | MFT_RIGHTJUSTIFY | MFT_RIGHTORDER, -1, -1, -1, -1, -1, -1, hbm, -1, -1, }, OK)
1233 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1234 {, S, MIIM_TYPE, MFT_BITMAP | MFT_MENUBARBREAK | MFT_RADIOCHECK | MFT_RIGHTJUSTIFY | MFT_RIGHTORDER, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
1235 empty, OK, OK )
1236 TMII_DONE
1237 /* State flags */
1238 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, -1, -1, }, OK)
1239 TMII_SMII( {, S, MIIM_STATE, -1, MFS_CHECKED | MFS_DEFAULT | MFS_GRAYED | MFS_HILITE, -1, -1, -1, -1, -1, -1, -1, -1, }, OK)
1240 TMII_GMII ( {, S, MIIM_STATE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1241 {, S, MIIM_STATE, -9, MFS_CHECKED | MFS_DEFAULT | MFS_GRAYED | MFS_HILITE, -9, 0, -9, -9, -9, -9, -9, -9, },
1242 empty, OK, OK )
1243 TMII_DONE
1244 /* The style MFT_RADIOCHECK cannot be set with MIIM_CHECKMARKS only */
1245 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, -1, -1, }, OK)
1246 TMII_SMII( {, S, MIIM_CHECKMARKS, MFT_RADIOCHECK, -1, -1, -1, hbm, hbm, -1, -1, -1, -1, }, OK)
1247 TMII_GMII ( {, S, MIIM_CHECKMARKS | MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1248 {, S, MIIM_CHECKMARKS | MIIM_TYPE, MFT_BITMAP, -9, -9, 0, hbm, hbm, -9, hbm, 0, hbm, },
1249 empty, OK, OK )
1250 TMII_DONE
1251 /* MFT_BITMAP is added automatically by GetMenuItemInfo() for MIIM_TYPE */
1252 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, -1, -1, }, OK)
1253 TMII_SMII( {, S, MIIM_FTYPE, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, 0x1234, -1, -1, }, OK)
1254 TMII_GMII ( {, S, MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1255 {, S, MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, -9, -9, -9, },
1256 empty, OK, OK )
1257 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1258 {, S, MIIM_TYPE, MFT_BITMAP | MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
1259 empty, OK, OK )
1260 TMII_GMII ( {, S, MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1261 {, S, MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, -9, -9, -9, },
1262 empty, OK, OK )
1263 TMII_SMII( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, NULL, }, OK)
1264 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1265 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 0, NULL, },
1266 empty, OK, OK )
1267 TMII_DONE
1268 /* Bitmaps inserted with MIIM_TYPE and MFT_BITMAP:
1269 Only the low word of the dwTypeData is used.
1270 Use a magic bitmap here (Word 95 uses this to create its MDI menu buttons) */
1271 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP | MFT_RIGHTJUSTIFY, -1, -1, -1, -1, -1, -1, MAKELONG(HBMMENU_MBAR_CLOSE, 0x1234), -1, -1, }, OK)
1272 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1273 {, S, MIIM_TYPE, MFT_BITMAP | MFT_RIGHTJUSTIFY, -9, -9, 0, -9, -9, -9, HBMMENU_MBAR_CLOSE, 0, HBMMENU_MBAR_CLOSE, },
1274 empty, OK, OK )
1275 TMII_DONE
1276 /* Type flags */
1277 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP | MFT_MENUBARBREAK | MFT_RADIOCHECK | MFT_RIGHTJUSTIFY | MFT_RIGHTORDER, -1, -1, -1, -1, -1, -1, hbm, -1, -1, }, OK)
1278 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1279 {, S, MIIM_TYPE, MFT_BITMAP | MFT_MENUBARBREAK | MFT_RADIOCHECK | MFT_RIGHTJUSTIFY | MFT_RIGHTORDER, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
1280 empty, OK, OK )
1281 TMII_DONE
1282 /* State flags */
1283 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, -1, -1, }, OK)
1284 TMII_SMII( {, S, MIIM_STATE, -1, MFS_CHECKED | MFS_DEFAULT | MFS_GRAYED | MFS_HILITE, -1, -1, -1, -1, -1, -1, -1, -1, }, OK)
1285 TMII_GMII ( {, S, MIIM_STATE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1286 {, S, MIIM_STATE, -9, MFS_CHECKED | MFS_DEFAULT | MFS_GRAYED | MFS_HILITE, -9, 0, -9, -9, -9, -9, -9, -9, },
1287 empty, OK, OK )
1288 TMII_DONE
1289 /* The style MFT_RADIOCHECK cannot be set with MIIM_CHECKMARKS only */
1290 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, -1, -1, }, OK)
1291 TMII_SMII( {, S, MIIM_CHECKMARKS, MFT_RADIOCHECK, -1, -1, -1, hbm, hbm, -1, -1, -1, -1, }, OK)
1292 TMII_GMII ( {, S, MIIM_CHECKMARKS | MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1293 {, S, MIIM_CHECKMARKS | MIIM_TYPE, MFT_BITMAP, -9, -9, 0, hbm, hbm, -9, hbm, 0, hbm, },
1294 empty, OK, OK )
1295 TMII_DONE
1296 /* MFT_BITMAP is added automatically by GetMenuItemInfo() for MIIM_TYPE */
1297 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, -1, -1, }, OK)
1298 TMII_SMII( {, S, MIIM_FTYPE, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, 0x1234, -1, -1, }, OK)
1299 TMII_GMII ( {, S, MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1300 {, S, MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, -9, -9, -9, },
1301 empty, OK, OK )
1302 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1303 {, S, MIIM_TYPE, MFT_BITMAP | MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
1304 empty, OK, OK )
1305 TMII_GMII ( {, S, MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1306 {, S, MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, -9, -9, -9, },
1307 empty, OK, OK )
1308 TMII_SMII( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, NULL, }, OK)
1309 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1310 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 0, NULL, },
1311 empty, OK, OK )
1312 TMII_DONE
1313 } while( !(ansi = !ansi) );
1314 DeleteObject( hbm);
1315 }
1316
1317 /*
1318 The following tests try to confirm the algorithm used to return the menu items
1319 when there is a collision between a menu item and a popup menu
1320 */
1321 static void test_menu_search_bycommand( void )
1322 {
1323 HMENU hmenu, hmenuSub, hmenuSub2;
1324 MENUITEMINFO info;
1325 BOOL rc;
1326 UINT id;
1327 char strback[0x80];
1328 char strIn[0x80];
1329 static CHAR menuitem[] = "MenuItem",
1330 menuitem2[] = "MenuItem 2";
1331
1332 /* Case 1: Menu containing a menu item */
1333 hmenu = CreateMenu();
1334
1335 memset( &info, 0, sizeof info );
1336 info.cbSize = sizeof info;
1337 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1338 info.fType = MFT_STRING;
1339 strcpy(strIn, "Case 1 MenuItem");
1340 info.dwTypeData = strIn;
1341 info.wID = (UINT) 0x1234;
1342
1343 rc = InsertMenuItem(hmenu, 0, TRUE, &info );
1344 ok (rc, "Inserting the menuitem failed\n");
1345
1346 id = GetMenuItemID(hmenu, 0);
1347 ok (id == 0x1234, "Getting the menuitem id failed(gave %x)\n", id);
1348
1349 /* Confirm the menuitem was given the id supplied (getting by position) */
1350 memset( &info, 0, sizeof info );
1351 strback[0] = 0x00;
1352 info.cbSize = sizeof(MENUITEMINFO);
1353 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1354 info.dwTypeData = strback;
1355 info.cch = sizeof(strback);
1356
1357 rc = GetMenuItemInfo(hmenu, 0, TRUE, &info); /* Get by position */
1358 ok (rc, "Getting the menu items info failed\n");
1359 ok (info.wID == 0x1234, "IDs differ for the menuitem\n");
1360 ok (!strcmp(info.dwTypeData, "Case 1 MenuItem"), "Returned item has wrong label\n");
1361
1362 /* Search by id - Should return the item */
1363 memset( &info, 0, sizeof info );
1364 strback[0] = 0x00;
1365 info.cbSize = sizeof(MENUITEMINFO);
1366 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1367 info.dwTypeData = strback;
1368 info.cch = sizeof(strback);
1369 rc = GetMenuItemInfo(hmenu, 0x1234, FALSE, &info); /* Get by ID */
1370
1371 ok (rc, "Getting the menu items info failed\n");
1372 ok (info.wID == 0x1234, "IDs differ for the menuitem\n");
1373 ok (!strcmp(info.dwTypeData, "Case 1 MenuItem"), "Returned item has wrong label\n");
1374
1375 DestroyMenu( hmenu );
1376
1377 /* Case 2: Menu containing a popup menu */
1378 hmenu = CreateMenu();
1379 hmenuSub = CreateMenu();
1380
1381 strcpy(strIn, "Case 2 SubMenu");
1382 rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, strIn);
1383 ok (rc, "Inserting the popup menu into the main menu failed\n");
1384
1385 id = GetMenuItemID(hmenu, 0);
1386 ok (id == -1, "Getting the menuitem id unexpectedly worked (gave %x)\n", id);
1387
1388 /* Confirm the menuitem itself was given an id the same as the HMENU, (getting by position) */
1389 memset( &info, 0, sizeof info );
1390 strback[0] = 0x00;
1391 info.cbSize = sizeof(MENUITEMINFO);
1392 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1393 info.dwTypeData = strback;
1394 info.cch = sizeof(strback);
1395 info.wID = 0xdeadbeef;
1396
1397 rc = GetMenuItemInfo(hmenu, 0, TRUE, &info); /* Get by position */
1398 ok (rc, "Getting the menu items info failed\n");
1399 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the menuitem\n");
1400 ok (!strcmp(info.dwTypeData, "Case 2 SubMenu"), "Returned item has wrong label\n");
1401
1402 /* Search by id - returns the popup menu itself */
1403 memset( &info, 0, sizeof info );
1404 strback[0] = 0x00;
1405 info.cbSize = sizeof(MENUITEMINFO);
1406 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1407 info.dwTypeData = strback;
1408 info.cch = sizeof(strback);
1409 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
1410
1411 ok (rc, "Getting the menu items info failed\n");
1412 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
1413 ok (!strcmp(info.dwTypeData, "Case 2 SubMenu"), "Returned item has wrong label\n");
1414
1415 /*
1416 Now add an item after it with the same id
1417 */
1418 memset( &info, 0, sizeof info );
1419 info.cbSize = sizeof info;
1420 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1421 info.fType = MFT_STRING;
1422 strcpy(strIn, "Case 2 MenuItem 1");
1423 info.dwTypeData = strIn;
1424 info.wID = (UINT_PTR) hmenuSub;
1425 rc = InsertMenuItem(hmenu, -1, TRUE, &info );
1426 ok (rc, "Inserting the menuitem failed\n");
1427
1428 /* Search by id - returns the item which follows the popup menu */
1429 memset( &info, 0, sizeof info );
1430 strback[0] = 0x00;
1431 info.cbSize = sizeof(MENUITEMINFO);
1432 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1433 info.dwTypeData = strback;
1434 info.cch = sizeof(strback);
1435 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
1436
1437 ok (rc, "Getting the menu items info failed\n");
1438 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
1439 ok (!strcmp(info.dwTypeData, "Case 2 MenuItem 1"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1440
1441 /*
1442 Now add an item before the popup (with the same id)
1443 */
1444 memset( &info, 0, sizeof info );
1445 info.cbSize = sizeof info;
1446 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1447 info.fType = MFT_STRING;
1448 strcpy(strIn, "Case 2 MenuItem 2");
1449 info.dwTypeData = strIn;
1450 info.wID = (UINT_PTR) hmenuSub;
1451 rc = InsertMenuItem(hmenu, 0, TRUE, &info );
1452 ok (rc, "Inserting the menuitem failed\n");
1453
1454 /* Search by id - returns the item which precedes the popup menu */
1455 memset( &info, 0, sizeof info );
1456 strback[0] = 0x00;
1457 info.cbSize = sizeof(MENUITEMINFO);
1458 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1459 info.dwTypeData = strback;
1460 info.cch = sizeof(strback);
1461 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
1462
1463 ok (rc, "Getting the menu items info failed\n");
1464 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
1465 ok (!strcmp(info.dwTypeData, "Case 2 MenuItem 2"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1466
1467 DestroyMenu( hmenu );
1468 DestroyMenu( hmenuSub );
1469
1470 /*
1471 Case 3: Menu containing a popup menu which in turn
1472 contains 2 items with the same id as the popup itself
1473 */
1474
1475 hmenu = CreateMenu();
1476 hmenuSub = CreateMenu();
1477
1478 memset( &info, 0, sizeof info );
1479 info.cbSize = sizeof info;
1480 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1481 info.fType = MFT_STRING;
1482 info.dwTypeData = menuitem;
1483 info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1484
1485 rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
1486 ok (rc, "Inserting the popup menu into the main menu failed\n");
1487
1488 rc = InsertMenuItem(hmenuSub, 0, TRUE, &info );
1489 ok (rc, "Inserting the sub menu menuitem failed\n");
1490
1491 memset( &info, 0, sizeof info );
1492 info.cbSize = sizeof info;
1493 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1494 info.fType = MFT_STRING;
1495 info.dwTypeData = menuitem2;
1496 info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1497
1498 rc = InsertMenuItem(hmenuSub, 1, TRUE, &info );
1499 ok (rc, "Inserting the sub menu menuitem 2 failed\n");
1500
1501 /* Prove that you can't query the id of a popup directly (By position) */
1502 id = GetMenuItemID(hmenu, 0);
1503 ok (id == -1, "Getting the sub menu id should have failed because its a popup (gave %x)\n", id);
1504
1505 /* Prove getting the item info via ID returns the first item (not the popup or 2nd item)*/
1506 memset( &info, 0, sizeof info );
1507 strback[0] = 0x00;
1508 info.cbSize = sizeof(MENUITEMINFO);
1509 info.fMask = MIIM_STRING | MIIM_ID;
1510 info.dwTypeData = strback;
1511 info.cch = sizeof(strback);
1512
1513 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info);
1514 ok (rc, "Getting the menus info failed\n");
1515 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for popup menu\n");
1516 ok (!strcmp(info.dwTypeData, "MenuItem"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1517 DestroyMenu( hmenu );
1518 DestroyMenu( hmenuSub );
1519
1520 /*
1521 Case 4: Menu containing 2 popup menus, the second
1522 contains 2 items with the same id as the first popup menu
1523 */
1524 hmenu = CreateMenu();
1525 hmenuSub = CreateMenu();
1526 hmenuSub2 = CreateMenu();
1527
1528 rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
1529 ok (rc, "Inserting the popup menu into the main menu failed\n");
1530
1531 rc = InsertMenu(hmenu, 1, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub2, "Submenu2");
1532 ok (rc, "Inserting the popup menu into the main menu failed\n");
1533
1534 memset( &info, 0, sizeof info );
1535 info.cbSize = sizeof info;
1536 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1537 info.fType = MFT_STRING;
1538 info.dwTypeData = menuitem;
1539 info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1540
1541 rc = InsertMenuItem(hmenuSub2, 0, TRUE, &info );
1542 ok (rc, "Inserting the sub menu menuitem failed\n");
1543
1544 memset( &info, 0, sizeof info );
1545 info.cbSize = sizeof info;
1546 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1547 info.fType = MFT_STRING;
1548 info.dwTypeData = menuitem2;
1549 info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1550
1551 rc = InsertMenuItem(hmenuSub2, 1, TRUE, &info );
1552 ok (rc, "Inserting the sub menu menuitem 2 failed\n");
1553
1554 /* Prove getting the item info via ID returns the first item (not the popup or 2nd item)*/
1555 memset( &info, 0, sizeof info );
1556 strback[0] = 0x00;
1557 info.cbSize = sizeof(MENUITEMINFO);
1558 info.fMask = MIIM_STRING | MIIM_ID;
1559 info.dwTypeData = strback;
1560 info.cch = sizeof(strback);
1561
1562 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info);
1563 ok (rc, "Getting the menus info failed\n");
1564 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for popup menu\n");
1565 ok (!strcmp(info.dwTypeData, "MenuItem"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1566
1567 memset( &info, 0, sizeof info );
1568 strback[0] = 0x00;
1569 info.cbSize = sizeof(MENUITEMINFO);
1570 info.fMask = MIIM_STRING | MIIM_ID;
1571 info.dwTypeData = strback;
1572 info.cch = sizeof(strback);
1573
1574 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub2, FALSE, &info);
1575 ok (rc, "Getting the menus info failed\n");
1576 ok (info.wID == (UINT)hmenuSub2, "IDs differ for popup menu\n");
1577 ok (!strcmp(info.dwTypeData, "Submenu2"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1578
1579 DestroyMenu( hmenu );
1580 DestroyMenu( hmenuSub );
1581 DestroyMenu( hmenuSub2 );
1582
1583
1584 /*
1585 Case 5: Menu containing a popup menu which in turn
1586 contains an item with a different id than the popup menu.
1587 This tests the fallback to a popup menu ID.
1588 */
1589
1590 hmenu = CreateMenu();
1591 hmenuSub = CreateMenu();
1592
1593 rc = AppendMenu(hmenu, MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
1594 ok (rc, "Appending the popup menu to the main menu failed\n");
1595
1596 rc = AppendMenu(hmenuSub, MF_STRING, 102, "Item");
1597 ok (rc, "Appending the item to the popup menu failed\n");
1598
1599 /* Set the ID for hmenuSub */
1600 info.cbSize = sizeof(info);
1601 info.fMask = MIIM_ID;
1602 info.wID = 101;
1603
1604 rc = SetMenuItemInfo(hmenu, 0, TRUE, &info);
1605 ok(rc, "Setting the ID for the popup menu failed\n");
1606
1607 /* Check if the ID has been set */
1608 info.wID = 0;
1609 rc = GetMenuItemInfo(hmenu, 0, TRUE, &info);
1610 ok(rc, "Getting the ID for the popup menu failed\n");
1611 ok(info.wID == 101, "The ID for the popup menu has not been set\n");
1612
1613 /* Prove getting the item info via ID returns the popup menu */
1614 memset( &info, 0, sizeof(info));
1615 strback[0] = 0x00;
1616 info.cbSize = sizeof(MENUITEMINFO);
1617 info.fMask = MIIM_STRING | MIIM_ID;
1618 info.dwTypeData = strback;
1619 info.cch = sizeof(strback);
1620
1621 rc = GetMenuItemInfo(hmenu, 101, FALSE, &info);
1622 ok (rc, "Getting the menu info failed\n");
1623 ok (info.wID == 101, "IDs differ\n");
1624 ok (!strcmp(info.dwTypeData, "Submenu"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1625
1626 /* Also look for the menu item */
1627 memset( &info, 0, sizeof(info));
1628 strback[0] = 0x00;
1629 info.cbSize = sizeof(MENUITEMINFO);
1630 info.fMask = MIIM_STRING | MIIM_ID;
1631 info.dwTypeData = strback;
1632 info.cch = sizeof(strback);
1633
1634 rc = GetMenuItemInfo(hmenu, 102, FALSE, &info);
1635 ok (rc, "Getting the menu info failed\n");
1636 ok (info.wID == 102, "IDs differ\n");
1637 ok (!strcmp(info.dwTypeData, "Item"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1638
1639 DestroyMenu(hmenu);
1640 DestroyMenu(hmenuSub);
1641 }
1642
1643 struct menu_item_pair_s {
1644 UINT uMenu; /* 1 - top level menu, [0-Menu 1-Enabled 2-Disabled]
1645 * 2 - 2nd level menu, [0-Popup 1-Enabled 2-Disabled]
1646 * 3 - 3rd level menu, [0-Enabled 1-Disabled] */
1647 UINT uItem;
1648 };
1649
1650 static struct menu_mouse_tests_s {
1651 DWORD type;
1652 struct menu_item_pair_s menu_item_pairs[5]; /* for mousing */
1653 WORD wVk[5]; /* keys */
1654 BOOL bMenuVisible;
1655 BOOL _todo_wine;
1656 } menu_tests[] = {
1657 /* for each test, send keys or clicks and check for menu visibility */
1658 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 0}, TRUE, FALSE }, /* test 0 */
1659 { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, 0}, FALSE, FALSE },
1660 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 0}, TRUE, FALSE },
1661 { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
1662 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 0}, TRUE, FALSE },
1663 { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
1664 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 0}, TRUE, FALSE },
1665 { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, VK_ESCAPE, 0}, FALSE, FALSE },
1666 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', VK_ESCAPE, 0}, TRUE, FALSE },
1667 { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, 0}, FALSE, FALSE },
1668 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 0}, TRUE, FALSE },
1669 { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
1670 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 0}, TRUE, FALSE },
1671 { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
1672 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 'P', 0}, TRUE, FALSE },
1673 { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
1674 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 'P', 0}, TRUE, FALSE },
1675 { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
1676
1677 { INPUT_MOUSE, {{1, 2}, {0}}, {0}, TRUE, TRUE }, /* test 18 */
1678 { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
1679 { INPUT_MOUSE, {{1, 0}, {0}}, {0}, TRUE, TRUE },
1680 { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
1681 { INPUT_MOUSE, {{1, 0}, {2, 2}, {0}}, {0}, TRUE, TRUE },
1682 { INPUT_MOUSE, {{2, 1}, {0}}, {0}, FALSE, FALSE },
1683 { INPUT_MOUSE, {{1, 0}, {2, 0}, {0}}, {0}, TRUE, TRUE },
1684 { INPUT_MOUSE, {{3, 0}, {0}}, {0}, FALSE, FALSE },
1685 { INPUT_MOUSE, {{1, 0}, {2, 0}, {0}}, {0}, TRUE, TRUE },
1686 { INPUT_MOUSE, {{3, 1}, {0}}, {0}, TRUE, TRUE },
1687 { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
1688 { -1 }
1689 };
1690
1691 static void send_key(WORD wVk)
1692 {
1693 TEST_INPUT i[2];
1694 memset(&i, 0, 2*sizeof(INPUT));
1695 i[0].type = i[1].type = INPUT_KEYBOARD;
1696 i[0].u.ki.wVk = i[1].u.ki.wVk = wVk;
1697 i[1].u.ki.dwFlags = KEYEVENTF_KEYUP;
1698 pSendInput(2, (INPUT *) i, sizeof(INPUT));
1699 }
1700
1701 static void click_menu(HANDLE hWnd, struct menu_item_pair_s *mi)
1702 {
1703 HMENU hMenu = hMenus[mi->uMenu];
1704 TEST_INPUT i[3];
1705 MSG msg;
1706 RECT r;
1707 int screen_w = GetSystemMetrics(SM_CXSCREEN);
1708 int screen_h = GetSystemMetrics(SM_CYSCREEN);
1709
1710 GetMenuItemRect(mi->uMenu > 2 ? NULL : hWnd, hMenu, mi->uItem, &r);
1711
1712 memset(&i, 0, 3*sizeof(INPUT));
1713 i[0].type = i[1].type = i[2].type = INPUT_MOUSE;
1714 i[0].u.mi.dx = i[1].u.mi.dx = i[2].u.mi.dx
1715 = ((r.left + 5) * 65535) / screen_w;
1716 i[0].u.mi.dy = i[1].u.mi.dy = i[2].u.mi.dy
1717 = ((r.top + 5) * 65535) / screen_h;
1718 i[0].u.mi.dwFlags = i[1].u.mi.dwFlags = i[2].u.mi.dwFlags
1719 = MOUSEEVENTF_ABSOLUTE;
1720 i[0].u.mi.dwFlags |= MOUSEEVENTF_MOVE;
1721 i[1].u.mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
1722 i[2].u.mi.dwFlags |= MOUSEEVENTF_LEFTUP;
1723 pSendInput(3, (INPUT *) i, sizeof(INPUT));
1724
1725 /* hack to prevent mouse message buildup in Wine */
1726 while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
1727 }
1728
1729 static DWORD WINAPI test_menu_input_thread(LPVOID lpParameter)
1730 {
1731 int i, j;
1732 HANDLE hWnd = lpParameter;
1733
1734 Sleep(500);
1735 /* mixed keyboard/mouse test */
1736 for (i = 0; menu_tests[i].type != -1; i++)
1737 {
1738 int elapsed = 0;
1739
1740 if (menu_tests[i].type == INPUT_KEYBOARD)
1741 for (j = 0; menu_tests[i].wVk[j] != 0; j++)
1742 send_key(menu_tests[i].wVk[j]);
1743 else
1744 for (j = 0; menu_tests[i].menu_item_pairs[j].uMenu != 0; j++)
1745 click_menu(hWnd, &menu_tests[i].menu_item_pairs[j]);
1746
1747 while (menu_tests[i].bMenuVisible != bMenuVisible)
1748 {
1749 if (elapsed > 200)
1750 break;
1751 elapsed += 20;
1752 Sleep(20);
1753 }
1754
1755 if (menu_tests[i]._todo_wine)
1756 {
1757 todo_wine {
1758 ok(menu_tests[i].bMenuVisible == bMenuVisible, "test %d\n", i);
1759 }
1760 }
1761 else
1762 ok(menu_tests[i].bMenuVisible == bMenuVisible, "test %d\n", i);
1763 }
1764 return 0;
1765 }
1766
1767 static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam,
1768 LPARAM lParam)
1769 {
1770 switch (msg) {
1771 case WM_ENTERMENULOOP:
1772 bMenuVisible = TRUE;
1773 break;
1774 case WM_EXITMENULOOP:
1775 bMenuVisible = FALSE;
1776 break;
1777 default:
1778 return( DefWindowProcA( hWnd, msg, wParam, lParam ) );
1779 }
1780 return 0;
1781 }
1782
1783 static void test_menu_input(void) {
1784 MSG msg;
1785 WNDCLASSA wclass;
1786 HINSTANCE hInstance = GetModuleHandleA( NULL );
1787 HANDLE hThread, hWnd;
1788
1789 wclass.lpszClassName = "MenuTestClass";
1790 wclass.style = CS_HREDRAW | CS_VREDRAW;
1791 wclass.lpfnWndProc = WndProc;
1792 wclass.hInstance = hInstance;
1793 wclass.hIcon = LoadIconA( 0, (LPSTR)IDI_APPLICATION );
1794 wclass.hCursor = LoadCursorA( NULL, (LPSTR)IDC_ARROW);
1795 wclass.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1);
1796 wclass.lpszMenuName = 0;
1797 wclass.cbClsExtra = 0;
1798 wclass.cbWndExtra = 0;
1799 assert (RegisterClassA( &wclass ));
1800 assert (hWnd = CreateWindowA( wclass.lpszClassName, "MenuTest",
1801 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0,
1802 400, 200, NULL, NULL, hInstance, NULL) );
1803
1804 /* fixed menus */
1805 hMenus[3] = CreatePopupMenu();
1806 AppendMenu(hMenus[3], MF_STRING, 0, "&Enabled");
1807 AppendMenu(hMenus[3], MF_STRING|MF_DISABLED, 0, "&Disabled");
1808
1809 hMenus[2] = CreatePopupMenu();
1810 AppendMenu(hMenus[2], MF_STRING|MF_POPUP, (UINT_PTR) hMenus[3], "&Popup");
1811 AppendMenu(hMenus[2], MF_STRING, 0, "&Enabled");
1812 AppendMenu(hMenus[2], MF_STRING|MF_DISABLED, 0, "&Disabled");
1813
1814 hMenus[1] = CreateMenu();
1815 AppendMenu(hMenus[1], MF_STRING|MF_POPUP, (UINT_PTR) hMenus[2], "&Menu");
1816 AppendMenu(hMenus[1], MF_STRING, 0, "&Enabled");
1817 AppendMenu(hMenus[1], MF_STRING|MF_DISABLED, 0, "&Disabled");
1818
1819 SetMenu(hWnd, hMenus[1]);
1820 ShowWindow(hWnd, SW_SHOW);
1821 UpdateWindow(hWnd);
1822
1823 hThread = CreateThread(NULL, 0, test_menu_input_thread, hWnd, 0, NULL);
1824 while(1)
1825 {
1826 if (WAIT_TIMEOUT != WaitForSingleObject(hThread, 50))
1827 break;
1828 while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
1829 }
1830 DestroyWindow(hWnd);
1831 }
1832
1833 static void test_menu_flags( void )
1834 {
1835 HMENU hMenu, hPopupMenu;
1836
1837 hMenu = CreateMenu();
1838 hPopupMenu = CreatePopupMenu();
1839
1840 AppendMenu(hMenu, MF_POPUP | MF_STRING, (UINT)hPopupMenu, "Popup");
1841
1842 AppendMenu(hPopupMenu, MF_STRING | MF_HILITE | MF_DEFAULT, 101, "Item 1");
1843 InsertMenu(hPopupMenu, 1, MF_BYPOSITION | MF_STRING | MF_HILITE | MF_DEFAULT, 102, "Item 2");
1844 AppendMenu(hPopupMenu, MF_STRING, 103, "Item 3");
1845 ModifyMenu(hPopupMenu, 2, MF_BYPOSITION | MF_STRING | MF_HILITE | MF_DEFAULT, 103, "Item 3");
1846
1847 ok(GetMenuState(hPopupMenu, 0, MF_BYPOSITION) & MF_HILITE,
1848 "AppendMenu should accept MF_HILITE\n");
1849 ok(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE,
1850 "InsertMenu should accept MF_HILITE\n");
1851 ok(GetMenuState(hPopupMenu, 2, MF_BYPOSITION) & MF_HILITE,
1852 "ModifyMenu should accept MF_HILITE\n");
1853
1854 ok(!(GetMenuState(hPopupMenu, 0, MF_BYPOSITION) & MF_DEFAULT),
1855 "AppendMenu must not accept MF_DEFAULT\n");
1856 ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_DEFAULT),
1857 "InsertMenu must not accept MF_DEFAULT\n");
1858 ok(!(GetMenuState(hPopupMenu, 2, MF_BYPOSITION) & MF_DEFAULT),
1859 "ModifyMenu must not accept MF_DEFAULT\n");
1860
1861 DestroyMenu(hMenu);
1862 }
1863
1864 static void test_menu_hilitemenuitem( void )
1865 {
1866 HMENU hMenu, hPopupMenu;
1867 WNDCLASSA wclass;
1868 HWND hWnd;
1869
1870 wclass.lpszClassName = "HiliteMenuTestClass";
1871 wclass.style = CS_HREDRAW | CS_VREDRAW;
1872 wclass.lpfnWndProc = WndProc;
1873 wclass.hInstance = GetModuleHandleA( NULL );
1874 wclass.hIcon = LoadIconA( 0, (LPSTR)IDI_APPLICATION );
1875 wclass.hCursor = LoadCursorA( NULL, (LPSTR)IDC_ARROW);
1876 wclass.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1);
1877 wclass.lpszMenuName = 0;
1878 wclass.cbClsExtra = 0;
1879 wclass.cbWndExtra = 0;
1880 assert (RegisterClassA( &wclass ));
1881 assert (hWnd = CreateWindowA( wclass.lpszClassName, "HiliteMenuTest",
1882 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0,
1883 400, 200, NULL, NULL, wclass.hInstance, NULL) );
1884
1885 hMenu = CreateMenu();
1886 hPopupMenu = CreatePopupMenu();
1887
1888 AppendMenu(hMenu, MF_POPUP | MF_STRING, (UINT)hPopupMenu, "Popup");
1889
1890 AppendMenu(hPopupMenu, MF_STRING, 101, "Item 1");
1891 AppendMenu(hPopupMenu, MF_STRING, 102, "Item 2");
1892 AppendMenu(hPopupMenu, MF_STRING, 103, "Item 3");
1893
1894 SetMenu(hWnd, hMenu);
1895
1896 /* test invalid arguments */
1897
1898 ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE),
1899 "HiliteMenuItem: Item 2 is hilited\n");
1900
1901 SetLastError(0xdeadbeef);
1902 todo_wine
1903 {
1904 ok(!HiliteMenuItem(NULL, hPopupMenu, 1, MF_HILITE | MF_BYPOSITION),
1905 "HiliteMenuItem: call should have failed.\n");
1906 }
1907 ok(GetLastError() == 0xdeadbeef || /* 9x */
1908 GetLastError() == ERROR_INVALID_WINDOW_HANDLE /* NT */,
1909 "HiliteMenuItem: expected error ERROR_INVALID_WINDOW_HANDLE, got: %d\n", GetLastError());
1910
1911 SetLastError(0xdeadbeef);
1912 ok(!HiliteMenuItem(hWnd, NULL, 1, MF_HILITE | MF_BYPOSITION),
1913 "HiliteMenuItem: call should have failed.\n");
1914 ok(GetLastError() == 0xdeadbeef || /* 9x */
1915 GetLastError() == ERROR_INVALID_MENU_HANDLE /* NT */,
1916 "HiliteMenuItem: expected error ERROR_INVALID_MENU_HANDLE, got: %d\n", GetLastError());
1917
1918 ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE),
1919 "HiliteMenuItem: Item 2 is hilited\n");
1920
1921 /* either MF_HILITE or MF_UNHILITE *and* MF_BYCOMMAND or MF_BYPOSITION need to be set */
1922
1923 SetLastError(0xdeadbeef);
1924 ok(HiliteMenuItem(hWnd, hPopupMenu, 1, MF_BYPOSITION),
1925 "HiliteMenuItem: call should have succeeded.\n");
1926 ok(GetLastError() == 0xdeadbeef,
1927 "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
1928
1929 ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE),
1930 "HiliteMenuItem: Item 2 is hilited\n");
1931
1932 SetLastError(0xdeadbeef);
1933 todo_wine
1934 {
1935 ok(HiliteMenuItem(hWnd, hPopupMenu, 1, MF_HILITE),
1936 "HiliteMenuItem: call should have succeeded.\n");
1937 }
1938 ok(GetLastError() == 0xdeadbeef,
1939 "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
1940
1941 ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE),
1942 "HiliteMenuItem: Item 2 is hilited\n");
1943
1944 /* hilite a menu item (by position) */
1945
1946 SetLastError(0xdeadbeef);
1947 ok(HiliteMenuItem(hWnd, hPopupMenu, 1, MF_HILITE | MF_BYPOSITION),
1948 "HiliteMenuItem: call should not have failed.\n");
1949 ok(GetLastError() == 0xdeadbeef,
1950 "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
1951
1952 todo_wine
1953 {
1954 ok(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE,
1955 "HiliteMenuItem: Item 2 is not hilited\n");
1956 }
1957
1958 /* unhilite a menu item (by position) */
1959
1960 SetLastError(0xdeadbeef);
1961 ok(HiliteMenuItem(hWnd, hPopupMenu, 1, MF_UNHILITE | MF_BYPOSITION),
1962 "HiliteMenuItem: call should not have failed.\n");
1963 ok(GetLastError() == 0xdeadbeef,
1964 "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
1965
1966 ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE),
1967 "HiliteMenuItem: Item 2 is hilited\n");
1968
1969 /* hilite a menu item (by command) */
1970
1971 SetLastError(0xdeadbeef);
1972 ok(HiliteMenuItem(hWnd, hPopupMenu, 103, MF_HILITE | MF_BYCOMMAND),
1973 "HiliteMenuItem: call should not have failed.\n");
1974 ok(GetLastError() == 0xdeadbeef,
1975 "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
1976
1977 todo_wine
1978 {
1979 ok(GetMenuState(hPopupMenu, 2, MF_BYPOSITION) & MF_HILITE,
1980 "HiliteMenuItem: Item 3 is not hilited\n");
1981 }
1982
1983 /* unhilite a menu item (by command) */
1984
1985 SetLastError(0xdeadbeef);
1986 ok(HiliteMenuItem(hWnd, hPopupMenu, 103, MF_UNHILITE | MF_BYCOMMAND),
1987 "HiliteMenuItem: call should not have failed.\n");
1988 ok(GetLastError() == 0xdeadbeef,
1989 "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
1990
1991 ok(!(GetMenuState(hPopupMenu, 2, MF_BYPOSITION) & MF_HILITE),
1992 "HiliteMenuItem: Item 3 is hilited\n");
1993
1994 DestroyWindow(hWnd);
1995 }
1996
1997 static void check_menu_items(HMENU hmenu, UINT checked_cmd, UINT checked_type,
1998 UINT checked_state)
1999 {
2000 UINT i, count;
2001
2002 count = GetMenuItemCount(hmenu);
2003
2004 for (i = 0; i < count; i++)
2005 {
2006 BOOL ret;
2007 MENUITEMINFO mii;
2008
2009 memset(&mii, 0, sizeof(mii));
2010 mii.cbSize = sizeof(mii);
2011 mii.fMask = MIIM_FTYPE | MIIM_STATE | MIIM_ID | MIIM_SUBMENU;
2012 ret = GetMenuItemInfo(hmenu, i, TRUE, &mii);
2013 ok(ret, "GetMenuItemInfo(%u) failed\n", i);
2014 #if 0
2015 trace("item #%u: fType %04x, fState %04x, wID %u, hSubMenu %p\n",
2016 i, mii.fType, mii.fState, mii.wID, mii.hSubMenu);
2017 #endif
2018 if (mii.hSubMenu)
2019 {
2020 ok((HMENU)mii.wID == mii.hSubMenu, "id %u: wID should be equal to hSubMenu\n", checked_cmd);
2021 check_menu_items(mii.hSubMenu, checked_cmd, checked_type, checked_state);
2022 }
2023 else
2024 {
2025 if (mii.wID == checked_cmd)
2026 {
2027 ok(mii.fType == checked_type, "id %u: expected fType %04x, got %04x\n", checked_cmd, checked_type, mii.fType);
2028 ok(mii.fState == checked_state, "id %u: expected fState %04x, got %04x\n", checked_cmd, checked_state, mii.fState);
2029 ok(mii.wID != 0, "id %u: not expected wID 0\n", checked_cmd);
2030 }
2031 else
2032 {
2033 ok(mii.fType != MFT_RADIOCHECK, "id %u: not expected fType MFT_RADIOCHECK on cmd %u\n", checked_cmd, mii.wID);
2034
2035 if (mii.fType == MFT_SEPARATOR)
2036 {
2037 ok(mii.fState == MFS_GRAYED, "id %u: expected fState MFS_GRAYED, got %04x\n", checked_cmd, mii.fState);
2038 ok(mii.wID == 0, "id %u: expected wID 0, got %u\n", checked_cmd, mii.wID);
2039 }
2040 else
2041 {
2042 ok(mii.fState == 0, "id %u: expected fState 0, got %04x\n", checked_cmd, mii.fState);
2043 ok(mii.wID != 0, "id %u: not expected wID 0\n", checked_cmd);
2044 }
2045 }
2046 }
2047 }
2048 }
2049
2050 static void clear_ftype_and_state(HMENU hmenu, UINT id, UINT flags)
2051 {
2052 BOOL ret;
2053 MENUITEMINFO mii;
2054
2055 memset(&mii, 0, sizeof(mii));
2056 mii.cbSize = sizeof(mii);
2057 mii.fMask = MIIM_FTYPE | MIIM_STATE;
2058 ret = SetMenuItemInfo(hmenu, id, (flags & MF_BYPOSITION) != 0, &mii);
2059 ok(ret, "SetMenuItemInfo(%u) failed\n", id);
2060 }
2061
2062 static void test_CheckMenuRadioItem(void)
2063 {
2064 BOOL ret;
2065 HMENU hmenu;
2066
2067 hmenu = LoadMenu(GetModuleHandle(0), MAKEINTRESOURCE(1));
2068 assert(hmenu != 0);
2069
2070 check_menu_items(hmenu, -1, 0, 0);
2071
2072 ret = CheckMenuRadioItem(hmenu, 100, 100, 100, MF_BYCOMMAND);
2073 ok(ret, "CheckMenuRadioItem failed\n");
2074 check_menu_items(hmenu, 100, MFT_RADIOCHECK, MFS_CHECKED);
2075
2076 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
2077 ret = CheckMenuRadioItem(hmenu, 100, 100, -1, MF_BYCOMMAND);
2078 ok(!ret, "CheckMenuRadioItem should return FALSE\n");
2079 check_menu_items(hmenu, 100, MFT_RADIOCHECK, 0);
2080
2081 /* clear check */
2082 clear_ftype_and_state(hmenu, 100, MF_BYCOMMAND);
2083 check_menu_items(hmenu, -1, 0, 0);
2084
2085 /* first and checked items are on different menus */
2086 ret = CheckMenuRadioItem(hmenu, 0, 300, 202, MF_BYCOMMAND);
2087 ok(!ret, "CheckMenuRadioItem should return FALSE\n");
2088 check_menu_items(hmenu, -1, 0, 0);
2089
2090 ret = CheckMenuRadioItem(hmenu, 200, 300, 202, MF_BYCOMMAND);
2091 ok(ret, "CheckMenuRadioItem failed\n");
2092 check_menu_items(hmenu, 202, MFT_RADIOCHECK, MFS_CHECKED);
2093
2094 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
2095 ret = CheckMenuRadioItem(hmenu, 202, 202, -1, MF_BYCOMMAND);
2096 ok(!ret, "CheckMenuRadioItem should return FALSE\n");
2097 check_menu_items(hmenu, 202, MFT_RADIOCHECK, 0);
2098
2099 /* clear check */
2100 clear_ftype_and_state(hmenu, 202, MF_BYCOMMAND);
2101 check_menu_items(hmenu, -1, 0, 0);
2102
2103 /* just for fun, try to check separator */
2104 ret = CheckMenuRadioItem(hmenu, 0, 300, 0, MF_BYCOMMAND);
2105 ok(!ret, "CheckMenuRadioItem should return FALSE\n");
2106 check_menu_items(hmenu, -1, 0, 0);
2107 }
2108
2109 static void test_menu_resource_layout(void)
2110 {
2111 static const struct
2112 {
2113 MENUITEMTEMPLATEHEADER mith;
2114 WORD data[14];
2115 } menu_template =
2116 {
2117 { 0, 0 }, /* versionNumber, offset */
2118 {
2119 /* mtOption, mtID, mtString[] '\0' terminated */
2120 MF_STRING, 1, 'F', 0,
2121 MF_STRING, 2, 0,
2122 MF_SEPARATOR, 3, 0,
2123 /* MF_SEPARATOR, 4, 'S', 0, FIXME: Wine ignores 'S' */
2124 MF_STRING|MF_GRAYED|MF_END, 5, 'E', 0
2125 }
2126 };
2127 static const struct
2128 {
2129 UINT type, state, id;
2130 const char *str;
2131 } menu_data[] =
2132 {
2133 { MF_STRING, MF_ENABLED, 1, "F" },
2134 { MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 2, "" },
2135 { MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 3, "" },
2136 /*{ MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 4, "S" }, FIXME: Wine ignores 'S'*/
2137 { MF_STRING, MF_GRAYED, 5, "E" },
2138 { MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 6, "" },
2139 { MF_STRING, MF_ENABLED, 7, "" },
2140 { MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 8, "" }
2141 };
2142 HMENU hmenu;
2143 UINT count, i;
2144 BOOL ret;
2145
2146 hmenu = LoadMenuIndirect(&menu_template);
2147 ok(hmenu != 0, "LoadMenuIndirect error %u\n", GetLastError());
2148
2149 ret = AppendMenu(hmenu, MF_STRING, 6, NULL);
2150 ok(ret, "AppendMenu failed\n");
2151 ret = AppendMenu(hmenu, MF_STRING, 7, "\0");
2152 ok(ret, "AppendMenu failed\n");
2153 ret = AppendMenu(hmenu, MF_SEPARATOR, 8, "separator");
2154 ok(ret, "AppendMenu failed\n");
2155
2156 count = GetMenuItemCount(hmenu);
2157 ok(count == sizeof(menu_data)/sizeof(menu_data[0]),
2158 "expected %u menu items, got %u\n",
2159 (UINT)(sizeof(menu_data)/sizeof(menu_data[0])), count);
2160
2161 for (i = 0; i < count; i++)
2162 {
2163 char buf[20];
2164 MENUITEMINFO mii;
2165
2166 memset(&mii, 0, sizeof(mii));
2167 mii.cbSize = sizeof(mii);
2168 mii.dwTypeData = buf;
2169 mii.cch = sizeof(buf);
2170 mii.fMask = MIIM_FTYPE | MIIM_STATE | MIIM_ID | MIIM_STRING;
2171 ret = GetMenuItemInfo(hmenu, i, TRUE, &mii);
2172 ok(ret, "GetMenuItemInfo(%u) failed\n", i);
2173 #if 0
2174 trace("item #%u: fType %04x, fState %04x, wID %u, dwTypeData %s\n",
2175 i, mii.fType, mii.fState, mii.wID, (LPCSTR)mii.dwTypeData);
2176 #endif
2177 ok(mii.fType == menu_data[i].type,
2178 "%u: expected fType %04x, got %04x\n", i, menu_data[i].type, mii.fType);
2179 ok(mii.fState == menu_data[i].state,
2180 "%u: expected fState %04x, got %04x\n", i, menu_data[i].state, mii.fState);
2181 ok(mii.wID == menu_data[i].id,
2182 "%u: expected wID %04x, got %04x\n", i, menu_data[i].id, mii.wID);
2183 ok(mii.cch == strlen(menu_data[i].str),
2184 "%u: expected cch %u, got %u\n", i, (UINT)strlen(menu_data[i].str), mii.cch);
2185 ok(!strcmp((LPCSTR)mii.dwTypeData, menu_data[i].str),
2186 "%u: expected dwTypeData %s, got %s\n", i, menu_data[i].str, (LPCSTR)mii.dwTypeData);
2187 }
2188
2189 DestroyMenu(hmenu);
2190 }
2191
2192 struct menu_data
2193 {
2194 UINT type, id;
2195 const char *str;
2196 };
2197
2198 static HMENU create_menu_from_data(const struct menu_data *item, INT item_count)
2199 {
2200 HMENU hmenu;
2201 INT i;
2202 BOOL ret;
2203
2204 hmenu = CreateMenu();
2205 assert(hmenu != 0);
2206
2207 for (i = 0; i < item_count; i++)
2208 {
2209 SetLastError(0xdeadbeef);
2210 ret = AppendMenu(hmenu, item[i].type, item[i].id, item[i].str);
2211 ok(ret, "%d: AppendMenu(%04x, %04x, %p) error %u\n",
2212 i, item[i].type, item[i].id, item[i].str, GetLastError());
2213 }
2214 return hmenu;
2215 }
2216
2217 static void compare_menu_data(HMENU hmenu, const struct menu_data *item, INT item_count)
2218 {
2219 INT count, i;
2220 BOOL ret;
2221
2222 count = GetMenuItemCount(hmenu);
2223 ok(count == item_count, "expected %d, got %d menu items\n", count, item_count);
2224
2225 for (i = 0; i < count; i++)
2226 {
2227 char buf[20];
2228 MENUITEMINFO mii;
2229
2230 memset(&mii, 0, sizeof(mii));
2231 mii.cbSize = sizeof(mii);
2232 mii.dwTypeData = buf;
2233 mii.cch = sizeof(buf);
2234 mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING | MIIM_BITMAP;
2235 ret = GetMenuItemInfo(hmenu, i, TRUE, &mii);
2236 ok(ret, "GetMenuItemInfo(%u) failed\n", i);
2237 #if 0
2238 trace("item #%u: fType %04x, fState %04x, wID %04x, hbmp %p\n",
2239 i, mii.fType, mii.fState, mii.wID, mii.hbmpItem);
2240 #endif
2241 ok(mii.fType == item[i].type,
2242 "%u: expected fType %04x, got %04x\n", i, item[i].type, mii.fType);
2243 ok(mii.wID == item[i].id,
2244 "%u: expected wID %04x, got %04x\n", i, item[i].id, mii.wID);
2245 if (item[i].type & (MF_BITMAP | MF_SEPARATOR))
2246 {
2247 /* For some reason Windows sets high word to not 0 for
2248 * not "magic" ids.
2249 */
2250 ok(LOWORD(mii.hbmpItem) == LOWORD(item[i].str),
2251 "%u: expected hbmpItem %p, got %p\n", i, item[i].str, mii.hbmpItem);
2252 }
2253 else
2254 {
2255 ok(mii.cch == strlen(item[i].str),
2256 "%u: expected cch %u, got %u\n", i, (UINT)strlen(item[i].str), mii.cch);
2257 ok(!strcmp((LPCSTR)mii.dwTypeData, item[i].str),
2258 "%u: expected dwTypeData %s, got %s\n", i, item[i].str, (LPCSTR)mii.dwTypeData);
2259 }
2260 }
2261 }
2262
2263 static void test_InsertMenu(void)
2264 {
2265 /* Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2266 * regardless of their id.
2267 */
2268 static const struct menu_data in1[] =
2269 {
2270 { MF_STRING, 1, "File" },
2271 { MF_BITMAP|MF_HELP, SC_CLOSE, MAKEINTRESOURCE(1) },
2272 { MF_STRING|MF_HELP, 2, "Help" }
2273 };
2274 static const struct menu_data out1[] =
2275 {
2276 { MF_STRING, 1, "File" },
2277 { MF_STRING|MF_HELP, 2, "Help" },
2278 { MF_BITMAP|MF_HELP, SC_CLOSE, MAKEINTRESOURCE(1) }
2279 };
2280 static const struct menu_data in2[] =
2281 {
2282 { MF_STRING, 1, "File" },
2283 { MF_BITMAP|MF_HELP, SC_CLOSE, MAKEINTRESOURCE(100) },
2284 { MF_STRING|MF_HELP, 2, "Help" }
2285 };
2286 static const struct menu_data out2[] =
2287 {
2288 { MF_STRING, 1, "File" },
2289 { MF_BITMAP|MF_HELP, SC_CLOSE, MAKEINTRESOURCE(100) },
2290 { MF_STRING|MF_HELP, 2, "Help" }
2291 };
2292 static const struct menu_data in3[] =
2293 {
2294 { MF_STRING, 1, "File" },
2295 { MF_SEPARATOR|MF_HELP, SC_CLOSE, MAKEINTRESOURCE(1) },
2296 { MF_STRING|MF_HELP, 2, "Help" }
2297 };
2298 static const struct menu_data out3[] =
2299 {
2300 { MF_STRING, 1, "File" },
2301 { MF_SEPARATOR|MF_HELP, SC_CLOSE, MAKEINTRESOURCE(0) },
2302 { MF_STRING|MF_HELP, 2, "Help" },
2303 };
2304 static const struct menu_data in4[] =
2305 {
2306 { MF_STRING, 1, "File" },
2307 { MF_BITMAP|MF_HELP, 1, MAKEINTRESOURCE(1) },
2308 { MF_STRING|MF_HELP, 2, "Help" }
2309 };
2310 static const struct menu_data out4[] =
2311 {
2312 { MF_STRING, 1, "File" },
2313 { MF_STRING|MF_HELP, 2, "Help" },
2314 { MF_BITMAP|MF_HELP, 1, MAKEINTRESOURCE(1) }
2315 };
2316 HMENU hmenu;
2317
2318 #define create_menu(a) create_menu_from_data((a), sizeof(a)/sizeof((a)[0]))
2319 #define compare_menu(h, a) compare_menu_data((h), (a), sizeof(a)/sizeof((a)[0]))
2320
2321 hmenu = create_menu(in1);
2322 compare_menu(hmenu, out1);
2323 DestroyMenu(hmenu);
2324
2325 hmenu = create_menu(in2);
2326 compare_menu(hmenu, out2);
2327 DestroyMenu(hmenu);
2328
2329 hmenu = create_menu(in3);
2330 compare_menu(hmenu, out3);
2331 DestroyMenu(hmenu);
2332
2333 hmenu = create_menu(in4);
2334 compare_menu(hmenu, out4);
2335 DestroyMenu(hmenu);
2336
2337 #undef create_menu
2338 #undef compare_menu
2339 }
2340
2341 START_TEST(menu)
2342 {
2343 init_function_pointers();
2344
2345 register_menu_check_class();
2346
2347 test_menu_locked_by_window();
2348 test_menu_ownerdraw();
2349 test_menu_add_string();
2350 test_menu_iteminfo();
2351 test_menu_search_bycommand();
2352 test_menu_bmp_and_string();
2353
2354 if( !pSendInput)
2355 skip("SendInput is not available\n");
2356 else
2357 test_menu_input();
2358 test_menu_flags();
2359
2360 test_menu_hilitemenuitem();
2361 test_CheckMenuRadioItem();
2362 test_menu_resource_layout();
2363 test_InsertMenu();
2364 }