ReactOS  0.4.13-dev-99-g7e18b6d
menu.c
Go to the documentation of this file.
1 /*
2  * COPYRIGHT: See COPYING in the top level directory
3  * PROJECT: ReactOS kernel
4  * PURPOSE: Menus
5  * FILE: win32ss/user/ntuser/menu.c
6  * PROGRAMER: Thomas Weidenmueller (w3seek@users.sourceforge.net)
7  */
8 
9 #include <win32k.h>
10 DBG_DEFAULT_CHANNEL(UserMenu);
11 
12 /* INTERNAL ******************************************************************/
13 
17 
18 /* Use global popup window because there's no way 2 menus can
19  * be tracked at the same time. */
20 static HWND top_popup = NULL;
22 
25 
26 /* internal popup menu window messages */
27 
28 #define MM_SETMENUHANDLE (WM_USER + 0)
29 #define MM_GETMENUHANDLE (WM_USER + 1)
30 
31 /* internal flags for menu tracking */
32 
33 #define TF_ENDMENU 0x10000
34 #define TF_SUSPENDPOPUP 0x20000
35 #define TF_SKIPREMOVE 0x40000
36 
37 
38 /* maximum allowed depth of any branch in the menu tree.
39  * This value is slightly larger than in windows (25) to
40  * stay on the safe side. */
41 #define MAXMENUDEPTH 30
42 
43 #define MNS_STYLE_MASK (MNS_NOCHECK|MNS_MODELESS|MNS_DRAGDROP|MNS_AUTODISMISS|MNS_NOTIFYBYPOS|MNS_CHECKORBMP)
44 
45 #define MENUITEMINFO_TYPE_MASK \
46  (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
47  MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
48  MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
49 
50 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
51 
52 #define STATE_MASK (~TYPE_MASK)
53 
54 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
55 
56 #define MII_STATE_MASK (MFS_GRAYED|MFS_CHECKED|MFS_HILITE|MFS_DEFAULT)
57 
58 #define IS_SYSTEM_MENU(MenuInfo) \
59  (!((MenuInfo)->fFlags & MNF_POPUP) && ((MenuInfo)->fFlags & MNF_SYSMENU))
60 
61 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
62 
63 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
64 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
65 
66 /* Maximum number of menu items a menu can contain */
67 #define MAX_MENU_ITEMS (0x4000)
68 #define MAX_GOINTOSUBMENU (0x10)
69 
70 /* Space between 2 columns */
71 #define MENU_COL_SPACE 4
72 
73 /* top and bottom margins for popup menus */
74 #define MENU_TOP_MARGIN 3
75 #define MENU_BOTTOM_MARGIN 2
76 
77 #define MENU_ITEM_HBMP_SPACE (5)
78 #define MENU_BAR_ITEMS_SPACE (12)
79 #define SEPARATOR_HEIGHT (5)
80 #define MENU_TAB_SPACE (8)
81 
82 typedef struct
83 {
85  PMENU CurrentMenu; /* current submenu (can be equal to hTopMenu)*/
86  PMENU TopMenu; /* initial menu */
87  PWND OwnerWnd; /* where notifications are sent */
89 } MTRACKER;
90 
91 /* Internal MenuTrackMenu() flags */
92 #define TPM_INTERNAL 0xF0000000
93 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
94 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
95 
96 #define ITEM_PREV -1
97 #define ITEM_NEXT 1
98 
99 #define UpdateMenuItemState(state, change) \
100 {\
101  if((change) & MF_GRAYED) { \
102  (state) |= MF_GRAYED; \
103  } else { \
104  (state) &= ~MF_GRAYED; \
105  } /* Separate the two for test_menu_resource_layout.*/ \
106  if((change) & MF_DISABLED) { \
107  (state) |= MF_DISABLED; \
108  } else { \
109  (state) &= ~MF_DISABLED; \
110  } \
111  if((change) & MFS_CHECKED) { \
112  (state) |= MFS_CHECKED; \
113  } else { \
114  (state) &= ~MFS_CHECKED; \
115  } \
116  if((change) & MFS_HILITE) { \
117  (state) |= MFS_HILITE; \
118  } else { \
119  (state) &= ~MFS_HILITE; \
120  } \
121  if((change) & MFS_DEFAULT) { \
122  (state) |= MFS_DEFAULT; \
123  } else { \
124  (state) &= ~MFS_DEFAULT; \
125  } \
126  if((change) & MF_MOUSESELECT) { \
127  (state) |= MF_MOUSESELECT; \
128  } else { \
129  (state) &= ~MF_MOUSESELECT; \
130  } \
131 }
132 
133 #if 0
134 void FASTCALL
135 DumpMenuItemList(PMENU Menu, PITEM MenuItem)
136 {
137  UINT cnt = 0, i = Menu->cItems;
138  while(i)
139  {
140  if(MenuItem->lpstr.Length)
141  DbgPrint(" %d. %wZ\n", ++cnt, &MenuItem->lpstr);
142  else
143  DbgPrint(" %d. NO TEXT dwTypeData==%d\n", ++cnt, (DWORD)MenuItem->lpstr.Buffer);
144  DbgPrint(" fType=");
145  if(MFT_BITMAP & MenuItem->fType)
146  DbgPrint("MFT_BITMAP ");
147  if(MFT_MENUBARBREAK & MenuItem->fType)
148  DbgPrint("MFT_MENUBARBREAK ");
149  if(MFT_MENUBREAK & MenuItem->fType)
150  DbgPrint("MFT_MENUBREAK ");
151  if(MFT_OWNERDRAW & MenuItem->fType)
152  DbgPrint("MFT_OWNERDRAW ");
153  if(MFT_RADIOCHECK & MenuItem->fType)
154  DbgPrint("MFT_RADIOCHECK ");
155  if(MFT_RIGHTJUSTIFY & MenuItem->fType)
156  DbgPrint("MFT_RIGHTJUSTIFY ");
157  if(MFT_SEPARATOR & MenuItem->fType)
158  DbgPrint("MFT_SEPARATOR ");
159  if(MFT_STRING & MenuItem->fType)
160  DbgPrint("MFT_STRING ");
161  DbgPrint("\n fState=");
162  if(MFS_DISABLED & MenuItem->fState)
163  DbgPrint("MFS_DISABLED ");
164  else
165  DbgPrint("MFS_ENABLED ");
166  if(MFS_CHECKED & MenuItem->fState)
167  DbgPrint("MFS_CHECKED ");
168  else
169  DbgPrint("MFS_UNCHECKED ");
170  if(MFS_HILITE & MenuItem->fState)
171  DbgPrint("MFS_HILITE ");
172  else
173  DbgPrint("MFS_UNHILITE ");
174  if(MFS_DEFAULT & MenuItem->fState)
175  DbgPrint("MFS_DEFAULT ");
176  if(MFS_GRAYED & MenuItem->fState)
177  DbgPrint("MFS_GRAYED ");
178  DbgPrint("\n wId=%d\n", MenuItem->wID);
179  MenuItem++;
180  i--;
181  }
182  DbgPrint("Entries: %d\n", cnt);
183  return;
184 }
185 #endif
186 
187 #define FreeMenuText(Menu,MenuItem) \
188 { \
189  if((MENU_ITEM_TYPE((MenuItem)->fType) == MF_STRING) && \
190  (MenuItem)->lpstr.Length) { \
191  DesktopHeapFree(((PMENU)Menu)->head.rpdesk, (MenuItem)->lpstr.Buffer); \
192  } \
193 }
194 
197 {
198  PMENU Menu = UserGetMenuObject(hMenu);
199  if (Menu)
200  Menu->head.cLockObj++;
201 
202  return Menu;
203 }
204 
206 {
207  HMENU hMenu;
208  PITEM pItem;
209  ULONG Error;
210  UINT i;
211  if (!pMenu) return NULL;
212 
214 
215  _SEH2_TRY
216  {
217  hMenu = UserHMGetHandle(pMenu);
218  pItem = pMenu->rgItems;
219  if (pItem)
220  {
221  i = pItem[0].wID;
222  pItem[0].wID = i;
223  }
224  }
226  {
227  ERR("Run away LOOP!\n");
229  _SEH2_YIELD(return NULL);
230  }
231  _SEH2_END
232 
233  if ( UserObjectInDestroy(hMenu))
234  {
235  ERR("Menu is marked for destruction!\n");
236  pMenu = NULL;
237  }
239  return pMenu;
240 }
241 
242 BOOL
243 FASTCALL
245 {
246  if (UserGetMenuObject(Menu)) return TRUE;
247  return FALSE;
248 }
249 
250 
253 {
254  PWND Wnd = ValidateHwndNoErr(hWnd);
255 
256  if (!Wnd)
257  return NULL;
258 
259  return UserGetMenuObject(UlongToHandle(Wnd->IDMenu));
260 }
261 
263 {
264  PMENU ret = 0;
265  WND *win = ValidateHwndNoErr( hwnd );
266  if (win)
267  {
268  ret = UserGetMenuObject(win->SystemMenu);
269  }
270  return ret;
271 }
272 
273 BOOL IntDestroyMenu( PMENU pMenu, BOOL bRecurse)
274 {
275  PMENU SubMenu;
276 
278  if (pMenu->rgItems) /* recursively destroy submenus */
279  {
280  int i;
281  ITEM *item = pMenu->rgItems;
282  for (i = pMenu->cItems; i > 0; i--, item++)
283  {
284  SubMenu = item->spSubMenu;
285  item->spSubMenu = NULL;
286 
287  /* Remove Item Text */
288  FreeMenuText(pMenu,item);
289 
290  /* Remove Item Bitmap and set it for this process */
291  if (item->hbmp && !(item->fState & MFS_HBMMENUBMP))
292  {
294  item->hbmp = NULL;
295  }
296 
297  /* Remove Item submenu */
298  if (bRecurse && SubMenu)//VerifyMenu(SubMenu))
299  {
300  /* Release submenu since it was referenced when inserted */
301  IntReleaseMenuObject(SubMenu);
302  IntDestroyMenuObject(SubMenu, bRecurse);
303  }
304  }
305  /* Free the Item */
306  DesktopHeapFree(pMenu->head.rpdesk, pMenu->rgItems );
307  pMenu->rgItems = NULL;
308  pMenu->cItems = 0;
309  }
310  return TRUE;
311 }
312 
313 /* Callback for the object manager */
314 BOOLEAN
316 {
318 }
319 
322 {
324  if (Menu)
325  {
326  PWND Window;
327 
328  if (PsGetCurrentProcessSessionId() == Menu->head.rpdesk->rpwinstaParent->dwSessionId)
329  {
330  BOOL ret;
331  if (Menu->hWnd)
332  {
333  Window = ValidateHwndNoErr(Menu->hWnd);
334  if (Window)
335  {
336  //Window->IDMenu = 0; Only in Win9x!! wine win test_SetMenu test...
337 
338  /* DestroyMenu should not destroy system menu popup owner */
339  if ((Menu->fFlags & (MNF_POPUP | MNF_SYSSUBMENU)) == MNF_POPUP)
340  {
341  // Should we check it to see if it has Class?
342  ERR("FIXME Pop up menu window thing'ie\n");
343  //co_UserDestroyWindow( Window );
344  //Menu->hWnd = 0;
345  }
346  }
347  }
348 
349  if (!UserMarkObjectDestroy(Menu)) return TRUE;
350 
351  /* Remove all menu items */
352  IntDestroyMenu( Menu, bRecurse);
353 
354  ret = UserDeleteObject(Menu->head.h, TYPE_MENU);
355  TRACE("IntDestroyMenuObject %d\n",ret);
356  return ret;
357  }
358  }
359  return FALSE;
360 }
361 
362 BOOL
364 {
365  NONCLIENTMETRICSW ncm;
366 
367  /* get the menu font */
368  if (!ghMenuFont || !ghMenuFontBold)
369  {
370  ncm.cbSize = sizeof(ncm);
371  if(!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
372  {
373  ERR("MenuInit(): SystemParametersInfo(SPI_GETNONCLIENTMETRICS) failed!\n");
374  return FALSE;
375  }
376 
377  ghMenuFont = GreCreateFontIndirectW(&ncm.lfMenuFont);
378  if (ghMenuFont == NULL)
379  {
380  ERR("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
381  return FALSE;
382  }
383  ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
384  ghMenuFontBold = GreCreateFontIndirectW(&ncm.lfMenuFont);
385  if (ghMenuFontBold == NULL)
386  {
387  ERR("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
389  ghMenuFont = NULL;
390  return FALSE;
391  }
392 
395 
396  co_IntSetupOBM();
397  }
398 
399  return TRUE;
400 }
401 
402 
403 /**********************************************************************
404  * MENU_depth
405  *
406  * detect if there are loops in the menu tree (or the depth is too large)
407  */
408 int FASTCALL MENU_depth( PMENU pmenu, int depth)
409 {
410  UINT i;
411  ITEM *item;
412  int subdepth;
413 
414  if (!pmenu) return depth;
415 
416  depth++;
417  if( depth > MAXMENUDEPTH) return depth;
418  item = pmenu->rgItems;
419  subdepth = depth;
420  for( i = 0; i < pmenu->cItems && subdepth <= MAXMENUDEPTH; i++, item++)
421  {
422  if( item->spSubMenu)//VerifyMenu(item->spSubMenu))
423  {
424  int bdepth = MENU_depth( item->spSubMenu, depth);
425  if( bdepth > subdepth) subdepth = bdepth;
426  }
427  if( subdepth > MAXMENUDEPTH)
428  TRACE("<- hmenu %p\n", item->spSubMenu);
429  }
430  return subdepth;
431 }
432 
433 
434 /******************************************************************************
435  *
436  * UINT MenuGetStartOfNextColumn(
437  * PMENU Menu)
438  *
439  *****************************************************************************/
440 
442  PMENU menu )
443 {
444  PITEM pItem;
445  UINT i;
446 
447  if(!menu)
448  return NO_SELECTED_ITEM;
449 
450  i = menu->iItem + 1;
451  if( i == NO_SELECTED_ITEM )
452  return i;
453 
454  pItem = menu->rgItems;
455  if (!pItem) return NO_SELECTED_ITEM;
456 
457  for( ; i < menu->cItems; ++i ) {
458  if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
459  return i;
460  }
461 
462  return NO_SELECTED_ITEM;
463 }
464 
465 /******************************************************************************
466  *
467  * UINT MenuGetStartOfPrevColumn(
468  * PMENU Menu)
469  *
470  *****************************************************************************/
472  PMENU menu )
473 {
474  UINT i;
475  PITEM pItem;
476 
477  if( !menu )
478  return NO_SELECTED_ITEM;
479 
480  if( menu->iItem == 0 || menu->iItem == NO_SELECTED_ITEM )
481  return NO_SELECTED_ITEM;
482 
483  pItem = menu->rgItems;
484  if (!pItem) return NO_SELECTED_ITEM;
485 
486  /* Find the start of the column */
487 
488  for(i = menu->iItem; i != 0 &&
489  !(pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
490  --i); /* empty */
491 
492  if(i == 0)
493  return NO_SELECTED_ITEM;
494 
495  for(--i; i != 0; --i) {
496  if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
497  break;
498  }
499 
500  TRACE("ret %d.\n", i );
501 
502  return i;
503 }
504 
505 /***********************************************************************
506  * MENU_FindItem
507  *
508  * Find a menu item. Return a pointer on the item, and modifies *hmenu
509  * in case the item was in a sub-menu.
510  */
512 {
513  MENU *menu = *pmenu;
514  ITEM *fallback = NULL;
515  UINT fallback_pos = 0;
516  UINT i;
517 
518  if (!menu) return NULL;
519 
520  if (wFlags & MF_BYPOSITION)
521  {
522  if (!menu->cItems) return NULL;
523  if (*nPos >= menu->cItems) return NULL;
524  return &menu->rgItems[*nPos];
525  }
526  else
527  {
528  PITEM item = menu->rgItems;
529  for (i = 0; i < menu->cItems; i++, item++)
530  {
531  if (item->spSubMenu)
532  {
533  PMENU psubmenu = item->spSubMenu;//VerifyMenu(item->spSubMenu);
534  PITEM subitem = MENU_FindItem( &psubmenu, nPos, wFlags );
535  if (subitem)
536  {
537  *pmenu = psubmenu;
538  return subitem;
539  }
540  else if (item->wID == *nPos)
541  {
542  /* fallback to this item if nothing else found */
543  fallback_pos = i;
544  fallback = item;
545  }
546  }
547  else if (item->wID == *nPos)
548  {
549  *nPos = i;
550  return item;
551  }
552  }
553  }
554 
555  if (fallback)
556  *nPos = fallback_pos;
557 
558  return fallback;
559 }
560 
561 /***********************************************************************
562  * MenuFindSubMenu
563  *
564  * Find a Sub menu. Return the position of the submenu, and modifies
565  * *hmenu in case it is found in another sub-menu.
566  * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
567  */
568 static UINT FASTCALL MENU_FindSubMenu(PMENU *menu, PMENU SubTarget )
569 {
570  UINT i;
571  PITEM item;
572 
573  item = ((PMENU)*menu)->rgItems;
574  for (i = 0; i < ((PMENU)*menu)->cItems; i++, item++)
575  {
576  if (!item->spSubMenu)
577  continue;
578  else
579  {
580  if (item->spSubMenu == SubTarget)
581  {
582  return i;
583  }
584  else
585  {
586  PMENU pSubMenu = item->spSubMenu;
587  UINT pos = MENU_FindSubMenu( &pSubMenu, SubTarget );
588  if (pos != NO_SELECTED_ITEM)
589  {
590  *menu = pSubMenu;
591  return pos;
592  }
593  }
594  }
595  }
596  return NO_SELECTED_ITEM;
597 }
598 
600 IntRemoveMenuItem( PMENU pMenu, UINT nPos, UINT wFlags, BOOL bRecurse )
601 {
602  PITEM item;
603  PITEM newItems;
604 
605  TRACE("(menu=%p pos=%04x flags=%04x)\n",pMenu, nPos, wFlags);
606  if (!(item = MENU_FindItem( &pMenu, &nPos, wFlags ))) return FALSE;
607 
608  /* Remove item */
609 
610  FreeMenuText(pMenu,item);
611  if (bRecurse && item->spSubMenu)
612  {
613  IntDestroyMenuObject(item->spSubMenu, bRecurse);
614  }
616  if (--pMenu->cItems == 0)
617  {
618  DesktopHeapFree(pMenu->head.rpdesk, pMenu->rgItems );
619  pMenu->rgItems = NULL;
620  }
621  else
622  {
623  while (nPos < pMenu->cItems)
624  {
625  *item = *(item+1);
626  item++;
627  nPos++;
628  }
629  newItems = DesktopHeapReAlloc(pMenu->head.rpdesk, pMenu->rgItems, pMenu->cItems * sizeof(ITEM));
630  if (newItems)
631  {
632  pMenu->rgItems = newItems;
633  }
634  }
635  return TRUE;
636 }
637 
638 /**********************************************************************
639  * MENU_InsertItem
640  *
641  * Insert (allocate) a new item into a menu.
642  */
644 {
645  ITEM *newItems;
646 
647  /* Find where to insert new item */
648 
649  if (flags & MF_BYPOSITION) {
650  if (pos > menu->cItems)
651  pos = menu->cItems;
652  } else {
653  if (!MENU_FindItem( &menu, &pos, flags ))
654  {
655  if (submenu) *submenu = menu;
656  if (npos) *npos = pos;
657  pos = menu->cItems;
658  }
659  }
660 
661  /* Make sure that MDI system buttons stay on the right side.
662  * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
663  * regardless of their id.
664  */
665  while ( pos > 0 &&
666  (INT_PTR)menu->rgItems[pos - 1].hbmp >= (INT_PTR)HBMMENU_SYSTEM &&
668  pos--;
669 
670  TRACE("inserting at %u flags %x\n", pos, flags);
671 
672  /* Create new items array */
673 
674  newItems = DesktopHeapAlloc(menu->head.rpdesk, sizeof(ITEM) * (menu->cItems+1) );
675  if (!newItems)
676  {
677  WARN("allocation failed\n" );
678  return NULL;
679  }
680  if (menu->cItems > 0)
681  {
682  /* Copy the old array into the new one */
683  if (pos > 0) RtlCopyMemory( newItems, menu->rgItems, pos * sizeof(ITEM) );
684  if (pos < menu->cItems) RtlCopyMemory( &newItems[pos+1], &menu->rgItems[pos], (menu->cItems-pos)*sizeof(ITEM) );
685  DesktopHeapFree(menu->head.rpdesk, menu->rgItems );
686  }
687  menu->rgItems = newItems;
688  menu->cItems++;
689  RtlZeroMemory( &newItems[pos], sizeof(*newItems) );
690  menu->cyMenu = 0; /* force size recalculate */
691  return &newItems[pos];
692 }
693 
696  _In_ PMENU MenuObject,
697  UINT uItem,
698  BOOL fByPosition,
699  PROSMENUITEMINFO ItemInfo,
701 {
702  PITEM MenuItem;
703  PMENU SubMenu = NULL;
704 
705  NT_ASSERT(MenuObject != NULL);
706 
707  if (MAX_MENU_ITEMS <= MenuObject->cItems)
708  {
710  return FALSE;
711  }
712 
713  SubMenu = MenuObject;
714 
715  if(!(MenuItem = MENU_InsertItem( SubMenu, uItem, fByPosition ? MF_BYPOSITION : MF_BYCOMMAND, &SubMenu, &uItem ))) return FALSE;
716 
717  if(!IntSetMenuItemInfo(SubMenu, MenuItem, ItemInfo, lpstr))
718  {
719  IntRemoveMenuItem(SubMenu, uItem, fByPosition ? MF_BYPOSITION : MF_BYCOMMAND, FALSE);
720  return FALSE;
721  }
722 
723  /* Force size recalculation! */
724  SubMenu->cyMenu = 0;
725  MenuItem->hbmpChecked = MenuItem->hbmpUnchecked = 0;
726 
727  TRACE("IntInsertMenuItemToList = %u %i\n", uItem, (BOOL)((INT)uItem >= 0));
728 
729  return TRUE;
730 }
731 
735  _In_ BOOL IsMenuBar,
737  _In_ PPROCESSINFO ppi)
738 {
739  PMENU Menu;
740 
742  Desktop,
743  ppi->ptiList,
744  Handle,
745  TYPE_MENU,
746  sizeof(MENU));
747  if(!Menu)
748  {
749  *Handle = 0;
750  return NULL;
751  }
752 
753  Menu->cyMax = 0; /* Default */
754  Menu->hbrBack = NULL; /* No brush */
755  Menu->dwContextHelpId = 0; /* Default */
756  Menu->dwMenuData = 0; /* Default */
757  Menu->iItem = NO_SELECTED_ITEM; // Focused item
758  Menu->fFlags = (IsMenuBar ? 0 : MNF_POPUP);
759  Menu->spwndNotify = NULL;
760  Menu->cyMenu = 0; // Height
761  Menu->cxMenu = 0; // Width
762  Menu->cItems = 0; // Item count
763  Menu->iTop = 0;
764  Menu->iMaxTop = 0;
765  Menu->cxTextAlign = 0;
766  Menu->rgItems = NULL;
767 
768  Menu->hWnd = NULL;
769  Menu->TimeToHide = FALSE;
770 
771  return Menu;
772 }
773 
776 {
777  PITEM MenuItem, NewMenuItem = NULL;
778  UINT i;
779 
780  if(!Source->cItems)
781  return FALSE;
782 
783  NewMenuItem = DesktopHeapAlloc(Destination->head.rpdesk, Source->cItems * sizeof(ITEM));
784  if(!NewMenuItem) return FALSE;
785 
786  RtlZeroMemory(NewMenuItem, Source->cItems * sizeof(ITEM));
787 
788  Destination->rgItems = NewMenuItem;
789 
790  MenuItem = Source->rgItems;
791  for (i = 0; i < Source->cItems; i++, MenuItem++, NewMenuItem++)
792  {
793  NewMenuItem->fType = MenuItem->fType;
794  NewMenuItem->fState = MenuItem->fState;
795  NewMenuItem->wID = MenuItem->wID;
796  NewMenuItem->spSubMenu = MenuItem->spSubMenu;
797  NewMenuItem->hbmpChecked = MenuItem->hbmpChecked;
798  NewMenuItem->hbmpUnchecked = MenuItem->hbmpUnchecked;
799  NewMenuItem->dwItemData = MenuItem->dwItemData;
800  if (MenuItem->lpstr.Length)
801  {
802  NewMenuItem->lpstr.Length = 0;
803  NewMenuItem->lpstr.MaximumLength = MenuItem->lpstr.MaximumLength;
804  NewMenuItem->lpstr.Buffer = DesktopHeapAlloc(Destination->head.rpdesk, MenuItem->lpstr.MaximumLength);
805  if (!NewMenuItem->lpstr.Buffer)
806  {
807  DesktopHeapFree(Destination->head.rpdesk, NewMenuItem);
808  break;
809  }
810  RtlCopyUnicodeString(&NewMenuItem->lpstr, &MenuItem->lpstr);
811  NewMenuItem->lpstr.Buffer[MenuItem->lpstr.Length / sizeof(WCHAR)] = 0;
812  NewMenuItem->Xlpstr = NewMenuItem->lpstr.Buffer;
813  }
814  else
815  {
816  NewMenuItem->lpstr.Buffer = MenuItem->lpstr.Buffer;
817  NewMenuItem->Xlpstr = NewMenuItem->lpstr.Buffer;
818  }
819  NewMenuItem->hbmp = MenuItem->hbmp;
820  Destination->cItems = i + 1;
821  }
822  return TRUE;
823 }
824 
827 {
828  HANDLE hMenu;
829  PMENU Menu;
830 
831  if(!Source)
832  return NULL;
833 
834  /* A menu is valid process wide. We can pass to the object manager any thread ptr */
836  Source->head.rpdesk,
837  ((PPROCESSINFO)Source->head.hTaskWow)->ptiList,
838  &hMenu,
839  TYPE_MENU,
840  sizeof(MENU));
841  if(!Menu)
842  return NULL;
843 
844  Menu->fFlags = Source->fFlags;
845  Menu->cyMax = Source->cyMax;
846  Menu->hbrBack = Source->hbrBack;
847  Menu->dwContextHelpId = Source->dwContextHelpId;
848  Menu->dwMenuData = Source->dwMenuData;
849  Menu->iItem = NO_SELECTED_ITEM;
850  Menu->spwndNotify = NULL;
851  Menu->cyMenu = 0;
852  Menu->cxMenu = 0;
853  Menu->cItems = 0;
854  Menu->iTop = 0;
855  Menu->iMaxTop = 0;
856  Menu->cxTextAlign = 0;
857  Menu->rgItems = NULL;
858 
859  Menu->hWnd = NULL;
860  Menu->TimeToHide = FALSE;
861 
862  IntCloneMenuItems(Menu, Source);
863 
864  return Menu;
865 }
866 
869 {
870  ERR("SetMenuFlagRtoL\n");
871  Menu->fFlags |= MNF_RTOL;
872  return TRUE;
873 }
874 
876 IntSetMenuContextHelpId(PMENU Menu, DWORD dwContextHelpId)
877 {
878  Menu->dwContextHelpId = dwContextHelpId;
879  return TRUE;
880 }
881 
884 {
885  if(lpmi->fMask & MIM_BACKGROUND)
886  lpmi->hbrBack = Menu->hbrBack;
887  if(lpmi->fMask & MIM_HELPID)
888  lpmi->dwContextHelpID = Menu->dwContextHelpId;
889  if(lpmi->fMask & MIM_MAXHEIGHT)
890  lpmi->cyMax = Menu->cyMax;
891  if(lpmi->fMask & MIM_MENUDATA)
892  lpmi->dwMenuData = Menu->dwMenuData;
893  if(lpmi->fMask & MIM_STYLE)
894  lpmi->dwStyle = Menu->fFlags & MNS_STYLE_MASK;
895 
896  if (sizeof(MENUINFO) < lpmi->cbSize)
897  {
898  lpmi->cItems = Menu->cItems;
899 
900  lpmi->iItem = Menu->iItem;
901  lpmi->cxMenu = Menu->cxMenu;
902  lpmi->cyMenu = Menu->cyMenu;
903  lpmi->spwndNotify = Menu->spwndNotify;
904  lpmi->cxTextAlign = Menu->cxTextAlign;
905  lpmi->iTop = Menu->iTop;
906  lpmi->iMaxTop = Menu->iMaxTop;
907  lpmi->dwArrowsOn = Menu->dwArrowsOn;
908 
909  lpmi->fFlags = Menu->fFlags;
910  lpmi->Self = Menu->head.h;
911  lpmi->TimeToHide = Menu->TimeToHide;
912  lpmi->Wnd = Menu->hWnd;
913  }
914  return TRUE;
915 }
916 
919 {
920  if(lpmi->fMask & MIM_BACKGROUND)
921  Menu->hbrBack = lpmi->hbrBack;
922  if(lpmi->fMask & MIM_HELPID)
923  Menu->dwContextHelpId = lpmi->dwContextHelpID;
924  if(lpmi->fMask & MIM_MAXHEIGHT)
925  Menu->cyMax = lpmi->cyMax;
926  if(lpmi->fMask & MIM_MENUDATA)
927  Menu->dwMenuData = lpmi->dwMenuData;
928  if(lpmi->fMask & MIM_STYLE)
929  Menu->fFlags ^= (Menu->fFlags ^ lpmi->dwStyle) & MNS_STYLE_MASK;
930  if(lpmi->fMask & MIM_APPLYTOSUBMENUS)
931  {
932  int i;
933  PITEM item = Menu->rgItems;
934  for ( i = Menu->cItems; i; i--, item++)
935  {
936  if ( item->spSubMenu )
937  {
938  IntSetMenuInfo( item->spSubMenu, lpmi);
939  }
940  }
941  }
942  if (sizeof(MENUINFO) < lpmi->cbSize)
943  {
944  Menu->iItem = lpmi->iItem;
945  Menu->cyMenu = lpmi->cyMenu;
946  Menu->cxMenu = lpmi->cxMenu;
947  Menu->spwndNotify = lpmi->spwndNotify;
948  Menu->cxTextAlign = lpmi->cxTextAlign;
949  Menu->iTop = lpmi->iTop;
950  Menu->iMaxTop = lpmi->iMaxTop;
951  Menu->dwArrowsOn = lpmi->dwArrowsOn;
952 
953  Menu->TimeToHide = lpmi->TimeToHide;
954  Menu->hWnd = lpmi->Wnd;
955  }
956  if ( lpmi->fMask & MIM_STYLE)
957  {
958  if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented wine\n");
959  if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented wine\n");
960  if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented wine\n");
961  }
962  return TRUE;
963 }
964 
966 IntGetMenuItemInfo(PMENU Menu, /* UNUSED PARAM!! */
967  PITEM MenuItem, PROSMENUITEMINFO lpmii)
968 {
970 
971  if(lpmii->fMask & (MIIM_FTYPE | MIIM_TYPE))
972  {
973  lpmii->fType = MenuItem->fType;
974  }
975  if(lpmii->fMask & MIIM_BITMAP)
976  {
977  lpmii->hbmpItem = MenuItem->hbmp;
978  }
979  if(lpmii->fMask & MIIM_CHECKMARKS)
980  {
981  lpmii->hbmpChecked = MenuItem->hbmpChecked;
982  lpmii->hbmpUnchecked = MenuItem->hbmpUnchecked;
983  }
984  if(lpmii->fMask & MIIM_DATA)
985  {
986  lpmii->dwItemData = MenuItem->dwItemData;
987  }
988  if(lpmii->fMask & MIIM_ID)
989  {
990  lpmii->wID = MenuItem->wID;
991  }
992  if(lpmii->fMask & MIIM_STATE)
993  {
994  lpmii->fState = MenuItem->fState;
995  }
996  if(lpmii->fMask & MIIM_SUBMENU)
997  {
998  lpmii->hSubMenu = MenuItem->spSubMenu ? MenuItem->spSubMenu->head.h : NULL;
999  }
1000 
1001  if ((lpmii->fMask & MIIM_STRING) ||
1002  ((lpmii->fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(lpmii->fType) == MF_STRING)))
1003  {
1004  if (lpmii->dwTypeData == NULL)
1005  {
1006  lpmii->cch = MenuItem->lpstr.Length / sizeof(WCHAR);
1007  }
1008  else
1009  {
1010  Status = MmCopyToCaller(lpmii->dwTypeData, MenuItem->lpstr.Buffer,
1011  min(lpmii->cch * sizeof(WCHAR),
1012  MenuItem->lpstr.MaximumLength));
1013  if (! NT_SUCCESS(Status))
1014  {
1016  return FALSE;
1017  }
1018  }
1019  }
1020 
1021  if (sizeof(ROSMENUITEMINFO) == lpmii->cbSize)
1022  {
1023  lpmii->Rect.left = MenuItem->xItem;
1024  lpmii->Rect.top = MenuItem->yItem;
1025  lpmii->Rect.right = MenuItem->cxItem; // Do this for now......
1026  lpmii->Rect.bottom = MenuItem->cyItem;
1027  lpmii->dxTab = MenuItem->dxTab;
1028  lpmii->lpstr = MenuItem->lpstr.Buffer;
1029  lpmii->maxBmpSize.cx = MenuItem->cxBmp;
1030  lpmii->maxBmpSize.cy = MenuItem->cyBmp;
1031  }
1032 
1033  return TRUE;
1034 }
1035 
1036 BOOL FASTCALL
1038 {
1039  PMENU SubMenuObject;
1040  BOOL circref = FALSE;
1041 
1042  if(!MenuItem || !MenuObject || !lpmii)
1043  {
1044  return FALSE;
1045  }
1046  if ( lpmii->fMask & MIIM_FTYPE )
1047  {
1048  MenuItem->fType &= ~MENUITEMINFO_TYPE_MASK;
1049  MenuItem->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
1050  }
1051  if (lpmii->fMask & MIIM_TYPE)
1052  {
1053  #if 0
1054  if (lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP))
1055  {
1056  ERR("IntSetMenuItemInfo: Invalid combination of fMask bits used\n");
1058  /* This does not happen on Win9x/ME */
1060  return FALSE;
1061  }
1062  #endif
1063  /*
1064  * Delete the menu item type when changing type from
1065  * MF_STRING.
1066  */
1067  if (MenuItem->fType != lpmii->fType &&
1068  MENU_ITEM_TYPE(MenuItem->fType) == MFT_STRING)
1069  {
1070  FreeMenuText(MenuObject,MenuItem);
1071  RtlInitUnicodeString(&MenuItem->lpstr, NULL);
1072  MenuItem->Xlpstr = NULL;
1073  }
1074  if(lpmii->fType & MFT_BITMAP)
1075  {
1076  if(lpmii->hbmpItem)
1077  MenuItem->hbmp = lpmii->hbmpItem;
1078  else
1079  { /* Win 9x/Me stuff */
1080  MenuItem->hbmp = (HBITMAP)((ULONG_PTR)(LOWORD(lpmii->dwTypeData)));
1081  }
1082  lpmii->dwTypeData = 0;
1083  }
1084  }
1085  if(lpmii->fMask & MIIM_BITMAP)
1086  {
1087  MenuItem->hbmp = lpmii->hbmpItem;
1088  if (MenuItem->hbmp <= HBMMENU_POPUP_MINIMIZE && MenuItem->hbmp >= HBMMENU_CALLBACK)
1089  MenuItem->fState |= MFS_HBMMENUBMP;
1090  else
1091  MenuItem->fState &= ~MFS_HBMMENUBMP;
1092  }
1093  if(lpmii->fMask & MIIM_CHECKMARKS)
1094  {
1095  MenuItem->hbmpChecked = lpmii->hbmpChecked;
1096  MenuItem->hbmpUnchecked = lpmii->hbmpUnchecked;
1097  }
1098  if(lpmii->fMask & MIIM_DATA)
1099  {
1100  MenuItem->dwItemData = lpmii->dwItemData;
1101  }
1102  if(lpmii->fMask & MIIM_ID)
1103  {
1104  MenuItem->wID = lpmii->wID;
1105  }
1106  if(lpmii->fMask & MIIM_STATE)
1107  {
1108  /* Remove MFS_DEFAULT flag from all other menu items if this item
1109  has the MFS_DEFAULT state */
1110  if(lpmii->fState & MFS_DEFAULT)
1111  UserSetMenuDefaultItem(MenuObject, -1, 0);
1112  /* Update the menu item state flags */
1113  UpdateMenuItemState(MenuItem->fState, lpmii->fState);
1114  }
1115 
1116  if(lpmii->fMask & MIIM_SUBMENU)
1117  {
1118  if (lpmii->hSubMenu)
1119  {
1120  SubMenuObject = UserGetMenuObject(lpmii->hSubMenu);
1121  if ( SubMenuObject && !(UserObjectInDestroy(lpmii->hSubMenu)) )
1122  {
1125  if (MenuObject == SubMenuObject)
1126  {
1127  HANDLE hMenu;
1128  ERR("Pop Up Menu Double Trouble!\n");
1129  SubMenuObject = IntCreateMenu(&hMenu,
1130  FALSE,
1131  MenuObject->head.rpdesk,
1132  (PPROCESSINFO)MenuObject->head.hTaskWow); // It will be marked.
1133  if (!SubMenuObject) return FALSE;
1134  IntReleaseMenuObject(SubMenuObject); // This will be referenced again after insertion.
1135  circref = TRUE;
1136  }
1137  if ( MENU_depth( SubMenuObject, 0) > MAXMENUDEPTH )
1138  {
1139  ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
1140  if (circref) IntDestroyMenuObject(SubMenuObject, FALSE);
1141  return FALSE;
1142  }
1143  /* Make sure the submenu is marked as a popup menu */
1144  SubMenuObject->fFlags |= MNF_POPUP;
1145  // Now fix the test_subpopup_locked_by_menu tests....
1146  if (MenuItem->spSubMenu) IntReleaseMenuObject(MenuItem->spSubMenu);
1147  MenuItem->spSubMenu = SubMenuObject;
1148  UserReferenceObject(SubMenuObject);
1149  }
1150  else
1151  {
1153  return FALSE;
1154  }
1155  }
1156  else
1157  { // If submenu just dereference it.
1158  if (MenuItem->spSubMenu) IntReleaseMenuObject(MenuItem->spSubMenu);
1159  MenuItem->spSubMenu = NULL;
1160  }
1161  }
1162 
1163  if ((lpmii->fMask & MIIM_STRING) ||
1164  ((lpmii->fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(lpmii->fType) == MF_STRING)))
1165  {
1166  /* free the string when used */
1167  FreeMenuText(MenuObject,MenuItem);
1168  RtlInitUnicodeString(&MenuItem->lpstr, NULL);
1169  MenuItem->Xlpstr = NULL;
1170 
1171  if(lpmii->dwTypeData && lpmii->cch && lpstr && lpstr->Buffer)
1172  {
1174 
1175  if (!NT_VERIFY(lpmii->cch <= UNICODE_STRING_MAX_CHARS))
1176  {
1177  return FALSE;
1178  }
1179 
1180  Source.Length = Source.MaximumLength = (USHORT)(lpmii->cch * sizeof(WCHAR));
1181  Source.Buffer = lpmii->dwTypeData;
1182 
1183  MenuItem->lpstr.Buffer = DesktopHeapAlloc( MenuObject->head.rpdesk, Source.Length + sizeof(WCHAR));
1184  if(MenuItem->lpstr.Buffer != NULL)
1185  {
1186  MenuItem->lpstr.Length = 0;
1187  MenuItem->lpstr.MaximumLength = Source.Length + sizeof(WCHAR);
1188  RtlCopyUnicodeString(&MenuItem->lpstr, &Source);
1189  MenuItem->lpstr.Buffer[MenuItem->lpstr.Length / sizeof(WCHAR)] = 0;
1190 
1191  MenuItem->cch = MenuItem->lpstr.Length / sizeof(WCHAR);
1192  MenuItem->Xlpstr = (USHORT*)MenuItem->lpstr.Buffer;
1193  }
1194  }
1195  }
1196 
1197  if( !(MenuObject->fFlags & MNF_SYSMENU) &&
1198  !MenuItem->Xlpstr &&
1199  !lpmii->dwTypeData &&
1200  !(MenuItem->fType & MFT_OWNERDRAW) &&
1201  !MenuItem->hbmp)
1202  MenuItem->fType |= MFT_SEPARATOR;
1203 
1204  if (sizeof(ROSMENUITEMINFO) == lpmii->cbSize)
1205  {
1206  MenuItem->xItem = lpmii->Rect.left;
1207  MenuItem->yItem = lpmii->Rect.top;
1208  MenuItem->cxItem = lpmii->Rect.right; // Do this for now......
1209  MenuItem->cyItem = lpmii->Rect.bottom;
1210  MenuItem->dxTab = lpmii->dxTab;
1211  lpmii->lpstr = MenuItem->lpstr.Buffer; /* Send back new allocated string or zero */
1212  MenuItem->cxBmp = lpmii->maxBmpSize.cx;
1213  MenuItem->cyBmp = lpmii->maxBmpSize.cy;
1214  }
1215 
1216  return TRUE;
1217 }
1218 
1219 
1220 UINT FASTCALL
1221 IntEnableMenuItem(PMENU MenuObject, UINT uIDEnableItem, UINT uEnable)
1222 {
1223  PITEM MenuItem;
1224  UINT res;
1225 
1226  if (!(MenuItem = MENU_FindItem( &MenuObject, &uIDEnableItem, uEnable ))) return (UINT)-1;
1227 
1228  res = MenuItem->fState & (MF_GRAYED | MF_DISABLED);
1229 
1230  MenuItem->fState ^= (res ^ uEnable) & (MF_GRAYED | MF_DISABLED);
1231 
1232  /* If the close item in the system menu change update the close button */
1233  if (res != uEnable)
1234  {
1235  switch (MenuItem->wID) // More than just close.
1236  {
1237  case SC_CLOSE:
1238  case SC_MAXIMIZE:
1239  case SC_MINIMIZE:
1240  case SC_MOVE:
1241  case SC_RESTORE:
1242  case SC_SIZE:
1243  if (MenuObject->fFlags & MNF_SYSSUBMENU && MenuObject->spwndNotify != 0)
1244  {
1245  //RECTL rc = MenuObject->spwndNotify->rcWindow;
1246 
1247  /* Refresh the frame to reflect the change */
1248  //IntMapWindowPoints(0, MenuObject->spwndNotify, (POINT *)&rc, 2);
1249  //rc.bottom = 0;
1250  //co_UserRedrawWindow(MenuObject->spwndNotify, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
1251 
1252  // Allow UxTheme!
1253  UserPaintCaption(MenuObject->spwndNotify, DC_BUTTONS);
1254  }
1255  default:
1256  break;
1257  }
1258  }
1259  return res;
1260 }
1261 
1263 IntCheckMenuItem(PMENU MenuObject, UINT uIDCheckItem, UINT uCheck)
1264 {
1265  PITEM MenuItem;
1266  DWORD res;
1267 
1268  if (!(MenuItem = MENU_FindItem( &MenuObject, &uIDCheckItem, uCheck ))) return -1;
1269 
1270  res = (DWORD)(MenuItem->fState & MF_CHECKED);
1271 
1272  MenuItem->fState ^= (res ^ uCheck) & MF_CHECKED;
1273 
1274  return res;
1275 }
1276 
1277 BOOL FASTCALL
1278 UserSetMenuDefaultItem(PMENU MenuObject, UINT uItem, UINT fByPos)
1279 {
1280  UINT i;
1281  PITEM MenuItem = MenuObject->rgItems;
1282 
1283  if (!MenuItem) return FALSE;
1284 
1285  /* reset all default-item flags */
1286  for (i = 0; i < MenuObject->cItems; i++, MenuItem++)
1287  {
1288  MenuItem->fState &= ~MFS_DEFAULT;
1289  }
1290 
1291  /* no default item */
1292  if(uItem == (UINT)-1)
1293  {
1294  return TRUE;
1295  }
1296  MenuItem = MenuObject->rgItems;
1297  if ( fByPos )
1298  {
1299  if ( uItem >= MenuObject->cItems ) return FALSE;
1300  MenuItem[uItem].fState |= MFS_DEFAULT;
1301  return TRUE;
1302  }
1303  else
1304  {
1305  for (i = 0; i < MenuObject->cItems; i++, MenuItem++)
1306  {
1307  if (MenuItem->wID == uItem)
1308  {
1309  MenuItem->fState |= MFS_DEFAULT;
1310  return TRUE;
1311  }
1312  }
1313 
1314  }
1315  return FALSE;
1316 }
1317 
1318 UINT FASTCALL
1319 IntGetMenuDefaultItem(PMENU MenuObject, UINT fByPos, UINT gmdiFlags, DWORD *gismc)
1320 {
1321  UINT i = 0;
1322  PITEM MenuItem = MenuObject->rgItems;
1323 
1324  /* empty menu */
1325  if (!MenuItem) return -1;
1326 
1327  while ( !( MenuItem->fState & MFS_DEFAULT ) )
1328  {
1329  i++; MenuItem++;
1330  if (i >= MenuObject->cItems ) return -1;
1331  }
1332 
1333  /* default: don't return disabled items */
1334  if ( (!(GMDI_USEDISABLED & gmdiFlags)) && (MenuItem->fState & MFS_DISABLED )) return -1;
1335 
1336  /* search rekursiv when needed */
1337  if ( (gmdiFlags & GMDI_GOINTOPOPUPS) && MenuItem->spSubMenu )
1338  {
1339  UINT ret;
1340  (*gismc)++;
1341  ret = IntGetMenuDefaultItem( MenuItem->spSubMenu, fByPos, gmdiFlags, gismc );
1342  (*gismc)--;
1343  if ( -1 != ret ) return ret;
1344 
1345  /* when item not found in submenu, return the popup item */
1346  }
1347  return ( fByPos ) ? i : MenuItem->wID;
1348 }
1349 
1350 PMENU
1351 FASTCALL
1353  PMENU pMenu,
1354  int nPos)
1355 {
1356  PITEM pItem;
1357  if (!(pItem = MENU_FindItem( &pMenu, (UINT*)&nPos, MF_BYPOSITION ))) return NULL;
1358  return pItem->spSubMenu;
1359 }
1360 
1361 /***********************************************************************
1362  * MenuInitSysMenuPopup
1363  *
1364  * Grey the appropriate items in System menu.
1365  */
1366 void FASTCALL MENU_InitSysMenuPopup(PMENU menu, DWORD style, DWORD clsStyle, LONG HitTest )
1367 {
1368  BOOL gray;
1369  UINT DefItem;
1370 
1371  gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
1372  IntEnableMenuItem( menu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
1373  gray = ((style & WS_MAXIMIZE) != 0);
1374  IntEnableMenuItem( menu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
1375  gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
1376  IntEnableMenuItem( menu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
1377  gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
1378  IntEnableMenuItem( menu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
1379  gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
1380  IntEnableMenuItem( menu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
1381  gray = (clsStyle & CS_NOCLOSE) != 0;
1382 
1383  /* The menu item must keep its state if it's disabled */
1384  if(gray)
1386 
1387  /* Set default menu item */
1388  if(style & WS_MINIMIZE) DefItem = SC_RESTORE;
1389  else if(HitTest == HTCAPTION) DefItem = ((style & (WS_MAXIMIZE | WS_MINIMIZE)) ? SC_RESTORE : SC_MAXIMIZE);
1390  else DefItem = SC_CLOSE;
1391 
1392  UserSetMenuDefaultItem(menu, DefItem, MF_BYCOMMAND);
1393 }
1394 
1395 
1396 /***********************************************************************
1397  * MenuDrawPopupGlyph
1398  *
1399  * Draws popup magic glyphs (can be found in system menu).
1400  */
1401 static void FASTCALL
1402 MENU_DrawPopupGlyph(HDC dc, LPRECT r, INT_PTR popupMagic, BOOL inactive, BOOL hilite)
1403 {
1404  LOGFONTW lf;
1405  HFONT hFont, hOldFont;
1406  COLORREF clrsave;
1407  INT bkmode;
1408  WCHAR symbol;
1409  switch (popupMagic)
1410  {
1412  symbol = '2';
1413  break;
1415  symbol = '0';
1416  break;
1418  symbol = '1';
1419  break;
1421  symbol = 'r';
1422  break;
1423  default:
1424  ERR("Invalid popup magic bitmap %d\n", (int)popupMagic);
1425  return;
1426  }
1427  RtlZeroMemory(&lf, sizeof(LOGFONTW));
1428  RECTL_vInflateRect(r, -2, -2);
1429  lf.lfHeight = r->bottom - r->top;
1430  lf.lfWidth = 0;
1431  lf.lfWeight = FW_NORMAL;
1433  RtlCopyMemory(lf.lfFaceName, L"Marlett", sizeof(L"Marlett"));
1435  /* save font and text color */
1436  hOldFont = NtGdiSelectFont(dc, hFont);
1437  clrsave = GreGetTextColor(dc);
1438  bkmode = GreGetBkMode(dc);
1439  /* set color and drawing mode */
1441  if (inactive)
1442  {
1443  /* draw shadow */
1444  if (!hilite)
1445  {
1447  GreTextOutW(dc, r->left + 1, r->top + 1, &symbol, 1);
1448  }
1449  }
1451  /* draw selected symbol */
1452  GreTextOutW(dc, r->left, r->top, &symbol, 1);
1453  /* restore previous settings */
1454  IntGdiSetTextColor(dc, clrsave);
1455  NtGdiSelectFont(dc, hOldFont);
1456  IntGdiSetBkMode(dc, bkmode);
1458 }
1459 
1460 /***********************************************************************
1461  * MENU_AdjustMenuItemRect
1462  *
1463  * Adjust menu item rectangle according to scrolling state.
1464  */
1465 VOID FASTCALL
1467 {
1468  if (menu->dwArrowsOn)
1469  {
1470  UINT arrow_bitmap_height;
1471  arrow_bitmap_height = gpsi->oembmi[OBI_UPARROW].cy;
1472  rect->top += arrow_bitmap_height - menu->iTop;
1473  rect->bottom += arrow_bitmap_height - menu->iTop;
1474  }
1475 }
1476 
1477 /***********************************************************************
1478  * MENU_FindItemByCoords
1479  *
1480  * Find the item at the specified coordinates (screen coords). Does
1481  * not work for child windows and therefore should not be called for
1482  * an arbitrary system menu.
1483  */
1485 {
1486  ITEM *item;
1487  UINT i;
1488  RECT rect;
1489  PWND pWnd = ValidateHwndNoErr(menu->hWnd);
1490 
1491  if (!IntGetWindowRect(pWnd, &rect)) return NULL;
1492  if (pWnd->ExStyle & WS_EX_LAYOUTRTL)
1493  pt.x = rect.right - 1 - pt.x;
1494  else
1495  pt.x -= rect.left;
1496  pt.y -= rect.top;
1497  item = menu->rgItems;
1498  for (i = 0; i < menu->cItems; i++, item++)
1499  {
1500  //rect = item->rect;
1501  rect.left = item->xItem;
1502  rect.top = item->yItem;
1503  rect.right = item->cxItem; // Do this for now......
1504  rect.bottom = item->cyItem;
1505 
1506  MENU_AdjustMenuItemRect(menu, &rect);
1507  if (RECTL_bPointInRect(&rect, pt.x, pt.y))
1508  {
1509  if (pos) *pos = i;
1510  return item;
1511  }
1512  }
1513  return NULL;
1514 }
1515 
1517 {
1518  MENU *menu = UserGetMenuObject(hMenu);
1519  UINT pos;
1520 
1521  /*FIXME: Do we have to handle hWnd here? */
1522  if (!menu) return -1;
1523  if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
1524  return pos;
1525 }
1526 
1527 /***********************************************************************
1528  * MenuFindItemByKey
1529  *
1530  * Find the menu item selected by a key press.
1531  * Return item id, -1 if none, -2 if we should close the menu.
1532  */
1534  WCHAR Key, BOOL ForceMenuChar)
1535 {
1536  LRESULT MenuChar;
1537  WORD Flags = 0;
1538 
1539  TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)Key, Key, menu );
1540 
1541  if (!menu || !VerifyMenu(menu))
1542  menu = co_IntGetSubMenu( UserGetMenuObject(WndOwner->SystemMenu), 0 );
1543  if (menu)
1544  {
1545  ITEM *item = menu->rgItems;
1546 
1547  if ( !ForceMenuChar )
1548  {
1549  UINT i;
1551 
1552  for (i = 0; i < menu->cItems; i++, item++)
1553  {
1554  LPWSTR text = item->Xlpstr;
1555  if( text)
1556  {
1557  const WCHAR *p = text - 2;
1558  do
1559  {
1560  const WCHAR *q = p + 2;
1561  p = wcschr (q, '&');
1562  if (!p && cjk) p = wcschr (q, '\036'); /* Japanese Win16 */
1563  }
1564  while (p != NULL && p [1] == '&');
1565  if (p && (towupper(p[1]) == towupper(Key))) return i;
1566  }
1567  }
1568  }
1569 
1570  Flags |= menu->fFlags & MNF_POPUP ? MF_POPUP : 0;
1571  Flags |= menu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0;
1572 
1573  MenuChar = co_IntSendMessage( UserHMGetHandle(WndOwner), WM_MENUCHAR,
1575  if (HIWORD(MenuChar) == MNC_EXECUTE) return LOWORD(MenuChar);
1576  if (HIWORD(MenuChar) == MNC_CLOSE) return (UINT)(-2);
1577  }
1578  return (UINT)(-1);
1579 }
1580 
1581 /***********************************************************************
1582  * MenuGetBitmapItemSize
1583  *
1584  * Get the size of a bitmap item.
1585  */
1586 static void FASTCALL MENU_GetBitmapItemSize(PITEM lpitem, SIZE *size, PWND WndOwner)
1587 {
1588  BITMAP bm;
1589  HBITMAP bmp = lpitem->hbmp;
1590 
1591  size->cx = size->cy = 0;
1592 
1593  /* check if there is a magic menu item associated with this item */
1594  if (IS_MAGIC_BITMAP(bmp))
1595  {
1596  switch((INT_PTR) bmp)
1597  {
1598  case (INT_PTR)HBMMENU_CALLBACK:
1599  {
1600  MEASUREITEMSTRUCT measItem;
1601  measItem.CtlType = ODT_MENU;
1602  measItem.CtlID = 0;
1603  measItem.itemID = lpitem->wID;
1604  measItem.itemWidth = lpitem->cxItem - lpitem->xItem; //lpitem->Rect.right - lpitem->Rect.left;
1605  measItem.itemHeight = lpitem->cyItem - lpitem->yItem; //lpitem->Rect.bottom - lpitem->Rect.top;
1606  measItem.itemData = lpitem->dwItemData;
1607  co_IntSendMessage( UserHMGetHandle(WndOwner), WM_MEASUREITEM, 0, (LPARAM)&measItem);
1608  size->cx = measItem.itemWidth;
1609  size->cy = measItem.itemHeight;
1610  TRACE("HBMMENU_CALLBACK Height %d Width %d\n",measItem.itemHeight,measItem.itemWidth);
1611  return;
1612  }
1613  break;
1614 
1615  case (INT_PTR) HBMMENU_SYSTEM:
1616  if (lpitem->dwItemData)
1617  {
1618  bmp = (HBITMAP) lpitem->dwItemData;
1619  break;
1620  }
1621  /* fall through */
1624  case (INT_PTR) HBMMENU_MBAR_CLOSE:
1631  /* FIXME: Why we need to subtract these magic values? */
1632  /* to make them smaller than the menu bar? */
1633  size->cx = UserGetSystemMetrics(SM_CXSIZE) - 2;
1634  size->cy = UserGetSystemMetrics(SM_CYSIZE) - 4;
1635  return;
1636  }
1637  }
1638 
1639  if (GreGetObject(bmp, sizeof(BITMAP), &bm))
1640  {
1641  size->cx = bm.bmWidth;
1642  size->cy = bm.bmHeight;
1643  }
1644 }
1645 
1646 /***********************************************************************
1647  * MenuDrawBitmapItem
1648  *
1649  * Draw a bitmap item.
1650  */
1651 static void FASTCALL MENU_DrawBitmapItem(HDC hdc, PITEM lpitem, const RECT *rect,
1652  PMENU Menu, PWND WndOwner, UINT odaction, BOOL MenuBar)
1653 {
1654  BITMAP bm;
1655  DWORD rop;
1656  HDC hdcMem;
1657  HBITMAP bmp;
1658  int w = rect->right - rect->left;
1659  int h = rect->bottom - rect->top;
1660  int bmp_xoffset = 0;
1661  int left, top;
1662  BOOL flat_menu;
1663  HBITMAP hbmToDraw = lpitem->hbmp;
1664  bmp = hbmToDraw;
1665 
1666  UserSystemParametersInfo(SPI_GETFLATMENU, 0, &flat_menu, 0);
1667 
1668  /* Check if there is a magic menu item associated with this item */
1669  if (IS_MAGIC_BITMAP(hbmToDraw))
1670  {
1671  UINT flags = 0;
1672  RECT r;
1673 
1674  r = *rect;
1675  switch ((INT_PTR)hbmToDraw)
1676  {
1677  case (INT_PTR)HBMMENU_SYSTEM:
1678  if (lpitem->dwItemData)
1679  {
1680  if (ValidateHwndNoErr((HWND)lpitem->dwItemData))
1681  {
1682  ERR("Get Item Data from this Window!!!\n");
1683  }
1684 
1685  ERR("Draw Bitmap\n");
1686  bmp = (HBITMAP)lpitem->dwItemData;
1687  if (!GreGetObject( bmp, sizeof(bm), &bm )) return;
1688  }
1689  else
1690  {
1691  PCURICON_OBJECT pIcon = NULL;
1692  //if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
1693  //bmp = BmpSysMenu;
1694  //if (! GreGetObject(bmp, sizeof(bm), &bm)) return;
1695  /* only use right half of the bitmap */
1696  //bmp_xoffset = bm.bmWidth / 2;
1697  //bm.bmWidth -= bmp_xoffset;
1698  if (WndOwner)
1699  {
1700  pIcon = NC_IconForWindow(WndOwner);
1701  // FIXME: NC_IconForWindow should reference it for us */
1702  if (pIcon) UserReferenceObject(pIcon);
1703  }
1704  ERR("Draw ICON\n");
1705  if (pIcon)
1706  {
1709  LONG x = rect->left - cx/2 + 1 + (rect->bottom - rect->top)/2; // this is really what Window does
1710  LONG y = (rect->top + rect->bottom)/2 - cy/2; // center
1711  UserDrawIconEx(hdc, x, y, pIcon, cx, cy, 0, NULL, DI_NORMAL);
1712  UserDereferenceObject(pIcon);
1713  }
1714  return;
1715  }
1716  goto got_bitmap;
1719  break;
1721  r.right += 1;
1723  break;
1725  r.right += 1;
1727  break;
1730  break;
1733  break;
1734  case (INT_PTR)HBMMENU_CALLBACK:
1735  {
1736  DRAWITEMSTRUCT drawItem;
1737  POINT origorg;
1738  drawItem.CtlType = ODT_MENU;
1739  drawItem.CtlID = 0;
1740  drawItem.itemID = lpitem->wID;
1741  drawItem.itemAction = odaction;
1742  drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1743  drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1744  drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1745  drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1746  drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1747  drawItem.itemState |= (!(Menu->fFlags & MNF_UNDERLINE))?ODS_NOACCEL:0;
1748  drawItem.itemState |= (Menu->fFlags & MNF_INACTIVE)?ODS_INACTIVE:0;
1749  drawItem.hwndItem = (HWND)UserHMGetHandle(Menu);
1750  drawItem.hDC = hdc;
1751  drawItem.rcItem = *rect;
1752  drawItem.itemData = lpitem->dwItemData;
1753  /* some applications make this assumption on the DC's origin */
1754  GreSetViewportOrgEx( hdc, lpitem->xItem, lpitem->yItem, &origorg);
1755  RECTL_vOffsetRect( &drawItem.rcItem, - lpitem->xItem, - lpitem->yItem);
1756  co_IntSendMessage( UserHMGetHandle(WndOwner), WM_DRAWITEM, 0, (LPARAM)&drawItem);
1757  GreSetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1758  return;
1759  }
1760  break;
1761 
1766  MENU_DrawPopupGlyph(hdc, &r, (INT_PTR)hbmToDraw, lpitem->fState & MF_GRAYED, lpitem->fState & MF_HILITE);
1767  return;
1768  }
1769  RECTL_vInflateRect(&r, -1, -1);
1770  if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
1772  return;
1773  }
1774 
1775  if (!bmp || !GreGetObject( bmp, sizeof(bm), &bm )) return;
1776 
1777  got_bitmap:
1780  /* handle fontsize > bitmap_height */
1781  top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
1782  left=rect->left;
1783  rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
1784  if ((lpitem->fState & MF_HILITE) && lpitem->hbmp)
1786  if (MenuBar &&
1787  !flat_menu &&
1788  (lpitem->fState & (MF_HILITE | MF_GRAYED)) == MF_HILITE)
1789  {
1790  ++left;
1791  ++top;
1792  }
1793  NtGdiBitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop , 0, 0);
1795 }
1796 
1797 LONG
1799 {
1800  static DWORD units;
1801 
1802  if (!units)
1803  {
1804  HDC hdc;
1805  SIZE size;
1806 
1807  if ((hdc = UserGetDCEx(NULL, NULL, DCX_CACHE)))
1808  {
1810  if (size.cx) units = MAKELONG( size.cx, size.cy );
1811  UserReleaseDC( 0, hdc, FALSE);
1812  }
1813  }
1814  return units;
1815 }
1816 
1817 
1818 /***********************************************************************
1819  * MenuCalcItemSize
1820  *
1821  * Calculate the size of the menu item and store it in lpitem->rect.
1822  */
1823 static void FASTCALL MENU_CalcItemSize( HDC hdc, PITEM lpitem, PMENU Menu, PWND pwndOwner,
1824  INT orgX, INT orgY, BOOL menuBar, BOOL textandbmp)
1825 {
1826  WCHAR *p;
1827  UINT check_bitmap_width = UserGetSystemMetrics( SM_CXMENUCHECK );
1828  UINT arrow_bitmap_width;
1829  RECT Rect;
1830  INT itemheight = 0;
1831 
1832  TRACE("dc=%x owner=%x (%d,%d)\n", hdc, pwndOwner, orgX, orgY);
1833 
1834  arrow_bitmap_width = gpsi->oembmi[OBI_MNARROW].cx;
1835 
1837 
1838  RECTL_vSetRect( &Rect, orgX, orgY, orgX, orgY );
1839 
1840  if (lpitem->fType & MF_OWNERDRAW)
1841  {
1842  MEASUREITEMSTRUCT mis;
1843  mis.CtlType = ODT_MENU;
1844  mis.CtlID = 0;
1845  mis.itemID = lpitem->wID;
1846  mis.itemData = lpitem->dwItemData;
1848  mis.itemWidth = 0;
1849  co_IntSendMessage( UserHMGetHandle(pwndOwner), WM_MEASUREITEM, 0, (LPARAM)&mis );
1850  /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1851  * width of a menufont character to the width of an owner-drawn menu.
1852  */
1853  Rect.right += mis.itemWidth + 2 * MenuCharSize.cx;
1854  if (menuBar) {
1855  /* under at least win95 you seem to be given a standard
1856  height for the menu and the height value is ignored */
1858  } else
1859  Rect.bottom += mis.itemHeight;
1860  // Or this,
1861  //lpitem->cxBmp = mis.itemWidth;
1862  //lpitem->cyBmp = mis.itemHeight;
1863  TRACE("MF_OWNERDRAW Height %d Width %d\n",mis.itemHeight,mis.itemWidth);
1864  TRACE("MF_OWNERDRAW id=%04lx size=%dx%d cx %d cy %d\n",
1865  lpitem->wID, Rect.right-Rect.left,
1866  Rect.bottom-Rect.top, MenuCharSize.cx, MenuCharSize.cy);
1867 
1868  lpitem->xItem = Rect.left;
1869  lpitem->yItem = Rect.top;
1870  lpitem->cxItem = Rect.right;
1871  lpitem->cyItem = Rect.bottom;
1872 
1873  return;
1874  }
1875 
1876  lpitem->xItem = orgX;
1877  lpitem->yItem = orgY;
1878  lpitem->cxItem = orgX;
1879  lpitem->cyItem = orgY;
1880 
1881  if (lpitem->fType & MF_SEPARATOR)
1882  {
1883  lpitem->cyItem += UserGetSystemMetrics( SM_CYMENUSIZE)/2;//SEPARATOR_HEIGHT;
1884  if( !menuBar)
1885  lpitem->cxItem += arrow_bitmap_width + MenuCharSize.cx;
1886  return;
1887  }
1888 
1889  lpitem->dxTab = 0;
1890 
1891  if (lpitem->hbmp)
1892  {
1893  SIZE size;
1894 
1895  if (!menuBar) {
1896  MENU_GetBitmapItemSize(lpitem, &size, pwndOwner );
1897  /* Keep the size of the bitmap in callback mode to be able
1898  * to draw it correctly */
1899  lpitem->cxBmp = size.cx;
1900  lpitem->cyBmp = size.cy;
1901  Menu->cxTextAlign = max(Menu->cxTextAlign, size.cx);
1902  lpitem->cxItem += size.cx + 2;
1903  itemheight = size.cy + 2;
1904 
1905  if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
1906  lpitem->cxItem += 2 * check_bitmap_width;
1907  lpitem->cxItem += 4 + MenuCharSize.cx;
1908  lpitem->dxTab = lpitem->cxItem;
1909  lpitem->cxItem += arrow_bitmap_width;
1910  } else /* hbmpItem & MenuBar */ {
1911  MENU_GetBitmapItemSize(lpitem, &size, pwndOwner );
1912  lpitem->cxItem += size.cx;
1913  if( lpitem->Xlpstr) lpitem->cxItem += 2;
1914  itemheight = size.cy;
1915 
1916  /* Special case: Minimize button doesn't have a space behind it. */
1917  if (lpitem->hbmp == (HBITMAP)HBMMENU_MBAR_MINIMIZE ||
1918  lpitem->hbmp == (HBITMAP)HBMMENU_MBAR_MINIMIZE_D)
1919  lpitem->cxItem -= 1;
1920  }
1921  }
1922  else if (!menuBar) {
1923  if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
1924  lpitem->cxItem += check_bitmap_width;
1925  lpitem->cxItem += 4 + MenuCharSize.cx;
1926  lpitem->dxTab = lpitem->cxItem;
1927  lpitem->cxItem += arrow_bitmap_width;
1928  }
1929 
1930  /* it must be a text item - unless it's the system menu */
1931  if (!(lpitem->fType & MF_SYSMENU) && lpitem->Xlpstr) {
1932  HFONT hfontOld = NULL;
1933  RECT rc;// = lpitem->Rect;
1934  LONG txtheight, txtwidth;
1935 
1936  rc.left = lpitem->xItem;
1937  rc.top = lpitem->yItem;
1938  rc.right = lpitem->cxItem; // Do this for now......
1939  rc.bottom = lpitem->cyItem;
1940 
1941  if ( lpitem->fState & MFS_DEFAULT ) {
1942  hfontOld = NtGdiSelectFont( hdc, ghMenuFontBold );
1943  }
1944  if (menuBar) {
1945  txtheight = DrawTextW( hdc, lpitem->Xlpstr, -1, &rc, DT_SINGLELINE|DT_CALCRECT);
1946 
1947  lpitem->cxItem += rc.right - rc.left;
1948  itemheight = max( max( itemheight, txtheight), UserGetSystemMetrics( SM_CYMENU) - 1);
1949 
1950  lpitem->cxItem += 2 * MenuCharSize.cx;
1951  } else {
1952  if ((p = wcschr( lpitem->Xlpstr, '\t' )) != NULL) {
1953  RECT tmprc = rc;
1954  LONG tmpheight;
1955  int n = (int)( p - lpitem->Xlpstr);
1956  /* Item contains a tab (only meaningful in popup menus) */
1957  /* get text size before the tab */
1958  txtheight = DrawTextW( hdc, lpitem->Xlpstr, n, &rc,
1960  txtwidth = rc.right - rc.left;
1961  p += 1; /* advance past the Tab */
1962  /* get text size after the tab */
1963  tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1965  lpitem->dxTab += txtwidth;
1966  txtheight = max( txtheight, tmpheight);
1967  txtwidth += MenuCharSize.cx + /* space for the tab */
1968  tmprc.right - tmprc.left; /* space for the short cut */
1969  } else {
1970  txtheight = DrawTextW( hdc, lpitem->Xlpstr, -1, &rc,
1972  txtwidth = rc.right - rc.left;
1973  lpitem->dxTab += txtwidth;
1974  }
1975  lpitem->cxItem += 2 + txtwidth;
1976  itemheight = max( itemheight,
1977  max( txtheight + 2, MenuCharSize.cy + 4));
1978  }
1979  if (hfontOld)
1980  {
1981  NtGdiSelectFont (hdc, hfontOld);
1982  }
1983  } else if( menuBar) {
1984  itemheight = max( itemheight, UserGetSystemMetrics(SM_CYMENU)-1);
1985  }
1986  lpitem->cyItem += itemheight;
1987  TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->xItem, lpitem->yItem, lpitem->cxItem, lpitem->cyItem);
1988 }
1989 
1990 /***********************************************************************
1991  * MENU_GetMaxPopupHeight
1992  */
1993 static UINT
1995 {
1996  if (lppop->cyMax)
1997  {
1998  //ERR("MGMaxPH cyMax %d\n",lppop->cyMax);
1999  return lppop->cyMax;
2000  }
2001  //ERR("MGMaxPH SyMax %d\n",UserGetSystemMetrics(SM_CYSCREEN) - UserGetSystemMetrics(SM_CYBORDER));
2003 }
2004 
2005 /***********************************************************************
2006  * MenuPopupMenuCalcSize
2007  *
2008  * Calculate the size of a popup menu.
2009  */
2010 static void FASTCALL MENU_PopupMenuCalcSize(PMENU Menu, PWND WndOwner)
2011 {
2012  PITEM lpitem;
2013  HDC hdc;
2014  int start, i;
2015  int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
2016  BOOL textandbmp = FALSE;
2017 
2018  Menu->cxMenu = Menu->cyMenu = 0;
2019  if (Menu->cItems == 0) return;
2020 
2022 
2024 
2025  start = 0;
2026  maxX = 2 + 1;
2027 
2028  Menu->cxTextAlign = 0;
2029 
2030  while (start < Menu->cItems)
2031  {
2032  lpitem = &Menu->rgItems[start];
2033  orgX = maxX;
2034  if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
2035  orgX += MENU_COL_SPACE;
2036  orgY = MENU_TOP_MARGIN;
2037 
2038  maxTab = maxTabWidth = 0;
2039  /* Parse items until column break or end of menu */
2040  for (i = start; i < Menu->cItems; i++, lpitem++)
2041  {
2042  if (i != start &&
2043  (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
2044 
2045  MENU_CalcItemSize(hdc, lpitem, Menu, WndOwner, orgX, orgY, FALSE, textandbmp);
2046  maxX = max(maxX, lpitem->cxItem);
2047  orgY = lpitem->cyItem;
2048  if (IS_STRING_ITEM(lpitem->fType) && lpitem->dxTab )
2049  {
2050  maxTab = max( maxTab, lpitem->dxTab );
2051  maxTabWidth = max(maxTabWidth, lpitem->cxItem - lpitem->dxTab);
2052  }
2053  if( lpitem->Xlpstr && lpitem->hbmp) textandbmp = TRUE;
2054  }
2055 
2056  /* Finish the column (set all items to the largest width found) */
2057  maxX = max( maxX, maxTab + maxTabWidth );
2058  for (lpitem = &Menu->rgItems[start]; start < i; start++, lpitem++)
2059  {
2060  lpitem->cxItem = maxX;
2061  if (IS_STRING_ITEM(lpitem->fType) && lpitem->dxTab)
2062  lpitem->dxTab = maxTab;
2063  }
2064  Menu->cyMenu = max(Menu->cyMenu, orgY);
2065  }
2066 
2067  Menu->cxMenu = maxX;
2068  /* if none of the items have both text and bitmap then
2069  * the text and bitmaps are all aligned on the left. If there is at
2070  * least one item with both text and bitmap then bitmaps are
2071  * on the left and texts left aligned with the right hand side
2072  * of the bitmaps */
2073  if( !textandbmp) Menu->cxTextAlign = 0;
2074 
2075  /* space for 3d border */
2076  Menu->cyMenu += MENU_BOTTOM_MARGIN;
2077  Menu->cxMenu += 2;
2078 
2079  /* Adjust popup height if it exceeds maximum */
2080  maxHeight = MENU_GetMaxPopupHeight(Menu);
2081  Menu->iMaxTop = Menu->cyMenu - MENU_TOP_MARGIN;
2082  if (Menu->cyMenu >= maxHeight)
2083  {
2084  Menu->cyMenu = maxHeight;
2085  Menu->dwArrowsOn = 1;
2086  }
2087  else
2088  {
2089  Menu->dwArrowsOn = 0;
2090  }
2091  UserReleaseDC( 0, hdc, FALSE );
2092 }
2093 
2094 /***********************************************************************
2095  * MENU_MenuBarCalcSize
2096  *
2097  * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
2098  * height is off by 1 pixel which causes lengthy window relocations when
2099  * active document window is maximized/restored.
2100  *
2101  * Calculate the size of the menu bar.
2102  */
2103 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect, PMENU lppop, PWND pwndOwner )
2104 {
2105  ITEM *lpitem;
2106  UINT start, i, helpPos;
2107  int orgX, orgY, maxY;
2108 
2109  if ((lprect == NULL) || (lppop == NULL)) return;
2110  if (lppop->cItems == 0) return;
2111  //TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
2112  lppop->cxMenu = lprect->right - lprect->left;
2113  lppop->cyMenu = 0;
2114  maxY = lprect->top;
2115  start = 0;
2116  helpPos = ~0U;
2117  lppop->cxTextAlign = 0;
2118  while (start < lppop->cItems)
2119  {
2120  lpitem = &lppop->rgItems[start];
2121  orgX = lprect->left;
2122  orgY = maxY;
2123 
2124  /* Parse items until line break or end of menu */
2125  for (i = start; i < lppop->cItems; i++, lpitem++)
2126  {
2127  if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
2128  if ((i != start) &&
2129  (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
2130 
2131  TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
2132  //debug_print_menuitem (" item: ", lpitem, "");
2133  //MENU_CalcItemSize( hdc, lpitem, pwndOwner, orgX, orgY, TRUE, lppop );
2134  MENU_CalcItemSize(hdc, lpitem, lppop, pwndOwner, orgX, orgY, TRUE, FALSE);
2135 
2136  if (lpitem->cxItem > lprect->right)
2137  {
2138  if (i != start) break;
2139  else lpitem->cxItem = lprect->right;
2140  }
2141  maxY = max( maxY, lpitem->cyItem );
2142  orgX = lpitem->cxItem;
2143  }
2144 
2145  /* Finish the line (set all items to the largest height found) */
2146 
2147 /* FIXME: Is this really needed? */ /*NO! it is not needed, why make the
2148  HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
2149 #if 0
2150  while (start < i) lppop->rgItems[start++].cyItem = maxY;
2151 #endif
2152  start = i; /* This works! */
2153  }
2154 
2155  lprect->bottom = maxY + 1;
2156  lppop->cyMenu = lprect->bottom - lprect->top;
2157 
2158  /* Flush right all items between the MF_RIGHTJUSTIFY and */
2159  /* the last item (if several lines, only move the last line) */
2160  if (helpPos == ~0U) return;
2161  lpitem = &lppop->rgItems[lppop->cItems-1];
2162  orgY = lpitem->yItem;
2163  orgX = lprect->right;
2164  for (i = lppop->cItems - 1; i >= helpPos; i--, lpitem--) {
2165  if (lpitem->yItem != orgY) break; /* Other line */
2166  if (lpitem->cxItem >= orgX) break; /* Too far right already */
2167  lpitem->xItem += orgX - lpitem->cxItem;
2168  lpitem->cxItem = orgX;
2169  orgX = lpitem->xItem;
2170  }
2171 }
2172 
2173 /***********************************************************************
2174  * MENU_DrawScrollArrows
2175  *
2176  * Draw scroll arrows.
2177  */
2178 static void MENU_DrawScrollArrows(PMENU lppop, HDC hdc)
2179 {
2180  UINT arrow_bitmap_width, arrow_bitmap_height;
2181  RECT rect, dfcrc;
2182  UINT Flags = 0;
2183 
2184  arrow_bitmap_width = gpsi->oembmi[OBI_DNARROW].cx;
2185  arrow_bitmap_height = gpsi->oembmi[OBI_DNARROW].cy;
2186 
2187  rect.left = 0;
2188  rect.top = 0;
2189  rect.right = lppop->cxMenu;
2190  rect.bottom = arrow_bitmap_height;
2192  dfcrc.left = (lppop->cxMenu - arrow_bitmap_width) / 2;
2193  dfcrc.top = 0;
2194  dfcrc.right = arrow_bitmap_width;
2195  dfcrc.bottom = arrow_bitmap_height;
2196  DrawFrameControl(hdc, &dfcrc, DFC_MENU, (lppop->iTop ? 0 : DFCS_INACTIVE)|DFCS_MENUARROWUP);
2197 
2198  rect.top = lppop->cyMenu - arrow_bitmap_height;
2199  rect.bottom = lppop->cyMenu;
2201  if (!(lppop->iTop < lppop->iMaxTop - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height)))
2202  Flags = DFCS_INACTIVE;
2203  dfcrc.left = (lppop->cxMenu - arrow_bitmap_width) / 2;
2204  dfcrc.top = lppop->cyMenu - arrow_bitmap_height;
2205  dfcrc.right = arrow_bitmap_width;
2206  dfcrc.bottom = lppop->cyMenu;
2208 }
2209 
2210 /***********************************************************************
2211  * MenuDrawMenuItem
2212  *
2213  * Draw a single menu item.
2214  */
2215 static void FASTCALL MENU_DrawMenuItem(PWND Wnd, PMENU Menu, PWND WndOwner, HDC hdc,
2216  PITEM lpitem, UINT Height, BOOL menuBar, UINT odaction)
2217 {
2218  RECT rect;
2219  PWCHAR Text;
2220  BOOL flat_menu = FALSE;
2221  int bkgnd;
2222  UINT arrow_bitmap_width = 0;
2223  //RECT bmprc;
2224 
2225  if (!menuBar) {
2226  arrow_bitmap_width = gpsi->oembmi[OBI_MNARROW].cx;
2227  }
2228 
2229  if (lpitem->fType & MF_SYSMENU)
2230  {
2231  if (!(Wnd->style & WS_MINIMIZE))
2232  {
2233  NC_GetInsideRect(Wnd, &rect);
2235  }
2236  return;
2237  }
2238 
2239  UserSystemParametersInfo (SPI_GETFLATMENU, 0, &flat_menu, 0);
2240  bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
2241 
2242  /* Setup colors */
2243 
2244  if (lpitem->fState & MF_HILITE)
2245  {
2246  if(menuBar && !flat_menu) {
2249  } else {
2250  if (lpitem->fState & MF_GRAYED)
2252  else
2255  }
2256  }
2257  else
2258  {
2259  if (lpitem->fState & MF_GRAYED)
2261  else
2263  IntGdiSetBkColor( hdc, IntGetSysColor( bkgnd ) );
2264  }
2265 
2266  //TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->Rect));
2267  //rect = lpitem->Rect;
2268  rect.left = lpitem->xItem;
2269  rect.top = lpitem->yItem;
2270  rect.right = lpitem->cxItem; // Do this for now......
2271  rect.bottom = lpitem->cyItem;
2272 
2273  MENU_AdjustMenuItemRect(Menu, &rect);
2274 
2275  if (lpitem->fType & MF_OWNERDRAW)
2276  {
2277  /*
2278  ** Experimentation under Windows reveals that an owner-drawn
2279  ** menu is given the rectangle which includes the space it requested
2280  ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
2281  ** and a popup-menu arrow. This is the value of lpitem->rect.
2282  ** Windows will leave all drawing to the application except for
2283  ** the popup-menu arrow. Windows always draws that itself, after
2284  ** the menu owner has finished drawing.
2285  */
2286  DRAWITEMSTRUCT dis;
2287  COLORREF old_bk, old_text;
2288 
2289  dis.CtlType = ODT_MENU;
2290  dis.CtlID = 0;
2291  dis.itemID = lpitem->wID;
2292  dis.itemData = (DWORD)lpitem->dwItemData;
2293  dis.itemState = 0;
2294  if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
2295  if (lpitem->fState & MF_DEFAULT) dis.itemState |= ODS_DEFAULT;
2296  if (lpitem->fState & MF_DISABLED) dis.itemState |= ODS_DISABLED;
2297  if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED | ODS_DISABLED;
2298  if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
2299  if (!(Menu->fFlags & MNF_UNDERLINE)) dis.itemState |= ODS_NOACCEL;
2300  if (Menu->fFlags & MNF_INACTIVE) dis.itemState |= ODS_INACTIVE;
2301  dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
2302  dis.hwndItem = (HWND) UserHMGetHandle(Menu);
2303  dis.hDC = hdc;
2304  dis.rcItem = rect;
2305  TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
2306  "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", Wnd,
2307  dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
2308  dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
2309  dis.rcItem.bottom);
2310  TRACE("Ownerdraw: Width %d Height %d\n", dis.rcItem.right-dis.rcItem.left, dis.rcItem.bottom-dis.rcItem.top);
2311  old_bk = GreGetBkColor(hdc);
2312  old_text = GreGetTextColor(hdc);
2313  co_IntSendMessage(UserHMGetHandle(WndOwner), WM_DRAWITEM, 0, (LPARAM) &dis);
2314  IntGdiSetBkColor(hdc, old_bk);
2315  IntGdiSetTextColor(hdc, old_text);
2316  /* Draw the popup-menu arrow */
2317  if (!menuBar && lpitem->spSubMenu)
2318  {
2319  RECT rectTemp;
2320  RtlCopyMemory(&rectTemp, &rect, sizeof(RECT));
2321  rectTemp.left = rectTemp.right - UserGetSystemMetrics(SM_CXMENUCHECK);
2323  }
2324  return;
2325  }
2326 
2327  if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
2328 
2329  if (lpitem->fState & MF_HILITE)
2330  {
2331  if (flat_menu)
2332  {
2333  RECTL_vInflateRect (&rect, -1, -1);
2334  FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENUHILIGHT));
2335  RECTL_vInflateRect (&rect, 1, 1);
2337  }
2338  else
2339  {
2340  if (menuBar)
2341  {
2344  }
2345  else
2346  {
2348  }
2349  }
2350  }
2351  else
2352  FillRect( hdc, &rect, IntGetSysColorBrush(bkgnd) );
2353 
2355 
2356  /* vertical separator */
2357  if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
2358  {
2359  HPEN oldPen;
2360  RECT rc = rect;
2361 
2362  rc.left -= 3;//MENU_COL_SPACE / 2 + 1; == 3!!
2363  rc.top = 3;
2364  rc.bottom = Height - 3;
2365  if (flat_menu)
2366  {
2367  oldPen = NtGdiSelectPen( hdc, NtGdiGetStockObject(DC_PEN) );
2369  GreMoveTo( hdc, rc.left, rc.top, NULL );
2370  NtGdiLineTo( hdc, rc.left, rc.bottom );
2371  NtGdiSelectPen( hdc, oldPen );
2372  }
2373  else
2374  DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
2375  }
2376 
2377  /* horizontal separator */
2378  if (lpitem->fType & MF_SEPARATOR)
2379  {
2380  HPEN oldPen;
2381  RECT rc = rect;
2382 
2383  rc.left++;
2384  rc.right--;
2385  rc.top = (rc.top + rc.bottom) / 2 - 1;
2386  if (flat_menu)
2387  {
2388  oldPen = NtGdiSelectPen( hdc, NtGdiGetStockObject(DC_PEN) );
2390  GreMoveTo( hdc, rc.left, rc.top, NULL );
2391  NtGdiLineTo( hdc, rc.right, rc.top );
2392  NtGdiSelectPen( hdc, oldPen );
2393  }
2394  else
2395  DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
2396  return;
2397  }
2398 #if 0
2399  /* helper lines for debugging */
2400  /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
2404  GreMoveTo(hdc, rect.left, (rect.top + rect.bottom) / 2, NULL);
2405  NtGdiLineTo(hdc, rect.right, (rect.top + rect.bottom) / 2);
2406 #endif
2407 #if 0 // breaks mdi menu bar icons.
2408  if (lpitem->hbmp) {
2409  /* calculate the bitmap rectangle in coordinates relative
2410  * to the item rectangle */
2411  if( menuBar) {
2412  if( lpitem->hbmp == HBMMENU_CALLBACK)
2413  bmprc.left = 3;
2414  else
2415  bmprc.left = lpitem->Xlpstr ? MenuCharSize.cx : 0;
2416  }
2417  else if ((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK)
2418  bmprc.left = 4;
2419  else if ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)
2420  bmprc.left = 2;
2421  else
2422  bmprc.left = 4 + UserGetSystemMetrics(SM_CXMENUCHECK);
2423 
2424  bmprc.right = bmprc.left + lpitem->cxBmp;
2425 
2426  if( menuBar && !(lpitem->hbmp == HBMMENU_CALLBACK))
2427  bmprc.top = 0;
2428  else
2429  bmprc.top = (rect.bottom - rect.top - lpitem->cyBmp) / 2;
2430 
2431  bmprc.bottom = bmprc.top + lpitem->cyBmp;
2432  }
2433 #endif
2434  if (!menuBar)
2435  {
2436  HBITMAP bm;
2437  INT y = rect.top + rect.bottom;
2438  RECT rc = rect;
2439  BOOL checked = FALSE;
2440  UINT check_bitmap_width = UserGetSystemMetrics( SM_CXMENUCHECK );
2441  UINT check_bitmap_height = UserGetSystemMetrics( SM_CYMENUCHECK );
2442  /* Draw the check mark
2443  *
2444  * FIXME:
2445  * Custom checkmark bitmaps are monochrome but not always 1bpp.
2446  */
2447  if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK)) {
2448  bm = (lpitem->fState & MF_CHECKED) ? lpitem->hbmpChecked :
2449  lpitem->hbmpUnchecked;
2450  if (bm) /* we have a custom bitmap */
2451  {
2453 
2454  NtGdiSelectBitmap( hdcMem, bm );
2455  NtGdiBitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
2456  check_bitmap_width, check_bitmap_height,
2457  hdcMem, 0, 0, SRCCOPY, 0,0);
2459  checked = TRUE;
2460  }
2461  else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
2462  {
2463  RECT r;
2464  r = rect;
2465  r.right = r.left + check_bitmap_width;
2467  (lpitem->fType & MFT_RADIOCHECK) ?
2469  checked = TRUE;
2470  }
2471  }
2472  if ( lpitem->hbmp )//&& !( checked && ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)))
2473  {
2474  RECT bmpRect = rect;
2475  if (!((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP) && !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
2476  bmpRect.left += check_bitmap_width + 2;
2477  if (!(checked && ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)))
2478  {
2479  bmpRect.right = bmpRect.left + lpitem->cxBmp;
2480  MENU_DrawBitmapItem(hdc, lpitem, &bmpRect, Menu, WndOwner, odaction, menuBar);
2481  }
2482  }
2483  /* Draw the popup-menu arrow */
2484  if (lpitem->spSubMenu)
2485  {
2486  RECT rectTemp;
2487  RtlCopyMemory(&rectTemp, &rect, sizeof(RECT));
2488  rectTemp.left = rectTemp.right - check_bitmap_width;
2490  }
2491  rect.left += 4;
2492  if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
2493  rect.left += check_bitmap_width;
2494  rect.right -= arrow_bitmap_width;
2495  }
2496  else if( lpitem->hbmp)
2497  { /* Draw the bitmap */
2498  MENU_DrawBitmapItem(hdc, lpitem, &rect/*bmprc*/, Menu, WndOwner, odaction, menuBar);
2499  }
2500 
2501  /* process text if present */
2502  if (lpitem->Xlpstr)
2503  {
2504  int i = 0;
2505  HFONT hfontOld = 0;
2506 
2507  UINT uFormat = menuBar ?
2510 
2511  if (((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP))
2512  rect.left += max(0, (int)(Menu->cxTextAlign - UserGetSystemMetrics(SM_CXMENUCHECK)));
2513  else
2514  rect.left += Menu->cxTextAlign;
2515 
2516  if ( lpitem->fState & MFS_DEFAULT )
2517  {
2518  hfontOld = NtGdiSelectFont(hdc, ghMenuFontBold);
2519  }
2520 
2521  if (menuBar) {
2522  if( lpitem->hbmp)
2523  rect.left += lpitem->cxBmp;
2524  if( !(lpitem->hbmp == HBMMENU_CALLBACK))
2525  rect.left += MenuCharSize.cx;
2526  rect.right -= MenuCharSize.cx;
2527  }
2528 
2529  Text = lpitem->Xlpstr;
2530  if(Text)
2531  {
2532  for (i = 0; Text[i]; i++)
2533  if (Text[i] == L'\t' || Text[i] == L'\b')
2534  break;
2535  }
2536 
2537  if (menuBar &&
2538  !flat_menu &&
2539  (lpitem->fState & (MF_HILITE | MF_GRAYED)) == MF_HILITE)
2540  {
2541  RECTL_vOffsetRect(&rect, +1, +1);
2542  }
2543 
2544  if (!menuBar)
2545  --rect.bottom;
2546 
2547  if(lpitem->fState & MF_GRAYED)
2548  {
2549  if (!(lpitem->fState & MF_HILITE) )
2550  {
2551  ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
2553  DrawTextW( hdc, Text, i, &rect, uFormat );
2554  --rect.left; --rect.top; --rect.right; --rect.bottom;
2555  }
2557  }
2558  DrawTextW( hdc, Text, i, &rect, uFormat);
2559 
2560  /* paint the shortcut text */
2561  if (!menuBar && L'\0' != Text[i]) /* There's a tab or flush-right char */
2562  {
2563  if (L'\t' == Text[i])
2564  {
2565  rect.left = lpitem->dxTab;
2566  uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
2567  }
2568  else
2569  {
2570  rect.right = lpitem->dxTab;
2571  uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
2572  }
2573 
2574  if (lpitem->fState & MF_GRAYED)
2575  {
2576  if (!(lpitem->fState & MF_HILITE) )
2577  {
2578  ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
2580  DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat);
2581  --rect.left; --rect.top; --rect.right; --rect.bottom;
2582  }
2584  }
2585  DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat );
2586  }
2587 
2588  if (!menuBar)
2589  ++rect.bottom;
2590 
2591  if (menuBar &&
2592  !flat_menu &&
2593  (lpitem->fState & (MF_HILITE | MF_GRAYED)) == MF_HILITE)
2594  {
2595  RECTL_vOffsetRect(&rect, -1, -1);
2596  }
2597 
2598  if (hfontOld)
2599  {
2600  NtGdiSelectFont (hdc, hfontOld);
2601  }
2602  }
2603 }
2604 
2605 /***********************************************************************
2606  * MenuDrawPopupMenu
2607  *
2608  * Paint a popup menu.
2609  */
2610 static void FASTCALL MENU_DrawPopupMenu(PWND wnd, HDC hdc, PMENU menu )
2611 {
2612  HBRUSH hPrevBrush = 0, brush = IntGetSysColorBrush(COLOR_MENU);
2613  RECT rect;
2614 
2615  TRACE("DPM wnd=%p dc=%p menu=%p\n", wnd, hdc, menu);
2616 
2617  IntGetClientRect( wnd, &rect );
2618 
2619  if (menu && menu->hbrBack) brush = menu->hbrBack;
2620  if((hPrevBrush = NtGdiSelectBrush( hdc, brush ))
2621  && (NtGdiSelectFont( hdc, ghMenuFont)))
2622  {
2623  HPEN hPrevPen;
2624 
2625  NtGdiRectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
2626 
2627  hPrevPen = NtGdiSelectPen( hdc, NtGdiGetStockObject( NULL_PEN ) );
2628  if ( hPrevPen )
2629  {
2630  BOOL flat_menu = FALSE;
2631 
2632  UserSystemParametersInfo (SPI_GETFLATMENU, 0, &flat_menu, 0);
2633  if (flat_menu)
2635  else
2637 
2638  TRACE("hmenu %p Style %08x\n", UserHMGetHandle(menu), (menu->fFlags & MNS_STYLE_MASK));
2639  /* draw menu items */
2640  if (menu && menu->cItems)
2641  {
2642  ITEM *item;
2643  UINT u;
2644 
2645  item = menu->rgItems;
2646  for( u = menu->cItems; u > 0; u--, item++)
2647  {
2648  MENU_DrawMenuItem(wnd, menu, menu->spwndNotify, hdc, item,
2649  menu->cyMenu, FALSE, ODA_DRAWENTIRE);
2650  }
2651  /* draw scroll arrows */
2652  if (menu->dwArrowsOn)
2653  {
2654  MENU_DrawScrollArrows(menu, hdc);
2655  }
2656  }
2657  }
2658  else
2659  {
2660  NtGdiSelectBrush( hdc, hPrevBrush );
2661  }
2662  }
2663 }
2664 
2665 /**********************************************************************
2666  * MENU_IsMenuActive
2667  */
2669 {
2670  return ValidateHwndNoErr(top_popup);
2671 }
2672 
2673 /**********************************************************************
2674  * MENU_EndMenu
2675  *
2676  * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2677  *
2678  * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2679  */
2680 void MENU_EndMenu( PWND pwnd )
2681 {
2682  PMENU menu = NULL;
2684  if ( menu && ( UserHMGetHandle(pwnd) == menu->hWnd || pwnd == menu->spwndNotify ) )
2685  {
2686  if (fInsideMenuLoop && top_popup)
2687  {
2689 
2690  if (fInEndMenu)
2691  {
2692  ERR("Already in End loop\n");
2693  return;
2694  }
2695 
2696  fInEndMenu = TRUE;
2698  }
2699  }
2700 }
2701 
2702 DWORD WINAPI
2704 {
2705  UINT i;
2706  HFONT FontOld = NULL;
2707  BOOL flat_menu = FALSE;
2708 
2709  UserSystemParametersInfo(SPI_GETFLATMENU, 0, &flat_menu, 0);
2710 
2711  if (!pMenu)
2712  {
2713  pMenu = UserGetMenuObject(UlongToHandle(pWnd->IDMenu));
2714  }
2715 
2716  if (!Font)
2717  {
2718  Font = ghMenuFont;
2719  }
2720 
2721  if (Rect == NULL || !pMenu)
2722  {
2724  }
2725 
2726  TRACE("(%x, %x, %p, %x, %x)\n", pWnd, hDC, Rect, pMenu, Font);
2727 
2728  FontOld = NtGdiSelectFont(hDC, Font);
2729 
2730  if (pMenu->cyMenu == 0)
2731  {
2732  MENU_MenuBarCalcSize(hDC, Rect, pMenu, pWnd);
2733  }
2734 
2735  Rect->bottom = Rect->top + pMenu->cyMenu;
2736 
2737  FillRect(hDC, Rect, IntGetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
2738 
2741  GreMoveTo(hDC, Rect->left, Rect->bottom - 1, NULL);
2742  NtGdiLineTo(hDC, Rect->right, Rect->bottom - 1);
2743 
2744  if (pMenu->cItems == 0)
2745  {
2746  NtGdiSelectFont(hDC, FontOld);
2748  }
2749 
2750  for (i = 0; i < pMenu->cItems; i++)
2751  {
2752  MENU_DrawMenuItem(pWnd, pMenu, pWnd, hDC, &pMenu->rgItems[i], pMenu->cyMenu, TRUE, ODA_DRAWENTIRE);
2753  }
2754 
2755  NtGdiSelectFont(hDC, FontOld);
2756 
2757  return pMenu->cyMenu;
2758 }
2759 
2760 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, PWND pWnd, BOOL suppress_draw )
2761 {
2762  HFONT hfontOld = 0;
2763  PMENU lppop = UserGetMenuObject(UlongToHandle(pWnd->IDMenu));
2764 
2765  if (lppop == NULL)
2766  {
2767  // No menu. Do not reserve any space
2768  return 0;
2769  }
2770 
2771  if (lprect == NULL)
2772  {
2774  }
2775 
2776  if (suppress_draw)
2777  {
2778  hfontOld = NtGdiSelectFont(hDC, ghMenuFont);
2779 
2780  MENU_MenuBarCalcSize(hDC, lprect, lppop, pWnd);
2781 
2782  lprect->bottom = lprect->top + lppop->cyMenu;
2783 
2784  if (hfontOld) NtGdiSelectFont( hDC, hfontOld);
2785 
2786  return lppop->cyMenu;
2787  }
2788  else
2789  {
2790  return IntDrawMenuBarTemp(pWnd, hDC, lprect, lppop, NULL);
2791  }
2792 }
2793 
2794 /***********************************************************************
2795  * MENU_InitPopup
2796  *
2797  * Popup menu initialization before WM_ENTERMENULOOP.
2798  */
2799 static BOOL MENU_InitPopup( PWND pWndOwner, PMENU menu, UINT flags )
2800 {
2801  PWND pWndCreated;
2802  PPOPUPMENU pPopupMenu;
2803  CREATESTRUCTW Cs;
2804  LARGE_STRING WindowName;
2805  UNICODE_STRING ClassName;
2806  DWORD ex_style = WS_EX_TOOLWINDOW;
2807 
2808  TRACE("owner=%p hmenu=%p\n", pWndOwner, menu);
2809 
2810  menu->spwndNotify = pWndOwner;
2811 
2812  if (flags & TPM_LAYOUTRTL || pWndOwner->ExStyle & WS_EX_LAYOUTRTL)
2813  ex_style = WS_EX_LAYOUTRTL;
2814 
2815  ClassName.Buffer = WC_MENU;
2816  ClassName.Length = 0;
2817 
2818  RtlZeroMemory(&WindowName, sizeof(WindowName));
2819  RtlZeroMemory(&Cs, sizeof(Cs));
2820  Cs.style = WS_POPUP;
2821  Cs.dwExStyle = ex_style;
2822  Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
2823  Cs.lpszName = (LPCWSTR) &WindowName;
2824  Cs.lpszClass = (LPCWSTR) &ClassName;
2825  Cs.lpCreateParams = UserHMGetHandle(menu);
2826  Cs.hwndParent = UserHMGetHandle(pWndOwner);
2827 
2828  /* NOTE: In Windows, top menu popup is not owned. */
2829  pWndCreated = co_UserCreateWindowEx( &Cs, &ClassName, &WindowName, NULL);
2830 
2831  if( !pWndCreated ) return FALSE;
2832 
2833  //
2834  // Setup pop up menu structure.
2835  //
2836  menu->hWnd = UserHMGetHandle(pWndCreated);
2837 
2838  pPopupMenu = ((PMENUWND)pWndCreated)->ppopupmenu;
2839 
2840  pPopupMenu->spwndActivePopup = pWndCreated; // top_popup = MenuInfo.Wnd or menu->hWnd
2841  pPopupMenu->spwndNotify = pWndOwner; // Same as MenuInfo.spwndNotify(which could be wrong) or menu->hwndOwner
2842  //pPopupMenu->spmenu = menu; Should be set up already from WM_CREATE!
2843 
2844  pPopupMenu->fIsTrackPopup = !!(flags & TPM_POPUPMENU);
2845  pPopupMenu->fIsSysMenu = !!(flags & TPM_SYSTEM_MENU);
2846  pPopupMenu->fNoNotify = !!(flags & TPM_NONOTIFY);
2847  pPopupMenu->fRightButton = !!(flags & TPM_RIGHTBUTTON);
2848  pPopupMenu->fSynchronous = !!(flags & TPM_RETURNCMD);
2849 
2850  if (pPopupMenu->fRightButton)
2851  pPopupMenu->fFirstClick = !!(UserGetKeyState(VK_RBUTTON) & 0x8000);
2852  else
2853  pPopupMenu->fFirstClick = !!(UserGetKeyState(VK_LBUTTON) & 0x8000);
2854 
2855  if (gpsi->aiSysMet[SM_MENUDROPALIGNMENT] ||
2856  menu->fFlags & MNF_RTOL)
2857  {
2858  pPopupMenu->fDroppedLeft = TRUE;
2859  }
2860  return TRUE;
2861 }
2862 
2863 /***********************************************************************
2864  * MenuShowPopup
2865  *
2866  * Display a popup menu.
2867  */
2868 static BOOL FASTCALL MENU_ShowPopup(PWND pwndOwner, PMENU menu, UINT id, UINT flags,
2869  INT x, INT y)
2870 {
2871  UINT width, height;
2872  POINT pt;
2873  PMONITOR monitor;
2874  PWND pWnd;
2876  BOOL bIsPopup = (flags & TPM_POPUPMENU) != 0;
2877 
2878  TRACE("owner=%p menu=%p id=0x%04x x=0x%04x y=0x%04x\n",
2879  pwndOwner, menu, id, x, y);
2880 
2881  if (menu->iItem != NO_SELECTED_ITEM)
2882  {
2883  menu->rgItems[menu->iItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
2884  menu->iItem = NO_SELECTED_ITEM;
2885  }
2886 
2887  menu->dwArrowsOn = 0;
2888  MENU_PopupMenuCalcSize(menu, pwndOwner);
2889 
2890  /* adjust popup menu pos so that it fits within the desktop */
2891 
2894 
2895  /* FIXME: should use item rect */
2896  pt.x = x;
2897  pt.y = y;
2898  monitor = UserMonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
2899 
2900  if (flags & TPM_LAYOUTRTL)
2901  flags ^= TPM_RIGHTALIGN;
2902 
2903  if( flags & TPM_RIGHTALIGN ) x -= width;
2904  if( flags & TPM_CENTERALIGN ) x -= width / 2;
2905 
2906  if( flags & TPM_BOTTOMALIGN ) y -= height;
2907  if( flags & TPM_VCENTERALIGN ) y -= height / 2;
2908 
2909  if( x + width > monitor->rcMonitor.right)
2910  {
2911  if( x + width > monitor->rcMonitor.right)
2912  {
2913  /* If we would flip around our origin, would we go off screen on the other side?
2914  Or is our origin itself too far to the right already? */
2915  if (!bIsPopup || x - width < monitor->rcMonitor.left || x > monitor->rcMonitor.right)
2916  x = monitor->rcMonitor.right - width;
2917  else
2918  x -= width;
2919  }
2920  }
2921  if( x < monitor->rcMonitor.left )
2922  {
2923  /* If we would flip around our origin, would we go off screen on the other side? */
2924  if (!bIsPopup || x + width > monitor->rcMonitor.right)
2925  x = monitor->rcMonitor.left;
2926  else
2927  x += width;
2928  }
2929 
2930  if( y + height > monitor->rcMonitor.bottom)
2931  {
2932  if( y + height > monitor->rcMonitor.bottom)
2933  {
2934  /* If we would flip around our origin, would we go off screen on the other side?
2935  Or is our origin itself too far to the bottom already? */
2936  if (!bIsPopup || y - height < monitor->rcMonitor.top || y > monitor->rcMonitor.bottom)
2937  y = monitor->rcMonitor.bottom - height;
2938  else
2939  y -= height;
2940  }
2941  }
2942  if( y < monitor->rcMonitor.top )
2943  {
2944  /* If we would flip around our origin, would we go off screen on the other side? */
2945  if (!bIsPopup || y + height > monitor->rcMonitor.bottom)
2946  y = monitor->rcMonitor.top;
2947  else
2948  y += height;
2949  }
2950 
2951  pWnd = ValidateHwndNoErr( menu->hWnd );
2952 
2953  if (!pWnd)
2954  {
2955  ERR("menu->hWnd bad hwnd %p\n",menu->hWnd);
2956  return FALSE;
2957  }
2958 
2959  if (!top_popup) {
2960  top_popup = menu->hWnd;
2962  }
2963 
2964  /* Display the window */
2965  UserRefObjectCo(pWnd, &Ref);
2967 
2969 
2970  IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, pWnd, OBJID_CLIENT, CHILDID_SELF, 0);
2971  UserDerefObjectCo(pWnd);
2972 
2973  return TRUE;
2974 }
2975 
2976 /***********************************************************************
2977  * MENU_EnsureMenuItemVisible
2978  */
2980 {
2982  if (lppop->dwArrowsOn)
2983  {
2984  ITEM *item = &lppop->rgItems[wIndex];
2985  UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
2986  UINT nOldPos = lppop->iTop;
2987  RECT rc;
2988  UINT arrow_bitmap_height;
2989  PWND pWnd = ValidateHwndNoErr(lppop->hWnd);
2990 
2991  IntGetClientRect(pWnd, &rc);
2992 
2993  arrow_bitmap_height = gpsi->oembmi[OBI_DNARROW].cy;
2994 
2995  rc.top += arrow_bitmap_height;
2996  rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
2997 
2998  nMaxHeight -= UserGetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
2999  UserRefObjectCo(pWnd, &Ref);
3000  if (item->cyItem > lppop->iTop + nMaxHeight)
3001  {
3002  lppop->iTop = item->cyItem - nMaxHeight;
3003  IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc);
3004  MENU_DrawScrollArrows(lppop, hdc);
3005  //ERR("Scroll Down iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
3006  }
3007  else if (item->yItem - MENU_TOP_MARGIN < lppop->iTop)
3008  {
3009  lppop->iTop = item->yItem - MENU_TOP_MARGIN;
3010  IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc);
3011  MENU_DrawScrollArrows(lppop, hdc);
3012  //ERR("Scroll Up iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
3013  }
3014  UserDerefObjectCo(pWnd);
3015  }
3016 }
3017 
3018 /***********************************************************************
3019  * MenuSelectItem
3020  */
3021 static void FASTCALL MENU_SelectItem(PWND pwndOwner, PMENU menu, UINT wIndex,
3022  BOOL sendMenuSelect, PMENU topmenu)
3023 {
3024  HDC hdc;
3025  PWND pWnd;
3026 
3027  TRACE("M_SI: owner=%p menu=%p index=0x%04x select=0x%04x\n", pwndOwner, menu, wIndex, sendMenuSelect);
3028 
3029  if (!menu || !menu->cItems) return;
3030 
3031  pWnd = ValidateHwndNoErr(menu->hWnd);
3032 
3033  if (!pWnd) return;
3034 
3035  if (menu->iItem == wIndex) return;
3036 
3037  if (menu->fFlags & MNF_POPUP)
3038  hdc = UserGetDCEx(pWnd, 0, DCX_USESTYLE);
3039  else
3040  hdc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW);
3041 
3042  if (!top_popup) {
3043  top_popup = menu->hWnd; //pPopupMenu->spwndActivePopup or
3044  //pPopupMenu->fIsTrackPopup set pPopupMenu->spwndPopupMenu;
3045  top_popup_hmenu = UserHMGetHandle(menu); //pPopupMenu->spmenu
3046  }
3047 
3049 
3050  /* Clear previous highlighted item */
3051  if (menu->iItem != NO_SELECTED_ITEM)
3052  {
3053  menu->rgItems[menu->iItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
3054  MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc, &menu->rgItems[menu->iItem],
3055  menu->cyMenu, !(menu->fFlags & MNF_POPUP),
3056  ODA_SELECT);
3057  }
3058 
3059  /* Highlight new item (if any) */
3060  menu->iItem = wIndex;
3061  if (menu->iItem != NO_SELECTED_ITEM)
3062  {
3063  if (!(menu->rgItems[wIndex].fType & MF_SEPARATOR))
3064  {
3065  menu->rgItems[wIndex].fState |= MF_HILITE;
3066  MENU_EnsureMenuItemVisible(menu, wIndex, hdc);
3067  MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc,
3068  &menu->rgItems[wIndex], menu->cyMenu, !(menu->fFlags & MNF_POPUP), ODA_SELECT);
3069  }
3070  if (sendMenuSelect)
3071  {
3072  ITEM *ip = &menu->rgItems[menu->iItem];
3073  WPARAM wParam = MAKEWPARAM( ip->spSubMenu ? wIndex : ip->wID,
3074  ip->fType | ip->fState |
3075  (ip->spSubMenu ? MF_POPUP : 0) |
3076  (menu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) );
3077 
3079  }
3080  }
3081  else if (sendMenuSelect)
3082  {
3083  if (topmenu)
3084  {
3085  int pos;
3086  pos = MENU_FindSubMenu(&topmenu, menu);
3087  if (pos != NO_SELECTED_ITEM)
3088  {
3089  ITEM *ip = &topmenu->rgItems[pos];
3090  WPARAM wParam = MAKEWPARAM( Pos, ip->fType | ip->fState |
3091  (ip->spSubMenu ? MF_POPUP : 0) |
3092  (topmenu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) );
3093 
3095  }
3096  }
3097  }
3098  UserReleaseDC(pWnd, hdc, FALSE);
3099 }
3100 
3101 /***********************************************************************
3102  * MenuMoveSelection
3103  *
3104  * Moves currently selected item according to the Offset parameter.
3105  * If there is no selection then it should select the last item if
3106  * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
3107  */
3108 static void FASTCALL MENU_MoveSelection(PWND pwndOwner, PMENU menu, INT offset)
3109 {
3110  INT i;
3111 
3112  TRACE("pwnd=%x menu=%x off=0x%04x\n", pwndOwner, menu, offset);
3113 
3114  if ((!menu) || (!menu->rgItems)) return;
3115 
3116  if ( menu->iItem != NO_SELECTED_ITEM )
3117  {
3118  if ( menu->cItems == 1 )
3119  return;
3120  else
3121  for (i = menu->iItem + offset ; i >= 0 && i < menu->cItems
3122  ; i += offset)
3123  if (!(menu->rgItems[i].fType & MF_SEPARATOR))
3124  {
3125  MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 );
3126  return;
3127  }
3128  }
3129 
3130  for ( i = (offset > 0) ? 0 : menu->cItems - 1;
3131  i >= 0 && i < menu->cItems ; i += offset)
3132  if (!(menu->rgItems[i].fType & MF_SEPARATOR))
3133  {
3134  MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 );
3135  return;
3136  }
3137 }
3138 
3139 /***********************************************************************
3140  * MenuHideSubPopups
3141  *
3142  * Hide the sub-popup menus of this menu.
3143  */
3144 static void FASTCALL MENU_HideSubPopups(PWND pWndOwner, PMENU Menu,
3145  BOOL SendMenuSelect, UINT wFlags)
3146 {
3147  TRACE("owner=%x menu=%x 0x%04x\n", pWndOwner, Menu, SendMenuSelect);
3148 
3149  if ( Menu && top_popup )
3150  {
3151  PITEM Item;
3152 
3153  if (Menu->iItem != NO_SELECTED_ITEM)
3154  {
3155  Item = &Menu->rgItems[Menu->iItem];
3156  if (!(Item->spSubMenu) ||
3157  !(Item->fState & MF_MOUSESELECT)) return;
3158  Item->fState &= ~MF_MOUSESELECT;
3159  }
3160  else
3161  return;
3162 
3163  if (Item->spSubMenu)
3164  {
3165  PWND pWnd;
3166  if (!VerifyMenu(Item->spSubMenu)) return;
3167  pWnd = ValidateHwndNoErr(Item->spSubMenu->hWnd);
3168  MENU_HideSubPopups(pWndOwner, Item->spSubMenu, FALSE, wFlags);
3169  MENU_SelectItem(pWndOwner, Item->spSubMenu, NO_SELECTED_ITEM, SendMenuSelect, NULL);
3170  TRACE("M_HSP top p hm %p pWndOwner IDMenu %p\n",top_popup_hmenu,pWndOwner->IDMenu);
3171  co_UserDestroyWindow(pWnd);
3172 
3173  /* Native returns handle to destroyed window */
3174  if (!(wFlags & TPM_NONOTIFY))
3175  {
3176  co_IntSendMessage( UserHMGetHandle(pWndOwner), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(Item->spSubMenu),
3177  MAKELPARAM(0, IS_SYSTEM_MENU(Item->spSubMenu)) );
3178  }
3180  // Call WM_UNINITMENUPOPUP FIRST before destroy!!
3181  // Fixes todo_wine User32 test menu.c line 2239 GetMenuBarInfo callback....
3182  //
3183  Item->spSubMenu->hWnd = NULL;
3185  }
3186  }
3187 }
3188 
3189 /***********************************************************************
3190  * MenuShowSubPopup
3191  *
3192  * Display the sub-menu of the selected item of this menu.
3193  * Return the handle of the submenu, or menu if no submenu to display.
3194  */
3195 static PMENU FASTCALL MENU_ShowSubPopup(PWND WndOwner, PMENU Menu, BOOL SelectFirst, UINT Flags)
3196 {
3197  RECT Rect;
3198  ITEM *Item;
3199  HDC Dc;
3200  PWND pWnd;
3201 
3202  TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, Menu, SelectFirst);
3203 
3204  if (!Menu) return Menu;
3205 
3206  if (Menu->iItem == NO_SELECTED_ITEM) return Menu;
3207 
3208  Item = &Menu->rgItems[Menu->iItem];
3209  if (!(Item->spSubMenu) || (Item->fState & (MF_GRAYED | MF_DISABLED)))
3210  return Menu;
3211 
3212  /* message must be sent before using item,
3213  because nearly everything may be changed by the application ! */
3214 
3215  /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3216  if (!(Flags & TPM_NONOTIFY))
3217  {
3219  (WPARAM) UserHMGetHandle(Item->spSubMenu),
3220  MAKELPARAM(Menu->iItem, IS_SYSTEM_MENU(Menu)));
3221  }
3222 
3223  Item = &Menu->rgItems[Menu->iItem];
3224  //Rect = ItemInfo.Rect;
3225  Rect.left = Item->xItem;
3226  Rect.top = Item->yItem;
3227  Rect.right = Item->cxItem; // Do this for now......
3228  Rect.bottom = Item->cyItem;
3229 
3230  pWnd = ValidateHwndNoErr(Menu->hWnd);
3231 
3232  /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
3233  if (!(Item->fState & MF_HILITE))
3234  {
3235  if (Menu->fFlags & MNF_POPUP) Dc = UserGetDCEx(pWnd, NULL, DCX_USESTYLE);
3236  else Dc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW);
3237 
3239 
3240  Item->fState |= MF_HILITE;
3241  MENU_DrawMenuItem(pWnd, Menu, WndOwner, Dc, Item, Menu->cyMenu,
3242  !(Menu->fFlags & MNF_POPUP), ODA_DRAWENTIRE);
3243 
3244  UserReleaseDC(pWnd, Dc, FALSE);
3245  }
3246 
3247  if (!Item->yItem && !Item->xItem && !Item->cyItem && !Item->cxItem)
3248  {
3249  Item->xItem = Rect.left;
3250  Item->yItem = Rect.top;
3251  Item->cxItem = Rect.right; // Do this for now......
3252  Item->cyItem = Rect.bottom;
3253  }
3254  Item->fState |= MF_MOUSESELECT;
3255 
3256  if (IS_SYSTEM_MENU(Menu))
3257  {
3258  MENU_InitSysMenuPopup(Item->spSubMenu, pWnd->style, pWnd->pcls->style, HTSYSMENU);
3259 
3260  NC_GetSysPopupPos(pWnd, &Rect);
3261  if (Flags & TPM_LAYOUTRTL) Rect.left = Rect.right;
3262  Rect.top = Rect.bottom;
3265  }
3266  else
3267  {
3268  IntGetWindowRect(pWnd, &Rect);
3269  if (Menu->fFlags & MNF_POPUP)
3270  {
3271  RECT rc;
3272  rc.left = Item->xItem;
3273  rc.top = Item->yItem;
3274  rc.right = Item->cxItem; // Do this for now......
3275  rc.bottom = Item->cyItem;
3276 
3277  MENU_AdjustMenuItemRect(Menu, &rc);
3278 
3279  /* The first item in the popup menu has to be at the
3280  same y position as the focused menu item */
3281  if(Flags & TPM_LAYOUTRTL)
3283  else
3284  Rect.left += rc.right /*ItemInfo.Rect.right*/ - UserGetSystemMetrics(SM_CXBORDER);
3285  Rect.top += rc.top - MENU_TOP_MARGIN;//3;
3286  Rect.right = rc.left - rc.right + UserGetSystemMetrics(SM_CXBORDER);
3287  Rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN - MENU_BOTTOM_MARGIN/*2*/
3289  }
3290  else
3291  {
3292  if(Flags & TPM_LAYOUTRTL)
3293  Rect.left += Rect.right - Item->xItem; //ItemInfo.Rect.left;
3294  else
3295  Rect.left += Item->xItem; //ItemInfo.Rect.left;
3296  Rect.top += Item->cyItem; //ItemInfo.Rect.bottom;
3297  Rect.right = Item->cxItem - Item->xItem; //ItemInfo.Rect.right - ItemInfo.Rect.left;
3298  Rect.bottom = Item->cyItem - Item->yItem; //ItemInfo.Rect.bottom - ItemInfo.Rect.top;
3299  }
3300  }
3301 
3302  /* use default alignment for submenus */
3304 
3305  MENU_InitPopup( WndOwner, Item->spSubMenu, Flags );
3306 
3307  MENU_ShowPopup( WndOwner, Item->spSubMenu, Menu->iItem, Flags,
3308  Rect.left, Rect.top);
3309  if (SelectFirst)
3310  {
3311  MENU_MoveSelection(WndOwner, Item->spSubMenu, ITEM_NEXT);
3312  }
3313  return Item->spSubMenu;
3314 }
3315 
3316 /***********************************************************************
3317  * MenuExecFocusedItem
3318  *
3319  * Execute a menu item (for instance when user pressed Enter).
3320  * Return the wID of the executed item. Otherwise, -1 indicating
3321  * that no menu item was executed, -2 if a popup is shown;
3322  * Have to receive the flags for the TrackPopupMenu options to avoid
3323  * sending unwanted message.
3324  *
3325  */
3327 {
3328  PITEM Item;
3329 
3330  TRACE("%p menu=%p\n", pmt, Menu);
3331 
3332  if (!Menu || !Menu->cItems || Menu->iItem == NO_SELECTED_ITEM)
3333  {
3334  return -1;
3335  }
3336 
3337  Item = &Menu->rgItems[Menu->iItem];
3338 
3339  TRACE("%p %08x %p\n", Menu, Item->wID, Item->spSubMenu);
3340 
3341  if (!(Item->spSubMenu))
3342  {
3343  if (!(Item->fState & (MF_GRAYED | MF_DISABLED)) && !(Item->fType & MF_SEPARATOR))
3344  {
3345  /* If TPM_RETURNCMD is set you return the id, but
3346  do not send a message to the owner */
3347  if (!(Flags & TPM_RETURNCMD))
3348  {
3349  if (Menu->fFlags & MNF_SYSMENU)
3350  {
3352  MAKELPARAM((SHORT) pmt->Pt.x, (SHORT) pmt->Pt.y));
3353  }
3354  else
3355  {
3356  DWORD dwStyle = ((Menu->fFlags & MNS_STYLE_MASK) | ( pmt->TopMenu ? (pmt->TopMenu->fFlags & MNS_STYLE_MASK) : 0) );
3357 
3358  if (dwStyle & MNS_NOTIFYBYPOS)
3359  UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_MENUCOMMAND, Menu->iItem, (LPARAM)UserHMGetHandle(Menu));
3360  else
3362  }
3363  }
3364  return Item->wID;
3365  }
3366  }
3367  else
3368  {
3369  pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, Menu, TRUE, Flags);
3370  return -2;
3371  }
3372 
3373  return -1;
3374 }
3375 
3376 /***********************************************************************
3377  * MenuSwitchTracking
3378  *
3379  * Helper function for menu navigation routines.
3380  */
3382 {
3383  TRACE("%x menu=%x 0x%04x\n", pmt, PtMenu, Index);
3384 
3385  if ( pmt->TopMenu != PtMenu &&
3386  !((PtMenu->fFlags | pmt->TopMenu->fFlags) & MNF_POPUP) )
3387  {
3388  /* both are top level menus (system and menu-bar) */
3391  pmt->TopMenu = PtMenu;
3392  }
3393  else
3394  {
3395  MENU_HideSubPopups(pmt->OwnerWnd, PtMenu, FALSE, wFlags);
3396  }
3397 
3398  MENU_SelectItem(pmt->OwnerWnd, PtMenu, Index, TRUE, NULL);
3399 }
3400 
3401 /***********************************************************************
3402  * MenuButtonDown
3403  *
3404  * Return TRUE if we can go on with menu tracking.
3405  */
3407 {
3408  TRACE("%x PtMenu=%p\n", pmt, PtMenu);
3409 
3410  if (PtMenu)
3411  {
3412  UINT id = 0;
3413  PITEM item;
3414  if (IS_SYSTEM_MENU(PtMenu))
3415  {
3416  item = PtMenu->rgItems;
3417  }
3418  else
3419  {
3420  item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &id );
3421  }
3422 
3423  if (item)
3424  {
3425  if (PtMenu->iItem != id)
3426  MENU_SwitchTracking(pmt, PtMenu, id, Flags);
3427 
3428  /* If the popup menu is not already "popped" */
3429  if (!(item->fState & MF_MOUSESELECT))
3430  {
3431  pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags);
3432  }
3433 
3434  return TRUE;
3435  }
3436  /* Else the click was on the menu bar, finish the tracking */
3437  }
3438  return FALSE;
3439 }
3440 
3441 /***********************************************************************
3442  * MenuButtonUp
3443  *
3444  * Return the value of MenuExecFocusedItem if
3445  * the selected item was not a popup. Else open the popup.
3446  * A -1 return value indicates that we go on with menu tracking.
3447  *
3448  */
3450 {
3451  TRACE("%p pmenu=%x\n", pmt, PtMenu);
3452 
3453  if (PtMenu)
3454  {
3455  UINT Id = 0;
3456  ITEM *item;
3457 
3458  if ( IS_SYSTEM_MENU(PtMenu) )
3459  {
3460  item = PtMenu->rgItems;
3461  }
3462  else
3463  {
3464  item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &Id );
3465  }
3466 
3467  if (item && ( PtMenu->iItem == Id))
3468  {
3469  if (!(item->spSubMenu))
3470  {
3471  INT ExecutedMenuId = MENU_ExecFocusedItem( pmt, PtMenu, Flags);
3472  if (ExecutedMenuId == -1 || ExecutedMenuId == -2) return -1;
3473  return ExecutedMenuId;
3474  }
3475 
3476  /* If we are dealing with the menu bar */
3477  /* and this is a click on an already "popped" item: */
3478  /* Stop the menu tracking and close the opened submenus */
3479  if (pmt->TopMenu == PtMenu && PtMenu->TimeToHide)
3480  {
3481  return 0;
3482  }
3483  }
3484  if ( IntGetMenu(PtMenu->hWnd) == PtMenu )
3485  {
3486  PtMenu->TimeToHide = TRUE;
3487  }
3488  }
3489  return -1;
3490 }
3491 
3492 /***********************************************************************
3493  * MenuPtMenu
3494  *
3495  * Walks menu chain trying to find a menu pt maps to.
3496  */
3498 {
3499  PITEM pItem;
3500  PMENU ret = NULL;
3501 
3502  if (!menu) return NULL;
3503 
3504  /* try subpopup first (if any) */
3505  if (menu->iItem != NO_SELECTED_ITEM)
3506  {
3507  pItem = menu->rgItems;
3508  if ( pItem ) pItem = &pItem[menu->iItem];
3509  if ( pItem && pItem->spSubMenu && pItem->fState & MF_MOUSESELECT)
3510  {
3511  ret = MENU_PtMenu( pItem->spSubMenu, pt);
3512  }
3513  }
3514 
3515  /* check the current window (avoiding WM_HITTEST) */
3516  if (!ret)
3517  {
3518  PWND pWnd = ValidateHwndNoErr(menu->hWnd);
3519  INT ht = GetNCHitEx(pWnd, pt);
3520  if ( menu->fFlags & MNF_POPUP )
3521  {
3522  if (ht != HTNOWHERE && ht != HTERROR) ret = menu;
3523  }
3524  else if (ht == HTSYSMENU)
3525  ret = get_win_sys_menu(menu->hWnd);
3526  else if (ht == HTMENU)
3527  ret = IntGetMenu( menu->hWnd );
3528  }
3529  return ret;
3530 }
3531 
3532 /***********************************************************************
3533  * MenuMouseMove
3534  *
3535  * Return TRUE if we can go on with menu tracking.
3536  */
3538 {
3540 
3541  if ( PtMenu )
3542  {
3543  if (IS_SYSTEM_MENU(PtMenu))
3544  {
3545  Index = 0;
3547  // Windows tracks mouse moves to the system menu but does not open it.
3548  // Only keyboard tracking can do that.
3549  //
3550  TRACE("SystemMenu\n");
3551  return TRUE; // Stay inside the Loop!
3552  }
3553  else
3554  MENU_FindItemByCoords( PtMenu, pmt->Pt, &Index );
3555  }
3556 
3557  if (Index == NO_SELECTED_ITEM)
3558  {
3560  }
3561  else if (PtMenu->iItem != Index)
3562  {
3563  MENU_SwitchTracking(pmt, PtMenu, Index, Flags);
3564  pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags);
3565  }
3566  return TRUE;
3567 }
3568 
3569 /***********************************************************************
3570  * MenuGetSubPopup
3571  *
3572  * Return the handle of the selected sub-popup menu (if any).
3573  */
3575 {
3576  ITEM *item;
3577 
3578  if ((!menu) || (menu->iItem == NO_SELECTED_ITEM)) return 0;
3579 
3580  item = &menu->rgItems[menu->iItem];
3581  if (item && (item->spSubMenu) && (item->fState & MF_MOUSESELECT))
3582  {
3583  return item->spSubMenu;
3584  }
3585  return 0;
3586 }
3587 
3588 /***********************************************************************
3589  * MenuDoNextMenu
3590  *
3591  * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
3592  */
3594 {
3595  BOOL atEnd = FALSE;
3596 
3597  /* When skipping left, we need to do something special after the
3598  first menu. */
3599  if (Vk == VK_LEFT && pmt->TopMenu->iItem == 0)
3600  {
3601  atEnd = TRUE;
3602  }
3603  /* When skipping right, for the non-system menu, we need to
3604  handle the last non-special menu item (ie skip any window
3605  icons such as MDI maximize, restore or close) */
3606  else if ((Vk == VK_RIGHT) && !IS_SYSTEM_MENU(pmt->TopMenu))
3607  {
3608  UINT i = pmt->TopMenu->iItem + 1;
3609  while (i < pmt->TopMenu->cItems) {
3610  if ((pmt->TopMenu->rgItems[i].wID >= SC_SIZE &&
3611  pmt->TopMenu->rgItems[i].wID <= SC_RESTORE)) {
3612  i++;
3613  } else break;
3614  }
3615  if (i == pmt->TopMenu->cItems) {
3616  atEnd = TRUE;
3617  }
3618  }
3619  /* When skipping right, we need to cater for the system menu */
3620  else if ((Vk == VK_RIGHT) && IS_SYSTEM_MENU(pmt->TopMenu))
3621  {
3622  if (pmt->TopMenu->iItem == (pmt->TopMenu->cItems - 1)) {
3623  atEnd = TRUE;
3624  }
3625  }
3626 
3627  if ( atEnd )
3628  {
3629  MDINEXTMENU NextMenu;
3630  PMENU MenuTmp;
3631  PWND pwndTemp;
3632  HMENU hNewMenu;
3633  HWND hNewWnd;
3634  UINT Id = 0;
3635 
3636  MenuTmp = (IS_SYSTEM_MENU(pmt->TopMenu)) ? co_IntGetSubMenu(pmt->TopMenu, 0) : pmt->TopMenu;
3637  NextMenu.hmenuIn = UserHMGetHandle(MenuTmp);
3638  NextMenu.hmenuNext = NULL;
3639  NextMenu.hwndNext = NULL;
3640  co_IntSendMessage(UserHMGetHandle(pmt->OwnerWnd), WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
3641 
3642  TRACE("%p [%p] -> %p [%p]\n",
3643  pmt->CurrentMenu, pmt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
3644 
3645  if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
3646  {
3647  hNewWnd = UserHMGetHandle(pmt->OwnerWnd);
3648  if (IS_SYSTEM_MENU(pmt->TopMenu))
3649  {
3650  /* switch to the menu bar */
3651 
3652  if (pmt->OwnerWnd->style & WS_CHILD || !(MenuTmp = IntGetMenu(hNewWnd))) return FALSE;
3653 
3654  if (Vk == VK_LEFT)
3655  {
3656  Id = MenuTmp->cItems - 1;
3657 
3658  /* Skip backwards over any system predefined icons,
3659  eg. MDI close, restore etc icons */
3660  while ((Id > 0) &&
3661  (MenuTmp->rgItems[Id].wID >= SC_SIZE &&
3662  MenuTmp->rgItems[Id].wID <= SC_RESTORE)) Id--;
3663 
3664  }
3665  hNewMenu = UserHMGetHandle(MenuTmp);
3666  }
3667  else if (pmt->OwnerWnd->style & WS_SYSMENU)
3668  {
3669  /* switch to the system menu */
3670  MenuTmp = get_win_sys_menu(hNewWnd);
3671  if (MenuTmp) hNewMenu = UserHMGetHandle(MenuTmp);
3672  }
3673  else
3674  return FALSE;
3675  }
3676  else /* application returned a new menu to switch to */
3677  {
3678  hNewMenu = NextMenu.hmenuNext;
3679  hNewWnd = NextMenu.hwndNext;
3680 
3681  if ((MenuTmp = UserGetMenuObject(hNewMenu)) && (pwndTemp = ValidateHwndNoErr(hNewWnd)))
3682  {
3683  if ( pwndTemp->style & WS_SYSMENU && (get_win_sys_menu(hNewWnd) == MenuTmp) )
3684  {
3685  /* get the real system menu */
3686  MenuTmp = get_win_sys_menu(hNewWnd);
3687  hNewMenu = UserHMGetHandle(MenuTmp);
3688  }
3689  else if (pwndTemp->style & WS_CHILD || IntGetMenu(hNewWnd) != MenuTmp)
3690  {
3691  /* FIXME: Not sure what to do here;
3692  * perhaps try to track NewMenu as a popup? */
3693 
3694  WARN(" -- got confused.\n");
3695  return FALSE;
3696  }
3697  }
3698  else return FALSE;
3699  }
3700 
3701  if (hNewMenu != UserHMGetHandle(pmt->TopMenu))
3702  {
3704 
3705  if (pmt->CurrentMenu != pmt->TopMenu)
3707  }
3708 
3709  if (hNewWnd != UserHMGetHandle(pmt->OwnerWnd))
3710  {
3712  pmt->OwnerWnd = ValidateHwndNoErr(hNewWnd);
3714  MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, hNewWnd);
3715  pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
3717  pti->MessageQueue->QF_flags |= QF_CAPTURELOCKED;
3718  }
3719 
3720  pmt->TopMenu = pmt->CurrentMenu = UserGetMenuObject(hNewMenu); /* all subpopups are hidden */
3721  MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, Id, TRUE, 0);
3722 
3723  return TRUE;
3724  }
3725  return FALSE;
3726 }
3727 
3728 /***********************************************************************
3729  * MenuSuspendPopup
3730  *
3731  * The idea is not to show the popup if the next input message is
3732  * going to hide it anyway.
3733  */
3735 {
3736  MSG msg;
3737 
3738  msg.hwnd = UserHMGetHandle(pmt->OwnerWnd);
3739 
3740  co_IntGetPeekMessage( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE, FALSE);
3741  pmt->TrackFlags |= TF_SKIPREMOVE;
3742 
3743  switch( uMsg )
3744  {
3745  case WM_KEYDOWN:
3747  if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
3748  {
3751  if( msg.message == WM_KEYDOWN &&
3752  (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
3753  {
3754  pmt->TrackFlags |= TF_SUSPENDPOPUP;
3755  return TRUE;
3756  }
3757  }
3758  break;
3759  }
3760  /* failures go through this */
3761  pmt->TrackFlags &= ~TF_SUSPENDPOPUP;
3762  return FALSE;
3763 }
3764 
3765 /***********************************************************************
3766  * MenuKeyEscape
3767  *
3768  * Handle a VK_ESCAPE key event in a menu.
3769  */
3771 {
3772  BOOL EndMenu = TRUE;
3773 
3774  if (pmt->CurrentMenu != pmt->TopMenu)
3775  {
3776  if (pmt->CurrentMenu && (pmt->CurrentMenu->fFlags & MNF_POPUP))
3777  {
3778  PMENU MenuPrev, MenuTmp;
3779 
3780  MenuPrev = MenuTmp = pmt->TopMenu;
3781 
3782  /* close topmost popup */
3783  while (MenuTmp != pmt->CurrentMenu)
3784  {
3785  MenuPrev = MenuTmp;
3786  MenuTmp = MENU_GetSubPopup(MenuPrev);
3787  }
3788 
3789  MENU_HideSubPopups(pmt->OwnerWnd, MenuPrev, TRUE, Flags);
3790  pmt->CurrentMenu = MenuPrev;
3791  EndMenu = FALSE;
3792  }
3793  }
3794 
3795  return EndMenu;
3796 }
3797 
3798 /***********************************************************************
3799  * MenuKeyLeft
3800  *
3801  * Handle a VK_LEFT key event in a menu.
3802  */
3804 {
3805  PMENU MenuTmp, MenuPrev;
3806  UINT PrevCol;
3807 
3808  MenuPrev = MenuTmp = pmt->TopMenu;
3809 
3810  /* Try to move 1 column left (if possible) */
3811  if ( (PrevCol = MENU_GetStartOfPrevColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM)
3812  {
3813  MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, PrevCol, TRUE, 0);
3814  return;
3815  }
3816 
3817  /* close topmost popup */
3818  while (MenuTmp != pmt->CurrentMenu)
3819  {
3820  MenuPrev = MenuTmp;
3821  MenuTmp = MENU_GetSubPopup(MenuPrev);
3822  }
3823 
3824  MENU_HideSubPopups(pmt->OwnerWnd, MenuPrev, TRUE, Flags);
3825  pmt->CurrentMenu = MenuPrev;
3826 
3827  if ((MenuPrev == pmt->TopMenu) && !(pmt->TopMenu->fFlags & MNF_POPUP))
3828  {
3829  /* move menu bar selection if no more popups are left */
3830 
3831  if (!MENU_DoNextMenu(pmt, VK_LEFT, Flags))
3833 
3834  if (MenuPrev != MenuTmp || pmt->TrackFlags & TF_SUSPENDPOPUP)
3835  {
3836  /* A sublevel menu was displayed - display the next one
3837  * unless there is another displacement coming up */
3838 
3839  if (!MENU_SuspendPopup(pmt, msg))
3840  pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, pmt->TopMenu,
3841  TRUE, Flags);
3842  }
3843  }
3844 }
3845 
3846 /***********************************************************************
3847  * MenuKeyRight
3848  *
3849  * Handle a VK_RIGHT key event in a menu.
3850  */
3852 {
3853  PMENU menutmp;
3854  UINT NextCol;
3855 
3856  TRACE("MenuKeyRight called, cur %p, top %p.\n",
3857  pmt->CurrentMenu, pmt->TopMenu);
3858 
3859  if ((pmt->TopMenu->fFlags & MNF_POPUP) || (pmt->CurrentMenu != pmt->TopMenu))
3860  {
3861  /* If already displaying a popup, try to display sub-popup */
3862 
3863  menutmp = pmt->CurrentMenu;
3864  pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, menutmp, TRUE, Flags);
3865 
3866  /* if subpopup was displayed then we are done */
3867  if (menutmp != pmt->CurrentMenu) return;
3868  }
3869 
3870  /* Check to see if there's another column */
3871  if ( (NextCol = MENU_GetStartOfNextColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM)
3872  {
3873  TRACE("Going to %d.\n", NextCol);
3874  MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, NextCol, TRUE, 0);
3875  return;
3876  }
3877 
3878  if (!(pmt->TopMenu->fFlags & MNF_POPUP)) /* menu bar tracking */
3879  {
3880  if (pmt->CurrentMenu != pmt->TopMenu)
3881  {
3883  menutmp = pmt->CurrentMenu = pmt->TopMenu;
3884  }
3885  else
3886  {
3887  menutmp = NULL;
3888  }
3889 
3890  /* try to move to the next item */
3891  if ( !MENU_DoNextMenu(pmt, VK_RIGHT, Flags))
3893 
3894  if ( menutmp || pmt->TrackFlags & TF_SUSPENDPOPUP )
3895  {
3896  if ( !MENU_SuspendPopup(pmt, msg) )
3897  pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, pmt->TopMenu, TRUE, Flags);
3898  }
3899  }
3900 }
3901 
3902 /***********************************************************************
3903  * MenuTrackMenu
3904  *
3905  * Menu tracking code.
3906  */
3908  PWND pwnd, const RECT *lprect )
3909 {
3910  MSG msg;
3911  BOOL fRemove;
3912  INT executedMenuId = -1;
3913  MTRACKER mt;
3914  HWND capture_win;
3915  PMENU pmMouse;
3916  BOOL enterIdleSent = FALSE;
3918 
3919  if (pti != pwnd->head.pti)
3920  {
3921  ERR("Not the same PTI!!!!\n");
3922  }
3923 
3924  mt.TrackFlags = 0;
3925  mt.CurrentMenu = pmenu;
3926  mt.TopMenu = pmenu;
3927  mt.OwnerWnd = pwnd;
3928  mt.Pt.x = x;
3929  mt.Pt.y = y;
3930 
3931  TRACE("MTM : hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n",
3932  UserHMGetHandle(pmenu), wFlags, x, y, UserHMGetHandle(pwnd), lprect ? lprect->left : 0, lprect ? lprect->top : 0,
3933  lprect ? lprect->right : 0, lprect ? lprect->bottom : 0);
3934 
3935  pti->MessageQueue->QF_flags &= ~QF_ACTIVATIONCHANGE;
3936 
3937  if (wFlags & TPM_BUTTONDOWN)
3938  {
3939  /* Get the result in order to start the tracking or not */
3940  fRemove = MENU_ButtonDown( &mt, pmenu, wFlags );
3941  fInsideMenuLoop = fRemove;
3942  }
3943 
3945 
3946  if (wFlags & TPM_POPUPMENU && pmenu->cItems == 0) // Tracking empty popup menu...
3947  {
3949  pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
3950  co_UserSetCapture(NULL); /* release the capture */
3951  return 0;
3952  }
3953 
3954  capture_win = IntGetCapture();
3955 
3956  while (fInsideMenuLoop)
3957  {
3958  BOOL ErrorExit = FALSE;
3959  if (!VerifyMenu( mt.CurrentMenu )) /* sometimes happens if I do a window manager close */
3960  break;
3961 
3962  /* we have to keep the message in the queue until it's
3963  * clear that menu loop is not over yet. */
3964 
3965  for (;;)
3966  {
3967  if (co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOREMOVE, FALSE ))
3968  {
3969  if (!IntCallMsgFilter( &msg, MSGF_MENU )) break;
3970  /* remove the message from the queue */
3971  co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
3972  }
3973  else
3974  {
3975  /* ReactOS Checks */
3976  if (!VerifyWnd(mt.OwnerWnd) ||
3978  pti->MessageQueue->QF_flags & QF_ACTIVATIONCHANGE ||
3979  capture_win != IntGetCapture() ) // Should not happen, but this is ReactOS...
3980  {
3981  ErrorExit = TRUE; // Do not wait on dead windows, now win test_capture_4 works.
3982  break;
3983  }
3984 
3985  if (!enterIdleSent)
3986  {
3988  enterIdleSent = TRUE;
3990  }
3991  co_IntWaitMessage(NULL, 0, 0);
3992  }
3993  }
3994 
3995  if (ErrorExit) break; // Gracefully dropout.
3996 
3997  /* check if EndMenu() tried to cancel us, by posting this message */
3998  if (msg.message == WM_CANCELMODE)
3999  {
4000  /* we are now out of the loop */
4002 
4003  /* remove the message from the queue */
4004  co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4005 
4006  /* break out of internal loop, ala ESCAPE */
4007  break;
4008  }
4009 
4010  mt.Pt = msg.pt;
4011 
4012  if ( (msg.hwnd == mt.CurrentMenu->hWnd) || ((msg.message!=WM_TIMER) && (msg.message!=WM_SYSTIMER)) )
4013  enterIdleSent=FALSE;
4014 
4015  fRemove = FALSE;
4016  if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
4017  {
4018  /*
4019  * Use the mouse coordinates in lParam instead of those in the MSG
4020  * struct to properly handle synthetic messages. They are already
4021  * in screen coordinates.
4022  */
4023  mt.Pt.x = (short)LOWORD(msg.lParam);
4024  mt.Pt.y = (short)HIWORD(msg.lParam);
4025 
4026  /* Find a menu for this mouse event */
4027  pmMouse = MENU_PtMenu( mt.TopMenu, mt.Pt );
4028 
4029  switch(msg.message)
4030  {
4031  /* no WM_NC... messages in captured state */
4032 
4033  case WM_RBUTTONDBLCLK:
4034  case WM_RBUTTONDOWN:
4035  if (!(wFlags & TPM_RIGHTBUTTON))
4036  {
4037  if ( msg.message == WM_RBUTTONDBLCLK ) fInsideMenuLoop = FALSE; // Must exit or loop forever!
4038  break;
4039  }
4040  /* fall through */
4041  case WM_LBUTTONDBLCLK:
4042  case WM_LBUTTONDOWN:
4043  /* If the message belongs to the menu, removes it from the queue */
4044  /* Else, end menu tracking */
4045  fRemove = MENU_ButtonDown(&mt, pmMouse, wFlags);
4046  fInsideMenuLoop = fRemove;
4047  if ( msg.message == WM_LBUTTONDBLCLK ||
4048  msg.message == WM_RBUTTONDBLCLK ) fInsideMenuLoop = FALSE; // Must exit or loop forever!
4049  break;
4050 
4051  case WM_RBUTTONUP:
4052  if (!(wFlags & TPM_RIGHTBUTTON)) break;
4053  /* fall through */
4054  case WM_LBUTTONUP:
4055  /* Check if a menu was selected by the mouse */
4056  if (pmMouse)
4057  {
4058  executedMenuId = MENU_ButtonUp( &mt, pmMouse, wFlags);
4059 
4060  /* End the loop if executedMenuId is an item ID */
4061  /* or if the job was done (executedMenuId = 0). */
4062  fRemove = (executedMenuId != -1);
4063  fInsideMenuLoop = !fRemove;
4064  }
4065  /* No menu was selected by the mouse */
4066  /* if the function was called by TrackPopupMenu, continue
4067  with the menu tracking. If not, stop it */
4068  else
4070 
4071  break;
4072 
4073  case WM_MOUSEMOVE:
4074  /* the selected menu item must be changed every time */
4075  /* the mouse moves. */
4076 
4077  if (pmMouse)
4078  fInsideMenuLoop |= MENU_MouseMove( &mt, pmMouse, wFlags );
4079 
4080  } /* switch(msg.message) - mouse */
4081  }
4082  else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
4083  {
4084  fRemove = TRUE; /* Keyboard messages are always removed */
4085  switch(msg.message)
4086  {
4087  case WM_KEYDOWN:
4088  case WM_SYSKEYDOWN:
4089  switch(msg.wParam)
4090  {
4091  case VK_MENU:
4092  case VK_F10:
4094  break;
4095 
4096  case VK_HOME:
4097  case VK_END:
4100  break;
4101 
4102  case VK_UP:
4103  case VK_DOWN: /* If on menu bar, pull-down the menu */
4104  if (!(mt.CurrentMenu->fFlags & MNF_POPUP))
4106  else /* otherwise try to move selection */
4108  break;
4109 
4110  case VK_LEFT:
4111  MENU_KeyLeft( &mt, wFlags, msg.message );
4112  break;
4113 
4114  case VK_RIGHT:
4115  MENU_KeyRight( &mt, wFlags, msg.message );
4116  break;
4117 
4118  case VK_ESCAPE:
4120  break;
4121 
4122  case VK_F1:
4123  {
4124  HELPINFO hi;
4125  hi.cbSize = sizeof(HELPINFO);
4127  if (mt.CurrentMenu->iItem == NO_SELECTED_ITEM)
4128  hi.iCtrlId = 0;
4129  else
4130  hi.iCtrlId = pmenu->rgItems[mt.CurrentMenu->iItem].wID;
4132  hi.dwContextId = pmenu->dwContextHelpId;
4133  hi.MousePos = msg.pt;
4134  co_IntSendMessage( UserHMGetHandle(pwnd), WM_HELP, 0, (LPARAM)&hi);
4135  break;
4136  }
4137 
4138  default:
4140  break;
4141  }
4142  break; /* WM_KEYDOWN */
4143 
4144  case WM_CHAR:
4145  case WM_SYSCHAR:
4146  {
4147  UINT pos;
4148  BOOL fEndMenu;
4149 
4150  if (msg.wParam == L'\r' || msg.wParam == L' ')
4151  {
4152  executedMenuId = MENU_ExecFocusedItem(&mt, mt.CurrentMenu, wFlags);
4153  fEndMenu = (executedMenuId != -2);
4154  fInsideMenuLoop = !fEndMenu;
4155  break;
4156  }
4157 
4158  /* Hack to avoid control chars. */
4159  /* We will find a better way real soon... */
4160  if (msg.wParam < 32) break;
4161 
4163 
4164  if (pos == (UINT)-2) fInsideMenuLoop = FALSE;
4165  else if (pos == (UINT)-1) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, 0); //MessageBeep(0);
4166  else
4167  {
4169  executedMenuId = MENU_ExecFocusedItem(&mt, mt.CurrentMenu, wFlags);
4170  fEndMenu = (executedMenuId != -2);
4171  fInsideMenuLoop = !fEndMenu;
4172  }
4173  }
4174  break;
4175  } /* switch(msg.message) - kbd */
4176  }
4177  else
4178  {
4179  co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4180  IntDispatchMessage( &msg );
4181  continue;
4182  }
4183 
4184  if (fInsideMenuLoop) fRemove = TRUE;
4185 
4186  /* finally remove message from the queue */
4187 
4188  if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) )
4189  co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4190  else mt.TrackFlags &= ~TF_SKIPREMOVE;
4191  }
4192 
4194  pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
4195  co_UserSetCapture(NULL); /* release the capture */
4196 
4197  /* If dropdown is still painted and the close box is clicked on
4198  then the menu will be destroyed as part of the DispatchMessage above.
4199  This will then invalidate the menu handle in mt.hTopMenu. We should
4200  check for this first. */
4201  if ( VerifyMenu( mt.TopMenu ) )
4202  {
4203  if (VerifyWnd(mt.OwnerWnd))
4204  {
4206 
4207  if (mt.TopMenu->fFlags & MNF_POPUP)
4208  {
4209  PWND pwndTM = ValidateHwndNoErr(mt.TopMenu->hWnd);
4210  if (pwndTM)
4211  {
4212  IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, pwndTM, OBJID_CLIENT, CHILDID_SELF, 0);
4213 
4214  co_UserDestroyWindow(pwndTM);
4215  }
4216  mt.TopMenu->hWnd = NULL;
4217 
4218  if (!(wFlags & TPM_NONOTIFY))
4219  {
4221  MAKELPARAM(0, IS_SYSTEM_MENU(mt.TopMenu)) );
4222  }
4223  }
4226  }
4227 
4228  /* Reset the variable for hiding menu */
4229  mt.TopMenu->TimeToHide = FALSE;
4230  }
4231 
4233  /* The return value is only used by TrackPopupMenu */
4234  if (!(wFlags & TPM_RETURNCMD)) return TRUE;
4235  if (executedMenuId == -1) executedMenuId = 0;
4236  return executedMenuId;
4237 }
4238 
4239 /***********************************************************************
4240  * MenuInitTracking
4241  */
4243 {
4244  HWND capture_win;
4246 
4247  TRACE("hwnd=%p hmenu=%p\n", UserHMGetHandle(pWnd), UserHMGetHandle(Menu));
4248 
4249  co_UserHideCaret(0);
4250 
4251  /* This makes the menus of applications built with Delphi work.
4252  * It also enables menus to be displayed in more than one window,
4253  * but there are some bugs left that need to be fixed in this case.
4254  */
4255  if (!bPopup)
4256  {
4257  Menu->hWnd = UserHMGetHandle(pWnd);
4258  }
4259 
4260  if (!top_popup) {
4261  top_popup = Menu->hWnd;
4263  }
4264 
4266  fInEndMenu = FALSE;
4267 
4268  /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
4269  if (!(wFlags & TPM_NONOTIFY))
4270  {
4271  co_IntSendMessage( UserHMGetHandle(pWnd), WM_ENTERMENULOOP, bPopup, 0 );
4272  }
4273 
4274  //
4275  // Capture is set before calling WM_INITMENU and after WM_ENTERMENULOOP, see msg_menu.
4276  //
4277  capture_win = (wFlags & TPM_POPUPMENU) ? Menu->hWnd : UserHMGetHandle(pWnd);
4278  MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, capture_win); // 1
4279  co_UserSetCapture(capture_win); // 2
4280  pti->MessageQueue->QF_flags |= QF_CAPTURELOCKED; // Set the Q bits so noone can change this!
4281 
4283 
4284  if (!(wFlags & TPM_NONOTIFY))
4285  {
4287  /* If an app changed/recreated menu bar entries in WM_INITMENU
4288  * menu sizes will be recalculated once the menu created/shown.
4289  */
4290  }
4291 
4292  IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART,
4293  pWnd,
4295  CHILDID_SELF, 0);
4296  return TRUE;
4297 }
4298 
4299 /***********************************************************************
4300  * MenuExitTracking
4301  */
4303 {
4304  TRACE("Exit Track hwnd=%p bPopup %d\n", UserHMGetHandle(pWnd), bPopup);
4305 
4306  IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, pWnd, OBJID_WINDOW, CHILDID_SELF, 0);
4307 
4308  if (!(wFlags & TPM_NONOTIFY))
4309  co_IntSendMessage( UserHMGetHandle(pWnd), WM_EXITMENULOOP, bPopup, 0 );
4310 
4311  co_UserShowCaret(0);
4312 
4313  top_popup = 0;
4315 
4316  return TRUE;
4317 }
4318 
4319 /***********************************************************************
4320  * MenuTrackMouseMenuBar
4321  *
4322  * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
4323  */
4325 {
4326  PMENU pMenu = (ht == HTSYSMENU) ? IntGetSystemMenu(pWnd, FALSE) : IntGetMenu( UserHMGetHandle(pWnd) ); // See 74276 and CORE-12801
4328 
4329  TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", pWnd, ht, pt.x, pt.y);
4330 
4331  if (pWnd->ExStyle & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
4332  if (VerifyMenu(pMenu))
4333  {
4334  /* map point to parent client coordinates */
4336  if (Parent != UserGetDesktopWindow())
4337  {
4339  }
4340 
4341  MENU_InitTracking(pWnd, pMenu, FALSE, wFlags);
4342  /* fetch the window menu again, it may have changed */
4343  pMenu = (ht == HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pWnd) ) : IntGetMenu( UserHMGetHandle(pWnd) );
4344