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