ReactOS  0.4.15-dev-2355-gaf9df93
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;