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