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