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