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