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