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