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