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