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