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