ReactOS  0.4.11-dev-195-gef016bf
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  HBITMAP hbmToDraw = lpitem->hbmp;
1661  bmp = hbmToDraw;
1662 
1663  /* Check if there is a magic menu item associated with this item */
1664  if (IS_MAGIC_BITMAP(hbmToDraw))
1665  {
1666  UINT flags = 0;
1667  RECT r;
1668 
1669  r = *rect;
1670  switch ((INT_PTR)hbmToDraw)
1671  {
1672  case (INT_PTR)HBMMENU_SYSTEM:
1673  if (lpitem->dwItemData)
1674  {
1675  if (ValidateHwndNoErr((HWND)lpitem->dwItemData))
1676  {
1677  ERR("Get Item Data from this Window!!!\n");
1678  }
1679 
1680  ERR("Draw Bitmap\n");
1681  bmp = (HBITMAP)lpitem->dwItemData;
1682  if (!GreGetObject( bmp, sizeof(bm), &bm )) return;
1683  }
1684  else
1685  {
1686  PCURICON_OBJECT pIcon = NULL;
1687  //if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
1688  //bmp = BmpSysMenu;
1689  //if (! GreGetObject(bmp, sizeof(bm), &bm)) return;
1690  /* only use right half of the bitmap */
1691  //bmp_xoffset = bm.bmWidth / 2;
1692  //bm.bmWidth -= bmp_xoffset;
1693  if (WndOwner)
1694  {
1695  pIcon = NC_IconForWindow(WndOwner);
1696  // FIXME: NC_IconForWindow should reference it for us */
1697  if (pIcon) UserReferenceObject(pIcon);
1698  }
1699  ERR("Draw ICON\n");
1700  if (pIcon)
1701  {
1704  LONG x = rect->left - cx/2 + 1 + (rect->bottom - rect->top)/2; // this is really what Window does
1705  LONG y = (rect->top + rect->bottom)/2 - cy/2; // center
1706  UserDrawIconEx(hdc, x, y, pIcon, cx, cy, 0, NULL, DI_NORMAL);
1707  UserDereferenceObject(pIcon);
1708  }
1709  return;
1710  }
1711  goto got_bitmap;
1713  flags = DFCS_CAPTIONRESTORE;
1714  break;
1716  r.right += 1;
1717  flags = DFCS_CAPTIONMIN;
1718  break;
1720  r.right += 1;
1721  flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
1722  break;
1724  flags = DFCS_CAPTIONCLOSE;
1725  break;
1727  flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
1728  break;
1729  case (INT_PTR)HBMMENU_CALLBACK:
1730  {
1731  DRAWITEMSTRUCT drawItem;
1732  POINT origorg;
1733  drawItem.CtlType = ODT_MENU;
1734  drawItem.CtlID = 0;
1735  drawItem.itemID = lpitem->wID;
1736  drawItem.itemAction = odaction;
1737  drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1738  drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1739  drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1740  drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1741  drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1742  drawItem.itemState |= (!(Menu->fFlags & MNF_UNDERLINE))?ODS_NOACCEL:0;
1743  drawItem.itemState |= (Menu->fFlags & MNF_INACTIVE)?ODS_INACTIVE:0;
1744  drawItem.hwndItem = (HWND)UserHMGetHandle(Menu);
1745  drawItem.hDC = hdc;
1746  drawItem.rcItem = *rect;
1747  drawItem.itemData = lpitem->dwItemData;
1748  /* some applications make this assumption on the DC's origin */
1749  GreSetViewportOrgEx( hdc, lpitem->xItem, lpitem->yItem, &origorg);
1750  RECTL_vOffsetRect( &drawItem.rcItem, - lpitem->xItem, - lpitem->yItem);
1751  co_IntSendMessage( UserHMGetHandle(WndOwner), WM_DRAWITEM, 0, (LPARAM)&drawItem);
1752  GreSetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1753  return;
1754  }
1755  break;
1756 
1761  MENU_DrawPopupGlyph(hdc, &r, (INT_PTR)hbmToDraw, lpitem->fState & MF_GRAYED, lpitem->fState & MF_HILITE);
1762  return;
1763  }
1764  RECTL_vInflateRect(&r, -1, -1);
1765  if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
1766  DrawFrameControl(hdc, &r, DFC_CAPTION, flags);
1767  return;
1768  }
1769 
1770  if (!bmp || !GreGetObject( bmp, sizeof(bm), &bm )) return;
1771 
1772  got_bitmap:
1773  hdcMem = NtGdiCreateCompatibleDC( hdc );
1774  NtGdiSelectBitmap( hdcMem, bmp );
1775  /* handle fontsize > bitmap_height */
1776  top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
1777  left=rect->left;
1778  rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
1779  if ((lpitem->fState & MF_HILITE) && lpitem->hbmp)
1781  NtGdiBitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop , 0, 0);
1782  IntGdiDeleteDC( hdcMem, FALSE );
1783 }
1784 
1785 LONG
1787 {
1788  static DWORD units;
1789 
1790  if (!units)
1791  {
1792  HDC hdc;
1793  SIZE size;
1794 
1795  if ((hdc = UserGetDCEx(NULL, NULL, DCX_CACHE)))
1796  {
1797  size.cx = IntGetCharDimensions( hdc, NULL, (PDWORD)&size.cy );
1798  if (size.cx) units = MAKELONG( size.cx, size.cy );
1799  UserReleaseDC( 0, hdc, FALSE);
1800  }
1801  }
1802  return units;
1803 }
1804 
1805 
1806 /***********************************************************************
1807  * MenuCalcItemSize
1808  *
1809  * Calculate the size of the menu item and store it in lpitem->rect.
1810  */
1811 static void FASTCALL MENU_CalcItemSize( HDC hdc, PITEM lpitem, PMENU Menu, PWND pwndOwner,
1812  INT orgX, INT orgY, BOOL menuBar, BOOL textandbmp)
1813 {
1814  WCHAR *p;
1815  UINT check_bitmap_width = UserGetSystemMetrics( SM_CXMENUCHECK );
1816  UINT arrow_bitmap_width;
1817  RECT Rect;
1818  INT itemheight = 0;
1819 
1820  TRACE("dc=%x owner=%x (%d,%d)\n", hdc, pwndOwner, orgX, orgY);
1821 
1822  arrow_bitmap_width = gpsi->oembmi[OBI_MNARROW].cx;
1823 
1824  MenuCharSize.cx = IntGetCharDimensions( hdc, NULL, (PDWORD)&MenuCharSize.cy );
1825 
1826  RECTL_vSetRect( &Rect, orgX, orgY, orgX, orgY );
1827 
1828  if (lpitem->fType & MF_OWNERDRAW)
1829  {
1830  MEASUREITEMSTRUCT mis;
1831  mis.CtlType = ODT_MENU;
1832  mis.CtlID = 0;
1833  mis.itemID = lpitem->wID;
1834  mis.itemData = lpitem->dwItemData;
1836  mis.itemWidth = 0;
1837  co_IntSendMessage( UserHMGetHandle(pwndOwner), WM_MEASUREITEM, 0, (LPARAM)&mis );
1838  /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1839  * width of a menufont character to the width of an owner-drawn menu.
1840  */
1841  Rect.right += mis.itemWidth + 2 * MenuCharSize.cx;
1842  if (menuBar) {
1843  /* under at least win95 you seem to be given a standard
1844  height for the menu and the height value is ignored */
1846  } else
1847  Rect.bottom += mis.itemHeight;
1848  // Or this,
1849  //lpitem->cxBmp = mis.itemWidth;
1850  //lpitem->cyBmp = mis.itemHeight;
1851  TRACE("MF_OWNERDRAW Height %d Width %d\n",mis.itemHeight,mis.itemWidth);
1852  TRACE("MF_OWNERDRAW id=%04lx size=%dx%d cx %d cy %d\n",
1853  lpitem->wID, Rect.right-Rect.left,
1854  Rect.bottom-Rect.top, MenuCharSize.cx, MenuCharSize.cy);
1855 
1856  lpitem->xItem = Rect.left;
1857  lpitem->yItem = Rect.top;
1858  lpitem->cxItem = Rect.right;
1859  lpitem->cyItem = Rect.bottom;
1860 
1861  return;
1862  }
1863 
1864  lpitem->xItem = orgX;
1865  lpitem->yItem = orgY;
1866  lpitem->cxItem = orgX;
1867  lpitem->cyItem = orgY;
1868 
1869  if (lpitem->fType & MF_SEPARATOR)
1870  {
1871  lpitem->cyItem += UserGetSystemMetrics( SM_CYMENUSIZE)/2;//SEPARATOR_HEIGHT;
1872  if( !menuBar)
1873  lpitem->cxItem += arrow_bitmap_width + MenuCharSize.cx;
1874  return;
1875  }
1876 
1877  lpitem->dxTab = 0;
1878 
1879  if (lpitem->hbmp)
1880  {
1881  SIZE size;
1882 
1883  if (!menuBar) {
1884  MENU_GetBitmapItemSize(lpitem, &size, pwndOwner );
1885  /* Keep the size of the bitmap in callback mode to be able
1886  * to draw it correctly */
1887  lpitem->cxBmp = size.cx;
1888  lpitem->cyBmp = size.cy;
1889  Menu->cxTextAlign = max(Menu->cxTextAlign, size.cx);
1890  lpitem->cxItem += size.cx + 2;
1891  itemheight = size.cy + 2;
1892 
1893  if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
1894  lpitem->cxItem += 2 * check_bitmap_width;
1895  lpitem->cxItem += 4 + MenuCharSize.cx;
1896  lpitem->dxTab = lpitem->cxItem;
1897  lpitem->cxItem += arrow_bitmap_width;
1898  } else /* hbmpItem & MenuBar */ {
1899  MENU_GetBitmapItemSize(lpitem, &size, pwndOwner );
1900  lpitem->cxItem += size.cx;
1901  if( lpitem->Xlpstr) lpitem->cxItem += 2;
1902  itemheight = size.cy;
1903 
1904  /* Special case: Minimize button doesn't have a space behind it. */
1905  if (lpitem->hbmp == (HBITMAP)HBMMENU_MBAR_MINIMIZE ||
1906  lpitem->hbmp == (HBITMAP)HBMMENU_MBAR_MINIMIZE_D)
1907  lpitem->cxItem -= 1;
1908  }
1909  }
1910  else if (!menuBar) {
1911  if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
1912  lpitem->cxItem += check_bitmap_width;
1913  lpitem->cxItem += 4 + MenuCharSize.cx;
1914  lpitem->dxTab = lpitem->cxItem;
1915  lpitem->cxItem += arrow_bitmap_width;
1916  }
1917 
1918  /* it must be a text item - unless it's the system menu */
1919  if (!(lpitem->fType & MF_SYSMENU) && lpitem->Xlpstr) {
1920  HFONT hfontOld = NULL;
1921  RECT rc;// = lpitem->Rect;
1922  LONG txtheight, txtwidth;
1923 
1924  rc.left = lpitem->xItem;
1925  rc.top = lpitem->yItem;
1926  rc.right = lpitem->cxItem; // Do this for now......
1927  rc.bottom = lpitem->cyItem;
1928 
1929  if ( lpitem->fState & MFS_DEFAULT ) {
1930  hfontOld = NtGdiSelectFont( hdc, ghMenuFontBold );
1931  }
1932  if (menuBar) {
1933  txtheight = DrawTextW( hdc, lpitem->Xlpstr, -1, &rc, DT_SINGLELINE|DT_CALCRECT);
1934 
1935  lpitem->cxItem += rc.right - rc.left;
1936  itemheight = max( max( itemheight, txtheight), UserGetSystemMetrics( SM_CYMENU) - 1);
1937 
1938  lpitem->cxItem += 2 * MenuCharSize.cx;
1939  } else {
1940  if ((p = wcschr( lpitem->Xlpstr, '\t' )) != NULL) {
1941  RECT tmprc = rc;
1942  LONG tmpheight;
1943  int n = (int)( p - lpitem->Xlpstr);
1944  /* Item contains a tab (only meaningful in popup menus) */
1945  /* get text size before the tab */
1946  txtheight = DrawTextW( hdc, lpitem->Xlpstr, n, &rc,
1948  txtwidth = rc.right - rc.left;
1949  p += 1; /* advance past the Tab */
1950  /* get text size after the tab */
1951  tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1953  lpitem->dxTab += txtwidth;
1954  txtheight = max( txtheight, tmpheight);
1955  txtwidth += MenuCharSize.cx + /* space for the tab */
1956  tmprc.right - tmprc.left; /* space for the short cut */
1957  } else {
1958  txtheight = DrawTextW( hdc, lpitem->Xlpstr, -1, &rc,
1960  txtwidth = rc.right - rc.left;
1961  lpitem->dxTab += txtwidth;
1962  }
1963  lpitem->cxItem += 2 + txtwidth;
1964  itemheight = max( itemheight,
1965  max( txtheight + 2, MenuCharSize.cy + 4));
1966  }
1967  if (hfontOld)
1968  {
1969  NtGdiSelectFont (hdc, hfontOld);
1970  }
1971  } else if( menuBar) {
1972  itemheight = max( itemheight, UserGetSystemMetrics(SM_CYMENU)-1);
1973  }
1974  lpitem->cyItem += itemheight;
1975  TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->xItem, lpitem->yItem, lpitem->cxItem, lpitem->cyItem);
1976 }
1977 
1978 /***********************************************************************
1979  * MENU_GetMaxPopupHeight
1980  */
1981 static UINT
1983 {
1984  if (lppop->cyMax)
1985  {
1986  //ERR("MGMaxPH cyMax %d\n",lppop->cyMax);
1987  return lppop->cyMax;
1988  }
1989  //ERR("MGMaxPH SyMax %d\n",UserGetSystemMetrics(SM_CYSCREEN) - UserGetSystemMetrics(SM_CYBORDER));
1991 }
1992 
1993 /***********************************************************************
1994  * MenuPopupMenuCalcSize
1995  *
1996  * Calculate the size of a popup menu.
1997  */
1998 static void FASTCALL MENU_PopupMenuCalcSize(PMENU Menu, PWND WndOwner)
1999 {
2000  PITEM lpitem;
2001  HDC hdc;
2002  int start, i;
2003  int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
2004  BOOL textandbmp = FALSE;
2005 
2006  Menu->cxMenu = Menu->cyMenu = 0;
2007  if (Menu->cItems == 0) return;
2008 
2009  hdc = UserGetDCEx(NULL, NULL, DCX_CACHE);
2010 
2011  NtGdiSelectFont( hdc, ghMenuFont );
2012 
2013  start = 0;
2014  maxX = 2 + 1;
2015 
2016  Menu->cxTextAlign = 0;
2017 
2018  while (start < Menu->cItems)
2019  {
2020  lpitem = &Menu->rgItems[start];
2021  orgX = maxX;
2022  if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
2023  orgX += MENU_COL_SPACE;
2024  orgY = MENU_TOP_MARGIN;
2025 
2026  maxTab = maxTabWidth = 0;
2027  /* Parse items until column break or end of menu */
2028  for (i = start; i < Menu->cItems; i++, lpitem++)
2029  {
2030  if (i != start &&
2031  (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
2032 
2033  MENU_CalcItemSize(hdc, lpitem, Menu, WndOwner, orgX, orgY, FALSE, textandbmp);
2034  maxX = max(maxX, lpitem->cxItem);
2035  orgY = lpitem->cyItem;
2036  if (IS_STRING_ITEM(lpitem->fType) && lpitem->dxTab )
2037  {
2038  maxTab = max( maxTab, lpitem->dxTab );
2039  maxTabWidth = max(maxTabWidth, lpitem->cxItem - lpitem->dxTab);
2040  }
2041  if( lpitem->Xlpstr && lpitem->hbmp) textandbmp = TRUE;
2042  }
2043 
2044  /* Finish the column (set all items to the largest width found) */
2045  maxX = max( maxX, maxTab + maxTabWidth );
2046  for (lpitem = &Menu->rgItems[start]; start < i; start++, lpitem++)
2047  {
2048  lpitem->cxItem = maxX;
2049  if (IS_STRING_ITEM(lpitem->fType) && lpitem->dxTab)
2050  lpitem->dxTab = maxTab;
2051  }
2052  Menu->cyMenu = max(Menu->cyMenu, orgY);
2053  }
2054 
2055  Menu->cxMenu = maxX;
2056  /* if none of the items have both text and bitmap then
2057  * the text and bitmaps are all aligned on the left. If there is at
2058  * least one item with both text and bitmap then bitmaps are
2059  * on the left and texts left aligned with the right hand side
2060  * of the bitmaps */
2061  if( !textandbmp) Menu->cxTextAlign = 0;
2062 
2063  /* space for 3d border */
2064  Menu->cyMenu += MENU_BOTTOM_MARGIN;
2065  Menu->cxMenu += 2;
2066 
2067  /* Adjust popup height if it exceeds maximum */
2068  maxHeight = MENU_GetMaxPopupHeight(Menu);
2069  Menu->iMaxTop = Menu->cyMenu - MENU_TOP_MARGIN;
2070  if (Menu->cyMenu >= maxHeight)
2071  {
2072  Menu->cyMenu = maxHeight;
2073  Menu->dwArrowsOn = 1;
2074  }
2075  else
2076  {
2077  Menu->dwArrowsOn = 0;
2078  }
2079  UserReleaseDC( 0, hdc, FALSE );
2080 }
2081 
2082 /***********************************************************************
2083  * MENU_MenuBarCalcSize
2084  *
2085  * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
2086  * height is off by 1 pixel which causes lengthy window relocations when
2087  * active document window is maximized/restored.
2088  *
2089  * Calculate the size of the menu bar.
2090  */
2091 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect, PMENU lppop, PWND pwndOwner )
2092 {
2093  ITEM *lpitem;
2094  UINT start, i, helpPos;
2095  int orgX, orgY, maxY;
2096 
2097  if ((lprect == NULL) || (lppop == NULL)) return;
2098  if (lppop->cItems == 0) return;
2099  //TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
2100  lppop->cxMenu = lprect->right - lprect->left;
2101  lppop->cyMenu = 0;
2102  maxY = lprect->top+1;
2103  start = 0;
2104  helpPos = ~0U;
2105  lppop->cxTextAlign = 0;
2106  while (start < lppop->cItems)
2107  {
2108  lpitem = &lppop->rgItems[start];
2109  orgX = lprect->left;
2110  orgY = maxY;
2111 
2112  /* Parse items until line break or end of menu */
2113  for (i = start; i < lppop->cItems; i++, lpitem++)
2114  {
2115  if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
2116  if ((i != start) &&
2117  (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
2118 
2119  TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
2120  //debug_print_menuitem (" item: ", lpitem, "");
2121  //MENU_CalcItemSize( hdc, lpitem, pwndOwner, orgX, orgY, TRUE, lppop );
2122  MENU_CalcItemSize(hdc, lpitem, lppop, pwndOwner, orgX, orgY, TRUE, FALSE);
2123 
2124  if (lpitem->cxItem > lprect->right)
2125  {
2126  if (i != start) break;
2127  else lpitem->cxItem = lprect->right;
2128  }
2129  maxY = max( maxY, lpitem->cyItem );
2130  orgX = lpitem->cxItem;
2131  }
2132 
2133  /* Finish the line (set all items to the largest height found) */
2134 
2135 /* FIXME: Is this really needed? */ /*NO! it is not needed, why make the
2136  HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
2137 #if 0
2138  while (start < i) lppop->rgItems[start++].cyItem = maxY;
2139 #endif
2140  start = i; /* This works! */
2141  }
2142 
2143  lprect->bottom = maxY;
2144  lppop->cyMenu = lprect->bottom - lprect->top;
2145 
2146  /* Flush right all items between the MF_RIGHTJUSTIFY and */
2147  /* the last item (if several lines, only move the last line) */
2148  if (helpPos == ~0U) return;
2149  lpitem = &lppop->rgItems[lppop->cItems-1];
2150  orgY = lpitem->yItem;
2151  orgX = lprect->right;
2152  for (i = lppop->cItems - 1; i >= helpPos; i--, lpitem--) {
2153  if (lpitem->yItem != orgY) break; /* Other line */
2154  if (lpitem->cxItem >= orgX) break; /* Too far right already */
2155  lpitem->xItem += orgX - lpitem->cxItem;
2156  lpitem->cxItem = orgX;
2157  orgX = lpitem->xItem;
2158  }
2159 }
2160 
2161 /***********************************************************************
2162  * MENU_DrawScrollArrows
2163  *
2164  * Draw scroll arrows.
2165  */
2166 static void MENU_DrawScrollArrows(PMENU lppop, HDC hdc)
2167 {
2168  UINT arrow_bitmap_width, arrow_bitmap_height;
2169  RECT rect, dfcrc;
2170  UINT Flags = 0;
2171 
2172  arrow_bitmap_width = gpsi->oembmi[OBI_DNARROW].cx;
2173  arrow_bitmap_height = gpsi->oembmi[OBI_DNARROW].cy;
2174 
2175  rect.left = 0;
2176  rect.top = 0;
2177  rect.right = lppop->cxMenu;
2178  rect.bottom = arrow_bitmap_height;
2179  FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENU));
2180  dfcrc.left = (lppop->cxMenu - arrow_bitmap_width) / 2;
2181  dfcrc.top = 0;
2182  dfcrc.right = arrow_bitmap_width;
2183  dfcrc.bottom = arrow_bitmap_height;
2184  DrawFrameControl(hdc, &dfcrc, DFC_MENU, (lppop->iTop ? 0 : DFCS_INACTIVE)|DFCS_MENUARROWUP);
2185 
2186  rect.top = lppop->cyMenu - arrow_bitmap_height;
2187  rect.bottom = lppop->cyMenu;
2188  FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENU));
2189  if (!(lppop->iTop < lppop->iMaxTop - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height)))
2190  Flags = DFCS_INACTIVE;
2191  dfcrc.left = (lppop->cxMenu - arrow_bitmap_width) / 2;
2192  dfcrc.top = lppop->cyMenu - arrow_bitmap_height;
2193  dfcrc.right = arrow_bitmap_width;
2194  dfcrc.bottom = lppop->cyMenu;
2195  DrawFrameControl(hdc, &dfcrc, DFC_MENU, Flags|DFCS_MENUARROWDOWN);
2196 }
2197 
2198 /***********************************************************************
2199  * MenuDrawMenuItem
2200  *
2201  * Draw a single menu item.
2202  */
2203 static void FASTCALL MENU_DrawMenuItem(PWND Wnd, PMENU Menu, PWND WndOwner, HDC hdc,
2204  PITEM lpitem, UINT Height, BOOL menuBar, UINT odaction)
2205 {
2206  RECT rect;
2207  PWCHAR Text;
2208  BOOL flat_menu = FALSE;
2209  int bkgnd;
2210  UINT arrow_bitmap_width = 0;
2211  //RECT bmprc;
2212 
2213  if (!menuBar) {
2214  arrow_bitmap_width = gpsi->oembmi[OBI_MNARROW].cx;
2215  }
2216 
2217  if (lpitem->fType & MF_SYSMENU)
2218  {
2219  if (!(Wnd->style & WS_MINIMIZE))
2220  {
2221  NC_GetInsideRect(Wnd, &rect);
2222  UserDrawSysMenuButton(Wnd, hdc, &rect, lpitem->fState & (MF_HILITE | MF_MOUSESELECT));
2223  }
2224  return;
2225  }
2226 
2227  UserSystemParametersInfo (SPI_GETFLATMENU, 0, &flat_menu, 0);
2228  bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
2229 
2230  /* Setup colors */
2231 
2232  if (lpitem->fState & MF_HILITE)
2233  {
2234  if(menuBar && !flat_menu) {
2237  } else {
2238  if (lpitem->fState & MF_GRAYED)
2240  else
2243  }
2244  }
2245  else
2246  {
2247  if (lpitem->fState & MF_GRAYED)
2249  else
2251  IntGdiSetBkColor( hdc, IntGetSysColor( bkgnd ) );
2252  }
2253 
2254  //TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->Rect));
2255  //rect = lpitem->Rect;
2256  rect.left = lpitem->xItem;
2257  rect.top = lpitem->yItem;
2258  rect.right = lpitem->cxItem; // Do this for now......
2259  rect.bottom = lpitem->cyItem;
2260 
2261  MENU_AdjustMenuItemRect(Menu, &rect);
2262 
2263  if (lpitem->fType & MF_OWNERDRAW)
2264  {
2265  /*
2266  ** Experimentation under Windows reveals that an owner-drawn
2267  ** menu is given the rectangle which includes the space it requested
2268  ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
2269  ** and a popup-menu arrow. This is the value of lpitem->rect.
2270  ** Windows will leave all drawing to the application except for
2271  ** the popup-menu arrow. Windows always draws that itself, after
2272  ** the menu owner has finished drawing.
2273  */
2274  DRAWITEMSTRUCT dis;
2275  COLORREF old_bk, old_text;
2276 
2277  dis.CtlType = ODT_MENU;
2278  dis.CtlID = 0;
2279  dis.itemID = lpitem->wID;
2280  dis.itemData = (DWORD)lpitem->dwItemData;
2281  dis.itemState = 0;
2282  if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
2283  if (lpitem->fState & MF_DEFAULT) dis.itemState |= ODS_DEFAULT;
2284  if (lpitem->fState & MF_DISABLED) dis.itemState |= ODS_DISABLED;
2285  if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED | ODS_DISABLED;
2286  if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
2287  if (!(Menu->fFlags & MNF_UNDERLINE)) dis.itemState |= ODS_NOACCEL;
2288  if (Menu->fFlags & MNF_INACTIVE) dis.itemState |= ODS_INACTIVE;
2289  dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
2290  dis.hwndItem = (HWND) UserHMGetHandle(Menu);
2291  dis.hDC = hdc;
2292  dis.rcItem = rect;
2293  TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
2294  "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", Wnd,
2295  dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
2296  dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
2297  dis.rcItem.bottom);
2298  TRACE("Ownerdraw: Width %d Height %d\n", dis.rcItem.right-dis.rcItem.left, dis.rcItem.bottom-dis.rcItem.top);
2299  old_bk = GreGetBkColor(hdc);
2300  old_text = GreGetTextColor(hdc);
2301  co_IntSendMessage(UserHMGetHandle(WndOwner), WM_DRAWITEM, 0, (LPARAM) &dis);
2302  IntGdiSetBkColor(hdc, old_bk);
2303  IntGdiSetTextColor(hdc, old_text);
2304  /* Draw the popup-menu arrow */
2305  if (!menuBar && lpitem->spSubMenu)
2306  {
2307  RECT rectTemp;
2308  RtlCopyMemory(&rectTemp, &rect, sizeof(RECT));
2309  rectTemp.left = rectTemp.right - UserGetSystemMetrics(SM_CXMENUCHECK);
2310  DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
2311  }
2312  return;
2313  }
2314 
2315  if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
2316 
2317  if (lpitem->fState & MF_HILITE)
2318  {
2319  if (flat_menu)
2320  {
2321  RECTL_vInflateRect (&rect, -1, -1);
2322  FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENUHILIGHT));
2323  RECTL_vInflateRect (&rect, 1, 1);
2325  }
2326  else
2327  {
2328  if(menuBar)
2329  DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
2330  else
2332  }
2333  }
2334  else
2335  FillRect( hdc, &rect, IntGetSysColorBrush(bkgnd) );
2336 
2337  IntGdiSetBkMode( hdc, TRANSPARENT );
2338 
2339  /* vertical separator */
2340  if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
2341  {
2342  HPEN oldPen;
2343  RECT rc = rect;
2344 
2345  rc.left -= 3;//MENU_COL_SPACE / 2 + 1; == 3!!
2346  rc.top = 3;
2347  rc.bottom = Height - 3;
2348  if (flat_menu)
2349  {
2350  oldPen = NtGdiSelectPen( hdc, NtGdiGetStockObject(DC_PEN) );
2352  GreMoveTo( hdc, rc.left, rc.top, NULL );
2353  NtGdiLineTo( hdc, rc.left, rc.bottom );
2354  NtGdiSelectPen( hdc, oldPen );
2355  }
2356  else
2357  DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
2358  }
2359 
2360  /* horizontal separator */
2361  if (lpitem->fType & MF_SEPARATOR)
2362  {
2363  HPEN oldPen;
2364  RECT rc = rect;
2365 
2366  rc.left++;
2367  rc.right--;
2368  rc.top = ( rc.top + rc.bottom) / 2;
2369  if (flat_menu)
2370  {
2371  oldPen = NtGdiSelectPen( hdc, NtGdiGetStockObject(DC_PEN) );
2373  GreMoveTo( hdc, rc.left, rc.top, NULL );
2374  NtGdiLineTo( hdc, rc.right, rc.top );
2375  NtGdiSelectPen( hdc, oldPen );
2376  }
2377  else
2378  DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
2379  return;
2380  }
2381 #if 0
2382  /* helper lines for debugging */
2383  /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
2385  NtGdiSelectPen(hdc, NtGdiGetStockObject(DC_PEN));
2387  GreMoveTo(hdc, rect.left, (rect.top + rect.bottom) / 2, NULL);
2388  NtGdiLineTo(hdc, rect.right, (rect.top + rect.bottom) / 2);
2389 #endif
2390 #if 0 // breaks mdi menu bar icons.
2391  if (lpitem->hbmp) {
2392  /* calculate the bitmap rectangle in coordinates relative
2393  * to the item rectangle */
2394  if( menuBar) {
2395  if( lpitem->hbmp == HBMMENU_CALLBACK)
2396  bmprc.left = 3;
2397  else
2398  bmprc.left = lpitem->Xlpstr ? MenuCharSize.cx : 0;
2399  }
2400  else if ((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK)
2401  bmprc.left = 4;
2402  else if ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)
2403  bmprc.left = 2;
2404  else
2405  bmprc.left = 4 + UserGetSystemMetrics(SM_CXMENUCHECK);
2406 
2407  bmprc.right = bmprc.left + lpitem->cxBmp;
2408 
2409  if( menuBar && !(lpitem->hbmp == HBMMENU_CALLBACK))
2410  bmprc.top = 0;
2411  else
2412  bmprc.top = (rect.bottom - rect.top - lpitem->cyBmp) / 2;
2413 
2414  bmprc.bottom = bmprc.top + lpitem->cyBmp;
2415  }
2416 #endif
2417  if (!menuBar)
2418  {
2419  HBITMAP bm;
2420  INT y = rect.top + rect.bottom;
2421  RECT rc = rect;
2422  BOOL checked = FALSE;
2423  UINT check_bitmap_width = UserGetSystemMetrics( SM_CXMENUCHECK );
2424  UINT check_bitmap_height = UserGetSystemMetrics( SM_CYMENUCHECK );
2425  /* Draw the check mark
2426  *
2427  * FIXME:
2428  * Custom checkmark bitmaps are monochrome but not always 1bpp.
2429  */
2430  if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK)) {
2431  bm = (lpitem->fState & MF_CHECKED) ? lpitem->hbmpChecked :
2432  lpitem->hbmpUnchecked;
2433  if (bm) /* we have a custom bitmap */
2434  {
2436 
2437  NtGdiSelectBitmap( hdcMem, bm );
2438  NtGdiBitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
2439  check_bitmap_width, check_bitmap_height,
2440  hdcMem, 0, 0, SRCCOPY, 0,0);
2441  IntGdiDeleteDC( hdcMem, FALSE );
2442  checked = TRUE;
2443  }
2444  else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
2445  {
2446  RECT r;
2447  r = rect;
2448  r.right = r.left + check_bitmap_width;
2449  DrawFrameControl( hdc, &r, DFC_MENU,
2450  (lpitem->fType & MFT_RADIOCHECK) ?
2452  checked = TRUE;
2453  }
2454  }
2455  if ( lpitem->hbmp )//&& !( checked && ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)))
2456  {
2457  RECT bmpRect = rect;
2458  if (!((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP) && !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
2459  bmpRect.left += check_bitmap_width + 2;
2460  if (!(checked && ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)))
2461  {
2462  bmpRect.right = bmpRect.left + lpitem->cxBmp;
2463  MENU_DrawBitmapItem(hdc, lpitem, &bmpRect, Menu, WndOwner, odaction, menuBar);
2464  }
2465  }
2466  /* Draw the popup-menu arrow */
2467  if (lpitem->spSubMenu)
2468  {
2469  RECT rectTemp;
2470  RtlCopyMemory(&rectTemp, &rect, sizeof(RECT));
2471  rectTemp.left = rectTemp.right - check_bitmap_width;
2472  DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
2473  }
2474  rect.left += 4;
2475  if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
2476  rect.left += check_bitmap_width;
2477  rect.right -= arrow_bitmap_width;
2478  }
2479  else if( lpitem->hbmp)
2480  { /* Draw the bitmap */
2481  MENU_DrawBitmapItem(hdc, lpitem, &rect/*bmprc*/, Menu, WndOwner, odaction, menuBar);
2482  }
2483 
2484  /* process text if present */
2485  if (lpitem->Xlpstr)
2486  {
2487  int i = 0;
2488  HFONT hfontOld = 0;
2489 
2490  UINT uFormat = menuBar ?
2493 
2494  if (((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP))
2495  rect.left += max(0, (int)(Menu->cxTextAlign - UserGetSystemMetrics(SM_CXMENUCHECK)));
2496  else
2497  rect.left += Menu->cxTextAlign;
2498 
2499  if ( lpitem->fState & MFS_DEFAULT )
2500  {
2501  hfontOld = NtGdiSelectFont(hdc, ghMenuFontBold);
2502  }
2503 
2504  if (menuBar) {
2505  if( lpitem->hbmp)
2506  rect.left += lpitem->cxBmp;
2507  if( !(lpitem->hbmp == HBMMENU_CALLBACK))
2508  rect.left += MenuCharSize.cx;
2509  rect.right -= MenuCharSize.cx;
2510  }
2511 
2512  Text = lpitem->Xlpstr;
2513  if(Text)
2514  {
2515  for (i = 0; Text[i]; i++)
2516  if (Text[i] == L'\t' || Text[i] == L'\b')
2517  break;
2518  }
2519 
2520  if(lpitem->fState & MF_GRAYED)
2521  {
2522  if (!(lpitem->fState & MF_HILITE) )
2523  {
2524  ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
2526  DrawTextW( hdc, Text, i, &rect, uFormat );
2527  --rect.left; --rect.top; --rect.right; --rect.bottom;
2528  }
2530  }
2531  DrawTextW( hdc, Text, i, &rect, uFormat);
2532 
2533  /* paint the shortcut text */
2534  if (!menuBar && L'\0' != Text[i]) /* There's a tab or flush-right char */
2535  {
2536  if (L'\t' == Text[i])
2537  {
2538  rect.left = lpitem->dxTab;
2539  uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
2540  }
2541  else
2542  {
2543  rect.right = lpitem->dxTab;
2544  uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
2545  }
2546 
2547  if (lpitem->fState & MF_GRAYED)
2548  {
2549  if (!(lpitem->fState & MF_HILITE) )
2550  {
2551  ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
2553  DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat);
2554  --rect.left; --rect.top; --rect.right; --rect.bottom;
2555  }
2557  }
2558  DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat );
2559  }
2560 
2561  if (hfontOld)
2562  {
2563  NtGdiSelectFont (hdc, hfontOld);
2564  }
2565  }
2566 }
2567 
2568 /***********************************************************************
2569  * MenuDrawPopupMenu
2570  *
2571  * Paint a popup menu.
2572  */
2573 static void FASTCALL MENU_DrawPopupMenu(PWND wnd, HDC hdc, PMENU menu )
2574 {
2575  HBRUSH hPrevBrush = 0, brush = IntGetSysColorBrush(COLOR_MENU);
2576  RECT rect;
2577 
2578  TRACE("DPM wnd=%p dc=%p menu=%p\n", wnd, hdc, menu);
2579 
2580  IntGetClientRect( wnd, &rect );
2581 
2582  if (menu && menu->hbrBack) brush = menu->hbrBack;
2583  if((hPrevBrush = NtGdiSelectBrush( hdc, brush ))
2584  && (NtGdiSelectFont( hdc, ghMenuFont)))
2585  {
2586  HPEN hPrevPen;
2587 
2588  NtGdiRectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
2589 
2590  hPrevPen = NtGdiSelectPen( hdc, NtGdiGetStockObject( NULL_PEN ) );
2591  if ( hPrevPen )
2592  {
2593  BOOL flat_menu = FALSE;
2594 
2595  UserSystemParametersInfo (SPI_GETFLATMENU, 0, &flat_menu, 0);
2596  if (flat_menu)
2598  else
2599  DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
2600 
2601  TRACE("hmenu %p Style %08x\n", UserHMGetHandle(menu), (menu->fFlags & MNS_STYLE_MASK));
2602  /* draw menu items */
2603  if (menu && menu->cItems)
2604  {
2605  ITEM *item;
2606  UINT u;
2607 
2608  item = menu->rgItems;
2609  for( u = menu->cItems; u > 0; u--, item++)
2610  {
2611  MENU_DrawMenuItem(wnd, menu, menu->spwndNotify, hdc, item,
2612  menu->cyMenu, FALSE, ODA_DRAWENTIRE);
2613  }
2614  /* draw scroll arrows */
2615  if (menu->dwArrowsOn)
2616  {
2617  MENU_DrawScrollArrows(menu, hdc);
2618  }
2619  }
2620  }
2621  else
2622  {
2623  NtGdiSelectBrush( hdc, hPrevBrush );
2624  }
2625  }
2626 }
2627 
2628 /**********************************************************************
2629  * MENU_IsMenuActive
2630  */
2632 {
2633  return ValidateHwndNoErr(top_popup);
2634 }
2635 
2636 /**********************************************************************
2637  * MENU_EndMenu
2638  *
2639  * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2640  *
2641  * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2642  */
2643 void MENU_EndMenu( PWND pwnd )
2644 {
2645  PMENU menu = NULL;
2647  if ( menu && ( UserHMGetHandle(pwnd) == menu->hWnd || pwnd == menu->spwndNotify ) )
2648  {
2649  if (fInsideMenuLoop && top_popup)
2650  {
2652 
2653  if (fInEndMenu)
2654  {
2655  ERR("Already in End loop\n");
2656  return;
2657  }
2658 
2659  fInEndMenu = TRUE;
2661  }
2662  }
2663 }
2664 
2665 DWORD WINAPI
2667 {
2668  UINT i;
2669  HFONT FontOld = NULL;
2670  BOOL flat_menu = FALSE;
2671 
2672  UserSystemParametersInfo(SPI_GETFLATMENU, 0, &flat_menu, 0);
2673 
2674  if (!pMenu)
2675  {
2676  pMenu = UserGetMenuObject(UlongToHandle(pWnd->IDMenu));
2677  }
2678 
2679  if (!Font)
2680  {
2681  Font = ghMenuFont;
2682  }
2683 
2684  if (Rect == NULL || !pMenu)
2685  {
2687  }
2688 
2689  TRACE("(%x, %x, %p, %x, %x)\n", pWnd, hDC, Rect, pMenu, Font);
2690 
2691  FontOld = NtGdiSelectFont(hDC, Font);
2692 
2693  if (pMenu->cyMenu == 0)
2694  {
2695  MENU_MenuBarCalcSize(hDC, Rect, pMenu, pWnd);
2696  }
2697 
2698  Rect->bottom = Rect->top + pMenu->cyMenu;
2699 
2700  FillRect(hDC, Rect, IntGetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
2701 
2702  NtGdiSelectPen(hDC, NtGdiGetStockObject(DC_PEN));
2704  GreMoveTo(hDC, Rect->left, Rect->bottom - 1, NULL);
2705  NtGdiLineTo(hDC, Rect->right, Rect->bottom - 1);
2706 
2707  if (pMenu->cItems == 0)
2708  {
2709  NtGdiSelectFont(hDC, FontOld);
2711  }
2712 
2713  for (i = 0; i < pMenu->cItems; i++)
2714  {
2715  MENU_DrawMenuItem(pWnd, pMenu, pWnd, hDC, &pMenu->rgItems[i], pMenu->cyMenu, TRUE, ODA_DRAWENTIRE);
2716  }
2717 
2718  NtGdiSelectFont(hDC, FontOld);
2719 
2720  return pMenu->cyMenu;
2721 }
2722 
2723 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, PWND pWnd, BOOL suppress_draw )
2724 {
2725  HFONT hfontOld = 0;
2726  PMENU lppop = UserGetMenuObject(UlongToHandle(pWnd->IDMenu));
2727 
2728  if (lppop == NULL)
2729  {
2730  // No menu. Do not reserve any space
2731  return 0;
2732  }
2733 
2734  if (lprect == NULL)
2735  {
2737  }
2738 
2739  if (suppress_draw)
2740  {
2741  hfontOld = NtGdiSelectFont(hDC, ghMenuFont);
2742 
2743  MENU_MenuBarCalcSize(hDC, lprect, lppop, pWnd);
2744 
2745  lprect->bottom = lprect->top + lppop->cyMenu;
2746 
2747  if (hfontOld) NtGdiSelectFont( hDC, hfontOld);
2748 
2749  return lppop->cyMenu;
2750  }
2751  else
2752  {
2753  return IntDrawMenuBarTemp(pWnd, hDC, lprect, lppop, NULL);
2754  }
2755 }
2756 
2757 /***********************************************************************
2758  * MENU_InitPopup
2759  *
2760  * Popup menu initialization before WM_ENTERMENULOOP.
2761  */
2762 static BOOL MENU_InitPopup( PWND pWndOwner, PMENU menu, UINT flags )
2763 {
2764  PWND pWndCreated;
2765  PPOPUPMENU pPopupMenu;
2766  CREATESTRUCTW Cs;
2767  LARGE_STRING WindowName;
2768  UNICODE_STRING ClassName;
2769  DWORD ex_style = WS_EX_TOOLWINDOW;
2770 
2771  TRACE("owner=%p hmenu=%p\n", pWndOwner, menu);
2772 
2773  menu->spwndNotify = pWndOwner;
2774 
2775  if (flags & TPM_LAYOUTRTL || pWndOwner->ExStyle & WS_EX_LAYOUTRTL)
2776  ex_style = WS_EX_LAYOUTRTL;
2777 
2778  ClassName.Buffer = WC_MENU;
2779  ClassName.Length = 0;
2780 
2781  RtlZeroMemory(&WindowName, sizeof(WindowName));
2782  RtlZeroMemory(&Cs, sizeof(Cs));
2783  Cs.style = WS_POPUP;
2784  Cs.dwExStyle = ex_style;
2785  Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
2786  Cs.lpszName = (LPCWSTR) &WindowName;
2787  Cs.lpszClass = (LPCWSTR) &ClassName;
2788  Cs.lpCreateParams = UserHMGetHandle(menu);
2789  Cs.hwndParent = UserHMGetHandle(pWndOwner);
2790 
2791  /* NOTE: In Windows, top menu popup is not owned. */
2792  pWndCreated = co_UserCreateWindowEx( &Cs, &ClassName, &WindowName, NULL);
2793 
2794  if( !pWndCreated ) return FALSE;
2795 
2796  //
2797  // Setup pop up menu structure.
2798  //
2799  menu->hWnd = UserHMGetHandle(pWndCreated);
2800 
2801  pPopupMenu = ((PMENUWND)pWndCreated)->ppopupmenu;
2802 
2803  pPopupMenu->spwndActivePopup = pWndCreated; // top_popup = MenuInfo.Wnd or menu->hWnd
2804  pPopupMenu->spwndNotify = pWndOwner; // Same as MenuInfo.spwndNotify(which could be wrong) or menu->hwndOwner
2805  //pPopupMenu->spmenu = menu; Should be set up already from WM_CREATE!
2806 
2807  pPopupMenu->fIsTrackPopup = !!(flags & TPM_POPUPMENU);
2808  pPopupMenu->fIsSysMenu = !!(flags & TPM_SYSTEM_MENU);
2809  pPopupMenu->fNoNotify = !!(flags & TPM_NONOTIFY);
2810  pPopupMenu->fRightButton = !!(flags & TPM_RIGHTBUTTON);
2811  pPopupMenu->fSynchronous = !!(flags & TPM_RETURNCMD);
2812 
2813  if (pPopupMenu->fRightButton)
2814  pPopupMenu->fFirstClick = !!(UserGetKeyState(VK_RBUTTON) & 0x8000);
2815  else
2816  pPopupMenu->fFirstClick = !!(UserGetKeyState(VK_LBUTTON) & 0x8000);
2817 
2818  if (gpsi->aiSysMet[SM_MENUDROPALIGNMENT] ||
2819  menu->fFlags & MNF_RTOL)
2820  {
2821  pPopupMenu->fDroppedLeft = TRUE;
2822  }
2823  return TRUE;
2824 }
2825 
2826 /***********************************************************************
2827  * MenuShowPopup
2828  *
2829  * Display a popup menu.
2830  */
2831 static BOOL FASTCALL MENU_ShowPopup(PWND pwndOwner, PMENU menu, UINT id, UINT flags,
2832  INT x, INT y, INT xanchor, INT yanchor )
2833 {
2834  UINT width, height;
2835  POINT pt;
2836  PMONITOR monitor;
2837  PWND pWnd;
2839 
2840  TRACE("owner=%p menu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
2841  pwndOwner, menu, id, x, y, xanchor, yanchor);
2842 
2843  if (menu->iItem != NO_SELECTED_ITEM)
2844  {
2845  menu->rgItems[menu->iItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
2846  menu->iItem = NO_SELECTED_ITEM;
2847  }
2848 
2849  menu->dwArrowsOn = 0;
2850  MENU_PopupMenuCalcSize(menu, pwndOwner);
2851 
2852  /* adjust popup menu pos so that it fits within the desktop */
2853 
2854  width = menu->cxMenu + UserGetSystemMetrics(SM_CXBORDER);
2855  height = menu->cyMenu + UserGetSystemMetrics(SM_CYBORDER);
2856 
2857  /* FIXME: should use item rect */
2858  pt.x = x;
2859  pt.y = y;
2860  monitor = UserMonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
2861 
2862  if (flags & TPM_LAYOUTRTL)
2863  flags ^= TPM_RIGHTALIGN;
2864 
2865  if( flags & TPM_RIGHTALIGN ) x -= width;
2866  if( flags & TPM_CENTERALIGN ) x -= width / 2;
2867 
2868  if( flags & TPM_BOTTOMALIGN ) y -= height;
2869  if( flags & TPM_VCENTERALIGN ) y -= height / 2;
2870 
2871  if( x + width > monitor->rcMonitor.right)
2872  {
2873  if( xanchor && x >= width - xanchor )
2874  x -= width - xanchor;
2875 
2876  if( x + width > monitor->rcMonitor.right)
2877  x = monitor->rcMonitor.right - width;
2878  }
2879  if( x < monitor->rcMonitor.left ) x = monitor->rcMonitor.left;
2880 
2881  if( y + height > monitor->rcMonitor.bottom)
2882  {
2883  if( yanchor && y >= height + yanchor )
2884  y -= height + yanchor;
2885 
2886  if( y + height > monitor->rcMonitor.bottom)
2887  y = monitor->rcMonitor.bottom - height;
2888  }
2889  if( y < monitor->rcMonitor.top ) y = monitor->rcMonitor.top;
2890 
2891  pWnd = ValidateHwndNoErr( menu->hWnd );
2892 
2893  if (!pWnd)
2894  {
2895  ERR("menu->hWnd bad hwnd %p\n",menu->hWnd);
2896  return FALSE;
2897  }
2898 
2899  if (!top_popup) {
2900  top_popup = menu->hWnd;
2902  }
2903 
2904  /* Display the window */
2905  UserRefObjectCo(pWnd, &Ref);
2906  co_WinPosSetWindowPos( pWnd, HWND_TOPMOST, x, y, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE);
2907 
2909 
2910  IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, pWnd, OBJID_CLIENT, CHILDID_SELF, 0);
2911  UserDerefObjectCo(pWnd);
2912 
2913  return TRUE;
2914 }
2915 
2916 /***********************************************************************
2917  * MENU_EnsureMenuItemVisible
2918  */
2920 {
2922  if (lppop->dwArrowsOn)
2923  {
2924  ITEM *item = &lppop->rgItems[wIndex];
2925  UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
2926  UINT nOldPos = lppop->iTop;
2927  RECT rc;
2928  UINT arrow_bitmap_height;
2929  PWND pWnd = ValidateHwndNoErr(lppop->hWnd);
2930 
2931  IntGetClientRect(pWnd, &rc);
2932 
2933  arrow_bitmap_height = gpsi->oembmi[OBI_DNARROW].cy;
2934 
2935  rc.top += arrow_bitmap_height;
2936  rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
2937 
2938  nMaxHeight -= UserGetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
2939  UserRefObjectCo(pWnd, &Ref);
2940  if (item->cyItem > lppop->iTop + nMaxHeight)
2941  {
2942  lppop->iTop = item->cyItem - nMaxHeight;
2943  IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc);
2944  MENU_DrawScrollArrows(lppop, hdc);
2945  //ERR("Scroll Down iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
2946  }
2947  else if (item->yItem - MENU_TOP_MARGIN < lppop->iTop)
2948  {
2949  lppop->iTop = item->yItem - MENU_TOP_MARGIN;
2950  IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc);
2951  MENU_DrawScrollArrows(lppop, hdc);
2952  //ERR("Scroll Up iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
2953  }
2954  UserDerefObjectCo(pWnd);
2955  }
2956 }
2957 
2958 /***********************************************************************
2959  * MenuSelectItem
2960  */
2961 static void FASTCALL MENU_SelectItem(PWND pwndOwner, PMENU menu, UINT wIndex,
2962  BOOL sendMenuSelect, PMENU topmenu)
2963 {
2964  HDC hdc;
2965  PWND pWnd;
2966 
2967  TRACE("M_SI: owner=%p menu=%p index=0x%04x select=0x%04x\n", pwndOwner, menu, wIndex, sendMenuSelect);
2968 
2969  if (!menu || !menu->cItems) return;
2970 
2971  pWnd = ValidateHwndNoErr(menu->hWnd);
2972 
2973  if (!pWnd) return;
2974 
2975  if (menu->iItem == wIndex) return;
2976 
2977  if (menu->fFlags & MNF_POPUP)
2978  hdc = UserGetDCEx(pWnd, 0, DCX_USESTYLE);
2979  else
2980  hdc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW);
2981 
2982  if (!top_popup) {
2983  top_popup = menu->hWnd; //pPopupMenu->spwndActivePopup or
2984  //pPopupMenu->fIsTrackPopup set pPopupMenu->spwndPopupMenu;
2985  top_popup_hmenu = UserHMGetHandle(menu); //pPopupMenu->spmenu
2986  }
2987 
2988  NtGdiSelectFont( hdc, ghMenuFont );
2989 
2990  /* Clear previous highlighted item */
2991  if (menu->iItem != NO_SELECTED_ITEM)
2992  {
2993  menu->rgItems[menu->iItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
2994  MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc, &menu->rgItems[menu->iItem],
2995  menu->cyMenu, !(menu->fFlags & MNF_POPUP),
2996  ODA_SELECT);
2997  }
2998 
2999  /* Highlight new item (if any) */
3000  menu->iItem = wIndex;
3001  if (menu->iItem != NO_SELECTED_ITEM)
3002  {
3003  if (!(menu->rgItems[wIndex].fType & MF_SEPARATOR))
3004  {
3005  menu->rgItems[wIndex].fState |= MF_HILITE;
3006  MENU_EnsureMenuItemVisible(menu, wIndex, hdc);
3007  MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc,
3008  &menu->rgItems[wIndex], menu->cyMenu, !(menu->fFlags & MNF_POPUP), ODA_SELECT);
3009  }
3010  if (sendMenuSelect)
3011  {
3012  ITEM *ip = &menu->rgItems[menu->iItem];
3013  WPARAM wParam = MAKEWPARAM( ip->spSubMenu ? wIndex : ip->wID,
3014  ip->fType | ip->fState |
3015  (ip->spSubMenu ? MF_POPUP : 0) |
3016  (menu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) );
3017 
3019  }
3020  }
3021  else if (sendMenuSelect)
3022  {
3023  if (topmenu)
3024  {
3025  int pos;
3026  pos = MENU_FindSubMenu(&topmenu, menu);
3027  if (pos != NO_SELECTED_ITEM)
3028  {
3029  ITEM *ip = &topmenu->rgItems[pos];
3030  WPARAM wParam = MAKEWPARAM( Pos, ip->fType | ip->fState |
3031  (ip->spSubMenu ? MF_POPUP : 0) |
3032  (topmenu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) );
3033 
3034  co_IntSendMessage(UserHMGetHandle(pwndOwner), WM_MENUSELECT, wParam, (LPARAM) UserHMGetHandle(topmenu));
3035  }
3036  }
3037  }
3038  UserReleaseDC(pWnd, hdc, FALSE);
3039 }
3040 
3041 /***********************************************************************
3042  * MenuMoveSelection
3043  *
3044  * Moves currently selected item according to the Offset parameter.
3045  * If there is no selection then it should select the last item if
3046  * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
3047  */
3048 static void FASTCALL MENU_MoveSelection(PWND pwndOwner, PMENU menu, INT offset)
3049 {
3050  INT i;
3051 
3052  TRACE("pwnd=%x menu=%x off=0x%04x\n", pwndOwner, menu, offset);
3053 
3054  if ((!menu) || (!menu->rgItems)) return;
3055 
3056  if ( menu->iItem != NO_SELECTED_ITEM )
3057  {
3058  if ( menu->cItems == 1 )
3059  return;
3060  else
3061  for (i = menu->iItem + offset ; i >= 0 && i < menu->cItems
3062  ; i += offset)
3063  if (!(menu->rgItems[i].fType & MF_SEPARATOR))
3064  {
3065  MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 );
3066  return;
3067  }
3068  }
3069 
3070  for ( i = (offset > 0) ? 0 : menu->cItems - 1;
3071  i >= 0 && i < menu->cItems ; i += offset)
3072  if (!(menu->rgItems[i].fType & MF_SEPARATOR))
3073  {
3074  MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 );
3075  return;
3076  }
3077 }
3078 
3079 /***********************************************************************
3080  * MenuHideSubPopups
3081  *
3082  * Hide the sub-popup menus of this menu.
3083  */
3084 static void FASTCALL MENU_HideSubPopups(PWND pWndOwner, PMENU Menu,
3085  BOOL SendMenuSelect, UINT wFlags)
3086 {
3087  TRACE("owner=%x menu=%x 0x%04x\n", pWndOwner, Menu, SendMenuSelect);
3088 
3089  if ( Menu && top_popup )
3090  {
3091  PITEM Item;
3092 
3093  if (Menu->iItem != NO_SELECTED_ITEM)
3094  {
3095  Item = &Menu->rgItems[Menu->iItem];
3096  if (!(Item->spSubMenu) ||
3097  !(Item->fState & MF_MOUSESELECT)) return;
3098  Item->fState &= ~MF_MOUSESELECT;
3099  }
3100  else
3101  return;
3102 
3103  if (Item->spSubMenu)
3104  {
3105  PWND pWnd;
3106  if (!VerifyMenu(Item->spSubMenu)) return;
3107  pWnd = ValidateHwndNoErr(Item->spSubMenu->hWnd);
3108  MENU_HideSubPopups(pWndOwner, Item->spSubMenu, FALSE, wFlags);
3109  MENU_SelectItem(pWndOwner, Item->spSubMenu, NO_SELECTED_ITEM, SendMenuSelect, NULL);
3110  TRACE("M_HSP top p hm %p pWndOwner IDMenu %p\n",top_popup_hmenu,pWndOwner->IDMenu);
3111  co_UserDestroyWindow(pWnd);
3112 
3113  /* Native returns handle to destroyed window */
3114  if (!(wFlags & TPM_NONOTIFY))
3115  {
3116  co_IntSendMessage( UserHMGetHandle(pWndOwner), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(Item->spSubMenu),
3117  MAKELPARAM(0, IS_SYSTEM_MENU(Item->spSubMenu)) );
3118  }
3120  // Call WM_UNINITMENUPOPUP FIRST before destroy!!
3121  // Fixes todo_wine User32 test menu.c line 2239 GetMenuBarInfo callback....
3122  //
3123  Item->spSubMenu->hWnd = NULL;
3125  }
3126  }
3127 }
3128 
3129 /***********************************************************************
3130  * MenuShowSubPopup
3131  *
3132  * Display the sub-menu of the selected item of this menu.
3133  * Return the handle of the submenu, or menu if no submenu to display.
3134  */
3135 static PMENU FASTCALL MENU_ShowSubPopup(PWND WndOwner, PMENU Menu, BOOL SelectFirst, UINT Flags)
3136 {
3137  RECT Rect;
3138  ITEM *Item;
3139  HDC Dc;
3140  PWND pWnd;
3141 
3142  TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, Menu, SelectFirst);
3143 
3144  if (!Menu) return Menu;
3145 
3146  if (Menu->iItem == NO_SELECTED_ITEM) return Menu;
3147 
3148  Item = &Menu->rgItems[Menu->iItem];
3149  if (!(Item->spSubMenu) || (Item->fState & (MF_GRAYED | MF_DISABLED)))
3150  return Menu;
3151 
3152  /* message must be sent before using item,
3153  because nearly everything may be changed by the application ! */
3154 
3155  /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3156  if (!(Flags & TPM_NONOTIFY))
3157  {
3159  (WPARAM) UserHMGetHandle(Item->spSubMenu),
3160  MAKELPARAM(Menu->iItem, IS_SYSTEM_MENU(Menu)));
3161  }
3162 
3163  Item = &Menu->rgItems[Menu->iItem];
3164  //Rect = ItemInfo.Rect;
3165  Rect.left = Item->xItem;
3166  Rect.top = Item->yItem;
3167  Rect.right = Item->cxItem; // Do this for now......
3168  Rect.bottom = Item->cyItem;
3169 
3170  pWnd = ValidateHwndNoErr(Menu->hWnd);
3171 
3172  /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
3173  if (!(Item->fState & MF_HILITE))
3174  {
3175  if (Menu->fFlags & MNF_POPUP) Dc = UserGetDCEx(pWnd, NULL, DCX_USESTYLE);
3176  else Dc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW);
3177 
3179 
3180  Item->fState |= MF_HILITE;
3181  MENU_DrawMenuItem(pWnd, Menu, WndOwner, Dc, Item, Menu->cyMenu,
3182  !(Menu->fFlags & MNF_POPUP), ODA_DRAWENTIRE);
3183 
3184  UserReleaseDC(pWnd, Dc, FALSE);
3185  }
3186 
3187  if (!Item->yItem && !Item->xItem && !Item->cyItem && !Item->cxItem)
3188  {
3189  Item->xItem = Rect.left;
3190  Item->yItem = Rect.top;
3191  Item->cxItem = Rect.right; // Do this for now......
3192  Item->cyItem = Rect.bottom;
3193  }
3194  Item->fState |= MF_MOUSESELECT;
3195 
3196  if (IS_SYSTEM_MENU(Menu))
3197  {
3198  MENU_InitSysMenuPopup(Item->spSubMenu, pWnd->style, pWnd->pcls->style, HTSYSMENU);
3199 
3200  NC_GetSysPopupPos(pWnd, &Rect);
3201  if (Flags & TPM_LAYOUTRTL) Rect.left = Rect.right;
3202  Rect.top = Rect.bottom;
3205  }
3206  else
3207  {
3208  IntGetWindowRect(pWnd, &Rect);
3209  if (Menu->fFlags & MNF_POPUP)
3210  {
3211  RECT rc;
3212  rc.left = Item->xItem;
3213  rc.top = Item->yItem;
3214  rc.right = Item->cxItem; // Do this for now......
3215  rc.bottom = Item->cyItem;
3216 
3217  MENU_AdjustMenuItemRect(Menu, &rc);
3218 
3219  /* The first item in the popup menu has to be at the
3220  same y position as the focused menu item */
3221  if(Flags & TPM_LAYOUTRTL)
3223  else
3224  Rect.left += rc.right /*ItemInfo.Rect.right*/ - UserGetSystemMetrics(SM_CXBORDER);
3225  Rect.top += rc.top - MENU_TOP_MARGIN;//3;
3226  Rect.right = rc.left - rc.right + UserGetSystemMetrics(SM_CXBORDER);
3227  Rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN - MENU_BOTTOM_MARGIN/*2*/
3229  }
3230  else
3231  {
3232  if(Flags & TPM_LAYOUTRTL)
3233  Rect.left += Rect.right - Item->xItem; //ItemInfo.Rect.left;
3234  else
3235  Rect.left += Item->xItem; //ItemInfo.Rect.left;
3236  Rect.top += Item->cyItem; //ItemInfo.Rect.bottom;
3237  Rect.right = Item->cxItem - Item->xItem; //ItemInfo.Rect.right - ItemInfo.Rect.left;
3238  Rect.bottom = Item->cyItem - Item->yItem; //ItemInfo.Rect.bottom - ItemInfo.Rect.top;
3239  }
3240  }
3241 
3242  /* use default alignment for submenus */
3244 
3245  MENU_InitPopup( WndOwner, Item->spSubMenu, Flags );
3246 
3247  MENU_ShowPopup( WndOwner, Item->spSubMenu, Menu->iItem, Flags,
3248  Rect.left, Rect.top, Rect.right, Rect.bottom );
3249  if (SelectFirst)
3250  {
3251  MENU_MoveSelection(WndOwner, Item->spSubMenu, ITEM_NEXT);
3252  }
3253  return Item->spSubMenu;
3254 }
3255 
3256 /***********************************************************************
3257  * MenuExecFocusedItem
3258  *
3259  * Execute a menu item (for instance when user pressed Enter).
3260  * Return the wID of the executed item. Otherwise, -1 indicating
3261  * that no menu item was executed, -2 if a popup is shown;
3262  * Have to receive the flags for the TrackPopupMenu options to avoid
3263  * sending unwanted message.
3264  *
3265  */
3267 {
3268  PITEM Item;
3269 
3270  TRACE("%p menu=%p\n", pmt, Menu);
3271 
3272  if (!Menu || !Menu->cItems || Menu->iItem == NO_SELECTED_ITEM)
3273  {
3274  return -1;
3275  }
3276 
3277  Item = &Menu->rgItems[Menu->iItem];
3278 
3279  TRACE("%p %08x %p\n", Menu, Item->wID, Item->spSubMenu);
3280 
3281  if (!(Item->spSubMenu))
3282  {
3283  if (!(Item->fState & (MF_GRAYED | MF_DISABLED)) && !(Item->fType & MF_SEPARATOR))
3284  {
3285  /* If TPM_RETURNCMD is set you return the id, but
3286  do not send a message to the owner */
3287  if (!(Flags & TPM_RETURNCMD))
3288  {
3289  if (Menu->fFlags & MNF_SYSMENU)
3290  {
3292  MAKELPARAM((SHORT) pmt->Pt.x, (SHORT) pmt->Pt.y));
3293  }
3294  else
3295  {
3296  DWORD dwStyle = ((Menu->fFlags & MNS_STYLE_MASK) | ( pmt->TopMenu ? (pmt->TopMenu->fFlags & MNS_STYLE_MASK) : 0) );
3297 
3298  if (dwStyle & MNS_NOTIFYBYPOS)
3299  UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_MENUCOMMAND, Menu->iItem, (LPARAM)UserHMGetHandle(Menu));
3300  else
3302  }
3303  }
3304  return Item->wID;
3305  }
3306  }
3307  else
3308  {
3309  pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, Menu, TRUE, Flags);
3310  return -2;
3311  }
3312 
3313  return -1;
3314 }
3315 
3316 /***********************************************************************
3317  * MenuSwitchTracking
3318  *
3319  * Helper function for menu navigation routines.
3320  */
3322 {
3323  TRACE("%x menu=%x 0x%04x\n", pmt, PtMenu, Index);
3324 
3325  if ( pmt->TopMenu != PtMenu &&
3326  !((PtMenu->fFlags | pmt->TopMenu->fFlags) & MNF_POPUP) )
3327  {
3328  /* both are top level menus (system and menu-bar) */
3329  MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, wFlags);
3331  pmt->TopMenu = PtMenu;
3332  }
3333  else
3334  {
3335  MENU_HideSubPopups(pmt->OwnerWnd, PtMenu, FALSE, wFlags);
3336  }
3337 
3338  MENU_SelectItem(pmt->OwnerWnd, PtMenu, Index, TRUE, NULL);
3339 }
3340 
3341 /***********************************************************************
3342  * MenuButtonDown
3343  *
3344  * Return TRUE if we can go on with menu tracking.
3345  */
3347 {
3348  TRACE("%x PtMenu=%p\n", pmt, PtMenu);
3349 
3350  if (PtMenu)
3351  {
3352  UINT id = 0;
3353  PITEM item;
3354  if (IS_SYSTEM_MENU(PtMenu))
3355  {
3356  item = PtMenu->rgItems;
3357  }
3358  else
3359  {
3360  item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &id );
3361  }
3362 
3363  if (item)
3364  {
3365  if (PtMenu->iItem != id)
3366  MENU_SwitchTracking(pmt, PtMenu, id, Flags);
3367 
3368  /* If the popup menu is not already "popped" */
3369  if (!(item->fState & MF_MOUSESELECT))
3370  {
3371  pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags);
3372  }
3373 
3374  return TRUE;
3375  }
3376  /* Else the click was on the menu bar, finish the tracking */
3377  }
3378  return FALSE;
3379 }
3380 
3381 /***********************************************************************
3382  * MenuButtonUp
3383  *
3384  * Return the value of MenuExecFocusedItem if
3385  * the selected item was not a popup. Else open the popup.
3386  * A -1 return value indicates that we go on with menu tracking.
3387  *
3388  */
3390 {
3391  TRACE("%p pmenu=%x\n", pmt, PtMenu);
3392 
3393  if (PtMenu)
3394  {
3395  UINT Id = 0;
3396  ITEM *item;
3397 
3398  if ( IS_SYSTEM_MENU(PtMenu) )
3399  {
3400  item = PtMenu->rgItems;
3401  }
3402  else
3403  {
3404  item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &Id );
3405  }
3406 
3407  if (item && ( PtMenu->iItem == Id))
3408  {
3409  if (!(item->spSubMenu))
3410  {
3411  INT ExecutedMenuId = MENU_ExecFocusedItem( pmt, PtMenu, Flags);
3412  if (ExecutedMenuId == -1 || ExecutedMenuId == -2) return -1;
3413  return ExecutedMenuId;
3414  }
3415 
3416  /* If we are dealing with the menu bar */
3417  /* and this is a click on an already "popped" item: */
3418  /* Stop the menu tracking and close the opened submenus */
3419  if (pmt->TopMenu == PtMenu && PtMenu->TimeToHide)
3420  {
3421  return 0;
3422  }
3423  }
3424  if ( IntGetMenu(PtMenu->hWnd) == PtMenu )
3425  {
3426  PtMenu->TimeToHide = TRUE;
3427  }
3428  }
3429  return -1;
3430 }
3431 
3432 /***********************************************************************
3433  * MenuPtMenu
3434  *
3435  * Walks menu chain trying to find a menu pt maps to.
3436  */
3438 {
3439  PITEM pItem;
3440  PMENU ret = NULL;
3441 
3442  if (!menu) return NULL;
3443 
3444  /* try subpopup first (if any) */
3445  if (menu->iItem != NO_SELECTED_ITEM)
3446  {
3447  pItem = menu->rgItems;
3448  if ( pItem ) pItem = &pItem[menu->iItem];
3449  if ( pItem && pItem->spSubMenu && pItem->fState & MF_MOUSESELECT)
3450  {
3451  ret = MENU_PtMenu( pItem->spSubMenu, pt);
3452  }
3453  }
3454 
3455  /* check the current window (avoiding WM_HITTEST) */
3456  if (!ret)
3457  {
3458  PWND pWnd = ValidateHwndNoErr(menu->hWnd);
3459  INT ht = GetNCHitEx(pWnd, pt);
3460  if ( menu->fFlags & MNF_POPUP )
3461  {
3462  if (ht != HTNOWHERE && ht != HTERROR) ret = menu;
3463  }
3464  else if (ht == HTSYSMENU)
3465  ret = get_win_sys_menu(menu->hWnd);
3466  else if (ht == HTMENU)
3467  ret = IntGetMenu( menu->hWnd );
3468  }
3469  return ret;
3470 }
3471 
3472 /***********************************************************************
3473  * MenuMouseMove
3474  *
3475  * Return TRUE if we can go on with menu tracking.
3476  */
3478 {
3480 
3481  if ( PtMenu )
3482  {
3483  if (IS_SYSTEM_MENU(PtMenu))
3484  {
3485  Index = 0;
3487  // Windows tracks mouse moves to the system menu but does not open it.
3488  // Only keyboard tracking can do that.
3489  //
3490  TRACE("SystemMenu\n");
3491  return TRUE; // Stay inside the Loop!
3492  }
3493  else
3494  MENU_FindItemByCoords( PtMenu, pmt->Pt, &Index );
3495  }
3496 
3497  if (Index == NO_SELECTED_ITEM)
3498  {
3500  }
3501  else if (PtMenu->iItem != Index)
3502  {
3503  MENU_SwitchTracking(pmt, PtMenu, Index, Flags);
3504  pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags);
3505  }
3506  return TRUE;
3507 }
3508 
3509 /***********************************************************************
3510  * MenuGetSubPopup
3511  *
3512  * Return the handle of the selected sub-popup menu (if any).
3513  */
3515 {
3516  ITEM *item;
3517 
3518  if ((!menu) || (menu->iItem == NO_SELECTED_ITEM)) return 0;
3519 
3520  item = &menu->rgItems[menu->iItem];
3521  if (item && (item->spSubMenu) && (item->fState & MF_MOUSESELECT))
3522  {
3523  return item->spSubMenu;
3524  }
3525  return 0;
3526 }
3527 
3528 /***********************************************************************
3529  * MenuDoNextMenu
3530  *
3531  * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
3532  */
3534 {
3535  BOOL atEnd = FALSE;
3536 
3537  /* When skipping left, we need to do something special after the
3538  first menu. */
3539  if (Vk == VK_LEFT && pmt->TopMenu->iItem == 0)
3540  {
3541  atEnd = TRUE;
3542  }
3543  /* When skipping right, for the non-system menu, we need to
3544  handle the last non-special menu item (ie skip any window
3545  icons such as MDI maximize, restore or close) */
3546  else if ((Vk == VK_RIGHT) && !IS_SYSTEM_MENU(pmt->TopMenu))
3547  {
3548  UINT i = pmt->TopMenu->iItem + 1;
3549  while (i < pmt->TopMenu->cItems) {
3550  if ((pmt->TopMenu->rgItems[i].wID >= SC_SIZE &&
3551  pmt->TopMenu->rgItems[i].wID <= SC_RESTORE)) {
3552  i++;
3553  } else break;
3554  }
3555  if (i == pmt->TopMenu->cItems) {
3556  atEnd = TRUE;
3557  }
3558  }
3559  /* When skipping right, we need to cater for the system menu */
3560  else if ((Vk == VK_RIGHT) && IS_SYSTEM_MENU(pmt->TopMenu))
3561  {
3562  if (pmt->TopMenu->iItem == (pmt->TopMenu->cItems - 1)) {
3563  atEnd = TRUE;
3564  }
3565  }
3566 
3567  if ( atEnd )
3568  {
3569  MDINEXTMENU NextMenu;
3570  PMENU MenuTmp;
3571  PWND pwndTemp;
3572  HMENU hNewMenu;
3573  HWND hNewWnd;
3574  UINT Id = 0;
3575 
3576  MenuTmp = (IS_SYSTEM_MENU(pmt->TopMenu)) ? co_IntGetSubMenu(pmt->TopMenu, 0) : pmt->TopMenu;
3577  NextMenu.hmenuIn = UserHMGetHandle(MenuTmp);
3578  NextMenu.hmenuNext = NULL;
3579  NextMenu.hwndNext = NULL;
3580  co_IntSendMessage(UserHMGetHandle(pmt->OwnerWnd), WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
3581 
3582  TRACE("%p [%p] -> %p [%p]\n",
3583  pmt->CurrentMenu, pmt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
3584 
3585  if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
3586  {
3587  hNewWnd = UserHMGetHandle(pmt->OwnerWnd);
3588  if (IS_SYSTEM_MENU(pmt->TopMenu))
3589  {
3590  /* switch to the menu bar */
3591 
3592  if (pmt->OwnerWnd->style & WS_CHILD || !(MenuTmp = IntGetMenu(hNewWnd))) return FALSE;
3593 
3594  if (Vk == VK_LEFT)
3595  {
3596  Id = MenuTmp->cItems - 1;
3597 
3598  /* Skip backwards over any system predefined icons,
3599  eg. MDI close, restore etc icons */
3600  while ((Id > 0) &&
3601  (MenuTmp->rgItems[Id].wID >= SC_SIZE &&
3602  MenuTmp->rgItems[Id].wID <= SC_RESTORE)) Id--;
3603 
3604  }
3605  hNewMenu = UserHMGetHandle(MenuTmp);
3606  }
3607  else if (pmt->OwnerWnd->style & WS_SYSMENU)
3608  {
3609  /* switch to the system menu */
3610  MenuTmp = get_win_sys_menu(hNewWnd);
3611  if (MenuTmp) hNewMenu = UserHMGetHandle(MenuTmp);
3612  }
3613  else
3614  return FALSE;
3615  }
3616  else /* application returned a new menu to switch to */
3617  {
3618  hNewMenu = NextMenu.hmenuNext;
3619  hNewWnd = NextMenu.hwndNext;
3620 
3621  if ((MenuTmp = UserGetMenuObject(hNewMenu)) && (pwndTemp = ValidateHwndNoErr(hNewWnd)))
3622  {
3623  if ( pwndTemp->style & WS_SYSMENU && (get_win_sys_menu(hNewWnd) == MenuTmp) )
3624  {
3625  /* get the real system menu */
3626  MenuTmp = get_win_sys_menu(hNewWnd);
3627  hNewMenu = UserHMGetHandle(MenuTmp);
3628  }
3629  else if (pwndTemp->style & WS_CHILD || IntGetMenu(hNewWnd) != MenuTmp)
3630  {
3631  /* FIXME: Not sure what to do here;
3632  * perhaps try to track NewMenu as a popup? */
3633 
3634  WARN(" -- got confused.\n");
3635  return FALSE;
3636  }
3637  }
3638  else return FALSE;
3639  }
3640 
3641  if (hNewMenu != UserHMGetHandle(pmt->TopMenu))
3642  {
3644 
3645  if (pmt->CurrentMenu != pmt->TopMenu)
3646  MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, wFlags);
3647  }
3648 
3649  if (hNewWnd != UserHMGetHandle(pmt->OwnerWnd))
3650  {
3652  pmt->OwnerWnd = ValidateHwndNoErr(hNewWnd);
3654  MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, hNewWnd);
3655  pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
3657  pti->MessageQueue->QF_flags |= QF_CAPTURELOCKED;
3658  }
3659 
3660  pmt->TopMenu = pmt->CurrentMenu = UserGetMenuObject(hNewMenu); /* all subpopups are hidden */
3661  MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, Id, TRUE, 0);
3662 
3663  return TRUE;
3664  }
3665  return FALSE;
3666 }
3667 
3668 /***********************************************************************
3669  * MenuSuspendPopup
3670  *
3671  * The idea is not to show the popup if the next input message is
3672  * going to hide it anyway.
3673  */
3675 {
3676  MSG msg;
3677 
3678  msg.hwnd = UserHMGetHandle(pmt->OwnerWnd);
3679 
3680  co_IntGetPeekMessage( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE, FALSE);
3681  pmt->TrackFlags |= TF_SKIPREMOVE;
3682 
3683  switch( uMsg )
3684  {
3685  case WM_KEYDOWN:
3686  co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE, FALSE);
3687  if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
3688  {
3689  co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE, FALSE);
3690  co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE, FALSE);
3691  if( msg.message == WM_KEYDOWN &&
3692  (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
3693  {
3694  pmt->TrackFlags |= TF_SUSPENDPOPUP;
3695  return TRUE;
3696  }
3697  }
3698  break;
3699  }
3700  /* failures go through this */
3701  pmt->TrackFlags &= ~TF_SUSPENDPOPUP;
3702  return FALSE;
3703 }
3704 
3705 /***********************************************************************
3706  * MenuKeyEscape
3707  *
3708  * Handle a VK_ESCAPE key event in a menu.
3709  */
3711 {
3712  BOOL EndMenu = TRUE;
3713 
3714  if (pmt->CurrentMenu != pmt->TopMenu)
3715  {
3716  if (pmt->CurrentMenu && (pmt->CurrentMenu->fFlags & MNF_POPUP))
3717  {
3718  PMENU MenuPrev, MenuTmp;
3719 
3720  MenuPrev = MenuTmp = pmt->TopMenu;
3721 
3722  /* close topmost popup */
3723  while (MenuTmp != pmt->CurrentMenu)
3724  {
3725  MenuPrev = MenuTmp;
3726  MenuTmp = MENU_GetSubPopup(MenuPrev);
3727  }
3728 
3729  MENU_HideSubPopups(pmt->OwnerWnd, MenuPrev, TRUE, Flags);
3730  pmt->CurrentMenu = MenuPrev;
3731  EndMenu = FALSE;
3732  }
3733  }
3734 
3735  return EndMenu;
3736 }
3737 
3738 /***********************************************************************
3739  * MenuKeyLeft
3740  *
3741  * Handle a VK_LEFT key event in a menu.
3742  */
3744 {
3745  PMENU MenuTmp, MenuPrev;
3746  UINT PrevCol;
3747 
3748  MenuPrev = MenuTmp = pmt->TopMenu;
3749 
3750  /* Try to move 1 column left (if possible) */
3751  if ( (PrevCol = MENU_GetStartOfPrevColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM)
3752  {
3753  MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, PrevCol, TRUE, 0);
3754  return;
3755  }
3756 
3757  /* close topmost popup */
3758  while (MenuTmp != pmt->CurrentMenu)
3759  {
3760  MenuPrev = MenuTmp;
3761  MenuTmp = MENU_GetSubPopup(MenuPrev);
3762  }
3763 
3764  MENU_HideSubPopups(pmt->OwnerWnd, MenuPrev, TRUE, Flags);
3765  pmt->CurrentMenu = MenuPrev;
3766 
3767  if ((MenuPrev == pmt->TopMenu) && !(pmt->TopMenu->fFlags & MNF_POPUP))
3768  {
3769  /* move menu bar selection if no more popups are left */
3770 
3771  if (!MENU_DoNextMenu(pmt, VK_LEFT, Flags))
3773 
3774  if (MenuPrev != MenuTmp || pmt->TrackFlags & TF_SUSPENDPOPUP)
3775  {
3776  /* A sublevel menu was displayed - display the next one
3777  * unless there is another displacement coming up */
3778 
3779  if (!MENU_SuspendPopup(pmt, msg))
3780  pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, pmt->TopMenu,
3781  TRUE, Flags);
3782  }
3783  }
3784 }
3785 
3786 /***********************************************************************
3787  * MenuKeyRight
3788  *
3789  * Handle a VK_RIGHT key event in a menu.
3790  */
3792 {
3793  PMENU menutmp;
3794  UINT NextCol;
3795 
3796  TRACE("MenuKeyRight called, cur %p, top %p.\n",
3797  pmt->CurrentMenu, pmt->TopMenu);
3798 
3799  if ((pmt->TopMenu->fFlags & MNF_POPUP) || (pmt->CurrentMenu != pmt->TopMenu))
3800  {
3801  /* If already displaying a popup, try to display sub-popup */
3802 
3803  menutmp = pmt->CurrentMenu;
3804  pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, menutmp, TRUE, Flags);
3805 
3806  /* if subpopup was displayed then we are done */
3807  if (menutmp != pmt->CurrentMenu) return;
3808  }
3809 
3810  /* Check to see if there's another column */
3811  if ( (NextCol = MENU_GetStartOfNextColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM)
3812  {
3813  TRACE("Going to %d.\n", NextCol);
3814  MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, NextCol, TRUE, 0);
3815  return;
3816  }
3817 
3818  if (!(pmt->TopMenu->fFlags & MNF_POPUP)) /* menu bar tracking */
3819  {
3820  if (pmt->CurrentMenu != pmt->TopMenu)
3821  {
3822  MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, Flags);
3823  menutmp = pmt->CurrentMenu = pmt->TopMenu;
3824  }
3825  else
3826  {
3827  menutmp = NULL;
3828  }
3829 
3830  /* try to move to the next item */
3831  if ( !MENU_DoNextMenu(pmt, VK_RIGHT, Flags))
3833 
3834  if ( menutmp || pmt->TrackFlags & TF_SUSPENDPOPUP )
3835  {
3836  if ( !MENU_SuspendPopup(pmt, msg) )
3837  pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, pmt->TopMenu, TRUE, Flags);
3838  }
3839  }
3840 }
3841 
3842 /***********************************************************************
3843  * MenuTrackMenu
3844  *
3845  * Menu tracking code.
3846  */
3848  PWND pwnd, const RECT *lprect )
3849 {
3850  MSG msg;
3851  BOOL fRemove;
3852  INT executedMenuId = -1;
3853  MTRACKER mt;
3854  HWND capture_win;
3855  PMENU pmMouse;
3856  BOOL enterIdleSent = FALSE;
3858 
3859  if (pti != pwnd->head.pti)
3860  {
3861  ERR("Not the same PTI!!!!\n");
3862  }
3863 
3864  mt.TrackFlags = 0;
3865  mt.CurrentMenu = pmenu;
3866  mt.TopMenu = pmenu;
3867  mt.OwnerWnd = pwnd;
3868  mt.Pt.x = x;
3869  mt.Pt.y = y;
3870 
3871  TRACE("MTM : hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n",
3872  UserHMGetHandle(pmenu), wFlags, x, y, UserHMGetHandle(pwnd), lprect ? lprect->left : 0, lprect ? lprect->top : 0,
3873  lprect ? lprect->right : 0, lprect ? lprect->bottom : 0);
3874 
3875  pti->MessageQueue->QF_flags &= ~QF_ACTIVATIONCHANGE;
3876 
3877  if (wFlags & TPM_BUTTONDOWN)
3878  {
3879  /* Get the result in order to start the tracking or not */
3880  fRemove = MENU_ButtonDown( &mt, pmenu, wFlags );
3881  fInsideMenuLoop = fRemove;
3882  }
3883 
3884  if (wFlags & TF_ENDMENU) fInsideMenuLoop = FALSE;
3885 
3886  if (wFlags & TPM_POPUPMENU && pmenu->cItems == 0) // Tracking empty popup menu...
3887  {
3889  pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
3890  co_UserSetCapture(NULL); /* release the capture */
3891  return 0;
3892  }
3893 
3894  capture_win = IntGetCapture();
3895 
3896  while (fInsideMenuLoop)
3897  {
3898  BOOL ErrorExit = FALSE;
3899  if (!VerifyMenu( mt.CurrentMenu )) /* sometimes happens if I do a window manager close */
3900  break;
3901 
3902  /* we have to keep the message in the queue until it's
3903  * clear that menu loop is not over yet. */
3904 
3905  for (;;)
3906  {
3907  if (co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOREMOVE, FALSE ))
3908  {
3909  if (!IntCallMsgFilter( &msg, MSGF_MENU )) break;
3910  /* remove the message from the queue */
3911  co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
3912  }
3913  else
3914  {
3915  /* ReactOS Checks */
3916  if (!VerifyWnd(mt.OwnerWnd) ||
3918  pti->MessageQueue->QF_flags & QF_ACTIVATIONCHANGE ||
3919  capture_win != IntGetCapture() ) // Should not happen, but this is ReactOS...
3920  {
3921  ErrorExit = TRUE; // Do not wait on dead windows, now win test_capture_4 works.
3922  break;
3923  }
3924 
3925  if (!enterIdleSent)
3926  {
3928  enterIdleSent = TRUE;
3930  }
3931  co_IntWaitMessage(NULL, 0, 0);
3932  }
3933  }
3934 
3935  if (ErrorExit) break; // Gracefully dropout.
3936 
3937  /* check if EndMenu() tried to cancel us, by posting this message */
3938  if (msg.message == WM_CANCELMODE)
3939  {
3940  /* we are now out of the loop */
3942 
3943  /* remove the message from the queue */
3944  co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
3945 
3946  /* break out of internal loop, ala ESCAPE */
3947  break;
3948  }
3949 
3950  mt.Pt = msg.pt;
3951 
3952  if ( (msg.hwnd == mt.CurrentMenu->hWnd) || ((msg.message!=WM_TIMER) && (msg.message!=WM_SYSTIMER)) )
3953  enterIdleSent=FALSE;
3954 
3955  fRemove = FALSE;
3956  if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3957  {
3958  /*
3959  * Use the mouse coordinates in lParam instead of those in the MSG
3960  * struct to properly handle synthetic messages. They are already
3961  * in screen coordinates.
3962  */
3963  mt.Pt.x = (short)LOWORD(msg.lParam);
3964  mt.Pt.y = (short)HIWORD(msg.lParam);
3965 
3966  /* Find a menu for this mouse event */
3967  pmMouse = MENU_PtMenu( mt.TopMenu, mt.Pt );
3968 
3969  switch(msg.message)
3970  {
3971  /* no WM_NC... messages in captured state */
3972 
3973  case WM_RBUTTONDBLCLK:
3974  case WM_RBUTTONDOWN:
3975  if (!(wFlags & TPM_RIGHTBUTTON))
3976  {
3977  if ( msg.message == WM_RBUTTONDBLCLK ) fInsideMenuLoop = FALSE; // Must exit or loop forever!
3978  break;
3979  }
3980  /* fall through */
3981  case WM_LBUTTONDBLCLK:
3982  case WM_LBUTTONDOWN:
3983  /* If the message belongs to the menu, removes it from the queue */
3984  /* Else, end menu tracking */
3985  fRemove = MENU_ButtonDown(&mt, pmMouse, wFlags);
3986  fInsideMenuLoop = fRemove;
3987  if ( msg.message == WM_LBUTTONDBLCLK ||
3988  msg.message == WM_RBUTTONDBLCLK ) fInsideMenuLoop = FALSE; // Must exit or loop forever!
3989  break;
3990 
3991  case WM_RBUTTONUP:
3992  if (!(wFlags & TPM_RIGHTBUTTON)) break;
3993  /* fall through */
3994  case WM_LBUTTONUP:
3995  /* Check if a menu was selected by the mouse */
3996  if (pmMouse)
3997  {
3998  executedMenuId = MENU_ButtonUp( &mt, pmMouse, wFlags);
3999 
4000  /* End the loop if executedMenuId is an item ID */
4001  /* or if the job was done (executedMenuId = 0). */
4002  fRemove = (executedMenuId != -1);
4003  fInsideMenuLoop = !fRemove;
4004  }
4005  /* No menu was selected by the mouse */
4006  /* if the function was called by TrackPopupMenu, continue
4007  with the menu tracking. If not, stop it */
4008  else
4009  fInsideMenuLoop = ((wFlags & TPM_POPUPMENU) ? TRUE : FALSE);
4010 
4011  break;
4012 
4013  case WM_MOUSEMOVE:
4014  /* the selected menu item must be changed every time */
4015  /* the mouse moves. */
4016 
4017  if (pmMouse)
4018  fInsideMenuLoop |= MENU_MouseMove( &mt, pmMouse, wFlags );
4019 
4020  } /* switch(msg.message) - mouse */
4021  }
4022  else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
4023  {
4024  fRemove = TRUE; /* Keyboard messages are always removed */
4025  switch(msg.message)
4026  {
4027  case WM_KEYDOWN:
4028  case WM_SYSKEYDOWN:
4029  switch(msg.wParam)
4030  {
4031  case VK_MENU:
4032  case VK_F10:
4034  break;
4035 
4036  case VK_HOME:
4037  case VK_END:
4040  break;
4041 
4042  case VK_UP:
4043  case VK_DOWN: /* If on menu bar, pull-down the menu */
4044  if (!(mt.CurrentMenu->fFlags & MNF_POPUP))
4045  mt.CurrentMenu = MENU_ShowSubPopup(mt.OwnerWnd, mt.TopMenu, TRUE, wFlags);
4046  else /* otherwise try to move selection */
4047  MENU_MoveSelection(mt.OwnerWnd, mt.CurrentMenu, (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
4048  break;
4049 
4050  case VK_LEFT:
4051  MENU_KeyLeft( &mt, wFlags, msg.message );
4052  break;
4053 
4054  case VK_RIGHT:
4055  MENU_KeyRight( &mt, wFlags, msg.message );
4056  break;
4057 
4058  case VK_ESCAPE:
4059  fInsideMenuLoop = !MENU_KeyEscape(&mt, wFlags);
4060  break;
4061 
4062  case VK_F1:
4063  {
4064  HELPINFO hi;
4065  hi.cbSize = sizeof(HELPINFO);
4067  if (mt.CurrentMenu->iItem == NO_SELECTED_ITEM)
4068  hi.iCtrlId = 0;
4069  else
4070  hi.iCtrlId = pmenu->rgItems[mt.CurrentMenu->iItem].wID;
4072  hi.dwContextId = pmenu->dwContextHelpId;
4073  hi.MousePos = msg.pt;
4074  co_IntSendMessage( UserHMGetHandle(pwnd), WM_HELP, 0, (LPARAM)&hi);
4075  break;
4076  }
4077 
4078  default:
4079  IntTranslateKbdMessage(&msg, 0);
4080  break;
4081  }
4082  break; /* WM_KEYDOWN */
4083 
4084  case WM_CHAR:
4085  case WM_SYSCHAR:
4086  {
4087  UINT pos;
4088  BOOL fEndMenu;
4089 
4090  if (msg.wParam == L'\r' || msg.wParam == L' ')
4091  {
4092  executedMenuId = MENU_ExecFocusedItem(&mt, mt.CurrentMenu, wFlags);
4093  fEndMenu = (executedMenuId != -2);
4094  fInsideMenuLoop = !fEndMenu;
4095  break;
4096  }
4097 
4098  /* Hack to avoid control chars. */
4099  /* We will find a better way real soon... */
4100  if (msg.wParam < 32) break;
4101 
4102  pos = MENU_FindItemByKey(mt.OwnerWnd, mt.CurrentMenu, LOWORD(msg.wParam), FALSE);
4103 
4104  if (pos == (UINT)-2) fInsideMenuLoop = FALSE;
4105  else if (pos == (UINT)-1) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, 0); //MessageBeep(0);
4106  else
4107  {
4108  MENU_SelectItem(mt.OwnerWnd, mt.CurrentMenu, pos, TRUE, 0);
4109  executedMenuId = MENU_ExecFocusedItem(&mt, mt.CurrentMenu, wFlags);
4110  fEndMenu = (executedMenuId != -2);
4111  fInsideMenuLoop = !fEndMenu;
4112  }
4113  }
4114  break;
4115  } /* switch(msg.message) - kbd */
4116  }
4117  else
4118  {
4119  co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4120  IntDispatchMessage( &msg );
4121  continue;
4122  }
4123 
4124  if (fInsideMenuLoop) fRemove = TRUE;
4125 
4126  /* finally remove message from the queue */
4127 
4128  if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) )
4129  co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE );
4130  else mt.TrackFlags &= ~TF_SKIPREMOVE;
4131  }
4132 
4134  pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
4135  co_UserSetCapture(NULL); /* release the capture */
4136 
4137  /* If dropdown is still painted and the close box is clicked on
4138  then the menu will be destroyed as part of the DispatchMessage above.
4139  This will then invalidate the menu handle in mt.hTopMenu. We should
4140  check for this first. */
4141  if ( VerifyMenu( mt.TopMenu ) )
4142  {
4143  if (VerifyWnd(mt.OwnerWnd))
4144  {
4145  MENU_HideSubPopups(mt.OwnerWnd, mt.TopMenu, FALSE, wFlags);
4146 
4147  if (mt.TopMenu->fFlags & MNF_POPUP)
4148  {
4149  PWND pwndTM = ValidateHwndNoErr(mt.TopMenu->hWnd);
4150  if (pwndTM)
4151  {
4152  IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, pwndTM, OBJID_CLIENT, CHILDID_SELF, 0);
4153 
4154  co_UserDestroyWindow(pwndTM);
4155  }
4156  mt.TopMenu->hWnd = NULL;
4157 
4158  if (!(wFlags & TPM_NONOTIFY))
4159  {
4161  MAKELPARAM(0, IS_SYSTEM_MENU(mt.TopMenu)) );
4162  }
4163  }
4166  }
4167 
4168  /* Reset the variable for hiding menu */
4169  mt.TopMenu->TimeToHide = FALSE;
4170  }
4171 
4173  /* The return value is only used by TrackPopupMenu */
4174  if (!(wFlags & TPM_RETURNCMD)) return TRUE;
4175  if (executedMenuId == -1) executedMenuId = 0;
4176  return executedMenuId;
4177 }
4178 
4179 /***********************************************************************
4180  * MenuInitTracking
4181  */
4183 {
4184  HWND capture_win;
4186 
4187  TRACE("hwnd=%p hmenu=%p\n", UserHMGetHandle(pWnd), UserHMGetHandle(Menu));
4188 
4189  co_UserHideCaret(0);
4190 
4191  /* This makes the menus of applications built with Delphi work.
4192  * It also enables menus to be displayed in more than one window,
4193  * but there are some bugs left that need to be fixed in this case.
4194  */
4195  if (!bPopup)
4196  {
4197  Menu->hWnd = UserHMGetHandle(pWnd);
4198  }
4199 
4200  if (!top_popup) {
4201  top_popup = Menu->hWnd;
4203  }
4204 
4206  fInEndMenu = FALSE;
4207 
4208  /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
4209  if (!(wFlags & TPM_NONOTIFY))
4210  {
4211  co_IntSendMessage( UserHMGetHandle(pWnd), WM_ENTERMENULOOP, bPopup, 0 );
4212  }
4213 
4214  //
4215  // Capture is set before calling WM_INITMENU and after WM_ENTERMENULOOP, see msg_menu.
4216  //
4217  capture_win = (wFlags & TPM_POPUPMENU) ? Menu->hWnd : UserHMGetHandle(pWnd);
4218  MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, capture_win); // 1
4219  co_UserSetCapture(capture_win); // 2
4220  pti->MessageQueue->QF_flags |= QF_CAPTURELOCKED; // Set the Q bits so noone can change this!
4221 
4223 
4224  if (!(wFlags & TPM_NONOTIFY))
4225  {
4227  /* If an app changed/recreated menu bar entries in WM_INITMENU
4228  * menu sizes will be recalculated once the menu created/shown.
4229  */
4230  }
4231 
4232  IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART,
4233  pWnd,
4235  CHILDID_SELF, 0);
4236  return TRUE;
4237 }
4238 
4239 /***********************************************************************
4240  * MenuExitTracking
4241  */
4243 {
4244  TRACE("Exit Track hwnd=%p bPopup %d\n", UserHMGetHandle(pWnd), bPopup);
4245 
4246  IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, pWnd, OBJID_WINDOW, CHILDID_SELF, 0);
4247 
4248  if (!(wFlags & TPM_NONOTIFY))
4249  co_IntSendMessage( UserHMGetHandle(pWnd), WM_EXITMENULOOP, bPopup, 0 );
4250 
4251  co_UserShowCaret(0);
4252 
4253  top_popup = 0;
4255 
4256  return TRUE;
4257 }
4258 
4259 /***********************************************************************
4260  * MenuTrackMouseMenuBar
4261  *
4262  * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
4263  */
4265 {
4266  PMENU pMenu = (ht == HTSYSMENU) ? IntGetSystemMenu(pWnd, FALSE) : IntGetMenu( UserHMGetHandle(pWnd) ); // See 74276 and CORE-12801
4268 
4269  TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", pWnd, ht, pt.x, pt.y);
4270 
4271  if (pWnd->ExStyle & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
4272  if (VerifyMenu(pMenu))
4273  {
4274  /* map point to parent client coordinates */
4276  if (Parent != UserGetDesktopWindow())
4277  {
4278  IntScreenToClient(Parent, &pt);
4279  }
4280 
4281  MENU_InitTracking(pWnd, pMenu, FALSE, wFlags);
4282  /* fetch the window menu again, it may have changed */
4283  pMenu = (ht == HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pWnd) ) : IntGetMenu( UserHMGetHandle(pWnd) );
4284  MENU_TrackMenu(pMenu, wFlags, pt.x, pt.y, pWnd, NULL);
4285  MENU_ExitTracking(pWnd, FALSE, wFlags);
4286  }
4287 }
4288 
4289 /***********************************************************************
4290  * MenuTrackKbdMenuBar
4291  *
4292  * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
4293  */
4295 {
4296  UINT uItem = NO_SELECTED_ITEM;
4297  PMENU TrackMenu;
4299 
4300  TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", UserHMGetHandle(pwnd), wParam, wChar);
4301 
4302  /* find window that has a menu */
4303 
4304  while (!( (pwnd->style & (WS_CHILD | WS_POPUP)) != WS_CHILD ) )
4305  if (!(pwnd = UserGetAncestor( pwnd, GA_PARENT ))) return;
4306 
4307  /* check if we have to track a system menu */
4308 
4309  TrackMenu = IntGetMenu( UserHMGetHandle(pwnd) );
4310  if (!TrackMenu || (pwnd->style & WS_MINIMIZE) != 0 || wChar == ' ' )
4311  {
4312  if (!(pwnd->style & WS_SYSMENU)) return;
4313  TrackMenu = get_win_sys_menu( UserHMGetHandle(pwnd) );
4314  uItem = 0;
4315  wParam |= HTSYSMENU; /* prevent item lookup */
4316  }
4317 
4318  if (!VerifyMenu( TrackMenu )) return;
4319 
4320  MENU_InitTracking( pwnd, TrackMenu, FALSE, wFlags );
4321 
4322  /* fetch the window menu again, it may have changed */
4323  TrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pwnd) ) : IntGetMenu( UserHMGetHandle(pwnd) );
4324 
4325  if( wChar && wChar != ' ' )
4326  {
4327  uItem = MENU_FindItemByKey( pwnd, TrackMenu, wChar, (wParam & HTSYSMENU) );
4328  if ( uItem >= (UINT)(-2) )
4329  {
4330  if( uItem == (UINT)(-1) ) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, 0); //MessageBeep(0);
4331  /* schedule end of menu tracking */
4332  wFlags |= TF_ENDMENU;
4333  goto track_menu;
4334  }
4335  }
4336 
4337  MENU_SelectItem( pwnd, TrackMenu, uItem, TRUE, 0 );
4338 
4339  if (!(wParam & HTSYSMENU) || wChar == ' ')
4340  {
4341  if( uItem == NO_SELECTED_ITEM )
4342  MENU_MoveSelection( pwnd, TrackMenu, ITEM_NEXT );
4343  else
4345  }
4346 
4347 track_menu:
4348  MENU_TrackMenu( TrackMenu, wFlags, 0, 0, pwnd, NULL );
4349  MENU_ExitTracking( pwnd, FALSE, wFlags);
4350 }
4351 
4352 /**********************************************************************
4353  * TrackPopupMenuEx (USER32.@)
4354  */
4356  PWND pWnd, LPTPMPARAMS lpTpm)
4357 {
4358  BOOL ret = FALSE;
4360 
4361  if (pti != pWnd->head.pti)
4362  {
4363  ERR("Must be the same pti!\n");
4364  return ret;
4365  }
4366 
4367  TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p \n", //rect %s\n",
4368  UserHMGetHandle(menu), wFlags, x, y, UserHMGetHandle(pWnd), lpTpm); //,
4369  //lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
4370 
4371  if (menu->hWnd && IntIsWindow(menu->hWnd))
4372  {
4374  return FALSE;
4375  }
4376 
4377  if (MENU_InitPopup( pWnd, menu, wFlags ))
4378  {
4379  MENU_InitTracking(pWnd, menu, TRUE, wFlags);
4380 
4381  /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
4382  if (!(wFlags & TPM_NONOTIFY))
4383  {
4385  }
4386 
4387  if (menu->fFlags & MNF_SYSMENU)
4388  MENU_InitSysMenuPopup( menu, pWnd->style, pWnd->pcls->style, HTSYSMENU);
4389 
4390  if (MENU_ShowPopup(pWnd, menu, 0, wFlags, x, y, 0, 0 ))
4391  ret = MENU_TrackMenu( menu, wFlags | TPM_POPUPMENU, 0, 0, pWnd,
4392  lpTpm ? &lpTpm->rcExclude : NULL);
4393  else
4394  {
4396  pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED;
4397  co_UserSetCapture(NULL); /* release the capture */
4398  }
4399 
4400  MENU_ExitTracking(pWnd, TRUE, wFlags);
4401 
4402  if (menu->hWnd)
4403  {
4404  PWND pwndM = ValidateHwndNoErr( menu->hWnd );
4405  if (pwndM) // wine hack around this with their destroy function.
4406  {
4407  if (!(pWnd->state & WNDS_DESTROYED))
4408  co_UserDestroyWindow( pwndM ); // Fix wrong error return.
4409  }
4410  menu->hWnd = 0;
4411 
4412  if (!(wFlags & TPM_NONOTIFY))
4413  {
4414  co_IntSendMessage( UserHMGetHandle(pWnd), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(menu),
4415  MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
4416  }
4417  }
4418  }
4419  return ret;
4420 }
4421 
4422 //
4423 // Menu Class Proc.
4424 //
4425 BOOL WINAPI
4427  PWND Wnd,
4428  UINT Message,
4429  WPARAM wParam,
4430  LPARAM lParam,
4431  LRESULT *lResult)
4432 {
4433  PPOPUPMENU pPopupMenu;
4434 
4435  *lResult = 0;
4436 
4437  TRACE("PMWP : pwnd=%x msg=%d wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
4438 
4439  if (Wnd)
4440  {
4441  if (!Wnd->fnid)
4442  {
4443  if (Message != WM_NCCREATE)
4444  {
4445  *lResult = IntDefWindowProc(Wnd, Message, wParam, lParam, FALSE);
4446  return TRUE;
4447  }
4448  Wnd->fnid = FNID_MENU;
4449  pPopupMenu = DesktopHeapAlloc( Wnd->head.rpdesk, sizeof(POPUPMENU) );
4450  if (pPopupMenu == NULL)
4451  {
4452  return TRUE;
4453  }
4454  pPopupMenu->posSelectedItem = NO_SELECTED_ITEM;
4455  pPopupMenu->spwndPopupMenu = Wnd;
4456  ((PMENUWND)Wnd)->ppopupmenu = pPopupMenu;
4457  TRACE("Pop Up Menu is Setup! Msg %d\n",Message);
4458  *lResult = 1;
4459  return TRUE;
4460  }
4461  else
4462  {
4463  if (Wnd->fnid != FNID_MENU)
4464  {
4465  ERR("Wrong window class for Menu! fnid %x\n",Wnd->fnid);
4466  return TRUE;
4467  }
4468  pPopupMenu = ((PMENUWND)Wnd)->ppopupmenu;
4469  }
4470  }
4471 
4472  switch(Message)
4473  {
4474  case WM_CREATE:
4475  {
4476  CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
4477  pPopupMenu->spmenu = UserGetMenuObject(cs->lpCreateParams);
4478  break;
4479  }
4480 
4481  case WM_MOUSEACTIVATE: /* We don't want to be activated */
4482  *lResult = MA_NOACTIVATE;
4483  break;
4484 
4485  case WM_PAINT:
4486  {
4487  PAINTSTRUCT ps;
4488  IntBeginPaint(Wnd, &ps);
4489  MENU_DrawPopupMenu(Wnd, ps.hdc, pPopupMenu->spmenu);
4490  IntEndPaint(Wnd, &ps);
4491  break;
4492  }
4493 
4494  case WM_PRINTCLIENT:
4495  {
4496  MENU_DrawPopupMenu( Wnd, (HDC)wParam, pPopupMenu->spmenu);
4497  break;
4498  }
4499 
4500  case WM_ERASEBKGND:
4501  *lResult = 1;
4502  break;
4503 
4504  case WM_DESTROY:
4505  /* zero out global pointer in case resident popup window was destroyed. */
4506  if (pPopupMenu)
4507  {
4508  if (UserHMGetHandle(Wnd) == top_popup)
4509  {
4510  top_popup = NULL;
4512  }
4513  }
4514  else
4515  {
4516  ERR("No Window Pop Up!\n");
4517  }
4518  break;
4519 
4520  case WM_NCDESTROY:
4521  {
4522  DesktopHeapFree(Wnd->head.rpdesk, pPopupMenu );
4523  ((PMENUWND)Wnd)->ppopupmenu = 0;
4524  Wnd->fnid = FNID_DESTROY;
4525  break;
4526  }
4527 
4528  case MM_SETMENUHANDLE: // wine'isms
4529  case MN_SETHMENU:
4530  {
4531  PMENU pmenu = UserGetMenuObject((HMENU)wParam);
4532  if (!pmenu)
4533  {
4534  ERR("Bad Menu Handle\n");
4535  break;
4536  }
4537  pPopupMenu->spmenu = pmenu;
4538  break;
4539  }
4540 
4541  case MM_GETMENUHANDLE: // wine'isms
4542  case MN_GETHMENU:
4543  *lResult = (LRESULT)(pPopupMenu ? (pPopupMenu->spmenu ? UserHMGetHandle(pPopupMenu->spmenu) : NULL) : NULL);
4544  break;
4545 
4546  default:
4547  if (Message > MN_GETHMENU && Message < MN_GETHMENU+19)
4548  {
4549  ERR("Someone is passing unknown menu messages %d\n",Message);
4550  }
4551  TRACE("PMWP to IDWP %d\n",Message);
4552  *lResult = IntDefWindowProc(Wnd, Message, wParam, lParam, FALSE);
4553  break;
4554  }
4555 
4556  return TRUE;
4557 }
4558 
4559 BOOL FASTCALL
4561  PMENU MenuObject,
4562  UINT uItemHilite,
4563  UINT uHilite)
4564 {
4565  PITEM MenuItem;
4566  UINT uItem = uItemHilite;
4567 
4568  if (!(MenuItem = MENU_FindItem( &MenuObject, &uItem, uHilite ))) return TRUE;
4569 
4570  if (uHilite & MF_HILITE)
4571  {
4572  MenuItem->fState |= MF_HILITE;
4573  }
4574  else
4575  {
4576  MenuItem->fState &= ~MF_HILITE;
4577  }
4578  if (MenuObject->iItem == uItemHilite) return TRUE;
4579  MENU_HideSubPopups( WindowObject, MenuObject, FALSE, 0 );
4580  MENU_SelectItem( WindowObject, MenuObject, uItemHilite, TRUE, 0 );
4581 
4582  return TRUE; // Always returns true!!!!
4583 }
4584 
4587 {
4588 
4589  DWORD dwStyle = 0;
4590  DWORD dwExStyle = 0;
4591  BOOLEAN retValue = TRUE;
4592 
4593  if (bti->cbSize == sizeof(TITLEBARINFO))
4594  {
4595  RtlZeroMemory(&bti->rgstate[0],sizeof(DWORD)*(CCHILDREN_TITLEBAR+1));
4596 
4597  bti->rgstate[0] = STATE_SYSTEM_FOCUSABLE;
4598 
4599  dwStyle = pWindowObject->style;
4600  dwExStyle = pWindowObject->ExStyle;
4601 
4602  bti->rcTitleBar.top = 0;
4603  bti->rcTitleBar.left = 0;
4604  bti->rcTitleBar.right = pWindowObject->rcWindow.right - pWindowObject->rcWindow.left;
4605  bti->rcTitleBar.bottom = pWindowObject->rcWindow.bottom - pWindowObject->rcWindow.top;
4606 
4607  /* Is it iconiced ? */
4608  if ((dwStyle & WS_ICONIC)!=WS_ICONIC)
4609  {
4610  /* Remove frame from rectangle */
4611  if (HAS_THICKFRAME( dwStyle, dwExStyle ))
4612  {
4613  /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXFRAME) and UserGetSystemMetrics(SM_CYFRAME) */
4615  }
4616  else if (HAS_DLGFRAME( dwStyle, dwExStyle ))
4617  {
4618  /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXDLGFRAME) and UserGetSystemMetrics(SM_CYDLGFRAME) */
4620  }
4621  else if (HAS_THINFRAME( dwStyle, dwExStyle))
4622  {
4623  /* FIXME: Note this value should exists in pWindowObject for UserGetSystemMetrics(SM_CXBORDER) and UserGetSystemMetrics(SM_CYBORDER) */
4625  }
4626 
4627  /* We have additional border information if the window
4628  * is a child (but not an MDI child) */
4629