winapi_test: Updated so the generated tests work.
[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_PTR)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, MAKEINTRESOURCE(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_PTR)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( hbm != 0, "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 static void insert_menu_item( int line, HMENU hmenu, BOOL ansi, UINT mask, UINT type, UINT state, UINT id,
721                               HMENU submenu, HBITMAP checked, HBITMAP unchecked, ULONG_PTR data,
722                               void *type_data, UINT len, HBITMAP item, BOOL expect )
723 {
724     MENUITEMINFOA info;
725     BOOL ret;
726
727     /* magic bitmap handle to test smaller cbSize */
728     if (item == (HBITMAP)(ULONG_PTR)0xdeadbeef)
729         info.cbSize = FIELD_OFFSET(MENUITEMINFOA,hbmpItem);
730     else
731         info.cbSize = sizeof(info);
732     info.fMask = mask;
733     info.fType = type;
734     info.fState = state;
735     info.wID = id;
736     info.hSubMenu = submenu;
737     info.hbmpChecked = checked;
738     info.hbmpUnchecked = unchecked;
739     info.dwItemData = data;
740     info.dwTypeData = type_data;
741     info.cch = len;
742     info.hbmpItem = item;
743     SetLastError( 0xdeadbeef );
744     if (ansi) ret = InsertMenuItemA( hmenu, 0, TRUE, &info );
745     else ret = InsertMenuItemW( hmenu, 0, TRUE, (MENUITEMINFOW*)&info );
746     if (!expect) ok_(__FILE__, line)( !ret, "InsertMenuItem should have failed.\n" );
747     else ok_(__FILE__, line)( ret, "InsertMenuItem failed, err %u\n", GetLastError());
748 }
749
750 static void check_menu_item_info( int line, HMENU hmenu, BOOL ansi, UINT mask, UINT type, UINT state,
751                                   UINT id, HMENU submenu, HBITMAP checked, HBITMAP unchecked,
752                                   ULONG_PTR data, void *type_data, UINT in_len, UINT out_len,
753                                   HBITMAP item, LPCSTR expname, BOOL expect, BOOL expstring )
754 {
755     MENUITEMINFOA info;
756     BOOL ret;
757     WCHAR buffer[80];
758
759     SetLastError( 0xdeadbeef );
760     memset( &info, 0xcc, sizeof(info) );
761     info.cbSize = sizeof(info);
762     info.fMask = mask;
763     info.dwTypeData = type_data;
764     info.cch = in_len;
765
766     ret = ansi ? GetMenuItemInfoA( hmenu, 0, TRUE, &info ) :
767                  GetMenuItemInfoW( hmenu, 0, TRUE, (MENUITEMINFOW *)&info );
768     if (!expect)
769     {
770         ok_(__FILE__, line)( !ret, "GetMenuItemInfo should have failed.\n" );
771         return;
772     }
773     ok_(__FILE__, line)( ret, "GetMenuItemInfo failed, err %u\n", GetLastError());
774     if (mask & MIIM_TYPE)
775         ok_(__FILE__, line)( info.fType == type || info.fType == LOWORD(type),
776                              "wrong type %x/%x\n", info.fType, type );
777     if (mask & MIIM_STATE)
778         ok_(__FILE__, line)( info.fState == state || info.fState == LOWORD(state),
779                              "wrong state %x/%x\n", info.fState, state );
780     if (mask & MIIM_ID)
781         ok_(__FILE__, line)( info.wID == id || info.wID == LOWORD(id),
782                              "wrong id %x/%x\n", info.wID, id );
783     if (mask & MIIM_SUBMENU)
784         ok_(__FILE__, line)( info.hSubMenu == submenu || (ULONG_PTR)info.hSubMenu == LOWORD(submenu),
785                              "wrong submenu %p/%p\n", info.hSubMenu, submenu );
786     if (mask & MIIM_CHECKMARKS)
787     {
788         ok_(__FILE__, line)( info.hbmpChecked == checked || (ULONG_PTR)info.hbmpChecked == LOWORD(checked),
789                              "wrong bmpchecked %p/%p\n", info.hbmpChecked, checked );
790         ok_(__FILE__, line)( info.hbmpUnchecked == unchecked || (ULONG_PTR)info.hbmpUnchecked == LOWORD(unchecked),
791                              "wrong bmpunchecked %p/%p\n", info.hbmpUnchecked, unchecked );
792     }
793     if (mask & MIIM_DATA)
794         ok_(__FILE__, line)( info.dwItemData == data || info.dwItemData == LOWORD(data),
795                              "wrong item data %lx/%lx\n", info.dwItemData, data );
796     if (mask & MIIM_BITMAP)
797         ok_(__FILE__, line)( info.hbmpItem == item || (ULONG_PTR)info.hbmpItem == LOWORD(item),
798                              "wrong bmpitem %p/%p\n", info.hbmpItem, item );
799     ok_(__FILE__, line)( info.dwTypeData == type_data || (ULONG_PTR)info.dwTypeData == LOWORD(type_data),
800                          "wrong type data %p/%p\n", info.dwTypeData, type_data );
801     ok_(__FILE__, line)( info.cch == out_len, "wrong len %x/%x\n", info.cch, out_len );
802     if (expname)
803     {
804         if(ansi)
805             ok_(__FILE__, line)( !strncmp( expname, info.dwTypeData, out_len ),
806                                  "menu item name differed from '%s' '%s'\n", expname, info.dwTypeData );
807         else
808             ok_(__FILE__, line)( !strncmpW( (WCHAR *)expname, (WCHAR *)info.dwTypeData, out_len ),
809                                  "menu item name wrong\n" );
810
811         SetLastError( 0xdeadbeef );
812         ret = ansi ? GetMenuStringA( hmenu, 0, (char *)buffer, 80, MF_BYPOSITION ) :
813             GetMenuStringW( hmenu, 0, buffer, 80, MF_BYPOSITION );
814         if (expstring)
815             ok_(__FILE__, line)( ret, "GetMenuString failed, err %u\n", GetLastError());
816         else
817             ok_(__FILE__, line)( !ret, "GetMenuString should have failed\n" );
818     }
819 }
820
821 static void modify_menu( int line, HMENU hmenu, BOOL ansi, UINT flags, UINT_PTR id, void *data )
822 {
823     BOOL ret;
824
825     SetLastError( 0xdeadbeef );
826     if (ansi) ret = ModifyMenuA( hmenu, 0, flags, id, data );
827     else ret = ModifyMenuW( hmenu, 0, flags, id, (WCHAR *)data );
828     ok_(__FILE__,line)( ret, "ModifyMenuA failed, err %u\n", GetLastError());
829 }
830
831 static void set_menu_item_info( int line, HMENU hmenu, BOOL ansi, UINT mask, UINT type, UINT state,
832                                 UINT id, HMENU submenu, HBITMAP checked, HBITMAP unchecked, ULONG_PTR data,
833                                 void *type_data, UINT len, HBITMAP item )
834
835 {
836     MENUITEMINFOA info;
837     BOOL ret;
838
839     /* magic bitmap handle to test smaller cbSize */
840     if (item == (HBITMAP)(ULONG_PTR)0xdeadbeef)
841         info.cbSize = FIELD_OFFSET(MENUITEMINFOA,hbmpItem);
842     else
843         info.cbSize = sizeof(info);
844     info.fMask = mask;
845     info.fType = type;
846     info.fState = state;
847     info.wID = id;
848     info.hSubMenu = submenu;
849     info.hbmpChecked = checked;
850     info.hbmpUnchecked = unchecked;
851     info.dwItemData = data;
852     info.dwTypeData = type_data;
853     info.cch = len;
854     info.hbmpItem = item;
855     SetLastError( 0xdeadbeef );
856     if (ansi) ret = SetMenuItemInfoA( hmenu, 0, TRUE, &info );
857     else ret = SetMenuItemInfoW( hmenu, 0, TRUE, (MENUITEMINFOW*)&info );
858     ok_(__FILE__, line)( ret, "SetMenuItemInfo failed, err %u\n", GetLastError());
859 }
860
861 #define TMII_INSMI( c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,eret1 )\
862     hmenu = CreateMenu();\
863     submenu = CreateMenu();\
864     if(ansi)strcpy( string, init);\
865     else strcpyW( (WCHAR*)string, (WCHAR*)init);\
866     insert_menu_item( __LINE__, hmenu, ansi, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, eret1 )
867
868 /* GetMenuItemInfo + GetMenuString  */
869 #define TMII_GMII( c2,l2,\
870     d3,e3,f3,g3,h3,i3,j3,k3,l3,m3,\
871     expname, eret2, eret3)\
872     check_menu_item_info( __LINE__, hmenu, ansi, c2, d3, e3, f3, g3, h3, i3, j3, k3, l2, l3, m3, \
873                           expname, eret2, eret3 )
874
875 #define TMII_DONE \
876     RemoveMenu(hmenu, 0, TRUE );\
877     DestroyMenu( hmenu );\
878     DestroyMenu( submenu );
879
880 /* modify menu */
881 #define TMII_MODM( flags, id, data ) \
882     modify_menu( __LINE__, hmenu, ansi, flags, id, data )
883
884 /* SetMenuItemInfo */
885 #define TMII_SMII( c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1 ) \
886     set_menu_item_info( __LINE__, hmenu, ansi, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1 )
887
888
889 #define OK 1
890 #define ER 0
891
892
893 static void test_menu_iteminfo( void )
894 {
895   int ansi = TRUE;
896   char txtA[]="wine";
897   char initA[]="XYZ";
898   char emptyA[]="";
899   WCHAR txtW[]={'W','i','n','e',0};
900   WCHAR initW[]={'X','Y','Z',0};
901   WCHAR emptyW[]={0};
902   void *txt, *init, *empty, *string;
903   HBITMAP hbm = CreateBitmap(1,1,1,1,NULL);
904   char stringA[0x80];
905   HMENU hmenu, submenu=CreateMenu();
906   HBITMAP dummy_hbm = (HBITMAP)(ULONG_PTR)0xdeadbeef;
907
908   do {
909     if( ansi) {txt=txtA;init=initA;empty=emptyA;string=stringA;}
910     else {txt=txtW;init=initW;empty=emptyW;string=stringA;}
911     trace( "%s string %p hbm %p txt %p\n", ansi ?  "ANSI tests:   " : "Unicode tests:", string, hbm, txt);
912     /* test all combinations of MFT_STRING, MFT_OWNERDRAW and MFT_BITMAP */
913     /* (since MFT_STRING is zero, there are four of them) */
914     TMII_INSMI( MIIM_TYPE, MFT_STRING, 0, 0, 0, 0, 0, 0, txt, 0, 0, OK );
915     TMII_GMII ( MIIM_TYPE, 80,
916         MFT_STRING, 0, 0, 0, 0, 0, 0, string, 4, 0,
917         txt, OK, OK );
918     TMII_DONE
919     TMII_INSMI( MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 0, 0, OK );
920     TMII_GMII ( MIIM_TYPE, 80,
921         MFT_STRING|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, 0, 0, 0,
922         NULL, OK, ER );
923     TMII_DONE
924     TMII_INSMI( MIIM_TYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, hbm, 6, 0, OK );
925     TMII_GMII ( MIIM_TYPE, 80,
926         MFT_BITMAP, 0, 0, 0, 0, 0, 0, hbm, 0, hbm,
927         NULL, OK, ER );
928     TMII_DONE
929     TMII_INSMI( MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, hbm, 6, 0, OK );
930     TMII_GMII ( MIIM_TYPE, 80,
931         MFT_BITMAP|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, hbm, 0, hbm,
932         NULL, OK, ER );
933     TMII_DONE
934     /* not enough space for name*/
935     TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
936     TMII_GMII ( MIIM_TYPE, 0,
937         MFT_STRING, 0, 0, 0, 0, 0, 0, NULL, 4, 0,
938         NULL, OK, OK );
939     TMII_DONE
940     TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
941     TMII_GMII ( MIIM_TYPE, 5,
942         MFT_STRING, 0, 0, 0, 0, 0, 0, string, 4, 0,
943         txt, OK, OK );
944     TMII_DONE
945     TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
946     TMII_GMII ( MIIM_TYPE, 4,
947         MFT_STRING, 0, 0, 0, 0, 0, 0, string, 3, 0,
948         txt, OK, OK );
949     TMII_DONE
950     TMII_INSMI( MIIM_FTYPE|MIIM_STRING, MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, NULL, 0, 0, OK );
951     TMII_GMII ( MIIM_TYPE, 0,
952         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, NULL, 0, 0,
953         NULL, OK, ER );
954     TMII_DONE
955     /* cannot combine MIIM_TYPE with some other flags */
956     TMII_INSMI( MIIM_TYPE|MIIM_STRING, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, ER );
957     TMII_DONE
958     TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
959     TMII_GMII ( MIIM_TYPE|MIIM_STRING, 80,
960         0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
961         NULL, ER, OK );
962     TMII_DONE
963     TMII_INSMI( MIIM_TYPE|MIIM_FTYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, ER );
964     TMII_DONE
965     TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
966     TMII_GMII ( MIIM_TYPE|MIIM_FTYPE, 80,
967         0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
968         NULL, ER, OK );
969     TMII_DONE
970     TMII_INSMI( MIIM_TYPE|MIIM_BITMAP, MFT_BITMAP, -1, -1, 0, 0, 0, -1, hbm, 6, hbm, ER );
971     TMII_DONE
972         /* but succeeds with some others */
973     TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
974     TMII_GMII ( MIIM_TYPE|MIIM_SUBMENU, 80,
975         MFT_STRING, 0, 0, 0, 0, 0, 0, string, 4, 0,
976         txt, OK, OK );
977     TMII_DONE
978     TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
979     TMII_GMII ( MIIM_TYPE|MIIM_STATE, 80,
980         MFT_STRING, 0, 0, 0, 0, 0, 0, string, 4, 0,
981         txt, OK, OK );
982     TMII_DONE
983     TMII_INSMI( MIIM_TYPE|MIIM_ID, MFT_STRING, -1, 888, 0, 0, 0, -1, txt, 6, 0, OK );
984     TMII_GMII ( MIIM_TYPE|MIIM_ID, 80,
985         MFT_STRING, 0, 888, 0, 0, 0, 0, string, 4, 0,
986         txt, OK, OK );
987     TMII_DONE
988     TMII_INSMI( MIIM_TYPE|MIIM_DATA, MFT_STRING, -1, -1, 0, 0, 0, 999, txt, 6, 0, OK );
989     TMII_GMII ( MIIM_TYPE|MIIM_DATA, 80,
990         MFT_STRING, 0, 0, 0, 0, 0, 999, string, 4, 0,
991         txt, OK, OK );
992     TMII_DONE
993     /* to be continued */
994     /* set text with MIIM_TYPE and retrieve with MIIM_STRING */ 
995     TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
996     TMII_GMII ( MIIM_STRING|MIIM_FTYPE, 80,
997         MFT_STRING, 0, 0, 0, 0, 0, 0, string, 4, 0,
998         txt, OK, OK );
999     TMII_DONE
1000     /* set text with MIIM_TYPE and retrieve with MIIM_STRING; MFT_OWNERDRAW causes an empty string */ 
1001     TMII_INSMI( MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
1002     TMII_GMII ( MIIM_STRING|MIIM_FTYPE, 80,
1003         MFT_STRING|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, string, 0, 0,
1004         empty, OK, ER );
1005     TMII_DONE
1006     TMII_INSMI( MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, NULL, 0, 0, OK );
1007     TMII_GMII ( MIIM_STRING|MIIM_FTYPE, 80,
1008         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, string, 0, 0,
1009         empty, OK, ER );
1010     TMII_DONE
1011     TMII_INSMI( MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, NULL, 0, 0, OK );
1012     TMII_GMII ( MIIM_FTYPE, 80,
1013         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, string, 80, 0,
1014         init, OK, ER );
1015     TMII_DONE
1016     TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 0, 0, OK );
1017     TMII_GMII ( 0, 80,
1018         0, 0, 0, 0, 0, 0, 0, string, 80, 0,
1019         init, OK, OK );
1020     TMII_DONE
1021     /* contrary to MIIM_TYPE,you can set the text for an owner draw menu */ 
1022     TMII_INSMI( MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 0, 0, OK );
1023     TMII_GMII ( MIIM_STRING|MIIM_FTYPE, 80,
1024         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, string, 4, 0,
1025         txt, OK, OK );
1026     TMII_DONE
1027     /* same but retrieve with MIIM_TYPE */ 
1028     TMII_INSMI( MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 0, 0, OK );
1029     TMII_GMII ( MIIM_TYPE, 80,
1030         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, NULL, 4, NULL,
1031         NULL, OK, OK );
1032     TMII_DONE
1033     TMII_INSMI( MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, NULL, 0, 0, OK );
1034     TMII_GMII ( MIIM_STRING|MIIM_FTYPE, 80,
1035         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, string, 0, 0,
1036         empty, OK, ER );
1037     TMII_DONE
1038     TMII_INSMI( MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, NULL, 0, 0, OK );
1039     TMII_GMII ( MIIM_STRING|MIIM_FTYPE, 80,
1040         MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, string, 0, 0,
1041         empty, OK, ER );
1042     TMII_DONE
1043
1044     /* How is that with bitmaps? */ 
1045     TMII_INSMI( MIIM_BITMAP, -1, -1, -1, 0, 0, 0, -1, 0, -1, hbm, OK );
1046     TMII_GMII ( MIIM_TYPE, 80,
1047         MFT_BITMAP, 0, 0, 0, 0, 0, 0, hbm, 0, hbm,
1048         NULL, OK, ER );
1049     TMII_DONE
1050     TMII_INSMI( MIIM_BITMAP, -1, -1, -1, 0, 0, 0, -1, 0, -1, hbm, OK );
1051     TMII_GMII ( MIIM_BITMAP|MIIM_FTYPE, 80,
1052         0, 0, 0, 0, 0, 0, 0, string, 80, hbm,
1053         init, OK, ER );
1054     TMII_DONE
1055         /* MIIM_BITMAP does not like MFT_BITMAP */
1056     TMII_INSMI( MIIM_BITMAP|MIIM_FTYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, 0, -1, hbm, ER );
1057     TMII_DONE
1058         /* no problem with OWNERDRAWN */
1059     TMII_INSMI( MIIM_BITMAP|MIIM_FTYPE, MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, 0, -1, hbm, OK );
1060     TMII_GMII ( MIIM_BITMAP|MIIM_FTYPE, 80,
1061         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, string, 80, hbm,
1062         init, OK, ER );
1063     TMII_DONE
1064         /* setting MFT_BITMAP with MFT_FTYPE fails anyway */
1065     TMII_INSMI( MIIM_FTYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, 0, -1, 0, ER );
1066     TMII_DONE
1067
1068     /* menu with submenu */
1069     TMII_INSMI( MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, 0, 0, -1, txt, 0, 0, OK );
1070     TMII_GMII ( MIIM_SUBMENU, 80,
1071         0, 0, 0, submenu, 0, 0, 0, string, 80, 0,
1072         init, OK, ER );
1073     TMII_DONE
1074     TMII_INSMI( MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, 0, 0, -1, empty, 0, 0, OK );
1075     TMII_GMII ( MIIM_SUBMENU, 80,
1076         0, 0, 0, submenu, 0, 0, 0, string, 80, 0,
1077         init, OK, ER );
1078     TMII_DONE
1079     /* menu with submenu, without MIIM_SUBMENU the submenufield is cleared */
1080     TMII_INSMI( MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, 0, 0, -1, txt, 0, 0, OK );
1081     TMII_GMII ( MIIM_STRING|MIIM_FTYPE, 80,
1082         MFT_STRING|MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, string, 0, 0,
1083         empty, OK, ER );
1084     TMII_GMII ( MIIM_SUBMENU|MIIM_FTYPE, 80,
1085         MFT_SEPARATOR, 0, 0, submenu, 0, 0, 0, string, 80, 0,
1086         empty, OK, ER );
1087     TMII_DONE
1088     /* menu with invalid submenu */
1089     TMII_INSMI( MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, (HMENU)999, 0, 0, -1, txt, 0, 0, ER );
1090     TMII_DONE
1091     /* Separator */
1092     TMII_INSMI( MIIM_TYPE, MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, txt, 0, 0, OK );
1093     TMII_GMII ( MIIM_TYPE, 80,
1094         MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1095         NULL, OK, ER );
1096     TMII_DONE
1097     TMII_INSMI( MIIM_TYPE, MFT_BITMAP|MFT_SEPARATOR, -1, -1, 0, 0, 0, -1, hbm, 6, 0, OK );
1098     TMII_GMII ( MIIM_TYPE, 80,
1099         MFT_BITMAP|MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, hbm, 0, hbm,
1100         NULL, OK, ER );
1101     TMII_DONE
1102      /* SEPARATOR and STRING go well together */
1103     /* BITMAP and STRING go well together */
1104     TMII_INSMI( MIIM_STRING|MIIM_BITMAP, -1, -1, -1, 0, 0, 0, -1, txt, 6, hbm, OK );
1105     TMII_GMII ( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, 80,
1106         MFT_STRING, 0, 0, 0, 0, 0, 0, string, 4, hbm,
1107         txt, OK, OK );
1108     TMII_DONE
1109      /* BITMAP, SEPARATOR and STRING go well together */
1110     TMII_INSMI( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, 0, 0, 0, -1, txt, 6, hbm, OK );
1111     TMII_GMII ( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, 80,
1112         MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, string, 4, hbm,
1113         txt, OK, OK );
1114     TMII_DONE
1115      /* last two tests, but use MIIM_TYPE to retrieve info */
1116     TMII_INSMI( MIIM_FTYPE|MIIM_STRING, MFT_SEPARATOR, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
1117     TMII_GMII ( MIIM_TYPE, 80,
1118         MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, NULL, 4, NULL,
1119         NULL, OK, OK );
1120     TMII_DONE
1121     TMII_INSMI( MIIM_STRING|MIIM_BITMAP, -1, -1, -1, 0, 0, 0, -1, txt, 6, hbm, OK );
1122     TMII_GMII ( MIIM_TYPE, 80,
1123         MFT_BITMAP, 0, 0, 0, 0, 0, 0, hbm, 4, hbm,
1124         NULL, OK, OK );
1125     TMII_DONE
1126     TMII_INSMI( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, 0, 0, 0, -1, txt, 6, hbm, OK );
1127     TMII_GMII ( MIIM_TYPE, 80,
1128         MFT_SEPARATOR|MFT_BITMAP, 0, 0, 0, 0, 0, 0, hbm, 4, hbm,
1129         NULL, OK, OK );
1130     TMII_DONE
1131      /* same three with MFT_OWNERDRAW */
1132     TMII_INSMI( MIIM_FTYPE|MIIM_STRING, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
1133     TMII_GMII ( MIIM_TYPE, 80,
1134         MFT_SEPARATOR|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, NULL, 4, NULL,
1135         NULL, OK, OK );
1136     TMII_DONE
1137     TMII_INSMI( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 6, hbm, OK );
1138     TMII_GMII ( MIIM_TYPE, 80,
1139         MFT_BITMAP|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, hbm, 4, hbm,
1140         NULL, OK, OK );
1141     TMII_DONE
1142     TMII_INSMI( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 6, hbm, OK );
1143     TMII_GMII ( MIIM_TYPE, 80,
1144         MFT_SEPARATOR|MFT_BITMAP|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, hbm, 4, hbm,
1145         NULL, OK, OK );
1146     TMII_DONE
1147
1148     TMII_INSMI( MIIM_STRING|MIIM_FTYPE|MIIM_ID, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 0, 0, OK );
1149     TMII_GMII ( MIIM_TYPE, 80,
1150         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, NULL, 4, NULL,
1151         NULL,  OK, OK );
1152     TMII_DONE
1153     /* test with modifymenu: string is preserved after setting OWNERDRAW */
1154     TMII_INSMI( MIIM_STRING, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 0, 0, OK );
1155     TMII_MODM( MFT_OWNERDRAW, -1, (void*)787 );
1156     TMII_GMII ( MIIM_FTYPE|MIIM_STRING|MIIM_DATA, 80,
1157         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 787, string, 4, 0,
1158         txt,  OK, OK );
1159     TMII_DONE
1160     /* same with bitmap: now the text is cleared */
1161     TMII_INSMI( MIIM_STRING, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 0, 0, OK );
1162     TMII_MODM( MFT_BITMAP, 545, hbm );
1163     TMII_GMII ( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, 80,
1164         MFT_BITMAP, 0, 545, 0, 0, 0, 0, string, 0, hbm,
1165         empty,  OK, ER );
1166     TMII_DONE
1167     /* start with bitmap: now setting text clears it (though he flag is raised) */
1168     TMII_INSMI( MIIM_BITMAP, MFT_STRING, -1, -1, 0, 0, 0, -1, 0, -1, hbm, OK );
1169     TMII_GMII ( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, 80,
1170         MFT_STRING, 0, 0, 0, 0, 0, 0, string, 0, hbm,
1171         empty,  OK, ER );
1172     TMII_MODM( MFT_STRING, 545, txt );
1173     TMII_GMII ( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, 80,
1174         MFT_STRING, 0, 545, 0, 0, 0, 0, string, 4, 0,
1175         txt,  OK, OK );
1176     TMII_DONE
1177     /*repeat with text NULL */
1178     TMII_INSMI( MIIM_BITMAP, MFT_STRING, -1, -1, 0, 0, 0, -1, 0, -1, hbm, OK );
1179     TMII_MODM( MFT_STRING, 545, NULL );
1180     TMII_GMII ( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, 80,
1181         MFT_SEPARATOR, 0, 545, 0, 0, 0, 0, string, 0, 0,
1182         empty,  OK, ER );
1183     TMII_DONE
1184     /* repeat with text "" */
1185     TMII_INSMI( MIIM_BITMAP, -1 , -1, -1, 0, 0, 0, -1, 0, -1, hbm, OK );
1186     TMII_MODM( MFT_STRING, 545, empty );
1187     TMII_GMII ( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, 80,
1188         MFT_STRING, 0, 545, 0, 0, 0, 0, string, 0, 0,
1189         empty,  OK, ER );
1190     TMII_DONE
1191     /* start with bitmap: set ownerdraw */
1192     TMII_INSMI( MIIM_BITMAP, -1, -1, -1, 0, 0, 0, -1, 0, -1, hbm, OK );
1193     TMII_MODM( MFT_OWNERDRAW, -1, (void *)232 );
1194     TMII_GMII ( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_DATA, 80,
1195         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 232, string, 0, hbm,
1196         empty,  OK, ER );
1197     TMII_DONE
1198     /* ask nothing */
1199     TMII_INSMI( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, 0, 0, 0, -1, txt, 6, hbm, OK );
1200     TMII_GMII ( 0, 80,
1201                 0, 0, 0,  0, 0, 0, 0, string, 80, 0,
1202         init, OK, OK );
1203     TMII_DONE
1204     /* some tests with small cbSize: the hbmpItem is to be ignored */
1205     TMII_INSMI( MIIM_BITMAP, -1, -1, -1, 0, 0, 0, -1, 0, -1, dummy_hbm, OK );
1206     TMII_GMII ( MIIM_TYPE, 80,
1207         MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, NULL, 0, NULL,
1208         NULL, OK, ER );
1209     TMII_DONE
1210     TMII_INSMI( MIIM_BITMAP, -1, -1, -1, 0, 0, 0, -1, 0, -1, dummy_hbm, OK );
1211     TMII_GMII ( MIIM_BITMAP|MIIM_FTYPE, 80,
1212         MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, string, 80, NULL,
1213         init, OK, ER );
1214     TMII_DONE
1215     TMII_INSMI( MIIM_STRING|MIIM_BITMAP, -1, -1, -1, 0, 0, 0, -1, txt, 6, dummy_hbm, OK );
1216     TMII_GMII ( MIIM_TYPE, 80,
1217         MFT_STRING, 0, 0, 0, 0, 0, 0, string, 4, NULL,
1218         txt, OK, OK );
1219     TMII_DONE
1220     TMII_INSMI( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, 0, 0, 0, -1, txt, 6, dummy_hbm, OK );
1221     TMII_GMII ( MIIM_TYPE, 80,
1222         MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, NULL, 4, NULL,
1223         NULL, OK, OK );
1224     TMII_DONE
1225     TMII_INSMI( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 6, dummy_hbm, OK );
1226     TMII_GMII ( MIIM_TYPE, 80,
1227         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, NULL, 4, NULL,
1228         NULL, OK, OK );
1229     TMII_DONE
1230     TMII_INSMI( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 6, dummy_hbm, OK );
1231     TMII_GMII ( MIIM_TYPE, 80,
1232         MFT_SEPARATOR|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, NULL, 4, NULL,
1233         NULL, OK, OK );
1234     TMII_DONE
1235     /* MIIM_TYPE by itself does not get/set the dwItemData for OwnerDrawn menus  */
1236     TMII_INSMI( MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, 343, txt, 0, 0, OK );
1237     TMII_GMII ( MIIM_TYPE|MIIM_DATA, 80,
1238         MFT_STRING|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 343, 0, 0, 0,
1239         NULL, OK, ER );
1240     TMII_DONE
1241     TMII_INSMI( MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, 343, txt, 0, 0, OK );
1242     TMII_GMII ( MIIM_TYPE, 80,
1243         MFT_STRING|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1244         NULL, OK, ER );
1245     TMII_DONE
1246     TMII_INSMI( MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, 343, txt, 0, 0, OK );
1247     TMII_GMII ( MIIM_TYPE|MIIM_DATA, 80,
1248         MFT_STRING|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1249         NULL, OK, ER );
1250     TMII_DONE
1251     /* set a string menu to ownerdraw with MIIM_TYPE */
1252     TMII_INSMI( MIIM_TYPE, MFT_STRING, -2, -2, 0, 0, 0, -2, txt, -2, 0, OK );
1253     TMII_SMII ( MIIM_TYPE, MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
1254     TMII_GMII ( MIIM_STRING|MIIM_FTYPE, 80,
1255         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, string, 4, 0,
1256         txt, OK, OK );
1257     TMII_DONE
1258     /* test with modifymenu add submenu */
1259     TMII_INSMI( MIIM_STRING, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 0, 0, OK );
1260     TMII_MODM( MF_POPUP, (UINT_PTR)submenu, txt );
1261     TMII_GMII ( MIIM_FTYPE|MIIM_STRING|MIIM_SUBMENU, 80,
1262         MFT_STRING, 0, 0, submenu, 0, 0, 0, string, 4, 0,
1263         txt,  OK, OK );
1264     TMII_GMII ( MIIM_TYPE, 80,
1265         MFT_STRING, 0, 0, 0, 0, 0, 0, string, 4, 0,
1266         txt,  OK, OK );
1267     TMII_DONE
1268     /* MFT_SEPARATOR bit is kept when the text is added */
1269     TMII_INSMI( MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, NULL, 0, 0, OK );
1270     TMII_SMII( MIIM_STRING, 0, 0, 0, 0, 0, 0, 0, txt, 0, 0 );
1271     TMII_GMII ( MIIM_STRING|MIIM_FTYPE, 80,
1272         MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, string, 4, 0,
1273         txt, OK, OK );
1274     TMII_DONE
1275     /* MFT_SEPARATOR bit is kept when bitmap is added */
1276     TMII_INSMI( MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, NULL, 0, 0, OK );
1277     TMII_SMII( MIIM_BITMAP, 0, 0, 0, 0, 0, 0, 0, 0, 0, hbm );
1278     TMII_GMII ( MIIM_BITMAP|MIIM_FTYPE, 80,
1279         MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, string, 80, hbm,
1280         init, OK, ER );
1281     TMII_DONE
1282     /* Bitmaps inserted with MIIM_TYPE and MFT_BITMAP:
1283        Only the low word of the dwTypeData is used.
1284        Use a magic bitmap here (Word 95 uses this to create its MDI menu buttons) */
1285     TMII_INSMI( MIIM_TYPE, MFT_BITMAP | MFT_RIGHTJUSTIFY, -1, -1, 0, 0, 0, -1,
1286                 (HMENU)MAKELONG(HBMMENU_MBAR_CLOSE, 0x1234), -1, 0, OK );
1287     TMII_GMII ( MIIM_TYPE, 80,
1288         MFT_BITMAP | MFT_RIGHTJUSTIFY, 0, 0, 0, 0, 0, 0, HBMMENU_MBAR_CLOSE, 0, HBMMENU_MBAR_CLOSE,
1289         NULL, OK, OK );
1290     TMII_DONE
1291     /* Type flags */
1292     TMII_INSMI( MIIM_TYPE, MFT_BITMAP | MFT_MENUBARBREAK | MFT_RADIOCHECK | MFT_RIGHTJUSTIFY | MFT_RIGHTORDER, -1, -1, 0, 0, 0, -1, hbm, -1, 0, OK );
1293     TMII_GMII ( MIIM_TYPE, 80,
1294         MFT_BITMAP | MFT_MENUBARBREAK | MFT_RADIOCHECK | MFT_RIGHTJUSTIFY | MFT_RIGHTORDER, 0, 0, 0, 0, 0, 0, hbm, 0, hbm,
1295         NULL, OK, OK );
1296     TMII_DONE
1297     /* State flags */
1298     TMII_INSMI( MIIM_TYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, hbm, -1, 0, OK );
1299     TMII_SMII( MIIM_STATE, -1, MFS_CHECKED | MFS_DEFAULT | MFS_GRAYED | MFS_HILITE, 0, 0, 0, 0, 0, 0, 0, 0 );
1300     TMII_GMII ( MIIM_STATE, 80,
1301         0, MFS_CHECKED | MFS_DEFAULT | MFS_GRAYED | MFS_HILITE, 0, 0, 0, 0, 0, 0, 80, 0,
1302         NULL, OK, OK );
1303     TMII_DONE
1304     /* The style MFT_RADIOCHECK cannot be set with MIIM_CHECKMARKS only */
1305     TMII_INSMI( MIIM_TYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, hbm, -1, 0, OK );
1306     TMII_SMII( MIIM_CHECKMARKS, MFT_RADIOCHECK, 0, 0, 0, hbm, hbm, 0, 0, 0, 0 );
1307     TMII_GMII ( MIIM_CHECKMARKS | MIIM_TYPE, 80,
1308         MFT_BITMAP, 0, 0, 0, hbm, hbm, 0, hbm, 0, hbm,
1309         NULL, OK, OK );
1310     TMII_DONE
1311     /* MFT_BITMAP is added automatically by GetMenuItemInfo() for MIIM_TYPE */
1312     TMII_INSMI( MIIM_TYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, hbm, -1, 0, OK );
1313     TMII_SMII( MIIM_FTYPE, MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, (HBITMAP)0x1234, 0, 0 );
1314     TMII_GMII ( MIIM_FTYPE, 80,
1315         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, 0, 80, 0,
1316         NULL, OK, OK );
1317     TMII_GMII ( MIIM_TYPE, 80,
1318         MFT_BITMAP | MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, hbm, 0, hbm,
1319         NULL, OK, OK );
1320     TMII_GMII ( MIIM_FTYPE, 80,
1321         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, 0, 80, 0,
1322         NULL, OK, OK );
1323     TMII_SMII( MIIM_BITMAP, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL );
1324     TMII_GMII ( MIIM_TYPE, 80,
1325         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, NULL, 0, NULL,
1326         NULL, OK, OK );
1327     TMII_DONE
1328     /* Bitmaps inserted with MIIM_TYPE and MFT_BITMAP:
1329        Only the low word of the dwTypeData is used.
1330        Use a magic bitmap here (Word 95 uses this to create its MDI menu buttons) */
1331     TMII_INSMI( MIIM_TYPE, MFT_BITMAP | MFT_RIGHTJUSTIFY, -1, -1, 0, 0, 0, -1,
1332                 (HMENU)MAKELONG(HBMMENU_MBAR_CLOSE, 0x1234), -1, 0, OK );
1333     TMII_GMII ( MIIM_TYPE, 80,
1334         MFT_BITMAP | MFT_RIGHTJUSTIFY, 0, 0, 0, 0, 0, 0, HBMMENU_MBAR_CLOSE, 0, HBMMENU_MBAR_CLOSE,
1335         NULL, OK, OK );
1336     TMII_DONE
1337     /* Type flags */
1338     TMII_INSMI( MIIM_TYPE, MFT_BITMAP | MFT_MENUBARBREAK | MFT_RADIOCHECK | MFT_RIGHTJUSTIFY | MFT_RIGHTORDER, -1, -1, 0, 0, 0, -1, hbm, -1, 0, OK );
1339     TMII_GMII ( MIIM_TYPE, 80,
1340         MFT_BITMAP | MFT_MENUBARBREAK | MFT_RADIOCHECK | MFT_RIGHTJUSTIFY | MFT_RIGHTORDER, 0, 0, 0, 0, 0, 0, hbm, 0, hbm,
1341         NULL, OK, OK );
1342     TMII_DONE
1343     /* State flags */
1344     TMII_INSMI( MIIM_TYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, hbm, -1, 0, OK );
1345     TMII_SMII( MIIM_STATE, -1, MFS_CHECKED | MFS_DEFAULT | MFS_GRAYED | MFS_HILITE, 0, 0, 0, 0, 0, 0, 0, 0 );
1346     TMII_GMII ( MIIM_STATE, 80,
1347         0, MFS_CHECKED | MFS_DEFAULT | MFS_GRAYED | MFS_HILITE, 0, 0, 0, 0, 0, 0, 80, 0,
1348         NULL, OK, OK );
1349     TMII_DONE
1350     /* The style MFT_RADIOCHECK cannot be set with MIIM_CHECKMARKS only */
1351     TMII_INSMI( MIIM_TYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, hbm, -1, 0, OK );
1352     TMII_SMII( MIIM_CHECKMARKS, MFT_RADIOCHECK, 0, 0, 0, hbm, hbm, 0, 0, 0, 0 );
1353     TMII_GMII ( MIIM_CHECKMARKS | MIIM_TYPE, 80,
1354         MFT_BITMAP, 0, 0, 0, hbm, hbm, 0, hbm, 0, hbm,
1355         NULL, OK, OK );
1356     TMII_DONE
1357     /* MFT_BITMAP is added automatically by GetMenuItemInfo() for MIIM_TYPE */
1358     TMII_INSMI( MIIM_TYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, hbm, -1, 0, OK );
1359     TMII_SMII( MIIM_FTYPE, MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, (HBITMAP)0x1234, 0, 0 );
1360     TMII_GMII ( MIIM_FTYPE, 80,
1361         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, 0, 80, 0,
1362         NULL, OK, OK );
1363     TMII_GMII ( MIIM_TYPE, 80,
1364         MFT_BITMAP | MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, hbm, 0, hbm,
1365         NULL, OK, OK );
1366     TMII_GMII ( MIIM_FTYPE, 80,
1367         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, 0, 80, 0,
1368         NULL, OK, OK );
1369     TMII_SMII( MIIM_BITMAP, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL );
1370     TMII_GMII ( MIIM_TYPE, 80,
1371         MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, NULL, 0, NULL,
1372         NULL, OK, OK );
1373     TMII_DONE
1374   } while( !(ansi = !ansi) );
1375   DeleteObject( hbm);
1376 }
1377
1378 /* 
1379    The following tests try to confirm the algorithm used to return the menu items
1380    when there is a collision between a menu item and a popup menu
1381  */
1382 static void test_menu_search_bycommand( void )
1383 {
1384     HMENU        hmenu, hmenuSub, hmenuSub2;
1385     MENUITEMINFO info;
1386     BOOL         rc;
1387     UINT         id;
1388     char         strback[0x80];
1389     char         strIn[0x80];
1390     static CHAR menuitem[]  = "MenuItem",
1391                 menuitem2[] = "MenuItem 2";
1392
1393     /* Case 1: Menu containing a menu item */
1394     hmenu = CreateMenu();
1395     
1396     memset( &info, 0, sizeof info );
1397     info.cbSize = sizeof info;
1398     info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1399     info.fType = MFT_STRING;
1400     strcpy(strIn, "Case 1 MenuItem");
1401     info.dwTypeData = strIn;
1402     info.wID = (UINT) 0x1234;
1403     
1404     rc = InsertMenuItem(hmenu, 0, TRUE, &info );
1405     ok (rc, "Inserting the menuitem failed\n");
1406
1407     id = GetMenuItemID(hmenu, 0);
1408     ok (id == 0x1234, "Getting the menuitem id failed(gave %x)\n", id);
1409
1410     /* Confirm the menuitem was given the id supplied (getting by position) */
1411     memset( &info, 0, sizeof info );
1412     strback[0] = 0x00;
1413     info.cbSize = sizeof(MENUITEMINFO);
1414     info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1415     info.dwTypeData = strback;
1416     info.cch = sizeof(strback);
1417
1418     rc = GetMenuItemInfo(hmenu, 0, TRUE, &info); /* Get by position */
1419     ok (rc, "Getting the menu items info failed\n");
1420     ok (info.wID == 0x1234, "IDs differ for the menuitem\n");
1421     ok (!strcmp(info.dwTypeData, "Case 1 MenuItem"), "Returned item has wrong label\n");
1422
1423     /* Search by id - Should return the item */
1424     memset( &info, 0, sizeof info );
1425     strback[0] = 0x00;
1426     info.cbSize = sizeof(MENUITEMINFO);
1427     info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1428     info.dwTypeData = strback;
1429     info.cch = sizeof(strback);
1430     rc = GetMenuItemInfo(hmenu, 0x1234, FALSE, &info); /* Get by ID */
1431
1432     ok (rc, "Getting the menu items info failed\n");
1433     ok (info.wID == 0x1234, "IDs differ for the menuitem\n");
1434     ok (!strcmp(info.dwTypeData, "Case 1 MenuItem"), "Returned item has wrong label\n");
1435
1436     DestroyMenu( hmenu );
1437
1438     /* Case 2: Menu containing a popup menu */
1439     hmenu = CreateMenu();
1440     hmenuSub = CreateMenu();
1441     
1442     strcpy(strIn, "Case 2 SubMenu");
1443     rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, strIn);
1444     ok (rc, "Inserting the popup menu into the main menu failed\n");
1445
1446     id = GetMenuItemID(hmenu, 0);
1447     ok (id == -1, "Getting the menuitem id unexpectedly worked (gave %x)\n", id);
1448
1449     /* Confirm the menuitem itself was given an id the same as the HMENU, (getting by position) */
1450     memset( &info, 0, sizeof info );
1451     strback[0] = 0x00;
1452     info.cbSize = sizeof(MENUITEMINFO);
1453     info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1454     info.dwTypeData = strback;
1455     info.cch = sizeof(strback);
1456     info.wID = 0xdeadbeef;
1457
1458     rc = GetMenuItemInfo(hmenu, 0, TRUE, &info); /* Get by position */
1459     ok (rc, "Getting the menu items info failed\n");
1460     ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the menuitem\n");
1461     ok (!strcmp(info.dwTypeData, "Case 2 SubMenu"), "Returned item has wrong label\n");
1462
1463     /* Search by id - returns the popup menu itself */
1464     memset( &info, 0, sizeof info );
1465     strback[0] = 0x00;
1466     info.cbSize = sizeof(MENUITEMINFO);
1467     info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1468     info.dwTypeData = strback;
1469     info.cch = sizeof(strback);
1470     rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
1471
1472     ok (rc, "Getting the menu items info failed\n");
1473     ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
1474     ok (!strcmp(info.dwTypeData, "Case 2 SubMenu"), "Returned item has wrong label\n");
1475
1476     /* 
1477         Now add an item after it with the same id
1478      */
1479     memset( &info, 0, sizeof info );
1480     info.cbSize = sizeof info;
1481     info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1482     info.fType = MFT_STRING;
1483     strcpy(strIn, "Case 2 MenuItem 1");
1484     info.dwTypeData = strIn;
1485     info.wID = (UINT_PTR) hmenuSub;
1486     rc = InsertMenuItem(hmenu, -1, TRUE, &info );
1487     ok (rc, "Inserting the menuitem failed\n");
1488
1489     /* Search by id - returns the item which follows the popup menu */
1490     memset( &info, 0, sizeof info );
1491     strback[0] = 0x00;
1492     info.cbSize = sizeof(MENUITEMINFO);
1493     info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1494     info.dwTypeData = strback;
1495     info.cch = sizeof(strback);
1496     rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
1497
1498     ok (rc, "Getting the menu items info failed\n");
1499     ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
1500     ok (!strcmp(info.dwTypeData, "Case 2 MenuItem 1"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1501
1502     /* 
1503         Now add an item before the popup (with the same id)
1504      */
1505     memset( &info, 0, sizeof info );
1506     info.cbSize = sizeof info;
1507     info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1508     info.fType = MFT_STRING;
1509     strcpy(strIn, "Case 2 MenuItem 2");
1510     info.dwTypeData = strIn;
1511     info.wID = (UINT_PTR) hmenuSub;
1512     rc = InsertMenuItem(hmenu, 0, TRUE, &info );
1513     ok (rc, "Inserting the menuitem failed\n");
1514
1515     /* Search by id - returns the item which precedes the popup menu */
1516     memset( &info, 0, sizeof info );
1517     strback[0] = 0x00;
1518     info.cbSize = sizeof(MENUITEMINFO);
1519     info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1520     info.dwTypeData = strback;
1521     info.cch = sizeof(strback);
1522     rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
1523
1524     ok (rc, "Getting the menu items info failed\n");
1525     ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
1526     ok (!strcmp(info.dwTypeData, "Case 2 MenuItem 2"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1527
1528     DestroyMenu( hmenu );
1529     DestroyMenu( hmenuSub );
1530
1531     /* 
1532         Case 3: Menu containing a popup menu which in turn 
1533            contains 2 items with the same id as the popup itself
1534      */
1535
1536     hmenu = CreateMenu();
1537     hmenuSub = CreateMenu();
1538
1539     memset( &info, 0, sizeof info );
1540     info.cbSize = sizeof info;
1541     info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1542     info.fType = MFT_STRING;
1543     info.dwTypeData = menuitem;
1544     info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1545
1546     rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
1547     ok (rc, "Inserting the popup menu into the main menu failed\n");
1548
1549     rc = InsertMenuItem(hmenuSub, 0, TRUE, &info );
1550     ok (rc, "Inserting the sub menu menuitem failed\n");
1551
1552     memset( &info, 0, sizeof info );
1553     info.cbSize = sizeof info;
1554     info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1555     info.fType = MFT_STRING;
1556     info.dwTypeData = menuitem2;
1557     info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1558
1559     rc = InsertMenuItem(hmenuSub, 1, TRUE, &info );
1560     ok (rc, "Inserting the sub menu menuitem 2 failed\n");
1561
1562     /* Prove that you can't query the id of a popup directly (By position) */
1563     id = GetMenuItemID(hmenu, 0);
1564     ok (id == -1, "Getting the sub menu id should have failed because its a popup (gave %x)\n", id);
1565
1566     /* Prove getting the item info via ID returns the first item (not the popup or 2nd item)*/
1567     memset( &info, 0, sizeof info );
1568     strback[0] = 0x00;
1569     info.cbSize = sizeof(MENUITEMINFO);
1570     info.fMask = MIIM_STRING | MIIM_ID;
1571     info.dwTypeData = strback;
1572     info.cch = sizeof(strback);
1573
1574     rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info);
1575     ok (rc, "Getting the menus info failed\n");
1576     ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for popup menu\n");
1577     ok (!strcmp(info.dwTypeData, "MenuItem"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1578     DestroyMenu( hmenu );
1579     DestroyMenu( hmenuSub );
1580
1581     /* 
1582         Case 4: Menu containing 2 popup menus, the second
1583            contains 2 items with the same id as the first popup menu
1584      */
1585     hmenu = CreateMenu();
1586     hmenuSub = CreateMenu();
1587     hmenuSub2 = CreateMenu();
1588     
1589     rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
1590     ok (rc, "Inserting the popup menu into the main menu failed\n");
1591     
1592     rc = InsertMenu(hmenu, 1, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub2, "Submenu2");
1593     ok (rc, "Inserting the popup menu into the main menu failed\n");
1594
1595     memset( &info, 0, sizeof info );
1596     info.cbSize = sizeof info;
1597     info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1598     info.fType = MFT_STRING;
1599     info.dwTypeData = menuitem;
1600     info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1601
1602     rc = InsertMenuItem(hmenuSub2, 0, TRUE, &info );
1603     ok (rc, "Inserting the sub menu menuitem failed\n");
1604
1605     memset( &info, 0, sizeof info );
1606     info.cbSize = sizeof info;
1607     info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1608     info.fType = MFT_STRING;
1609     info.dwTypeData = menuitem2;
1610     info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
1611
1612     rc = InsertMenuItem(hmenuSub2, 1, TRUE, &info );
1613     ok (rc, "Inserting the sub menu menuitem 2 failed\n");
1614
1615     /* Prove getting the item info via ID returns the first item (not the popup or 2nd item)*/
1616     memset( &info, 0, sizeof info );
1617     strback[0] = 0x00;
1618     info.cbSize = sizeof(MENUITEMINFO);
1619     info.fMask = MIIM_STRING | MIIM_ID;
1620     info.dwTypeData = strback;
1621     info.cch = sizeof(strback);
1622
1623     rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info);
1624     ok (rc, "Getting the menus info failed\n");
1625     ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for popup menu\n");
1626     ok (!strcmp(info.dwTypeData, "MenuItem"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1627
1628     memset( &info, 0, sizeof info );
1629     strback[0] = 0x00;
1630     info.cbSize = sizeof(MENUITEMINFO);
1631     info.fMask = MIIM_STRING | MIIM_ID;
1632     info.dwTypeData = strback;
1633     info.cch = sizeof(strback);
1634
1635     rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub2, FALSE, &info);
1636     ok (rc, "Getting the menus info failed\n");
1637     ok (info.wID == (UINT_PTR)hmenuSub2, "IDs differ for popup menu\n");
1638     ok (!strcmp(info.dwTypeData, "Submenu2"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1639
1640     DestroyMenu( hmenu );
1641     DestroyMenu( hmenuSub );
1642     DestroyMenu( hmenuSub2 );
1643
1644
1645     /* 
1646         Case 5: Menu containing a popup menu which in turn
1647            contains an item with a different id than the popup menu.
1648            This tests the fallback to a popup menu ID.
1649      */
1650
1651     hmenu = CreateMenu();
1652     hmenuSub = CreateMenu();
1653
1654     rc = AppendMenu(hmenu, MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
1655     ok (rc, "Appending the popup menu to the main menu failed\n");
1656
1657     rc = AppendMenu(hmenuSub, MF_STRING, 102, "Item");
1658     ok (rc, "Appending the item to the popup menu failed\n");
1659
1660     /* Set the ID for hmenuSub */
1661     info.cbSize = sizeof(info);
1662     info.fMask = MIIM_ID;
1663     info.wID = 101;
1664
1665     rc = SetMenuItemInfo(hmenu, 0, TRUE, &info);
1666     ok(rc, "Setting the ID for the popup menu failed\n");
1667
1668     /* Check if the ID has been set */
1669     info.wID = 0;
1670     rc = GetMenuItemInfo(hmenu, 0, TRUE, &info);
1671     ok(rc, "Getting the ID for the popup menu failed\n");
1672     ok(info.wID == 101, "The ID for the popup menu has not been set\n");
1673
1674     /* Prove getting the item info via ID returns the popup menu */
1675     memset( &info, 0, sizeof(info));
1676     strback[0] = 0x00;
1677     info.cbSize = sizeof(MENUITEMINFO);
1678     info.fMask = MIIM_STRING | MIIM_ID;
1679     info.dwTypeData = strback;
1680     info.cch = sizeof(strback);
1681
1682     rc = GetMenuItemInfo(hmenu, 101, FALSE, &info);
1683     ok (rc, "Getting the menu info failed\n");
1684     ok (info.wID == 101, "IDs differ\n");
1685     ok (!strcmp(info.dwTypeData, "Submenu"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1686
1687     /* Also look for the menu item  */
1688     memset( &info, 0, sizeof(info));
1689     strback[0] = 0x00;
1690     info.cbSize = sizeof(MENUITEMINFO);
1691     info.fMask = MIIM_STRING | MIIM_ID;
1692     info.dwTypeData = strback;
1693     info.cch = sizeof(strback);
1694
1695     rc = GetMenuItemInfo(hmenu, 102, FALSE, &info);
1696     ok (rc, "Getting the menu info failed\n");
1697     ok (info.wID == 102, "IDs differ\n");
1698     ok (!strcmp(info.dwTypeData, "Item"), "Returned item has wrong label (%s)\n", info.dwTypeData);
1699
1700     DestroyMenu(hmenu);
1701     DestroyMenu(hmenuSub);
1702 }
1703
1704 struct menu_item_pair_s {
1705     UINT uMenu; /* 1 - top level menu, [0-Menu 1-Enabled 2-Disabled]
1706                  * 2 - 2nd level menu, [0-Popup 1-Enabled 2-Disabled]
1707                  * 3 - 3rd level menu, [0-Enabled 1-Disabled] */
1708     UINT uItem;
1709 };
1710
1711 static struct menu_mouse_tests_s {
1712     DWORD type;
1713     struct menu_item_pair_s menu_item_pairs[5]; /* for mousing */
1714     WORD wVk[5]; /* keys */
1715     BOOL bMenuVisible;
1716     BOOL _todo_wine;
1717 } menu_tests[] = {
1718     /* for each test, send keys or clicks and check for menu visibility */
1719     { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 0}, TRUE, FALSE }, /* test 0 */
1720     { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, 0}, FALSE, FALSE },
1721     { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 0}, TRUE, FALSE },
1722     { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
1723     { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 0}, TRUE, FALSE },
1724     { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
1725     { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 0}, TRUE, FALSE },
1726     { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, VK_ESCAPE, 0}, FALSE, FALSE },
1727     { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', VK_ESCAPE, 0}, TRUE, FALSE },
1728     { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, 0}, FALSE, FALSE },
1729     { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 0}, TRUE, FALSE },
1730     { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
1731     { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 0}, TRUE, FALSE },
1732     { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
1733     { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 'P', 0}, TRUE, FALSE },
1734     { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
1735     { INPUT_KEYBOARD, {{0}}, {VK_LMENU, 'M', 'P', 0}, TRUE, FALSE },
1736     { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
1737
1738     { INPUT_MOUSE, {{1, 2}, {0}}, {0}, TRUE, TRUE }, /* test 18 */
1739     { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
1740     { INPUT_MOUSE, {{1, 0}, {0}}, {0}, TRUE, TRUE },
1741     { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
1742     { INPUT_MOUSE, {{1, 0}, {2, 2}, {0}}, {0}, TRUE, TRUE },
1743     { INPUT_MOUSE, {{2, 1}, {0}}, {0}, FALSE, FALSE },
1744     { INPUT_MOUSE, {{1, 0}, {2, 0}, {0}}, {0}, TRUE, TRUE },
1745     { INPUT_MOUSE, {{3, 0}, {0}}, {0}, FALSE, FALSE },
1746     { INPUT_MOUSE, {{1, 0}, {2, 0}, {0}}, {0}, TRUE, TRUE },
1747     { INPUT_MOUSE, {{3, 1}, {0}}, {0}, TRUE, TRUE },
1748     { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
1749     { -1 }
1750 };
1751
1752 static void send_key(WORD wVk)
1753 {
1754     TEST_INPUT i[2];
1755     memset(i, 0, sizeof(i));
1756     i[0].type = i[1].type = INPUT_KEYBOARD;
1757     i[0].u.ki.wVk = i[1].u.ki.wVk = wVk;
1758     i[1].u.ki.dwFlags = KEYEVENTF_KEYUP;
1759     pSendInput(2, (INPUT *) i, sizeof(INPUT));
1760 }
1761
1762 static void click_menu(HANDLE hWnd, struct menu_item_pair_s *mi)
1763 {
1764     HMENU hMenu = hMenus[mi->uMenu];
1765     TEST_INPUT i[3];
1766     MSG msg;
1767     RECT r;
1768     int screen_w = GetSystemMetrics(SM_CXSCREEN);
1769     int screen_h = GetSystemMetrics(SM_CYSCREEN);
1770     BOOL ret = GetMenuItemRect(mi->uMenu > 2 ? NULL : hWnd, hMenu, mi->uItem, &r);
1771     if(!ret) return;
1772
1773     memset(i, 0, sizeof(i));
1774     i[0].type = i[1].type = i[2].type = INPUT_MOUSE;
1775     i[0].u.mi.dx = i[1].u.mi.dx = i[2].u.mi.dx
1776             = ((r.left + 5) * 65535) / screen_w;
1777     i[0].u.mi.dy = i[1].u.mi.dy = i[2].u.mi.dy
1778             = ((r.top + 5) * 65535) / screen_h;
1779     i[0].u.mi.dwFlags = i[1].u.mi.dwFlags = i[2].u.mi.dwFlags
1780             = MOUSEEVENTF_ABSOLUTE;
1781     i[0].u.mi.dwFlags |= MOUSEEVENTF_MOVE;
1782     i[1].u.mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
1783     i[2].u.mi.dwFlags |= MOUSEEVENTF_LEFTUP;
1784     pSendInput(3, (INPUT *) i, sizeof(INPUT));
1785
1786     /* hack to prevent mouse message buildup in Wine */
1787     while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
1788 }
1789
1790 static DWORD WINAPI test_menu_input_thread(LPVOID lpParameter)
1791 {
1792     int i, j;
1793     HANDLE hWnd = lpParameter;
1794
1795     Sleep(500);
1796     /* mixed keyboard/mouse test */
1797     for (i = 0; menu_tests[i].type != -1; i++)
1798     {
1799         int elapsed = 0;
1800
1801         if (menu_tests[i].type == INPUT_KEYBOARD)
1802             for (j = 0; menu_tests[i].wVk[j] != 0; j++)
1803                 send_key(menu_tests[i].wVk[j]);
1804         else
1805             for (j = 0; menu_tests[i].menu_item_pairs[j].uMenu != 0; j++)
1806                 click_menu(hWnd, &menu_tests[i].menu_item_pairs[j]);
1807
1808         while (menu_tests[i].bMenuVisible != bMenuVisible)
1809         {
1810             if (elapsed > 200)
1811                 break;
1812             elapsed += 20;
1813             Sleep(20);
1814         }
1815
1816         if (menu_tests[i]._todo_wine)
1817         {
1818             todo_wine {
1819                 ok(menu_tests[i].bMenuVisible == bMenuVisible, "test %d\n", i);
1820             }
1821         }
1822         else
1823             ok(menu_tests[i].bMenuVisible == bMenuVisible, "test %d\n", i);
1824     }
1825     return 0;
1826 }
1827
1828 static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam,
1829         LPARAM lParam)
1830 {
1831     switch (msg) {
1832         case WM_ENTERMENULOOP:
1833             bMenuVisible = TRUE;
1834             break;
1835         case WM_EXITMENULOOP:
1836             bMenuVisible = FALSE;
1837             break;
1838         default:
1839             return( DefWindowProcA( hWnd, msg, wParam, lParam ) );
1840     }
1841     return 0;
1842 }
1843
1844 static void test_menu_input(void) {
1845     MSG msg;
1846     WNDCLASSA  wclass;
1847     HINSTANCE hInstance = GetModuleHandleA( NULL );
1848     HANDLE hThread, hWnd;
1849     DWORD tid;
1850
1851     wclass.lpszClassName = "MenuTestClass";
1852     wclass.style         = CS_HREDRAW | CS_VREDRAW;
1853     wclass.lpfnWndProc   = WndProc;
1854     wclass.hInstance     = hInstance;
1855     wclass.hIcon         = LoadIconA( 0, (LPSTR)IDI_APPLICATION );
1856     wclass.hCursor       = LoadCursorA( NULL, (LPSTR)IDC_ARROW);
1857     wclass.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1);
1858     wclass.lpszMenuName  = 0;
1859     wclass.cbClsExtra    = 0;
1860     wclass.cbWndExtra    = 0;
1861     assert (RegisterClassA( &wclass ));
1862     assert (hWnd = CreateWindowA( wclass.lpszClassName, "MenuTest",
1863                                   WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0,
1864                                   400, 200, NULL, NULL, hInstance, NULL) );
1865
1866     /* fixed menus */
1867     hMenus[3] = CreatePopupMenu();
1868     AppendMenu(hMenus[3], MF_STRING, 0, "&Enabled");
1869     AppendMenu(hMenus[3], MF_STRING|MF_DISABLED, 0, "&Disabled");
1870
1871     hMenus[2] = CreatePopupMenu();
1872     AppendMenu(hMenus[2], MF_STRING|MF_POPUP, (UINT_PTR) hMenus[3], "&Popup");
1873     AppendMenu(hMenus[2], MF_STRING, 0, "&Enabled");
1874     AppendMenu(hMenus[2], MF_STRING|MF_DISABLED, 0, "&Disabled");
1875
1876     hMenus[1] = CreateMenu();
1877     AppendMenu(hMenus[1], MF_STRING|MF_POPUP, (UINT_PTR) hMenus[2], "&Menu");
1878     AppendMenu(hMenus[1], MF_STRING, 0, "&Enabled");
1879     AppendMenu(hMenus[1], MF_STRING|MF_DISABLED, 0, "&Disabled");
1880
1881     SetMenu(hWnd, hMenus[1]);
1882     ShowWindow(hWnd, SW_SHOW);
1883     UpdateWindow(hWnd);
1884
1885     hThread = CreateThread(NULL, 0, test_menu_input_thread, hWnd, 0, &tid);
1886     while(1)
1887     {
1888         if (WAIT_TIMEOUT != WaitForSingleObject(hThread, 50))
1889             break;
1890         while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
1891     }
1892     DestroyWindow(hWnd);
1893 }
1894
1895 static void test_menu_flags( void )
1896 {
1897     HMENU hMenu, hPopupMenu;
1898
1899     hMenu = CreateMenu();
1900     hPopupMenu = CreatePopupMenu();
1901
1902     AppendMenu(hMenu, MF_POPUP | MF_STRING, (UINT_PTR)hPopupMenu, "Popup");
1903
1904     AppendMenu(hPopupMenu, MF_STRING | MF_HILITE | MF_DEFAULT, 101, "Item 1");
1905     InsertMenu(hPopupMenu, 1, MF_BYPOSITION | MF_STRING | MF_HILITE | MF_DEFAULT, 102, "Item 2");
1906     AppendMenu(hPopupMenu, MF_STRING, 103, "Item 3");
1907     ModifyMenu(hPopupMenu, 2, MF_BYPOSITION | MF_STRING | MF_HILITE | MF_DEFAULT, 103, "Item 3");
1908
1909     ok(GetMenuState(hPopupMenu, 0, MF_BYPOSITION) & MF_HILITE,
1910       "AppendMenu should accept MF_HILITE\n");
1911     ok(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE,
1912       "InsertMenu should accept MF_HILITE\n");
1913     ok(GetMenuState(hPopupMenu, 2, MF_BYPOSITION) & MF_HILITE,
1914       "ModifyMenu should accept MF_HILITE\n");
1915
1916     ok(!(GetMenuState(hPopupMenu, 0, MF_BYPOSITION) & MF_DEFAULT),
1917       "AppendMenu must not accept MF_DEFAULT\n");
1918     ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_DEFAULT),
1919       "InsertMenu must not accept MF_DEFAULT\n");
1920     ok(!(GetMenuState(hPopupMenu, 2, MF_BYPOSITION) & MF_DEFAULT),
1921       "ModifyMenu must not accept MF_DEFAULT\n");
1922
1923     DestroyMenu(hMenu);
1924 }
1925
1926 static void test_menu_hilitemenuitem( void )
1927 {
1928     HMENU hMenu, hPopupMenu;
1929     WNDCLASSA wclass;
1930     HWND hWnd;
1931
1932     wclass.lpszClassName = "HiliteMenuTestClass";
1933     wclass.style         = CS_HREDRAW | CS_VREDRAW;
1934     wclass.lpfnWndProc   = WndProc;
1935     wclass.hInstance     = GetModuleHandleA( NULL );
1936     wclass.hIcon         = LoadIconA( 0, (LPSTR)IDI_APPLICATION );
1937     wclass.hCursor       = LoadCursorA( NULL, (LPSTR)IDC_ARROW);
1938     wclass.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1);
1939     wclass.lpszMenuName  = 0;
1940     wclass.cbClsExtra    = 0;
1941     wclass.cbWndExtra    = 0;
1942     assert (RegisterClassA( &wclass ));
1943     assert (hWnd = CreateWindowA( wclass.lpszClassName, "HiliteMenuTest",
1944                                   WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0,
1945                                   400, 200, NULL, NULL, wclass.hInstance, NULL) );
1946
1947     hMenu = CreateMenu();
1948     hPopupMenu = CreatePopupMenu();
1949
1950     AppendMenu(hMenu, MF_POPUP | MF_STRING, (UINT_PTR)hPopupMenu, "Popup");
1951
1952     AppendMenu(hPopupMenu, MF_STRING, 101, "Item 1");
1953     AppendMenu(hPopupMenu, MF_STRING, 102, "Item 2");
1954     AppendMenu(hPopupMenu, MF_STRING, 103, "Item 3");
1955
1956     SetMenu(hWnd, hMenu);
1957
1958     /* test invalid arguments */
1959
1960     ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE),
1961       "HiliteMenuItem: Item 2 is hilited\n");
1962
1963     SetLastError(0xdeadbeef);
1964     todo_wine
1965     {
1966     ok(!HiliteMenuItem(NULL, hPopupMenu, 1, MF_HILITE | MF_BYPOSITION),
1967       "HiliteMenuItem: call should have failed.\n");
1968     }
1969     ok(GetLastError() == 0xdeadbeef || /* 9x */
1970        GetLastError() == ERROR_INVALID_WINDOW_HANDLE /* NT */,
1971       "HiliteMenuItem: expected error ERROR_INVALID_WINDOW_HANDLE, got: %d\n", GetLastError());
1972
1973     SetLastError(0xdeadbeef);
1974     ok(!HiliteMenuItem(hWnd, NULL, 1, MF_HILITE | MF_BYPOSITION),
1975       "HiliteMenuItem: call should have failed.\n");
1976     ok(GetLastError() == 0xdeadbeef || /* 9x */
1977        GetLastError() == ERROR_INVALID_MENU_HANDLE /* NT */,
1978       "HiliteMenuItem: expected error ERROR_INVALID_MENU_HANDLE, got: %d\n", GetLastError());
1979
1980     ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE),
1981       "HiliteMenuItem: Item 2 is hilited\n");
1982
1983     /* either MF_HILITE or MF_UNHILITE *and* MF_BYCOMMAND or MF_BYPOSITION need to be set */
1984
1985     SetLastError(0xdeadbeef);
1986     ok(HiliteMenuItem(hWnd, hPopupMenu, 1, MF_BYPOSITION),
1987       "HiliteMenuItem: call should have succeeded.\n");
1988     ok(GetLastError() == 0xdeadbeef,
1989       "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
1990
1991     ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE),
1992       "HiliteMenuItem: Item 2 is hilited\n");
1993
1994     SetLastError(0xdeadbeef);
1995     todo_wine
1996     {
1997     ok(HiliteMenuItem(hWnd, hPopupMenu, 1, MF_HILITE),
1998       "HiliteMenuItem: call should have succeeded.\n");
1999     }
2000     ok(GetLastError() == 0xdeadbeef,
2001       "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
2002
2003     ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE),
2004       "HiliteMenuItem: Item 2 is hilited\n");
2005
2006     /* hilite a menu item (by position) */
2007
2008     SetLastError(0xdeadbeef);
2009     ok(HiliteMenuItem(hWnd, hPopupMenu, 1, MF_HILITE | MF_BYPOSITION),
2010       "HiliteMenuItem: call should not have failed.\n");
2011     ok(GetLastError() == 0xdeadbeef,
2012       "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
2013
2014     todo_wine
2015     {
2016     ok(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE,
2017       "HiliteMenuItem: Item 2 is not hilited\n");
2018     }
2019
2020     /* unhilite a menu item (by position) */
2021
2022     SetLastError(0xdeadbeef);
2023     ok(HiliteMenuItem(hWnd, hPopupMenu, 1, MF_UNHILITE | MF_BYPOSITION),
2024       "HiliteMenuItem: call should not have failed.\n");
2025     ok(GetLastError() == 0xdeadbeef,
2026       "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
2027
2028     ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE),
2029       "HiliteMenuItem: Item 2 is hilited\n");
2030
2031     /* hilite a menu item (by command) */
2032
2033     SetLastError(0xdeadbeef);
2034     ok(HiliteMenuItem(hWnd, hPopupMenu, 103, MF_HILITE | MF_BYCOMMAND),
2035       "HiliteMenuItem: call should not have failed.\n");
2036     ok(GetLastError() == 0xdeadbeef,
2037       "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
2038
2039     todo_wine
2040     {
2041     ok(GetMenuState(hPopupMenu, 2, MF_BYPOSITION) & MF_HILITE,
2042       "HiliteMenuItem: Item 3 is not hilited\n");
2043     }
2044
2045     /* unhilite a menu item (by command) */
2046
2047     SetLastError(0xdeadbeef);
2048     ok(HiliteMenuItem(hWnd, hPopupMenu, 103, MF_UNHILITE | MF_BYCOMMAND),
2049       "HiliteMenuItem: call should not have failed.\n");
2050     ok(GetLastError() == 0xdeadbeef,
2051       "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
2052
2053     ok(!(GetMenuState(hPopupMenu, 2, MF_BYPOSITION) & MF_HILITE),
2054       "HiliteMenuItem: Item 3 is hilited\n");
2055
2056     DestroyWindow(hWnd);
2057 }
2058
2059 static void check_menu_items(HMENU hmenu, UINT checked_cmd, UINT checked_type,
2060                              UINT checked_state)
2061 {
2062     INT i, count;
2063
2064     count = GetMenuItemCount(hmenu);
2065     ok (count != -1, "GetMenuItemCount returned -1\n");
2066
2067     for (i = 0; i < count; i++)
2068     {
2069         BOOL ret;
2070         MENUITEMINFO mii;
2071
2072         memset(&mii, 0, sizeof(mii));
2073         mii.cbSize = sizeof(mii);
2074         mii.fMask  = MIIM_FTYPE | MIIM_STATE | MIIM_ID | MIIM_SUBMENU;
2075         ret = GetMenuItemInfo(hmenu, i, TRUE, &mii);
2076         ok(ret, "GetMenuItemInfo(%u) failed\n", i);
2077 #if 0
2078         trace("item #%u: fType %04x, fState %04x, wID %u, hSubMenu %p\n",
2079                i, mii.fType, mii.fState, mii.wID, mii.hSubMenu);
2080 #endif
2081         if (mii.hSubMenu)
2082         {
2083             ok(mii.wID == (UINT_PTR)mii.hSubMenu, "id %u: wID should be equal to hSubMenu\n", checked_cmd);
2084             check_menu_items(mii.hSubMenu, checked_cmd, checked_type, checked_state);
2085         }
2086         else
2087         {
2088             if (mii.wID == checked_cmd)
2089             {
2090                 ok(mii.fType == checked_type, "id %u: expected fType %04x, got %04x\n", checked_cmd, checked_type, mii.fType);
2091                 ok(mii.fState == checked_state, "id %u: expected fState %04x, got %04x\n", checked_cmd, checked_state, mii.fState);
2092                 ok(mii.wID != 0, "id %u: not expected wID 0\n", checked_cmd);
2093             }
2094             else
2095             {
2096                 ok(mii.fType != MFT_RADIOCHECK, "id %u: not expected fType MFT_RADIOCHECK on cmd %u\n", checked_cmd, mii.wID);
2097
2098                 if (mii.fType == MFT_SEPARATOR)
2099                 {
2100                     ok(mii.fState == MFS_GRAYED, "id %u: expected fState MFS_GRAYED, got %04x\n", checked_cmd, mii.fState);
2101                     ok(mii.wID == 0, "id %u: expected wID 0, got %u\n", checked_cmd, mii.wID);
2102                 }
2103                 else
2104                 {
2105                     ok(mii.fState == 0, "id %u: expected fState 0, got %04x\n", checked_cmd, mii.fState);
2106                     ok(mii.wID != 0, "id %u: not expected wID 0\n", checked_cmd);
2107                 }
2108             }
2109         }
2110     }
2111 }
2112
2113 static void clear_ftype_and_state(HMENU hmenu, UINT id, UINT flags)
2114 {
2115     BOOL ret;
2116     MENUITEMINFO mii;
2117
2118     memset(&mii, 0, sizeof(mii));
2119     mii.cbSize = sizeof(mii);
2120     mii.fMask  = MIIM_FTYPE | MIIM_STATE;
2121     ret = SetMenuItemInfo(hmenu, id, (flags & MF_BYPOSITION) != 0, &mii);
2122     ok(ret, "SetMenuItemInfo(%u) failed\n", id);
2123 }
2124
2125 static void test_CheckMenuRadioItem(void)
2126 {
2127     BOOL ret;
2128     HMENU hmenu;
2129
2130     hmenu = LoadMenu(GetModuleHandle(0), MAKEINTRESOURCE(1));
2131     assert(hmenu != 0);
2132
2133     check_menu_items(hmenu, -1, 0, 0);
2134
2135     ret = CheckMenuRadioItem(hmenu, 100, 100, 100, MF_BYCOMMAND);
2136     ok(ret, "CheckMenuRadioItem failed\n");
2137     check_menu_items(hmenu, 100, MFT_RADIOCHECK, MFS_CHECKED);
2138
2139     /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
2140     ret = CheckMenuRadioItem(hmenu, 100, 100, -1, MF_BYCOMMAND);
2141     ok(!ret, "CheckMenuRadioItem should return FALSE\n");
2142     check_menu_items(hmenu, 100, MFT_RADIOCHECK, 0);
2143
2144     /* clear check */
2145     clear_ftype_and_state(hmenu, 100, MF_BYCOMMAND);
2146     check_menu_items(hmenu, -1, 0, 0);
2147
2148     /* first and checked items are on different menus */
2149     ret = CheckMenuRadioItem(hmenu, 0, 300, 202, MF_BYCOMMAND);
2150     ok(!ret, "CheckMenuRadioItem should return FALSE\n");
2151     check_menu_items(hmenu, -1, 0, 0);
2152
2153     ret = CheckMenuRadioItem(hmenu, 200, 300, 202, MF_BYCOMMAND);
2154     ok(ret, "CheckMenuRadioItem failed\n");
2155     check_menu_items(hmenu, 202, MFT_RADIOCHECK, MFS_CHECKED);
2156
2157     /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
2158     ret = CheckMenuRadioItem(hmenu, 202, 202, -1, MF_BYCOMMAND);
2159     ok(!ret, "CheckMenuRadioItem should return FALSE\n");
2160     check_menu_items(hmenu, 202, MFT_RADIOCHECK, 0);
2161
2162     /* clear check */
2163     clear_ftype_and_state(hmenu, 202, MF_BYCOMMAND);
2164     check_menu_items(hmenu, -1, 0, 0);
2165
2166     /* just for fun, try to check separator */
2167     ret = CheckMenuRadioItem(hmenu, 0, 300, 0, MF_BYCOMMAND);
2168     ok(!ret, "CheckMenuRadioItem should return FALSE\n");
2169     check_menu_items(hmenu, -1, 0, 0);
2170 }
2171
2172 static void test_menu_resource_layout(void)
2173 {
2174     static const struct
2175     {
2176         MENUITEMTEMPLATEHEADER mith;
2177         WORD data[14];
2178     } menu_template =
2179     {
2180         { 0, 0 }, /* versionNumber, offset */
2181         {
2182             /* mtOption, mtID, mtString[] '\0' terminated */
2183             MF_STRING, 1, 'F', 0,
2184             MF_STRING, 2, 0,
2185             MF_SEPARATOR, 3, 0,
2186             /* MF_SEPARATOR, 4, 'S', 0, FIXME: Wine ignores 'S' */
2187             MF_STRING|MF_GRAYED|MF_END, 5, 'E', 0
2188         }
2189     };
2190     static const struct
2191     {
2192         UINT type, state, id;
2193         const char *str;
2194     } menu_data[] =
2195     {
2196         { MF_STRING, MF_ENABLED, 1, "F" },
2197         { MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 2, "" },
2198         { MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 3, "" },
2199         /*{ MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 4, "S" }, FIXME: Wine ignores 'S'*/
2200         { MF_STRING, MF_GRAYED, 5, "E" },
2201         { MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 6, "" },
2202         { MF_STRING, MF_ENABLED, 7, "" },
2203         { MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 8, "" }
2204     };
2205     HMENU hmenu;
2206     INT count, i;
2207     BOOL ret;
2208
2209     hmenu = LoadMenuIndirect(&menu_template);
2210     ok(hmenu != 0, "LoadMenuIndirect error %u\n", GetLastError());
2211
2212     ret = AppendMenu(hmenu, MF_STRING, 6, NULL);
2213     ok(ret, "AppendMenu failed\n");
2214     ret = AppendMenu(hmenu, MF_STRING, 7, "\0");
2215     ok(ret, "AppendMenu failed\n");
2216     ret = AppendMenu(hmenu, MF_SEPARATOR, 8, "separator");
2217     ok(ret, "AppendMenu failed\n");
2218
2219     count = GetMenuItemCount(hmenu);
2220     ok(count == sizeof(menu_data)/sizeof(menu_data[0]),
2221        "expected %u menu items, got %u\n",
2222        (UINT)(sizeof(menu_data)/sizeof(menu_data[0])), count);
2223
2224     for (i = 0; i < count; i++)
2225     {
2226         char buf[20];
2227         MENUITEMINFO mii;
2228
2229         memset(&mii, 0, sizeof(mii));
2230         mii.cbSize = sizeof(mii);
2231         mii.dwTypeData = buf;
2232         mii.cch = sizeof(buf);
2233         mii.fMask  = MIIM_FTYPE | MIIM_STATE | MIIM_ID | MIIM_STRING;
2234         ret = GetMenuItemInfo(hmenu, i, TRUE, &mii);
2235         ok(ret, "GetMenuItemInfo(%u) failed\n", i);
2236 #if 0
2237         trace("item #%u: fType %04x, fState %04x, wID %u, dwTypeData %s\n",
2238                i, mii.fType, mii.fState, mii.wID, (LPCSTR)mii.dwTypeData);
2239 #endif
2240         ok(mii.fType == menu_data[i].type,
2241            "%u: expected fType %04x, got %04x\n", i, menu_data[i].type, mii.fType);
2242         ok(mii.fState == menu_data[i].state,
2243            "%u: expected fState %04x, got %04x\n", i, menu_data[i].state, mii.fState);
2244         ok(mii.wID == menu_data[i].id,
2245            "%u: expected wID %04x, got %04x\n", i, menu_data[i].id, mii.wID);
2246         ok(mii.cch == strlen(menu_data[i].str),
2247            "%u: expected cch %u, got %u\n", i, (UINT)strlen(menu_data[i].str), mii.cch);
2248         ok(!strcmp((LPCSTR)mii.dwTypeData, menu_data[i].str),
2249            "%u: expected dwTypeData %s, got %s\n", i, menu_data[i].str, (LPCSTR)mii.dwTypeData);
2250     }
2251
2252     DestroyMenu(hmenu);
2253 }
2254
2255 struct menu_data
2256 {
2257     UINT type, id;
2258     const char *str;
2259 };
2260
2261 static HMENU create_menu_from_data(const struct menu_data *item, INT item_count)
2262 {
2263     HMENU hmenu;
2264     INT i;
2265     BOOL ret;
2266
2267     hmenu = CreateMenu();
2268     assert(hmenu != 0);
2269
2270     for (i = 0; i < item_count; i++)
2271     {
2272         SetLastError(0xdeadbeef);
2273         ret = AppendMenu(hmenu, item[i].type, item[i].id, item[i].str);
2274         ok(ret, "%d: AppendMenu(%04x, %04x, %p) error %u\n",
2275            i, item[i].type, item[i].id, item[i].str, GetLastError());
2276     }
2277     return hmenu;
2278 }
2279
2280 static void compare_menu_data(HMENU hmenu, const struct menu_data *item, INT item_count)
2281 {
2282     INT count, i;
2283     BOOL ret;
2284
2285     count = GetMenuItemCount(hmenu);
2286     ok(count == item_count, "expected %d, got %d menu items\n", count, item_count);
2287
2288     for (i = 0; i < count; i++)
2289     {
2290         char buf[20];
2291         MENUITEMINFO mii;
2292
2293         memset(&mii, 0, sizeof(mii));
2294         mii.cbSize = sizeof(mii);
2295         mii.dwTypeData = buf;
2296         mii.cch = sizeof(buf);
2297         mii.fMask  = MIIM_FTYPE | MIIM_ID | MIIM_STRING | MIIM_BITMAP;
2298         ret = GetMenuItemInfo(hmenu, i, TRUE, &mii);
2299         ok(ret, "GetMenuItemInfo(%u) failed\n", i);
2300 #if 0
2301         trace("item #%u: fType %04x, fState %04x, wID %04x, hbmp %p\n",
2302                i, mii.fType, mii.fState, mii.wID, mii.hbmpItem);
2303 #endif
2304         ok(mii.fType == item[i].type,
2305            "%u: expected fType %04x, got %04x\n", i, item[i].type, mii.fType);
2306         ok(mii.wID == item[i].id,
2307            "%u: expected wID %04x, got %04x\n", i, item[i].id, mii.wID);
2308         if (item[i].type & (MF_BITMAP | MF_SEPARATOR))
2309         {
2310             /* For some reason Windows sets high word to not 0 for
2311              * not "magic" ids.
2312              */
2313             ok(LOWORD(mii.hbmpItem) == LOWORD(item[i].str),
2314                "%u: expected hbmpItem %p, got %p\n", i, item[i].str, mii.hbmpItem);
2315         }
2316         else
2317         {
2318             ok(mii.cch == strlen(item[i].str),
2319                "%u: expected cch %u, got %u\n", i, (UINT)strlen(item[i].str), mii.cch);
2320             ok(!strcmp((LPCSTR)mii.dwTypeData, item[i].str),
2321                "%u: expected dwTypeData %s, got %s\n", i, item[i].str, (LPCSTR)mii.dwTypeData);
2322         }
2323     }
2324 }
2325
2326 static void test_InsertMenu(void)
2327 {
2328     /* Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2329      * regardless of their id.
2330      */
2331     static const struct menu_data in1[] =
2332     {
2333         { MF_STRING, 1, "File" },
2334         { MF_BITMAP|MF_HELP, SC_CLOSE, MAKEINTRESOURCE(1) },
2335         { MF_STRING|MF_HELP, 2, "Help" }
2336     };
2337     static const struct menu_data out1[] =
2338     {
2339         { MF_STRING, 1, "File" },
2340         { MF_STRING|MF_HELP, 2, "Help" },
2341         { MF_BITMAP|MF_HELP, SC_CLOSE, MAKEINTRESOURCE(1) }
2342     };
2343     static const struct menu_data in2[] =
2344     {
2345         { MF_STRING, 1, "File" },
2346         { MF_BITMAP|MF_HELP, SC_CLOSE, MAKEINTRESOURCE(100) },
2347         { MF_STRING|MF_HELP, 2, "Help" }
2348     };
2349     static const struct menu_data out2[] =
2350     {
2351         { MF_STRING, 1, "File" },
2352         { MF_BITMAP|MF_HELP, SC_CLOSE, MAKEINTRESOURCE(100) },
2353         { MF_STRING|MF_HELP, 2, "Help" }
2354     };
2355     static const struct menu_data in3[] =
2356     {
2357         { MF_STRING, 1, "File" },
2358         { MF_SEPARATOR|MF_HELP, SC_CLOSE, MAKEINTRESOURCE(1) },
2359         { MF_STRING|MF_HELP, 2, "Help" }
2360     };
2361     static const struct menu_data out3[] =
2362     {
2363         { MF_STRING, 1, "File" },
2364         { MF_SEPARATOR|MF_HELP, SC_CLOSE, MAKEINTRESOURCE(0) },
2365         { MF_STRING|MF_HELP, 2, "Help" },
2366     };
2367     static const struct menu_data in4[] =
2368     {
2369         { MF_STRING, 1, "File" },
2370         { MF_BITMAP|MF_HELP, 1, MAKEINTRESOURCE(1) },
2371         { MF_STRING|MF_HELP, 2, "Help" }
2372     };
2373     static const struct menu_data out4[] =
2374     {
2375         { MF_STRING, 1, "File" },
2376         { MF_STRING|MF_HELP, 2, "Help" },
2377         { MF_BITMAP|MF_HELP, 1, MAKEINTRESOURCE(1) }
2378     };
2379     HMENU hmenu;
2380
2381 #define create_menu(a) create_menu_from_data((a), sizeof(a)/sizeof((a)[0]))
2382 #define compare_menu(h, a) compare_menu_data((h), (a), sizeof(a)/sizeof((a)[0]))
2383
2384     hmenu = create_menu(in1);
2385     compare_menu(hmenu, out1);
2386     DestroyMenu(hmenu);
2387
2388     hmenu = create_menu(in2);
2389     compare_menu(hmenu, out2);
2390     DestroyMenu(hmenu);
2391
2392     hmenu = create_menu(in3);
2393     compare_menu(hmenu, out3);
2394     DestroyMenu(hmenu);
2395
2396     hmenu = create_menu(in4);
2397     compare_menu(hmenu, out4);
2398     DestroyMenu(hmenu);
2399
2400 #undef create_menu
2401 #undef compare_menu
2402 }
2403
2404 START_TEST(menu)
2405 {
2406     init_function_pointers();
2407
2408     /* Wine defines MENUITEMINFO for W2K and above. NT4 and below can't
2409      * handle that.
2410      */
2411     if (correct_behavior())
2412     {
2413         test_menu_add_string();
2414         test_menu_iteminfo();
2415         test_menu_search_bycommand();
2416         test_CheckMenuRadioItem();
2417         test_menu_resource_layout();
2418         test_InsertMenu();
2419     }
2420
2421     register_menu_check_class();
2422
2423     test_menu_locked_by_window();
2424     test_menu_ownerdraw();
2425     test_menu_bmp_and_string();
2426
2427     if( !pSendInput)
2428         skip("SendInput is not available\n");
2429     else
2430         test_menu_input();
2431     test_menu_flags();
2432
2433     test_menu_hilitemenuitem();
2434 }