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