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
35 #include "wine/test.h"
37 static ATOM atomMenuCheckClass;
39 static BOOL (WINAPI *pSetMenuInfo)(HMENU,LPCMENUINFO);
40 static BOOL (WINAPI *pGetMenuInfo)(HMENU,LPCMENUINFO);
42 static LRESULT WINAPI menu_check_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
46 case WM_ENTERMENULOOP:
47 /* mark window as having entered menu loop */
48 SetWindowLongPtr(hwnd, GWLP_USERDATA, TRUE);
49 /* exit menu modal loop
50 * ( A SendMessage does not work on NT3.51 here ) */
51 return PostMessage(hwnd, WM_CANCELMODE, 0, 0);
53 return DefWindowProc(hwnd, msg, wparam, lparam);
56 /* The MSVC headers ignore our NONAMELESSUNION requests so we have to define
69 /* globals to communicate between test and wndproc */
71 static BOOL bMenuVisible;
72 static HMENU hMenus[4];
77 /* menu texts with their sizes */
80 SIZE size; /* size of text up to any \t */
81 SIZE sc_size; /* size of the short-cut */
90 unsigned int MOD_maxid;
91 RECT MOD_rc[MOD_NRMENUS];
92 int MOD_avec, MOD_hic;
94 SIZE MODsizes[MOD_NRMENUS]= { {MOD_SIZE, MOD_SIZE},{MOD_SIZE, MOD_SIZE},
95 {MOD_SIZE, MOD_SIZE},{MOD_SIZE, MOD_SIZE}};
96 int MOD_GotDrawItemMsg = FALSE;
97 /* wndproc used by test_menu_ownerdraw() */
98 static LRESULT WINAPI menu_ownerdraw_wnd_proc(HWND hwnd, UINT msg,
99 WPARAM wparam, LPARAM lparam)
105 MEASUREITEMSTRUCT* pmis = (MEASUREITEMSTRUCT*)lparam;
107 trace("WM_MEASUREITEM received data %lx size %dx%d\n",
108 pmis->itemData, pmis->itemWidth, pmis->itemHeight);
109 MOD_odheight = pmis->itemHeight;
110 pmis->itemWidth = MODsizes[pmis->itemData].cx;
111 pmis->itemHeight = MODsizes[pmis->itemData].cy;
116 DRAWITEMSTRUCT * pdis;
119 char chrs[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
122 pdis = (DRAWITEMSTRUCT *) lparam;
123 if( winetest_debug) {
125 GetMenuItemRect( hwnd, (HMENU)pdis->hwndItem, pdis->itemData ,&rc);
126 trace("WM_DRAWITEM received hwnd %p hmenu %p itemdata %ld item %d rc %ld,%ld-%ld,%ld itemrc: %ld,%ld-%ld,%ld\n",
127 hwnd, (HMENU)pdis->hwndItem, pdis->itemData,
128 pdis->itemID, pdis->rcItem.left, pdis->rcItem.top,
129 pdis->rcItem.right,pdis->rcItem.bottom,
130 rc.left,rc.top,rc.right,rc.bottom);
131 oldpen=SelectObject( pdis->hDC, GetStockObject(
132 pdis->itemState & ODS_SELECTED ? WHITE_PEN :BLACK_PEN));
133 Rectangle( pdis->hDC, pdis->rcItem.left,pdis->rcItem.top,
134 pdis->rcItem.right,pdis->rcItem.bottom );
135 SelectObject( pdis->hDC, oldpen);
137 /* calculate widths of some menu texts */
138 if( ! MOD_txtsizes[0].size.cx)
139 for(i = 0; MOD_txtsizes[i].text; i++) {
142 strcpy( buf, MOD_txtsizes[i].text);
143 if( ( p = strchr( buf, '\t'))) {
145 DrawText( pdis->hDC, p + 1, -1, &rc,
146 DT_SINGLELINE|DT_CALCRECT);
147 MOD_txtsizes[i].sc_size.cx= rc.right - rc.left;
148 MOD_txtsizes[i].sc_size.cy= rc.bottom - rc.top;
150 DrawText( pdis->hDC, buf, -1, &rc,
151 DT_SINGLELINE|DT_CALCRECT);
152 MOD_txtsizes[i].size.cx= rc.right - rc.left;
153 MOD_txtsizes[i].size.cy= rc.bottom - rc.top;
156 if( pdis->itemData > MOD_maxid) return TRUE;
157 /* store the rectangl */
158 MOD_rc[pdis->itemData] = pdis->rcItem;
159 /* calculate average character width */
160 GetTextExtentPoint( pdis->hDC, chrs, 52, &sz );
161 MOD_avec = (sz.cx + 26)/52;
162 GetTextMetrics( pdis->hDC, &tm);
163 MOD_hic = tm.tmHeight;
164 MOD_GotDrawItemMsg = TRUE;
169 PostMessage(hwnd, WM_CANCELMODE, 0, 0);
174 return DefWindowProc(hwnd, msg, wparam, lparam);
177 static void register_menu_check_class(void)
185 GetModuleHandle(NULL),
187 LoadCursor(NULL, IDC_ARROW),
188 (HBRUSH)(COLOR_BTNFACE+1),
190 TEXT("WineMenuCheck"),
193 atomMenuCheckClass = RegisterClass(&wc);
196 /* demonstrates that windows locks the menu object so that it is still valid
197 * even after a client calls DestroyMenu on it */
198 static void test_menu_locked_by_window(void)
202 HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
203 WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200,
204 NULL, NULL, NULL, NULL);
205 ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError());
206 hmenu = CreateMenu();
207 ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError());
208 ret = InsertMenu(hmenu, 0, MF_STRING, 0, TEXT("&Test"));
209 ok(ret, "InsertMenu failed with error %ld\n", GetLastError());
210 ret = SetMenu(hwnd, hmenu);
211 ok(ret, "SetMenu failed with error %ld\n", GetLastError());
212 ret = DestroyMenu(hmenu);
213 ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
215 ret = DrawMenuBar(hwnd);
217 ok(ret, "DrawMenuBar failed with error %ld\n", GetLastError());
219 ret = IsMenu(GetMenu(hwnd));
220 ok(!ret, "Menu handle should have been destroyed\n");
222 SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
223 /* did we process the WM_INITMENU message? */
224 ret = GetWindowLongPtr(hwnd, GWLP_USERDATA);
226 ok(ret, "WM_INITMENU should have been sent\n");
232 static void test_menu_ownerdraw(void)
238 HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
239 WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200,
240 NULL, NULL, NULL, NULL);
241 ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError());
243 SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG)menu_ownerdraw_wnd_proc);
244 hmenu = CreatePopupMenu();
245 ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError());
246 if( !hmenu) { DestroyWindow(hwnd);return;}
248 for( j=0;j<2;j++) /* create columns */
249 for(i=0;i<2;i++) { /* create rows */
250 ret = AppendMenu( hmenu, MF_OWNERDRAW |
251 (i==0 ? MF_MENUBREAK : 0), k, (LPCTSTR) k);
253 ok( ret, "AppendMenu failed for %d\n", k-1);
256 assert( k <= sizeof(MOD_rc)/sizeof(RECT));
257 /* display the menu */
258 ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
260 /* columns have a 4 pixel gap between them */
261 ok( MOD_rc[0].right + 4 == MOD_rc[2].left,
262 "item rectangles are not separated by 4 pixels space\n");
263 /* height should be what the MEASUREITEM message has returned */
264 ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE,
265 "menu item has wrong height: %ld should be %d\n",
266 MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE);
267 /* no gaps between the rows */
268 ok( MOD_rc[0].bottom - MOD_rc[1].top == 0,
269 "There should not be a space between the rows, gap is %ld\n",
270 MOD_rc[0].bottom - MOD_rc[1].top);
271 /* test the correct value of the item height that was sent
272 * by the WM_MEASUREITEM message */
273 ok( MOD_odheight == HIWORD( GetDialogBaseUnits()) || /* WinNT,2k,XP */
274 MOD_odheight == MOD_hic, /* Win95,98,ME */
275 "Wrong height field in MEASUREITEMSTRUCT, expected %d or %d actual %d\n",
276 HIWORD( GetDialogBaseUnits()), MOD_hic, MOD_odheight);
277 /* test what MF_MENUBREAK did at the first position. Also show
278 * that an MF_SEPARATOR is ignored in the height calculation. */
279 leftcol= MOD_rc[0].left;
280 ModifyMenu( hmenu, 0, MF_BYCOMMAND| MF_OWNERDRAW| MF_SEPARATOR, 0, 0);
281 /* display the menu */
282 ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
283 /* left should be 4 pixels less now */
284 ok( leftcol == MOD_rc[0].left + 4,
285 "columns should be 4 pixels to the left (actual %ld).\n",
286 leftcol - MOD_rc[0].left);
288 ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE,
289 "width of owner drawn menu item is wrong. Got %ld expected %d\n",
290 MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE);
292 ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE,
293 "Height is incorrect. Got %ld expected %d\n",
294 MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE);
296 /* test width/height of an ownerdraw menu bar as well */
297 ret = DestroyMenu(hmenu);
298 ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
299 hmenu = CreateMenu();
300 ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError());
301 if( !hmenu) { DestroyWindow(hwnd);return;}
304 ret = AppendMenu( hmenu, MF_OWNERDRAW , i, 0);
305 ok( ret, "AppendMenu failed for %d\n", i);
307 ret = SetMenu( hwnd, hmenu);
308 UpdateWindow( hwnd); /* hack for wine to draw the window + menu */
309 ok(ret, "SetMenu failed with error %ld\n", GetLastError());
311 ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE,
312 "width of owner drawn menu item is wrong. Got %ld expected %d\n",
313 MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE);
315 ok( MOD_rc[0].bottom - MOD_rc[0].top == GetSystemMetrics( SM_CYMENU) - 1,
316 "Height of owner drawn menu item is wrong. Got %ld expected %d\n",
317 MOD_rc[0].bottom - MOD_rc[0].top, GetSystemMetrics( SM_CYMENU) - 1);
320 ret = DestroyMenu(hmenu);
321 ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
325 /* helper for test_menu_bmp_and_string() */
326 static void test_mbs_help( int ispop, int hassub, int mnuopt,
327 HWND hwnd, int arrowwidth, int count, HBITMAP hbmp,
328 SIZE bmpsize, LPCSTR text, SIZE size, SIZE sc_size)
331 HMENU hmenu, submenu;
332 MENUITEMINFO mii={ sizeof( MENUITEMINFO )};
339 MOD_GotDrawItemMsg = FALSE;
340 mii.fMask = MIIM_FTYPE | MIIM_DATA | MIIM_STATE;
342 mii.fState = MF_CHECKED;
344 MODsizes[0] = bmpsize;
348 mii.fMask |= MIIM_STRING;
349 strcpy(text_copy, text);
350 mii.dwTypeData = text_copy; /* structure member declared non-const */
351 if( ( p = strchr( text, '\t'))) {
352 hastab = *(p + 1) ? 2 : 1;
355 /* tabs don't make sense in menubars */
356 if(hastab && !ispop) return;
358 mii.fMask |= MIIM_BITMAP;
361 submenu = CreateMenu();
362 ok( submenu != 0, "CreateMenu failed with error %ld\n", GetLastError());
364 hmenu = CreatePopupMenu();
366 hmenu = CreateMenu();
367 ok( hmenu != 0, "Create{Popup}Menu failed with error %ld\n", GetLastError());
369 mii.fMask |= MIIM_SUBMENU;
370 mii.hSubMenu = submenu;
373 mi.cbSize = sizeof(mi);
374 mi.fMask = MIM_STYLE;
375 pGetMenuInfo( hmenu, &mi);
376 mi.dwStyle |= mnuopt == 1 ? MNS_NOCHECK : MNS_CHECKORBMP;
377 ret = pSetMenuInfo( hmenu, &mi);
378 ok( ret, "SetMenuInfo failed with error %ld\n", GetLastError());
380 ret = InsertMenuItem( hmenu, 0, FALSE, &mii);
381 ok( ret, "InsertMenuItem failed with error %ld\n", GetLastError());
383 if( winetest_debug) {
385 RECT rc = {100, 50, 400, 70};
388 sprintf( buf,"%d text \"%s\" mnuopt %d", count, text ? text: "(nil)", mnuopt);
389 FillRect( hdc, &rc, (HBRUSH) COLOR_WINDOW);
390 TextOut( hdc, 100, 50, buf, strlen( buf));
391 ReleaseDC( hwnd, hdc);
394 ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
396 ret = SetMenu( hwnd, hmenu);
397 ok(ret, "SetMenu failed with error %ld\n", GetLastError());
400 ret = GetMenuItemRect( hwnd, hmenu, 0, &rc);
401 /* check menu width */
403 expect = ( text || hbmp ?
404 4 + (mnuopt != 1 ? GetSystemMetrics(SM_CXMENUCHECK) : 0)
406 arrowwidth + MOD_avec + (hbmp ? bmpsize.cx + 2 : 0) +
407 (text && hastab ? /* TAB space */
408 MOD_avec + ( hastab==2 ? sc_size.cx : 0) : 0) +
409 (text ? 2 + (text[0] ? size.cx :0): 0) ;
411 expect = !(text || hbmp) ? 0 :
412 ( hbmp ? (text ? 2:0) + bmpsize.cx : 0 ) +
413 (text ? 2 * MOD_avec + (text[0] ? size.cx :0): 0) ;
414 ok( rc.right - rc.left == expect,
415 "menu width wrong, got %ld expected %d\n", rc.right - rc.left, expect);
416 failed = failed || !(rc.right - rc.left == expect);
417 /* check menu height */
419 expect = max( ( !(text || hbmp) ? GetSystemMetrics( SM_CYMENUSIZE)/2 : 0),
420 max( (text ? max( 2 + size.cy, MOD_hic + 4) : 0),
421 (hbmp ? bmpsize.cy + 2 : 0)));
423 expect = ( !(text || hbmp) ? GetSystemMetrics( SM_CYMENUSIZE)/2 :
424 max( GetSystemMetrics( SM_CYMENU) - 1, (hbmp ? bmpsize.cy : 0)));
425 ok( rc.bottom - rc.top == expect,
426 "menu height wrong, got %ld expected %d (%d)\n",
427 rc.bottom - rc.top, expect, GetSystemMetrics( SM_CYMENU));
428 failed = failed || !(rc.bottom - rc.top == expect);
429 if( hbmp == HBMMENU_CALLBACK && MOD_GotDrawItemMsg) {
430 /* check the position of the bitmap */
432 expect = ispop ? (4 + ( mnuopt ? 0 : GetSystemMetrics(SM_CXMENUCHECK)))
434 ok( expect == MOD_rc[0].left,
435 "bitmap left is %ld expected %d\n", MOD_rc[0].left, expect);
436 failed = failed || !(expect == MOD_rc[0].left);
438 expect = (rc.bottom - rc.top - MOD_rc[0].bottom + MOD_rc[0].top) / 2;
439 ok( expect == MOD_rc[0].top,
440 "bitmap top is %ld expected %d\n", MOD_rc[0].top, expect);
441 failed = failed || !(expect == MOD_rc[0].top);
443 /* if there was a failure, report details */
445 trace("*** count %d text \"%s\" bitmap %p bmsize %ld,%ld textsize %ld+%ld,%ld mnuopt %d hastab %d\n",
446 count, text ? text: "(nil)", hbmp, bmpsize.cx, bmpsize.cy,
447 size.cx, size.cy, sc_size.cx, mnuopt, hastab);
448 trace(" check %d,%d arrow %d avechar %d\n",
449 GetSystemMetrics(SM_CXMENUCHECK ),
450 GetSystemMetrics(SM_CYMENUCHECK ),arrowwidth, MOD_avec);
451 if( hbmp == HBMMENU_CALLBACK)
452 trace( " rc %ld,%ld-%ld,%ld bmp.rc %ld,%ld-%ld,%ld\n",
453 rc.left, rc.top, rc.top, rc.bottom, MOD_rc[0].left,
454 MOD_rc[0].top,MOD_rc[0].right, MOD_rc[0].bottom);
457 ret = DestroyMenu(submenu);
458 ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
459 ret = DestroyMenu(hmenu);
460 ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
464 static void test_menu_bmp_and_string(void)
471 int count, szidx, txtidx, bmpidx, hassub, mnuopt, ispop;
473 if( !pGetMenuInfo) return;
475 memset( bmfill, 0x55, sizeof( bmfill));
476 hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
477 WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200,
478 NULL, NULL, NULL, NULL);
479 hbm_arrow=LoadBitmap( 0, (CHAR*)OBM_MNARROW);
480 GetObject( hbm_arrow, sizeof(bm), &bm);
481 arrowwidth = bm.bmWidth;
483 ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError());
485 SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG)menu_ownerdraw_wnd_proc);
488 trace(" check %d,%d arrow %d avechar %d\n",
489 GetSystemMetrics(SM_CXMENUCHECK ),
490 GetSystemMetrics(SM_CYMENUCHECK ),arrowwidth, MOD_avec);
493 for( ispop=1; ispop >= 0; ispop--){
494 static SIZE bmsizes[]= {
495 {10,10},{38,38},{1,30},{55,5}};
496 for( szidx=0; szidx < sizeof( bmsizes) / sizeof( SIZE); szidx++) {
497 HBITMAP hbm = CreateBitmap( bmsizes[szidx].cx, bmsizes[szidx].cy,1,1,bmfill);
498 HBITMAP bitmaps[] = { HBMMENU_CALLBACK, hbm, NULL };
499 ok( (int)hbm, "CreateBitmap failed err %ld\n", GetLastError());
500 for( txtidx = 0; txtidx < sizeof(MOD_txtsizes)/sizeof(MOD_txtsizes[0]); txtidx++) {
501 for( hassub = 0; hassub < 2 ; hassub++) { /* add submenu item */
502 for( mnuopt = 0; mnuopt < 3 ; mnuopt++){ /* test MNS_NOCHECK/MNS_CHECKORBMP */
503 for( bmpidx = 0; bmpidx <sizeof(bitmaps)/sizeof(HBITMAP); bmpidx++) {
504 /* no need to test NULL bitmaps of several sizes */
505 if( !bitmaps[bmpidx] && szidx > 0) continue;
506 if( !ispop && hassub) continue;
507 test_mbs_help( ispop, hassub, mnuopt,
508 hwnd, arrowwidth, ++count,
511 MOD_txtsizes[txtidx].text,
512 MOD_txtsizes[txtidx].size,
513 MOD_txtsizes[txtidx].sc_size);
525 static void test_menu_add_string( void )
535 WCHAR strbackW[0x80];
536 static CHAR blah[] = "blah";
537 static const WCHAR expectedString[] = {'D','u','m','m','y',' ','s','t','r','i','n','g', 0};
539 hmenu = CreateMenu();
541 memset( &info, 0, sizeof info );
542 info.cbSize = sizeof info;
543 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_ID;
544 info.dwTypeData = blah;
549 InsertMenuItem(hmenu, 0, TRUE, &info );
551 memset( &info, 0, sizeof info );
552 info.cbSize = sizeof info;
553 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_DATA | MIIM_ID;
554 info.dwTypeData = string;
555 info.cch = sizeof string;
557 GetMenuItemInfo( hmenu, 0, TRUE, &info );
559 ok( !strcmp( string, "blah" ), "menu item name differed\n");
561 /* Test combination of ownerdraw and strings with GetMenuItemString(A/W) */
562 strcpy(string, "Dummy string");
563 memset(&info, 0x00, sizeof(info));
564 info.cbSize= sizeof(MENUITEMINFO);
565 info.fMask= MIIM_FTYPE | MIIM_STRING; /* Set OwnerDraw + typeData */
566 info.fType= MFT_OWNERDRAW;
567 info.dwTypeData= string;
568 rc = InsertMenuItem( hmenu, 0, TRUE, &info );
569 ok (rc, "InsertMenuItem failed\n");
571 strcpy(string,"Garbage");
572 ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
573 ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
575 ok (GetMenuStringW( hmenu, 0, (WCHAR *)strbackW, 99, MF_BYPOSITION), "GetMenuStringW on ownerdraw entry failed\n");
576 ok (!lstrcmpW( strbackW, expectedString ), "Menu text from Unicode version incorrect\n");
578 /* Just change ftype to string and see what text is stored */
579 memset(&info, 0x00, sizeof(info));
580 info.cbSize= sizeof(MENUITEMINFO);
581 info.fMask= MIIM_FTYPE; /* Set string type */
582 info.fType= MFT_STRING;
583 info.dwTypeData= (char *)0xdeadbeef;
584 rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
585 ok (rc, "SetMenuItemInfo failed\n");
587 /* Did we keep the old dwTypeData? */
588 ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
589 ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
591 /* Ensure change to bitmap type fails */
592 memset(&info, 0x00, sizeof(info));
593 info.cbSize= sizeof(MENUITEMINFO);
594 info.fMask= MIIM_FTYPE; /* Set as bitmap type */
595 info.fType= MFT_BITMAP;
596 info.dwTypeData= (char *)0xdeadbee2;
597 rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
598 ok (!rc, "SetMenuItemInfo unexpectedly worked\n");
600 /* Just change ftype back and ensure data hasn't been freed */
601 info.fType= MFT_OWNERDRAW; /* Set as ownerdraw type */
602 info.dwTypeData= (char *)0xdeadbee3;
603 rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
604 ok (rc, "SetMenuItemInfo failed\n");
606 /* Did we keep the old dwTypeData? */
607 ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
608 ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
610 /* Just change string value (not type) */
611 memset(&info, 0x00, sizeof(info));
612 info.cbSize= sizeof(MENUITEMINFO);
613 info.fMask= MIIM_STRING; /* Set typeData */
614 strcpy(string2, "string2");
615 info.dwTypeData= string2;
616 rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
617 ok (rc, "SetMenuItemInfo failed\n");
619 ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
620 ok (!strcmp( strback, "string2" ), "Menu text from Ansi version incorrect\n");
622 /* crashes with wine 0.9.5 */
623 memset(&info, 0x00, sizeof(info));
624 info.cbSize= sizeof(MENUITEMINFO);
625 info.fMask= MIIM_FTYPE | MIIM_STRING; /* Set OwnerDraw + typeData */
626 info.fType= MFT_OWNERDRAW;
627 rc = InsertMenuItem( hmenu, 0, TRUE, &info );
628 ok (rc, "InsertMenuItem failed\n");
629 ok (!GetMenuString( hmenu, 0, NULL, 0, MF_BYPOSITION),
630 "GetMenuString on ownerdraw entry succeeded.\n");
631 ok (!GetMenuStringW( hmenu, 0, NULL, 0, MF_BYPOSITION),
632 "GetMenuStringW on ownerdraw entry succeeded.\n");
635 DestroyMenu( hmenu );
638 /* define building blocks for the menu item info tests */
639 static int strncmpW( const WCHAR *str1, const WCHAR *str2, int n )
641 if (n <= 0) return 0;
642 while ((--n > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; }
643 return *str1 - *str2;
646 static WCHAR *strcpyW( WCHAR *dst, const WCHAR *src )
649 while ((*p++ = *src++));
654 #define DMIINFF( i, e, field)\
655 ok((int)((i)->field)==(int)((e)->field) || (int)((i)->field)==(0xffff & (int)((e)->field)), \
656 "%s got 0x%x expected 0x%x\n", #field, (int)((i)->field), (int)((e)->field));
658 #define DUMPMIINF(s,i,e)\
660 DMIINFF( i, e, fMask)\
661 DMIINFF( i, e, fType)\
662 DMIINFF( i, e, fState)\
664 DMIINFF( i, e, hSubMenu)\
665 DMIINFF( i, e, hbmpChecked)\
666 DMIINFF( i, e, hbmpUnchecked)\
667 DMIINFF( i, e, dwItemData)\
668 DMIINFF( i, e, dwTypeData)\
670 if( s==sizeof(MENUITEMINFOA)) DMIINFF( i, e, hbmpItem)\
673 /* insert menu item */
674 #define TMII_INSMI( a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,n1,\
677 MENUITEMINFOA info1=a1 b1,c1,d1,e1,f1,(void*)g1,(void*)h1,(void*)i1,j1,(void*)k1,l1,(void*)m1 n1;\
678 HMENU hmenu = CreateMenu();\
679 BOOL ret, stop = FALSE;\
680 SetLastError( 0xdeadbeef);\
681 if(ansi)strcpy( string, init);\
682 else strcpyW( (WCHAR*)string, (WCHAR*)init);\
683 if( ansi) ret = InsertMenuItemA(hmenu, 0, TRUE, &info1 );\
684 else ret = InsertMenuItemW(hmenu, 0, TRUE, (MENUITEMINFOW*)&info1 );\
685 if( !(eret1)) { ok( (eret1)==ret,"InsertMenuItem should have failed.\n");\
687 } else ok( (eret1)==ret,"InsertMenuItem failed, err %ld\n",GetLastError());\
690 /* GetMenuItemInfo + GetMenuString */
691 #define TMII_GMII( a2,b2,c2,d2,e2,f2,g2,h2,i2,j2,k2,l2,m2,n2,\
692 a3,b3,c3,d3,e3,f3,g3,h3,i3,j3,k3,l3,m3,n3,\
693 expname, eret2, eret3)\
695 MENUITEMINFOA info2A=a2 b2,c2,d2,e2,f2,(void*)g2,(void*)h2,(void*)i2,j2,(void*)k2,l2,(void*)m2 n2;\
696 MENUITEMINFOA einfoA=a3 b3,c3,d3,e3,f3,(void*)g3,(void*)h3,(void*)i3,j3,(void*)k3,l3,(void*)m3 n3;\
697 MENUITEMINFOA *info2 = &info2A;\
698 MENUITEMINFOA *einfo = &einfoA;\
699 MENUITEMINFOW *info2W = (MENUITEMINFOW *)&info2A;\
701 ret = ansi ? GetMenuItemInfoA( hmenu, 0, TRUE, info2 ) :\
702 GetMenuItemInfoW( hmenu, 0, TRUE, info2W );\
703 if( !(eret2)) ok( (eret2)==ret,"GetMenuItemInfo should have failed.\n");\
705 ok( (eret2)==ret,"GetMenuItemInfo failed, err %ld\n",GetLastError());\
706 ret = memcmp( info2, einfo, sizeof einfoA);\
707 /* ok( ret==0, "Got wrong menu item info data\n");*/\
708 if( ret) DUMPMIINF(info2A.cbSize, &info2A, &einfoA)\
709 if( einfo->dwTypeData == string) {\
710 if(ansi) ok( !strncmp( expname, info2->dwTypeData, einfo->cch ), "menu item name differed \"%s\"\n",\
711 einfo->dwTypeData ? einfo->dwTypeData: "");\
712 else ok( !strncmpW( (WCHAR*)expname, (WCHAR*)info2->dwTypeData, einfo->cch ), "menu item name differed \"%s\"\n",\
713 einfo->dwTypeData ? einfo->dwTypeData: "");\
714 ret = ansi ? GetMenuStringA( hmenu, 0, string, 80, MF_BYPOSITION) :\
715 GetMenuStringW( hmenu, 0, string, 80, MF_BYPOSITION);\
717 ok( ret, "GetMenuString failed, err %ld\n",GetLastError());\
719 ok( !ret, "GetMenuString should have failed\n");\
726 RemoveMenu(hmenu, 0, TRUE );\
727 DestroyMenu( hmenu );\
728 DestroyMenu( submenu );\
729 submenu = CreateMenu();\
732 #define TMII_MODM( flags, id, data, eret )\
734 if(ansi)ret = ModifyMenuA( hmenu, 0, flags, (UINT_PTR)id, (char*)data);\
735 else ret = ModifyMenuW( hmenu, 0, flags, (UINT_PTR)id, (WCHAR*)data);\
736 if( !(eret)) ok( (eret)==ret,"ModifyMenuA should have failed.\n");\
737 else ok( (eret)==ret,"ModifyMenuA failed, err %ld\n",GetLastError());\
740 /* SetMenuItemInfo */
741 #define TMII_SMII( a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,n1,\
744 MENUITEMINFOA info1=a1 b1,c1,d1,e1,f1,(void*)g1,(void*)h1,(void*)i1,j1,(void*)k1,l1,(void*)m1 n1;\
745 SetLastError( 0xdeadbeef);\
746 if(ansi)strcpy( string, init);\
747 else strcpyW( (WCHAR*)string, (WCHAR*)init);\
748 if( ansi) ret = SetMenuItemInfoA(hmenu, 0, TRUE, &info1 );\
749 else ret = SetMenuItemInfoW(hmenu, 0, TRUE, (MENUITEMINFOW*)&info1 );\
750 if( !(eret1)) { ok( (eret1)==ret,"InsertMenuItem should have failed.\n");\
752 } else ok( (eret1)==ret,"InsertMenuItem failed, err %ld\n",GetLastError());\
761 static void test_menu_iteminfo( void )
763 int S=sizeof( MENUITEMINFOA);
768 WCHAR txtW[]={'W','i','n','e',0};
769 WCHAR initW[]={'X','Y','Z',0};
771 void *txt, *init, *empty, *string;
772 HBITMAP hbm = CreateBitmap(1,1,1,1,NULL);
774 HMENU submenu=CreateMenu();
777 if( ansi) {txt=txtA;init=initA;empty=emptyA;string=stringA;}
778 else {txt=txtW;init=initW;empty=emptyW;string=stringA;}
779 trace( "%s string %p hbm %p txt %p\n", ansi ? "ANSI tests: " : "Unicode tests:", string, hbm, txt);
780 /* test all combinations of MFT_STRING, MFT_OWNERDRAW and MFT_BITMAP */
781 /* (since MFT_STRING is zero, there are four of them) */
782 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, 0, 0, 0, 0, 0, 0, txt, 0, 0, }, OK)
783 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
784 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
787 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
788 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
789 {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, 0, 0, 0, },
792 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, 6, -1, }, OK)
793 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
794 {, S, MIIM_TYPE, MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
797 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, hbm, 6, -1, }, OK)
798 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
799 {, S, MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
802 /* not enough space for name*/
803 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
804 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, NULL, 0, -9, },
805 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, NULL, 4, 0, },
808 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
809 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 5, -9, },
810 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
813 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
814 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 4, -9, },
815 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 3, 0, },
818 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
819 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, NULL, 0, -9, },
820 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 0, 0, },
823 /* cannot combine MIIM_TYPE with some other flags */
824 TMII_INSMI( {, S, MIIM_TYPE|MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, ER)
825 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
826 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
829 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
830 TMII_GMII ( {, S, MIIM_TYPE|MIIM_STRING, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
831 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
834 TMII_INSMI( {, S, MIIM_TYPE|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, ER)
835 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
836 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
839 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
840 TMII_GMII ( {, S, MIIM_TYPE|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
841 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
844 TMII_INSMI( {, S, MIIM_TYPE|MIIM_BITMAP, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, 6, hbm, }, ER)
845 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
846 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
849 /* but succeeds with some others */
850 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
851 TMII_GMII ( {, S, MIIM_TYPE|MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
852 {, S, MIIM_TYPE|MIIM_SUBMENU, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
855 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
856 TMII_GMII ( {, S, MIIM_TYPE|MIIM_STATE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
857 {, S, MIIM_TYPE|MIIM_STATE, MFT_STRING, 0, -9, 0, -9, -9, -9, string, 4, 0, },
860 TMII_INSMI( {, S, MIIM_TYPE|MIIM_ID, MFT_STRING, -1, 888, -1, -1, -1, -1, txt, 6, -1, }, OK)
861 TMII_GMII ( {, S, MIIM_TYPE|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
862 {, S, MIIM_TYPE|MIIM_ID, MFT_STRING, -9, 888, 0, -9, -9, -9, string, 4, 0, },
865 TMII_INSMI( {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING, -1, -1, -1, -1, -1, 999, txt, 6, -1, }, OK)
866 TMII_GMII ( {, S, MIIM_TYPE|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
867 {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING, -9, -9, 0, -9, -9, 999, string, 4, 0, },
870 /* to be continued */
871 /* set text with MIIM_TYPE and retrieve with MIIM_STRING */
872 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
873 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
874 {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, -9, },
877 /* set text with MIIM_TYPE and retrieve with MIIM_STRING; MFT_OWNERDRAW causes an empty string */
878 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
879 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
880 {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 0, -9, },
883 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
884 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
885 {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 0, -9, },
888 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
889 TMII_GMII ( {, S, MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
890 {, S, MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 80, -9, },
893 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
894 TMII_GMII ( {, S, 0, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
895 {, S, 0, -9, -9, -9, 0, -9, -9, -9, string, 80, -9, },
898 /* contrary to MIIM_TYPE,you can set the text for an owner draw menu */
899 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
900 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
901 {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 4, -9, },
904 /* same but retrieve with MIIM_TYPE */
905 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
906 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
907 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
910 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
911 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
912 {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 0, -9, },
915 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
916 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
917 {, S, MIIM_STRING|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 0, -9, },
921 /* How is that with bitmaps? */
922 TMII_INSMI( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
923 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
924 {, S, MIIM_TYPE, MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
927 TMII_INSMI( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
928 TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
929 {, S, MIIM_BITMAP|MIIM_FTYPE, 0, -9, -9, 0, -9, -9, -9, string, 80, hbm, },
932 /* MIIM_BITMAP does not like MFT_BITMAP */
933 TMII_INSMI( {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, ER)
934 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
935 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
938 /* no problem with OWNERDRAWN */
939 TMII_INSMI( {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
940 TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
941 {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 80, hbm, },
944 /* setting MFT_BITMAP with MFT_FTYPE fails anyway */
945 TMII_INSMI( {, S, MIIM_FTYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, }, ER)
946 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
947 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
951 /* menu with submenu */
952 TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, -1, -1, -1, txt, 0, -1, }, OK)
953 TMII_GMII ( {, S, MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
954 {, S, MIIM_SUBMENU, -9, -9, -9, submenu, -9, -9, -9, string, 80, -9, },
957 TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, -1, -1, -1, empty, 0, -1, }, OK)
958 TMII_GMII ( {, S, MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
959 {, S, MIIM_SUBMENU, -9, -9, -9, submenu, -9, -9, -9, string, 80, -9, },
962 /* menu with submenu, without MIIM_SUBMENU the submenufield is cleared */
963 TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, -1, -1, -1, txt, 0, -1, }, OK)
964 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
965 {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 0, -9, },
967 TMII_GMII ( {, S, MIIM_SUBMENU|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
968 {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, submenu, -9, -9, -9, string, 80, -9, },
971 /* menu with invalid submenu */
972 TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, 999, -1, -1, -1, txt, 0, -1, }, ER)
973 TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
974 {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
978 TMII_INSMI( {, S, MIIM_TYPE, MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, txt, 0, 0, }, OK)
979 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
980 {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, 0, 0, 0, },
983 TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP|MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, hbm, 6, -1, }, OK)
984 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
985 {, S, MIIM_TYPE, MFT_BITMAP|MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
988 /* SEPARATOR and STRING go well together */
989 /* BITMAP and STRING go well together */
990 TMII_INSMI( {, S, MIIM_STRING|MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
991 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
992 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, hbm, },
995 /* BITMAP, SEPARATOR and STRING go well together */
996 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
997 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
998 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 4, hbm, },
1001 /* last two tests, but use MIIM_TYPE to retrieve info */
1002 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
1003 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1004 {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1007 TMII_INSMI( {, S, MIIM_STRING|MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1008 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1009 {, S, MIIM_TYPE, MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
1012 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1013 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1014 {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
1017 /* same three with MFT_OWNERDRAW */
1018 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
1019 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1020 {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1023 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1024 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1025 {, S, MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
1028 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1029 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1030 {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_BITMAP|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
1034 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE|MIIM_ID, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
1035 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1036 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1039 /* test with modifymenu: string is preserved after seting OWNERDRAW */
1040 TMII_INSMI( {, S, MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
1041 TMII_MODM( MFT_OWNERDRAW, -1, 787, OK)
1042 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1043 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_DATA, MFT_OWNERDRAW, -9, -9, 0, -9, -9, 787, string, 4, -9, },
1046 /* same with bitmap: now the text is cleared */
1047 TMII_INSMI( {, S, MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
1048 TMII_MODM( MFT_BITMAP, 545, hbm, OK)
1049 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1050 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_BITMAP, -9, 545, 0, -9, -9, -9, string, 0, hbm, },
1053 /* start with bitmap: now setting text clears it (though he flag is raised) */
1054 TMII_INSMI( {, S, MIIM_BITMAP, MFT_STRING, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1055 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1056 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_STRING, -9, 0, 0, -9, -9, -9, string, 0, hbm, },
1058 TMII_MODM( MFT_STRING, 545, txt, OK)
1059 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1060 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_STRING, -9, 545, 0, -9, -9, -9, string, 4, 0, },
1063 /*repeat with text NULL */
1064 TMII_INSMI( {, S, MIIM_BITMAP, MFT_STRING, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1065 TMII_MODM( MFT_STRING, 545, NULL, OK)
1066 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1067 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_SEPARATOR, -9, 545, 0, -9, -9, -9, string, 0, 0, },
1070 /* repeat with text "" */
1071 TMII_INSMI( {, S, MIIM_BITMAP, -1 , -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1072 TMII_MODM( MFT_STRING, 545, empty, OK)
1073 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1074 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_STRING, -9, 545, 0, -9, -9, -9, string, 0, 0, },
1077 /* start with bitmap: set ownerdraw */
1078 TMII_INSMI( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1079 TMII_MODM( MFT_OWNERDRAW, -1, 232, OK)
1080 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1081 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_DATA, MFT_OWNERDRAW, -9, -9, 0, -9, -9, 232, string, 0, hbm, },
1085 TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1086 TMII_GMII ( {, S, 0, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1087 {, S, 0, -9, -9, -9, 0, -9, -9, -9, string, 80, -9, },
1090 /* some tests with small cbSize: the hbmpItem is to be ignored */
1091 TMII_INSMI( {, S - 4, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1092 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1093 {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, NULL, 0, NULL, },
1096 TMII_INSMI( {, S - 4, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1097 TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1098 {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 80, NULL, },
1101 TMII_INSMI( {, S - 4, MIIM_STRING|MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1102 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1103 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, NULL, },
1106 TMII_INSMI( {, S - 4, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1107 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1108 {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1111 TMII_INSMI( {, S - 4, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1112 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1113 {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1116 TMII_INSMI( {, S - 4, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
1117 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1118 {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
1121 /* MIIM_TYPE by itself does not get/set the dwItemData for OwnerDrawn menus */
1122 TMII_INSMI( {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, 343, txt, 0, -1, }, OK)
1123 TMII_GMII ( {, S, MIIM_TYPE|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1124 {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, 343, 0, 0, 0, },
1127 TMII_INSMI( {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, 343, txt, 0, -1, }, OK)
1128 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1129 {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, 0, 0, 0, },
1132 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, 343, txt, 0, -1, }, OK)
1133 TMII_GMII ( {, S, MIIM_TYPE|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1134 {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, 0, 0, 0, 0, },
1137 /* set a string menu to ownerdraw with MIIM_TYPE */
1138 TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -2, -2, -2, -2, -2, -2, txt, -2, -2, }, OK)
1139 TMII_SMII( {, S, MIIM_TYPE, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, -1, -1, -1, }, OK)
1140 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1141 {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 4, -9, },
1144 /* test with modifymenu add submenu */
1145 TMII_INSMI( {, S, MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
1146 TMII_MODM( MF_POPUP, submenu, txt, OK)
1147 TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1148 {, S, MIIM_FTYPE|MIIM_STRING|MIIM_SUBMENU, MFT_STRING, -9, -9, submenu, -9, -9, -9, string, 4, -9, },
1150 TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1151 {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
1154 /* MFT_SEPARATOR bit is kept when the text is added */
1155 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
1156 TMII_SMII( {, S, MIIM_STRING, -1, -1, -1, -1, -1, -1, -1, txt, -1, -1, }, OK)
1157 TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1158 {, S, MIIM_STRING|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 4, -9, },
1161 /* MFT_SEPARATOR bit is kept when bitmap is added */
1162 TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
1163 TMII_SMII( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
1164 TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
1165 {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 80, hbm, },
1169 } while( !(ansi = !ansi) );
1174 The following tests try to confirm the algorithm used to return the menu items
1175 when there is a collision between a menu item and a popup menu
1177 void test_menu_search_bycommand( void )
1179 HMENU hmenu, hmenuSub, hmenuSub2;
1185 static CHAR menuitem[] = "MenuItem",
1186 menuitem2[] = "MenuItem 2";
1188 /* Case 1: Menu containing a menu item */
1189 hmenu = CreateMenu();
1191 memset( &info, 0, sizeof info );
1192 info.cbSize = sizeof info;
1193 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1194 info.fType = MFT_STRING;
1195 strcpy(strIn, "Case 1 MenuItem");
1196 info.dwTypeData = strIn;
1197 info.wID = (UINT) 0x1234;
1199 rc = InsertMenuItem(hmenu, 0, TRUE, &info );
1200 ok (rc, "Inserting the menuitem failed\n");
1202 id = GetMenuItemID(hmenu, 0);
1203 ok (id == 0x1234, "Getting the menuitem id failed(gave %x)\n", id);
1205 /* Confirm the menuitem was given the id supplied (getting by position) */
1206 memset( &info, 0, sizeof info );
1208 info.cbSize = sizeof(MENUITEMINFO);
1209 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1210 info.dwTypeData = strback;
1211 info.cch = sizeof(strback);
1213 rc = GetMenuItemInfo(hmenu, 0, TRUE, &info); /* Get by position */
1214 ok (rc, "Getting the menu items info failed\n");
1215 ok (info.wID == 0x1234, "IDs differ for the menuitem\n");
1216 ok (!strcmp(info.dwTypeData, "Case 1 MenuItem"), "Returned item has wrong label\n");
1218 /* Search by id - Should return the item */
1219 memset( &info, 0, sizeof info );
1221 info.cbSize = sizeof(MENUITEMINFO);
1222 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1223 info.dwTypeData = strback;
1224 info.cch = sizeof(strback);
1225 rc = GetMenuItemInfo(hmenu, 0x1234, FALSE, &info); /* Get by ID */
1227 ok (rc, "Getting the menu items info failed\n");
1228 ok (info.wID == 0x1234, "IDs differ for the menuitem\n");
1229 ok (!strcmp(info.dwTypeData, "Case 1 MenuItem"), "Returned item has wrong label\n");
1231 DestroyMenu( hmenu );
1233 /* Case 2: Menu containing a popup menu */
1234 hmenu = CreateMenu();
1235 hmenuSub = CreateMenu();
1237 strcpy(strIn, "Case 2 SubMenu");
1238 rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, strIn);
1239 ok (rc, "Inserting the popup menu into the main menu failed\n");
1241 id = GetMenuItemID(hmenu, 0);
1242 ok (id == -1, "Getting the menuitem id unexpectedly worked (gave %x)\n", id);
1244 /* Confirm the menuitem itself was given an id the same as the HMENU, (getting by position) */
1245 memset( &info, 0, sizeof info );
1247 info.cbSize = sizeof(MENUITEMINFO);
1248 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1249 info.dwTypeData = strback;
1250 info.cch = sizeof(strback);
1251 info.wID = 0xdeadbeef;
1253 rc = GetMenuItemInfo(hmenu, 0, TRUE, &info); /* Get by position */
1254 ok (rc, "Getting the menu items info failed\n");
1255 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the menuitem\n");
1256 ok (!strcmp(info.dwTypeData, "Case 2 SubMenu"), "Returned item has wrong label\n");
1258 /* Search by id - returns the popup menu itself */
1259 memset( &info, 0, sizeof info );
1261 info.cbSize = sizeof(MENUITEMINFO);
1262 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1263 info.dwTypeData = strback;
1264 info.cch = sizeof(strback);
1265 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
1267 ok (rc, "Getting the menu items info failed\n");
1268 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
1269 ok (!strcmp(info.dwTypeData, "Case 2 SubMenu"), "Returned item has wrong label\n");
1272 Now add an item after it with the same id
1274 memset( &info, 0, sizeof info );
1275 info.cbSize = sizeof info;
1276 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1277 info.fType = MFT_STRING;
1278 strcpy(strIn, "Case 2 MenuItem 1");
1279 info.dwTypeData = strIn;
1280 info.wID = (UINT_PTR) hmenuSub;
1281 rc = InsertMenuItem(hmenu, -1, TRUE, &info );
1282 ok (rc, "Inserting the menuitem failed\n");
1284 /* Search by id - returns the item which follows the popup menu */
1285 memset( &info, 0, sizeof info );
1287 info.cbSize = sizeof(MENUITEMINFO);
1288 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1289 info.dwTypeData = strback;
1290 info.cch = sizeof(strback);
1291 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
1293 ok (rc, "Getting the menu items info failed\n");
1294 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
1295 ok (!strcmp(info.dwTypeData, "Case 2 MenuItem 1"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1298 Now add an item before the popup (with the same id)
1300 memset( &info, 0, sizeof info );
1301 info.cbSize = sizeof info;
1302 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1303 info.fType = MFT_STRING;
1304 strcpy(strIn, "Case 2 MenuItem 2");
1305 info.dwTypeData = strIn;
1306 info.wID = (UINT_PTR) hmenuSub;
1307 rc = InsertMenuItem(hmenu, 0, TRUE, &info );
1308 ok (rc, "Inserting the menuitem failed\n");
1310 /* Search by id - returns the item which precedes the popup menu */
1311 memset( &info, 0, sizeof info );
1313 info.cbSize = sizeof(MENUITEMINFO);
1314 info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1315 info.dwTypeData = strback;
1316 info.cch = sizeof(strback);
1317 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
1319 ok (rc, "Getting the menu items info failed\n");
1320 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
1321 ok (!strcmp(info.dwTypeData, "Case 2 MenuItem 2"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1323 DestroyMenu( hmenu );
1324 DestroyMenu( hmenuSub );
1327 Case 3: Menu containing a popup menu which in turn
1328 contains 2 items with the same id as the popup itself
1331 hmenu = CreateMenu();
1332 hmenuSub = CreateMenu();
1334 memset( &info, 0, sizeof info );
1335 info.cbSize = sizeof info;
1336 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1337 info.fType = MFT_STRING;
1338 info.dwTypeData = menuitem;
1339 info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1341 rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
1342 ok (rc, "Inserting the popup menu into the main menu failed\n");
1344 rc = InsertMenuItem(hmenuSub, 0, TRUE, &info );
1345 ok (rc, "Inserting the sub menu menuitem failed\n");
1347 memset( &info, 0, sizeof info );
1348 info.cbSize = sizeof info;
1349 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1350 info.fType = MFT_STRING;
1351 info.dwTypeData = menuitem2;
1352 info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1354 rc = InsertMenuItem(hmenuSub, 1, TRUE, &info );
1355 ok (rc, "Inserting the sub menu menuitem 2 failed\n");
1357 /* Prove that you can't query the id of a popup directly (By position) */
1358 id = GetMenuItemID(hmenu, 0);
1359 ok (id == -1, "Getting the sub menu id should have failed because its a popup (gave %x)\n", id);
1361 /* Prove getting the item info via ID returns the first item (not the popup or 2nd item)*/
1362 memset( &info, 0, sizeof info );
1364 info.cbSize = sizeof(MENUITEMINFO);
1365 info.fMask = MIIM_STRING | MIIM_ID;
1366 info.dwTypeData = strback;
1367 info.cch = sizeof(strback);
1369 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info);
1370 ok (rc, "Getting the menus info failed\n");
1371 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for popup menu\n");
1372 ok (!strcmp(info.dwTypeData, "MenuItem"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1373 DestroyMenu( hmenu );
1374 DestroyMenu( hmenuSub );
1377 Case 4: Menu containing 2 popup menus, the second
1378 contains 2 items with the same id as the first popup menu
1380 hmenu = CreateMenu();
1381 hmenuSub = CreateMenu();
1382 hmenuSub2 = CreateMenu();
1384 rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
1385 ok (rc, "Inserting the popup menu into the main menu failed\n");
1387 rc = InsertMenu(hmenu, 1, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub2, "Submenu2");
1388 ok (rc, "Inserting the popup menu into the main menu failed\n");
1390 memset( &info, 0, sizeof info );
1391 info.cbSize = sizeof info;
1392 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1393 info.fType = MFT_STRING;
1394 info.dwTypeData = menuitem;
1395 info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1397 rc = InsertMenuItem(hmenuSub2, 0, TRUE, &info );
1398 ok (rc, "Inserting the sub menu menuitem failed\n");
1400 memset( &info, 0, sizeof info );
1401 info.cbSize = sizeof info;
1402 info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1403 info.fType = MFT_STRING;
1404 info.dwTypeData = menuitem2;
1405 info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1407 rc = InsertMenuItem(hmenuSub2, 1, TRUE, &info );
1408 ok (rc, "Inserting the sub menu menuitem 2 failed\n");
1410 /* Prove getting the item info via ID returns the first item (not the popup or 2nd item)*/
1411 memset( &info, 0, sizeof info );
1413 info.cbSize = sizeof(MENUITEMINFO);
1414 info.fMask = MIIM_STRING | MIIM_ID;
1415 info.dwTypeData = strback;
1416 info.cch = sizeof(strback);
1418 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info);
1419 ok (rc, "Getting the menus info failed\n");
1420 ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for popup menu\n");
1421 ok (!strcmp(info.dwTypeData, "MenuItem"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1423 memset( &info, 0, sizeof info );
1425 info.cbSize = sizeof(MENUITEMINFO);
1426 info.fMask = MIIM_STRING | MIIM_ID;
1427 info.dwTypeData = strback;
1428 info.cch = sizeof(strback);
1430 rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub2, FALSE, &info);
1431 ok (rc, "Getting the menus info failed\n");
1432 ok (info.wID == (UINT)hmenuSub2, "IDs differ for popup menu\n");
1433 ok (!strcmp(info.dwTypeData, "Submenu2"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1435 DestroyMenu( hmenu );
1436 DestroyMenu( hmenuSub );
1437 DestroyMenu( hmenuSub2 );
1441 Case 5: Menu containing a popup menu which in turn
1442 contains an item with a different id than the popup menu.
1443 This tests the fallback to a popup menu ID.
1446 hmenu = CreateMenu();
1447 hmenuSub = CreateMenu();
1449 rc = AppendMenu(hmenu, MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
1450 ok (rc, "Appending the popup menu to the main menu failed\n");
1452 rc = AppendMenu(hmenuSub, MF_STRING, 102, "Item");
1453 ok (rc, "Appending the item to the popup menu failed\n");
1455 /* Set the ID for hmenuSub */
1456 info.cbSize = sizeof(info);
1457 info.fMask = MIIM_ID;
1460 rc = SetMenuItemInfo(hmenu, 0, TRUE, &info);
1461 ok(rc, "Setting the ID for the popup menu failed\n");
1463 /* Check if the ID has been set */
1465 rc = GetMenuItemInfo(hmenu, 0, TRUE, &info);
1466 ok(rc, "Getting the ID for the popup menu failed\n");
1467 ok(info.wID == 101, "The ID for the popup menu has not been set\n");
1469 /* Prove getting the item info via ID returns the popup menu */
1470 memset( &info, 0, sizeof(info));
1472 info.cbSize = sizeof(MENUITEMINFO);
1473 info.fMask = MIIM_STRING | MIIM_ID;
1474 info.dwTypeData = strback;
1475 info.cch = sizeof(strback);
1477 rc = GetMenuItemInfo(hmenu, 101, FALSE, &info);
1478 ok (rc, "Getting the menu info failed\n");
1479 ok (info.wID == 101, "IDs differ\n");
1480 ok (!strcmp(info.dwTypeData, "Submenu"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1482 /* Also look for the menu item */
1483 memset( &info, 0, sizeof(info));
1485 info.cbSize = sizeof(MENUITEMINFO);
1486 info.fMask = MIIM_STRING | MIIM_ID;
1487 info.dwTypeData = strback;
1488 info.cch = sizeof(strback);
1490 rc = GetMenuItemInfo(hmenu, 102, FALSE, &info);
1491 ok (rc, "Getting the menu info failed\n");
1492 ok (info.wID == 102, "IDs differ\n");
1493 ok (!strcmp(info.dwTypeData, "Item"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1496 DestroyMenu(hmenuSub);
1499 struct menu_item_pair_s {
1500 UINT uMenu; /* 1 - top level menu, [0-Menu 1-Enabled 2-Disabled]
1501 * 2 - 2nd level menu, [0-Popup 1-Enabled 2-Disabled]
1502 * 3 - 3rd level menu, [0-Enabled 1-Disabled] */
1506 static struct menu_mouse_tests_s {
1508 struct menu_item_pair_s menu_item_pairs[5]; /* for mousing */
1509 WORD wVk[5]; /* keys */
1513 /* for each test, send keys or clicks and check for menu visibility */
1514 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 0}, TRUE, FALSE }, /* test 0 */
1515 { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, 0}, FALSE, FALSE },
1516 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 0}, TRUE, FALSE },
1517 { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
1518 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 0}, TRUE, FALSE },
1519 { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
1520 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 0}, TRUE, FALSE },
1521 { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, VK_ESCAPE, 0}, FALSE, FALSE },
1522 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', VK_ESCAPE, 0}, TRUE, FALSE },
1523 { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, 0}, FALSE, FALSE },
1524 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 0}, TRUE, FALSE },
1525 { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
1526 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 0}, TRUE, FALSE },
1527 { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
1528 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 'P', 0}, TRUE, FALSE },
1529 { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
1530 { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 'P', 0}, TRUE, FALSE },
1531 { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
1533 { INPUT_MOUSE, {{1, 2}, {0}}, {0}, TRUE, TRUE }, /* test 18 */
1534 { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
1535 { INPUT_MOUSE, {{1, 0}, {0}}, {0}, TRUE, TRUE },
1536 { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
1537 { INPUT_MOUSE, {{1, 0}, {2, 2}, {0}}, {0}, TRUE, TRUE },
1538 { INPUT_MOUSE, {{2, 1}, {0}}, {0}, FALSE, FALSE },
1539 { INPUT_MOUSE, {{1, 0}, {2, 0}, {0}}, {0}, TRUE, TRUE },
1540 { INPUT_MOUSE, {{3, 0}, {0}}, {0}, FALSE, FALSE },
1541 { INPUT_MOUSE, {{1, 0}, {2, 0}, {0}}, {0}, TRUE, TRUE },
1542 { INPUT_MOUSE, {{3, 1}, {0}}, {0}, TRUE, TRUE },
1543 { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
1547 static void send_key(WORD wVk)
1550 memset(&i, 0, 2*sizeof(INPUT));
1551 i[0].type = i[1].type = INPUT_KEYBOARD;
1552 i[0].u.ki.wVk = i[1].u.ki.wVk = wVk;
1553 i[1].u.ki.dwFlags = KEYEVENTF_KEYUP;
1554 SendInput(2, (INPUT *) i, sizeof(INPUT));
1557 static void click_menu(HANDLE hWnd, struct menu_item_pair_s *mi)
1559 HMENU hMenu = hMenus[mi->uMenu];
1563 int screen_w = GetSystemMetrics(SM_CXSCREEN);
1564 int screen_h = GetSystemMetrics(SM_CYSCREEN);
1566 GetMenuItemRect(mi->uMenu > 2 ? NULL : hWnd, hMenu, mi->uItem, &r);
1568 memset(&i, 0, 3*sizeof(INPUT));
1569 i[0].type = i[1].type = i[2].type = INPUT_MOUSE;
1570 i[0].u.mi.dx = i[1].u.mi.dx = i[2].u.mi.dx
1571 = ((r.left + 5) * 65535) / screen_w;
1572 i[0].u.mi.dy = i[1].u.mi.dy = i[2].u.mi.dy
1573 = ((r.top + 5) * 65535) / screen_h;
1574 i[0].u.mi.dwFlags = i[1].u.mi.dwFlags = i[2].u.mi.dwFlags
1575 = MOUSEEVENTF_ABSOLUTE;
1576 i[0].u.mi.dwFlags |= MOUSEEVENTF_MOVE;
1577 i[1].u.mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
1578 i[2].u.mi.dwFlags |= MOUSEEVENTF_LEFTUP;
1579 SendInput(3, (INPUT *) i, sizeof(INPUT));
1581 /* hack to prevent mouse message buildup in Wine */
1582 while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
1585 static DWORD WINAPI test_menu_input_thread(LPVOID lpParameter)
1588 HANDLE hWnd = lpParameter;
1591 /* mixed keyboard/mouse test */
1592 for (i = 0; menu_tests[i].type != -1; i++)
1596 if (menu_tests[i].type == INPUT_KEYBOARD)
1597 for (j = 0; menu_tests[i].wVk[j] != 0; j++)
1598 send_key(menu_tests[i].wVk[j]);
1600 for (j = 0; menu_tests[i].menu_item_pairs[j].uMenu != 0; j++)
1601 click_menu(hWnd, &menu_tests[i].menu_item_pairs[j]);
1603 while (menu_tests[i].bMenuVisible != bMenuVisible)
1611 if (menu_tests[i]._todo_wine)
1614 ok(menu_tests[i].bMenuVisible == bMenuVisible, "test %d\n", i);
1618 ok(menu_tests[i].bMenuVisible == bMenuVisible, "test %d\n", i);
1623 static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam,
1627 case WM_ENTERMENULOOP:
1628 bMenuVisible = TRUE;
1630 case WM_EXITMENULOOP:
1631 bMenuVisible = FALSE;
1634 return( DefWindowProcA( hWnd, msg, wParam, lParam ) );
1639 static void test_menu_input(void) {
1642 HINSTANCE hInstance = GetModuleHandleA( NULL );
1643 HANDLE hThread, hWnd;
1645 wclass.lpszClassName = "MenuTestClass";
1646 wclass.style = CS_HREDRAW | CS_VREDRAW;
1647 wclass.lpfnWndProc = WndProc;
1648 wclass.hInstance = hInstance;
1649 wclass.hIcon = LoadIconA( 0, (LPSTR)IDI_APPLICATION );
1650 wclass.hCursor = LoadCursorA( NULL, (LPSTR)IDC_ARROW);
1651 wclass.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1);
1652 wclass.lpszMenuName = 0;
1653 wclass.cbClsExtra = 0;
1654 wclass.cbWndExtra = 0;
1655 assert (RegisterClassA( &wclass ));
1656 assert (hWnd = CreateWindowA( wclass.lpszClassName, "MenuTest",
1657 WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0,
1658 400, 200, NULL, NULL, hInstance, NULL) );
1661 hMenus[3] = CreatePopupMenu();
1662 AppendMenu(hMenus[3], MF_STRING, 0, "&Enabled");
1663 AppendMenu(hMenus[3], MF_STRING|MF_DISABLED, 0, "&Disabled");
1665 hMenus[2] = CreatePopupMenu();
1666 AppendMenu(hMenus[2], MF_STRING|MF_POPUP, (UINT_PTR) hMenus[3], "&Popup");
1667 AppendMenu(hMenus[2], MF_STRING, 0, "&Enabled");
1668 AppendMenu(hMenus[2], MF_STRING|MF_DISABLED, 0, "&Disabled");
1670 hMenus[1] = CreateMenu();
1671 AppendMenu(hMenus[1], MF_STRING|MF_POPUP, (UINT_PTR) hMenus[2], "&Menu");
1672 AppendMenu(hMenus[1], MF_STRING, 0, "&Enabled");
1673 AppendMenu(hMenus[1], MF_STRING|MF_DISABLED, 0, "&Disabled");
1675 SetMenu(hWnd, hMenus[1]);
1676 ShowWindow(hWnd, SW_SHOW);
1679 hThread = CreateThread(NULL, 0, test_menu_input_thread, hWnd, 0, NULL);
1682 if (WAIT_TIMEOUT != WaitForSingleObject(hThread, 50))
1684 while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
1686 DestroyWindow(hWnd);
1692 (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "SetMenuInfo" );
1694 (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetMenuInfo" );
1696 register_menu_check_class();
1698 test_menu_locked_by_window();
1699 test_menu_ownerdraw();
1700 test_menu_add_string();
1701 test_menu_iteminfo();
1702 test_menu_search_bycommand();
1703 test_menu_bmp_and_string();