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