ReactOS  0.4.15-dev-1033-gd7d716a
menu.c
Go to the documentation of this file.
1 /*
2  * Unit tests for menus
3  *
4  * Copyright 2005 Robert Shearman
5  * Copyright 2007 Dmitry Timoshkov
6  *
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.
11  *
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.
16  *
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
20  */
21 
22 #include <stdarg.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <assert.h>
26 
27 #define OEMRESOURCE /* For OBM_MNARROW */
28 
29 #include "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 
34 #include "wine/test.h"
35 
37 
39 {
40  switch (msg)
41  {
42  case WM_ENTERMENULOOP:
43  /* mark window as having entered menu loop */
45  /* exit menu modal loop
46  * ( A SendMessage does not work on NT3.51 here ) */
47  return PostMessageA(hwnd, WM_CANCELMODE, 0, 0);
48  }
49  return DefWindowProcA(hwnd, msg, wparam, lparam);
50 }
51 
52 /* The MSVC headers ignore our NONAMELESSUNION requests so we have to define
53  * our own type */
54 typedef struct
55 {
56  DWORD type;
57  union
58  {
59  MOUSEINPUT mi;
60  KEYBDINPUT ki;
61  HARDWAREINPUT hi;
62  } u;
63 } TEST_INPUT;
64 
65 /* globals to communicate between test and wndproc */
66 
68 static INT popmenu;
69 static BOOL got_input;
70 static HMENU hMenus[4];
71 
72 #define MOD_SIZE 10
73 #define MOD_NRMENUS 8
74 
75  /* menu texts with their sizes */
76 static struct {
78  SIZE size; /* size of text up to any \t */
79  SIZE sc_size; /* size of the short-cut */
80 } MOD_txtsizes[] = {
81  { "Pinot &Noir" },
82  { "&Merlot\bF4" },
83  { "Shira&z\tAlt+S" },
84  { "" },
85  { NULL }
86 };
87 
88 static unsigned int MOD_maxid;
90 static int MOD_avec, MOD_hic;
91 static int MOD_odheight;
101 
102 /* wndproc used by test_menu_ownerdraw() */
105 {
106  static HMENU hmenupopup;
107  switch (msg)
108  {
109  case WM_INITMENUPOPUP:
111  hmenupopup = (HMENU) wparam;
112  break;
113  case WM_ENTERMENULOOP:
115  break;
116  case WM_INITMENU:
117  gflag_initmenu++;
118  break;
119  case WM_MEASUREITEM:
120  {
122  if (winetest_debug > 1)
123  trace("WM_MEASUREITEM received data %lx size %dx%d\n",
124  pmis->itemData, pmis->itemWidth, pmis->itemHeight);
125  ok( !wparam, "wrong wparam %lx\n", wparam );
126  ok( pmis->CtlType == ODT_MENU, "wrong type %x\n", pmis->CtlType );
127  MOD_odheight = pmis->itemHeight;
128  pmis->itemWidth = MODsizes[pmis->itemData].cx;
129  pmis->itemHeight = MODsizes[pmis->itemData].cy;
130  return TRUE;
131  }
132  case WM_DRAWITEM:
133  {
134  DRAWITEMSTRUCT * pdis;
135  TEXTMETRICA tm;
136  HPEN oldpen;
137  char chrs[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
138  SIZE sz;
139  int i;
140  pdis = (DRAWITEMSTRUCT *) lparam;
141  if (winetest_debug > 1) {
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 %s itemrc: %s\n",
145  hwnd, pdis->hwndItem, pdis->itemData, pdis->itemID,
147  oldpen=SelectObject( pdis->hDC, GetStockObject(
149  Rectangle( pdis->hDC, pdis->rcItem.left,pdis->rcItem.top,
150  pdis->rcItem.right,pdis->rcItem.bottom );
151  SelectObject( pdis->hDC, oldpen);
152  }
153  ok( !wparam, "wrong wparam %lx\n", wparam );
154  ok( pdis->CtlType == ODT_MENU, "wrong type %x\n", pdis->CtlType );
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};
161  if( ( p = strchr( buf, '\t'))) {
162  *p = '\0';
163  DrawTextA( pdis->hDC, p + 1, -1, &rc,
165  MOD_txtsizes[i].sc_size.cx= rc.right - rc.left;
166  MOD_txtsizes[i].sc_size.cy= rc.bottom - rc.top;
167  }
168  DrawTextA( pdis->hDC, buf, -1, &rc,
170  MOD_txtsizes[i].size.cx= rc.right - rc.left;
171  MOD_txtsizes[i].size.cy= rc.bottom - rc.top;
172  }
173 
174  if( pdis->itemData > MOD_maxid) return TRUE;
175  /* store the rectangle */
176  MOD_rc[pdis->itemData] = pdis->rcItem;
177  /* calculate average character width */
178  GetTextExtentPointA( pdis->hDC, chrs, 52, &sz );
179  MOD_avec = (sz.cx + 26)/52;
180  GetTextMetricsA( pdis->hDC, &tm);
181  MOD_hic = tm.tmHeight;
183  return TRUE;
184  }
185  case WM_ENTERIDLE:
186  {
187  gflag_enteridle++;
188  ok( lparam || broken(!lparam), /* win9x, nt4 */
189  "Menu window handle is NULL!\n");
190  if( lparam) {
192  ok( hmenupopup == hmenu, "MN_GETHMENU returns %p expected %p\n",
193  hmenu, hmenupopup);
194  }
196  return TRUE;
197  }
198  case WM_MENUSELECT:
201  break;
202  }
203  return DefWindowProcA(hwnd, msg, wparam, lparam);
204 }
205 
206 static void register_menu_check_class(void)
207 {
208  WNDCLASSA wc =
209  {
210  0,
212  0,
213  0,
215  NULL,
217  (HBRUSH)(COLOR_BTNFACE+1),
218  NULL,
219  "WineMenuCheck",
220  };
221 
223 }
224 
225 static void test_getmenubarinfo(void)
226 {
227  BOOL ret;
228  HMENU hmenu;
229  MENUBARINFO mbi;
230  RECT rcw, rci;
231  HWND hwnd;
232  INT err;
233 
234  mbi.cbSize = sizeof(MENUBARINFO);
235 
238  NULL, NULL, NULL, NULL);
239  ok(hwnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
240 
241  /* no menu: getmenubarinfo should fail */
242  SetLastError(0xdeadbeef);
243  ret = GetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi);
244  err = GetLastError();
245  ok(ret == FALSE, "GetMenuBarInfo should not have been successful\n");
246  ok(err == 0xdeadbeef, "err = %d\n", err);
247 
248  /* create menubar, no items yet */
249  hmenu = CreateMenu();
250  ok(hmenu != NULL, "CreateMenu failed with error %d\n", GetLastError());
251 
252  ret = SetMenu(hwnd, hmenu);
253  ok(ret, "SetMenu failed with error %d\n", GetLastError());
254 
255  SetLastError(0xdeadbeef);
256  ret = GetMenuBarInfo(NULL, OBJID_CLIENT, 0, &mbi);
257  err = GetLastError();
258  ok(!ret, "GetMenuBarInfo succeeded\n");
259  ok(err == ERROR_INVALID_WINDOW_HANDLE, "err = %d\n", err);
260 
261  SetLastError(0xdeadbeef);
262  ret = GetMenuBarInfo(hwnd, OBJID_CLIENT, 0, &mbi);
263  err = GetLastError();
264  ok(!ret, "GetMenuBarInfo succeeded\n");
265  ok(err==ERROR_INVALID_MENU_HANDLE || broken(err==0xdeadbeef) /* NT, W2K, XP */, "err = %d\n", err);
266 
267  SetLastError(0xdeadbeef);
268  ret = GetMenuBarInfo(hwnd, OBJID_MENU, -1, &mbi);
269  err = GetLastError();
270  ok(ret == FALSE, "GetMenuBarInfo should have failed\n");
271  ok(err == 0xdeadbeef, "err = %d\n", err);
272 
273  mbi.cbSize = 1000;
274  SetLastError(0xdeadbeef);
275  ret = GetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi);
276  err = GetLastError();
277  ok(ret == FALSE, "GetMenuBarInfo should have failed\n");
278  ok(err == ERROR_INVALID_PARAMETER, "err = %d\n", err);
279  mbi.cbSize = sizeof(MENUBARINFO);
280 
281  SetLastError(0xdeadbeef);
282  ret = GetMenuBarInfo(hwnd, 123, 0, &mbi);
283  err = GetLastError();
284  ok(ret == FALSE, "GetMenuBarInfo should have failed\n");
285  ok(err == 0xdeadbeef, "err = %d\n", err);
286 
287  ret = GetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi);
288  ok(ret, "GetMenuBarInfo failed with error %d\n", GetLastError());
289 
290  ok(mbi.rcBar.left == 0 && mbi.rcBar.top == 0 && mbi.rcBar.bottom == 0 && mbi.rcBar.right == 0,
291  "rcBar: Expected (0,0)-(0,0), got: %s\n", wine_dbgstr_rect(&mbi.rcBar));
292  ok(mbi.hMenu == hmenu, "hMenu: Got %p instead of %p\n",
293  mbi.hMenu, hmenu);
294  ok(mbi.fBarFocused == 0, "fBarFocused: Got %d instead of 0.\n", mbi.fBarFocused);
295  ok(mbi.fFocused == 0, "fFocused: Got %d instead of 0.\n", mbi.fFocused);
296 
297  /* add some items */
298  ret = AppendMenuA(hmenu, MF_STRING , 100, "item 1");
299  ok(ret, "AppendMenu failed.\n");
300  ret = AppendMenuA(hmenu, MF_STRING , 101, "item 2");
301  ok(ret, "AppendMenu failed.\n");
302  ret = SetMenu(hwnd, hmenu);
303  ok(ret, "SetMenu failed with error %d\n", GetLastError());
304 
305  SetLastError(0xdeadbeef);
306  ret = GetMenuBarInfo(hwnd, OBJID_MENU, 200, &mbi);
307  err = GetLastError();
308  ok(ret == FALSE, "GetMenuBarInfo should have failed\n");
309  ok(err == 0xdeadbeef, "err = %d\n", err);
310 
311  /* get info for the whole menu */
312  ret = GetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi);
313  ok(ret, "GetMenuBarInfo failed with error %d\n", GetLastError());
314 
315  /* calculate menu rectangle, from window rectangle and the position of the first item */
316  ret = GetWindowRect(hwnd, &rcw);
317  ok(ret, "GetWindowRect failed.\n");
318  ret = GetMenuItemRect(hwnd, hmenu, 0, &rci);
319  ok(ret, "GetMenuItemRect failed.\n");
320  todo_wine ok(mbi.rcBar.left == rci.left && mbi.rcBar.top == rci.top &&
321  mbi.rcBar.bottom == rci.bottom && mbi.rcBar.right == rcw.right - rci.left + rcw.left,
322  "rcBar: Got %s instead of (%d,%d)-(%d,%d)\n", wine_dbgstr_rect(&mbi.rcBar),
323  rci.left, rci.top, rcw.right - rci.left + rcw.left, rci.bottom);
324  ok(mbi.hMenu == hmenu, "hMenu: Got %p instead of %p\n", mbi.hMenu, hmenu);
325  ok(mbi.fBarFocused == 0, "fBarFocused: got %d instead of 0\n", mbi.fBarFocused);
326  ok(mbi.fFocused == 0, "fFocused: got %d instead of 0\n", mbi.fFocused);
327 
328  /* get info for item nr.2 */
329  ret = GetMenuBarInfo(hwnd, OBJID_MENU, 2, &mbi);
330  ok(ret, "GetMenuBarInfo failed with error %d\n", GetLastError());
331  ret = GetMenuItemRect(hwnd, hmenu, 1, &rci);
332  ok(ret, "GetMenuItemRect failed.\n");
333  ok(EqualRect(&mbi.rcBar, &rci), "rcBar: Got %s instead of %s\n", wine_dbgstr_rect(&mbi.rcBar),
334  wine_dbgstr_rect(&rci));
335  ok(mbi.hMenu == hmenu, "hMenu: Got %p instead of %p\n", mbi.hMenu, hmenu);
336  ok(mbi.fBarFocused == 0, "fBarFocused: got %d instead of 0\n", mbi.fBarFocused);
337  ok(mbi.fFocused == 0, "fFocused: got %d instead of 0\n", mbi.fFocused);
338 
340 }
341 
342 static void test_GetMenuItemRect(void)
343 {
344  HWND hwnd;
345  HMENU hmenu;
346  HMENU popup_hmenu;
347  RECT window_rect;
348  RECT item_rect;
349  POINT client_top_left;
350  INT caption_height;
351  BOOL ret;
352 
354  NULL, NULL, NULL);
355  ok(hwnd != NULL, "CreateWindow failed with error %d\n", GetLastError());
356  hmenu = CreateMenu();
357  ok(hmenu != NULL, "CreateMenu failed with error %d\n", GetLastError());
358  popup_hmenu = CreatePopupMenu();
359  ok(popup_hmenu != NULL, "CreatePopupMenu failed with error %d\n", GetLastError());
360  ret = AppendMenuA(popup_hmenu, MF_STRING, 0, "Popup");
361  ok(ret, "AppendMenu failed with error %d\n", GetLastError());
362  ret = AppendMenuA(hmenu, MF_STRING | MF_POPUP, (UINT_PTR)popup_hmenu, "Menu");
363  ok(ret, "AppendMenu failed with error %d\n", GetLastError());
364  ret = SetMenu(hwnd, hmenu);
365  ok(ret, "SetMenu failed with error %d\n", GetLastError());
366 
367  /* Get the menu item rectangle of the displayed sysmenu item */
368  ret = GetMenuItemRect(hwnd, hmenu, 0, &item_rect);
369  ok(ret, "GetMenuItemRect failed with error %d\n", GetLastError());
370  GetWindowRect(hwnd, &window_rect);
371  /* Get the screen coordinate of the left top corner of the client rectangle */
372  client_top_left.x = 0;
373  client_top_left.y = 0;
374  MapWindowPoints(hwnd, 0, &client_top_left, 1);
376 
377  ok(item_rect.left == client_top_left.x, "Expect item_rect.left %d == %d\n", item_rect.left, client_top_left.x);
378  ok(item_rect.right <= window_rect.right, "Expect item_rect.right %d <= %d\n", item_rect.right, window_rect.right);
379  /* A gap of 1 pixel is added deliberately in commit 75f9e64, so using equal operator would fail on Wine.
380  * Check that top and bottom are correct with 1 pixel margin tolerance */
381  ok(item_rect.top - (window_rect.top + caption_height) <= 1, "Expect item_rect.top %d - %d <= 1\n", item_rect.top,
382  window_rect.top + caption_height);
383  ok(item_rect.bottom - (client_top_left.y - 1) <= 1, "Expect item_rect.bottom %d - %d <= 1\n", item_rect.bottom,
384  client_top_left.y - 1);
385 
386  /* Get the item rectangle of the not yet displayed popup menu item. */
387  ret = GetMenuItemRect(hwnd, popup_hmenu, 0, &item_rect);
388  ok(ret, "GetMenuItemRect failed with error %d\n", GetLastError());
389  ok(item_rect.left == client_top_left.x, "Expect item_rect.left %d == %d\n", item_rect.left, client_top_left.x);
390  ok(item_rect.right == client_top_left.x, "Expect item_rect.right %d == %d\n", item_rect.right, client_top_left.x);
391  ok(item_rect.top == client_top_left.y, "Expect item_rect.top %d == %d\n", item_rect.top, client_top_left.y);
392  ok(item_rect.bottom == client_top_left.y, "Expect item_rect.bottom %d == %d\n", item_rect.bottom,
393  client_top_left.y);
394 
396 }
397 
398 static void test_system_menu(void)
399 {
400  WCHAR testW[] = {'t','e','s','t',0};
401  BOOL ret;
402  HMENU menu;
403  HWND hwnd;
406  char buffer[80];
407  char found[0x200];
408  int i, res;
409 
412  NULL, NULL, NULL, NULL);
413  ok(hwnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
414  menu = GetSystemMenu( hwnd, FALSE );
415  ok( menu != NULL, "no system menu\n" );
416 
417  for (i = 0xf000; i < 0xf200; i++)
418  {
419  memset( &info, 0xcc, sizeof(info) );
420  info.cbSize = sizeof(info);
421  info.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
422  info.dwTypeData = buffer;
423  info.cch = sizeof( buffer );
424  ret = GetMenuItemInfoA( menu, i, FALSE, &info );
425  if (ret) trace( "found %x: '%s'\n", i, buffer );
426  switch (i)
427  {
428  case SC_RESTORE:
429  case SC_SIZE:
430  case SC_MOVE:
431  case SC_MINIMIZE:
432  case SC_MAXIMIZE:
433  case SC_CLOSE:
434  ok( ret, "%x menu item not found\n", i );
435  break;
436  case SC_SCREENSAVE+1: /* used for the 'About Wine' entry, don't test */
437  break;
438  default:
439  ok( !ret, "%x menu item found\n", i );
440  break;
441  }
442  found[i - 0xf000] = ret;
443  }
444 
445  for (i = 0xf000; i < 0xf200; i++)
446  {
447  res = CheckMenuItem( menu, i, 0 );
448  if (res == -1) ok( !found[i - 0xf000], "could not check existent item %x\n", i );
449  else ok( found[i - 0xf000], "could check non-existent item %x\n", i );
450 
451  res = EnableMenuItem( menu, i, 0 );
452  if (res == -1) ok( !found[i - 0xf000], "could not enable existent item %x\n", i );
453  else ok( found[i - 0xf000], "could enable non-existent item %x\n", i );
454 
455  res = GetMenuState( menu, i, 0 );
456  if (res == -1) ok( !found[i - 0xf000], "could not get state existent item %x\n", i );
457  else ok( found[i - 0xf000], "could get state of non-existent item %x\n", i );
458 
459  if (!found[i - 0xf000]) /* don't remove the existing ones */
460  {
461  ret = RemoveMenu( menu, i, 0 );
462  ok( !ret, "could remove non-existent item %x\n", i );
463  }
464 
465  ret = ModifyMenuA( menu, i, 0, i, "test" );
466  if (i == SC_TASKLIST) ok( ret, "failed to modify SC_TASKLIST\n" );
467  else if (!ret) ok( !found[i - 0xf000], "could not modify existent item %x\n", i );
468  else ok( found[i - 0xf000], "could modify non-existent item %x\n", i );
469 
470  ret = ModifyMenuW( menu, i, 0, i, testW );
471  if (i == SC_TASKLIST) ok( ret, "failed to modify SC_TASKLIST\n" );
472  else if (!ret) ok( !found[i - 0xf000], "could not modify existent item %x\n", i );
473  else ok( found[i - 0xf000], "could modify non-existent item %x\n", i );
474 
475  ret = ModifyMenuA( menu, i, MF_BYPOSITION, i, "test" );
476  ok( !ret, "could modify non-existent item %x\n", i );
477 
478  strcpy( buffer, "test" );
479  memset( &info, 0xcc, sizeof(info) );
480  info.cbSize = sizeof(info);
481  info.fMask = MIIM_STRING | MIIM_ID;
482  info.wID = i;
483  info.dwTypeData = buffer;
484  info.cch = strlen( buffer );
485  ret = SetMenuItemInfoA( menu, i, FALSE, &info );
486  if (i == SC_TASKLIST) ok( ret, "failed to set SC_TASKLIST\n" );
487  else if (!ret) ok( !found[i - 0xf000], "could not set existent item %x\n", i );
488  else ok( found[i - 0xf000], "could set non-existent item %x\n", i );
489  ret = SetMenuItemInfoA( menu, i, TRUE, &info );
490  ok( !ret, "could modify non-existent item %x\n", i );
491 
492  memset( &infoW, 0xcc, sizeof(infoW) );
493  infoW.cbSize = sizeof(infoW);
494  infoW.fMask = MIIM_STRING | MIIM_ID;
495  infoW.wID = i;
496  infoW.dwTypeData = testW;
497  infoW.cch = lstrlenW( testW );
498  ret = SetMenuItemInfoW( menu, i, FALSE, &infoW );
499  if (i == SC_TASKLIST) ok( ret, "failed to set SC_TASKLIST\n" );
500  else if (!ret) ok( !found[i - 0xf000], "could not set existent item %x\n", i );
501  else ok( found[i - 0xf000], "could set non-existent item %x\n", i );
502  ret = SetMenuItemInfoW( menu, i, TRUE, &infoW );
503  ok( !ret, "could modify non-existent item %x\n", i );
504  }
505 
506  /* confirm that SC_TASKLIST still does not exist */
507  for (i = 0xf000; i < 0xf200; i++)
508  {
509  memset( &info, 0xcc, sizeof(info) );
510  info.cbSize = sizeof(info);
511  info.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
512  info.dwTypeData = buffer;
513  info.cch = sizeof( buffer );
514  ret = GetMenuItemInfoA( menu, i, FALSE, &info );
515  switch (i)
516  {
517  case SC_RESTORE:
518  case SC_SIZE:
519  case SC_MOVE:
520  case SC_MINIMIZE:
521  case SC_MAXIMIZE:
522  case SC_CLOSE:
523  ok( ret, "%x menu item not found\n", i );
524  break;
525  case SC_SCREENSAVE+1: /* used for the 'About Wine' entry, don't test */
526  break;
527  default:
528  ok( !ret, "%x menu item found\n", i );
529  break;
530  }
531  }
532 
533  /* now a normal (non-system) menu */
534 
535  menu = CreateMenu();
536  ok( menu != NULL, "CreateMenu failed with error %d\n", GetLastError() );
537 
538  res = CheckMenuItem( menu, SC_TASKLIST, 0 );
539  ok( res == -1, "CheckMenuItem succeeded\n" );
540  res = EnableMenuItem( menu, SC_TASKLIST, 0 );
541  ok( res == -1, "EnableMenuItem succeeded\n" );
542  res = GetMenuState( menu, SC_TASKLIST, 0 );
543  ok( res == -1, "GetMenuState succeeded\n" );
544  ret = RemoveMenu( menu, SC_TASKLIST, 0 );
545  ok( !ret, "RemoveMenu succeeded\n" );
546  ret = ModifyMenuA( menu, SC_TASKLIST, 0, SC_TASKLIST, "test" );
547  ok( ret, "ModifyMenuA failed err %d\n", GetLastError() );
548  ret = ModifyMenuW( menu, SC_TASKLIST, 0, SC_TASKLIST, testW );
549  ok( ret, "ModifyMenuW failed err %d\n", GetLastError() );
550  ret = ModifyMenuA( menu, SC_TASKLIST-1, 0, SC_TASKLIST, "test" );
551  ok( !ret, "ModifyMenu succeeded on SC_TASKLIST-1\n" );
552  strcpy( buffer, "test" );
553  memset( &info, 0xcc, sizeof(info) );
554  info.cbSize = sizeof(info);
555  info.fMask = MIIM_STRING | MIIM_ID;
556  info.wID = SC_TASKLIST;
557  info.dwTypeData = buffer;
558  info.cch = strlen( buffer );
559  ret = SetMenuItemInfoA( menu, SC_TASKLIST, FALSE, &info );
560  ok( ret, "failed to set SC_TASKLIST\n" );
561  ret = SetMenuItemInfoA( menu, SC_TASKLIST+1, FALSE, &info );
562  ok( !ret, "succeeded setting SC_TASKLIST+1\n" );
563  ret = SetMenuItemInfoA( menu, SC_TASKLIST, TRUE, &info );
564  ok( !ret, "succeeded setting by position\n" );
565 
566  memset( &infoW, 0xcc, sizeof(infoW) );
567  infoW.cbSize = sizeof(infoW);
568  infoW.fMask = MIIM_STRING | MIIM_ID;
569  infoW.wID = SC_TASKLIST;
570  infoW.dwTypeData = testW;
571  infoW.cch = lstrlenW( testW );
573  ok( ret, "failed to set SC_TASKLIST\n" );
574  ret = SetMenuItemInfoW( menu, SC_TASKLIST+1, FALSE, &infoW );
575  ok( !ret, "succeeded setting SC_TASKLIST+1\n" );
576  ret = SetMenuItemInfoW( menu, SC_TASKLIST, TRUE, &infoW );
577  ok( !ret, "succeeded setting by position\n" );
578 
579  DestroyMenu( menu );
580  DestroyWindow( hwnd );
581 }
582 
583 /* demonstrates that windows locks the menu object so that it is still valid
584  * even after a client calls DestroyMenu on it */
585 static void test_menu_locked_by_window(void)
586 {
587  BOOL ret;
588  HMENU hmenu;
591  NULL, NULL, NULL, NULL);
592  ok(hwnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
593  hmenu = CreateMenu();
594  ok(hmenu != NULL, "CreateMenu failed with error %d\n", GetLastError());
595  ret = InsertMenuA(hmenu, 0, MF_STRING, 0, "&Test");
596  ok(ret, "InsertMenu failed with error %d\n", GetLastError());
597  ret = SetMenu(hwnd, hmenu);
598  ok(ret, "SetMenu failed with error %d\n", GetLastError());
599  ret = DestroyMenu(hmenu);
600  ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
601 
602  ret = DrawMenuBar(hwnd);
603  ok(ret, "DrawMenuBar failed with error %d\n", GetLastError());
604  ret = IsMenu(GetMenu(hwnd));
605  ok(!ret || broken(ret) /* nt4 */, "Menu handle should have been destroyed\n");
606 
608  /* did we process the WM_INITMENU message? */
610  todo_wine {
611  ok(ret, "WM_INITMENU should have been sent\n");
612  }
613 
615 }
616 
617 /* demonstrates that subpopup's are locked
618  * even after a client calls DestroyMenu on it */
620 {
621  HWND hwndmenu;
622  switch (msg)
623  {
624  case WM_ENTERIDLE:
625  hwndmenu = GetCapture();
626  if( hwndmenu) {
627  PostMessageA( hwndmenu, WM_KEYDOWN, VK_DOWN, 0);
628  PostMessageA( hwndmenu, WM_KEYDOWN, VK_RIGHT, 0);
629  PostMessageA( hwndmenu, WM_KEYDOWN, VK_RETURN, 0);
630  }
631  }
632  return DefWindowProcA(hwnd, msg, wparam, lparam);
633 }
634 
636 {
637  BOOL ret;
638  HMENU hmenu, hsubmenu;
639  MENUINFO mi = { sizeof( MENUINFO)};
640  MENUITEMINFOA mii = { sizeof( MENUITEMINFOA)};
641  HWND hwnd;
642  const int itemid = 0x1234567;
643 
644  /* create window, popupmenu with one subpopup */
647  NULL, NULL, NULL, NULL);
648  ok(hwnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
651  ok(hmenu != NULL, "CreateMenu failed with error %d\n", GetLastError());
652  hsubmenu = CreatePopupMenu();
653  ok(hsubmenu != NULL, "CreateMenu failed with error %d\n", GetLastError());
655  "PopUpLockTest");
656  ok(ret, "InsertMenu failed with error %d\n", GetLastError());
657  ret = InsertMenuA(hsubmenu, 0, MF_BYPOSITION | MF_STRING, itemid, "PopUpMenu");
658  ok(ret, "InsertMenu failed with error %d\n", GetLastError());
659  /* first some tests that all this functions properly */
660  mii.fMask = MIIM_SUBMENU;
661  ret = GetMenuItemInfoA( hmenu, 0, TRUE, &mii);
662  ok( ret, "GetMenuItemInfo failed error %d\n", GetLastError());
663  ok( mii.hSubMenu == hsubmenu, "submenu is %p\n", mii.hSubMenu);
664  mi.fMask |= MIM_STYLE;
665  ret = GetMenuInfo( hsubmenu, &mi);
666  ok( ret , "GetMenuInfo returned 0 with error %d\n", GetLastError());
667  ret = IsMenu( hsubmenu);
668  ok( ret , "Menu handle is not valid\n");
669 
670  ret = TrackPopupMenu( hmenu, TPM_RETURNCMD, 100,100, 0, hwnd, NULL);
671  ok( ret == itemid , "TrackPopupMenu returned %d error is %d\n", ret, GetLastError());
672 
673  /* then destroy the sub-popup */
674  ret = DestroyMenu( hsubmenu);
675  ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
676  /* and repeat the tests */
677  mii.fMask = MIIM_SUBMENU;
678  ret = GetMenuItemInfoA( hmenu, 0, TRUE, &mii);
679  ok( ret, "GetMenuItemInfo failed error %d\n", GetLastError());
680  /* GetMenuInfo fails now */
681  ok( mii.hSubMenu == hsubmenu, "submenu is %p\n", mii.hSubMenu);
682  mi.fMask |= MIM_STYLE;
683  ret = GetMenuInfo( hsubmenu, &mi);
684  ok( !ret , "GetMenuInfo should have failed\n");
685  /* IsMenu says it is not */
686  ret = IsMenu( hsubmenu);
687  ok( !ret , "Menu handle should be invalid\n");
688 
689  /* but TrackPopupMenu still works! */
690  ret = TrackPopupMenu( hmenu, TPM_RETURNCMD, 100,100, 0, hwnd, NULL);
691  todo_wine {
692  ok( ret == itemid , "TrackPopupMenu returned %d error is %d\n", ret, GetLastError());
693  }
694 
695  /* clean up */
696  DestroyMenu( hmenu);
698 }
699 
700 static void test_menu_ownerdraw(void)
701 {
702  int i,j,k;
703  BOOL ret;
704  HMENU hmenu;
705  MENUITEMINFOA mii;
706  LONG leftcol;
709  NULL, NULL, NULL, NULL);
710  ok(hwnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
711  if( !hwnd) return;
714  ok(hmenu != NULL, "CreateMenu failed with error %d\n", GetLastError());
715  if( !hmenu) { DestroyWindow(hwnd);return;}
716  k=0;
717  for( j=0;j<2;j++) /* create columns */
718  for(i=0;i<2;i++) { /* create rows */
720  (i==0 ? MF_MENUBREAK : 0), k, (LPCSTR)MAKEINTRESOURCE(k));
721  k++;
722  ok( ret, "AppendMenu failed for %d\n", k-1);
723  }
724  MOD_maxid = k-1;
725  assert( k <= ARRAY_SIZE(MOD_rc));
726  /* display the menu */
727  TrackPopupMenu( hmenu, TPM_RETURNCMD, 100,100, 0, hwnd, NULL);
728 
729  /* columns have a 4 pixel gap between them */
730  ok( MOD_rc[0].right + 4 == MOD_rc[2].left,
731  "item rectangles are not separated by 4 pixels space\n");
732  /* height should be what the MEASUREITEM message has returned */
733  ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE,
734  "menu item has wrong height: %d should be %d\n",
735  MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE);
736  /* no gaps between the rows */
737  ok( MOD_rc[0].bottom - MOD_rc[1].top == 0,
738  "There should not be a space between the rows, gap is %d\n",
739  MOD_rc[0].bottom - MOD_rc[1].top);
740  /* test the correct value of the item height that was sent
741  * by the WM_MEASUREITEM message */
742  ok( MOD_odheight == HIWORD( GetDialogBaseUnits()) || /* WinNT,2k,XP */
743  MOD_odheight == MOD_hic, /* Win95,98,ME */
744  "Wrong height field in MEASUREITEMSTRUCT, expected %d or %d actual %d\n",
746  /* test what MF_MENUBREAK did at the first position. Also show
747  * that an MF_SEPARATOR is ignored in the height calculation. */
748  leftcol= MOD_rc[0].left;
750  /* display the menu */
751  TrackPopupMenu( hmenu, TPM_RETURNCMD, 100,100, 0, hwnd, NULL);
752  /* left should be 4 pixels less now */
753  ok( leftcol == MOD_rc[0].left + 4,
754  "columns should be 4 pixels to the left (actual %d).\n",
755  leftcol - MOD_rc[0].left);
756  /* test width */
757  ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE,
758  "width of owner drawn menu item is wrong. Got %d expected %d\n",
759  MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE);
760  /* and height */
761  ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE,
762  "Height is incorrect. Got %d expected %d\n",
763  MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE);
764 
765  /* test owner-drawn callback bitmap */
767  mii.cbSize = sizeof(mii);
769  if (GetMenuItemInfoA( hmenu, 1, TRUE, &mii ))
770  {
771  ok( mii.fType == MFT_BITMAP, "wrong type %x\n", mii.fType );
772  ok( mii.wID == 1, "wrong id %x\n", mii.wID );
773  ok( mii.hbmpItem == HBMMENU_CALLBACK, "wrong data %p\n", mii.hbmpItem );
774  }
775  TrackPopupMenu( hmenu, TPM_RETURNCMD, 100,100, 0, hwnd, NULL);
776 
777  /* test width/height of an ownerdraw menu bar as well */
778  ret = DestroyMenu(hmenu);
779  ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
780  hmenu = CreateMenu();
781  ok(hmenu != NULL, "CreateMenu failed with error %d\n", GetLastError());
782  if( !hmenu) { DestroyWindow(hwnd);return;}
783  MOD_maxid=1;
784  for(i=0;i<2;i++) {
785  ret = AppendMenuA( hmenu, MF_OWNERDRAW, i, 0 );
786  ok( ret, "AppendMenu failed for %d\n", i);
787  }
788  ret = SetMenu( hwnd, hmenu);
789  UpdateWindow( hwnd); /* hack for wine to draw the window + menu */
790  ok(ret, "SetMenu failed with error %d\n", GetLastError());
791  /* test width */
792  ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE,
793  "width of owner drawn menu item is wrong. Got %d expected %d\n",
794  MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE);
795  /* test height */
796  ok( MOD_rc[0].bottom - MOD_rc[0].top == GetSystemMetrics( SM_CYMENU) - 1,
797  "Height of owner drawn menu item is wrong. Got %d expected %d\n",
799 
800  /* clean up */
801  ret = DestroyMenu(hmenu);
802  ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
804 }
805 
806 /* helper for test_menu_bmp_and_string() */
807 static void test_mbs_help( int ispop, int hassub, int mnuopt,
808  HWND hwnd, int arrowwidth, int count, HBITMAP hbmp,
809  SIZE bmpsize, LPCSTR text, SIZE size, SIZE sc_size)
810 {
811  BOOL ret;
812  HMENU hmenu, submenu;
813  MENUITEMINFOA mii={ sizeof( MENUITEMINFOA )};
814  MENUINFO mi;
815  RECT rc;
816  CHAR text_copy[16];
817  int hastab, expect;
818  BOOL failed = FALSE;
819 
822  mii.fType = 0;
823  /* check the menu item unless MNS_CHECKORBMP is set */
824  mii.fState = (mnuopt != 2 ? MFS_CHECKED : MFS_UNCHECKED);
825  mii.dwItemData =0;
826  MODsizes[0] = bmpsize;
827  hastab = 0;
828  if( text ) {
829  char *p;
830  mii.fMask |= MIIM_STRING;
831  strcpy(text_copy, text);
832  mii.dwTypeData = text_copy; /* structure member declared non-const */
833  if( ( p = strchr( text, '\t'))) {
834  hastab = *(p + 1) ? 2 : 1;
835  }
836  }
837  /* tabs don't make sense in menubars */
838  if(hastab && !ispop) return;
839  if( hbmp) {
840  mii.fMask |= MIIM_BITMAP;
841  mii.hbmpItem = hbmp;
842  }
843  submenu = CreateMenu();
844  ok( submenu != 0, "CreateMenu failed with error %d\n", GetLastError());
845  if( ispop)
847  else
848  hmenu = CreateMenu();
849  ok( hmenu != 0, "Create{Popup}Menu failed with error %d\n", GetLastError());
850  if( hassub) {
851  mii.fMask |= MIIM_SUBMENU;
852  mii.hSubMenu = submenu;
853  }
854  if( mnuopt) {
855  mi.cbSize = sizeof(mi);
856  mi.fMask = MIM_STYLE;
857  GetMenuInfo( hmenu, &mi);
858  if( mnuopt) mi.dwStyle |= mnuopt == 1 ? MNS_NOCHECK : MNS_CHECKORBMP;
859  ret = SetMenuInfo( hmenu, &mi);
860  ok( ret, "SetMenuInfo failed with error %d\n", GetLastError());
861  }
862  ret = InsertMenuItemA( hmenu, 0, FALSE, &mii);
863  ok( ret, "InsertMenuItem failed with error %d\n", GetLastError());
864  failed = !ret;
865  if( winetest_debug) {
866  HDC hdc=GetDC(hwnd);
867  RECT rc = {100, 50, 400, 70};
868  char buf[100];
869 
870  sprintf( buf,"%d text \"%s\" mnuopt %d", count, text ? text: "(nil)", mnuopt);
871  FillRect( hdc, &rc, (HBRUSH) COLOR_WINDOW);
872  TextOutA( hdc, 10, 50, buf, strlen( buf));
873  ReleaseDC( hwnd, hdc);
874  }
875  if(ispop)
876  TrackPopupMenu( hmenu, TPM_RETURNCMD, 100,100, 0, hwnd, NULL);
877  else {
878  ret = SetMenu( hwnd, hmenu);
879  ok(ret, "SetMenu failed with error %d\n", GetLastError());
880  DrawMenuBar( hwnd);
881  }
882  ret = GetMenuItemRect( hwnd, hmenu, 0, &rc);
883  ok(ret, "GetMenuItemRect failed with error %d\n", GetLastError());
884 
885  if (0) /* comment out menu size checks, behavior is different in almost every Windows version */
886  /* the tests should however succeed on win2000, XP and Wine (at least up to 1.1.15) */
887  /* with a variety of dpis and desktop font sizes */
888  {
889  /* check menu width */
890  if( ispop)
891  expect = ( text || hbmp ?
892  4 + (mnuopt != 1 ? GetSystemMetrics(SM_CXMENUCHECK) : 0)
893  : 0) +
894  arrowwidth + MOD_avec + (hbmp ?
895  ((INT_PTR)hbmp<0||(INT_PTR)hbmp>12 ? bmpsize.cx + 2 : GetSystemMetrics( SM_CXMENUSIZE) + 2)
896  : 0) +
897  (text && hastab ? /* TAB space */
898  MOD_avec + ( hastab==2 ? sc_size.cx : 0) : 0) +
899  (text ? 2 + (text[0] ? size.cx :0): 0) ;
900  else
901  expect = !(text || hbmp) ? 0 :
902  ( hbmp ? (text ? 2:0) + bmpsize.cx : 0 ) +
903  (text ? 2 * MOD_avec + (text[0] ? size.cx :0): 0) ;
904  ok( rc.right - rc.left == expect,
905  "menu width wrong, got %d expected %d\n", rc.right - rc.left, expect);
906  failed = failed || !(rc.right - rc.left == expect);
907  /* check menu height */
908  if( ispop)
909  expect = max( ( !(text || hbmp) ? GetSystemMetrics( SM_CYMENUSIZE)/2 : 0),
910  max( (text ? max( 2 + size.cy, MOD_hic + 4) : 0),
911  (hbmp ?
912  ((INT_PTR)hbmp<0||(INT_PTR)hbmp>12 ?
913  bmpsize.cy + 2
915  : 0)));
916  else
917  expect = ( !(text || hbmp) ? GetSystemMetrics( SM_CYMENUSIZE)/2 :
918  max( GetSystemMetrics( SM_CYMENU) - 1, (hbmp ? bmpsize.cy : 0)));
919  ok( rc.bottom - rc.top == expect,
920  "menu height wrong, got %d expected %d (%d)\n",
922  failed = failed || !(rc.bottom - rc.top == expect);
924  /* check the position of the bitmap */
925  /* horizontal */
926  if (!ispop)
927  expect = 3;
928  else if (mnuopt == 0)
930  else if (mnuopt == 1)
931  expect = 4;
932  else /* mnuopt == 2 */
933  expect = 2;
934  ok( expect == MOD_rc[0].left,
935  "bitmap left is %d expected %d\n", MOD_rc[0].left, expect);
936  failed = failed || !(expect == MOD_rc[0].left);
937  /* vertical */
938  expect = (rc.bottom - rc.top - MOD_rc[0].bottom + MOD_rc[0].top) / 2;
939  ok( expect == MOD_rc[0].top,
940  "bitmap top is %d expected %d\n", MOD_rc[0].top, expect);
941  failed = failed || !(expect == MOD_rc[0].top);
942  }
943  }
944  /* if there was a failure, report details */
945  if( failed) {
946  trace("*** count %d %s text \"%s\" bitmap %p bmsize %d,%d textsize %d+%d,%d mnuopt %d hastab %d\n",
947  count, (ispop? "POPUP": "MENUBAR"),text ? text: "(nil)", hbmp, bmpsize.cx, bmpsize.cy,
948  size.cx, size.cy, sc_size.cx, mnuopt, hastab);
949  trace(" check %d,%d arrow %d avechar %d\n",
952  if( hbmp == HBMMENU_CALLBACK)
953  trace( " rc %s bmp.rc %s\n", wine_dbgstr_rect(&rc), wine_dbgstr_rect(&MOD_rc[0]));
954  }
955  /* clean up */
956  ret = DestroyMenu(submenu);
957  ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
958  ret = DestroyMenu(hmenu);
959  ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
960 }
961 
962 
963 static void test_menu_bmp_and_string(void)
964 {
965  BYTE bmfill[300];
966  HBITMAP hbm_arrow;
967  BITMAP bm;
968  INT arrowwidth;
969  HWND hwnd;
970  HMENU hsysmenu;
971  MENUINFO mi= {sizeof(MENUINFO)};
972  MENUITEMINFOA mii= {sizeof(MENUITEMINFOA)};
973  int count, szidx, txtidx, bmpidx, hassub, mnuopt, ispop;
974  BOOL got;
975 
976  memset( bmfill, 0xcc, sizeof( bmfill));
979  NULL, NULL, NULL, NULL);
980  hbm_arrow = LoadBitmapA( 0, (LPCSTR)OBM_MNARROW);
981  GetObjectA( hbm_arrow, sizeof(bm), &bm);
982  arrowwidth = bm.bmWidth;
983  ok(hwnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
984  if( !hwnd) return;
985  /* test system menu */
986  hsysmenu = GetSystemMenu( hwnd, FALSE);
987  ok( hsysmenu != NULL, "GetSystemMenu failed with error %d\n", GetLastError());
988  mi.fMask = MIM_STYLE;
989  mi.dwStyle = 0;
990  got = GetMenuInfo( hsysmenu, &mi);
991  ok( got, "GetMenuInfo failed gle=%d\n", GetLastError());
992  ok( MNS_CHECKORBMP == mi.dwStyle, "System Menu Style is %08x, without the bit %08x\n",
993  mi.dwStyle, MNS_CHECKORBMP);
994  mii.fMask = MIIM_BITMAP;
995  mii.hbmpItem = NULL;
996  got = GetMenuItemInfoA( hsysmenu, SC_CLOSE, FALSE, &mii);
997  ok( got, "GetMenuItemInfoA failed gle=%d\n", GetLastError());
998  ok( HBMMENU_POPUP_CLOSE == mii.hbmpItem, "Item info did not get the right hbitmap: got %p expected %p\n",
999  mii.hbmpItem, HBMMENU_POPUP_CLOSE);
1000 
1001  memset(&mii, 0x81, sizeof(mii));
1002  mii.cbSize = sizeof(mii);
1004  mii.dwTypeData = (LPSTR)bmfill;
1005  mii.cch = sizeof(bmfill);
1006  mii.dwItemData = 0x81818181;
1007  got = GetMenuItemInfoA(hsysmenu, SC_RESTORE, FALSE, &mii);
1008  ok(got, "GetMenuItemInfo failed\n");
1009  ok((mii.fType & ~(MFT_RIGHTJUSTIFY|MFT_RIGHTORDER)) == MFT_STRING, "expected MFT_STRING, got %#x\n", mii.fType);
1010  ok(mii.fState == MF_ENABLED, "expected MF_ENABLED, got %#x\n", mii.fState);
1011  ok(mii.wID == SC_RESTORE, "expected SC_RESTORE, got %#x\n", mii.wID);
1012  ok(mii.hSubMenu == 0, "expected 0, got %p\n", mii.hSubMenu);
1013  ok(mii.dwItemData == 0, "expected 0, got %#lx\n", mii.dwItemData);
1014  ok(mii.dwTypeData == (LPSTR)bmfill, "expected %p, got %p\n", bmfill, mii.dwTypeData);
1015  ok(mii.cch != 0, "cch should not be 0\n");
1016  ok(mii.hbmpItem == HBMMENU_POPUP_RESTORE, "expected HBMMENU_POPUP_RESTORE, got %p\n", mii.hbmpItem);
1017 
1018  mii.cbSize = sizeof(mii);
1019  mii.fMask = MIIM_TYPE;
1020  mii.hbmpItem = (HBITMAP)0x81818181;
1021  got = GetMenuItemInfoA(hsysmenu, SC_CLOSE, FALSE, &mii);
1022  ok(got, "GetMenuItemInfo failed\n");
1023  ok((mii.fType & ~(MFT_RIGHTJUSTIFY|MFT_RIGHTORDER)) == MFT_STRING, "expected MFT_STRING, got %#x\n", mii.fType);
1024  ok(mii.fState == MF_ENABLED, "expected MF_ENABLED, got %#x\n", mii.fState);
1025  ok(mii.wID == SC_RESTORE, "expected SC_RESTORE, got %#x\n", mii.wID);
1026  ok(mii.hSubMenu == 0, "expected 0, got %p\n", mii.hSubMenu);
1027  ok(mii.dwItemData == 0, "expected 0, got %#lx\n", mii.dwItemData);
1028  ok(mii.dwTypeData == (LPSTR)bmfill, "expected %p, got %p\n", bmfill, mii.dwTypeData);
1029  ok(mii.cch != 0, "cch should not be 0\n");
1030  ok(mii.hbmpItem == HBMMENU_POPUP_CLOSE, "expected HBMMENU_POPUP_CLOSE, got %p\n", mii.hbmpItem);
1031 
1033 
1034  if( winetest_debug)
1035  trace(" check %d,%d arrow %d avechar %d\n",
1037  GetSystemMetrics(SM_CYMENUCHECK ),arrowwidth, MOD_avec);
1038  count = 0;
1039  MOD_maxid = 0;
1040  for( ispop=1; ispop >= 0; ispop--){
1041  static SIZE bmsizes[]= {
1042  {10,10},{38,38},{1,30},{55,5}};
1043  for( szidx=0; szidx < ARRAY_SIZE(bmsizes); szidx++) {
1044  HBITMAP hbm = CreateBitmap( bmsizes[szidx].cx, bmsizes[szidx].cy,1,1,bmfill);
1046  ok( hbm != 0, "CreateBitmap failed err %d\n", GetLastError());
1047  for( txtidx = 0; txtidx < ARRAY_SIZE(MOD_txtsizes); txtidx++) {
1048  for( hassub = 0; hassub < 2 ; hassub++) { /* add submenu item */
1049  for( mnuopt = 0; mnuopt < 3 ; mnuopt++){ /* test MNS_NOCHECK/MNS_CHECKORBMP */
1050  for( bmpidx = 0; bmpidx <ARRAY_SIZE(bitmaps); bmpidx++) {
1051  /* no need to test NULL bitmaps of several sizes */
1052  if( !bitmaps[bmpidx] && szidx > 0) continue;
1053  /* the HBMMENU_POPUP not to test for menu bars */
1054  if( !ispop &&
1055  bitmaps[bmpidx] >= HBMMENU_POPUP_CLOSE &&
1056  bitmaps[bmpidx] <= HBMMENU_POPUP_MINIMIZE) continue;
1057  if( !ispop && hassub) continue;
1058  test_mbs_help( ispop, hassub, mnuopt,
1059  hwnd, arrowwidth, ++count,
1060  bitmaps[bmpidx],
1061  bmsizes[szidx],
1062  MOD_txtsizes[txtidx].text,
1063  MOD_txtsizes[txtidx].size,
1064  MOD_txtsizes[txtidx].sc_size);
1065  }
1066  }
1067  }
1068  }
1069  DeleteObject( hbm);
1070  }
1071  }
1072  /* clean up */
1074 }
1075 
1076 static void test_menu_add_string( void )
1077 {
1078  HMENU hmenu;
1080  BOOL rc;
1081  int ret;
1082 
1083  char string[0x80];
1084  char string2[0x80];
1085 
1086  char strback[0x80];
1087  WCHAR strbackW[0x80];
1088  static CHAR blah[] = "blah";
1089  static const WCHAR expectedString[] = {'D','u','m','m','y',' ','s','t','r','i','n','g', 0};
1090 
1091  hmenu = CreateMenu();
1092 
1093  memset( &info, 0, sizeof info );
1094  info.cbSize = sizeof info;
1096  info.dwTypeData = blah;
1097  info.cch = 6;
1098  info.dwItemData = 0;
1099  info.wID = 1;
1100  info.fState = 0;
1101  InsertMenuItemA(hmenu, 0, TRUE, &info );
1102 
1103  memset( &info, 0, sizeof info );
1104  info.cbSize = sizeof info;
1106  info.dwTypeData = string;
1107  info.cch = sizeof string;
1108  string[0] = 0;
1109  GetMenuItemInfoA( hmenu, 0, TRUE, &info );
1110 
1111  ok( !strcmp( string, "blah" ), "menu item name differed\n");
1112 
1113  /* Test combination of ownerdraw and strings with GetMenuItemString(A/W) */
1114  strcpy(string, "Dummy string");
1115  memset(&info, 0x00, sizeof(info));
1116  info.cbSize= sizeof(MENUITEMINFOA);
1117  info.fMask= MIIM_FTYPE | MIIM_STRING; /* Set OwnerDraw + typeData */
1118  info.fType= MFT_OWNERDRAW;
1119  info.dwTypeData= string;
1120  rc = InsertMenuItemA( hmenu, 0, TRUE, &info );
1121  ok (rc, "InsertMenuItem failed\n");
1122 
1123  strcpy(string,"Garbage");
1124  ok (GetMenuStringA( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
1125  ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
1126 
1127  ret = GetMenuStringW( hmenu, 0, strbackW, 99, MF_BYPOSITION );
1128  ok (ret, "GetMenuStringW on ownerdraw entry failed\n");
1129  ok (!lstrcmpW( strbackW, expectedString ), "Menu text from Unicode version incorrect\n");
1130 
1131  /* Just try some invalid parameter tests */
1132  SetLastError(0xdeadbeef);
1133  rc = SetMenuItemInfoA( hmenu, 0, TRUE, NULL );
1134  ret = GetLastError();
1135  ok (!rc, "SetMenuItemInfoA succeeded unexpectedly\n");
1136  ok (ret == ERROR_INVALID_PARAMETER, "Expected 87, got %d\n", ret);
1137 
1138  SetLastError(0xdeadbeef);
1139  rc = SetMenuItemInfoA( hmenu, 0, FALSE, NULL );
1140  ret = GetLastError();
1141  ok (!rc, "SetMenuItemInfoA succeeded unexpectedly\n");
1142  ok (ret == ERROR_INVALID_PARAMETER, "Expected 87, got %d\n", ret);
1143 
1144  /* Just change ftype to string and see what text is stored */
1145  memset(&info, 0x00, sizeof(info));
1146  info.cbSize= sizeof(MENUITEMINFOA);
1147  info.fMask= MIIM_FTYPE; /* Set string type */
1148  info.fType= MFT_STRING;
1149  info.dwTypeData= (char *)0xdeadbeef;
1150  rc = SetMenuItemInfoA( hmenu, 0, TRUE, &info );
1151  ok (rc, "SetMenuItemInfo failed\n");
1152 
1153  /* Did we keep the old dwTypeData? */
1154  ok (GetMenuStringA( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
1155  ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
1156 
1157  /* Ensure change to bitmap type fails */
1158  memset(&info, 0x00, sizeof(info));
1159  info.cbSize= sizeof(MENUITEMINFOA);
1160  info.fMask= MIIM_FTYPE; /* Set as bitmap type */
1161  info.fType= MFT_BITMAP;
1162  info.dwTypeData= (char *)0xdeadbee2;
1163  rc = SetMenuItemInfoA( hmenu, 0, TRUE, &info );
1164  ok (!rc, "SetMenuItemInfo unexpectedly worked\n");
1165 
1166  /* Just change ftype back and ensure data hasn't been freed */
1167  info.fType= MFT_OWNERDRAW; /* Set as ownerdraw type */
1168  info.dwTypeData= (char *)0xdeadbee3;
1169  rc = SetMenuItemInfoA( hmenu, 0, TRUE, &info );
1170  ok (rc, "SetMenuItemInfo failed\n");
1171 
1172  /* Did we keep the old dwTypeData? */
1173  ok (GetMenuStringA( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
1174  ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
1175 
1176  /* Just change string value (not type) */
1177  memset(&info, 0x00, sizeof(info));
1178  info.cbSize= sizeof(MENUITEMINFOA);
1179  info.fMask= MIIM_STRING; /* Set typeData */
1180  strcpy(string2, "string2");
1181  info.dwTypeData= string2;
1182  rc = SetMenuItemInfoA( hmenu, 0, TRUE, &info );
1183  ok (rc, "SetMenuItemInfo failed\n");
1184 
1185  ok (GetMenuStringA( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
1186  ok (!strcmp( strback, "string2" ), "Menu text from Ansi version incorrect\n");
1187 
1188  /* crashes with wine 0.9.5 */
1189  memset(&info, 0x00, sizeof(info));
1190  info.cbSize= sizeof(MENUITEMINFOA);
1191  info.fMask= MIIM_FTYPE | MIIM_STRING; /* Set OwnerDraw + typeData */
1192  info.fType= MFT_OWNERDRAW;
1193  rc = InsertMenuItemA( hmenu, 0, TRUE, &info );
1194  ok (rc, "InsertMenuItem failed\n");
1196  "GetMenuString on ownerdraw entry succeeded.\n");
1198  ok (!ret, "GetMenuStringW on ownerdraw entry succeeded.\n");
1199 
1200  DestroyMenu( hmenu );
1201 }
1202 
1203 /* define building blocks for the menu item info tests */
1204 static int strncmpW( const WCHAR *str1, const WCHAR *str2, int n )
1205 {
1206  if (n <= 0) return 0;
1207  while ((--n > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; }
1208  return *str1 - *str2;
1209 }
1210 
1211 static WCHAR *strcpyW( WCHAR *dst, const WCHAR *src )
1212 {
1213  WCHAR *p = dst;
1214  while ((*p++ = *src++));
1215  return dst;
1216 }
1217 
1219  HMENU submenu, HBITMAP checked, HBITMAP unchecked, ULONG_PTR data,
1220  void *type_data, UINT len, HBITMAP item, BOOL expect )
1221 {
1223  BOOL ret;
1224 
1225  /* magic bitmap handle to test smaller cbSize */
1226  if (item == (HBITMAP)(ULONG_PTR)0xdeadbeef)
1227  info.cbSize = FIELD_OFFSET(MENUITEMINFOA,hbmpItem);
1228  else
1229  info.cbSize = sizeof(info);
1230  info.fMask = mask;
1231  info.fType = type;
1232  info.fState = state;
1233  info.wID = id;
1234  info.hSubMenu = submenu;
1235  info.hbmpChecked = checked;
1236  info.hbmpUnchecked = unchecked;
1237  info.dwItemData = data;
1238  info.dwTypeData = type_data;
1239  info.cch = len;
1240  info.hbmpItem = item;
1241  SetLastError( 0xdeadbeef );
1242  if (ansi) ret = InsertMenuItemA( hmenu, 0, TRUE, &info );
1243  else ret = InsertMenuItemW( hmenu, 0, TRUE, (MENUITEMINFOW*)&info );
1244  if (!expect) ok_(__FILE__, line)( !ret, "InsertMenuItem should have failed.\n" );
1245  else ok_(__FILE__, line)( ret, "InsertMenuItem failed, err %u\n", GetLastError());
1246 }
1247 
1249  UINT id, HMENU submenu, HBITMAP checked, HBITMAP unchecked,
1250  ULONG_PTR data, void *type_data, UINT in_len, UINT out_len,
1251  HBITMAP item, LPCSTR expname, BOOL expect, BOOL expstring )
1252 {
1254  BOOL ret;
1255  WCHAR buffer[80];
1256 
1257  SetLastError( 0xdeadbeef );
1258  memset( &info, 0xcc, sizeof(info) );
1259  info.cbSize = sizeof(info);
1260  info.fMask = mask;
1261  info.dwTypeData = type_data;
1262  info.cch = in_len;
1263 
1264  ret = ansi ? GetMenuItemInfoA( hmenu, 0, TRUE, &info ) :
1266  if (!expect)
1267  {
1268  ok_(__FILE__, line)( !ret, "GetMenuItemInfo should have failed.\n" );
1269  return;
1270  }
1271  ok_(__FILE__, line)( ret, "GetMenuItemInfo failed, err %u\n", GetLastError());
1272  if (mask & MIIM_TYPE)
1273  ok_(__FILE__, line)( info.fType == type || info.fType == LOWORD(type),
1274  "wrong type %x/%x\n", info.fType, type );
1275  if (mask & MIIM_STATE)
1276  ok_(__FILE__, line)( info.fState == state || info.fState == LOWORD(state),
1277  "wrong state %x/%x\n", info.fState, state );
1278  if (mask & MIIM_ID)
1279  ok_(__FILE__, line)( info.wID == id || info.wID == LOWORD(id),
1280  "wrong id %x/%x\n", info.wID, id );
1281  if (mask & MIIM_SUBMENU)
1282  ok_(__FILE__, line)( info.hSubMenu == submenu || (ULONG_PTR)info.hSubMenu == LOWORD(submenu),
1283  "wrong submenu %p/%p\n", info.hSubMenu, submenu );
1284  if (mask & MIIM_CHECKMARKS)
1285  {
1286  ok_(__FILE__, line)( info.hbmpChecked == checked || (ULONG_PTR)info.hbmpChecked == LOWORD(checked),
1287  "wrong bmpchecked %p/%p\n", info.hbmpChecked, checked );
1288  ok_(__FILE__, line)( info.hbmpUnchecked == unchecked || (ULONG_PTR)info.hbmpUnchecked == LOWORD(unchecked),
1289  "wrong bmpunchecked %p/%p\n", info.hbmpUnchecked, unchecked );
1290  }
1291  if (mask & MIIM_DATA)
1292  ok_(__FILE__, line)( info.dwItemData == data || info.dwItemData == LOWORD(data),
1293  "wrong item data %lx/%lx\n", info.dwItemData, data );
1294  if (mask & MIIM_BITMAP)
1295  ok_(__FILE__, line)( info.hbmpItem == item || (ULONG_PTR)info.hbmpItem == LOWORD(item),
1296  "wrong bmpitem %p/%p\n", info.hbmpItem, item );
1297  ok_(__FILE__, line)( info.dwTypeData == type_data || (ULONG_PTR)info.dwTypeData == LOWORD(type_data),
1298  "wrong type data %p/%p\n", info.dwTypeData, type_data );
1299  ok_(__FILE__, line)( info.cch == out_len ||
1300  broken(! ansi && info.cch == 2 * out_len) /* East-Asian */,
1301  "wrong len %x/%x\n", info.cch, out_len );
1302  if (expname)
1303  {
1304  if(ansi)
1305  ok_(__FILE__, line)( !strncmp( expname, info.dwTypeData, out_len ),
1306  "menu item name differed from '%s' '%s'\n", expname, info.dwTypeData );
1307  else
1308  ok_(__FILE__, line)( !strncmpW( (WCHAR *)expname, (WCHAR *)info.dwTypeData, out_len ),
1309  "menu item name wrong\n" );
1310 
1311  SetLastError( 0xdeadbeef );
1312  ret = ansi ? GetMenuStringA( hmenu, 0, (char *)buffer, 80, MF_BYPOSITION ) :
1314  if (expstring)
1315  ok_(__FILE__, line)( ret, "GetMenuString failed, err %u\n", GetLastError());
1316  else
1317  ok_(__FILE__, line)( !ret, "GetMenuString should have failed\n" );
1318  }
1319 }
1320 
1321 static void modify_menu( int line, HMENU hmenu, BOOL ansi, UINT flags, UINT_PTR id, void *data )
1322 {
1323  BOOL ret;
1324 
1325  SetLastError( 0xdeadbeef );
1326  if (ansi) ret = ModifyMenuA( hmenu, 0, flags, id, data );
1327  else ret = ModifyMenuW( hmenu, 0, flags, id, data );
1328  ok_(__FILE__,line)( ret, "ModifyMenuA failed, err %u\n", GetLastError());
1329 }
1330 
1332  UINT id, HMENU submenu, HBITMAP checked, HBITMAP unchecked, ULONG_PTR data,
1333  void *type_data, UINT len, HBITMAP item )
1334 
1335 {
1337  BOOL ret;
1338 
1339  /* magic bitmap handle to test smaller cbSize */
1340  if (item == (HBITMAP)(ULONG_PTR)0xdeadbeef)
1341  info.cbSize = FIELD_OFFSET(MENUITEMINFOA,hbmpItem);
1342  else
1343  info.cbSize = sizeof(info);
1344  info.fMask = mask;
1345  info.fType = type;
1346  info.fState = state;
1347  info.wID = id;
1348  info.hSubMenu = submenu;
1349  info.hbmpChecked = checked;
1350  info.hbmpUnchecked = unchecked;
1351  info.dwItemData = data;
1352  info.dwTypeData = type_data;
1353  info.cch = len;
1354  info.hbmpItem = item;
1355  SetLastError( 0xdeadbeef );
1356  if (ansi) ret = SetMenuItemInfoA( hmenu, 0, TRUE, &info );
1357  else ret = SetMenuItemInfoW( hmenu, 0, TRUE, (MENUITEMINFOW*)&info );
1358  ok_(__FILE__, line)( ret, "SetMenuItemInfo failed, err %u\n", GetLastError());
1359 }
1360 
1361 #define TMII_INSMI( c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,eret1 )\
1362  hmenu = CreateMenu();\
1363  submenu = CreateMenu();\
1364  if(ansi)strcpy( string, init );\
1365  else strcpyW( string, init );\
1366  insert_menu_item( __LINE__, hmenu, ansi, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, eret1 )
1367 
1368 /* GetMenuItemInfo + GetMenuString */
1369 #define TMII_GMII( c2,l2,\
1370  d3,e3,f3,g3,h3,i3,j3,k3,l3,m3,\
1371  expname, eret2, eret3)\
1372  check_menu_item_info( __LINE__, hmenu, ansi, c2, d3, e3, f3, g3, h3, i3, j3, k3, l2, l3, m3, \
1373  expname, eret2, eret3 )
1374 
1375 #define TMII_DONE \
1376  RemoveMenu(hmenu, 0, TRUE );\
1377  DestroyMenu( hmenu );\
1378  DestroyMenu( submenu );
1379 
1380 /* modify menu */
1381 #define TMII_MODM( flags, id, data ) \
1382  modify_menu( __LINE__, hmenu, ansi, flags, id, data )
1383 
1384 /* SetMenuItemInfo */
1385 #define TMII_SMII( c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1 ) \
1386  set_menu_item_info( __LINE__, hmenu, ansi, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1 )
1387 
1388 
1389 #define OK 1
1390 #define ER 0
1391 
1392 
1393 static void test_menu_iteminfo( void )
1394 {
1395  BOOL ansi = TRUE;
1396  char txtA[]="wine";
1397  char initA[]="XYZ";
1398  char emptyA[]="";
1399  WCHAR txtW[]={'W','i','n','e',0};
1400  WCHAR initW[]={'X','Y','Z',0};
1401  WCHAR emptyW[]={0};
1402  void *txt, *init, *empty, *string;
1403  HBITMAP hbm = CreateBitmap(1,1,1,1,NULL);
1404  char stringA[0x80];
1405  HMENU hmenu, submenu;
1406  HBITMAP dummy_hbm = (HBITMAP)(ULONG_PTR)0xdeadbeef;
1407 
1408  do {
1409  if( ansi) {txt=txtA;init=initA;empty=emptyA;string=stringA;}
1410  else {txt=txtW;init=initW;empty=emptyW;string=stringA;}
1411  trace( "%s string %p hbm %p txt %p\n", ansi ? "ANSI tests: " : "Unicode tests:", string, hbm, txt);
1412  /* test all combinations of MFT_STRING, MFT_OWNERDRAW and MFT_BITMAP */
1413  /* (since MFT_STRING is zero, there are four of them) */
1414  TMII_INSMI( MIIM_TYPE, MFT_STRING, 0, 0, 0, 0, 0, 0, txt, 0, 0, OK );
1415  TMII_GMII ( MIIM_TYPE, 80,
1416  MFT_STRING, 0, 0, 0, 0, 0, 0, string, 4, 0,
1417  txt, OK, OK );
1418  TMII_DONE
1419  TMII_INSMI( MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 0, 0, OK );
1420  TMII_GMII ( MIIM_TYPE, 80,
1421  MFT_STRING|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1422  NULL, OK, ER );
1423  TMII_DONE
1424  TMII_INSMI( MIIM_TYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, hbm, 6, 0, OK );
1425  TMII_GMII ( MIIM_TYPE, 80,
1426  MFT_BITMAP, 0, 0, 0, 0, 0, 0, hbm, 0, hbm,
1427  NULL, OK, ER );
1428  TMII_DONE
1429  TMII_INSMI( MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, hbm, 6, 0, OK );
1430  TMII_GMII ( MIIM_TYPE, 80,
1431  MFT_BITMAP|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, hbm, 0, hbm,
1432  NULL, OK, ER );
1433  TMII_DONE
1434  /* not enough space for name*/
1435  TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
1436  TMII_GMII ( MIIM_TYPE, 0,
1437  MFT_STRING, 0, 0, 0, 0, 0, 0, NULL, 4, 0,
1438  NULL, OK, OK );
1439  TMII_DONE
1440  TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
1441  TMII_GMII ( MIIM_TYPE, 5,
1442  MFT_STRING, 0, 0, 0, 0, 0, 0, string, 4, 0,
1443  txt, OK, OK );
1444  TMII_DONE
1445  TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
1446  TMII_GMII ( MIIM_TYPE, 4,
1447  MFT_STRING, 0, 0, 0, 0, 0, 0, string, 3, 0,
1448  txt, OK, OK );
1449  TMII_DONE
1450  TMII_INSMI( MIIM_FTYPE|MIIM_STRING, MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, NULL, 0, 0, OK );
1451  TMII_GMII ( MIIM_TYPE, 0,
1452  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, NULL, 0, 0,
1453  NULL, OK, ER );
1454  TMII_DONE
1455  /* cannot combine MIIM_TYPE with some other flags */
1456  TMII_INSMI( MIIM_TYPE|MIIM_STRING, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, ER );
1457  TMII_DONE
1458  TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
1460  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1461  NULL, ER, OK );
1462  TMII_DONE
1463  TMII_INSMI( MIIM_TYPE|MIIM_FTYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, ER );
1464  TMII_DONE
1465  TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
1467  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1468  NULL, ER, OK );
1469  TMII_DONE
1470  TMII_INSMI( MIIM_TYPE|MIIM_BITMAP, MFT_BITMAP, -1, -1, 0, 0, 0, -1, hbm, 6, hbm, ER );
1471  TMII_DONE
1472  /* but succeeds with some others */
1473  TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
1475  MFT_STRING, 0, 0, 0, 0, 0, 0, string, 4, 0,
1476  txt, OK, OK );
1477  TMII_DONE
1478  TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
1480  MFT_STRING, 0, 0, 0, 0, 0, 0, string, 4, 0,
1481  txt, OK, OK );
1482  TMII_DONE
1483  TMII_INSMI( MIIM_TYPE|MIIM_ID, MFT_STRING, -1, 888, 0, 0, 0, -1, txt, 6, 0, OK );
1484  TMII_GMII ( MIIM_TYPE|MIIM_ID, 80,
1485  MFT_STRING, 0, 888, 0, 0, 0, 0, string, 4, 0,
1486  txt, OK, OK );
1487  TMII_DONE
1488  TMII_INSMI( MIIM_TYPE|MIIM_DATA, MFT_STRING, -1, -1, 0, 0, 0, 999, txt, 6, 0, OK );
1490  MFT_STRING, 0, 0, 0, 0, 0, 999, string, 4, 0,
1491  txt, OK, OK );
1492  TMII_DONE
1493  /* to be continued */
1494  /* set text with MIIM_TYPE and retrieve with MIIM_STRING */
1495  TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
1497  MFT_STRING, 0, 0, 0, 0, 0, 0, string, 4, 0,
1498  txt, OK, OK );
1499  TMII_DONE
1500  /* set text with MIIM_TYPE and retrieve with MIIM_STRING; MFT_OWNERDRAW causes an empty string */
1501  TMII_INSMI( MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
1503  MFT_STRING|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, string, 0, 0,
1504  empty, OK, ER );
1505  TMII_DONE
1506  TMII_INSMI( MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, NULL, 0, 0, OK );
1508  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, string, 0, 0,
1509  empty, OK, ER );
1510  TMII_DONE
1511  TMII_INSMI( MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, NULL, 0, 0, OK );
1512  TMII_GMII ( MIIM_FTYPE, 80,
1513  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, string, 80, 0,
1514  init, OK, ER );
1515  TMII_DONE
1516  TMII_INSMI( MIIM_TYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 0, 0, OK );
1517  TMII_GMII ( 0, 80,
1518  0, 0, 0, 0, 0, 0, 0, string, 80, 0,
1519  init, OK, OK );
1520  TMII_DONE
1521  /* contrary to MIIM_TYPE,you can set the text for an owner draw menu */
1522  TMII_INSMI( MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 0, 0, OK );
1524  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, string, 4, 0,
1525  txt, OK, OK );
1526  TMII_DONE
1527  /* same but retrieve with MIIM_TYPE */
1528  TMII_INSMI( MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 0, 0, OK );
1529  TMII_GMII ( MIIM_TYPE, 80,
1530  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, NULL, 4, NULL,
1531  NULL, OK, OK );
1532  TMII_DONE
1533  TMII_INSMI( MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, NULL, 0, 0, OK );
1535  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, string, 0, 0,
1536  empty, OK, ER );
1537  TMII_DONE
1538  TMII_INSMI( MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, NULL, 0, 0, OK );
1540  MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, string, 0, 0,
1541  empty, OK, ER );
1542  TMII_DONE
1543  TMII_INSMI( MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, NULL, 0, 0, OK );
1545  MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, NULL, 0, 0,
1546  NULL, OK, ER );
1547  TMII_DONE
1548 
1549  /* How is that with bitmaps? */
1550  TMII_INSMI( MIIM_BITMAP, -1, -1, -1, 0, 0, 0, -1, 0, -1, hbm, OK );
1551  TMII_GMII ( MIIM_TYPE, 80,
1552  MFT_BITMAP, 0, 0, 0, 0, 0, 0, hbm, 0, hbm,
1553  NULL, OK, ER );
1554  TMII_DONE
1555  TMII_INSMI( MIIM_BITMAP, -1, -1, -1, 0, 0, 0, -1, 0, -1, hbm, OK );
1557  0, 0, 0, 0, 0, 0, 0, string, 80, hbm,
1558  init, OK, ER );
1559  TMII_DONE
1560  /* MIIM_BITMAP does not like MFT_BITMAP */
1561  TMII_INSMI( MIIM_BITMAP|MIIM_FTYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, 0, -1, hbm, ER );
1562  TMII_DONE
1563  /* no problem with OWNERDRAWN */
1564  TMII_INSMI( MIIM_BITMAP|MIIM_FTYPE, MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, 0, -1, hbm, OK );
1566  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, string, 80, hbm,
1567  init, OK, ER );
1568  TMII_DONE
1569  /* setting MFT_BITMAP with MFT_FTYPE fails anyway */
1570  TMII_INSMI( MIIM_FTYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, 0, -1, 0, ER );
1571  TMII_DONE
1572 
1573  /* menu with submenu */
1574  TMII_INSMI( MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, 0, 0, -1, txt, 0, 0, OK );
1575  TMII_GMII ( MIIM_SUBMENU, 80,
1576  0, 0, 0, submenu, 0, 0, 0, string, 80, 0,
1577  init, OK, ER );
1578  TMII_DONE
1579  TMII_INSMI( MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, 0, 0, -1, empty, 0, 0, OK );
1580  TMII_GMII ( MIIM_SUBMENU, 80,
1581  0, 0, 0, submenu, 0, 0, 0, string, 80, 0,
1582  init, OK, ER );
1583  TMII_DONE
1584  /* menu with submenu, without MIIM_SUBMENU the submenufield is cleared */
1585  TMII_INSMI( MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, 0, 0, -1, txt, 0, 0, OK );
1587  MFT_STRING|MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, string, 0, 0,
1588  empty, OK, ER );
1590  MFT_SEPARATOR, 0, 0, submenu, 0, 0, 0, string, 80, 0,
1591  empty, OK, ER );
1592  TMII_DONE
1593  /* menu with invalid submenu */
1594  TMII_INSMI( MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, (HMENU)999, 0, 0, -1, txt, 0, 0, ER );
1595  TMII_DONE
1596  /* Separator */
1597  TMII_INSMI( MIIM_TYPE, MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, txt, 0, 0, OK );
1598  TMII_GMII ( MIIM_TYPE, 80,
1599  MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1600  NULL, OK, ER );
1601  TMII_DONE
1602  TMII_INSMI( MIIM_TYPE, MFT_BITMAP|MFT_SEPARATOR, -1, -1, 0, 0, 0, -1, hbm, 6, 0, OK );
1603  TMII_GMII ( MIIM_TYPE, 80,
1604  MFT_BITMAP|MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, hbm, 0, hbm,
1605  NULL, OK, ER );
1606  TMII_DONE
1607  /* SEPARATOR and STRING go well together */
1608  /* BITMAP and STRING go well together */
1609  TMII_INSMI( MIIM_STRING|MIIM_BITMAP, -1, -1, -1, 0, 0, 0, -1, txt, 6, hbm, OK );
1611  MFT_STRING, 0, 0, 0, 0, 0, 0, string, 4, hbm,
1612  txt, OK, OK );
1613  TMII_DONE
1614  /* BITMAP, SEPARATOR and STRING go well together */
1615  TMII_INSMI( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, 0, 0, 0, -1, txt, 6, hbm, OK );
1617  MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, string, 4, hbm,
1618  txt, OK, OK );
1619  TMII_DONE
1620  /* last two tests, but use MIIM_TYPE to retrieve info */
1621  TMII_INSMI( MIIM_FTYPE|MIIM_STRING, MFT_SEPARATOR, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
1622  TMII_GMII ( MIIM_TYPE, 80,
1623  MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, NULL, 4, NULL,
1624  NULL, OK, OK );
1625  TMII_DONE
1626  TMII_INSMI( MIIM_STRING|MIIM_BITMAP, -1, -1, -1, 0, 0, 0, -1, txt, 6, hbm, OK );
1627  TMII_GMII ( MIIM_TYPE, 80,
1628  MFT_BITMAP, 0, 0, 0, 0, 0, 0, hbm, 4, hbm,
1629  NULL, OK, OK );
1630  TMII_DONE
1631  TMII_INSMI( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, 0, 0, 0, -1, txt, 6, hbm, OK );
1632  TMII_GMII ( MIIM_TYPE, 80,
1633  MFT_SEPARATOR|MFT_BITMAP, 0, 0, 0, 0, 0, 0, hbm, 4, hbm,
1634  NULL, OK, OK );
1635  TMII_DONE
1636  /* same three with MFT_OWNERDRAW */
1637  TMII_INSMI( MIIM_FTYPE|MIIM_STRING, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 6, 0, OK );
1638  TMII_GMII ( MIIM_TYPE, 80,
1639  MFT_SEPARATOR|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, NULL, 4, NULL,
1640  NULL, OK, OK );
1641  TMII_DONE
1642  TMII_INSMI( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 6, hbm, OK );
1643  TMII_GMII ( MIIM_TYPE, 80,
1644  MFT_BITMAP|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, hbm, 4, hbm,
1645  NULL, OK, OK );
1646  TMII_DONE
1647  TMII_INSMI( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 6, hbm, OK );
1648  TMII_GMII ( MIIM_TYPE, 80,
1649  MFT_SEPARATOR|MFT_BITMAP|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, hbm, 4, hbm,
1650  NULL, OK, OK );
1651  TMII_DONE
1652 
1653  TMII_INSMI( MIIM_STRING|MIIM_FTYPE|MIIM_ID, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 0, 0, OK );
1654  TMII_GMII ( MIIM_TYPE, 80,
1655  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, NULL, 4, NULL,
1656  NULL, OK, OK );
1657  TMII_DONE
1658  /* test with modifymenu: string is preserved after setting OWNERDRAW */
1659  TMII_INSMI( MIIM_STRING, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 0, 0, OK );
1660  TMII_MODM( MFT_OWNERDRAW, -1, (void*)787 );
1662  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 787, string, 4, 0,
1663  txt, OK, OK );
1664  TMII_DONE
1665  /* same with bitmap: now the text is cleared */
1666  TMII_INSMI( MIIM_STRING, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 0, 0, OK );
1667  TMII_MODM( MFT_BITMAP, 545, hbm );
1669  MFT_BITMAP, 0, 545, 0, 0, 0, 0, string, 0, hbm,
1670  empty, OK, ER );
1671  TMII_DONE
1672  /* start with bitmap: now setting text clears it (though he flag is raised) */
1673  TMII_INSMI( MIIM_BITMAP, MFT_STRING, -1, -1, 0, 0, 0, -1, 0, -1, hbm, OK );
1675  MFT_STRING, 0, 0, 0, 0, 0, 0, string, 0, hbm,
1676  empty, OK, ER );
1677  TMII_MODM( MFT_STRING, 545, txt );
1679  MFT_STRING, 0, 545, 0, 0, 0, 0, string, 4, 0,
1680  txt, OK, OK );
1681  TMII_DONE
1682  /*repeat with text NULL */
1683  TMII_INSMI( MIIM_BITMAP, MFT_STRING, -1, -1, 0, 0, 0, -1, 0, -1, hbm, OK );
1684  TMII_MODM( MFT_STRING, 545, NULL );
1686  MFT_SEPARATOR, 0, 545, 0, 0, 0, 0, string, 0, 0,
1687  empty, OK, ER );
1688  TMII_DONE
1689  /* repeat with text "" */
1690  TMII_INSMI( MIIM_BITMAP, -1 , -1, -1, 0, 0, 0, -1, 0, -1, hbm, OK );
1691  TMII_MODM( MFT_STRING, 545, empty );
1693  MFT_STRING, 0, 545, 0, 0, 0, 0, string, 0, 0,
1694  empty, OK, ER );
1695  TMII_DONE
1696  /* start with bitmap: set ownerdraw */
1697  TMII_INSMI( MIIM_BITMAP, -1, -1, -1, 0, 0, 0, -1, 0, -1, hbm, OK );
1698  TMII_MODM( MFT_OWNERDRAW, -1, (void *)232 );
1700  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 232, string, 0, hbm,
1701  empty, OK, ER );
1702  TMII_DONE
1703  /* ask nothing */
1704  TMII_INSMI( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, 0, 0, 0, -1, txt, 6, hbm, OK );
1705  TMII_GMII ( 0, 80,
1706  0, 0, 0, 0, 0, 0, 0, string, 80, 0,
1707  init, OK, OK );
1708  TMII_DONE
1709  /* some tests with small cbSize: the hbmpItem is to be ignored */
1710  TMII_INSMI( MIIM_BITMAP, -1, -1, -1, 0, 0, 0, -1, 0, -1, dummy_hbm, OK );
1711  TMII_GMII ( MIIM_TYPE, 80,
1712  MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, NULL, 0, NULL,
1713  NULL, OK, ER );
1714  TMII_DONE
1715  TMII_INSMI( MIIM_BITMAP, -1, -1, -1, 0, 0, 0, -1, 0, -1, dummy_hbm, OK );
1717  MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, string, 80, NULL,
1718  init, OK, ER );
1719  TMII_DONE
1720  TMII_INSMI( MIIM_STRING|MIIM_BITMAP, -1, -1, -1, 0, 0, 0, -1, txt, 6, dummy_hbm, OK );
1721  TMII_GMII ( MIIM_TYPE, 80,
1722  MFT_STRING, 0, 0, 0, 0, 0, 0, string, 4, NULL,
1723  txt, OK, OK );
1724  TMII_DONE
1725  TMII_INSMI( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, 0, 0, 0, -1, txt, 6, dummy_hbm, OK );
1726  TMII_GMII ( MIIM_TYPE, 80,
1727  MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, NULL, 4, NULL,
1728  NULL, OK, OK );
1729  TMII_DONE
1730  TMII_INSMI( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 6, dummy_hbm, OK );
1731  TMII_GMII ( MIIM_TYPE, 80,
1732  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, NULL, 4, NULL,
1733  NULL, OK, OK );
1734  TMII_DONE
1735  TMII_INSMI( MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, 0, 0, 0, -1, txt, 6, dummy_hbm, OK );
1736  TMII_GMII ( MIIM_TYPE, 80,
1737  MFT_SEPARATOR|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, NULL, 4, NULL,
1738  NULL, OK, OK );
1739  TMII_DONE
1740  /* MIIM_TYPE by itself does not get/set the dwItemData for OwnerDrawn menus */
1741  TMII_INSMI( MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, 343, txt, 0, 0, OK );
1743  MFT_STRING|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 343, 0, 0, 0,
1744  NULL, OK, ER );
1745  TMII_DONE
1746  TMII_INSMI( MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, 343, txt, 0, 0, OK );
1747  TMII_GMII ( MIIM_TYPE, 80,
1748  MFT_STRING|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1749  NULL, OK, ER );
1750  TMII_DONE
1751  TMII_INSMI( MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, 0, 0, 0, 343, txt, 0, 0, OK );
1753  MFT_STRING|MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1754  NULL, OK, ER );
1755  TMII_DONE
1756  /* set a string menu to ownerdraw with MIIM_TYPE */
1757  TMII_INSMI( MIIM_TYPE, MFT_STRING, -2, -2, 0, 0, 0, -2, txt, -2, 0, OK );
1758  TMII_SMII ( MIIM_TYPE, MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
1760  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, string, 4, 0,
1761  txt, OK, OK );
1762  TMII_DONE
1763  /* test with modifymenu add submenu */
1764  TMII_INSMI( MIIM_STRING, MFT_STRING, -1, -1, 0, 0, 0, -1, txt, 0, 0, OK );
1765  TMII_MODM( MF_POPUP, (UINT_PTR)submenu, txt );
1767  MFT_STRING, 0, 0, submenu, 0, 0, 0, string, 4, 0,
1768  txt, OK, OK );
1769  TMII_GMII ( MIIM_TYPE, 80,
1770  MFT_STRING, 0, 0, 0, 0, 0, 0, string, 4, 0,
1771  txt, OK, OK );
1772  TMII_DONE
1773  /* MFT_SEPARATOR bit is kept when the text is added */
1774  TMII_INSMI( MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, NULL, 0, 0, OK );
1775  TMII_SMII( MIIM_STRING, 0, 0, 0, 0, 0, 0, 0, txt, 0, 0 );
1777  MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, string, 4, 0,
1778  txt, OK, OK );
1779  TMII_DONE
1780  /* MFT_SEPARATOR bit is kept when bitmap is added */
1781  TMII_INSMI( MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, 0, 0, 0, -1, NULL, 0, 0, OK );
1782  TMII_SMII( MIIM_BITMAP, 0, 0, 0, 0, 0, 0, 0, 0, 0, hbm );
1784  MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, string, 80, hbm,
1785  init, OK, ER );
1786  TMII_DONE
1787  /* Bitmaps inserted with MIIM_TYPE and MFT_BITMAP:
1788  Only the low word of the dwTypeData is used.
1789  Use a magic bitmap here (Word 95 uses this to create its MDI menu buttons) */
1790  TMII_INSMI( MIIM_TYPE, MFT_BITMAP | MFT_RIGHTJUSTIFY, -1, -1, 0, 0, 0, -1,
1791  (HMENU)MAKELPARAM(HBMMENU_MBAR_CLOSE, 0x1234), -1, 0, OK );
1792  TMII_GMII ( MIIM_TYPE, 80,
1794  NULL, OK, OK );
1795  TMII_DONE
1796  /* Type flags */
1798  TMII_GMII ( MIIM_TYPE, 80,
1800  NULL, OK, OK );
1801  TMII_DONE
1802  /* State flags */
1803  TMII_INSMI( MIIM_TYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, hbm, -1, 0, OK );
1804  TMII_SMII( MIIM_STATE, -1, MFS_CHECKED | MFS_DEFAULT | MFS_GRAYED | MFS_HILITE, 0, 0, 0, 0, 0, 0, 0, 0 );
1805  TMII_GMII ( MIIM_STATE, 80,
1806  0, MFS_CHECKED | MFS_DEFAULT | MFS_GRAYED | MFS_HILITE, 0, 0, 0, 0, 0, 0, 80, 0,
1807  NULL, OK, OK );
1808  TMII_DONE
1809  /* The style MFT_RADIOCHECK cannot be set with MIIM_CHECKMARKS only */
1810  TMII_INSMI( MIIM_TYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, hbm, -1, 0, OK );
1811  TMII_SMII( MIIM_CHECKMARKS, MFT_RADIOCHECK, 0, 0, 0, hbm, hbm, 0, 0, 0, 0 );
1813  MFT_BITMAP, 0, 0, 0, hbm, hbm, 0, hbm, 0, hbm,
1814  NULL, OK, OK );
1815  TMII_DONE
1816  /* MFT_BITMAP is added automatically by GetMenuItemInfo() for MIIM_TYPE */
1817  TMII_INSMI( MIIM_TYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, hbm, -1, 0, OK );
1818  TMII_SMII( MIIM_FTYPE, MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, (HBITMAP)0x1234, 0, 0 );
1819  TMII_GMII ( MIIM_FTYPE, 80,
1820  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, 0, 80, 0,
1821  NULL, OK, OK );
1822  TMII_GMII ( MIIM_TYPE, 80,
1823  MFT_BITMAP | MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, hbm, 0, hbm,
1824  NULL, OK, OK );
1825  TMII_GMII ( MIIM_FTYPE, 80,
1826  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, 0, 80, 0,
1827  NULL, OK, OK );
1828  TMII_SMII( MIIM_BITMAP, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL );
1829  TMII_GMII ( MIIM_TYPE, 80,
1830  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, NULL, 0, NULL,
1831  NULL, OK, OK );
1832  TMII_DONE
1833  /* Bitmaps inserted with MIIM_TYPE and MFT_BITMAP:
1834  Only the low word of the dwTypeData is used.
1835  Use a magic bitmap here (Word 95 uses this to create its MDI menu buttons) */
1836  TMII_INSMI( MIIM_TYPE, MFT_BITMAP | MFT_RIGHTJUSTIFY, -1, -1, 0, 0, 0, -1,
1837  (HMENU)MAKELPARAM(HBMMENU_MBAR_CLOSE, 0x1234), -1, 0, OK );
1838  TMII_GMII ( MIIM_TYPE, 80,
1840  NULL, OK, OK );
1841  TMII_DONE
1842  /* Type flags */
1844  TMII_GMII ( MIIM_TYPE, 80,
1846  NULL, OK, OK );
1847  TMII_DONE
1848  /* State flags */
1849  TMII_INSMI( MIIM_TYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, hbm, -1, 0, OK );
1850  TMII_SMII( MIIM_STATE, -1, MFS_CHECKED | MFS_DEFAULT | MFS_GRAYED | MFS_HILITE, 0, 0, 0, 0, 0, 0, 0, 0 );
1851  TMII_GMII ( MIIM_STATE, 80,
1852  0, MFS_CHECKED | MFS_DEFAULT | MFS_GRAYED | MFS_HILITE, 0, 0, 0, 0, 0, 0, 80, 0,
1853  NULL, OK, OK );
1854  TMII_DONE
1855  /* The style MFT_RADIOCHECK cannot be set with MIIM_CHECKMARKS only */
1856  TMII_INSMI( MIIM_TYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, hbm, -1, 0, OK );
1857  TMII_SMII( MIIM_CHECKMARKS, MFT_RADIOCHECK, 0, 0, 0, hbm, hbm, 0, 0, 0, 0 );
1859  MFT_BITMAP, 0, 0, 0, hbm, hbm, 0, hbm, 0, hbm,
1860  NULL, OK, OK );
1861  TMII_DONE
1862  /* MFT_BITMAP is added automatically by GetMenuItemInfo() for MIIM_TYPE */
1863  TMII_INSMI( MIIM_TYPE, MFT_BITMAP, -1, -1, 0, 0, 0, -1, hbm, -1, 0, OK );
1864  TMII_SMII( MIIM_FTYPE, MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, (HBITMAP)0x1234, 0, 0 );
1865  TMII_GMII ( MIIM_FTYPE, 80,
1866  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, 0, 80, 0,
1867  NULL, OK, OK );
1868  TMII_GMII ( MIIM_TYPE, 80,
1869  MFT_BITMAP | MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, hbm, 0, hbm,
1870  NULL, OK, OK );
1871  TMII_GMII ( MIIM_FTYPE, 80,
1872  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, 0, 80, 0,
1873  NULL, OK, OK );
1874  TMII_SMII( MIIM_BITMAP, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL );
1875  TMII_GMII ( MIIM_TYPE, 80,
1876  MFT_OWNERDRAW, 0, 0, 0, 0, 0, 0, NULL, 0, NULL,
1877  NULL, OK, OK );
1878  TMII_DONE
1879  } while( !(ansi = !ansi) );
1880  DeleteObject( hbm);
1881 }
1882 
1883 /*
1884  The following tests try to confirm the algorithm used to return the menu items
1885  when there is a collision between a menu item and a popup menu
1886  */
1887 static void test_menu_search_bycommand( void )
1888 {
1889  HMENU hmenu, hmenuSub, hmenuSub2;
1891  BOOL rc;
1892  UINT id;
1893  char strback[0x80];
1894  char strIn[0x80];
1895  static CHAR menuitem[] = "MenuItem",
1896  menuitem2[] = "MenuItem 2";
1897 
1898  /* Case 1: Menu containing a menu item */
1899  hmenu = CreateMenu();
1900 
1901  memset( &info, 0, sizeof info );
1902  info.cbSize = sizeof info;
1903  info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1904  info.fType = MFT_STRING;
1905  strcpy(strIn, "Case 1 MenuItem");
1906  info.dwTypeData = strIn;
1907  info.wID = (UINT) 0x1234;
1908 
1909  rc = InsertMenuItemA(hmenu, 0, TRUE, &info );
1910  ok (rc, "Inserting the menuitem failed\n");
1911 
1912  id = GetMenuItemID(hmenu, 0);
1913  ok (id == 0x1234, "Getting the menuitem id failed(gave %x)\n", id);
1914 
1915  /* Confirm the menuitem was given the id supplied (getting by position) */
1916  memset( &info, 0, sizeof info );
1917  strback[0] = 0x00;
1918  info.cbSize = sizeof(MENUITEMINFOA);
1919  info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1920  info.dwTypeData = strback;
1921  info.cch = sizeof(strback);
1922 
1923  rc = GetMenuItemInfoA(hmenu, 0, TRUE, &info); /* Get by position */
1924  ok (rc, "Getting the menu items info failed\n");
1925  ok (info.wID == 0x1234, "IDs differ for the menuitem\n");
1926  ok (!strcmp(info.dwTypeData, "Case 1 MenuItem"), "Returned item has wrong label\n");
1927 
1928  /* Search by id - Should return the item */
1929  memset( &info, 0, sizeof info );
1930  strback[0] = 0x00;
1931  info.cbSize = sizeof(MENUITEMINFOA);
1932  info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1933  info.dwTypeData = strback;
1934  info.cch = sizeof(strback);
1935  rc = GetMenuItemInfoA(hmenu, 0x1234, FALSE, &info); /* Get by ID */
1936 
1937  ok (rc, "Getting the menu items info failed\n");
1938  ok (info.wID == 0x1234, "IDs differ for the menuitem\n");
1939  ok (!strcmp(info.dwTypeData, "Case 1 MenuItem"), "Returned item has wrong label\n");
1940 
1941  DestroyMenu( hmenu );
1942 
1943  /* Case 2: Menu containing a popup menu */
1944  hmenu = CreateMenu();
1945  hmenuSub = CreateMenu();
1946 
1947  strcpy(strIn, "Case 2 SubMenu");
1948  rc = InsertMenuA(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, strIn);
1949  ok (rc, "Inserting the popup menu into the main menu failed\n");
1950 
1951  id = GetMenuItemID(hmenu, 0);
1952  ok (id == -1, "Getting the menuitem id unexpectedly worked (gave %x)\n", id);
1953 
1954  /* Confirm the menuitem itself was given an id the same as the HMENU, (getting by position) */
1955  memset( &info, 0, sizeof info );
1956  strback[0] = 0x00;
1957  info.cbSize = sizeof(MENUITEMINFOA);
1958  info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1959  info.dwTypeData = strback;
1960  info.cch = sizeof(strback);
1961  info.wID = 0xdeadbeef;
1962 
1963  rc = GetMenuItemInfoA(hmenu, 0, TRUE, &info); /* Get by position */
1964  ok (rc, "Getting the menu items info failed\n");
1965  ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the menuitem\n");
1966  ok (!strcmp(info.dwTypeData, "Case 2 SubMenu"), "Returned item has wrong label\n");
1967 
1968  /* Search by id - returns the popup menu itself */
1969  memset( &info, 0, sizeof info );
1970  strback[0] = 0x00;
1971  info.cbSize = sizeof(MENUITEMINFOA);
1972  info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1973  info.dwTypeData = strback;
1974  info.cch = sizeof(strback);
1975  rc = GetMenuItemInfoA(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
1976 
1977  ok (rc, "Getting the menu items info failed\n");
1978  ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
1979  ok (!strcmp(info.dwTypeData, "Case 2 SubMenu"), "Returned item has wrong label\n");
1980 
1981  /*
1982  Now add an item after it with the same id
1983  */
1984  memset( &info, 0, sizeof info );
1985  info.cbSize = sizeof info;
1986  info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
1987  info.fType = MFT_STRING;
1988  strcpy(strIn, "Case 2 MenuItem 1");
1989  info.dwTypeData = strIn;
1990  info.wID = (UINT_PTR) hmenuSub;
1991  rc = InsertMenuItemA(hmenu, -1, TRUE, &info );
1992  ok (rc, "Inserting the menuitem failed\n");
1993 
1994  /* Search by id - returns the item which follows the popup menu */
1995  memset( &info, 0, sizeof info );
1996  strback[0] = 0x00;
1997  info.cbSize = sizeof(MENUITEMINFOA);
1998  info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
1999  info.dwTypeData = strback;
2000  info.cch = sizeof(strback);
2001  rc = GetMenuItemInfoA(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
2002 
2003  ok (rc, "Getting the menu items info failed\n");
2004  ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
2005  ok (!strcmp(info.dwTypeData, "Case 2 MenuItem 1"), "Returned item has wrong label (%s)\n", info.dwTypeData);
2006 
2007  /*
2008  Now add an item before the popup (with the same id)
2009  */
2010  memset( &info, 0, sizeof info );
2011  info.cbSize = sizeof info;
2012  info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
2013  info.fType = MFT_STRING;
2014  strcpy(strIn, "Case 2 MenuItem 2");
2015  info.dwTypeData = strIn;
2016  info.wID = (UINT_PTR) hmenuSub;
2017  rc = InsertMenuItemA(hmenu, 0, TRUE, &info );
2018  ok (rc, "Inserting the menuitem failed\n");
2019 
2020  /* Search by id - returns the item which precedes the popup menu */
2021  memset( &info, 0, sizeof info );
2022  strback[0] = 0x00;
2023  info.cbSize = sizeof(MENUITEMINFOA);
2024  info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
2025  info.dwTypeData = strback;
2026  info.cch = sizeof(strback);
2027  rc = GetMenuItemInfoA(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
2028 
2029  ok (rc, "Getting the menu items info failed\n");
2030  ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
2031  ok (!strcmp(info.dwTypeData, "Case 2 MenuItem 2"), "Returned item has wrong label (%s)\n", info.dwTypeData);
2032 
2033  DestroyMenu( hmenu );
2034  DestroyMenu( hmenuSub );
2035 
2036  /*
2037  Case 3: Menu containing a popup menu which in turn
2038  contains 2 items with the same id as the popup itself
2039  */
2040 
2041  hmenu = CreateMenu();
2042  hmenuSub = CreateMenu();
2043 
2044  memset( &info, 0, sizeof info );
2045  info.cbSize = sizeof info;
2046  info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
2047  info.fType = MFT_STRING;
2048  info.dwTypeData = menuitem;
2049  info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
2050 
2051  rc = InsertMenuA(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
2052  ok (rc, "Inserting the popup menu into the main menu failed\n");
2053 
2054  rc = InsertMenuItemA(hmenuSub, 0, TRUE, &info );
2055  ok (rc, "Inserting the sub menu menuitem failed\n");
2056 
2057  memset( &info, 0, sizeof info );
2058  info.cbSize = sizeof info;
2059  info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
2060  info.fType = MFT_STRING;
2061  info.dwTypeData = menuitem2;
2062  info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
2063 
2064  rc = InsertMenuItemA(hmenuSub, 1, TRUE, &info );
2065  ok (rc, "Inserting the sub menu menuitem 2 failed\n");
2066 
2067  /* Prove that you can't query the id of a popup directly (By position) */
2068  id = GetMenuItemID(hmenu, 0);
2069  ok (id == -1, "Getting the sub menu id should have failed because it's a popup (gave %x)\n", id);
2070 
2071  /* Prove getting the item info via ID returns the first item (not the popup or 2nd item)*/
2072  memset( &info, 0, sizeof info );
2073  strback[0] = 0x00;
2074  info.cbSize = sizeof(MENUITEMINFOA);
2075  info.fMask = MIIM_STRING | MIIM_ID;
2076  info.dwTypeData = strback;
2077  info.cch = sizeof(strback);
2078 
2079  rc = GetMenuItemInfoA(hmenu, (UINT_PTR)hmenuSub, FALSE, &info);
2080  ok (rc, "Getting the menus info failed\n");
2081  ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for popup menu\n");
2082  ok (!strcmp(info.dwTypeData, "MenuItem"), "Returned item has wrong label (%s)\n", info.dwTypeData);
2083  DestroyMenu( hmenu );
2084  DestroyMenu( hmenuSub );
2085 
2086  /*
2087  Case 4: Menu containing 2 popup menus, the second
2088  contains 2 items with the same id as the first popup menu
2089  */
2090  hmenu = CreateMenu();
2091  hmenuSub = CreateMenu();
2092  hmenuSub2 = CreateMenu();
2093 
2094  rc = InsertMenuA(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
2095  ok (rc, "Inserting the popup menu into the main menu failed\n");
2096 
2097  rc = InsertMenuA(hmenu, 1, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub2, "Submenu2");
2098  ok (rc, "Inserting the popup menu into the main menu failed\n");
2099 
2100  memset( &info, 0, sizeof info );
2101  info.cbSize = sizeof info;
2102  info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
2103  info.fType = MFT_STRING;
2104  info.dwTypeData = menuitem;
2105  info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
2106 
2107  rc = InsertMenuItemA(hmenuSub2, 0, TRUE, &info );
2108  ok (rc, "Inserting the sub menu menuitem failed\n");
2109 
2110  memset( &info, 0, sizeof info );
2111  info.cbSize = sizeof info;
2112  info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
2113  info.fType = MFT_STRING;
2114  info.dwTypeData = menuitem2;
2115  info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
2116 
2117  rc = InsertMenuItemA(hmenuSub2, 1, TRUE, &info );
2118  ok (rc, "Inserting the sub menu menuitem 2 failed\n");
2119 
2120  /* Prove getting the item info via ID returns the first item (not the popup or 2nd item)*/
2121  memset( &info, 0, sizeof info );
2122  strback[0] = 0x00;
2123  info.cbSize = sizeof(MENUITEMINFOA);
2124  info.fMask = MIIM_STRING | MIIM_ID;
2125  info.dwTypeData = strback;
2126  info.cch = sizeof(strback);
2127 
2128  rc = GetMenuItemInfoA(hmenu, (UINT_PTR)hmenuSub, FALSE, &info);
2129  ok (rc, "Getting the menus info failed\n");
2130  ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for popup menu\n");
2131  ok (!strcmp(info.dwTypeData, "MenuItem"), "Returned item has wrong label (%s)\n", info.dwTypeData);
2132 
2133  memset( &info, 0, sizeof info );
2134  strback[0] = 0x00;
2135  info.cbSize = sizeof(MENUITEMINFOA);
2136  info.fMask = MIIM_STRING | MIIM_ID;
2137  info.dwTypeData = strback;
2138  info.cch = sizeof(strback);
2139 
2140  rc = GetMenuItemInfoA(hmenu, (UINT_PTR)hmenuSub2, FALSE, &info);
2141  ok (rc, "Getting the menus info failed\n");
2142  ok (info.wID == (UINT_PTR)hmenuSub2, "IDs differ for popup menu\n");
2143  ok (!strcmp(info.dwTypeData, "Submenu2"), "Returned item has wrong label (%s)\n", info.dwTypeData);
2144 
2145  DestroyMenu( hmenu );
2146  DestroyMenu( hmenuSub );
2147  DestroyMenu( hmenuSub2 );
2148 
2149 
2150  /*
2151  Case 5: Menu containing a popup menu which in turn
2152  contains an item with a different id than the popup menu.
2153  This tests the fallback to a popup menu ID.
2154  */
2155 
2156  hmenu = CreateMenu();
2157  hmenuSub = CreateMenu();
2158 
2159  rc = AppendMenuA(hmenu, MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
2160  ok (rc, "Appending the popup menu to the main menu failed\n");
2161 
2162  rc = AppendMenuA(hmenuSub, MF_STRING, 102, "Item");
2163  ok (rc, "Appending the item to the popup menu failed\n");
2164 
2165  /* Set the ID for hmenuSub */
2166  info.cbSize = sizeof(info);
2167  info.fMask = MIIM_ID;
2168  info.wID = 101;
2169 
2170  rc = SetMenuItemInfoA(hmenu, 0, TRUE, &info);
2171  ok(rc, "Setting the ID for the popup menu failed\n");
2172 
2173  /* Check if the ID has been set */
2174  info.wID = 0;
2175  rc = GetMenuItemInfoA(hmenu, 0, TRUE, &info);
2176  ok(rc, "Getting the ID for the popup menu failed\n");
2177  ok(info.wID == 101, "The ID for the popup menu has not been set\n");
2178 
2179  /* Prove getting the item info via ID returns the popup menu */
2180  memset( &info, 0, sizeof(info));
2181  strback[0] = 0x00;
2182  info.cbSize = sizeof(MENUITEMINFOA);
2183  info.fMask = MIIM_STRING | MIIM_ID;
2184  info.dwTypeData = strback;
2185  info.cch = sizeof(strback);
2186 
2187  rc = GetMenuItemInfoA(hmenu, 101, FALSE, &info);
2188  ok (rc, "Getting the menu info failed\n");
2189  ok (info.wID == 101, "IDs differ\n");
2190  ok (!strcmp(info.dwTypeData, "Submenu"), "Returned item has wrong label (%s)\n", info.dwTypeData);
2191 
2192  /* Also look for the menu item */
2193  memset( &info, 0, sizeof(info));
2194  strback[0] = 0x00;
2195  info.cbSize = sizeof(MENUITEMINFOA);
2196  info.fMask = MIIM_STRING | MIIM_ID;
2197  info.dwTypeData = strback;
2198  info.cch = sizeof(strback);
2199 
2200  rc = GetMenuItemInfoA(hmenu, 102, FALSE, &info);
2201  ok (rc, "Getting the menu info failed\n");
2202  ok (info.wID == 102, "IDs differ\n");
2203  ok (!strcmp(info.dwTypeData, "Item"), "Returned item has wrong label (%s)\n", info.dwTypeData);
2204 
2205  DestroyMenu(hmenu);
2206  DestroyMenu(hmenuSub);
2207 }
2208 
2210  UINT uMenu; /* 1 - top level menu, [0-Menu 1-Enabled 2-Disabled]
2211  * 2 - 2nd level menu, [0-Popup 1-Enabled 2-Disabled]
2212  * 3 - 3rd level menu, [0-Enabled 1-Disabled] */
2214 };
2215 
2216 static struct menu_mouse_tests_s {
2218  struct menu_item_pair_s menu_item_pairs[5]; /* for mousing */
2219  WORD wVk[5]; /* keys */
2222 } menu_tests[] = {
2223  /* for each test, send keys or clicks and check for menu visibility */
2224  { INPUT_KEYBOARD, {{0}}, {VK_MENU, 0}, TRUE, FALSE }, /* test 0 */
2225  { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, 0}, FALSE, FALSE },
2226  { INPUT_KEYBOARD, {{0}}, {VK_MENU, 0}, TRUE, FALSE },
2227  { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
2228  { INPUT_KEYBOARD, {{0}}, {VK_MENU, 0}, TRUE, FALSE },
2229  { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
2230  { INPUT_KEYBOARD, {{0}}, {VK_MENU, 'M', 0}, TRUE, FALSE },
2231  { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, VK_ESCAPE, 0}, FALSE, FALSE },
2232  { INPUT_KEYBOARD, {{0}}, {VK_MENU, 'M', VK_ESCAPE, 0}, TRUE, FALSE },
2233  { INPUT_KEYBOARD, {{0}}, {VK_ESCAPE, 0}, FALSE, FALSE },
2234  { INPUT_KEYBOARD, {{0}}, {VK_MENU, 'M', 0}, TRUE, FALSE },
2235  { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
2236  { INPUT_KEYBOARD, {{0}}, {VK_MENU, 'M', 0}, TRUE, FALSE },
2237  { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
2238  { INPUT_KEYBOARD, {{0}}, {VK_MENU, 'M', 'P', 0}, TRUE, FALSE },
2239  { INPUT_KEYBOARD, {{0}}, {'D', 0}, FALSE, FALSE },
2240  { INPUT_KEYBOARD, {{0}}, {VK_MENU, 'M', 'P', 0}, TRUE, FALSE },
2241  { INPUT_KEYBOARD, {{0}}, {'E', 0}, FALSE, FALSE },
2242  { INPUT_KEYBOARD, {{0}}, {VK_F10, 0}, TRUE, FALSE },
2243  { INPUT_KEYBOARD, {{0}}, {VK_F10, 0}, FALSE, FALSE },
2244 
2245  { INPUT_MOUSE, {{1, 2}, {0}}, {0}, TRUE, FALSE }, /* test 20 */
2246  { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
2247  { INPUT_MOUSE, {{1, 0}, {0}}, {0}, TRUE, FALSE },
2248  { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
2249  { INPUT_MOUSE, {{1, 0}, {2, 2}, {0}}, {0}, TRUE, FALSE },
2250  { INPUT_MOUSE, {{2, 1}, {0}}, {0}, FALSE, FALSE },
2251  { INPUT_MOUSE, {{1, 0}, {2, 0}, {0}}, {0}, TRUE, FALSE },
2252  { INPUT_MOUSE, {{3, 0}, {0}}, {0}, FALSE, FALSE },
2253  { INPUT_MOUSE, {{1, 0}, {2, 0}, {0}}, {0}, TRUE, FALSE },
2254  { INPUT_MOUSE, {{3, 1}, {0}}, {0}, TRUE, FALSE },
2255  { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
2256  { -1 }
2257 };
2258 
2259 static void send_key(WORD wVk)
2260 {
2261  TEST_INPUT i[2];
2262  memset(i, 0, sizeof(i));
2263  i[0].type = i[1].type = INPUT_KEYBOARD;
2264  i[0].u.ki.wVk = i[1].u.ki.wVk = wVk;
2265  i[1].u.ki.dwFlags = KEYEVENTF_KEYUP;
2266  SendInput(2, (INPUT *) i, sizeof(INPUT));
2267 }
2268 
2270 {
2271  HMENU hMenu = hMenus[mi->uMenu];
2272  TEST_INPUT i[3];
2273  MSG msg;
2274  RECT r;
2275  int screen_w = GetSystemMetrics(SM_CXSCREEN);
2276  int screen_h = GetSystemMetrics(SM_CYSCREEN);
2277  BOOL ret = GetMenuItemRect(mi->uMenu > 2 ? NULL : hWnd, hMenu, mi->uItem, &r);
2278  if(!ret) return FALSE;
2279 
2280  memset(i, 0, sizeof(i));
2281  i[0].type = i[1].type = i[2].type = INPUT_MOUSE;
2282  i[0].u.mi.dx = i[1].u.mi.dx = i[2].u.mi.dx
2283  = ((r.left + 5) * 65535) / screen_w;
2284  i[0].u.mi.dy = i[1].u.mi.dy = i[2].u.mi.dy
2285  = ((r.top + 5) * 65535) / screen_h;
2286  i[0].u.mi.dwFlags = i[1].u.mi.dwFlags = i[2].u.mi.dwFlags
2288  i[0].u.mi.dwFlags |= MOUSEEVENTF_MOVE;
2289  i[1].u.mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
2290  i[2].u.mi.dwFlags |= MOUSEEVENTF_LEFTUP;
2291  ret = SendInput(3, (INPUT *) i, sizeof(INPUT));
2292 
2293  /* hack to prevent mouse message buildup in Wine */
2294  while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
2295  return ret;
2296 }
2297 
2299 {
2300  int i, j;
2302 
2303  Sleep(500);
2304  /* mixed keyboard/mouse test */
2305  for (i = 0; menu_tests[i].type != -1; i++)
2306  {
2307  BOOL ret = TRUE;
2308  int elapsed;
2309 
2310  got_input = i && menu_tests[i-1].bMenuVisible;
2311 
2312  if (menu_tests[i].type == INPUT_KEYBOARD)
2313  for (j = 0; menu_tests[i].wVk[j] != 0; j++)
2314  send_key(menu_tests[i].wVk[j]);
2315  else
2316  for (j = 0; menu_tests[i].menu_item_pairs[j].uMenu != 0; j++)
2317  {
2318  /* Maybe clicking too fast before menu is initialized. Sleep 100 ms and retry */
2319  elapsed = 0;
2320  while (!(ret = click_menu(hWnd, &menu_tests[i].menu_item_pairs[j])))
2321  {
2322  if (elapsed > 1000) break;
2323  elapsed += 100;
2324  Sleep(100);
2325  }
2326  }
2327 
2328  if (!ret)
2329  {
2330  skip( "test %u: failed to send input\n", i );
2331  PostMessageA( hWnd, WM_CANCELMODE, 0, 0 );
2332  return 0;
2333  }
2334 
2335  elapsed = 0;
2337  {
2338  if (elapsed > 200)
2339  break;
2340  elapsed += 20;
2341  Sleep(20);
2342  }
2343 
2344  if (!got_input)
2345  {
2346  skip( "test %u: didn't receive input\n", i );
2347  PostMessageA( hWnd, WM_CANCELMODE, 0, 0 );
2348  return 0;
2349  }
2350 
2351  todo_wine_if (menu_tests[i]._todo_wine)
2352  ok(menu_tests[i].bMenuVisible == bMenuVisible, "test %d\n", i);
2353  }
2354  return 0;
2355 }
2356 
2358  LPARAM lParam)
2359 {
2360  MENUBARINFO mbi;
2361  HMENU hmenu;
2362  UINT state;
2363  BOOL br;
2364 
2365  switch (msg) {
2366  case WM_ENTERMENULOOP:
2367  bMenuVisible = TRUE;
2368  break;
2369  case WM_INITMENUPOPUP:
2370  case WM_UNINITMENUPOPUP:
2371  case WM_EXITMENULOOP:
2372  case WM_MENUSELECT:
2373  break;
2374 
2375  case WM_KEYDOWN:
2376  case WM_SYSKEYDOWN:
2377  case WM_MOUSEMOVE:
2378  case WM_LBUTTONDOWN:
2379  case WM_LBUTTONUP:
2380  case WM_NCMOUSEMOVE:
2381  case WM_NCLBUTTONDOWN:
2382  case WM_NCLBUTTONUP:
2383  got_input = TRUE;
2384  /* fall through */
2385  default:
2386  return( DefWindowProcA( hWnd, msg, wParam, lParam ) );
2387  }
2388 
2389  mbi.cbSize = sizeof(MENUBARINFO);
2390 
2391  /* get info for the menu */
2392  br = GetMenuBarInfo(hWnd, OBJID_MENU, 0, &mbi);
2393  ok(br, "msg %x: GetMenuBarInfo failed\n", msg);
2394  hmenu = GetMenu(hWnd);
2395  ok(!mbi.hwndMenu, "msg %x: GetMenuBarInfo.hwndMenu wrong: %p expected NULL\n",
2396  msg, mbi.hwndMenu);
2397  ok(mbi.hMenu == hmenu, "msg %x: GetMenuBarInfo got wrong menu: %p expected %p\n",
2398  msg, mbi.hMenu, hmenu);
2399  ok(!bMenuVisible == !mbi.fBarFocused, "msg %x: GetMenuBarInfo.fBarFocused (%d) is wrong\n",
2400  msg, mbi.fBarFocused != 0);
2401  ok(!bMenuVisible == !mbi.fFocused, "msg %x: GetMenuBarInfo.fFocused (%d) is wrong\n",
2402  msg, mbi.fFocused != 0);
2403 
2404  /* get info for the menu's first item */
2405  br = GetMenuBarInfo(hWnd, OBJID_MENU, 1, &mbi);
2406  ok(br, "msg %x: GetMenuBarInfo failed\n", msg);
2408  /* Native returns handle to destroyed window */
2409  todo_wine_if (msg==WM_UNINITMENUPOPUP && popmenu==1)
2410  ok(!mbi.hwndMenu == !popmenu,
2411  "msg %x: GetMenuBarInfo.hwndMenu wrong: %p expected %sNULL\n",
2412  msg, mbi.hwndMenu, popmenu ? "not " : "");
2413  ok(mbi.hMenu == hmenu, "msg %x: GetMenuBarInfo got wrong menu: %p expected %p\n",
2414  msg, mbi.hMenu, hmenu);
2415  ok(!bMenuVisible == !mbi.fBarFocused, "nsg %x: GetMenuBarInfo.fBarFocused (%d) is wrong\n",
2416  msg, mbi.fBarFocused != 0);
2417  ok(!(bMenuVisible && (state & MF_HILITE)) == !mbi.fFocused,
2418  "msg %x: GetMenuBarInfo.fFocused (%d) is wrong\n", msg, mbi.fFocused != 0);
2419 
2420  if (msg == WM_EXITMENULOOP)
2421  bMenuVisible = FALSE;
2422  else if (msg == WM_INITMENUPOPUP)
2423  popmenu++;
2424  else if (msg == WM_UNINITMENUPOPUP)
2425  popmenu--;
2426  return 0;
2427 }
2428 
2429 static void test_menu_input(void) {
2430  MSG msg;
2431  WNDCLASSA wclass;
2433  HANDLE hThread, hWnd;
2434  DWORD tid;
2435  ATOM aclass;
2436  POINT orig_pos;
2437 
2438  wclass.lpszClassName = "MenuTestClass";
2439  wclass.style = CS_HREDRAW | CS_VREDRAW;
2440  wclass.lpfnWndProc = WndProc;
2441  wclass.hInstance = hInstance;
2442  wclass.hIcon = LoadIconA( 0, (LPCSTR)IDI_APPLICATION );
2443  wclass.hCursor = LoadCursorA( 0, (LPCSTR)IDC_ARROW );
2444  wclass.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1 );
2445  wclass.lpszMenuName = 0;
2446  wclass.cbClsExtra = 0;
2447  wclass.cbWndExtra = 0;
2448  aclass = RegisterClassA( &wclass );
2449  ok (aclass, "MenuTest class not created\n");
2450  if (!aclass) return;
2451  hWnd = CreateWindowA( wclass.lpszClassName, "MenuTest",
2453  400, 200, NULL, NULL, hInstance, NULL);
2454  ok (hWnd != NULL, "MenuTest window not created\n");
2455  if (!hWnd) return;
2456  /* fixed menus */
2457  hMenus[3] = CreatePopupMenu();
2458  AppendMenuA(hMenus[3], MF_STRING, 0, "&Enabled");
2459  AppendMenuA(hMenus[3], MF_STRING|MF_DISABLED, 0, "&Disabled");
2460 
2461  hMenus[2] = CreatePopupMenu();
2462  AppendMenuA(hMenus[2], MF_STRING|MF_POPUP, (UINT_PTR) hMenus[3], "&Popup");
2463  AppendMenuA(hMenus[2], MF_STRING, 0, "&Enabled");
2464  AppendMenuA(hMenus[2], MF_STRING|MF_DISABLED, 0, "&Disabled");
2465 
2466  hMenus[1] = CreateMenu();
2467  AppendMenuA(hMenus[1], MF_STRING|MF_POPUP, (UINT_PTR) hMenus[2], "&Menu");
2468  AppendMenuA(hMenus[1], MF_STRING, 0, "&Enabled");
2469  AppendMenuA(hMenus[1], MF_STRING|MF_DISABLED, 0, "&Disabled");
2470 
2471  SetMenu(hWnd, hMenus[1]);
2473  UpdateWindow(hWnd);
2474 
2475  GetCursorPos(&orig_pos);
2476 
2478  while(1)
2479  {
2481  break;
2482  while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
2483  }
2484  SetCursorPos(orig_pos.x, orig_pos.y);
2486 }
2487 
2488 static void test_menu_flags( void )
2489 {
2490  HMENU hMenu, hPopupMenu;
2491 
2492  hMenu = CreateMenu();
2493  hPopupMenu = CreatePopupMenu();
2494 
2495  AppendMenuA(hMenu, MF_POPUP | MF_STRING, (UINT_PTR)hPopupMenu, "Popup");
2496 
2497  AppendMenuA(hPopupMenu, MF_STRING | MF_HILITE | MF_DEFAULT, 101, "Item 1");
2498  InsertMenuA(hPopupMenu, 1, MF_BYPOSITION | MF_STRING | MF_HILITE | MF_DEFAULT, 102, "Item 2");
2499  AppendMenuA(hPopupMenu, MF_STRING, 103, "Item 3");
2500  ModifyMenuA(hPopupMenu, 2, MF_BYPOSITION | MF_STRING | MF_HILITE | MF_DEFAULT, 103, "Item 3");
2501 
2502  ok(GetMenuState(hPopupMenu, 0, MF_BYPOSITION) & MF_HILITE,
2503  "AppendMenu should accept MF_HILITE\n");
2504  ok(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE,
2505  "InsertMenu should accept MF_HILITE\n");
2506  ok(GetMenuState(hPopupMenu, 2, MF_BYPOSITION) & MF_HILITE,
2507  "ModifyMenu should accept MF_HILITE\n");
2508 
2509  ok(!(GetMenuState(hPopupMenu, 0, MF_BYPOSITION) & MF_DEFAULT),
2510  "AppendMenu must not accept MF_DEFAULT\n");
2511  ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_DEFAULT),
2512  "InsertMenu must not accept MF_DEFAULT\n");
2513  ok(!(GetMenuState(hPopupMenu, 2, MF_BYPOSITION) & MF_DEFAULT),
2514  "ModifyMenu must not accept MF_DEFAULT\n");
2515 
2516  DestroyMenu(hMenu);
2517 }
2518 
2519 static void test_menu_hilitemenuitem( void )
2520 {
2521  HMENU hMenu, hPopupMenu;
2522  WNDCLASSA wclass;
2523  HWND hWnd;
2524  ATOM aclass;
2525 
2526  wclass.lpszClassName = "HiliteMenuTestClass";
2527  wclass.style = CS_HREDRAW | CS_VREDRAW;
2528  wclass.lpfnWndProc = WndProc;
2529  wclass.hInstance = GetModuleHandleA( NULL );
2530  wclass.hIcon = LoadIconA( 0, (LPCSTR)IDI_APPLICATION );
2531  wclass.hCursor = LoadCursorA( 0, (LPCSTR)IDC_ARROW );
2532  wclass.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1 );
2533  wclass.lpszMenuName = 0;
2534  wclass.cbClsExtra = 0;
2535  wclass.cbWndExtra = 0;
2536  aclass = RegisterClassA( &wclass );
2537  ok (aclass, "HiliteMenuTest class could not be created\n");
2538  if (!aclass) return;
2539  hWnd = CreateWindowA( wclass.lpszClassName, "HiliteMenuTest",
2541  400, 200, NULL, NULL, wclass.hInstance, NULL);
2542  ok (hWnd != NULL, "HiliteMenuTest window could not be created\n");
2543  if (!hWnd) return;
2544 
2545  hMenu = CreateMenu();
2546  hPopupMenu = CreatePopupMenu();
2547 
2548  AppendMenuA(hMenu, MF_POPUP | MF_STRING, (UINT_PTR)hPopupMenu, "Popup");
2549 
2550  AppendMenuA(hPopupMenu, MF_STRING, 101, "Item 1");
2551  AppendMenuA(hPopupMenu, MF_STRING, 102, "Item 2");
2552  AppendMenuA(hPopupMenu, MF_STRING, 103, "Item 3");
2553 
2554  SetMenu(hWnd, hMenu);
2555 
2556  /* test invalid arguments */
2557 
2558  ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE),
2559  "HiliteMenuItem: Item 2 is hilited\n");
2560 
2561  SetLastError(0xdeadbeef);
2562  todo_wine
2563  {
2564  ok(!HiliteMenuItem(NULL, hPopupMenu, 1, MF_HILITE | MF_BYPOSITION),
2565  "HiliteMenuItem: call should have failed.\n");
2566  }
2567  ok(GetLastError() == 0xdeadbeef || /* 9x */
2569  "HiliteMenuItem: expected error ERROR_INVALID_WINDOW_HANDLE, got: %d\n", GetLastError());
2570 
2571  SetLastError(0xdeadbeef);
2573  "HiliteMenuItem: call should have failed.\n");
2574  ok(GetLastError() == 0xdeadbeef || /* 9x */
2576  "HiliteMenuItem: expected error ERROR_INVALID_MENU_HANDLE, got: %d\n", GetLastError());
2577 
2578  ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE),
2579  "HiliteMenuItem: Item 2 is hilited\n");
2580 
2581  /* either MF_HILITE or MF_UNHILITE *and* MF_BYCOMMAND or MF_BYPOSITION need to be set */
2582 
2583  SetLastError(0xdeadbeef);
2584  ok(HiliteMenuItem(hWnd, hPopupMenu, 1, MF_BYPOSITION),
2585  "HiliteMenuItem: call should have succeeded.\n");
2586  ok(GetLastError() == 0xdeadbeef,
2587  "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
2588 
2589  ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE),
2590  "HiliteMenuItem: Item 2 is hilited\n");
2591 
2592  SetLastError(0xdeadbeef);
2593  todo_wine
2594  {
2595  ok(HiliteMenuItem(hWnd, hPopupMenu, 1, MF_HILITE),
2596  "HiliteMenuItem: call should have succeeded.\n");
2597  }
2598  ok(GetLastError() == 0xdeadbeef,
2599  "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
2600 
2601  ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE),
2602  "HiliteMenuItem: Item 2 is hilited\n");
2603 
2604  /* hilite a menu item (by position) */
2605 
2606  SetLastError(0xdeadbeef);
2607  ok(HiliteMenuItem(hWnd, hPopupMenu, 1, MF_HILITE | MF_BYPOSITION),
2608  "HiliteMenuItem: call should not have failed.\n");
2609  ok(GetLastError() == 0xdeadbeef,
2610  "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
2611 
2612  todo_wine
2613  {
2614  ok(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE,
2615  "HiliteMenuItem: Item 2 is not hilited\n");
2616  }
2617 
2618  /* unhilite a menu item (by position) */
2619 
2620  SetLastError(0xdeadbeef);
2621  ok(HiliteMenuItem(hWnd, hPopupMenu, 1, MF_UNHILITE | MF_BYPOSITION),
2622  "HiliteMenuItem: call should not have failed.\n");
2623  ok(GetLastError() == 0xdeadbeef,
2624  "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
2625 
2626  ok(!(GetMenuState(hPopupMenu, 1, MF_BYPOSITION) & MF_HILITE),
2627  "HiliteMenuItem: Item 2 is hilited\n");
2628 
2629  /* hilite a menu item (by command) */
2630 
2631  SetLastError(0xdeadbeef);
2632  ok(HiliteMenuItem(hWnd, hPopupMenu, 103, MF_HILITE | MF_BYCOMMAND),
2633  "HiliteMenuItem: call should not have failed.\n");
2634  ok(GetLastError() == 0xdeadbeef,
2635  "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
2636 
2637  todo_wine
2638  {
2639  ok(GetMenuState(hPopupMenu, 2, MF_BYPOSITION) & MF_HILITE,
2640  "HiliteMenuItem: Item 3 is not hilited\n");
2641  }
2642 
2643  /* unhilite a menu item (by command) */
2644 
2645  SetLastError(0xdeadbeef);
2646  ok(HiliteMenuItem(hWnd, hPopupMenu, 103, MF_UNHILITE | MF_BYCOMMAND),
2647  "HiliteMenuItem: call should not have failed.\n");
2648  ok(GetLastError() == 0xdeadbeef,
2649  "HiliteMenuItem: expected error 0xdeadbeef, got: %d\n", GetLastError());
2650 
2651  ok(!(GetMenuState(hPopupMenu, 2, MF_BYPOSITION) & MF_HILITE),
2652  "HiliteMenuItem: Item 3 is hilited\n");
2653 
2655 }
2656 
2657 static void check_menu_items(HMENU hmenu, UINT checked_cmd, UINT checked_type,
2658  UINT checked_state)
2659 {
2660  INT i, count;
2661 
2663  ok (count != -1, "GetMenuItemCount returned -1\n");
2664 
2665  for (i = 0; i < count; i++)
2666  {
2667  BOOL ret;
2668  MENUITEMINFOA mii;
2669 
2670  memset(&mii, 0, sizeof(mii));
2671  mii.cbSize = sizeof(mii);
2673  ret = GetMenuItemInfoA(hmenu, i, TRUE, &mii);
2674  ok(ret, "GetMenuItemInfo(%u) failed\n", i);
2675 
2676  if (winetest_debug > 1)
2677  trace("item #%u: fType %04x, fState %04x, wID %u, hSubMenu %p\n",
2678  i, mii.fType, mii.fState, mii.wID, mii.hSubMenu);
2679 
2680  if (mii.hSubMenu)
2681  {
2682  ok(mii.wID == (UINT_PTR)mii.hSubMenu, "id %u: wID %x should be equal to hSubMenu %p\n",
2683  checked_cmd, mii.wID, mii.hSubMenu);
2684  if (!GetMenuItemCount(mii.hSubMenu))
2685  {
2686  ok(mii.fType == checked_type, "id %u: expected fType %04x, got %04x\n", checked_cmd, checked_type, mii.fType);
2687  ok(mii.fState == checked_state, "id %u: expected fState %04x, got %04x\n", checked_cmd, checked_state, mii.fState);
2688  }
2689  check_menu_items(mii.hSubMenu, checked_cmd, checked_type, checked_state);
2690  }
2691  else
2692  {
2693  if (mii.wID == checked_cmd)
2694  {
2695  ok(mii.fType == checked_type, "id %u: expected fType %04x, got %04x\n", checked_cmd, checked_type, mii.fType);
2696  ok(mii.fState == checked_state, "id %u: expected fState %04x, got %04x\n", checked_cmd, checked_state, mii.fState);
2697  ok(mii.wID != 0, "id %u: not expected wID 0\n", checked_cmd);
2698  }
2699  else
2700  {
2701  ok(mii.fType != MFT_RADIOCHECK, "id %u: not expected fType MFT_RADIOCHECK on cmd %u\n", checked_cmd, mii.wID);
2702 
2703  if (mii.fType == MFT_SEPARATOR)
2704  {
2705  ok(mii.fState == MFS_GRAYED, "id %u: expected fState MFS_GRAYED, got %04x\n", checked_cmd, mii.fState);
2706  ok(mii.wID == 0, "id %u: expected wID 0, got %u\n", checked_cmd, mii.wID);
2707  }
2708  else
2709  {
2710  ok(mii.fState == 0, "id %u: expected fState 0, got %04x\n", checked_cmd, mii.fState);
2711  ok(mii.wID != 0, "id %u: not expected wID 0\n", checked_cmd);
2712  }
2713  }
2714  }
2715  }
2716 }
2717 
2719 {
2720  BOOL ret;
2721  MENUITEMINFOA mii;
2722 
2723  memset(&mii, 0, sizeof(mii));
2724  mii.cbSize = sizeof(mii);
2725  mii.fMask = MIIM_FTYPE | MIIM_STATE;
2726  ret = SetMenuItemInfoA(hmenu, id, (flags & MF_BYPOSITION) != 0, &mii);
2727  ok(ret, "SetMenuItemInfo(%u) failed\n", id);
2728 }
2729 
2730 static void test_CheckMenuRadioItem(void)
2731 {
2732  BOOL ret;
2733  HMENU hmenu;
2734 
2736  assert(hmenu != 0);
2737 
2738  check_menu_items(hmenu, -1, 0, 0);
2739 
2740  ret = CheckMenuRadioItem(hmenu, 100, 100, 100, MF_BYCOMMAND);
2741  ok(ret, "CheckMenuRadioItem failed\n");
2743 
2744  /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
2745  ret = CheckMenuRadioItem(hmenu, 100, 100, -1, MF_BYCOMMAND);
2746  ok(!ret, "CheckMenuRadioItem should return FALSE\n");
2748 
2749  /* clear check */
2751  check_menu_items(hmenu, -1, 0, 0);
2752 
2753  /* first and checked items are on different menus */
2754  ret = CheckMenuRadioItem(hmenu, 0, 300, 202, MF_BYCOMMAND);
2755  ok(!ret, "CheckMenuRadioItem should return FALSE\n");
2756  check_menu_items(hmenu, -1, 0, 0);
2757 
2758  ret = CheckMenuRadioItem(hmenu, 200, 300, 202, MF_BYCOMMAND);
2759  ok(ret, "CheckMenuRadioItem failed\n");
2761 
2762  /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
2763  ret = CheckMenuRadioItem(hmenu, 202, 202, -1, MF_BYCOMMAND);
2764  ok(!ret, "CheckMenuRadioItem should return FALSE\n");
2766 
2767  /* clear check */
2769  check_menu_items(hmenu, -1, 0, 0);
2770 
2771  /* just for fun, try to check separator */
2772  ret = CheckMenuRadioItem(hmenu, 0, 300, 0, MF_BYCOMMAND);
2773  ok(!ret, "CheckMenuRadioItem should return FALSE\n");
2774  check_menu_items(hmenu, -1, 0, 0);
2775 }
2776 
2777 static void test_menu_resource_layout(void)
2778 {
2779  static const struct
2780  {
2782  WORD data[14];
2783  } menu_template =
2784  {
2785  { 0, 0 }, /* versionNumber, offset */
2786  {
2787  /* mtOption, mtID, mtString[] '\0' terminated */
2788  MF_STRING, 1, 'F', 0,
2789  MF_STRING, 2, 0,
2790  MF_SEPARATOR, 3, 0,
2791  /* MF_SEPARATOR, 4, 'S', 0, FIXME: Wine ignores 'S' */
2792  MF_STRING|MF_GRAYED|MF_END, 5, 'E', 0
2793  }
2794  };
2795  static const struct
2796  {
2797  UINT type, state, id;
2798  const char *str;
2799  } menu_data[] =
2800  {
2801  { MF_STRING, MF_ENABLED, 1, "F" },
2802  { MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 2, "" },
2803  { MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 3, "" },
2804  /*{ MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 4, "S" }, FIXME: Wine ignores 'S'*/
2805  { MF_STRING, MF_GRAYED, 5, "E" },
2806  { MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 6, "" },
2807  { MF_STRING, MF_ENABLED, 7, "" },
2808  { MF_SEPARATOR, MF_GRAYED|MF_DISABLED, 8, "" }
2809  };
2810  HMENU hmenu;
2811  INT count, i;
2812  BOOL ret;
2813 
2814  hmenu = LoadMenuIndirectA(&menu_template);
2815  ok(hmenu != 0, "LoadMenuIndirect error %u\n", GetLastError());
2816 
2818  ok(ret, "AppendMenu failed\n");
2819  ret = AppendMenuA(hmenu, MF_STRING, 7, "\0");
2820  ok(ret, "AppendMenu failed\n");
2821  ret = AppendMenuA(hmenu, MF_SEPARATOR, 8, "separator");
2822  ok(ret, "AppendMenu failed\n");
2823 
2825  ok(count == ARRAY_SIZE(menu_data), "expected %u menu items, got %u\n",
2827 
2828  for (i = 0; i < count; i++)
2829  {
2830  char buf[20];
2831  MENUITEMINFOA mii;
2832 
2833  memset(&mii, 0, sizeof(mii));
2834  mii.cbSize = sizeof(mii);
2835  mii.dwTypeData = buf;
2836  mii.cch = sizeof(buf);
2838  ret = GetMenuItemInfoA(hmenu, i, TRUE, &mii);
2839  ok(ret, "GetMenuItemInfo(%u) failed\n", i);
2840  if (winetest_debug > 1)
2841  trace("item #%u: fType %04x, fState %04x, wID %u, dwTypeData %s\n",
2842  i, mii.fType, mii.fState, mii.wID, (LPCSTR)mii.dwTypeData);
2843 
2844  ok(mii.fType == menu_data[i].type,
2845  "%u: expected fType %04x, got %04x\n", i, menu_data[i].type, mii.fType);
2846  ok(mii.fState == menu_data[i].state,
2847  "%u: expected fState %04x, got %04x\n", i, menu_data[i].state, mii.fState);
2848  ok(mii.wID == menu_data[i].id,
2849  "%u: expected wID %04x, got %04x\n", i, menu_data[i].id, mii.wID);
2850  ok(mii.cch == strlen(menu_data[i].str),
2851  "%u: expected cch %u, got %u\n", i, (UINT)strlen(menu_data[i].str), mii.cch);
2852  ok(!strcmp(mii.dwTypeData, menu_data[i].str),
2853  "%u: expected dwTypeData %s, got %s\n", i, menu_data[i].str, (LPCSTR)mii.dwTypeData);
2854  }
2855 
2856  DestroyMenu(hmenu);
2857 }
2858 
2860 {
2862  const char *str;
2863 };
2864 
2865 static HMENU create_menu_from_data(const struct menu_data *item, INT item_count)
2866 {
2867  HMENU hmenu;
2868  INT i;
2869  BOOL ret;
2870 
2871  hmenu = CreateMenu();
2872  assert(hmenu != 0);
2873 
2874  for (i = 0; i < item_count; i++)
2875  {
2876  SetLastError(0xdeadbeef);
2877  ret = AppendMenuA(hmenu, item[i].type, item[i].id, item[i].str);
2878  ok(ret, "%d: AppendMenu(%04x, %04x, %p) error %u\n",
2879  i, item[i].type, item[i].id, item[i].str, GetLastError());
2880  }
2881  return hmenu;
2882 }
2883 
2884 /* use InsertMenuItem: does not set the MFT_BITMAP flag,
2885  * and does not accept non-magic bitmaps with invalid
2886  * bitmap handles */
2887 static HMENU create_menuitem_from_data(const struct menu_data *item, INT item_count)
2888 {
2889  HMENU hmenu;
2890  INT i;
2891  BOOL ret;
2892  MENUITEMINFOA mii = { sizeof( MENUITEMINFOA) };
2893 
2894  hmenu = CreateMenu();
2895  assert(hmenu != 0);
2896 
2897  for (i = 0; i < item_count; i++)
2898  {
2899  SetLastError(0xdeadbeef);
2900 
2901  mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STATE;
2902  mii.fType = 0;
2903  if( item[i].type & MFT_BITMAP)
2904  {
2905  mii.fMask |= MIIM_BITMAP;
2906  mii.hbmpItem = (HBITMAP)item[i].str;
2907  }
2908  else if( item[i].type & MFT_SEPARATOR)
2909  mii.fType = MFT_SEPARATOR;
2910  else
2911  {
2912  mii.fMask |= MIIM_STRING;
2913  mii.dwTypeData = (LPSTR)item[i].str;
2914  mii.cch = strlen( item[i].str);
2915  }
2916  mii.fState = 0;
2917  if( item[i].type & MF_HELP) mii.fType |= MF_HELP;
2918  mii.wID = item[i].id;
2919  ret = InsertMenuItemA( hmenu, -1, TRUE, &mii);
2920  ok(ret, "%d: InsertMenuItem(%04x, %04x, %p) error %u\n",
2921  i, item[i].type, item[i].id, item[i].str, GetLastError());
2922  }
2923  return hmenu;
2924 }
2925 
2926 static void compare_menu_data(HMENU hmenu, const struct menu_data *item, INT item_count)
2927 {
2928  INT count, i;
2929  BOOL ret;
2930 
2932  ok(count == item_count, "expected %d, got %d menu items\n", count, item_count);
2933 
2934  for (i = 0; i < count; i++)
2935  {
2936  char buf[20];
2937  MENUITEMINFOA mii;
2938 
2939  memset(&mii, 0, sizeof(mii));
2940  mii.cbSize = sizeof(mii);
2941  mii.dwTypeData = buf;
2942  mii.cch = sizeof(buf);
2944  ret = GetMenuItemInfoA(hmenu, i, TRUE, &mii);
2945  ok(ret, "GetMenuItemInfo(%u) failed\n", i);
2946 
2947  if (winetest_debug > 1)
2948  trace("item #%u: fType %04x, fState %04x, wID %04x, hbmp %p\n",
2949  i, mii.fType, mii.fState, mii.wID, mii.hbmpItem);
2950 
2951  ok(mii.fType == item[i].type,
2952  "%u: expected fType %04x, got %04x\n", i, item[i].type, mii.fType);
2953  ok(mii.wID == item[i].id,
2954  "%u: expected wID %04x, got %04x\n", i, item[i].id, mii.wID);
2955  if (mii.hbmpItem || !item[i].str)
2956  /* For some reason Windows sets high word to not 0 for
2957  * not "magic" ids.
2958  */
2959  ok(LOWORD(mii.hbmpItem) == LOWORD(item[i].str),
2960  "%u: expected hbmpItem %p, got %p\n", i, item[i].str, mii.hbmpItem);
2961  else
2962  {
2963  ok(mii.cch == strlen(item[i].str),
2964  "%u: expected cch %u, got %u\n", i, (UINT)strlen(item[i].str), mii.cch);
2965  ok(!strcmp(mii.dwTypeData, item[i].str),
2966  "%u: expected dwTypeData %s, got %s\n", i, item[i].str, (LPCSTR)mii.dwTypeData);
2967  }
2968  }
2969 }
2970 
2971 static void test_InsertMenu(void)
2972 {
2973  HBITMAP hbm = CreateBitmap(1,1,1,1,NULL);
2974  /* Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2975  * regardless of their id.
2976  */
2977  static const struct menu_data in1[] =
2978  {
2979  { MF_STRING, 1, "File" },
2981  { MF_STRING|MF_HELP, 2, "Help" }
2982  };
2983  static const struct menu_data out1[] =
2984  {
2985  { MF_STRING, 1, "File" },
2986  { MF_STRING|MF_HELP, 2, "Help" },
2988  };
2989  static const struct menu_data out1a[] =
2990  {
2991  { MF_STRING, 1, "File" },
2992  { MF_STRING|MF_HELP, 2, "Help" },
2994  };
2995  const struct menu_data in2[] =
2996  {
2997  { MF_STRING, 1, "File" },
2998  { MF_BITMAP|MF_HELP, SC_CLOSE, (char*)hbm },
2999  { MF_STRING|MF_HELP, 2, "Help" }
3000  };
3001  const struct menu_data out2[] =
3002  {
3003  { MF_STRING, 1, "File" },
3004  { MF_BITMAP|MF_HELP, SC_CLOSE, (char*)hbm },
3005  { MF_STRING|MF_HELP, 2, "Help" }
3006  };
3007  const struct menu_data out2a[] =
3008  {
3009  { MF_STRING, 1, "File" },
3010  { MF_HELP, SC_CLOSE, (char*)hbm },
3011  { MF_STRING|MF_HELP, 2, "Help" }
3012  };
3013  static const struct menu_data in3[] =
3014  {
3015  { MF_STRING, 1, "File" },
3017  { MF_STRING|MF_HELP, 2, "Help" }
3018  };
3019  static const struct menu_data out3[] =
3020  {
3021  { MF_STRING, 1, "File" },
3023  { MF_STRING|MF_HELP, 2, "Help" },
3024  };
3025  static const struct menu_data in4[] =
3026  {
3027  { MF_STRING, 1, "File" },
3028  { MF_BITMAP|MF_HELP, 1, MAKEINTRESOURCEA(1) },
3029  { MF_STRING|MF_HELP, 2, "Help" }
3030  };
3031  static const struct menu_data out4[] =
3032  {
3033  { MF_STRING, 1, "File" },
3034  { MF_STRING|MF_HELP, 2, "Help" },
3036  };
3037  static const struct menu_data out4a[] =
3038  {
3039  { MF_STRING, 1, "File" },
3040  { MF_STRING|MF_HELP, 2, "Help" },
3041  { MF_HELP, 1, MAKEINTRESOURCEA(1) }
3042  };
3043  HMENU hmenu;
3044 
3045 #define create_menu(a) create_menu_from_data((a), ARRAY_SIZE(a))
3046 #define create_menuitem(a) create_menuitem_from_data((a), ARRAY_SIZE(a))
3047 #define compare_menu(h, a) compare_menu_data((h), (a), ARRAY_SIZE(a))
3048 
3049  hmenu = create_menu(in1);
3050  compare_menu(hmenu, out1);
3051  DestroyMenu(hmenu);
3052 
3053  hmenu = create_menu(in2);
3054  compare_menu(hmenu, out2);
3055  DestroyMenu(hmenu);
3056 
3057  hmenu = create_menu(in3);
3058  compare_menu(hmenu, out3);
3059  DestroyMenu(hmenu);
3060 
3061  hmenu = create_menu(in4);
3062  compare_menu(hmenu, out4);
3063  DestroyMenu(hmenu);
3064 
3065  /* now using InsertMenuItemInfo */
3066  hmenu = create_menuitem(in1);
3067  compare_menu(hmenu, out1a);
3068  DestroyMenu(hmenu);
3069 
3070  hmenu = create_menuitem(in2);
3071  compare_menu(hmenu, out2a);
3072  DestroyMenu(hmenu);
3073 
3074  hmenu = create_menuitem(in3);
3075  compare_menu(hmenu, out3);
3076  DestroyMenu(hmenu);
3077 
3078  hmenu = create_menuitem(in4);
3079  compare_menu(hmenu, out4a);
3080  DestroyMenu(hmenu);
3081 
3082 #undef create_menu
3083 #undef create_menuitem
3084 #undef compare_menu
3085 }
3086 
3087 static void test_menu_getmenuinfo(void)
3088 {
3089  HMENU hmenu;
3090  MENUINFO mi = {0};
3091  BOOL ret;
3092  DWORD gle;
3093 
3094  /* create a menu */
3095  hmenu = CreateMenu();
3096  assert( hmenu);
3097  /* test some parameter errors */
3098  SetLastError(0xdeadbeef);
3099  ret = GetMenuInfo( hmenu, NULL);
3100  gle= GetLastError();
3101  ok( !ret, "GetMenuInfo() should have failed\n");
3102  ok( gle == ERROR_INVALID_PARAMETER ||
3103  broken(gle == 0xdeadbeef), /* Win98, WinME */
3104  "GetMenuInfo() error got %u expected %u\n", gle, ERROR_INVALID_PARAMETER);
3105  SetLastError(0xdeadbeef);
3106  mi.cbSize = 0;
3107  ret = GetMenuInfo( hmenu, &mi);
3108  gle= GetLastError();
3109  ok( !ret, "GetMenuInfo() should have failed\n");
3110  ok( gle == ERROR_INVALID_PARAMETER ||
3111  broken(gle == 0xdeadbeef), /* Win98, WinME */
3112  "GetMenuInfo() error got %u expected %u\n", gle, ERROR_INVALID_PARAMETER);
3113  SetLastError(0xdeadbeef);
3114  mi.cbSize = sizeof( MENUINFO);
3115  ret = GetMenuInfo( hmenu, &mi);
3116  gle= GetLastError();
3117  ok( ret, "GetMenuInfo() should have succeeded\n");
3118  ok( gle == 0xdeadbeef, "GetMenuInfo() error got %u\n", gle);
3119  SetLastError(0xdeadbeef);
3120  mi.cbSize = 0;
3121  ret = GetMenuInfo( NULL, &mi);
3122  gle= GetLastError();
3123  ok( !ret, "GetMenuInfo() should have failed\n");
3124  ok( gle == ERROR_INVALID_PARAMETER ||
3125  broken(gle == 0xdeadbeef), /* Win98, WinME */
3126  "GetMenuInfo() error got %u expected %u\n", gle, ERROR_INVALID_PARAMETER);
3127  /* clean up */
3128  DestroyMenu( hmenu);
3129  return;
3130 }
3131 
3132 static void test_menu_setmenuinfo(void)
3133 {
3134  HMENU hmenu, hsubmenu;
3135  MENUINFO mi = {0};
3136  MENUITEMINFOA mii = { sizeof(MENUITEMINFOA) };
3137  BOOL ret;
3138  DWORD gle;
3139  HBRUSH brush;
3140 
3141  /* create a menu with a submenu */
3142  hmenu = CreateMenu();
3143  hsubmenu = CreateMenu();
3144  assert( hmenu && hsubmenu);
3145  mii.fMask = MIIM_SUBMENU;
3146  mii.hSubMenu = hsubmenu;
3147  ret = InsertMenuItemA( hmenu, 0, FALSE, &mii);
3148  ok( ret, "InsertMenuItem failed with error %d\n", GetLastError());
3149  /* test some parameter errors */
3150  SetLastError(0xdeadbeef);
3151  ret = SetMenuInfo( hmenu, NULL);
3152  gle= GetLastError();
3153  ok( !ret, "SetMenuInfo() should have failed\n");
3154  ok( gle == ERROR_INVALID_PARAMETER ||
3155  broken(gle == 0xdeadbeef), /* Win98, WinME */
3156  "SetMenuInfo() error got %u expected %u\n", gle, ERROR_INVALID_PARAMETER);
3157  SetLastError(0xdeadbeef);
3158  mi.cbSize = 0;
3159  ret = SetMenuInfo( hmenu, &mi);
3160  gle= GetLastError();
3161  ok( !ret, "SetMenuInfo() should have failed\n");
3162  ok( gle == ERROR_INVALID_PARAMETER ||
3163  broken(gle == 0xdeadbeef), /* Win98, WinME */
3164  "SetMenuInfo() error got %u expected %u\n", gle, ERROR_INVALID_PARAMETER);
3165  SetLastError(0xdeadbeef);
3166  mi.cbSize = sizeof( MENUINFO);
3167  ret = SetMenuInfo( hmenu, &mi);
3168  gle= GetLastError();
3169  ok( ret, "SetMenuInfo() should have succeeded\n");
3170  ok( gle == 0xdeadbeef, "SetMenuInfo() error got %u\n", gle);
3171  SetLastError(0xdeadbeef);
3172  mi.cbSize = 0;
3173  ret = SetMenuInfo( NULL, &mi);
3174  gle= GetLastError();
3175  ok( !ret, "SetMenuInfo() should have failed\n");
3176  ok( gle == ERROR_INVALID_PARAMETER ||
3177  broken(gle == 0xdeadbeef), /* Win98, WinME */
3178  "SetMenuInfo() error got %u expected %u\n", gle, ERROR_INVALID_PARAMETER);
3179  /* functional tests */
3180  /* menu and submenu should have the CHECKORBMP style bit cleared */
3181  SetLastError(0xdeadbeef);
3182  mi.cbSize = sizeof( MENUINFO);
3183  mi.fMask = MIM_STYLE;
3184  ret = GetMenuInfo( hmenu, &mi);
3185  gle= GetLastError();
3186  ok( ret, "GetMenuInfo() should have succeeded\n");
3187  ok( gle == 0xdeadbeef, "GetMenuInfo() error got %u\n", gle);
3188  ok( !(mi.dwStyle & MNS_CHECKORBMP), "menustyle was not expected to have the MNS_CHECKORBMP flag\n");
3189  SetLastError(0xdeadbeef);
3190  mi.cbSize = sizeof( MENUINFO);
3191  mi.fMask = MIM_STYLE;
3192  ret = GetMenuInfo( hsubmenu, &mi);
3193  gle= GetLastError();
3194  ok( ret, "GetMenuInfo() should have succeeded\n");
3195  ok( gle == 0xdeadbeef, "GetMenuInfo() error got %u\n", gle);
3196  ok( !(mi.dwStyle & MNS_CHECKORBMP), "menustyle was not expected to have the MNS_CHECKORBMP flag\n");
3197  /* SetMenuInfo() */
3198  SetLastError(0xdeadbeef);
3199  mi.cbSize = sizeof( MENUINFO);
3200  mi.fMask = MIM_STYLE | MIM_APPLYTOSUBMENUS;
3201  mi.dwStyle = MNS_CHECKORBMP;
3202  ret = SetMenuInfo( hmenu, &mi);
3203  gle= GetLastError();
3204  ok( ret, "SetMenuInfo() should have succeeded\n");
3205  ok( gle == 0xdeadbeef, "SetMenuInfo() error got %u\n", gle);
3206  /* Now both menus should have the MNS_CHECKORBMP style bit set */
3207  SetLastError(0xdeadbeef);
3208  mi.cbSize = sizeof( MENUINFO);
3209  mi.fMask = MIM_STYLE;
3210  ret = GetMenuInfo( hmenu, &mi);
3211  gle= GetLastError();
3212  ok( ret, "GetMenuInfo() should have succeeded\n");
3213  ok( gle == 0xdeadbeef, "GetMenuInfo() error got %u\n", gle);
3214  ok( mi.dwStyle & MNS_CHECKORBMP, "menustyle was expected to have the MNS_CHECKORBMP flag\n");
3215  SetLastError(0xdeadbeef);
3216  mi.cbSize = sizeof( MENUINFO);
3217  mi.fMask = MIM_STYLE;
3218  ret = GetMenuInfo( hsubmenu, &mi);
3219  gle= GetLastError();
3220  ok( ret, "GetMenuInfo() should have succeeded\n");
3221  ok( gle == 0xdeadbeef, "GetMenuInfo() error got %u\n", gle);
3222  ok( mi.dwStyle & MNS_CHECKORBMP, "menustyle was expected to have the MNS_CHECKORBMP flag\n");
3223  /* now repeat that without the APPLYTOSUBMENUS flag and another style bit */
3224  SetLastError(0xdeadbeef);
3225  mi.cbSize = sizeof( MENUINFO);
3226  mi.fMask = MIM_STYLE ;
3227  mi.dwStyle = MNS_NOCHECK;
3228  ret = S