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