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