push 372bd5ac1f0687be3799022a01e6565204ccd49c
[wine/hacks.git] / dlls / user32 / tests / menu.c
blobb270a25737928b650e37eb3bfc040c3ead0c3133
1 /*
2 * Unit tests for menus
4 * Copyright 2005 Robert Shearman
5 * Copyright 2007 Dmitry Timoshkov
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.
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.
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
22 #define _WIN32_WINNT 0x0501
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <assert.h>
29 #define OEMRESOURCE /* For OBM_MNARROW */
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wingdi.h"
34 #include "winuser.h"
36 #include "wine/test.h"
38 static ATOM atomMenuCheckClass;
40 static BOOL (WINAPI *pSetMenuInfo)(HMENU,LPCMENUINFO);
41 static BOOL (WINAPI *pGetMenuInfo)(HMENU,LPCMENUINFO);
43 static LRESULT WINAPI menu_check_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
45 switch (msg)
47 case WM_ENTERMENULOOP:
48 /* mark window as having entered menu loop */
49 SetWindowLongPtr(hwnd, GWLP_USERDATA, TRUE);
50 /* exit menu modal loop
51 * ( A SendMessage does not work on NT3.51 here ) */
52 return PostMessage(hwnd, WM_CANCELMODE, 0, 0);
54 return DefWindowProc(hwnd, msg, wparam, lparam);
57 /* The MSVC headers ignore our NONAMELESSUNION requests so we have to define
58 * our own type */
59 typedef struct
61 DWORD type;
62 union
64 MOUSEINPUT mi;
65 KEYBDINPUT ki;
66 HARDWAREINPUT hi;
67 } u;
68 } TEST_INPUT;
70 /* globals to communicate between test and wndproc */
72 static BOOL bMenuVisible;
73 static HMENU hMenus[4];
75 #define MOD_SIZE 10
76 #define MOD_NRMENUS 8
78 /* menu texts with their sizes */
79 static struct {
80 LPCSTR text;
81 SIZE size; /* size of text up to any \t */
82 SIZE sc_size; /* size of the short-cut */
83 } MOD_txtsizes[] = {
84 { "Pinot &Noir" },
85 { "&Merlot\bF4" },
86 { "Shira&z\tAlt+S" },
87 { "" },
88 { NULL }
91 static unsigned int MOD_maxid;
92 static RECT MOD_rc[MOD_NRMENUS];
93 static int MOD_avec, MOD_hic;
94 static int MOD_odheight;
95 static SIZE MODsizes[MOD_NRMENUS]= { {MOD_SIZE, MOD_SIZE},{MOD_SIZE, MOD_SIZE},
96 {MOD_SIZE, MOD_SIZE},{MOD_SIZE, MOD_SIZE}};
97 static int MOD_GotDrawItemMsg = FALSE;
98 /* wndproc used by test_menu_ownerdraw() */
99 static LRESULT WINAPI menu_ownerdraw_wnd_proc(HWND hwnd, UINT msg,
100 WPARAM wparam, LPARAM lparam)
102 switch (msg)
104 case WM_MEASUREITEM:
106 MEASUREITEMSTRUCT* pmis = (MEASUREITEMSTRUCT*)lparam;
107 if( winetest_debug)
108 trace("WM_MEASUREITEM received data %lx size %dx%d\n",
109 pmis->itemData, pmis->itemWidth, pmis->itemHeight);
110 MOD_odheight = pmis->itemHeight;
111 pmis->itemWidth = MODsizes[pmis->itemData].cx;
112 pmis->itemHeight = MODsizes[pmis->itemData].cy;
113 return TRUE;
115 case WM_DRAWITEM:
117 DRAWITEMSTRUCT * pdis;
118 TEXTMETRIC tm;
119 HPEN oldpen;
120 char chrs[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
121 SIZE sz;
122 int i;
123 pdis = (DRAWITEMSTRUCT *) lparam;
124 if( winetest_debug) {
125 RECT rc;
126 GetMenuItemRect( hwnd, (HMENU)pdis->hwndItem, pdis->itemData ,&rc);
127 trace("WM_DRAWITEM received hwnd %p hmenu %p itemdata %ld item %d rc %d,%d-%d,%d itemrc: %d,%d-%d,%d\n",
128 hwnd, (HMENU)pdis->hwndItem, pdis->itemData,
129 pdis->itemID, pdis->rcItem.left, pdis->rcItem.top,
130 pdis->rcItem.right,pdis->rcItem.bottom,
131 rc.left,rc.top,rc.right,rc.bottom);
132 oldpen=SelectObject( pdis->hDC, GetStockObject(
133 pdis->itemState & ODS_SELECTED ? WHITE_PEN :BLACK_PEN));
134 Rectangle( pdis->hDC, pdis->rcItem.left,pdis->rcItem.top,
135 pdis->rcItem.right,pdis->rcItem.bottom );
136 SelectObject( pdis->hDC, oldpen);
138 /* calculate widths of some menu texts */
139 if( ! MOD_txtsizes[0].size.cx)
140 for(i = 0; MOD_txtsizes[i].text; i++) {
141 char buf[100], *p;
142 RECT rc={0,0,0,0};
143 strcpy( buf, MOD_txtsizes[i].text);
144 if( ( p = strchr( buf, '\t'))) {
145 *p = '\0';
146 DrawText( pdis->hDC, p + 1, -1, &rc,
147 DT_SINGLELINE|DT_CALCRECT);
148 MOD_txtsizes[i].sc_size.cx= rc.right - rc.left;
149 MOD_txtsizes[i].sc_size.cy= rc.bottom - rc.top;
151 DrawText( pdis->hDC, buf, -1, &rc,
152 DT_SINGLELINE|DT_CALCRECT);
153 MOD_txtsizes[i].size.cx= rc.right - rc.left;
154 MOD_txtsizes[i].size.cy= rc.bottom - rc.top;
157 if( pdis->itemData > MOD_maxid) return TRUE;
158 /* store the rectangl */
159 MOD_rc[pdis->itemData] = pdis->rcItem;
160 /* calculate average character width */
161 GetTextExtentPoint( pdis->hDC, chrs, 52, &sz );
162 MOD_avec = (sz.cx + 26)/52;
163 GetTextMetrics( pdis->hDC, &tm);
164 MOD_hic = tm.tmHeight;
165 MOD_GotDrawItemMsg = TRUE;
166 return TRUE;
168 case WM_ENTERIDLE:
170 PostMessage(hwnd, WM_CANCELMODE, 0, 0);
171 return TRUE;
175 return DefWindowProc(hwnd, msg, wparam, lparam);
178 static void register_menu_check_class(void)
180 WNDCLASS wc =
183 menu_check_wnd_proc,
186 GetModuleHandle(NULL),
187 NULL,
188 LoadCursor(NULL, IDC_ARROW),
189 (HBRUSH)(COLOR_BTNFACE+1),
190 NULL,
191 TEXT("WineMenuCheck"),
194 atomMenuCheckClass = RegisterClass(&wc);
197 /* demonstrates that windows locks the menu object so that it is still valid
198 * even after a client calls DestroyMenu on it */
199 static void test_menu_locked_by_window(void)
201 BOOL ret;
202 HMENU hmenu;
203 HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
204 WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200,
205 NULL, NULL, NULL, NULL);
206 ok(hwnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
207 hmenu = CreateMenu();
208 ok(hmenu != NULL, "CreateMenu failed with error %d\n", GetLastError());
209 ret = InsertMenu(hmenu, 0, MF_STRING, 0, TEXT("&Test"));
210 ok(ret, "InsertMenu failed with error %d\n", GetLastError());
211 ret = SetMenu(hwnd, hmenu);
212 ok(ret, "SetMenu failed with error %d\n", GetLastError());
213 ret = DestroyMenu(hmenu);
214 ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
216 ret = DrawMenuBar(hwnd);
217 todo_wine {
218 ok(ret, "DrawMenuBar failed with error %d\n", GetLastError());
220 ret = IsMenu(GetMenu(hwnd));
221 ok(!ret, "Menu handle should have been destroyed\n");
223 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
224 /* did we process the WM_INITMENU message? */
225 ret = GetWindowLongPtr(hwnd, GWLP_USERDATA);
226 todo_wine {
227 ok(ret, "WM_INITMENU should have been sent\n");
230 DestroyWindow(hwnd);
233 static void test_menu_ownerdraw(void)
235 int i,j,k;
236 BOOL ret;
237 HMENU hmenu;
238 LONG leftcol;
239 HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
240 WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200,
241 NULL, NULL, NULL, NULL);
242 ok(hwnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
243 if( !hwnd) return;
244 SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG)menu_ownerdraw_wnd_proc);
245 hmenu = CreatePopupMenu();
246 ok(hmenu != NULL, "CreateMenu failed with error %d\n", GetLastError());
247 if( !hmenu) { DestroyWindow(hwnd);return;}
248 k=0;
249 for( j=0;j<2;j++) /* create columns */
250 for(i=0;i<2;i++) { /* create rows */
251 ret = AppendMenu( hmenu, MF_OWNERDRAW |
252 (i==0 ? MF_MENUBREAK : 0), k, (LPCTSTR) k);
253 k++;
254 ok( ret, "AppendMenu failed for %d\n", k-1);
256 MOD_maxid = k-1;
257 assert( k <= sizeof(MOD_rc)/sizeof(RECT));
258 /* display the menu */
259 ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
261 /* columns have a 4 pixel gap between them */
262 ok( MOD_rc[0].right + 4 == MOD_rc[2].left,
263 "item rectangles are not separated by 4 pixels space\n");
264 /* height should be what the MEASUREITEM message has returned */
265 ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE,
266 "menu item has wrong height: %d should be %d\n",
267 MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE);
268 /* no gaps between the rows */
269 ok( MOD_rc[0].bottom - MOD_rc[1].top == 0,
270 "There should not be a space between the rows, gap is %d\n",
271 MOD_rc[0].bottom - MOD_rc[1].top);
272 /* test the correct value of the item height that was sent
273 * by the WM_MEASUREITEM message */
274 ok( MOD_odheight == HIWORD( GetDialogBaseUnits()) || /* WinNT,2k,XP */
275 MOD_odheight == MOD_hic, /* Win95,98,ME */
276 "Wrong height field in MEASUREITEMSTRUCT, expected %d or %d actual %d\n",
277 HIWORD( GetDialogBaseUnits()), MOD_hic, MOD_odheight);
278 /* test what MF_MENUBREAK did at the first position. Also show
279 * that an MF_SEPARATOR is ignored in the height calculation. */
280 leftcol= MOD_rc[0].left;
281 ModifyMenu( hmenu, 0, MF_BYCOMMAND| MF_OWNERDRAW| MF_SEPARATOR, 0, 0);
282 /* display the menu */
283 ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
284 /* left should be 4 pixels less now */
285 ok( leftcol == MOD_rc[0].left + 4,
286 "columns should be 4 pixels to the left (actual %d).\n",
287 leftcol - MOD_rc[0].left);
288 /* test width */
289 ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE,
290 "width of owner drawn menu item is wrong. Got %d expected %d\n",
291 MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE);
292 /* and height */
293 ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE,
294 "Height is incorrect. Got %d expected %d\n",
295 MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE);
297 /* test width/height of an ownerdraw menu bar as well */
298 ret = DestroyMenu(hmenu);
299 ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
300 hmenu = CreateMenu();
301 ok(hmenu != NULL, "CreateMenu failed with error %d\n", GetLastError());
302 if( !hmenu) { DestroyWindow(hwnd);return;}
303 MOD_maxid=1;
304 for(i=0;i<2;i++) {
305 ret = AppendMenu( hmenu, MF_OWNERDRAW , i, 0);
306 ok( ret, "AppendMenu failed for %d\n", i);
308 ret = SetMenu( hwnd, hmenu);
309 UpdateWindow( hwnd); /* hack for wine to draw the window + menu */
310 ok(ret, "SetMenu failed with error %d\n", GetLastError());
311 /* test width */
312 ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE,
313 "width of owner drawn menu item is wrong. Got %d expected %d\n",
314 MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE);
315 /* test hight */
316 ok( MOD_rc[0].bottom - MOD_rc[0].top == GetSystemMetrics( SM_CYMENU) - 1,
317 "Height of owner drawn menu item is wrong. Got %d expected %d\n",
318 MOD_rc[0].bottom - MOD_rc[0].top, GetSystemMetrics( SM_CYMENU) - 1);
320 /* clean up */
321 ret = DestroyMenu(hmenu);
322 ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
323 DestroyWindow(hwnd);
326 /* helper for test_menu_bmp_and_string() */
327 static void test_mbs_help( int ispop, int hassub, int mnuopt,
328 HWND hwnd, int arrowwidth, int count, HBITMAP hbmp,
329 SIZE bmpsize, LPCSTR text, SIZE size, SIZE sc_size)
331 BOOL ret;
332 HMENU hmenu, submenu;
333 MENUITEMINFO mii={ sizeof( MENUITEMINFO )};
334 MENUINFO mi;
335 RECT rc;
336 CHAR text_copy[16];
337 int hastab, expect;
338 int failed = 0;
340 MOD_GotDrawItemMsg = FALSE;
341 mii.fMask = MIIM_FTYPE | MIIM_DATA | MIIM_STATE;
342 mii.fType = 0;
343 mii.fState = MF_CHECKED;
344 mii.dwItemData =0;
345 MODsizes[0] = bmpsize;
346 hastab = 0;
347 if( text ) {
348 char *p;
349 mii.fMask |= MIIM_STRING;
350 strcpy(text_copy, text);
351 mii.dwTypeData = text_copy; /* structure member declared non-const */
352 if( ( p = strchr( text, '\t'))) {
353 hastab = *(p + 1) ? 2 : 1;
356 /* tabs don't make sense in menubars */
357 if(hastab && !ispop) return;
358 if( hbmp) {
359 mii.fMask |= MIIM_BITMAP;
360 mii.hbmpItem = hbmp;
362 submenu = CreateMenu();
363 ok( submenu != 0, "CreateMenu failed with error %d\n", GetLastError());
364 if( ispop)
365 hmenu = CreatePopupMenu();
366 else
367 hmenu = CreateMenu();
368 ok( hmenu != 0, "Create{Popup}Menu failed with error %d\n", GetLastError());
369 if( hassub) {
370 mii.fMask |= MIIM_SUBMENU;
371 mii.hSubMenu = submenu;
373 if( mnuopt) {
374 mi.cbSize = sizeof(mi);
375 mi.fMask = MIM_STYLE;
376 pGetMenuInfo( hmenu, &mi);
377 mi.dwStyle |= mnuopt == 1 ? MNS_NOCHECK : MNS_CHECKORBMP;
378 ret = pSetMenuInfo( hmenu, &mi);
379 ok( ret, "SetMenuInfo failed with error %d\n", GetLastError());
381 ret = InsertMenuItem( hmenu, 0, FALSE, &mii);
382 ok( ret, "InsertMenuItem failed with error %d\n", GetLastError());
383 failed = !ret;
384 if( winetest_debug) {
385 HDC hdc=GetDC(hwnd);
386 RECT rc = {100, 50, 400, 70};
387 char buf[100];
389 sprintf( buf,"%d text \"%s\" mnuopt %d", count, text ? text: "(nil)", mnuopt);
390 FillRect( hdc, &rc, (HBRUSH) COLOR_WINDOW);
391 TextOut( hdc, 100, 50, buf, strlen( buf));
392 ReleaseDC( hwnd, hdc);
394 if(ispop)
395 ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
396 else {
397 ret = SetMenu( hwnd, hmenu);
398 ok(ret, "SetMenu failed with error %d\n", GetLastError());
399 DrawMenuBar( hwnd);
401 ret = GetMenuItemRect( hwnd, hmenu, 0, &rc);
402 /* check menu width */
403 if( ispop)
404 expect = ( text || hbmp ?
405 4 + (mnuopt != 1 ? GetSystemMetrics(SM_CXMENUCHECK) : 0)
406 : 0) +
407 arrowwidth + MOD_avec + (hbmp ? bmpsize.cx + 2 : 0) +
408 (text && hastab ? /* TAB space */
409 MOD_avec + ( hastab==2 ? sc_size.cx : 0) : 0) +
410 (text ? 2 + (text[0] ? size.cx :0): 0) ;
411 else
412 expect = !(text || hbmp) ? 0 :
413 ( hbmp ? (text ? 2:0) + bmpsize.cx : 0 ) +
414 (text ? 2 * MOD_avec + (text[0] ? size.cx :0): 0) ;
415 ok( rc.right - rc.left == expect,
416 "menu width wrong, got %d expected %d\n", rc.right - rc.left, expect);
417 failed = failed || !(rc.right - rc.left == expect);
418 /* check menu height */
419 if( ispop)
420 expect = max( ( !(text || hbmp) ? GetSystemMetrics( SM_CYMENUSIZE)/2 : 0),
421 max( (text ? max( 2 + size.cy, MOD_hic + 4) : 0),
422 (hbmp ? bmpsize.cy + 2 : 0)));
423 else
424 expect = ( !(text || hbmp) ? GetSystemMetrics( SM_CYMENUSIZE)/2 :
425 max( GetSystemMetrics( SM_CYMENU) - 1, (hbmp ? bmpsize.cy : 0)));
426 ok( rc.bottom - rc.top == expect,
427 "menu height wrong, got %d expected %d (%d)\n",
428 rc.bottom - rc.top, expect, GetSystemMetrics( SM_CYMENU));
429 failed = failed || !(rc.bottom - rc.top == expect);
430 if( hbmp == HBMMENU_CALLBACK && MOD_GotDrawItemMsg) {
431 /* check the position of the bitmap */
432 /* horizontal */
433 expect = ispop ? (4 + ( mnuopt ? 0 : GetSystemMetrics(SM_CXMENUCHECK)))
434 : 3;
435 ok( expect == MOD_rc[0].left,
436 "bitmap left is %d expected %d\n", MOD_rc[0].left, expect);
437 failed = failed || !(expect == MOD_rc[0].left);
438 /* vertical */
439 expect = (rc.bottom - rc.top - MOD_rc[0].bottom + MOD_rc[0].top) / 2;
440 ok( expect == MOD_rc[0].top,
441 "bitmap top is %d expected %d\n", MOD_rc[0].top, expect);
442 failed = failed || !(expect == MOD_rc[0].top);
444 /* if there was a failure, report details */
445 if( failed) {
446 trace("*** count %d text \"%s\" bitmap %p bmsize %d,%d textsize %d+%d,%d mnuopt %d hastab %d\n",
447 count, text ? text: "(nil)", hbmp, bmpsize.cx, bmpsize.cy,
448 size.cx, size.cy, sc_size.cx, mnuopt, hastab);
449 trace(" check %d,%d arrow %d avechar %d\n",
450 GetSystemMetrics(SM_CXMENUCHECK ),
451 GetSystemMetrics(SM_CYMENUCHECK ),arrowwidth, MOD_avec);
452 if( hbmp == HBMMENU_CALLBACK)
453 trace( " rc %d,%d-%d,%d bmp.rc %d,%d-%d,%d\n",
454 rc.left, rc.top, rc.top, rc.bottom, MOD_rc[0].left,
455 MOD_rc[0].top,MOD_rc[0].right, MOD_rc[0].bottom);
457 /* clean up */
458 ret = DestroyMenu(submenu);
459 ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
460 ret = DestroyMenu(hmenu);
461 ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
465 static void test_menu_bmp_and_string(void)
467 BYTE bmfill[300];
468 HBITMAP hbm_arrow;
469 BITMAP bm;
470 INT arrowwidth;
471 HWND hwnd;
472 int count, szidx, txtidx, bmpidx, hassub, mnuopt, ispop;
474 if( !pGetMenuInfo) return;
476 memset( bmfill, 0x55, sizeof( bmfill));
477 hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
478 WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200,
479 NULL, NULL, NULL, NULL);
480 hbm_arrow=LoadBitmap( 0, (CHAR*)OBM_MNARROW);
481 GetObject( hbm_arrow, sizeof(bm), &bm);
482 arrowwidth = bm.bmWidth;
484 ok(hwnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
485 if( !hwnd) return;
486 SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG)menu_ownerdraw_wnd_proc);
488 if( winetest_debug)
489 trace(" check %d,%d arrow %d avechar %d\n",
490 GetSystemMetrics(SM_CXMENUCHECK ),
491 GetSystemMetrics(SM_CYMENUCHECK ),arrowwidth, MOD_avec);
492 count = 0;
493 MOD_maxid = 0;
494 for( ispop=1; ispop >= 0; ispop--){
495 static SIZE bmsizes[]= {
496 {10,10},{38,38},{1,30},{55,5}};
497 for( szidx=0; szidx < sizeof( bmsizes) / sizeof( SIZE); szidx++) {
498 HBITMAP hbm = CreateBitmap( bmsizes[szidx].cx, bmsizes[szidx].cy,1,1,bmfill);
499 HBITMAP bitmaps[] = { HBMMENU_CALLBACK, hbm, NULL };
500 ok( (int)hbm, "CreateBitmap failed err %d\n", GetLastError());
501 for( txtidx = 0; txtidx < sizeof(MOD_txtsizes)/sizeof(MOD_txtsizes[0]); txtidx++) {
502 for( hassub = 0; hassub < 2 ; hassub++) { /* add submenu item */
503 for( mnuopt = 0; mnuopt < 3 ; mnuopt++){ /* test MNS_NOCHECK/MNS_CHECKORBMP */
504 for( bmpidx = 0; bmpidx <sizeof(bitmaps)/sizeof(HBITMAP); bmpidx++) {
505 /* no need to test NULL bitmaps of several sizes */
506 if( !bitmaps[bmpidx] && szidx > 0) continue;
507 if( !ispop && hassub) continue;
508 test_mbs_help( ispop, hassub, mnuopt,
509 hwnd, arrowwidth, ++count,
510 bitmaps[bmpidx],
511 bmsizes[szidx],
512 MOD_txtsizes[txtidx].text,
513 MOD_txtsizes[txtidx].size,
514 MOD_txtsizes[txtidx].sc_size);
519 DeleteObject( hbm);
522 /* clean up */
523 DestroyWindow(hwnd);
526 static void test_menu_add_string( void )
528 HMENU hmenu;
529 MENUITEMINFO info;
530 BOOL rc;
531 int ret;
533 char string[0x80];
534 char string2[0x80];
536 char strback[0x80];
537 WCHAR strbackW[0x80];
538 static CHAR blah[] = "blah";
539 static const WCHAR expectedString[] = {'D','u','m','m','y',' ','s','t','r','i','n','g', 0};
541 hmenu = CreateMenu();
543 memset( &info, 0, sizeof info );
544 info.cbSize = sizeof info;
545 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_ID;
546 info.dwTypeData = blah;
547 info.cch = 6;
548 info.dwItemData = 0;
549 info.wID = 1;
550 info.fState = 0;
551 InsertMenuItem(hmenu, 0, TRUE, &info );
553 memset( &info, 0, sizeof info );
554 info.cbSize = sizeof info;
555 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_DATA | MIIM_ID;
556 info.dwTypeData = string;
557 info.cch = sizeof string;
558 string[0] = 0;
559 GetMenuItemInfo( hmenu, 0, TRUE, &info );
561 ok( !strcmp( string, "blah" ), "menu item name differed\n");
563 /* Test combination of ownerdraw and strings with GetMenuItemString(A/W) */
564 strcpy(string, "Dummy string");
565 memset(&info, 0x00, sizeof(info));
566 info.cbSize= sizeof(MENUITEMINFO);
567 info.fMask= MIIM_FTYPE | MIIM_STRING; /* Set OwnerDraw + typeData */
568 info.fType= MFT_OWNERDRAW;
569 info.dwTypeData= string;
570 rc = InsertMenuItem( hmenu, 0, TRUE, &info );
571 ok (rc, "InsertMenuItem failed\n");
573 strcpy(string,"Garbage");
574 ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
575 ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
577 SetLastError(0xdeadbeef);
578 ret = GetMenuStringW( hmenu, 0, (WCHAR *)strbackW, 99, MF_BYPOSITION);
579 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
580 skip("GetMenuStringW is not implemented\n");
581 else
583 ok (ret, "GetMenuStringW on ownerdraw entry failed\n");
584 ok (!lstrcmpW( strbackW, expectedString ), "Menu text from Unicode version incorrect\n");
587 /* Just change ftype to string and see what text is stored */
588 memset(&info, 0x00, sizeof(info));
589 info.cbSize= sizeof(MENUITEMINFO);
590 info.fMask= MIIM_FTYPE; /* Set string type */
591 info.fType= MFT_STRING;
592 info.dwTypeData= (char *)0xdeadbeef;
593 rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
594 ok (rc, "SetMenuItemInfo failed\n");
596 /* Did we keep the old dwTypeData? */
597 ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
598 ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
600 /* Ensure change to bitmap type fails */
601 memset(&info, 0x00, sizeof(info));
602 info.cbSize= sizeof(MENUITEMINFO);
603 info.fMask= MIIM_FTYPE; /* Set as bitmap type */
604 info.fType= MFT_BITMAP;
605 info.dwTypeData= (char *)0xdeadbee2;
606 rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
607 ok (!rc, "SetMenuItemInfo unexpectedly worked\n");
609 /* Just change ftype back and ensure data hasn't been freed */
610 info.fType= MFT_OWNERDRAW; /* Set as ownerdraw type */
611 info.dwTypeData= (char *)0xdeadbee3;
612 rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
613 ok (rc, "SetMenuItemInfo failed\n");
615 /* Did we keep the old dwTypeData? */
616 ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
617 ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
619 /* Just change string value (not type) */
620 memset(&info, 0x00, sizeof(info));
621 info.cbSize= sizeof(MENUITEMINFO);
622 info.fMask= MIIM_STRING; /* Set typeData */
623 strcpy(string2, "string2");
624 info.dwTypeData= string2;
625 rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
626 ok (rc, "SetMenuItemInfo failed\n");
628 ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
629 ok (!strcmp( strback, "string2" ), "Menu text from Ansi version incorrect\n");
631 /* crashes with wine 0.9.5 */
632 memset(&info, 0x00, sizeof(info));
633 info.cbSize= sizeof(MENUITEMINFO);
634 info.fMask= MIIM_FTYPE | MIIM_STRING; /* Set OwnerDraw + typeData */
635 info.fType= MFT_OWNERDRAW;
636 rc = InsertMenuItem( hmenu, 0, TRUE, &info );
637 ok (rc, "InsertMenuItem failed\n");
638 ok (!GetMenuString( hmenu, 0, NULL, 0, MF_BYPOSITION),
639 "GetMenuString on ownerdraw entry succeeded.\n");
640 SetLastError(0xdeadbeef);
641 ret = GetMenuStringW( hmenu, 0, NULL, 0, MF_BYPOSITION);
642 if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
643 skip("GetMenuStringW is not implemented\n");
644 else
645 ok (!ret, "GetMenuStringW on ownerdraw entry succeeded.\n");
647 DestroyMenu( hmenu );
650 /* define building blocks for the menu item info tests */
651 static int strncmpW( const WCHAR *str1, const WCHAR *str2, int n )
653 if (n <= 0) return 0;
654 while ((--n > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; }
655 return *str1 - *str2;
658 static WCHAR *strcpyW( WCHAR *dst, const WCHAR *src )
660 WCHAR *p = dst;
661 while ((*p++ = *src++));
662 return dst;
666 #define DMIINFF( i, e, field)\
667 ok((int)((i)->field)==(int)((e)->field) || (int)((i)->field)==(0xffff & (int)((e)->field)), \
668 "%s got 0x%x expected 0x%x\n", #field, (int)((i)->field), (int)((e)->field));
670 #define DUMPMIINF(s,i,e)\
672 DMIINFF( i, e, fMask)\
673 DMIINFF( i, e, fType)\
674 DMIINFF( i, e, fState)\
675 DMIINFF( i, e, wID)\
676 DMIINFF( i, e, hSubMenu)\
677 DMIINFF( i, e, hbmpChecked)\
678 DMIINFF( i, e, hbmpUnchecked)\
679 DMIINFF( i, e, dwItemData)\
680 DMIINFF( i, e, dwTypeData)\
681 DMIINFF( i, e, cch)\
682 if( s==sizeof(MENUITEMINFOA)) DMIINFF( i, e, hbmpItem)\
685 /* insert menu item */
686 #define TMII_INSMI( a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,n1,\
687 eret1)\
689 MENUITEMINFOA info1=a1 b1,c1,d1,e1,f1,(void*)g1,(void*)h1,(void*)i1,j1,(void*)k1,l1,(void*)m1 n1;\
690 HMENU hmenu = CreateMenu();\
691 BOOL ret, stop = FALSE;\
692 SetLastError( 0xdeadbeef);\
693 if(ansi)strcpy( string, init);\
694 else strcpyW( (WCHAR*)string, (WCHAR*)init);\
695 if( ansi) ret = InsertMenuItemA(hmenu, 0, TRUE, &info1 );\
696 else ret = InsertMenuItemW(hmenu, 0, TRUE, (MENUITEMINFOW*)&info1 );\
697 if( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)\
699 skip("InsertMenuItem%s not implemented\n", ansi ? "A" : "W");\
700 break;\
702 if( !(eret1)) { ok( (eret1)==ret,"InsertMenuItem should have failed.\n");\
703 stop = TRUE;\
704 } else ok( (eret1)==ret,"InsertMenuItem failed, err %d\n",GetLastError());\
707 /* GetMenuItemInfo + GetMenuString */
708 #define TMII_GMII( a2,b2,c2,d2,e2,f2,g2,h2,i2,j2,k2,l2,m2,n2,\
709 a3,b3,c3,d3,e3,f3,g3,h3,i3,j3,k3,l3,m3,n3,\
710 expname, eret2, eret3)\
712 MENUITEMINFOA info2A=a2 b2,c2,d2,e2,f2,(void*)g2,(void*)h2,(void*)i2,j2,(void*)k2,l2,(void*)m2 n2;\
713 MENUITEMINFOA einfoA=a3 b3,c3,d3,e3,f3,(void*)g3,(void*)h3,(void*)i3,j3,(void*)k3,l3,(void*)m3 n3;\
714 MENUITEMINFOA *info2 = &info2A;\
715 MENUITEMINFOA *einfo = &einfoA;\
716 MENUITEMINFOW *info2W = (MENUITEMINFOW *)&info2A;\
717 if( !stop) {\
718 SetLastError( 0xdeadbeef);\
719 ret = ansi ? GetMenuItemInfoA( hmenu, 0, TRUE, info2 ) :\
720 GetMenuItemInfoW( hmenu, 0, TRUE, info2W );\
721 if( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)\
723 skip("GetMenuItemInfo%s not implemented\n", ansi ? "A" : "W");\
724 break;\
726 if( !(eret2)) ok( (eret2)==ret,"GetMenuItemInfo should have failed.\n");\
727 else { \
728 ok( (eret2)==ret,"GetMenuItemInfo failed, err %d\n",GetLastError());\
729 ret = memcmp( info2, einfo, sizeof einfoA);\
730 /* ok( ret==0, "Got wrong menu item info data\n");*/\
731 if( ret) DUMPMIINF(info2A.cbSize, &info2A, &einfoA)\
732 if( einfo->dwTypeData == string) {\
733 if(ansi) ok( !strncmp( expname, info2->dwTypeData, einfo->cch ), "menu item name differed \"%s\"\n",\
734 einfo->dwTypeData ? einfo->dwTypeData: "");\
735 else ok( !strncmpW( (WCHAR*)expname, (WCHAR*)info2->dwTypeData, einfo->cch ), "menu item name differed \"%s\"\n",\
736 einfo->dwTypeData ? einfo->dwTypeData: "");\
737 ret = ansi ? GetMenuStringA( hmenu, 0, string, 80, MF_BYPOSITION) :\
738 GetMenuStringW( hmenu, 0, string, 80, MF_BYPOSITION);\
739 if( (eret3)){\
740 ok( ret, "GetMenuString failed, err %d\n",GetLastError());\
741 }else\
742 ok( !ret, "GetMenuString should have failed\n");\
748 #define TMII_DONE \
749 RemoveMenu(hmenu, 0, TRUE );\
750 DestroyMenu( hmenu );\
751 DestroyMenu( submenu );\
752 submenu = CreateMenu();\
754 /* modify menu */
755 #define TMII_MODM( flags, id, data, eret )\
756 if( !stop) {\
757 SetLastError( 0xdeadbeef);\
758 if(ansi)ret = ModifyMenuA( hmenu, 0, flags, (UINT_PTR)id, (char*)data);\
759 else ret = ModifyMenuW( hmenu, 0, flags, (UINT_PTR)id, (WCHAR*)data);\
760 if( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)\
762 skip("ModifyMenu%s not implemented\n", ansi ? "A" : "W");\
763 break;\
765 if( !(eret)) ok( (eret)==ret,"ModifyMenuA should have failed.\n");\
766 else ok( (eret)==ret,"ModifyMenuA failed, err %d\n",GetLastError());\
769 /* SetMenuItemInfo */
770 #define TMII_SMII( a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,n1,\
771 eret1)\
772 if( !stop) {\
773 MENUITEMINFOA info1=a1 b1,c1,d1,e1,f1,(void*)g1,(void*)h1,(void*)i1,j1,(void*)k1,l1,(void*)m1 n1;\
774 SetLastError( 0xdeadbeef);\
775 if(ansi)strcpy( string, init);\
776 else strcpyW( (WCHAR*)string, (WCHAR*)init);\
777 if( ansi) ret = SetMenuItemInfoA(hmenu, 0, TRUE, &info1 );\
778 else ret = SetMenuItemInfoW(hmenu, 0, TRUE, (MENUITEMINFOW*)&info1 );\
779 if( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)\
781 skip("SetMenuItemInfo%s not implemented\n", ansi ? "A" : "W");\
782 break;\
784 if( !(eret1)) { ok( (eret1)==ret,"InsertMenuItem should have failed.\n");\
785 stop = TRUE;\
786 } else ok( (eret1)==ret,"InsertMenuItem failed, err %d\n",GetLastError());\
791 #define OK 1
792 #define ER 0
795 static void test_menu_iteminfo( void )
797 int S=sizeof( MENUITEMINFOA);
798 int ansi = TRUE;
799 char txtA[]="wine";
800 char initA[]="XYZ";
801 char emptyA[]="";
802 WCHAR txtW[]={'W','i','n','e',0};
803 WCHAR initW[]={'X','Y','Z',0};
804 WCHAR emptyW[]={0};
805 void *txt, *init, *empty, *string;
806 HBITMAP hbm = CreateBitmap(1,1,1,1,NULL);
807 char stringA[0x80];
808 HMENU submenu=CreateMenu();
810 do {
811 if( ansi) {txt=txtA;init=initA;empty=emptyA;string=stringA;}
812 else {txt=txtW;init=initW;empty=emptyW;string=stringA;}
813 trace( "%s string %p hbm %p txt %p\n", ansi ? "ANSI tests: " : "Unicode tests:", string, hbm, txt);
814 /* test all combinations of MFT_STRING, MFT_OWNERDRAW and MFT_BITMAP */
815 /* (since MFT_STRING is zero, there are four of them) */
816 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, 0, 0, 0, 0, 0, 0, txt, 0, 0, }, OK)
817 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
818 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
819 txt, OK, OK )
820 TMII_DONE
821 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
822 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
823 {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, 0, 0, 0, },
824 empty, OK, ER )
825 TMII_DONE
826 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, 6, -1, }, OK)
827 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
828 {, S, MIIM_TYPE, MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
829 empty, OK, ER )
830 TMII_DONE
831 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, hbm, 6, -1, }, OK)
832 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
833 {, S, MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
834 empty, OK, ER )
835 TMII_DONE
836 /* not enough space for name*/
837 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
838 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, NULL, 0, -9, },
839 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, NULL, 4, 0, },
840 empty, OK, OK )
841 TMII_DONE
842 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
843 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 5, -9, },
844 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
845 txt, OK, OK )
846 TMII_DONE
847 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
848 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 4, -9, },
849 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 3, 0, },
850 txt, OK, OK )
851 TMII_DONE
852 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
853 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, NULL, 0, -9, },
854 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 0, 0, },
855 empty, OK, ER )
856 TMII_DONE
857 /* cannot combine MIIM_TYPE with some other flags */
858 TMII_INSMI( {, S, MIIM_TYPE|MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, ER)
859 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
860 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
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|MIIM_STRING, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
865 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
866 empty, ER, OK )
867 TMII_DONE
868 TMII_INSMI( {, S, MIIM_TYPE|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, ER)
869 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
870 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
871 empty, OK, OK )
872 TMII_DONE
873 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
874 TMII_GMII ( {, S, MIIM_TYPE|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
875 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
876 empty, ER, OK )
877 TMII_DONE
878 TMII_INSMI( {, S, MIIM_TYPE|MIIM_BITMAP, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, 6, hbm, }, ER)
879 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
880 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
881 empty, OK, OK )
882 TMII_DONE
883 /* but succeeds with some others */
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_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
886 {, S, MIIM_TYPE|MIIM_SUBMENU, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
887 txt, OK, OK )
888 TMII_DONE
889 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
890 TMII_GMII ( {, S, MIIM_TYPE|MIIM_STATE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
891 {, S, MIIM_TYPE|MIIM_STATE, MFT_STRING, 0, -9, 0, -9, -9, -9, string, 4, 0, },
892 txt, OK, OK )
893 TMII_DONE
894 TMII_INSMI( {, S, MIIM_TYPE|MIIM_ID, MFT_STRING, -1, 888, -1, -1, -1, -1, txt, 6, -1, }, OK)
895 TMII_GMII ( {, S, MIIM_TYPE|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
896 {, S, MIIM_TYPE|MIIM_ID, MFT_STRING, -9, 888, 0, -9, -9, -9, string, 4, 0, },
897 txt, OK, OK )
898 TMII_DONE
899 TMII_INSMI( {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING, -1, -1, -1, -1, -1, 999, txt, 6, -1, }, OK)
900 TMII_GMII ( {, S, MIIM_TYPE|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
901 {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING, -9, -9, 0, -9, -9, 999, string, 4, 0, },
902 txt, OK, OK )
903 TMII_DONE
904 /* to be continued */
905 /* set text with MIIM_TYPE and retrieve with MIIM_STRING */
906 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
907 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
908 {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, -9, },
909 txt, OK, OK )
910 TMII_DONE
911 /* set text with MIIM_TYPE and retrieve with MIIM_STRING; MFT_OWNERDRAW causes an empty string */
912 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
913 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
914 {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 0, -9, },
915 empty, OK, ER )
916 TMII_DONE
917 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
918 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
919 {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 0, -9, },
920 empty, OK, ER )
921 TMII_DONE
922 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
923 TMII_GMII ( {, S, MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
924 {, S, MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 80, -9, },
925 init, OK, ER )
926 TMII_DONE
927 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
928 TMII_GMII ( {, S, 0, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
929 {, S, 0, -9, -9, -9, 0, -9, -9, -9, string, 80, -9, },
930 init, OK, OK )
931 TMII_DONE
932 /* contrary to MIIM_TYPE,you can set the text for an owner draw menu */
933 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -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_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 4, -9, },
936 txt, OK, OK )
937 TMII_DONE
938 /* same but retrieve with MIIM_TYPE */
939 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
940 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
941 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
942 txt, OK, OK )
943 TMII_DONE
944 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
945 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
946 {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 0, -9, },
947 empty, OK, ER )
948 TMII_DONE
949 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
950 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
951 {, S, MIIM_STRING|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 0, -9, },
952 empty, OK, ER )
953 TMII_DONE
955 /* How is that with bitmaps? */
956 TMII_INSMI( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
957 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
958 {, S, MIIM_TYPE, MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
959 empty, OK, ER )
960 TMII_DONE
961 TMII_INSMI( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
962 TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
963 {, S, MIIM_BITMAP|MIIM_FTYPE, 0, -9, -9, 0, -9, -9, -9, string, 80, hbm, },
964 init, OK, ER )
965 TMII_DONE
966 /* MIIM_BITMAP does not like MFT_BITMAP */
967 TMII_INSMI( {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, ER)
968 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
969 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
970 init, OK, OK )
971 TMII_DONE
972 /* no problem with OWNERDRAWN */
973 TMII_INSMI( {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
974 TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
975 {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 80, hbm, },
976 init, OK, ER )
977 TMII_DONE
978 /* setting MFT_BITMAP with MFT_FTYPE fails anyway */
979 TMII_INSMI( {, S, MIIM_FTYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, }, ER)
980 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
981 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
982 empty, OK, OK )
983 TMII_DONE
985 /* menu with submenu */
986 TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, -1, -1, -1, txt, 0, -1, }, OK)
987 TMII_GMII ( {, S, MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
988 {, S, MIIM_SUBMENU, -9, -9, -9, submenu, -9, -9, -9, string, 80, -9, },
989 init, OK, ER )
990 TMII_DONE
991 TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, -1, -1, -1, empty, 0, -1, }, OK)
992 TMII_GMII ( {, S, MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
993 {, S, MIIM_SUBMENU, -9, -9, -9, submenu, -9, -9, -9, string, 80, -9, },
994 init, OK, ER )
995 TMII_DONE
996 /* menu with submenu, without MIIM_SUBMENU the submenufield is cleared */
997 TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, -1, -1, -1, txt, 0, -1, }, OK)
998 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
999 {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 0, -9, },
1000 empty, OK, ER )
1001 TMII_GMII ( {, S, MIIM_SUBMENU|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1002 {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, submenu, -9, -9, -9, string, 80, -9, },
1003 empty, OK, ER )
1004 TMII_DONE
1005 /* menu with invalid submenu */
1006 TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, 999, -1, -1, -1, txt, 0, -1, }, ER)
1007 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
1008 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
1009 init, OK, ER )
1010 TMII_DONE
1011 /* Separator */
1012 TMII_INSMI( {, S, MIIM_TYPE, MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, txt, 0, 0, }, OK)
1013 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1014 {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, 0, 0, 0, },
1015 empty, OK, ER )
1016 TMII_DONE
1017 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP|MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, hbm, 6, -1, }, OK)
1018 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1019 {, S, MIIM_TYPE, MFT_BITMAP|MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
1020 empty, OK, ER )
1021 TMII_DONE
1022 /* SEPARATOR and STRING go well together */
1023 /* BITMAP and STRING go well together */
1024 TMII_INSMI( {, S, MIIM_STRING|MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1025 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1026 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, hbm, },
1027 txt, OK, OK )
1028 TMII_DONE
1029 /* BITMAP, SEPARATOR and STRING go well together */
1030 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1031 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1032 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 4, hbm, },
1033 txt, OK, OK )
1034 TMII_DONE
1035 /* last two tests, but use MIIM_TYPE to retrieve info */
1036 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
1037 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1038 {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1039 txt, OK, OK )
1040 TMII_DONE
1041 TMII_INSMI( {, S, MIIM_STRING|MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1042 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1043 {, S, MIIM_TYPE, MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
1044 txt, OK, OK )
1045 TMII_DONE
1046 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1047 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1048 {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
1049 txt, OK, OK )
1050 TMII_DONE
1051 /* same three with MFT_OWNERDRAW */
1052 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
1053 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1054 {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1055 txt, OK, OK )
1056 TMII_DONE
1057 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1058 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1059 {, S, MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
1060 txt, OK, OK )
1061 TMII_DONE
1062 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR|MFT_OWNERDRAW, -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_SEPARATOR|MFT_BITMAP|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
1065 txt, OK, OK )
1066 TMII_DONE
1068 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE|MIIM_ID, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
1069 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1070 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1071 txt, OK, OK )
1072 TMII_DONE
1073 /* test with modifymenu: string is preserved after seting OWNERDRAW */
1074 TMII_INSMI( {, S, MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
1075 TMII_MODM( MFT_OWNERDRAW, -1, 787, OK)
1076 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1077 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_DATA, MFT_OWNERDRAW, -9, -9, 0, -9, -9, 787, string, 4, -9, },
1078 txt, OK, OK )
1079 TMII_DONE
1080 /* same with bitmap: now the text is cleared */
1081 TMII_INSMI( {, S, MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
1082 TMII_MODM( MFT_BITMAP, 545, hbm, OK)
1083 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1084 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_BITMAP, -9, 545, 0, -9, -9, -9, string, 0, hbm, },
1085 empty, OK, ER )
1086 TMII_DONE
1087 /* start with bitmap: now setting text clears it (though he flag is raised) */
1088 TMII_INSMI( {, S, MIIM_BITMAP, MFT_STRING, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1089 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1090 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_STRING, -9, 0, 0, -9, -9, -9, string, 0, hbm, },
1091 empty, OK, ER )
1092 TMII_MODM( MFT_STRING, 545, txt, OK)
1093 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1094 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_STRING, -9, 545, 0, -9, -9, -9, string, 4, 0, },
1095 txt, OK, OK )
1096 TMII_DONE
1097 /*repeat with text NULL */
1098 TMII_INSMI( {, S, MIIM_BITMAP, MFT_STRING, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1099 TMII_MODM( MFT_STRING, 545, NULL, OK)
1100 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1101 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_SEPARATOR, -9, 545, 0, -9, -9, -9, string, 0, 0, },
1102 empty, OK, ER )
1103 TMII_DONE
1104 /* repeat with text "" */
1105 TMII_INSMI( {, S, MIIM_BITMAP, -1 , -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1106 TMII_MODM( MFT_STRING, 545, empty, OK)
1107 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1108 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_STRING, -9, 545, 0, -9, -9, -9, string, 0, 0, },
1109 empty, OK, ER )
1110 TMII_DONE
1111 /* start with bitmap: set ownerdraw */
1112 TMII_INSMI( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1113 TMII_MODM( MFT_OWNERDRAW, -1, 232, OK)
1114 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1115 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_DATA, MFT_OWNERDRAW, -9, -9, 0, -9, -9, 232, string, 0, hbm, },
1116 empty, OK, ER )
1117 TMII_DONE
1118 /* ask nothing */
1119 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1120 TMII_GMII ( {, S, 0, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1121 {, S, 0, -9, -9, -9, 0, -9, -9, -9, string, 80, -9, },
1122 init, OK, OK )
1123 TMII_DONE
1124 /* some tests with small cbSize: the hbmpItem is to be ignored */
1125 TMII_INSMI( {, S - 4, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1126 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1127 {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, NULL, 0, NULL, },
1128 empty, OK, ER )
1129 TMII_DONE
1130 TMII_INSMI( {, S - 4, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1131 TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1132 {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 80, NULL, },
1133 init, OK, ER )
1134 TMII_DONE
1135 TMII_INSMI( {, S - 4, MIIM_STRING|MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1136 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1137 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, NULL, },
1138 txt, OK, OK )
1139 TMII_DONE
1140 TMII_INSMI( {, S - 4, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1141 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1142 {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1143 txt, OK, OK )
1144 TMII_DONE
1145 TMII_INSMI( {, S - 4, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1146 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1147 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1148 txt, OK, OK )
1149 TMII_DONE
1150 TMII_INSMI( {, S - 4, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1151 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1152 {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1153 txt, OK, OK )
1154 TMII_DONE
1155 /* MIIM_TYPE by itself does not get/set the dwItemData for OwnerDrawn menus */
1156 TMII_INSMI( {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, 343, txt, 0, -1, }, OK)
1157 TMII_GMII ( {, S, MIIM_TYPE|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1158 {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, 343, 0, 0, 0, },
1159 empty, OK, ER )
1160 TMII_DONE
1161 TMII_INSMI( {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, 343, txt, 0, -1, }, OK)
1162 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1163 {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, 0, 0, 0, },
1164 empty, OK, ER )
1165 TMII_DONE
1166 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, 343, txt, 0, -1, }, OK)
1167 TMII_GMII ( {, S, MIIM_TYPE|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1168 {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, 0, 0, 0, 0, },
1169 empty, OK, ER )
1170 TMII_DONE
1171 /* set a string menu to ownerdraw with MIIM_TYPE */
1172 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -2, -2, -2, -2, -2, -2, txt, -2, -2, }, OK)
1173 TMII_SMII( {, S, MIIM_TYPE, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, -1, -1, -1, }, OK)
1174 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1175 {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 4, -9, },
1176 txt, OK, OK )
1177 TMII_DONE
1178 /* test with modifymenu add submenu */
1179 TMII_INSMI( {, S, MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
1180 TMII_MODM( MF_POPUP, submenu, txt, OK)
1181 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1182 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_SUBMENU, MFT_STRING, -9, -9, submenu, -9, -9, -9, string, 4, -9, },
1183 txt, OK, OK )
1184 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1185 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
1186 txt, OK, OK )
1187 TMII_DONE
1188 /* MFT_SEPARATOR bit is kept when the text is added */
1189 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
1190 TMII_SMII( {, S, MIIM_STRING, -1, -1, -1, -1, -1, -1, -1, txt, -1, -1, }, OK)
1191 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1192 {, S, MIIM_STRING|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 4, -9, },
1193 txt, OK, OK )
1194 TMII_DONE
1195 /* MFT_SEPARATOR bit is kept when bitmap is added */
1196 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
1197 TMII_SMII( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1198 TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1199 {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 80, hbm, },
1200 init, OK, ER )
1201 TMII_DONE
1202 /* Bitmaps inserted with MIIM_TYPE and MFT_BITMAP:
1203 Only the low word of the dwTypeData is used.
1204 Use a magic bitmap here (Word 95 uses this to create its MDI menu buttons) */
1205 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP | MFT_RIGHTJUSTIFY, -1, -1, -1, -1, -1, -1, MAKELONG(HBMMENU_MBAR_CLOSE, 0x1234), -1, -1, }, OK)
1206 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1207 {, S, MIIM_TYPE, MFT_BITMAP | MFT_RIGHTJUSTIFY, -9, -9, 0, -9, -9, -9, HBMMENU_MBAR_CLOSE, 0, HBMMENU_MBAR_CLOSE, },
1208 empty, OK, OK )
1209 TMII_DONE
1210 /* Type flags */
1211 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)
1212 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1213 {, S, MIIM_TYPE, MFT_BITMAP | MFT_MENUBARBREAK | MFT_RADIOCHECK | MFT_RIGHTJUSTIFY | MFT_RIGHTORDER, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
1214 empty, OK, OK )
1215 TMII_DONE
1216 /* State flags */
1217 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, -1, -1, }, OK)
1218 TMII_SMII( {, S, MIIM_STATE, -1, MFS_CHECKED | MFS_DEFAULT | MFS_GRAYED | MFS_HILITE, -1, -1, -1, -1, -1, -1, -1, -1, }, OK)
1219 TMII_GMII ( {, S, MIIM_STATE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1220 {, S, MIIM_STATE, -9, MFS_CHECKED | MFS_DEFAULT | MFS_GRAYED | MFS_HILITE, -9, 0, -9, -9, -9, -9, -9, -9, },
1221 empty, OK, OK )
1222 TMII_DONE
1223 /* The style MFT_RADIOCHECK cannot be set with MIIM_CHECKMARKS only */
1224 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, -1, -1, }, OK)
1225 TMII_SMII( {, S, MIIM_CHECKMARKS, MFT_RADIOCHECK, -1, -1, -1, hbm, hbm, -1, -1, -1, -1, }, OK)
1226 TMII_GMII ( {, S, MIIM_CHECKMARKS | MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1227 {, S, MIIM_CHECKMARKS | MIIM_TYPE, MFT_BITMAP, -9, -9, 0, hbm, hbm, -9, hbm, 0, hbm, },
1228 empty, OK, OK )
1229 TMII_DONE
1230 /* MFT_BITMAP is added automatically by GetMenuItemInfo() for MIIM_TYPE */
1231 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, -1, -1, }, OK)
1232 TMII_SMII( {, S, MIIM_FTYPE, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, 0x1234, -1, -1, }, OK)
1233 TMII_GMII ( {, S, MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1234 {, S, MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, -9, -9, -9, },
1235 empty, OK, OK )
1236 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1237 {, S, MIIM_TYPE, MFT_BITMAP | MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
1238 empty, OK, OK )
1239 TMII_GMII ( {, S, MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1240 {, S, MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, -9, -9, -9, },
1241 empty, OK, OK )
1242 TMII_SMII( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, NULL, }, OK)
1243 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1244 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 0, NULL, },
1245 empty, OK, OK )
1246 TMII_DONE
1247 /* Bitmaps inserted with MIIM_TYPE and MFT_BITMAP:
1248 Only the low word of the dwTypeData is used.
1249 Use a magic bitmap here (Word 95 uses this to create its MDI menu buttons) */
1250 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP | MFT_RIGHTJUSTIFY, -1, -1, -1, -1, -1, -1, MAKELONG(HBMMENU_MBAR_CLOSE, 0x1234), -1, -1, }, OK)
1251 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1252 {, S, MIIM_TYPE, MFT_BITMAP | MFT_RIGHTJUSTIFY, -9, -9, 0, -9, -9, -9, HBMMENU_MBAR_CLOSE, 0, HBMMENU_MBAR_CLOSE, },
1253 empty, OK, OK )
1254 TMII_DONE
1255 /* Type flags */
1256 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)
1257 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1258 {, S, MIIM_TYPE, MFT_BITMAP | MFT_MENUBARBREAK | MFT_RADIOCHECK | MFT_RIGHTJUSTIFY | MFT_RIGHTORDER, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
1259 empty, OK, OK )
1260 TMII_DONE
1261 /* State flags */
1262 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, -1, -1, }, OK)
1263 TMII_SMII( {, S, MIIM_STATE, -1, MFS_CHECKED | MFS_DEFAULT | MFS_GRAYED | MFS_HILITE, -1, -1, -1, -1, -1, -1, -1, -1, }, OK)
1264 TMII_GMII ( {, S, MIIM_STATE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1265 {, S, MIIM_STATE, -9, MFS_CHECKED | MFS_DEFAULT | MFS_GRAYED | MFS_HILITE, -9, 0, -9, -9, -9, -9, -9, -9, },
1266 empty, OK, OK )
1267 TMII_DONE
1268 /* The style MFT_RADIOCHECK cannot be set with MIIM_CHECKMARKS only */
1269 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, -1, -1, }, OK)
1270 TMII_SMII( {, S, MIIM_CHECKMARKS, MFT_RADIOCHECK, -1, -1, -1, hbm, hbm, -1, -1, -1, -1, }, OK)
1271 TMII_GMII ( {, S, MIIM_CHECKMARKS | MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1272 {, S, MIIM_CHECKMARKS | MIIM_TYPE, MFT_BITMAP, -9, -9, 0, hbm, hbm, -9, hbm, 0, hbm, },
1273 empty, OK, OK )
1274 TMII_DONE
1275 /* MFT_BITMAP is added automatically by GetMenuItemInfo() for MIIM_TYPE */
1276 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, -1, -1, }, OK)
1277 TMII_SMII( {, S, MIIM_FTYPE, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, 0x1234, -1, -1, }, OK)
1278 TMII_GMII ( {, S, MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1279 {, S, MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, -9, -9, -9, },
1280 empty, OK, OK )
1281 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1282 {, S, MIIM_TYPE, MFT_BITMAP | MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
1283 empty, OK, OK )
1284 TMII_GMII ( {, S, MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1285 {, S, MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, -9, -9, -9, },
1286 empty, OK, OK )
1287 TMII_SMII( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, NULL, }, OK)
1288 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, },
1289 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 0, NULL, },
1290 empty, OK, OK )
1291 TMII_DONE
1292 } while( !(ansi = !ansi) );
1293 DeleteObject( hbm);
1297 The following tests try to confirm the algorithm used to return the menu items
1298 when there is a collision between a menu item and a popup menu
1300 static void test_menu_search_bycommand( void )
1302 HMENU hmenu, hmenuSub, hmenuSub2;
1303 MENUITEMINFO info;
1304 BOOL rc;
1305 UINT id;
1306 char strback[0x80];
1307 char strIn[0x80];
1308 static CHAR menuitem[] = "MenuItem",
1309 menuitem2[] = "MenuItem 2";
1311 /* Case 1: Menu containing a menu item */
1312 hmenu = CreateMenu();
1314 memset( &info, 0, sizeof info );
1315 info.cbSize = sizeof info;
1316 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1317 info.fType = MFT_STRING;
1318 strcpy(strIn, "Case 1 MenuItem");
1319 info.dwTypeData = strIn;
1320 info.wID = (UINT) 0x1234;
1322 rc = InsertMenuItem(hmenu, 0, TRUE, &info );
1323 ok (rc, "Inserting the menuitem failed\n");
1325 id = GetMenuItemID(hmenu, 0);
1326 ok (id == 0x1234, "Getting the menuitem id failed(gave %x)\n", id);
1328 /* Confirm the menuitem was given the id supplied (getting by position) */
1329 memset( &info, 0, sizeof info );
1330 strback[0] = 0x00;
1331 info.cbSize = sizeof(MENUITEMINFO);
1332 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1333 info.dwTypeData = strback;
1334 info.cch = sizeof(strback);
1336 rc = GetMenuItemInfo(hmenu, 0, TRUE, &info); /* Get by position */
1337 ok (rc, "Getting the menu items info failed\n");
1338 ok (info.wID == 0x1234, "IDs differ for the menuitem\n");
1339 ok (!strcmp(info.dwTypeData, "Case 1 MenuItem"), "Returned item has wrong label\n");
1341 /* Search by id - Should return the item */
1342 memset( &info, 0, sizeof info );
1343 strback[0] = 0x00;
1344 info.cbSize = sizeof(MENUITEMINFO);
1345 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1346 info.dwTypeData = strback;
1347 info.cch = sizeof(strback);
1348 rc = GetMenuItemInfo(hmenu, 0x1234, FALSE, &info); /* Get by ID */
1350 ok (rc, "Getting the menu items info failed\n");
1351 ok (info.wID == 0x1234, "IDs differ for the menuitem\n");
1352 ok (!strcmp(info.dwTypeData, "Case 1 MenuItem"), "Returned item has wrong label\n");
1354 DestroyMenu( hmenu );
1356 /* Case 2: Menu containing a popup menu */
1357 hmenu = CreateMenu();
1358 hmenuSub = CreateMenu();
1360 strcpy(strIn, "Case 2 SubMenu");
1361 rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, strIn);
1362 ok (rc, "Inserting the popup menu into the main menu failed\n");
1364 id = GetMenuItemID(hmenu, 0);
1365 ok (id == -1, "Getting the menuitem id unexpectedly worked (gave %x)\n", id);
1367 /* Confirm the menuitem itself was given an id the same as the HMENU, (getting by position) */
1368 memset( &info, 0, sizeof info );
1369 strback[0] = 0x00;
1370 info.cbSize = sizeof(MENUITEMINFO);
1371 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1372 info.dwTypeData = strback;
1373 info.cch = sizeof(strback);
1374 info.wID = 0xdeadbeef;
1376 rc = GetMenuItemInfo(hmenu, 0, TRUE, &info); /* Get by position */
1377 ok (rc, "Getting the menu items info failed\n");
1378 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the menuitem\n");
1379 ok (!strcmp(info.dwTypeData, "Case 2 SubMenu"), "Returned item has wrong label\n");
1381 /* Search by id - returns the popup menu itself */
1382 memset( &info, 0, sizeof info );
1383 strback[0] = 0x00;
1384 info.cbSize = sizeof(MENUITEMINFO);
1385 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1386 info.dwTypeData = strback;
1387 info.cch = sizeof(strback);
1388 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
1390 ok (rc, "Getting the menu items info failed\n");
1391 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
1392 ok (!strcmp(info.dwTypeData, "Case 2 SubMenu"), "Returned item has wrong label\n");
1395 Now add an item after it with the same id
1397 memset( &info, 0, sizeof info );
1398 info.cbSize = sizeof info;
1399 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1400 info.fType = MFT_STRING;
1401 strcpy(strIn, "Case 2 MenuItem 1");
1402 info.dwTypeData = strIn;
1403 info.wID = (UINT_PTR) hmenuSub;
1404 rc = InsertMenuItem(hmenu, -1, TRUE, &info );
1405 ok (rc, "Inserting the menuitem failed\n");
1407 /* Search by id - returns the item which follows the popup menu */
1408 memset( &info, 0, sizeof info );
1409 strback[0] = 0x00;
1410 info.cbSize = sizeof(MENUITEMINFO);
1411 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1412 info.dwTypeData = strback;
1413 info.cch = sizeof(strback);
1414 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
1416 ok (rc, "Getting the menu items info failed\n");
1417 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
1418 ok (!strcmp(info.dwTypeData, "Case 2 MenuItem 1"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1421 Now add an item before the popup (with the same id)
1423 memset( &info, 0, sizeof info );
1424 info.cbSize = sizeof info;
1425 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1426 info.fType = MFT_STRING;
1427 strcpy(strIn, "Case 2 MenuItem 2");
1428 info.dwTypeData = strIn;
1429 info.wID = (UINT_PTR) hmenuSub;
1430 rc = InsertMenuItem(hmenu, 0, TRUE, &info );
1431 ok (rc, "Inserting the menuitem failed\n");
1433 /* Search by id - returns the item which precedes the popup menu */
1434 memset( &info, 0, sizeof info );
1435 strback[0] = 0x00;
1436 info.cbSize = sizeof(MENUITEMINFO);
1437 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1438 info.dwTypeData = strback;
1439 info.cch = sizeof(strback);
1440 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
1442 ok (rc, "Getting the menu items info failed\n");
1443 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
1444 ok (!strcmp(info.dwTypeData, "Case 2 MenuItem 2"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1446 DestroyMenu( hmenu );
1447 DestroyMenu( hmenuSub );
1450 Case 3: Menu containing a popup menu which in turn
1451 contains 2 items with the same id as the popup itself
1454 hmenu = CreateMenu();
1455 hmenuSub = CreateMenu();
1457 memset( &info, 0, sizeof info );
1458 info.cbSize = sizeof info;
1459 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1460 info.fType = MFT_STRING;
1461 info.dwTypeData = menuitem;
1462 info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1464 rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
1465 ok (rc, "Inserting the popup menu into the main menu failed\n");
1467 rc = InsertMenuItem(hmenuSub, 0, TRUE, &info );
1468 ok (rc, "Inserting the sub menu menuitem failed\n");
1470 memset( &info, 0, sizeof info );
1471 info.cbSize = sizeof info;
1472 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1473 info.fType = MFT_STRING;
1474 info.dwTypeData = menuitem2;
1475 info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1477 rc = InsertMenuItem(hmenuSub, 1, TRUE, &info );
1478 ok (rc, "Inserting the sub menu menuitem 2 failed\n");
1480 /* Prove that you can't query the id of a popup directly (By position) */
1481 id = GetMenuItemID(hmenu, 0);
1482 ok (id == -1, "Getting the sub menu id should have failed because its a popup (gave %x)\n", id);
1484 /* Prove getting the item info via ID returns the first item (not the popup or 2nd item)*/
1485 memset( &info, 0, sizeof info );
1486 strback[0] = 0x00;
1487 info.cbSize = sizeof(MENUITEMINFO);
1488 info.fMask = MIIM_STRING | MIIM_ID;
1489 info.dwTypeData = strback;
1490 info.cch = sizeof(strback);
1492 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info);
1493 ok (rc, "Getting the menus info failed\n");
1494 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for popup menu\n");
1495 ok (!strcmp(info.dwTypeData, "MenuItem"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1496 DestroyMenu( hmenu );
1497 DestroyMenu( hmenuSub );
1500 Case 4: Menu containing 2 popup menus, the second
1501 contains 2 items with the same id as the first popup menu
1503 hmenu = CreateMenu();
1504 hmenuSub = CreateMenu();
1505 hmenuSub2 = CreateMenu();
1507 rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
1508 ok (rc, "Inserting the popup menu into the main menu failed\n");
1510 rc = InsertMenu(hmenu, 1, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub2, "Submenu2");
1511 ok (rc, "Inserting the popup menu into the main menu failed\n");
1513 memset( &info, 0, sizeof info );
1514 info.cbSize = sizeof info;
1515 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1516 info.fType = MFT_STRING;
1517 info.dwTypeData = menuitem;
1518 info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1520 rc = InsertMenuItem(hmenuSub2, 0, TRUE, &info );
1521 ok (rc, "Inserting the sub menu menuitem failed\n");
1523 memset( &info, 0, sizeof info );
1524 info.cbSize = sizeof info;
1525 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1526 info.fType = MFT_STRING;
1527 info.dwTypeData = menuitem2;
1528 info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1530 rc = InsertMenuItem(hmenuSub2, 1, TRUE, &info );
1531 ok (rc, "Inserting the sub menu menuitem 2 failed\n");
1533 /* Prove getting the item info via ID returns the first item (not the popup or 2nd item)*/
1534 memset( &info, 0, sizeof info );
1535 strback[0] = 0x00;
1536 info.cbSize = sizeof(MENUITEMINFO);
1537 info.fMask = MIIM_STRING | MIIM_ID;
1538 info.dwTypeData = strback;
1539 info.cch = sizeof(strback);
1541 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info);
1542 ok (rc, "Getting the menus info failed\n");
1543 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for popup menu\n");
1544 ok (!strcmp(info.dwTypeData, "MenuItem"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1546 memset( &info, 0, sizeof info );
1547 strback[0] = 0x00;
1548 info.cbSize = sizeof(MENUITEMINFO);
1549 info.fMask = MIIM_STRING | MIIM_ID;
1550 info.dwTypeData = strback;
1551 info.cch = sizeof(strback);
1553 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub2, FALSE, &info);
1554 ok (rc, "Getting the menus info failed\n");
1555 ok (info.wID == (UINT)hmenuSub2, "IDs differ for popup menu\n");
1556 ok (!strcmp(info.dwTypeData, "Submenu2"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1558 DestroyMenu( hmenu );
1559 DestroyMenu( hmenuSub );
1560 DestroyMenu( hmenuSub2 );
1564 Case 5: Menu containing a popup menu which in turn
1565 contains an item with a different id than the popup menu.
1566 This tests the fallback to a popup menu ID.
1569 hmenu = CreateMenu();
1570 hmenuSub = CreateMenu();
1572 rc = AppendMenu(hmenu, MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
1573 ok (rc, "Appending the popup menu to the main menu failed\n");
1575 rc = AppendMenu(hmenuSub, MF_STRING, 102, "Item");
1576 ok (rc, "Appending the item to the popup menu failed\n");
1578 /* Set the ID for hmenuSub */
1579 info.cbSize = sizeof(info);
1580 info.fMask = MIIM_ID;
1581 info.wID = 101;
1583 rc = SetMenuItemInfo(hmenu, 0, TRUE, &info);
1584 ok(rc, "Setting the ID for the popup menu failed\n");
1586 /* Check if the ID has been set */
1587 info.wID = 0;
1588 rc = GetMenuItemInfo(hmenu, 0, TRUE, &info);
1589 ok(rc, "Getting the ID for the popup menu failed\n");
1590 ok(info.wID == 101, "The ID for the popup menu has not been set\n");
1592 /* Prove getting the item info via ID returns the popup menu */
1593 memset( &info, 0, sizeof(info));
1594 strback[0] = 0x00;
1595 info.cbSize = sizeof(MENUITEMINFO);
1596 info.fMask = MIIM_STRING | MIIM_ID;
1597 info.dwTypeData = strback;
1598 info.cch = sizeof(strback);
1600 rc = GetMenuItemInfo(hmenu, 101, FALSE, &info);
1601 ok (rc, "Getting the menu info failed\n");
1602 ok (info.wID == 101, "IDs differ\n");
1603 ok (!strcmp(info.dwTypeData, "Submenu"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1605 /* Also look for the menu item */
1606 memset( &info, 0, sizeof(info));
1607 strback[0] = 0x00;
1608 info.cbSize = sizeof(MENUITEMINFO);
1609 info.fMask = MIIM_STRING | MIIM_ID;
1610 info.dwTypeData = strback;
1611 info.cch = sizeof(strback);
1613 rc = GetMenuItemInfo(hmenu, 102, FALSE, &info);
1614 ok (rc, "Getting the menu info failed\n");
1615 ok (info.wID == 102, "IDs differ\n");
1616 ok (!strcmp(info.dwTypeData, "Item"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1618 DestroyMenu(hmenu);
1619 DestroyMenu(hmenuSub);
1622 struct menu_item_pair_s {
1623 UINT uMenu; /* 1 - top level menu, [0-Menu 1-Enabled 2-Disabled]
1624 * 2 - 2nd level menu, [0-Popup 1-Enabled 2-Disabled]
1625 * 3 - 3rd level menu, [0-Enabled 1-Disabled] */
1626 UINT uItem;
1629 static struct menu_mouse_tests_s {
1630 DWORD type;
1631 struct menu_item_pair_s menu_item_pairs[5]; /* for mousing */
1632 WORD wVk[5]; /* keys */
1633 BOOL bMenuVisible;
1634 BOOL _todo_wine;
1635 } menu_tests[] = {
1636 /* for each test, send keys or clicks and check for menu visibility */
1637 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 0}, TRUE, FALSE }, /* test 0 */
1638 { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, 0}, FALSE, FALSE },
1639 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 0}, TRUE, FALSE },
1640 { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
1641 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 0}, TRUE, FALSE },
1642 { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
1643 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 0}, TRUE, FALSE },
1644 { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, VK_ESCAPE, 0}, FALSE, FALSE },
1645 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', VK_ESCAPE, 0}, TRUE, FALSE },
1646 { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, 0}, FALSE, FALSE },
1647 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 0}, TRUE, FALSE },
1648 { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
1649 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 0}, TRUE, FALSE },
1650 { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
1651 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 'P', 0}, TRUE, FALSE },
1652 { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
1653 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 'P', 0}, TRUE, FALSE },
1654 { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
1656 { INPUT_MOUSE, {{1, 2}, {0}}, {0}, TRUE, TRUE }, /* test 18 */
1657 { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
1658 { INPUT_MOUSE, {{1, 0}, {0}}, {0}, TRUE, TRUE },
1659 { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
1660 { INPUT_MOUSE, {{1, 0}, {2, 2}, {0}}, {0}, TRUE, TRUE },
1661 { INPUT_MOUSE, {{2, 1}, {0}}, {0}, FALSE, FALSE },
1662 { INPUT_MOUSE, {{1, 0}, {2, 0}, {0}}, {0}, TRUE, TRUE },
1663 { INPUT_MOUSE, {{3, 0}, {0}}, {0}, FALSE, FALSE },
1664 { INPUT_MOUSE, {{1, 0}, {2, 0}, {0}}, {0}, TRUE, TRUE },
1665 { INPUT_MOUSE, {{3, 1}, {0}}, {0}, TRUE, TRUE },
1666 { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
1667 { -1 }
1670 static void send_key(WORD wVk)
1672 TEST_INPUT i[2];
1673 memset(&i, 0, 2*sizeof(INPUT));
1674 i[0].type = i[1].type = INPUT_KEYBOARD;
1675 i[0].u.ki.wVk = i[1].u.ki.wVk = wVk;
1676 i[1].u.ki.dwFlags = KEYEVENTF_KEYUP;
1677 SendInput(2, (INPUT *) i, sizeof(INPUT));
1680 static void click_menu(HANDLE hWnd, struct menu_item_pair_s *mi)
1682 HMENU hMenu = hMenus[mi->uMenu];
1683 TEST_INPUT i[3];
1684 MSG msg;
1685 RECT r;
1686 int screen_w = GetSystemMetrics(SM_CXSCREEN);
1687 int screen_h = GetSystemMetrics(SM_CYSCREEN);
1689 GetMenuItemRect(mi->uMenu > 2 ? NULL : hWnd, hMenu, mi->uItem, &r);
1691 memset(&i, 0, 3*sizeof(INPUT));
1692 i[0].type = i[1].type = i[2].type = INPUT_MOUSE;
1693 i[0].u.mi.dx = i[1].u.mi.dx = i[2].u.mi.dx
1694 = ((r.left + 5) * 65535) / screen_w;
1695 i[0].u.mi.dy = i[1].u.mi.dy = i[2].u.mi.dy
1696 = ((r.top + 5) * 65535) / screen_h;
1697 i[0].u.mi.dwFlags = i[1].u.mi.dwFlags = i[2].u.mi.dwFlags
1698 = MOUSEEVENTF_ABSOLUTE;
1699 i[0].u.mi.dwFlags |= MOUSEEVENTF_MOVE;
1700 i[1].u.mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
1701 i[2].u.mi.dwFlags |= MOUSEEVENTF_LEFTUP;
1702 SendInput(3, (INPUT *) i, sizeof(INPUT));
1704 /* hack to prevent mouse message buildup in Wine */
1705 while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
1708 static DWORD WINAPI test_menu_input_thread(LPVOID lpParameter)
1710 int i, j;
1711 HANDLE hWnd = lpParameter;
1713 Sleep(500);
1714 /* mixed keyboard/mouse test */
1715 for (i = 0; menu_tests[i].type != -1; i++)
1717 int elapsed = 0;
1719 if (menu_tests[i].type == INPUT_KEYBOARD)
1720 for (j = 0; menu_tests[i].wVk[j] != 0; j++)
1721 send_key(menu_tests[i].wVk[j]);
1722 else
1723 for (j = 0; menu_tests[i].menu_item_pairs[j].uMenu != 0; j++)
1724 click_menu(hWnd, &menu_tests[i].menu_item_pairs[j]);
1726 while (menu_tests[i].bMenuVisible != bMenuVisible)
1728 if (elapsed > 200)
1729 break;
1730 elapsed += 20;
1731 Sleep(20);
1734 if (menu_tests[i]._todo_wine)
1736 todo_wine {
1737 ok(menu_tests[i].bMenuVisible == bMenuVisible, "test %d\n", i);
1740 else
1741 ok(menu_tests[i].bMenuVisible == bMenuVisible, "test %d\n", i);
1743 return 0;
1746 static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam,
1747 LPARAM lParam)
1749 switch (msg) {
1750 case WM_ENTERMENULOOP:
1751 bMenuVisible = TRUE;
1752 break;
1753 case WM_EXITMENULOOP:
1754 bMenuVisible = FALSE;
1755 break;
1756 default:
1757 return( DefWindowProcA( hWnd, msg, wParam, lParam ) );
1759 return 0;
1762 static void test_menu_input(void) {
1763 MSG msg;
1764 WNDCLASSA wclass;
1765 HINSTANCE hInstance = GetModuleHandleA( NULL );
1766 HANDLE hThread, hWnd;
1768 wclass.lpszClassName = "MenuTestClass";
1769 wclass.style = CS_HREDRAW | CS_VREDRAW;
1770 wclass.lpfnWndProc = WndProc;
1771 wclass.hInstance = hInstance;
1772 wclass.hIcon = LoadIconA( 0, (LPSTR)IDI_APPLICATION );
1773 wclass.hCursor = LoadCursorA( NULL, (LPSTR)IDC_ARROW);
1774 wclass.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1);
1775 wclass.lpszMenuName = 0;
1776 wclass.cbClsExtra = 0;
1777 wclass.cbWndExtra = 0;
1778 assert (RegisterClassA( &wclass ));
1779 assert (hWnd = CreateWindowA( wclass.lpszClassName, "MenuTest",
1780 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0,
1781 400, 200, NULL, NULL, hInstance, NULL) );
1783 /* fixed menus */
1784 hMenus[3] = CreatePopupMenu();
1785 AppendMenu(hMenus[3], MF_STRING, 0, "&Enabled");
1786 AppendMenu(hMenus[3], MF_STRING|MF_DISABLED, 0, "&Disabled");
1788 hMenus[2] = CreatePopupMenu();
1789 AppendMenu(hMenus[2], MF_STRING|MF_POPUP, (UINT_PTR) hMenus[3], "&Popup");
1790 AppendMenu(hMenus[2], MF_STRING, 0, "&Enabled");
1791 AppendMenu(hMenus[2], MF_STRING|MF_DISABLED, 0, "&Disabled");
1793 hMenus[1] = CreateMenu();
1794 AppendMenu(hMenus[1], MF_STRING|MF_POPUP, (UINT_PTR) hMenus[2], "&Menu");
1795 AppendMenu(hMenus[1], MF_STRING, 0, "&Enabled");
1796 AppendMenu(hMenus[1], MF_STRING|MF_DISABLED, 0, "&Disabled");
1798 SetMenu(hWnd, hMenus[1]);
1799 ShowWindow(hWnd, SW_SHOW);
1800 UpdateWindow(hWnd);
1802 hThread = CreateThread(NULL, 0, test_menu_input_thread, hWnd, 0, NULL);
1803 while(1)
1805 if (WAIT_TIMEOUT != WaitForSingleObject(hThread, 50))
1806 break;
1807 while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
1809 DestroyWindow(hWnd);
1812 static void test_menu_flags( void )
1814 HMENU hMenu, hPopupMenu;
1816 hMenu = CreateMenu();
1817 hPopupMenu = CreatePopupMenu();
1819 AppendMenu(hMenu, MF_POPUP | MF_STRING, (UINT)hPopupMenu, "Popup");
1821 AppendMenu(hPopupMenu, MF_STRING | MF_HILITE | MF_DEFAULT, 101, "Item 1");
1822 InsertMenu(hPopupMenu, 1, MF_BYPOSITION | MF_STRING | MF_HILITE | MF_DEFAULT, 102, "Item 2");
1823 AppendMenu(hPopupMenu, MF_STRING, 103, "Item 3");
1824 ModifyMenu(hPopupMenu, 2, MF_BYPOSITION | MF_STRING | MF_HILITE | MF_DEFAULT, 103, "Item 3");
1826 ok(GetMenuState(hPopupMenu, 0, MF_BYPOSITION) & MF_HILITE,
1827 "AppendMenu should accept MF_HILITE\n");
1828 ok(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE,
1829 "InsertMenu should accept MF_HILITE\n");
1830 ok(GetMenuState(hPopupMenu, 2, MF_BYPOSITION) & MF_HILITE,
1831 "ModifyMenu should accept MF_HILITE\n");
1833 ok(!(GetMenuState(hPopupMenu, 0, MF_BYPOSITION) & MF_DEFAULT),
1834 "AppendMenu must not accept MF_DEFAULT\n");
1835 ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_DEFAULT),
1836 "InsertMenu must not accept MF_DEFAULT\n");
1837 ok(!(GetMenuState(hPopupMenu, 2, MF_BYPOSITION) & MF_DEFAULT),
1838 "ModifyMenu must not accept MF_DEFAULT\n");
1840 DestroyMenu(hMenu);
1843 static void test_menu_hilitemenuitem( void )
1845 HMENU hMenu, hPopupMenu;
1847 hMenu = CreateMenu();
1848 hPopupMenu = CreatePopupMenu();
1850 AppendMenu(hMenu, MF_POPUP | MF_STRING, (UINT)hPopupMenu, "Popup");
1852 AppendMenu(hPopupMenu, MF_STRING, 101, "Item 1");
1853 AppendMenu(hPopupMenu, MF_STRING, 102, "Item 2");
1854 AppendMenu(hPopupMenu, MF_STRING, 103, "Item 3");
1856 HiliteMenuItem(NULL, hPopupMenu, 0, MF_HILITE);
1857 HiliteMenuItem(NULL, hPopupMenu, 1, MF_HILITE);
1858 HiliteMenuItem(NULL, hPopupMenu, 2, MF_HILITE);
1859 HiliteMenuItem(NULL, hPopupMenu, 1, MF_UNHILITE);
1861 todo_wine
1863 ok(GetMenuState(hPopupMenu, 0, MF_BYPOSITION) & MF_HILITE,
1864 "HiliteMenuItem: Item 1 is not hilited\n");
1866 ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE),
1867 "HiliteMenuItem: Item 2 is hilited\n");
1868 todo_wine
1870 ok(GetMenuState(hPopupMenu, 2, MF_BYPOSITION) & MF_HILITE,
1871 "HiliteMenuItem: Item 3 is not hilited\n");
1874 DestroyMenu(hMenu);
1877 static void check_menu_items(HMENU hmenu, UINT checked_cmd, UINT checked_type,
1878 UINT checked_state)
1880 UINT i, count;
1882 count = GetMenuItemCount(hmenu);
1884 for (i = 0; i < count; i++)
1886 BOOL ret;
1887 MENUITEMINFO mii;
1889 memset(&mii, 0, sizeof(mii));
1890 mii.cbSize = sizeof(mii);
1891 mii.fMask = MIIM_FTYPE | MIIM_STATE | MIIM_ID | MIIM_SUBMENU;
1892 ret = GetMenuItemInfo(hmenu, i, TRUE, &mii);
1893 ok(ret, "GetMenuItemInfo(%u) failed\n", i);
1894 #if 0
1895 trace("item #%u: fType %04x, fState %04x, wID %u, hSubMenu %p\n",
1896 i, mii.fType, mii.fState, mii.wID, mii.hSubMenu);
1897 #endif
1898 if (mii.hSubMenu)
1900 ok((HMENU)mii.wID == mii.hSubMenu, "id %u: wID should be equal to hSubMenu\n", checked_cmd);
1901 check_menu_items(mii.hSubMenu, checked_cmd, checked_type, checked_state);
1903 else
1905 if (mii.wID == checked_cmd)
1907 ok(mii.fType == checked_type, "id %u: expected fType %04x, got %04x\n", checked_cmd, checked_type, mii.fType);
1908 ok(mii.fState == checked_state, "id %u: expected fState %04x, got %04x\n", checked_cmd, checked_state, mii.fState);
1909 ok(mii.wID != 0, "id %u: not expected wID 0\n", checked_cmd);
1911 else
1913 ok(mii.fType != MFT_RADIOCHECK, "id %u: not expected fType MFT_RADIOCHECK on cmd %u\n", checked_cmd, mii.wID);
1915 if (mii.fType == MFT_SEPARATOR)
1917 ok(mii.fState == MFS_GRAYED, "id %u: expected fState MFS_GRAYED, got %04x\n", checked_cmd, mii.fState);
1918 ok(mii.wID == 0, "id %u: expected wID 0, got %u\n", checked_cmd, mii.wID);
1920 else
1922 ok(mii.fState == 0, "id %u: expected fState 0, got %04x\n", checked_cmd, mii.fState);
1923 ok(mii.wID != 0, "id %u: not expected wID 0\n", checked_cmd);
1930 static void clear_ftype_and_state(HMENU hmenu, UINT id, UINT flags)
1932 BOOL ret;
1933 MENUITEMINFO mii;
1935 memset(&mii, 0, sizeof(mii));
1936 mii.cbSize = sizeof(mii);
1937 mii.fMask = MIIM_FTYPE | MIIM_STATE;
1938 ret = SetMenuItemInfo(hmenu, id, (flags & MF_BYPOSITION) != 0, &mii);
1939 ok(ret, "SetMenuItemInfo(%u) failed\n", id);
1942 static void test_CheckMenuRadioItem(void)
1944 BOOL ret;
1945 HMENU hmenu;
1947 hmenu = LoadMenu(GetModuleHandle(0), MAKEINTRESOURCE(1));
1948 assert(hmenu != 0);
1950 check_menu_items(hmenu, -1, 0, 0);
1952 ret = CheckMenuRadioItem(hmenu, 100, 100, 100, MF_BYCOMMAND);
1953 ok(ret, "CheckMenuRadioItem failed\n");
1954 check_menu_items(hmenu, 100, MFT_RADIOCHECK, MFS_CHECKED);
1956 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
1957 ret = CheckMenuRadioItem(hmenu, 100, 100, -1, MF_BYCOMMAND);
1958 ok(!ret, "CheckMenuRadioItem should return FALSE\n");
1959 check_menu_items(hmenu, 100, MFT_RADIOCHECK, 0);
1961 /* clear check */
1962 clear_ftype_and_state(hmenu, 100, MF_BYCOMMAND);
1963 check_menu_items(hmenu, -1, 0, 0);
1965 /* first and checked items are on different menus */
1966 ret = CheckMenuRadioItem(hmenu, 0, 300, 202, MF_BYCOMMAND);
1967 ok(!ret, "CheckMenuRadioItem should return FALSE\n");
1968 check_menu_items(hmenu, -1, 0, 0);
1970 ret = CheckMenuRadioItem(hmenu, 200, 300, 202, MF_BYCOMMAND);
1971 ok(ret, "CheckMenuRadioItem failed\n");
1972 check_menu_items(hmenu, 202, MFT_RADIOCHECK, MFS_CHECKED);
1974 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
1975 ret = CheckMenuRadioItem(hmenu, 202, 202, -1, MF_BYCOMMAND);
1976 ok(!ret, "CheckMenuRadioItem should return FALSE\n");
1977 check_menu_items(hmenu, 202, MFT_RADIOCHECK, 0);
1979 /* clear check */
1980 clear_ftype_and_state(hmenu, 202, MF_BYCOMMAND);
1981 check_menu_items(hmenu, -1, 0, 0);
1983 /* just for fun, try to check separator */
1984 ret = CheckMenuRadioItem(hmenu, 0, 300, 0, MF_BYCOMMAND);
1985 ok(!ret, "CheckMenuRadioItem should return FALSE\n");
1986 check_menu_items(hmenu, -1, 0, 0);
1989 static void test_menu_resource_layout(void)
1991 static const struct
1993 MENUITEMTEMPLATEHEADER mith;
1994 WORD data[14];
1995 } menu_template =
1997 { 0, 0 }, /* versionNumber, offset */
1999 /* mtOption, mtID, mtString[] '\0' terminated */
2000 MF_STRING, 1, 'F', 0,
2001 MF_STRING, 2, 0,
2002 MF_SEPARATOR, 3, 0,
2003 /* MF_SEPARATOR, 4, 'S', 0, FIXME: Wine ignores 'S' */
2004 MF_STRING|MF_GRAYED|MF_END, 5, 'E', 0
2007 static const struct
2009 UINT type, state, id;
2010 const char *str;
2011 } menu_data[] =
2013 { MF_STRING, MF_ENABLED, 1, "F" },
2014 { MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 2, "" },
2015 { MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 3, "" },
2016 /*{ MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 4, "S" }, FIXME: Wine ignores 'S'*/
2017 { MF_STRING, MF_GRAYED, 5, "E" },
2018 { MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 6, "" },
2019 { MF_STRING, MF_ENABLED, 7, "" },
2020 { MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 8, "" }
2022 HMENU hmenu;
2023 UINT count, i;
2024 BOOL ret;
2026 hmenu = LoadMenuIndirect(&menu_template);
2027 ok(hmenu != 0, "LoadMenuIndirect error %u\n", GetLastError());
2029 ret = AppendMenu(hmenu, MF_STRING, 6, NULL);
2030 ok(ret, "AppendMenu failed\n");
2031 ret = AppendMenu(hmenu, MF_STRING, 7, "\0");
2032 ok(ret, "AppendMenu failed\n");
2033 ret = AppendMenu(hmenu, MF_SEPARATOR, 8, "separator");
2034 ok(ret, "AppendMenu failed\n");
2036 count = GetMenuItemCount(hmenu);
2037 ok(count == sizeof(menu_data)/sizeof(menu_data[0]),
2038 "expected %u menu items, got %u\n",
2039 (UINT)sizeof(menu_data)/sizeof(menu_data[0]), count);
2041 for (i = 0; i < count; i++)
2043 char buf[20];
2044 MENUITEMINFO mii;
2046 memset(&mii, 0, sizeof(mii));
2047 mii.cbSize = sizeof(mii);
2048 mii.dwTypeData = buf;
2049 mii.cch = sizeof(buf);
2050 mii.fMask = MIIM_FTYPE | MIIM_STATE | MIIM_ID | MIIM_STRING;
2051 ret = GetMenuItemInfo(hmenu, i, TRUE, &mii);
2052 ok(ret, "GetMenuItemInfo(%u) failed\n", i);
2053 #if 0
2054 trace("item #%u: fType %04x, fState %04x, wID %u, dwTypeData %s\n",
2055 i, mii.fType, mii.fState, mii.wID, (LPCSTR)mii.dwTypeData);
2056 #endif
2057 ok(mii.fType == menu_data[i].type,
2058 "%u: expected fType %04x, got %04x\n", i, menu_data[i].type, mii.fType);
2059 ok(mii.fState == menu_data[i].state,
2060 "%u: expected fState %04x, got %04x\n", i, menu_data[i].state, mii.fState);
2061 ok(mii.wID == menu_data[i].id,
2062 "%u: expected wID %04x, got %04x\n", i, menu_data[i].id, mii.wID);
2063 ok(mii.cch == strlen(menu_data[i].str),
2064 "%u: expected cch %u, got %u\n", i, (UINT)strlen(menu_data[i].str), mii.cch);
2065 ok(!strcmp((LPCSTR)mii.dwTypeData, menu_data[i].str),
2066 "%u: expected dwTypeData %s, got %s\n", i, menu_data[i].str, (LPCSTR)mii.dwTypeData);
2069 DestroyMenu(hmenu);
2072 START_TEST(menu)
2074 pSetMenuInfo =
2075 (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "SetMenuInfo" );
2076 pGetMenuInfo =
2077 (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetMenuInfo" );
2079 register_menu_check_class();
2081 test_menu_locked_by_window();
2082 test_menu_ownerdraw();
2083 test_menu_add_string();
2084 test_menu_iteminfo();
2085 test_menu_search_bycommand();
2086 test_menu_bmp_and_string();
2087 test_menu_input();
2088 test_menu_flags();
2089 test_menu_hilitemenuitem();
2090 test_CheckMenuRadioItem();
2091 test_menu_resource_layout();