4 * Copyright 2005 Robert Shearman
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define NONAMELESSUNION
22 #define NONAMELESSSTRUCT
34 #include "wine/test.h"
36 static ATOM atomMenuCheckClass;
38 static BOOL (WINAPI *pSetMenuInfo)(HMENU,LPCMENUINFO);
39 static BOOL (WINAPI *pGetMenuInfo)(HMENU,LPCMENUINFO);
41 static LRESULT WINAPI menu_check_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
45 case WM_ENTERMENULOOP:
46 /* mark window as having entered menu loop */
47 SetWindowLongPtr(hwnd, GWLP_USERDATA, TRUE);
48 /* exit menu modal loop
49 * ( A SendMessage does not work on NT3.51 here ) */
50 return PostMessage(hwnd, WM_CANCELMODE, 0, 0);
52 return DefWindowProc(hwnd, msg, wparam, lparam);
55 /* The MSVC headers ignore our NONAMELESSUNION requests so we have to define
68 /* globals to communicate between test and wndproc */
70 static BOOL bMenuVisible;
71 static HMENU hMenus[4];
76 /* menu texts with their sizes */
79 SIZE size; /* size of text up to any \t */
80 SIZE sc_size; /* size of the short-cut */
89 unsigned int MOD_maxid;
90 RECT MOD_rc[MOD_NRMENUS];
91 int MOD_avec, MOD_hic;
93 SIZE MODsizes[MOD_NRMENUS]= { {MOD_SIZE, MOD_SIZE},{MOD_SIZE, MOD_SIZE},
94 {MOD_SIZE, MOD_SIZE},{MOD_SIZE, MOD_SIZE}};
95 int MOD_GotDrawItemMsg = FALSE;
96 /* wndproc used by test_menu_ownerdraw() */
97 static LRESULT WINAPI menu_ownerdraw_wnd_proc(HWND hwnd, UINT msg,
98 WPARAM wparam, LPARAM lparam)
104 MEASUREITEMSTRUCT* pmis = (MEASUREITEMSTRUCT*)lparam;
106 trace("WM_MEASUREITEM received data %lx size %dx%d\n",
107 pmis->itemData, pmis->itemWidth, pmis->itemHeight);
108 MOD_odheight = pmis->itemHeight;
109 pmis->itemWidth = MODsizes[pmis->itemData].cx;
110 pmis->itemHeight = MODsizes[pmis->itemData].cy;
115 DRAWITEMSTRUCT * pdis;
118 char chrs[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
121 pdis = (DRAWITEMSTRUCT *) lparam;
122 if( winetest_debug) {
124 GetMenuItemRect( hwnd, (HMENU)pdis->hwndItem, pdis->itemData ,&rc);
125 trace("WM_DRAWITEM received hwnd %p hmenu %p itemdata %ld item %d rc %ld,%ld-%ld,%ld itemrc: %ld,%ld-%ld,%ld\n",
126 hwnd, (HMENU)pdis->hwndItem, pdis->itemData,
127 pdis->itemID, pdis->rcItem.left, pdis->rcItem.top,
128 pdis->rcItem.right,pdis->rcItem.bottom,
129 rc.left,rc.top,rc.right,rc.bottom);
130 oldpen=SelectObject( pdis->hDC, GetStockObject(
131 pdis->itemState & ODS_SELECTED ? WHITE_PEN :BLACK_PEN));
132 Rectangle( pdis->hDC, pdis->rcItem.left,pdis->rcItem.top,
133 pdis->rcItem.right,pdis->rcItem.bottom );
134 SelectObject( pdis->hDC, oldpen);
136 /* calculate widths of some menu texts */
137 if( ! MOD_txtsizes[0].size.cx)
138 for(i = 0; MOD_txtsizes[i].text; i++) {
141 strcpy( buf, MOD_txtsizes[i].text);
142 if( ( p = strchr( buf, '\t'))) {
144 DrawText( pdis->hDC, p + 1, -1, &rc,
145 DT_SINGLELINE|DT_CALCRECT);
146 MOD_txtsizes[i].sc_size.cx= rc.right - rc.left;
147 MOD_txtsizes[i].sc_size.cy= rc.bottom - rc.top;
149 DrawText( pdis->hDC, buf, -1, &rc,
150 DT_SINGLELINE|DT_CALCRECT);
151 MOD_txtsizes[i].size.cx= rc.right - rc.left;
152 MOD_txtsizes[i].size.cy= rc.bottom - rc.top;
155 if( pdis->itemData > MOD_maxid) return TRUE;
156 /* store the rectangl */
157 MOD_rc[pdis->itemData] = pdis->rcItem;
158 /* calculate average character width */
159 GetTextExtentPoint( pdis->hDC, chrs, 52, &sz );
160 MOD_avec = (sz.cx + 26)/52;
161 GetTextMetrics( pdis->hDC, &tm);
162 MOD_hic = tm.tmHeight;
163 MOD_GotDrawItemMsg = TRUE;
168 PostMessage(hwnd, WM_CANCELMODE, 0, 0);
173 return DefWindowProc(hwnd, msg, wparam, lparam);
176 static void register_menu_check_class(void)
184 GetModuleHandle(NULL),
186 LoadCursor(NULL, IDC_ARROW),
187 (HBRUSH)(COLOR_BTNFACE+1),
189 TEXT("WineMenuCheck"),
192 atomMenuCheckClass = RegisterClass(&wc);
195 /* demonstrates that windows locks the menu object so that it is still valid
196 * even after a client calls DestroyMenu on it */
197 static void test_menu_locked_by_window(void)
201 HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
202 WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200,
203 NULL, NULL, NULL, NULL);
204 ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError());
205 hmenu = CreateMenu();
206 ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError());
207 ret = InsertMenu(hmenu, 0, MF_STRING, 0, TEXT("&Test"));
208 ok(ret, "InsertMenu failed with error %ld\n", GetLastError());
209 ret = SetMenu(hwnd, hmenu);
210 ok(ret, "SetMenu failed with error %ld\n", GetLastError());
211 ret = DestroyMenu(hmenu);
212 ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
214 ret = DrawMenuBar(hwnd);
216 ok(ret, "DrawMenuBar failed with error %ld\n", GetLastError());
218 ret = IsMenu(GetMenu(hwnd));
219 ok(!ret, "Menu handle should have been destroyed\n");
221 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
222 /* did we process the WM_INITMENU message? */
223 ret = GetWindowLongPtr(hwnd, GWLP_USERDATA);
225 ok(ret, "WM_INITMENU should have been sent\n");
231 static void test_menu_ownerdraw(void)
237 HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
238 WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200,
239 NULL, NULL, NULL, NULL);
240 ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError());
242 SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG)menu_ownerdraw_wnd_proc);
243 hmenu = CreatePopupMenu();
244 ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError());
245 if( !hmenu) { DestroyWindow(hwnd);return;}
247 for( j=0;j<2;j++) /* create columns */
248 for(i=0;i<2;i++) { /* create rows */
249 ret = AppendMenu( hmenu, MF_OWNERDRAW |
250 (i==0 ? MF_MENUBREAK : 0), k, (LPCTSTR) k);
252 ok( ret, "AppendMenu failed for %d\n", k-1);
255 assert( k <= sizeof(MOD_rc)/sizeof(RECT));
256 /* display the menu */
257 ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
259 /* columns have a 4 pixel gap between them */
260 ok( MOD_rc[0].right + 4 == MOD_rc[2].left,
261 "item rectangles are not separated by 4 pixels space\n");
262 /* height should be what the MEASUREITEM message has returned */
263 ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE,
264 "menu item has wrong height: %ld should be %d\n",
265 MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE);
266 /* no gaps between the rows */
267 ok( MOD_rc[0].bottom - MOD_rc[1].top == 0,
268 "There should not be a space between the rows, gap is %ld\n",
269 MOD_rc[0].bottom - MOD_rc[1].top);
270 /* test the correct value of the item height that was sent
271 * by the WM_MEASUREITEM message */
272 ok( MOD_odheight == HIWORD( GetDialogBaseUnits()) || /* WinNT,2k,XP */
273 MOD_odheight == MOD_hic, /* Win95,98,ME */
274 "Wrong height field in MEASUREITEMSTRUCT, expected %d or %d actual %d\n",
275 HIWORD( GetDialogBaseUnits()), MOD_hic, MOD_odheight);
276 /* test what MF_MENUBREAK did at the first position. Also show
277 * that an MF_SEPARATOR is ignored in the height calculation. */
278 leftcol= MOD_rc[0].left;
279 ModifyMenu( hmenu, 0, MF_BYCOMMAND| MF_OWNERDRAW| MF_SEPARATOR, 0, 0);
280 /* display the menu */
281 ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
282 /* left should be 4 pixels less now */
283 ok( leftcol == MOD_rc[0].left + 4,
284 "columns should be 4 pixels to the left (actual %ld).\n",
285 leftcol - MOD_rc[0].left);
287 ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE,
288 "width of owner drawn menu item is wrong. Got %ld expected %d\n",
289 MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE);
291 ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE,
292 "Height is incorrect. Got %ld expected %d\n",
293 MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE);
295 /* test width/height of an ownerdraw menu bar as well */
296 ret = DestroyMenu(hmenu);
297 ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
298 hmenu = CreateMenu();
299 ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError());
300 if( !hmenu) { DestroyWindow(hwnd);return;}
303 ret = AppendMenu( hmenu, MF_OWNERDRAW , i, 0);
304 ok( ret, "AppendMenu failed for %d\n", i);
306 ret = SetMenu( hwnd, hmenu);
307 UpdateWindow( hwnd); /* hack for wine to draw the window + menu */
308 ok(ret, "SetMenu failed with error %ld\n", GetLastError());
310 ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE,
311 "width of owner drawn menu item is wrong. Got %ld expected %d\n",
312 MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE);
314 ok( MOD_rc[0].bottom - MOD_rc[0].top == GetSystemMetrics( SM_CYMENU) - 1,
315 "Height of owner drawn menu item is wrong. Got %ld expected %d\n",
316 MOD_rc[0].bottom - MOD_rc[0].top, GetSystemMetrics( SM_CYMENU) - 1);
319 ret = DestroyMenu(hmenu);
320 ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
324 /* helper for test_menu_bmp_and_string() */
325 static void test_mbs_help( int ispop, int hassub, int mnuopt,
326 HWND hwnd, int arrowwidth, int count, HBITMAP hbmp,
327 SIZE bmpsize, char *text, SIZE size, SIZE sc_size)
330 HMENU hmenu, submenu;
331 MENUITEMINFO mii={ sizeof( MENUITEMINFO )};
337 MOD_GotDrawItemMsg = FALSE;
338 mii.fMask = MIIM_FTYPE | MIIM_DATA | MIIM_STATE;
340 mii.fState = MF_CHECKED;
342 MODsizes[0] = bmpsize;
346 mii.fMask |= MIIM_STRING;
347 mii.dwTypeData = text;
348 if( ( p = strchr( text, '\t'))) {
349 hastab = *(p + 1) ? 2 : 1;
352 /* tabs don't make sense in menubars */
353 if(hastab && !ispop) return;
355 mii.fMask |= MIIM_BITMAP;
358 submenu = CreateMenu();
359 ok( submenu != 0, "CreateMenu failed with error %ld\n", GetLastError());
361 hmenu = CreatePopupMenu();
363 hmenu = CreateMenu();
364 ok( hmenu != 0, "Create{Popup}Menu failed with error %ld\n", GetLastError());
366 mii.fMask |= MIIM_SUBMENU;
367 mii.hSubMenu = submenu;
370 mi.cbSize = sizeof(mi);
371 mi.fMask = MIM_STYLE;
372 pGetMenuInfo( hmenu, &mi);
373 mi.dwStyle |= mnuopt == 1 ? MNS_NOCHECK : MNS_CHECKORBMP;
374 ret = pSetMenuInfo( hmenu, &mi);
375 ok( ret, "SetMenuInfo failed with error %ld\n", GetLastError());
377 ret = InsertMenuItem( hmenu, 0, FALSE, &mii);
378 ok( ret, "InsertMenuItem failed with error %ld\n", GetLastError());
380 if( winetest_debug) {
382 RECT rc = {100, 50, 400, 70};
385 sprintf( buf,"%d text \"%s\" mnuopt %d", count, text ? text: "(nil)", mnuopt);
386 FillRect( hdc, &rc, (HBRUSH) COLOR_WINDOW);
387 TextOut( hdc, 100, 50, buf, strlen( buf));
388 ReleaseDC( hwnd, hdc);
391 ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
393 ret = SetMenu( hwnd, hmenu);
394 ok(ret, "SetMenu failed with error %ld\n", GetLastError());
397 ret = GetMenuItemRect( hwnd, hmenu, 0, &rc);
398 /* check menu width */
400 expect = ( text || hbmp ?
401 4 + (mnuopt != 1 ? GetSystemMetrics(SM_CXMENUCHECK) : 0)
403 arrowwidth + MOD_avec + (hbmp ? bmpsize.cx + 2 : 0) +
404 (text && hastab ? /* TAB space */
405 MOD_avec + ( hastab==2 ? sc_size.cx : 0) : 0) +
406 (text ? 2 + (text[0] ? size.cx :0): 0) ;
408 expect = !(text || hbmp) ? 0 :
409 ( hbmp ? (text ? 2:0) + bmpsize.cx : 0 ) +
410 (text ? 2 * MOD_avec + (text[0] ? size.cx :0): 0) ;
411 ok( rc.right - rc.left == expect,
412 "menu width wrong, got %ld expected %d\n", rc.right - rc.left, expect);
413 failed = failed || !(rc.right - rc.left == expect);
414 /* check menu height */
416 expect = max( ( !(text || hbmp) ? GetSystemMetrics( SM_CYMENUSIZE)/2 : 0),
417 max( (text ? max( 2 + size.cy, MOD_hic + 4) : 0),
418 (hbmp ? bmpsize.cy + 2 : 0)));
420 expect = ( !(text || hbmp) ? GetSystemMetrics( SM_CYMENUSIZE)/2 :
421 max( GetSystemMetrics( SM_CYMENU) - 1, (hbmp ? bmpsize.cy : 0)));
422 ok( rc.bottom - rc.top == expect,
423 "menu height wrong, got %ld expected %d (%d)\n",
424 rc.bottom - rc.top, expect, GetSystemMetrics( SM_CYMENU));
425 failed = failed || !(rc.bottom - rc.top == expect);
426 if( hbmp == HBMMENU_CALLBACK && MOD_GotDrawItemMsg) {
427 /* check the position of the bitmap */
429 expect = ispop ? (4 + ( mnuopt ? 0 : GetSystemMetrics(SM_CXMENUCHECK)))
431 ok( expect == MOD_rc[0].left,
432 "bitmap left is %ld expected %d\n", MOD_rc[0].left, expect);
433 failed = failed || !(expect == MOD_rc[0].left);
435 expect = (rc.bottom - rc.top - MOD_rc[0].bottom + MOD_rc[0].top) / 2;
436 ok( expect == MOD_rc[0].top,
437 "bitmap top is %ld expected %d\n", MOD_rc[0].top, expect);
438 failed = failed || !(expect == MOD_rc[0].top);
440 /* if there was a failure, report details */
442 trace("*** count %d text \"%s\" bitmap %p bmsize %ld,%ld textsize %ld+%ld,%ld mnuopt %d hastab %d\n",
443 count, text ? text: "(nil)", hbmp, bmpsize.cx, bmpsize.cy,
444 size.cx, size.cy, sc_size.cx, mnuopt, hastab);
445 trace(" check %d,%d arrow %d avechar %d\n",
446 GetSystemMetrics(SM_CXMENUCHECK ),
447 GetSystemMetrics(SM_CYMENUCHECK ),arrowwidth, MOD_avec);
448 if( hbmp == HBMMENU_CALLBACK)
449 trace( " rc %ld,%ld-%ld,%ld bmp.rc %ld,%ld-%ld,%ld\n",
450 rc.left, rc.top, rc.top, rc.bottom, MOD_rc[0].left,
451 MOD_rc[0].top,MOD_rc[0].right, MOD_rc[0].bottom);
454 ret = DestroyMenu(submenu);
455 ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
456 ret = DestroyMenu(hmenu);
457 ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
461 static void test_menu_bmp_and_string(void)
468 int count, szidx, txtidx, bmpidx, hassub, mnuopt, ispop;
470 if( !pGetMenuInfo) return;
472 memset( bmfill, 0x55, sizeof( bmfill));
473 hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
474 WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200,
475 NULL, NULL, NULL, NULL);
476 hbm_arrow=LoadBitmap( 0, (CHAR*)OBM_MNARROW);
477 GetObject( hbm_arrow, sizeof(bm), &bm);
478 arrowwidth = bm.bmWidth;
480 ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError());
482 SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG)menu_ownerdraw_wnd_proc);
485 trace(" check %d,%d arrow %d avechar %d\n",
486 GetSystemMetrics(SM_CXMENUCHECK ),
487 GetSystemMetrics(SM_CYMENUCHECK ),arrowwidth, MOD_avec);
490 for( ispop=1; ispop >= 0; ispop--){
491 static SIZE bmsizes[]= {
492 {10,10},{38,38},{1,30},{55,5}};
493 for( szidx=0; szidx < sizeof( bmsizes) / sizeof( SIZE); szidx++) {
494 HBITMAP hbm = CreateBitmap( bmsizes[szidx].cx, bmsizes[szidx].cy,1,1,bmfill);
495 HBITMAP bitmaps[] = { HBMMENU_CALLBACK, hbm, NULL };
496 ok( (int)hbm, "CreateBitmap failed err %ld\n", GetLastError());
497 for( txtidx = 0; txtidx < sizeof(MOD_txtsizes)/sizeof(MOD_txtsizes[0]); txtidx++) {
498 for( hassub = 0; hassub < 2 ; hassub++) { /* add submenu item */
499 for( mnuopt = 0; mnuopt < 3 ; mnuopt++){ /* test MNS_NOCHECK/MNS_CHECKORBMP */
500 for( bmpidx = 0; bmpidx <sizeof(bitmaps)/sizeof(HBITMAP); bmpidx++) {
501 /* no need to test NULL bitmaps of several sizes */
502 if( !bitmaps[bmpidx] && szidx > 0) continue;
503 if( !ispop && hassub) continue;
504 test_mbs_help( ispop, hassub, mnuopt,
505 hwnd, arrowwidth, ++count,
508 MOD_txtsizes[txtidx].text,
509 MOD_txtsizes[txtidx].size,
510 MOD_txtsizes[txtidx].sc_size);
522 static void test_menu_add_string( void )
532 WCHAR strbackW[0x80];
533 static CHAR blah[] = "blah";
534 static const WCHAR expectedString[] = {'D','u','m','m','y',' ','s','t','r','i','n','g', 0};
536 hmenu = CreateMenu();
538 memset( &info, 0, sizeof info );
539 info.cbSize = sizeof info;
540 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_ID;
541 info.dwTypeData = blah;
546 InsertMenuItem(hmenu, 0, TRUE, &info );
548 memset( &info, 0, sizeof info );
549 info.cbSize = sizeof info;
550 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_DATA | MIIM_ID;
551 info.dwTypeData = string;
552 info.cch = sizeof string;
554 GetMenuItemInfo( hmenu, 0, TRUE, &info );
556 ok( !strcmp( string, "blah" ), "menu item name differed\n");
558 /* Test combination of ownerdraw and strings with GetMenuItemString(A/W) */
559 strcpy(string, "Dummy string");
560 memset(&info, 0x00, sizeof(info));
561 info.cbSize= sizeof(MENUITEMINFO);
562 info.fMask= MIIM_FTYPE | MIIM_STRING; /* Set OwnerDraw + typeData */
563 info.fType= MFT_OWNERDRAW;
564 info.dwTypeData= string;
565 rc = InsertMenuItem( hmenu, 0, TRUE, &info );
566 ok (rc, "InsertMenuItem failed\n");
568 strcpy(string,"Garbage");
569 ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
570 ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
572 ok (GetMenuStringW( hmenu, 0, (WCHAR *)strbackW, 99, MF_BYPOSITION), "GetMenuStringW on ownerdraw entry failed\n");
573 ok (!lstrcmpW( strbackW, expectedString ), "Menu text from Unicode version incorrect\n");
575 /* Just change ftype to string and see what text is stored */
576 memset(&info, 0x00, sizeof(info));
577 info.cbSize= sizeof(MENUITEMINFO);
578 info.fMask= MIIM_FTYPE; /* Set string type */
579 info.fType= MFT_STRING;
580 info.dwTypeData= (char *)0xdeadbeef;
581 rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
582 ok (rc, "SetMenuItemInfo failed\n");
584 /* Did we keep the old dwTypeData? */
585 ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
586 ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
588 /* Ensure change to bitmap type fails */
589 memset(&info, 0x00, sizeof(info));
590 info.cbSize= sizeof(MENUITEMINFO);
591 info.fMask= MIIM_FTYPE; /* Set as bitmap type */
592 info.fType= MFT_BITMAP;
593 info.dwTypeData= (char *)0xdeadbee2;
594 rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
595 ok (!rc, "SetMenuItemInfo unexpectedly worked\n");
597 /* Just change ftype back and ensure data hasn't been freed */
598 info.fType= MFT_OWNERDRAW; /* Set as ownerdraw type */
599 info.dwTypeData= (char *)0xdeadbee3;
600 rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
601 ok (rc, "SetMenuItemInfo failed\n");
603 /* Did we keep the old dwTypeData? */
604 ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
605 ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
607 /* Just change string value (not type) */
608 memset(&info, 0x00, sizeof(info));
609 info.cbSize= sizeof(MENUITEMINFO);
610 info.fMask= MIIM_STRING; /* Set typeData */
611 strcpy(string2, "string2");
612 info.dwTypeData= string2;
613 rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
614 ok (rc, "SetMenuItemInfo failed\n");
616 ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
617 ok (!strcmp( strback, "string2" ), "Menu text from Ansi version incorrect\n");
619 /* crashes with wine 0.9.5 */
620 memset(&info, 0x00, sizeof(info));
621 info.cbSize= sizeof(MENUITEMINFO);
622 info.fMask= MIIM_FTYPE | MIIM_STRING; /* Set OwnerDraw + typeData */
623 info.fType= MFT_OWNERDRAW;
624 rc = InsertMenuItem( hmenu, 0, TRUE, &info );
625 ok (rc, "InsertMenuItem failed\n");
626 ok (!GetMenuString( hmenu, 0, NULL, 0, MF_BYPOSITION),
627 "GetMenuString on ownerdraw entry succeeded.\n");
628 ok (!GetMenuStringW( hmenu, 0, NULL, 0, MF_BYPOSITION),
629 "GetMenuStringW on ownerdraw entry succeeded.\n");
632 DestroyMenu( hmenu );
635 /* define building blocks for the menu item info tests */
636 static int strncmpW( const WCHAR *str1, const WCHAR *str2, int n )
638 if (n <= 0) return 0;
639 while ((--n > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; }
640 return *str1 - *str2;
643 static WCHAR *strcpyW( WCHAR *dst, const WCHAR *src )
646 while ((*p++ = *src++));
651 #define DMIINFF( i, e, field)\
652 ok((int)((i)->field)==(int)((e)->field) || (int)((i)->field)==(0xffff & (int)((e)->field)), \
653 "%s got 0x%x expected 0x%x\n", #field, (int)((i)->field), (int)((e)->field));
655 #define DUMPMIINF(s,i,e)\
657 DMIINFF( i, e, fMask)\
658 DMIINFF( i, e, fType)\
659 DMIINFF( i, e, fState)\
661 DMIINFF( i, e, hSubMenu)\
662 DMIINFF( i, e, hbmpChecked)\
663 DMIINFF( i, e, hbmpUnchecked)\
664 DMIINFF( i, e, dwItemData)\
665 DMIINFF( i, e, dwTypeData)\
667 if( s==sizeof(MENUITEMINFOA)) DMIINFF( i, e, hbmpItem)\
670 /* insert menu item */
671 #define TMII_INSMI( a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,n1,\
674 MENUITEMINFOA info1=a1 b1,c1,d1,e1,f1,(void*)g1,(void*)h1,(void*)i1,j1,(void*)k1,l1,(void*)m1 n1;\
675 HMENU hmenu = CreateMenu();\
676 BOOL ret, stop = FALSE;\
677 SetLastError( 0xdeadbeef);\
678 if(ansi)strcpy( string, init);\
679 else strcpyW( (WCHAR*)string, (WCHAR*)init);\
680 if( ansi) ret = InsertMenuItemA(hmenu, 0, TRUE, &info1 );\
681 else ret = InsertMenuItemW(hmenu, 0, TRUE, (MENUITEMINFOW*)&info1 );\
682 if( !(eret1)) { ok( (eret1)==ret,"InsertMenuItem should have failed.\n");\
684 } else ok( (eret1)==ret,"InsertMenuItem failed, err %ld\n",GetLastError());\
687 /* GetMenuItemInfo + GetMenuString */
688 #define TMII_GMII( a2,b2,c2,d2,e2,f2,g2,h2,i2,j2,k2,l2,m2,n2,\
689 a3,b3,c3,d3,e3,f3,g3,h3,i3,j3,k3,l3,m3,n3,\
690 expname, eret2, eret3)\
692 MENUITEMINFOA info2A=a2 b2,c2,d2,e2,f2,(void*)g2,(void*)h2,(void*)i2,j2,(void*)k2,l2,(void*)m2 n2;\
693 MENUITEMINFOA einfoA=a3 b3,c3,d3,e3,f3,(void*)g3,(void*)h3,(void*)i3,j3,(void*)k3,l3,(void*)m3 n3;\
694 MENUITEMINFOA *info2 = &info2A;\
695 MENUITEMINFOA *einfo = &einfoA;\
696 MENUITEMINFOW *info2W = (MENUITEMINFOW *)&info2A;\
698 ret = ansi ? GetMenuItemInfoA( hmenu, 0, TRUE, info2 ) :\
699 GetMenuItemInfoW( hmenu, 0, TRUE, info2W );\
700 if( !(eret2)) ok( (eret2)==ret,"GetMenuItemInfo should have failed.\n");\
702 ok( (eret2)==ret,"GetMenuItemInfo failed, err %ld\n",GetLastError());\
703 ret = memcmp( info2, einfo, sizeof einfoA);\
704 /* ok( ret==0, "Got wrong menu item info data\n");*/\
705 if( ret) DUMPMIINF(info2A.cbSize, &info2A, &einfoA)\
706 if( einfo->dwTypeData == string) {\
707 if(ansi) ok( !strncmp( expname, info2->dwTypeData, einfo->cch ), "menu item name differed \"%s\"\n",\
708 einfo->dwTypeData ? einfo->dwTypeData: "");\
709 else ok( !strncmpW( (WCHAR*)expname, (WCHAR*)info2->dwTypeData, einfo->cch ), "menu item name differed \"%s\"\n",\
710 einfo->dwTypeData ? einfo->dwTypeData: "");\
711 ret = ansi ? GetMenuStringA( hmenu, 0, string, 80, MF_BYPOSITION) :\
712 GetMenuStringW( hmenu, 0, string, 80, MF_BYPOSITION);\
714 ok( ret, "GetMenuString failed, err %ld\n",GetLastError());\
716 ok( !ret, "GetMenuString should have failed\n");\
723 RemoveMenu(hmenu, 0, TRUE );\
724 DestroyMenu( hmenu );\
725 DestroyMenu( submenu );\
726 submenu = CreateMenu();\
729 #define TMII_MODM( flags, id, data, eret )\
731 if(ansi)ret = ModifyMenuA( hmenu, 0, flags, (UINT_PTR)id, (char*)data);\
732 else ret = ModifyMenuW( hmenu, 0, flags, (UINT_PTR)id, (WCHAR*)data);\
733 if( !(eret)) ok( (eret)==ret,"ModifyMenuA should have failed.\n");\
734 else ok( (eret)==ret,"ModifyMenuA failed, err %ld\n",GetLastError());\
737 /* SetMenuItemInfo */
738 #define TMII_SMII( a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,n1,\
741 MENUITEMINFOA info1=a1 b1,c1,d1,e1,f1,(void*)g1,(void*)h1,(void*)i1,j1,(void*)k1,l1,(void*)m1 n1;\
742 SetLastError( 0xdeadbeef);\
743 if(ansi)strcpy( string, init);\
744 else strcpyW( (WCHAR*)string, (WCHAR*)init);\
745 if( ansi) ret = SetMenuItemInfoA(hmenu, 0, TRUE, &info1 );\
746 else ret = SetMenuItemInfoW(hmenu, 0, TRUE, (MENUITEMINFOW*)&info1 );\
747 if( !(eret1)) { ok( (eret1)==ret,"InsertMenuItem should have failed.\n");\
749 } else ok( (eret1)==ret,"InsertMenuItem failed, err %ld\n",GetLastError());\
758 static void test_menu_iteminfo( void )
760 int S=sizeof( MENUITEMINFOA);
765 WCHAR txtW[]={'W','i','n','e',0};
766 WCHAR initW[]={'X','Y','Z',0};
768 void *txt, *init, *empty, *string;
769 HBITMAP hbm = CreateBitmap(1,1,1,1,NULL);
771 HMENU submenu=CreateMenu();
774 if( ansi) {txt=txtA;init=initA;empty=emptyA;string=stringA;}
775 else {txt=txtW;init=initW;empty=emptyW;string=stringA;}
776 trace( "%s string %p hbm %p txt %p\n", ansi ? "ANSI tests: " : "Unicode tests:", string, hbm, txt);
777 /* test all combinations of MFT_STRING, MFT_OWNERDRAW and MFT_BITMAP */
778 /* (since MFT_STRING is zero, there are four of them) */
779 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, 0, 0, 0, 0, 0, 0, txt, 0, 0, }, OK)
780 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
781 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
784 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
785 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
786 {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, 0, 0, 0, },
789 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, 6, -1, }, OK)
790 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
791 {, S, MIIM_TYPE, MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
794 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, hbm, 6, -1, }, OK)
795 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
796 {, S, MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
799 /* not enough space for name*/
800 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
801 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, NULL, 0, -9, },
802 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, NULL, 4, 0, },
805 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
806 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 5, -9, },
807 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
810 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
811 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 4, -9, },
812 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 3, 0, },
815 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
816 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, NULL, 0, -9, },
817 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 0, 0, },
820 /* cannot combine MIIM_TYPE with some other flags */
821 TMII_INSMI( {, S, MIIM_TYPE|MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, ER)
822 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
823 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
826 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
827 TMII_GMII ( {, S, MIIM_TYPE|MIIM_STRING, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
828 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
831 TMII_INSMI( {, S, MIIM_TYPE|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, ER)
832 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
833 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
836 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
837 TMII_GMII ( {, S, MIIM_TYPE|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
838 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
841 TMII_INSMI( {, S, MIIM_TYPE|MIIM_BITMAP, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, 6, hbm, }, ER)
842 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
843 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
846 /* but succeeds with some others */
847 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
848 TMII_GMII ( {, S, MIIM_TYPE|MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
849 {, S, MIIM_TYPE|MIIM_SUBMENU, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
852 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
853 TMII_GMII ( {, S, MIIM_TYPE|MIIM_STATE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
854 {, S, MIIM_TYPE|MIIM_STATE, MFT_STRING, 0, -9, 0, -9, -9, -9, string, 4, 0, },
857 TMII_INSMI( {, S, MIIM_TYPE|MIIM_ID, MFT_STRING, -1, 888, -1, -1, -1, -1, txt, 6, -1, }, OK)
858 TMII_GMII ( {, S, MIIM_TYPE|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
859 {, S, MIIM_TYPE|MIIM_ID, MFT_STRING, -9, 888, 0, -9, -9, -9, string, 4, 0, },
862 TMII_INSMI( {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING, -1, -1, -1, -1, -1, 999, txt, 6, -1, }, OK)
863 TMII_GMII ( {, S, MIIM_TYPE|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
864 {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING, -9, -9, 0, -9, -9, 999, string, 4, 0, },
867 /* to be continued */
868 /* set text with MIIM_TYPE and retrieve with MIIM_STRING */
869 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
870 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
871 {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, -9, },
874 /* set text with MIIM_TYPE and retrieve with MIIM_STRING; MFT_OWNERDRAW causes an empty string */
875 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
876 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
877 {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 0, -9, },
880 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
881 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
882 {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 0, -9, },
885 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
886 TMII_GMII ( {, S, MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
887 {, S, MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 80, -9, },
890 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
891 TMII_GMII ( {, S, 0, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
892 {, S, 0, -9, -9, -9, 0, -9, -9, -9, string, 80, -9, },
895 /* contrary to MIIM_TYPE,you can set the text for an owner draw menu */
896 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
897 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
898 {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 4, -9, },
901 /* same but retrieve with MIIM_TYPE */
902 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
903 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
904 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
907 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
908 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
909 {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 0, -9, },
912 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -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_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 0, -9, },
918 /* How is that with bitmaps? */
919 TMII_INSMI( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
920 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
921 {, S, MIIM_TYPE, MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
924 TMII_INSMI( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
925 TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
926 {, S, MIIM_BITMAP|MIIM_FTYPE, 0, -9, -9, 0, -9, -9, -9, string, 80, hbm, },
929 /* MIIM_BITMAP does not like MFT_BITMAP */
930 TMII_INSMI( {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, ER)
931 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
932 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
935 /* no problem with OWNERDRAWN */
936 TMII_INSMI( {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
937 TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
938 {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 80, hbm, },
941 /* setting MFT_BITMAP with MFT_FTYPE fails anyway */
942 TMII_INSMI( {, S, MIIM_FTYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, }, ER)
943 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
944 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
948 /* menu with submenu */
949 TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, -1, -1, -1, txt, 0, -1, }, OK)
950 TMII_GMII ( {, S, MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
951 {, S, MIIM_SUBMENU, -9, -9, -9, submenu, -9, -9, -9, string, 80, -9, },
954 TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, -1, -1, -1, empty, 0, -1, }, OK)
955 TMII_GMII ( {, S, MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
956 {, S, MIIM_SUBMENU, -9, -9, -9, submenu, -9, -9, -9, string, 80, -9, },
959 /* menu with submenu, without MIIM_SUBMENU the submenufield is cleared */
960 TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, -1, -1, -1, txt, 0, -1, }, OK)
961 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
962 {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 0, -9, },
964 TMII_GMII ( {, S, MIIM_SUBMENU|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
965 {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, submenu, -9, -9, -9, string, 80, -9, },
968 /* menu with invalid submenu */
969 TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, 999, -1, -1, -1, txt, 0, -1, }, ER)
970 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
971 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
975 TMII_INSMI( {, S, MIIM_TYPE, MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, txt, 0, 0, }, OK)
976 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
977 {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, 0, 0, 0, },
980 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP|MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, hbm, 6, -1, }, OK)
981 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
982 {, S, MIIM_TYPE, MFT_BITMAP|MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
985 /* SEPARATOR and STRING go well together */
986 /* BITMAP and STRING go well together */
987 TMII_INSMI( {, S, MIIM_STRING|MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
988 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
989 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, hbm, },
992 /* BITMAP, SEPARATOR and STRING go well together */
993 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
994 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
995 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 4, hbm, },
998 /* last two tests, but use MIIM_TYPE to retrieve info */
999 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
1000 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1001 {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1004 TMII_INSMI( {, S, MIIM_STRING|MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1005 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1006 {, S, MIIM_TYPE, MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
1009 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1010 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1011 {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
1014 /* same three with MFT_OWNERDRAW */
1015 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
1016 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1017 {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1020 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1021 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1022 {, S, MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
1025 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1026 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1027 {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_BITMAP|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
1031 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE|MIIM_ID, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
1032 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1033 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1036 /* test with modifymenu: string is preserved after seting OWNERDRAW */
1037 TMII_INSMI( {, S, MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
1038 TMII_MODM( MFT_OWNERDRAW, -1, 787, OK)
1039 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1040 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_DATA, MFT_OWNERDRAW, -9, -9, 0, -9, -9, 787, string, 4, -9, },
1043 /* same with bitmap: now the text is cleared */
1044 TMII_INSMI( {, S, MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
1045 TMII_MODM( MFT_BITMAP, 545, hbm, OK)
1046 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1047 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_BITMAP, -9, 545, 0, -9, -9, -9, string, 0, hbm, },
1050 /* start with bitmap: now setting text clears it (though he flag is raised) */
1051 TMII_INSMI( {, S, MIIM_BITMAP, MFT_STRING, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1052 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1053 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_STRING, -9, 0, 0, -9, -9, -9, string, 0, hbm, },
1055 TMII_MODM( MFT_STRING, 545, txt, OK)
1056 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1057 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_STRING, -9, 545, 0, -9, -9, -9, string, 4, 0, },
1060 /*repeat with text NULL */
1061 TMII_INSMI( {, S, MIIM_BITMAP, MFT_STRING, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1062 TMII_MODM( MFT_STRING, 545, NULL, OK)
1063 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1064 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_SEPARATOR, -9, 545, 0, -9, -9, -9, string, 0, 0, },
1067 /* repeat with text "" */
1068 TMII_INSMI( {, S, MIIM_BITMAP, -1 , -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1069 TMII_MODM( MFT_STRING, 545, empty, OK)
1070 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1071 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_STRING, -9, 545, 0, -9, -9, -9, string, 0, 0, },
1074 /* start with bitmap: set ownerdraw */
1075 TMII_INSMI( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1076 TMII_MODM( MFT_OWNERDRAW, -1, 232, OK)
1077 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1078 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_DATA, MFT_OWNERDRAW, -9, -9, 0, -9, -9, 232, string, 0, hbm, },
1082 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1083 TMII_GMII ( {, S, 0, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1084 {, S, 0, -9, -9, -9, 0, -9, -9, -9, string, 80, -9, },
1087 /* some tests with small cbSize: the hbmpItem is to be ignored */
1088 TMII_INSMI( {, S - 4, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1089 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1090 {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, NULL, 0, NULL, },
1093 TMII_INSMI( {, S - 4, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1094 TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1095 {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 80, NULL, },
1098 TMII_INSMI( {, S - 4, MIIM_STRING|MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1099 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1100 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, NULL, },
1103 TMII_INSMI( {, S - 4, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1104 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1105 {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1108 TMII_INSMI( {, S - 4, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1109 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1110 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1113 TMII_INSMI( {, S - 4, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1114 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1115 {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1118 /* MIIM_TYPE by itself does not get/set the dwItemData for OwnerDrawn menus */
1119 TMII_INSMI( {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, 343, txt, 0, -1, }, OK)
1120 TMII_GMII ( {, S, MIIM_TYPE|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1121 {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, 343, 0, 0, 0, },
1124 TMII_INSMI( {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, 343, txt, 0, -1, }, OK)
1125 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1126 {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, 0, 0, 0, },
1129 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, 343, txt, 0, -1, }, OK)
1130 TMII_GMII ( {, S, MIIM_TYPE|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1131 {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, 0, 0, 0, 0, },
1134 /* set a string menu to ownerdraw with MIIM_TYPE */
1135 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -2, -2, -2, -2, -2, -2, txt, -2, -2, }, OK)
1136 TMII_SMII( {, S, MIIM_TYPE, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, -1, -1, -1, }, OK)
1137 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1138 {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 4, -9, },
1141 /* test with modifymenu add submenu */
1142 TMII_INSMI( {, S, MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
1143 TMII_MODM( MF_POPUP, submenu, txt, OK)
1144 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1145 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_SUBMENU, MFT_STRING, -9, -9, submenu, -9, -9, -9, string, 4, -9, },
1147 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1148 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
1151 /* MFT_SEPARATOR bit is kept when the text is added */
1152 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
1153 TMII_SMII( {, S, MIIM_STRING, -1, -1, -1, -1, -1, -1, -1, txt, -1, -1, }, OK)
1154 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1155 {, S, MIIM_STRING|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 4, -9, },
1158 /* MFT_SEPARATOR bit is kept when bitmap is added */
1159 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
1160 TMII_SMII( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1161 TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1162 {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 80, hbm, },
1166 } while( !(ansi = !ansi) );
1171 The following tests try to confirm the algorithm used to return the menu items
1172 when there is a collision between a menu item and a popup menu
1174 void test_menu_search_bycommand( void )
1176 HMENU hmenu, hmenuSub, hmenuSub2;
1182 static CHAR menuitem[] = "MenuItem",
1183 menuitem2[] = "MenuItem 2";
1185 /* Case 1: Menu containing a menu item */
1186 hmenu = CreateMenu();
1188 memset( &info, 0, sizeof info );
1189 info.cbSize = sizeof info;
1190 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1191 info.fType = MFT_STRING;
1192 strcpy(strIn, "Case 1 MenuItem");
1193 info.dwTypeData = strIn;
1194 info.wID = (UINT) 0x1234;
1196 rc = InsertMenuItem(hmenu, 0, TRUE, &info );
1197 ok (rc, "Inserting the menuitem failed\n");
1199 id = GetMenuItemID(hmenu, 0);
1200 ok (id == 0x1234, "Getting the menuitem id failed(gave %x)\n", id);
1202 /* Confirm the menuitem was given the id supplied (getting by position) */
1203 memset( &info, 0, sizeof info );
1205 info.cbSize = sizeof(MENUITEMINFO);
1206 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1207 info.dwTypeData = strback;
1208 info.cch = sizeof(strback);
1210 rc = GetMenuItemInfo(hmenu, 0, TRUE, &info); /* Get by position */
1211 ok (rc, "Getting the menu items info failed\n");
1212 ok (info.wID == 0x1234, "IDs differ for the menuitem\n");
1213 ok (!strcmp(info.dwTypeData, "Case 1 MenuItem"), "Returned item has wrong label\n");
1215 /* Search by id - Should return the item */
1216 memset( &info, 0, sizeof info );
1218 info.cbSize = sizeof(MENUITEMINFO);
1219 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1220 info.dwTypeData = strback;
1221 info.cch = sizeof(strback);
1222 rc = GetMenuItemInfo(hmenu, 0x1234, FALSE, &info); /* Get by ID */
1224 ok (rc, "Getting the menu items info failed\n");
1225 ok (info.wID == 0x1234, "IDs differ for the menuitem\n");
1226 ok (!strcmp(info.dwTypeData, "Case 1 MenuItem"), "Returned item has wrong label\n");
1228 DestroyMenu( hmenu );
1230 /* Case 2: Menu containing a popup menu */
1231 hmenu = CreateMenu();
1232 hmenuSub = CreateMenu();
1234 strcpy(strIn, "Case 2 SubMenu");
1235 rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, strIn);
1236 ok (rc, "Inserting the popup menu into the main menu failed\n");
1238 id = GetMenuItemID(hmenu, 0);
1239 ok (id == -1, "Getting the menuitem id unexpectedly worked (gave %x)\n", id);
1241 /* Confirm the menuitem itself was given an id the same as the HMENU, (getting by position) */
1242 memset( &info, 0, sizeof info );
1244 info.cbSize = sizeof(MENUITEMINFO);
1245 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1246 info.dwTypeData = strback;
1247 info.cch = sizeof(strback);
1248 info.wID = 0xdeadbeef;
1250 rc = GetMenuItemInfo(hmenu, 0, TRUE, &info); /* Get by position */
1251 ok (rc, "Getting the menu items info failed\n");
1252 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the menuitem\n");
1253 ok (!strcmp(info.dwTypeData, "Case 2 SubMenu"), "Returned item has wrong label\n");
1255 /* Search by id - returns the popup menu itself */
1256 memset( &info, 0, sizeof info );
1258 info.cbSize = sizeof(MENUITEMINFO);
1259 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1260 info.dwTypeData = strback;
1261 info.cch = sizeof(strback);
1262 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
1264 ok (rc, "Getting the menu items info failed\n");
1265 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
1266 ok (!strcmp(info.dwTypeData, "Case 2 SubMenu"), "Returned item has wrong label\n");
1269 Now add an item after it with the same id
1271 memset( &info, 0, sizeof info );
1272 info.cbSize = sizeof info;
1273 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1274 info.fType = MFT_STRING;
1275 strcpy(strIn, "Case 2 MenuItem 1");
1276 info.dwTypeData = strIn;
1277 info.wID = (UINT_PTR) hmenuSub;
1278 rc = InsertMenuItem(hmenu, -1, TRUE, &info );
1279 ok (rc, "Inserting the menuitem failed\n");
1281 /* Search by id - returns the item which follows the popup menu */
1282 memset( &info, 0, sizeof info );
1284 info.cbSize = sizeof(MENUITEMINFO);
1285 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1286 info.dwTypeData = strback;
1287 info.cch = sizeof(strback);
1288 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
1290 ok (rc, "Getting the menu items info failed\n");
1291 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
1292 ok (!strcmp(info.dwTypeData, "Case 2 MenuItem 1"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1295 Now add an item before the popup (with the same id)
1297 memset( &info, 0, sizeof info );
1298 info.cbSize = sizeof info;
1299 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1300 info.fType = MFT_STRING;
1301 strcpy(strIn, "Case 2 MenuItem 2");
1302 info.dwTypeData = strIn;
1303 info.wID = (UINT_PTR) hmenuSub;
1304 rc = InsertMenuItem(hmenu, 0, TRUE, &info );
1305 ok (rc, "Inserting the menuitem failed\n");
1307 /* Search by id - returns the item which precedes the popup menu */
1308 memset( &info, 0, sizeof info );
1310 info.cbSize = sizeof(MENUITEMINFO);
1311 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1312 info.dwTypeData = strback;
1313 info.cch = sizeof(strback);
1314 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
1316 ok (rc, "Getting the menu items info failed\n");
1317 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
1318 ok (!strcmp(info.dwTypeData, "Case 2 MenuItem 2"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1320 DestroyMenu( hmenu );
1321 DestroyMenu( hmenuSub );
1324 Case 3: Menu containing a popup menu which in turn
1325 contains 2 items with the same id as the popup itself
1328 hmenu = CreateMenu();
1329 hmenuSub = CreateMenu();
1331 memset( &info, 0, sizeof info );
1332 info.cbSize = sizeof info;
1333 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1334 info.fType = MFT_STRING;
1335 info.dwTypeData = menuitem;
1336 info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1338 rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
1339 ok (rc, "Inserting the popup menu into the main menu failed\n");
1341 rc = InsertMenuItem(hmenuSub, 0, TRUE, &info );
1342 ok (rc, "Inserting the sub menu menuitem failed\n");
1344 memset( &info, 0, sizeof info );
1345 info.cbSize = sizeof info;
1346 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1347 info.fType = MFT_STRING;
1348 info.dwTypeData = menuitem2;
1349 info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1351 rc = InsertMenuItem(hmenuSub, 1, TRUE, &info );
1352 ok (rc, "Inserting the sub menu menuitem 2 failed\n");
1354 /* Prove that you can't query the id of a popup directly (By position) */
1355 id = GetMenuItemID(hmenu, 0);
1356 ok (id == -1, "Getting the sub menu id should have failed because its a popup (gave %x)\n", id);
1358 /* Prove getting the item info via ID returns the first item (not the popup or 2nd item)*/
1359 memset( &info, 0, sizeof info );
1361 info.cbSize = sizeof(MENUITEMINFO);
1362 info.fMask = MIIM_STRING | MIIM_ID;
1363 info.dwTypeData = strback;
1364 info.cch = sizeof(strback);
1366 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info);
1367 ok (rc, "Getting the menus info failed\n");
1368 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for popup menu\n");
1369 ok (!strcmp(info.dwTypeData, "MenuItem"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1370 DestroyMenu( hmenu );
1371 DestroyMenu( hmenuSub );
1374 Case 4: Menu containing 2 popup menus, the second
1375 contains 2 items with the same id as the first popup menu
1377 hmenu = CreateMenu();
1378 hmenuSub = CreateMenu();
1379 hmenuSub2 = CreateMenu();
1381 rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
1382 ok (rc, "Inserting the popup menu into the main menu failed\n");
1384 rc = InsertMenu(hmenu, 1, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub2, "Submenu2");
1385 ok (rc, "Inserting the popup menu into the main menu failed\n");
1387 memset( &info, 0, sizeof info );
1388 info.cbSize = sizeof info;
1389 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1390 info.fType = MFT_STRING;
1391 info.dwTypeData = menuitem;
1392 info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1394 rc = InsertMenuItem(hmenuSub2, 0, TRUE, &info );
1395 ok (rc, "Inserting the sub menu menuitem failed\n");
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 info.dwTypeData = menuitem2;
1402 info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1404 rc = InsertMenuItem(hmenuSub2, 1, TRUE, &info );
1405 ok (rc, "Inserting the sub menu menuitem 2 failed\n");
1407 /* Prove getting the item info via ID returns the first item (not the popup or 2nd item)*/
1408 memset( &info, 0, sizeof info );
1410 info.cbSize = sizeof(MENUITEMINFO);
1411 info.fMask = MIIM_STRING | MIIM_ID;
1412 info.dwTypeData = strback;
1413 info.cch = sizeof(strback);
1415 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info);
1416 ok (rc, "Getting the menus info failed\n");
1417 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for popup menu\n");
1418 ok (!strcmp(info.dwTypeData, "MenuItem"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1420 memset( &info, 0, sizeof info );
1422 info.cbSize = sizeof(MENUITEMINFO);
1423 info.fMask = MIIM_STRING | MIIM_ID;
1424 info.dwTypeData = strback;
1425 info.cch = sizeof(strback);
1427 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub2, FALSE, &info);
1428 ok (rc, "Getting the menus info failed\n");
1429 ok (info.wID == (UINT)hmenuSub2, "IDs differ for popup menu\n");
1430 ok (!strcmp(info.dwTypeData, "Submenu2"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1432 DestroyMenu( hmenu );
1433 DestroyMenu( hmenuSub );
1434 DestroyMenu( hmenuSub2 );
1438 Case 5: Menu containing a popup menu which in turn
1439 contains an item with a different id than the popup menu.
1440 This tests the fallback to a popup menu ID.
1443 hmenu = CreateMenu();
1444 hmenuSub = CreateMenu();
1446 rc = AppendMenu(hmenu, MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
1447 ok (rc, "Appending the popup menu to the main menu failed\n");
1449 rc = AppendMenu(hmenuSub, MF_STRING, 102, "Item");
1450 ok (rc, "Appending the item to the popup menu failed\n");
1452 /* Set the ID for hmenuSub */
1453 info.cbSize = sizeof(info);
1454 info.fMask = MIIM_ID;
1457 rc = SetMenuItemInfo(hmenu, 0, TRUE, &info);
1458 ok(rc, "Setting the ID for the popup menu failed\n");
1460 /* Check if the ID has been set */
1462 rc = GetMenuItemInfo(hmenu, 0, TRUE, &info);
1463 ok(rc, "Getting the ID for the popup menu failed\n");
1464 ok(info.wID == 101, "The ID for the popup menu has not been set\n");
1466 /* Prove getting the item info via ID returns the popup menu */
1467 memset( &info, 0, sizeof(info));
1469 info.cbSize = sizeof(MENUITEMINFO);
1470 info.fMask = MIIM_STRING | MIIM_ID;
1471 info.dwTypeData = strback;
1472 info.cch = sizeof(strback);
1474 rc = GetMenuItemInfo(hmenu, 101, FALSE, &info);
1475 ok (rc, "Getting the menu info failed\n");
1476 ok (info.wID == 101, "IDs differ\n");
1477 ok (!strcmp(info.dwTypeData, "Submenu"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1479 /* Also look for the menu item */
1480 memset( &info, 0, sizeof(info));
1482 info.cbSize = sizeof(MENUITEMINFO);
1483 info.fMask = MIIM_STRING | MIIM_ID;
1484 info.dwTypeData = strback;
1485 info.cch = sizeof(strback);
1487 rc = GetMenuItemInfo(hmenu, 102, FALSE, &info);
1488 ok (rc, "Getting the menu info failed\n");
1489 ok (info.wID == 102, "IDs differ\n");
1490 ok (!strcmp(info.dwTypeData, "Item"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1493 DestroyMenu(hmenuSub);
1496 struct menu_item_pair_s {
1497 UINT uMenu; /* 1 - top level menu, [0-Menu 1-Enabled 2-Disabled]
1498 * 2 - 2nd level menu, [0-Popup 1-Enabled 2-Disabled]
1499 * 3 - 3rd level menu, [0-Enabled 1-Disabled] */
1503 static struct menu_mouse_tests_s {
1505 struct menu_item_pair_s menu_item_pairs[5]; /* for mousing */
1506 WORD wVk[5]; /* keys */
1510 /* for each test, send keys or clicks and check for menu visibility */
1511 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 0}, TRUE, FALSE }, /* test 0 */
1512 { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, 0}, FALSE, FALSE },
1513 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 0}, TRUE, FALSE },
1514 { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
1515 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 0}, TRUE, FALSE },
1516 { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
1517 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 0}, TRUE, FALSE },
1518 { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, VK_ESCAPE, 0}, FALSE, FALSE },
1519 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', VK_ESCAPE, 0}, TRUE, FALSE },
1520 { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, 0}, FALSE, FALSE },
1521 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 0}, TRUE, FALSE },
1522 { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
1523 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 0}, TRUE, FALSE },
1524 { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
1525 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 'P', 0}, TRUE, FALSE },
1526 { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
1527 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 'P', 0}, TRUE, FALSE },
1528 { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
1530 { INPUT_MOUSE, {{1, 2}, {0}}, {0}, TRUE, TRUE }, /* test 18 */
1531 { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
1532 { INPUT_MOUSE, {{1, 0}, {0}}, {0}, TRUE, TRUE },
1533 { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
1534 { INPUT_MOUSE, {{1, 0}, {2, 2}, {0}}, {0}, TRUE, TRUE },
1535 { INPUT_MOUSE, {{2, 1}, {0}}, {0}, FALSE, FALSE },
1536 { INPUT_MOUSE, {{1, 0}, {2, 0}, {0}}, {0}, TRUE, TRUE },
1537 { INPUT_MOUSE, {{3, 0}, {0}}, {0}, FALSE, FALSE },
1538 { INPUT_MOUSE, {{1, 0}, {2, 0}, {0}}, {0}, TRUE, TRUE },
1539 { INPUT_MOUSE, {{3, 1}, {0}}, {0}, TRUE, TRUE },
1540 { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
1544 static void send_key(WORD wVk)
1547 memset(&i, 0, 2*sizeof(INPUT));
1548 i[0].type = i[1].type = INPUT_KEYBOARD;
1549 i[0].u.ki.wVk = i[1].u.ki.wVk = wVk;
1550 i[1].u.ki.dwFlags = KEYEVENTF_KEYUP;
1551 SendInput(2, (INPUT *) i, sizeof(INPUT));
1554 static void click_menu(HANDLE hWnd, struct menu_item_pair_s *mi)
1556 HMENU hMenu = hMenus[mi->uMenu];
1560 int screen_w = GetSystemMetrics(SM_CXSCREEN);
1561 int screen_h = GetSystemMetrics(SM_CYSCREEN);
1563 GetMenuItemRect(mi->uMenu > 2 ? NULL : hWnd, hMenu, mi->uItem, &r);
1565 memset(&i, 0, 3*sizeof(INPUT));
1566 i[0].type = i[1].type = i[2].type = INPUT_MOUSE;
1567 i[0].u.mi.dx = i[1].u.mi.dx = i[2].u.mi.dx
1568 = ((r.left + 5) * 65535) / screen_w;
1569 i[0].u.mi.dy = i[1].u.mi.dy = i[2].u.mi.dy
1570 = ((r.top + 5) * 65535) / screen_h;
1571 i[0].u.mi.dwFlags = i[1].u.mi.dwFlags = i[2].u.mi.dwFlags
1572 = MOUSEEVENTF_ABSOLUTE;
1573 i[0].u.mi.dwFlags |= MOUSEEVENTF_MOVE;
1574 i[1].u.mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
1575 i[2].u.mi.dwFlags |= MOUSEEVENTF_LEFTUP;
1576 SendInput(3, (INPUT *) i, sizeof(INPUT));
1578 /* hack to prevent mouse message buildup in Wine */
1579 while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
1582 static DWORD WINAPI test_menu_input_thread(LPVOID lpParameter)
1585 HANDLE hWnd = lpParameter;
1588 /* mixed keyboard/mouse test */
1589 for (i = 0; menu_tests[i].type != -1; i++)
1593 if (menu_tests[i].type == INPUT_KEYBOARD)
1594 for (j = 0; menu_tests[i].wVk[j] != 0; j++)
1595 send_key(menu_tests[i].wVk[j]);
1597 for (j = 0; menu_tests[i].menu_item_pairs[j].uMenu != 0; j++)
1598 click_menu(hWnd, &menu_tests[i].menu_item_pairs[j]);
1600 while (menu_tests[i].bMenuVisible != bMenuVisible)
1608 if (menu_tests[i]._todo_wine)
1611 ok(menu_tests[i].bMenuVisible == bMenuVisible, "test %d\n", i);
1615 ok(menu_tests[i].bMenuVisible == bMenuVisible, "test %d\n", i);
1620 static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam,
1624 case WM_ENTERMENULOOP:
1625 bMenuVisible = TRUE;
1627 case WM_EXITMENULOOP:
1628 bMenuVisible = FALSE;
1631 return( DefWindowProcA( hWnd, msg, wParam, lParam ) );
1636 static void test_menu_input(void) {
1639 HINSTANCE hInstance = GetModuleHandleA( NULL );
1640 HANDLE hThread, hWnd;
1642 wclass.lpszClassName = "MenuTestClass";
1643 wclass.style = CS_HREDRAW | CS_VREDRAW;
1644 wclass.lpfnWndProc = WndProc;
1645 wclass.hInstance = hInstance;
1646 wclass.hIcon = LoadIconA( 0, (LPSTR)IDI_APPLICATION );
1647 wclass.hCursor = LoadCursorA( NULL, (LPSTR)IDC_ARROW);
1648 wclass.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1);
1649 wclass.lpszMenuName = 0;
1650 wclass.cbClsExtra = 0;
1651 wclass.cbWndExtra = 0;
1652 assert (RegisterClassA( &wclass ));
1653 assert (hWnd = CreateWindowA( wclass.lpszClassName, "MenuTest",
1654 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0,
1655 400, 200, NULL, NULL, hInstance, NULL) );
1658 hMenus[3] = CreatePopupMenu();
1659 AppendMenu(hMenus[3], MF_STRING, 0, "&Enabled");
1660 AppendMenu(hMenus[3], MF_STRING|MF_DISABLED, 0, "&Disabled");
1662 hMenus[2] = CreatePopupMenu();
1663 AppendMenu(hMenus[2], MF_STRING|MF_POPUP, (UINT_PTR) hMenus[3], "&Popup");
1664 AppendMenu(hMenus[2], MF_STRING, 0, "&Enabled");
1665 AppendMenu(hMenus[2], MF_STRING|MF_DISABLED, 0, "&Disabled");
1667 hMenus[1] = CreateMenu();
1668 AppendMenu(hMenus[1], MF_STRING|MF_POPUP, (UINT_PTR) hMenus[2], "&Menu");
1669 AppendMenu(hMenus[1], MF_STRING, 0, "&Enabled");
1670 AppendMenu(hMenus[1], MF_STRING|MF_DISABLED, 0, "&Disabled");
1672 SetMenu(hWnd, hMenus[1]);
1673 ShowWindow(hWnd, SW_SHOW);
1676 hThread = CreateThread(NULL, 0, test_menu_input_thread, hWnd, 0, NULL);
1679 if (WAIT_TIMEOUT != WaitForSingleObject(hThread, 50))
1681 while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
1683 DestroyWindow(hWnd);
1689 (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "SetMenuInfo" );
1691 (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetMenuInfo" );
1693 register_menu_check_class();
1695 test_menu_locked_by_window();
1696 test_menu_ownerdraw();
1697 test_menu_add_string();
1698 test_menu_iteminfo();
1699 test_menu_search_bycommand();
1700 test_menu_bmp_and_string();