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