97e1fa2b80561c2169b6e58ee7c18042771962b5
[reactos.git] / win32ss / user / ntuser / menu.c
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
14 HFONT ghMenuFont = NULL;
15 HFONT ghMenuFontBold = NULL;
16 static SIZE MenuCharSize;
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;
21 static HMENU top_popup_hmenu = NULL;
22
23 BOOL fInsideMenuLoop = FALSE;
24 BOOL fInEndMenu = FALSE;
25
26 /* internal popup menu window messages */
27
28 #define MM_SETMENUHANDLE (WM_USER + 0)
29 #define MM_GETMENUHANDLE (WM_USER + 1)
30
31 /* internal flags for menu tracking */
32
33 #define TF_ENDMENU 0x10000
34 #define TF_SUSPENDPOPUP 0x20000
35 #define TF_SKIPREMOVE 0x40000
36
37
38 /* maximum allowed depth of any branch in the menu tree.
39 * This value is slightly larger than in windows (25) to
40 * stay on the safe side. */
41 #define MAXMENUDEPTH 30
42
43 #define MNS_STYLE_MASK (MNS_NOCHECK|MNS_MODELESS|MNS_DRAGDROP|MNS_AUTODISMISS|MNS_NOTIFYBYPOS|MNS_CHECKORBMP)
44
45 #define MENUITEMINFO_TYPE_MASK \
46 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
47 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
48 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
49
50 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
51
52 #define STATE_MASK (~TYPE_MASK)
53
54 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
55
56 #define MII_STATE_MASK (MFS_GRAYED|MFS_CHECKED|MFS_HILITE|MFS_DEFAULT)
57
58 #define IS_SYSTEM_MENU(MenuInfo) \
59 (!((MenuInfo)->fFlags & MNF_POPUP) && ((MenuInfo)->fFlags & MNF_SYSMENU))
60
61 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
62
63 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
64 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
65
66 /* Maximum number of menu items a menu can contain */
67 #define MAX_MENU_ITEMS (0x4000)
68 #define MAX_GOINTOSUBMENU (0x10)
69
70 /* Space between 2 columns */
71 #define MENU_COL_SPACE 4
72
73 /* top and bottom margins for popup menus */
74 #define MENU_TOP_MARGIN 3
75 #define MENU_BOTTOM_MARGIN 2
76
77 #define MENU_ITEM_HBMP_SPACE (5)
78 #define MENU_BAR_ITEMS_SPACE (12)
79 #define SEPARATOR_HEIGHT (5)
80 #define MENU_TAB_SPACE (8)
81
82 typedef struct
83 {
84 UINT TrackFlags;
85 PMENU CurrentMenu; /* current submenu (can be equal to hTopMenu)*/
86 PMENU TopMenu; /* initial menu */
87 PWND OwnerWnd; /* where notifications are sent */
88 POINT Pt;
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
195 PMENU FASTCALL
196 IntGetMenuObject(HMENU hMenu)
197 {
198 PMENU Menu = UserGetMenuObject(hMenu);
199 if (Menu)
200 Menu->head.cLockObj++;
201
202 return Menu;
203 }
204
205 PMENU FASTCALL VerifyMenu(PMENU pMenu)
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 }
225 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
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
244 IntIsMenu(HMENU Menu)
245 {
246 if (UserGetMenuObject(Menu)) return TRUE;
247 return FALSE;
248 }
249
250
251 PMENU WINAPI
252 IntGetMenu(HWND hWnd)
253 {
254 PWND Wnd = ValidateHwndNoErr(hWnd);
255
256 if (!Wnd)
257 return NULL;
258
259 return UserGetMenuObject(UlongToHandle(Wnd->IDMenu));
260 }
261
262 PMENU get_win_sys_menu( HWND hwnd )
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 ASSERT(UserIsEnteredExclusive());
278 if (pMenu->rgItems) /* recursively destroy submenus */
279 {
280 int i;
281 ITEM *item = pMenu->rgItems;
282 for (i = pMenu->cItems; i > 0; i--, item++)
283 {
284 SubMenu = item->spSubMenu;
285 item->spSubMenu = NULL;
286
287 /* Remove Item Text */
288 FreeMenuText(pMenu,item);
289
290 /* Remove Item Bitmap and set it for this process */
291 if (item->hbmp && !(item->fState & MFS_HBMMENUBMP))
292 {
293 GreSetObjectOwner(item->hbmp, GDI_OBJ_HMGR_POWNED);
294 item->hbmp = NULL;
295 }
296
297 /* Remove Item submenu */
298 if (bRecurse && SubMenu)//VerifyMenu(SubMenu))
299 {
300 /* Release submenu since it was referenced when inserted */
301 IntReleaseMenuObject(SubMenu);
302 IntDestroyMenuObject(SubMenu, bRecurse);
303 }
304 }
305 /* Free the Item */
306 DesktopHeapFree(pMenu->head.rpdesk, pMenu->rgItems );
307 pMenu->rgItems = NULL;
308 pMenu->cItems = 0;
309 }
310 return TRUE;
311 }
312
313 /* Callback for the object manager */
314 BOOLEAN
315 UserDestroyMenuObject(PVOID Object)
316 {
317 return IntDestroyMenuObject(Object, TRUE);
318 }
319
320 BOOL FASTCALL
321 IntDestroyMenuObject(PMENU Menu, BOOL bRecurse)
322 {
323 ASSERT(UserIsEnteredExclusive());
324 if (Menu)
325 {
326 PWND Window;
327
328 if (PsGetCurrentProcessSessionId() == Menu->head.rpdesk->rpwinstaParent->dwSessionId)
329 {
330 BOOL ret;
331 if (Menu->hWnd)
332 {
333 Window = ValidateHwndNoErr(Menu->hWnd);
334 if (Window)
335 {
336 //Window->IDMenu = 0; Only in Win9x!! wine win test_SetMenu test...
337
338 /* DestroyMenu should not destroy system menu popup owner */
339 if ((Menu->fFlags & (MNF_POPUP | MNF_SYSSUBMENU)) == MNF_POPUP)
340 {
341 // Should we check it to see if it has Class?
342 ERR("FIXME Pop up menu window thing'ie\n");
343 //co_UserDestroyWindow( Window );
344 //Menu->hWnd = 0;
345 }
346 }
347 }
348
349 if (!UserMarkObjectDestroy(Menu)) return TRUE;
350
351 /* Remove all menu items */
352 IntDestroyMenu( Menu, bRecurse);
353
354 ret = UserDeleteObject(Menu->head.h, TYPE_MENU);
355 TRACE("IntDestroyMenuObject %d\n",ret);
356 return ret;
357 }
358 }
359 return FALSE;
360 }
361
362 BOOL
363 MenuInit(VOID)
364 {
365 NONCLIENTMETRICSW ncm;
366
367 /* get the menu font */
368 if (!ghMenuFont || !ghMenuFontBold)
369 {
370 ncm.cbSize = sizeof(ncm);
371 if(!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
372 {
373 ERR("MenuInit(): SystemParametersInfo(SPI_GETNONCLIENTMETRICS) failed!\n");
374 return FALSE;
375 }
376
377 ghMenuFont = GreCreateFontIndirectW(&ncm.lfMenuFont);
378 if (ghMenuFont == NULL)
379 {
380 ERR("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
381 return FALSE;
382 }
383 ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
384 ghMenuFontBold = GreCreateFontIndirectW(&ncm.lfMenuFont);
385 if (ghMenuFontBold == NULL)
386 {
387 ERR("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
388 GreDeleteObject(ghMenuFont);
389 ghMenuFont = NULL;
390 return FALSE;
391 }
392
393 GreSetObjectOwner(ghMenuFont, GDI_OBJ_HMGR_PUBLIC);
394 GreSetObjectOwner(ghMenuFontBold, GDI_OBJ_HMGR_PUBLIC);
395
396 co_IntSetupOBM();
397 }
398
399 return TRUE;
400 }
401
402
403 /**********************************************************************
404 * MENU_depth
405 *
406 * detect if there are loops in the menu tree (or the depth is too large)
407 */
408 int FASTCALL MENU_depth( PMENU pmenu, int depth)
409 {
410 UINT i;
411 ITEM *item;
412 int subdepth;
413
414 if (!pmenu) return depth;
415
416 depth++;
417 if( depth > MAXMENUDEPTH) return depth;
418 item = pmenu->rgItems;
419 subdepth = depth;
420 for( i = 0; i < pmenu->cItems && subdepth <= MAXMENUDEPTH; i++, item++)
421 {
422 if( item->spSubMenu)//VerifyMenu(item->spSubMenu))
423 {
424 int bdepth = MENU_depth( item->spSubMenu, depth);
425 if( bdepth > subdepth) subdepth = bdepth;
426 }
427 if( subdepth > MAXMENUDEPTH)
428 TRACE("<- hmenu %p\n", item->spSubMenu);
429 }
430 return subdepth;
431 }
432
433
434 /******************************************************************************
435 *
436 * UINT MenuGetStartOfNextColumn(
437 * PMENU Menu)
438 *
439 *****************************************************************************/
440
441 static UINT MENU_GetStartOfNextColumn(
442 PMENU menu )
443 {
444 PITEM pItem;
445 UINT i;
446
447 if(!menu)
448 return NO_SELECTED_ITEM;
449
450 i = menu->iItem + 1;
451 if( i == NO_SELECTED_ITEM )
452 return i;
453
454 pItem = menu->rgItems;
455 if (!pItem) return NO_SELECTED_ITEM;
456
457 for( ; i < menu->cItems; ++i ) {
458 if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
459 return i;
460 }
461
462 return NO_SELECTED_ITEM;
463 }
464
465 /******************************************************************************
466 *
467 * UINT MenuGetStartOfPrevColumn(
468 * PMENU Menu)
469 *
470 *****************************************************************************/
471 static UINT MENU_GetStartOfPrevColumn(
472 PMENU menu )
473 {
474 UINT i;
475 PITEM pItem;
476
477 if( !menu )
478 return NO_SELECTED_ITEM;
479
480 if( menu->iItem == 0 || menu->iItem == NO_SELECTED_ITEM )
481 return NO_SELECTED_ITEM;
482
483 pItem = menu->rgItems;
484 if (!pItem) return NO_SELECTED_ITEM;
485
486 /* Find the start of the column */
487
488 for(i = menu->iItem; i != 0 &&
489 !(pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
490 --i); /* empty */
491
492 if(i == 0)
493 return NO_SELECTED_ITEM;
494
495 for(--i; i != 0; --i) {
496 if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
497 break;
498 }
499
500 TRACE("ret %d.\n", i );
501
502 return i;
503 }
504
505 /***********************************************************************
506 * MENU_FindItem
507 *
508 * Find a menu item. Return a pointer on the item, and modifies *hmenu
509 * in case the item was in a sub-menu.
510 */
511 PITEM FASTCALL MENU_FindItem( PMENU *pmenu, UINT *nPos, UINT wFlags )
512 {
513 MENU *menu = *pmenu;
514 ITEM *fallback = NULL;
515 UINT fallback_pos = 0;
516 UINT i;
517
518 if (!menu) return NULL;
519
520 if (wFlags & MF_BYPOSITION)
521 {
522 if (!menu->cItems) return NULL;
523 if (*nPos >= menu->cItems) return NULL;
524 return &menu->rgItems[*nPos];
525 }
526 else
527 {
528 PITEM item = menu->rgItems;
529 for (i = 0; i < menu->cItems; i++, item++)
530 {
531 if (item->spSubMenu)
532 {
533 PMENU psubmenu = item->spSubMenu;//VerifyMenu(item->spSubMenu);
534 PITEM subitem = MENU_FindItem( &psubmenu, nPos, wFlags );
535 if (subitem)
536 {
537 *pmenu = psubmenu;
538 return subitem;
539 }
540 else if (item->wID == *nPos)
541 {
542 /* fallback to this item if nothing else found */
543 fallback_pos = i;
544 fallback = item;
545 }
546 }
547 else if (item->wID == *nPos)
548 {
549 *nPos = i;
550 return item;
551 }
552 }
553 }
554
555 if (fallback)
556 *nPos = fallback_pos;
557
558 return fallback;
559 }
560
561 /***********************************************************************
562 * MenuFindSubMenu
563 *
564 * Find a Sub menu. Return the position of the submenu, and modifies
565 * *hmenu in case it is found in another sub-menu.
566 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
567 */
568 static UINT FASTCALL MENU_FindSubMenu(PMENU *menu, PMENU SubTarget )
569 {
570 UINT i;
571 PITEM item;
572
573 item = ((PMENU)*menu)->rgItems;
574 for (i = 0; i < ((PMENU)*menu)->cItems; i++, item++)
575 {
576 if (!item->spSubMenu)
577 continue;
578 else
579 {
580 if (item->spSubMenu == SubTarget)
581 {
582 return i;
583 }
584 else
585 {
586 PMENU pSubMenu = item->spSubMenu;
587 UINT pos = MENU_FindSubMenu( &pSubMenu, SubTarget );
588 if (pos != NO_SELECTED_ITEM)
589 {
590 *menu = pSubMenu;
591 return pos;
592 }
593 }
594 }
595 }
596 return NO_SELECTED_ITEM;
597 }
598
599 BOOL FASTCALL
600 IntRemoveMenuItem( PMENU pMenu, UINT nPos, UINT wFlags, BOOL bRecurse )
601 {
602 PITEM item;
603 PITEM newItems;
604
605 TRACE("(menu=%p pos=%04x flags=%04x)\n",pMenu, nPos, wFlags);
606 if (!(item = MENU_FindItem( &pMenu, &nPos, wFlags ))) return FALSE;
607
608 /* Remove item */
609
610 FreeMenuText(pMenu,item);
611 if (bRecurse && item->spSubMenu)
612 {
613 IntDestroyMenuObject(item->spSubMenu, bRecurse);
614 }
615 ////// Use cAlloced with inc's of 8's....
616 if (--pMenu->cItems == 0)
617 {
618 DesktopHeapFree(pMenu->head.rpdesk, pMenu->rgItems );
619 pMenu->rgItems = NULL;
620 }
621 else
622 {
623 while (nPos < pMenu->cItems)
624 {
625 *item = *(item+1);
626 item++;
627 nPos++;
628 }
629 newItems = DesktopHeapReAlloc(pMenu->head.rpdesk, pMenu->rgItems, pMenu->cItems * sizeof(ITEM));
630 if (newItems)
631 {
632 pMenu->rgItems = newItems;
633 }
634 }
635 return TRUE;
636 }
637
638 /**********************************************************************
639 * MENU_InsertItem
640 *
641 * Insert (allocate) a new item into a menu.
642 */
643 ITEM *MENU_InsertItem( PMENU menu, UINT pos, UINT flags, PMENU *submenu, UINT *npos )
644 {
645 ITEM *newItems;
646
647 /* Find where to insert new item */
648
649 if (flags & MF_BYPOSITION) {
650 if (pos > menu->cItems)
651 pos = menu->cItems;
652 } else {
653 if (!MENU_FindItem( &menu, &pos, flags ))
654 {
655 if (submenu) *submenu = menu;
656 if (npos) *npos = pos;
657 pos = menu->cItems;
658 }
659 }
660
661 /* Make sure that MDI system buttons stay on the right side.
662 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
663 * regardless of their id.
664 */
665 while ( pos > 0 &&
666 (INT_PTR)menu->rgItems[pos - 1].hbmp >= (INT_PTR)HBMMENU_SYSTEM &&
667 (INT_PTR)menu->rgItems[pos - 1].hbmp <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
668 pos--;
669
670 TRACE("inserting at %u flags %x\n", pos, flags);
671
672 /* Create new items array */
673
674 newItems = DesktopHeapAlloc(menu->head.rpdesk, sizeof(ITEM) * (menu->cItems+1) );
675 if (!newItems)
676 {
677 WARN("allocation failed\n" );
678 return NULL;
679 }
680 if (menu->cItems > 0)
681 {
682 /* Copy the old array into the new one */
683 if (pos > 0) RtlCopyMemory( newItems, menu->rgItems, pos * sizeof(ITEM) );
684 if (pos < menu->cItems) RtlCopyMemory( &newItems[pos+1], &menu->rgItems[pos], (menu->cItems-pos)*sizeof(ITEM) );
685 DesktopHeapFree(menu->head.rpdesk, menu->rgItems );
686 }
687 menu->rgItems = newItems;
688 menu->cItems++;
689 RtlZeroMemory( &newItems[pos], sizeof(*newItems) );
690 menu->cyMenu = 0; /* force size recalculate */
691 return &newItems[pos];
692 }
693
694 BOOL FASTCALL
695 IntInsertMenuItem(
696 _In_ PMENU MenuObject,
697 UINT uItem,
698 BOOL fByPosition,
699 PROSMENUITEMINFO ItemInfo,
700 PUNICODE_STRING lpstr)
701 {
702 PITEM MenuItem;
703 PMENU SubMenu = NULL;
704
705 NT_ASSERT(MenuObject != NULL);
706
707 if (MAX_MENU_ITEMS <= MenuObject->cItems)
708 {
709 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
710 return FALSE;
711 }
712
713 SubMenu = MenuObject;
714
715 if(!(MenuItem = MENU_InsertItem( SubMenu, uItem, fByPosition ? MF_BYPOSITION : MF_BYCOMMAND, &SubMenu, &uItem ))) return FALSE;
716
717 if(!IntSetMenuItemInfo(SubMenu, MenuItem, ItemInfo, lpstr))
718 {
719 IntRemoveMenuItem(SubMenu, uItem, fByPosition ? MF_BYPOSITION : MF_BYCOMMAND, FALSE);
720 return FALSE;
721 }
722
723 /* Force size recalculation! */
724 SubMenu->cyMenu = 0;
725 MenuItem->hbmpChecked = MenuItem->hbmpUnchecked = 0;
726
727 TRACE("IntInsertMenuItemToList = %u %i\n", uItem, (BOOL)((INT)uItem >= 0));
728
729 return TRUE;
730 }
731
732 PMENU FASTCALL
733 IntCreateMenu(
734 _Out_ PHANDLE Handle,
735 _In_ BOOL IsMenuBar,
736 _In_ PDESKTOP Desktop,
737 _In_ PPROCESSINFO ppi)
738 {
739 PMENU Menu;
740
741 Menu = (PMENU)UserCreateObject( gHandleTable,
742 Desktop,
743 ppi->ptiList,
744 Handle,
745 TYPE_MENU,
746 sizeof(MENU));
747 if(!Menu)
748 {
749 *Handle = 0;
750 return NULL;
751 }
752
753 Menu->cyMax = 0; /* Default */
754 Menu->hbrBack = NULL; /* No brush */
755 Menu->dwContextHelpId = 0; /* Default */
756 Menu->dwMenuData = 0; /* Default */
757 Menu->iItem = NO_SELECTED_ITEM; // Focused item
758 Menu->fFlags = (IsMenuBar ? 0 : MNF_POPUP);
759 Menu->spwndNotify = NULL;
760 Menu->cyMenu = 0; // Height
761 Menu->cxMenu = 0; // Width
762 Menu->cItems = 0; // Item count
763 Menu->iTop = 0;
764 Menu->iMaxTop = 0;
765 Menu->cxTextAlign = 0;
766 Menu->rgItems = NULL;
767
768 Menu->hWnd = NULL;
769 Menu->TimeToHide = FALSE;
770
771 return Menu;
772 }
773
774 BOOL FASTCALL
775 IntCloneMenuItems(PMENU Destination, PMENU Source)
776 {
777 PITEM MenuItem, NewMenuItem = NULL;
778 UINT i;
779
780 if(!Source->cItems)
781 return FALSE;
782
783 NewMenuItem = DesktopHeapAlloc(Destination->head.rpdesk, Source->cItems * sizeof(ITEM));
784 if(!NewMenuItem) return FALSE;
785
786 RtlZeroMemory(NewMenuItem, Source->cItems * sizeof(ITEM));
787
788 Destination->rgItems = NewMenuItem;
789
790 MenuItem = Source->rgItems;
791 for (i = 0; i < Source->cItems; i++, MenuItem++, NewMenuItem++)
792 {
793 NewMenuItem->fType = MenuItem->fType;
794 NewMenuItem->fState = MenuItem->fState;
795 NewMenuItem->wID = MenuItem->wID;
796 NewMenuItem->spSubMenu = MenuItem->spSubMenu;
797 NewMenuItem->hbmpChecked = MenuItem->hbmpChecked;
798 NewMenuItem->hbmpUnchecked = MenuItem->hbmpUnchecked;
799 NewMenuItem->dwItemData = MenuItem->dwItemData;
800 if (MenuItem->lpstr.Length)
801 {
802 NewMenuItem->lpstr.Length = 0;
803 NewMenuItem->lpstr.MaximumLength = MenuItem->lpstr.MaximumLength;
804 NewMenuItem->lpstr.Buffer = DesktopHeapAlloc(Destination->head.rpdesk, MenuItem->lpstr.MaximumLength);
805 if (!NewMenuItem->lpstr.Buffer)
806 {
807 DesktopHeapFree(Destination->head.rpdesk, NewMenuItem);
808 break;
809 }
810 RtlCopyUnicodeString(&NewMenuItem->lpstr, &MenuItem->lpstr);
811 NewMenuItem->lpstr.Buffer[MenuItem->lpstr.Length / sizeof(WCHAR)] = 0;
812 NewMenuItem->Xlpstr = NewMenuItem->lpstr.Buffer;
813 }
814 else
815 {
816 NewMenuItem->lpstr.Buffer = MenuItem->lpstr.Buffer;
817 NewMenuItem->Xlpstr = NewMenuItem->lpstr.Buffer;
818 }
819 NewMenuItem->hbmp = MenuItem->hbmp;
820 Destination->cItems = i + 1;
821 }
822 return TRUE;
823 }
824
825 PMENU FASTCALL
826 IntCloneMenu(PMENU Source)
827 {
828 HANDLE hMenu;
829 PMENU Menu;
830
831 if(!Source)
832 return NULL;
833
834 /* A menu is valid process wide. We can pass to the object manager any thread ptr */
835 Menu = (PMENU)UserCreateObject( gHandleTable,
836 Source->head.rpdesk,
837 ((PPROCESSINFO)Source->head.hTaskWow)->ptiList,
838 &hMenu,
839 TYPE_MENU,
840 sizeof(MENU));
841 if(!Menu)
842 return NULL;
843
844 Menu->fFlags = Source->fFlags;
845 Menu->cyMax = Source->cyMax;
846 Menu->hbrBack = Source->hbrBack;
847 Menu->dwContextHelpId = Source->dwContextHelpId;
848 Menu->dwMenuData = Source->dwMenuData;
849 Menu->iItem = NO_SELECTED_ITEM;
850 Menu->spwndNotify = NULL;
851 Menu->cyMenu = 0;
852 Menu->cxMenu = 0;
853 Menu->cItems = 0;
854 Menu->iTop = 0;
855 Menu->iMaxTop = 0;
856 Menu->cxTextAlign = 0;
857 Menu->rgItems = NULL;
858
859 Menu->hWnd = NULL;
860 Menu->TimeToHide = FALSE;
861
862 IntCloneMenuItems(Menu, Source);
863
864 return Menu;
865 }
866
867 BOOL FASTCALL
868 IntSetMenuFlagRtoL(PMENU Menu)
869 {
870 ERR("SetMenuFlagRtoL\n");
871 Menu->fFlags |= MNF_RTOL;
872 return TRUE;
873 }
874
875 BOOL FASTCALL
876 IntSetMenuContextHelpId(PMENU Menu, DWORD dwContextHelpId)
877 {
878 Menu->dwContextHelpId = dwContextHelpId;
879 return TRUE;
880 }
881
882 BOOL FASTCALL
883 IntGetMenuInfo(PMENU Menu, PROSMENUINFO lpmi)
884 {
885 if(lpmi->fMask & MIM_BACKGROUND)
886 lpmi->hbrBack = Menu->hbrBack;
887 if(lpmi->fMask & MIM_HELPID)
888 lpmi->dwContextHelpID = Menu->dwContextHelpId;
889 if(lpmi->fMask & MIM_MAXHEIGHT)
890 lpmi->cyMax = Menu->cyMax;
891 if(lpmi->fMask & MIM_MENUDATA)
892 lpmi->dwMenuData = Menu->dwMenuData;
893 if(lpmi->fMask & MIM_STYLE)
894 lpmi->dwStyle = Menu->fFlags & MNS_STYLE_MASK;
895
896 if (sizeof(MENUINFO) < lpmi->cbSize)
897 {
898 lpmi->cItems = Menu->cItems;
899
900 lpmi->iItem = Menu->iItem;
901 lpmi->cxMenu = Menu->cxMenu;
902 lpmi->cyMenu = Menu->cyMenu;
903 lpmi->spwndNotify = Menu->spwndNotify;
904 lpmi->cxTextAlign = Menu->cxTextAlign;
905 lpmi->iTop = Menu->iTop;
906 lpmi->iMaxTop = Menu->iMaxTop;
907 lpmi->dwArrowsOn = Menu->dwArrowsOn;
908
909 lpmi->fFlags = Menu->fFlags;
910 lpmi->Self = Menu->head.h;
911 lpmi->TimeToHide = Menu->TimeToHide;
912 lpmi->Wnd = Menu->hWnd;
913 }
914 return TRUE;
915 }
916
917 BOOL FASTCALL
918 IntSetMenuInfo(PMENU Menu, PROSMENUINFO lpmi)
919 {
920 if(lpmi->fMask & MIM_BACKGROUND)
921 Menu->hbrBack = lpmi->hbrBack;
922 if(lpmi->fMask & MIM_HELPID)
923 Menu->dwContextHelpId = lpmi->dwContextHelpID;
924 if(lpmi->fMask & MIM_MAXHEIGHT)
925 Menu->cyMax = lpmi->cyMax;
926 if(lpmi->fMask & MIM_MENUDATA)
927 Menu->dwMenuData = lpmi->dwMenuData;
928 if(lpmi->fMask & MIM_STYLE)
929 Menu->fFlags ^= (Menu->fFlags ^ lpmi->dwStyle) & MNS_STYLE_MASK;
930 if(lpmi->fMask & MIM_APPLYTOSUBMENUS)
931 {
932 int i;
933 PITEM item = Menu->rgItems;
934 for ( i = Menu->cItems; i; i--, item++)
935 {
936 if ( item->spSubMenu )
937 {
938 IntSetMenuInfo( item->spSubMenu, lpmi);
939 }
940 }
941 }
942 if (sizeof(MENUINFO) < lpmi->cbSize)
943 {
944 Menu->iItem = lpmi->iItem;
945 Menu->cyMenu = lpmi->cyMenu;
946 Menu->cxMenu = lpmi->cxMenu;
947 Menu->spwndNotify = lpmi->spwndNotify;
948 Menu->cxTextAlign = lpmi->cxTextAlign;
949 Menu->iTop = lpmi->iTop;
950 Menu->iMaxTop = lpmi->iMaxTop;
951 Menu->dwArrowsOn = lpmi->dwArrowsOn;
952
953 Menu->TimeToHide = lpmi->TimeToHide;
954 Menu->hWnd = lpmi->Wnd;
955 }
956 if ( lpmi->fMask & MIM_STYLE)
957 {
958 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented wine\n");
959 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented wine\n");
960 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented wine\n");
961 }
962 return TRUE;
963 }
964
965 BOOL FASTCALL
966 IntGetMenuItemInfo(PMENU Menu, /* UNUSED PARAM!! */
967 PITEM MenuItem, PROSMENUITEMINFO lpmii)
968 {
969 NTSTATUS Status;
970
971 if(lpmii->fMask & (MIIM_FTYPE | MIIM_TYPE))
972 {
973 lpmii->fType = MenuItem->fType;
974 }
975 if(lpmii->fMask & MIIM_BITMAP)
976 {
977 lpmii->hbmpItem = MenuItem->hbmp;
978 }
979 if(lpmii->fMask & MIIM_CHECKMARKS)
980 {
981 lpmii->hbmpChecked = MenuItem->hbmpChecked;
982 lpmii->hbmpUnchecked = MenuItem->hbmpUnchecked;
983 }
984 if(lpmii->fMask & MIIM_DATA)
985 {
986 lpmii->dwItemData = MenuItem->dwItemData;
987 }
988 if(lpmii->fMask & MIIM_ID)
989 {
990 lpmii->wID = MenuItem->wID;
991 }
992 if(lpmii->fMask & MIIM_STATE)
993 {
994 lpmii->fState = MenuItem->fState;
995 }
996 if(lpmii->fMask & MIIM_SUBMENU)
997 {
998 lpmii->hSubMenu = MenuItem->spSubMenu ? MenuItem->spSubMenu->head.h : NULL;
999 }
1000
1001 if ((lpmii->fMask & MIIM_STRING) ||
1002 ((lpmii->fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(lpmii->fType) == MF_STRING)))
1003 {
1004 if (lpmii->dwTypeData == NULL)
1005 {
1006 lpmii->cch = MenuItem->lpstr.Length / sizeof(WCHAR);
1007 }
1008 else
1009 { //// lpmii->lpstr can be read in user mode!!!!
1010 Status = MmCopyToCaller(lpmii->dwTypeData, MenuItem->lpstr.Buffer,
1011 min(lpmii->cch * sizeof(WCHAR),
1012 MenuItem->lpstr.MaximumLength));
1013 if (! NT_SUCCESS(Status))
1014 {
1015 SetLastNtError(Status);
1016 return FALSE;
1017 }
1018 }
1019 }
1020
1021 if (sizeof(ROSMENUITEMINFO) == lpmii->cbSize)
1022 {
1023 lpmii->Rect.left = MenuItem->xItem;
1024 lpmii->Rect.top = MenuItem->yItem;
1025 lpmii->Rect.right = MenuItem->cxItem; // Do this for now......
1026 lpmii->Rect.bottom = MenuItem->cyItem;
1027 lpmii->dxTab = MenuItem->dxTab;
1028 lpmii->lpstr = MenuItem->lpstr.Buffer;
1029 lpmii->maxBmpSize.cx = MenuItem->cxBmp;
1030 lpmii->maxBmpSize.cy = MenuItem->cyBmp;
1031 }
1032
1033 return TRUE;
1034 }
1035
1036 BOOL FASTCALL
1037 IntSetMenuItemInfo(PMENU MenuObject, PITEM MenuItem, PROSMENUITEMINFO lpmii, PUNICODE_STRING lpstr)
1038 {
1039 PMENU SubMenuObject;
1040 BOOL circref = FALSE;
1041
1042 if(!MenuItem || !MenuObject || !lpmii)
1043 {
1044 return FALSE;
1045 }
1046 if ( lpmii->fMask & MIIM_FTYPE )
1047 {
1048 MenuItem->fType &= ~MENUITEMINFO_TYPE_MASK;
1049 MenuItem->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
1050 }
1051 if (lpmii->fMask & MIIM_TYPE)
1052 {
1053 #if 0 //// Done in User32.
1054 if (lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP))
1055 {
1056 ERR("IntSetMenuItemInfo: Invalid combination of fMask bits used\n");
1057 KeRosDumpStackFrames(NULL, 20);
1058 /* This does not happen on Win9x/ME */
1059 SetLastNtError( ERROR_INVALID_PARAMETER);
1060 return FALSE;
1061 }
1062 #endif
1063 /*
1064 * Delete the menu item type when changing type from
1065 * MF_STRING.
1066 */
1067 if (MenuItem->fType != lpmii->fType &&
1068 MENU_ITEM_TYPE(MenuItem->fType) == MFT_STRING)
1069 {
1070 FreeMenuText(MenuObject,MenuItem);
1071 RtlInitUnicodeString(&MenuItem->lpstr, NULL);
1072 MenuItem->Xlpstr = NULL;
1073 }
1074 if(lpmii->fType & MFT_BITMAP)
1075 {
1076 if(lpmii->hbmpItem)
1077 MenuItem->hbmp = lpmii->hbmpItem;
1078 else
1079 { /* Win 9x/Me stuff */
1080 MenuItem->hbmp = (HBITMAP)((ULONG_PTR)(LOWORD(lpmii->dwTypeData)));
1081 }
1082 lpmii->dwTypeData = 0;
1083 }
1084 }
1085 if(lpmii->fMask & MIIM_BITMAP)
1086 {
1087 MenuItem->hbmp = lpmii->hbmpItem;
1088 if (MenuItem->hbmp <= HBMMENU_POPUP_MINIMIZE && MenuItem->hbmp >= HBMMENU_CALLBACK)
1089 MenuItem->fState |= MFS_HBMMENUBMP;
1090 else
1091 MenuItem->fState &= ~MFS_HBMMENUBMP;
1092 }
1093 if(lpmii->fMask & MIIM_CHECKMARKS)
1094 {
1095 MenuItem->hbmpChecked = lpmii->hbmpChecked;
1096 MenuItem->hbmpUnchecked = lpmii->hbmpUnchecked;
1097 }
1098 if(lpmii->fMask & MIIM_DATA)
1099 {
1100 MenuItem->dwItemData = lpmii->dwItemData;
1101 }
1102 if(lpmii->fMask & MIIM_ID)
1103 {
1104 MenuItem->wID = lpmii->wID;
1105 }
1106 if(lpmii->fMask & MIIM_STATE)
1107 {
1108 /* Remove MFS_DEFAULT flag from all other menu items if this item
1109 has the MFS_DEFAULT state */
1110 if(lpmii->fState & MFS_DEFAULT)
1111 UserSetMenuDefaultItem(MenuObject, -1, 0);
1112 /* Update the menu item state flags */
1113 UpdateMenuItemState(MenuItem->fState, lpmii->fState);
1114 }
1115
1116 if(lpmii->fMask & MIIM_SUBMENU)
1117 {
1118 if (lpmii->hSubMenu)
1119 {
1120 SubMenuObject = UserGetMenuObject(lpmii->hSubMenu);
1121 if ( SubMenuObject && !(UserObjectInDestroy(lpmii->hSubMenu)) )
1122 {
1123 //// wine Bug 12171 : Adding Popup Menu to itself! Could create endless loops.
1124 //// CORE-7967.
1125 if (MenuObject == SubMenuObject)
1126 {
1127 HANDLE hMenu;
1128 ERR("Pop Up Menu Double Trouble!\n");
1129 SubMenuObject = IntCreateMenu(&hMenu,
1130 FALSE,
1131 MenuObject->head.rpdesk,
1132 (PPROCESSINFO)MenuObject->head.hTaskWow); // It will be marked.
1133 if (!SubMenuObject) return FALSE;
1134 IntReleaseMenuObject(SubMenuObject); // This will be referenced again after insertion.
1135 circref = TRUE;
1136 }
1137 if ( MENU_depth( SubMenuObject, 0) > MAXMENUDEPTH )
1138 {
1139 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
1140 if (circref) IntDestroyMenuObject(SubMenuObject, FALSE);
1141 return FALSE;
1142 }
1143 /* Make sure the submenu is marked as a popup menu */
1144 SubMenuObject->fFlags |= MNF_POPUP;
1145 // Now fix the test_subpopup_locked_by_menu tests....
1146 if (MenuItem->spSubMenu) IntReleaseMenuObject(MenuItem->spSubMenu);
1147 MenuItem->spSubMenu = SubMenuObject;
1148 UserReferenceObject(SubMenuObject);
1149 }
1150 else
1151 {
1152 EngSetLastError( ERROR_INVALID_PARAMETER);
1153 return FALSE;
1154 }
1155 }
1156 else
1157 { // If submenu just dereference it.
1158 if (MenuItem->spSubMenu) IntReleaseMenuObject(MenuItem->spSubMenu);
1159 MenuItem->spSubMenu = NULL;
1160 }
1161 }
1162
1163 if ((lpmii->fMask & MIIM_STRING) ||
1164 ((lpmii->fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(lpmii->fType) == MF_STRING)))
1165 {
1166 /* free the string when used */
1167 FreeMenuText(MenuObject,MenuItem);
1168 RtlInitUnicodeString(&MenuItem->lpstr, NULL);
1169 MenuItem->Xlpstr = NULL;
1170
1171 if(lpmii->dwTypeData && lpmii->cch && lpstr && lpstr->Buffer)
1172 {
1173 UNICODE_STRING Source;
1174
1175 if (!NT_VERIFY(lpmii->cch <= UNICODE_STRING_MAX_CHARS))
1176 {
1177 return FALSE;
1178 }
1179
1180 Source.Length = Source.MaximumLength = (USHORT)(lpmii->cch * sizeof(WCHAR));
1181 Source.Buffer = lpmii->dwTypeData;
1182
1183 MenuItem->lpstr.Buffer = DesktopHeapAlloc( MenuObject->head.rpdesk, Source.Length + sizeof(WCHAR));
1184 if(MenuItem->lpstr.Buffer != NULL)
1185 {
1186 MenuItem->lpstr.Length = 0;
1187 MenuItem->lpstr.MaximumLength = Source.Length + sizeof(WCHAR);
1188 RtlCopyUnicodeString(&MenuItem->lpstr, &Source);
1189 MenuItem->lpstr.Buffer[MenuItem->lpstr.Length / sizeof(WCHAR)] = 0;
1190
1191 MenuItem->cch = MenuItem->lpstr.Length / sizeof(WCHAR);
1192 MenuItem->Xlpstr = (USHORT*)MenuItem->lpstr.Buffer;
1193 }
1194 }
1195 }
1196
1197 if( !(MenuObject->fFlags & MNF_SYSMENU) &&
1198 !MenuItem->Xlpstr &&
1199 !lpmii->dwTypeData &&
1200 !(MenuItem->fType & MFT_OWNERDRAW) &&
1201 !MenuItem->hbmp)
1202 MenuItem->fType |= MFT_SEPARATOR;
1203
1204 if (sizeof(ROSMENUITEMINFO) == lpmii->cbSize)
1205 {
1206 MenuItem->xItem = lpmii->Rect.left;
1207 MenuItem->yItem = lpmii->Rect.top;
1208 MenuItem->cxItem = lpmii->Rect.right; // Do this for now......
1209 MenuItem->cyItem = lpmii->Rect.bottom;
1210 MenuItem->dxTab = lpmii->dxTab;
1211 lpmii->lpstr = MenuItem->lpstr.Buffer; /* Send back new allocated string or zero */
1212 MenuItem->cxBmp = lpmii->maxBmpSize.cx;
1213 MenuItem->cyBmp = lpmii->maxBmpSize.cy;
1214 }
1215
1216 return TRUE;
1217 }
1218
1219
1220 UINT FASTCALL
1221 IntEnableMenuItem(PMENU MenuObject, UINT uIDEnableItem, UINT uEnable)
1222 {
1223 PITEM MenuItem;
1224 UINT res;
1225
1226 if (!(MenuItem = MENU_FindItem( &MenuObject, &uIDEnableItem, uEnable ))) return (UINT)-1;
1227
1228 res = MenuItem->fState & (MF_GRAYED | MF_DISABLED);
1229
1230 MenuItem->fState ^= (res ^ uEnable) & (MF_GRAYED | MF_DISABLED);
1231
1232 /* If the close item in the system menu change update the close button */
1233 if (res != uEnable)
1234 {
1235 switch (MenuItem->wID) // More than just close.
1236 {
1237 case SC_CLOSE:
1238 case SC_MAXIMIZE:
1239 case SC_MINIMIZE:
1240 case SC_MOVE:
1241 case SC_RESTORE:
1242 case SC_SIZE:
1243 if (MenuObject->fFlags & MNF_SYSSUBMENU && MenuObject->spwndNotify != 0)
1244 {
1245 //RECTL rc = MenuObject->spwndNotify->rcWindow;
1246
1247 /* Refresh the frame to reflect the change */
1248 //IntMapWindowPoints(0, MenuObject->spwndNotify, (POINT *)&rc, 2);
1249 //rc.bottom = 0;
1250 //co_UserRedrawWindow(MenuObject->spwndNotify, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
1251
1252 // Allow UxTheme!
1253 UserPaintCaption(MenuObject->spwndNotify, DC_BUTTONS);
1254 }
1255 default:
1256 break;
1257 }
1258 }
1259 return res;
1260 }
1261
1262 DWORD FASTCALL
1263 IntCheckMenuItem(PMENU MenuObject, UINT uIDCheckItem, UINT uCheck)
1264 {
1265 PITEM MenuItem;
1266 DWORD res;
1267
1268 if (!(MenuItem = MENU_FindItem( &MenuObject, &uIDCheckItem, uCheck ))) return -1;
1269
1270 res = (DWORD)(MenuItem->fState & MF_CHECKED);
1271
1272 MenuItem->fState ^= (res ^ uCheck) & MF_CHECKED;
1273
1274 return res;
1275 }
1276
1277 BOOL FASTCALL
1278 UserSetMenuDefaultItem(PMENU MenuObject, UINT uItem, UINT fByPos)
1279 {
1280 UINT i;
1281 PITEM MenuItem = MenuObject->rgItems;
1282
1283 if (!MenuItem) return FALSE;
1284
1285 /* reset all default-item flags */
1286 for (i = 0; i < MenuObject->cItems; i++, MenuItem++)
1287 {
1288 MenuItem->fState &= ~MFS_DEFAULT;
1289 }
1290
1291 /* no default item */
1292 if(uItem == (UINT)-1)
1293 {
1294 return TRUE;
1295 }
1296 MenuItem = MenuObject->rgItems;
1297 if ( fByPos )
1298 {
1299 if ( uItem >= MenuObject->cItems ) return FALSE;
1300 MenuItem[uItem].fState |= MFS_DEFAULT;
1301 return TRUE;
1302 }
1303 else
1304 {
1305 for (i = 0; i < MenuObject->cItems; i++, MenuItem++)
1306 {
1307 if (MenuItem->wID == uItem)
1308 {
1309 MenuItem->fState |= MFS_DEFAULT;
1310 return TRUE;
1311 }
1312 }
1313
1314 }
1315 return FALSE;
1316 }
1317
1318 UINT FASTCALL
1319 IntGetMenuDefaultItem(PMENU MenuObject, UINT fByPos, UINT gmdiFlags, DWORD *gismc)
1320 {
1321 UINT i = 0;
1322 PITEM MenuItem = MenuObject->rgItems;
1323
1324 /* empty menu */
1325 if (!MenuItem) return -1;
1326
1327 while ( !( MenuItem->fState & MFS_DEFAULT ) )
1328 {
1329 i++; MenuItem++;
1330 if (i >= MenuObject->cItems ) return -1;
1331 }
1332
1333 /* default: don't return disabled items */
1334 if ( (!(GMDI_USEDISABLED & gmdiFlags)) && (MenuItem->fState & MFS_DISABLED )) return -1;
1335
1336 /* search rekursiv when needed */
1337 if ( (gmdiFlags & GMDI_GOINTOPOPUPS) && MenuItem->spSubMenu )
1338 {
1339 UINT ret;
1340 (*gismc)++;
1341 ret = IntGetMenuDefaultItem( MenuItem->spSubMenu, fByPos, gmdiFlags, gismc );
1342 (*gismc)--;
1343 if ( -1 != ret ) return ret;
1344
1345 /* when item not found in submenu, return the popup item */
1346 }
1347 return ( fByPos ) ? i : MenuItem->wID;
1348 }
1349
1350 PMENU
1351 FASTCALL
1352 co_IntGetSubMenu(
1353 PMENU pMenu,
1354 int nPos)
1355 {
1356 PITEM pItem;
1357 if (!(pItem = MENU_FindItem( &pMenu, (UINT*)&nPos, MF_BYPOSITION ))) return NULL;
1358 return pItem->spSubMenu;
1359 }
1360
1361 /***********************************************************************
1362 * MenuInitSysMenuPopup
1363 *
1364 * Grey the appropriate items in System menu.
1365 */
1366 void FASTCALL MENU_InitSysMenuPopup(PMENU menu, DWORD style, DWORD clsStyle, LONG HitTest )
1367 {
1368 BOOL gray;
1369 UINT DefItem;
1370
1371 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
1372 IntEnableMenuItem( menu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
1373 gray = ((style & WS_MAXIMIZE) != 0);
1374 IntEnableMenuItem( menu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
1375 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
1376 IntEnableMenuItem( menu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
1377 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
1378 IntEnableMenuItem( menu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
1379 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
1380 IntEnableMenuItem( menu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
1381 gray = (clsStyle & CS_NOCLOSE) != 0;
1382
1383 /* The menu item must keep its state if it's disabled */
1384 if(gray)
1385 IntEnableMenuItem( menu, SC_CLOSE, MF_GRAYED);
1386
1387 /* Set default menu item */
1388 if(style & WS_MINIMIZE) DefItem = SC_RESTORE;
1389 else if(HitTest == HTCAPTION) DefItem = ((style & (WS_MAXIMIZE | WS_MINIMIZE)) ? SC_RESTORE : SC_MAXIMIZE);
1390 else DefItem = SC_CLOSE;
1391
1392 UserSetMenuDefaultItem(menu, DefItem, MF_BYCOMMAND);
1393 }
1394
1395
1396 /***********************************************************************
1397 * MenuDrawPopupGlyph
1398 *
1399 * Draws popup magic glyphs (can be found in system menu).
1400 */
1401 static void FASTCALL
1402 MENU_DrawPopupGlyph(HDC dc, LPRECT r, INT_PTR popupMagic, BOOL inactive, BOOL hilite)
1403 {
1404 LOGFONTW lf;
1405 HFONT hFont, hOldFont;
1406 COLORREF clrsave;
1407 INT bkmode;
1408 WCHAR symbol;
1409 switch (popupMagic)
1410 {
1411 case (INT_PTR) HBMMENU_POPUP_RESTORE:
1412 symbol = '2';
1413 break;
1414 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
1415 symbol = '0';
1416 break;
1417 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
1418 symbol = '1';
1419 break;
1420 case (INT_PTR) HBMMENU_POPUP_CLOSE:
1421 symbol = 'r';
1422 break;
1423 default:
1424 ERR("Invalid popup magic bitmap %d\n", (int)popupMagic);
1425 return;
1426 }
1427 RtlZeroMemory(&lf, sizeof(LOGFONTW));
1428 RECTL_vInflateRect(r, -2, -2);
1429 lf.lfHeight = r->bottom - r->top;
1430 lf.lfWidth = 0;
1431 lf.lfWeight = FW_NORMAL;
1432 lf.lfCharSet = DEFAULT_CHARSET;
1433 RtlCopyMemory(lf.lfFaceName, L"Marlett", sizeof(L"Marlett"));
1434 hFont = GreCreateFontIndirectW(&lf);
1435 /* save font and text color */
1436 hOldFont = NtGdiSelectFont(dc, hFont);
1437 clrsave = GreGetTextColor(dc);
1438 bkmode = GreGetBkMode(dc);
1439 /* set color and drawing mode */
1440 IntGdiSetBkMode(dc, TRANSPARENT);
1441 if (inactive)
1442 {
1443 /* draw shadow */
1444 if (!hilite)
1445 {
1446 IntGdiSetTextColor(dc, IntGetSysColor(COLOR_HIGHLIGHTTEXT));
1447 GreTextOutW(dc, r->left + 1, r->top + 1, &symbol, 1);
1448 }
1449 }
1450 IntGdiSetTextColor(dc, IntGetSysColor(inactive ? COLOR_GRAYTEXT : (hilite ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT)));
1451 /* draw selected symbol */
1452 GreTextOutW(dc, r->left, r->top, &symbol, 1);
1453 /* restore previous settings */
1454 IntGdiSetTextColor(dc, clrsave);
1455 NtGdiSelectFont(dc, hOldFont);
1456 IntGdiSetBkMode(dc, bkmode);
1457 GreDeleteObject(hFont);
1458 }
1459
1460 /***********************************************************************
1461 * MENU_AdjustMenuItemRect
1462 *
1463 * Adjust menu item rectangle according to scrolling state.
1464 */
1465 VOID FASTCALL
1466 MENU_AdjustMenuItemRect(PMENU menu, PRECTL rect)
1467 {
1468 if (menu->dwArrowsOn)
1469 {
1470 UINT arrow_bitmap_height;
1471 arrow_bitmap_height = gpsi->oembmi[OBI_UPARROW].cy; ///// Menu up arrow! OBM_UPARROW
1472 rect->top += arrow_bitmap_height - menu->iTop;
1473 rect->bottom += arrow_bitmap_height - menu->iTop;
1474 }
1475 }
1476
1477 /***********************************************************************
1478 * MENU_FindItemByCoords
1479 *
1480 * Find the item at the specified coordinates (screen coords). Does
1481 * not work for child windows and therefore should not be called for
1482 * an arbitrary system menu.
1483 */
1484 static ITEM *MENU_FindItemByCoords( MENU *menu, POINT pt, UINT *pos )
1485 {
1486 ITEM *item;
1487 UINT i;
1488 RECT rect;
1489 PWND pWnd = ValidateHwndNoErr(menu->hWnd);
1490
1491 if (!IntGetWindowRect(pWnd, &rect)) return NULL;
1492 if (pWnd->ExStyle & WS_EX_LAYOUTRTL)
1493 pt.x = rect.right - 1 - pt.x;
1494 else
1495 pt.x -= rect.left;
1496 pt.y -= rect.top;
1497 item = menu->rgItems;
1498 for (i = 0; i < menu->cItems; i++, item++)
1499 {
1500 //rect = item->rect;
1501 rect.left = item->xItem;
1502 rect.top = item->yItem;
1503 rect.right = item->cxItem; // Do this for now......
1504 rect.bottom = item->cyItem;
1505
1506 MENU_AdjustMenuItemRect(menu, &rect);
1507 if (RECTL_bPointInRect(&rect, pt.x, pt.y))
1508 {
1509 if (pos) *pos = i;
1510 return item;
1511 }
1512 }
1513 return NULL;
1514 }
1515
1516 INT FASTCALL IntMenuItemFromPoint(PWND pWnd, HMENU hMenu, POINT ptScreen)
1517 {
1518 MENU *menu = UserGetMenuObject(hMenu);
1519 UINT pos;
1520
1521 /*FIXME: Do we have to handle hWnd here? */
1522 if (!menu) return -1;
1523 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
1524 return pos;
1525 }
1526
1527 /***********************************************************************
1528 * MenuFindItemByKey
1529 *
1530 * Find the menu item selected by a key press.
1531 * Return item id, -1 if none, -2 if we should close the menu.
1532 */
1533 static UINT FASTCALL MENU_FindItemByKey(PWND WndOwner, PMENU menu,
1534 WCHAR Key, BOOL ForceMenuChar)
1535 {
1536 LRESULT MenuChar;
1537 WORD Flags = 0;
1538
1539 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)Key, Key, menu );
1540
1541 if (!menu || !VerifyMenu(menu))
1542 menu = co_IntGetSubMenu( UserGetMenuObject(WndOwner->SystemMenu), 0 );
1543 if (menu)
1544 {
1545 ITEM *item = menu->rgItems;
1546
1547 if ( !ForceMenuChar )
1548 {
1549 UINT i;
1550 BOOL cjk = UserGetSystemMetrics( SM_DBCSENABLED );
1551
1552 for (i = 0; i < menu->cItems; i++, item++)
1553 {
1554 LPWSTR text = item->Xlpstr;
1555 if( text)
1556 {
1557 const WCHAR *p = text - 2;
1558 do
1559 {
1560 const WCHAR *q = p + 2;
1561 p = wcschr (q, '&');
1562 if (!p && cjk) p = wcschr (q, '\036'); /* Japanese Win16 */
1563 }
1564 while (p != NULL && p [1] == '&');
1565 if (p && (towupper(p[1]) == towupper(Key))) return i;
1566 }
1567 }
1568 }
1569
1570 Flags |= menu->fFlags & MNF_POPUP ? MF_POPUP : 0;
1571 Flags |= menu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0;
1572
1573 MenuChar = co_IntSendMessage( UserHMGetHandle(WndOwner), WM_MENUCHAR,
1574 MAKEWPARAM(Key, Flags), (LPARAM) UserHMGetHandle(menu));
1575 if (HIWORD(MenuChar) == MNC_EXECUTE) return LOWORD(MenuChar);
1576 if (HIWORD(MenuChar) == MNC_CLOSE) return (UINT)(-2);
1577 }
1578 return (UINT)(-1);
1579 }
1580
1581 /***********************************************************************
1582 * MenuGetBitmapItemSize
1583 *
1584 * Get the size of a bitmap item.
1585 */
1586 static void FASTCALL MENU_GetBitmapItemSize(PITEM lpitem, SIZE *size, PWND WndOwner)
1587 {
1588 BITMAP bm;
1589 HBITMAP bmp = lpitem->hbmp;
1590
1591 size->cx = size->cy = 0;
1592
1593 /* check if there is a magic menu item associated with this item */
1594 if (IS_MAGIC_BITMAP(bmp))
1595 {
1596 switch((INT_PTR) bmp)
1597 {
1598 case (INT_PTR)HBMMENU_CALLBACK:
1599 {
1600 MEASUREITEMSTRUCT measItem;
1601 measItem.CtlType = ODT_MENU;
1602 measItem.CtlID = 0;
1603 measItem.itemID = lpitem->wID;
1604 measItem.itemWidth = lpitem->cxItem - lpitem->xItem; //lpitem->Rect.right - lpitem->Rect.left;
1605 measItem.itemHeight = lpitem->cyItem - lpitem->yItem; //lpitem->Rect.bottom - lpitem->Rect.top;
1606 measItem.itemData = lpitem->dwItemData;
1607 co_IntSendMessage( UserHMGetHandle(WndOwner), WM_MEASUREITEM, 0, (LPARAM)&measItem);
1608 size->cx = measItem.itemWidth;
1609 size->cy = measItem.itemHeight;
1610 TRACE("HBMMENU_CALLBACK Height %d Width %d\n",measItem.itemHeight,measItem.itemWidth);
1611 return;
1612 }
1613 break;
1614
1615 case (INT_PTR) HBMMENU_SYSTEM:
1616 if (lpitem->dwItemData)
1617 {
1618 bmp = (HBITMAP) lpitem->dwItemData;
1619 break;
1620 }
1621 /* fall through */
1622 case (INT_PTR) HBMMENU_MBAR_RESTORE:
1623 case (INT_PTR) HBMMENU_MBAR_MINIMIZE:
1624 case (INT_PTR) HBMMENU_MBAR_CLOSE:
1625 case (INT_PTR) HBMMENU_MBAR_MINIMIZE_D:
1626 case (INT_PTR) HBMMENU_MBAR_CLOSE_D:
1627 case (INT_PTR) HBMMENU_POPUP_CLOSE:
1628 case (INT_PTR) HBMMENU_POPUP_RESTORE:
1629 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
1630 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
1631 /* FIXME: Why we need to subtract these magic values? */
1632 /* to make them smaller than the menu bar? */
1633 size->cx = UserGetSystemMetrics(SM_CXSIZE) - 2;
1634 size->cy = UserGetSystemMetrics(SM_CYSIZE) - 4;
1635 return;
1636 }
1637 }
1638
1639 if (GreGetObject(bmp, sizeof(BITMAP), &bm))
1640 {
1641 size->cx = bm.bmWidth;
1642 size->cy = bm.bmHeight;
1643 }
1644 }
1645
1646 /***********************************************************************
1647 * MenuDrawBitmapItem
1648 *
1649 * Draw a bitmap item.
1650 */
1651 static void FASTCALL MENU_DrawBitmapItem(HDC hdc, PITEM lpitem, const RECT *rect,
1652 PMENU Menu, PWND WndOwner, UINT odaction, BOOL MenuBar)
1653 {
1654 BITMAP bm;
1655 DWORD rop;
1656 HDC hdcMem;
1657 HBITMAP bmp;
1658 int w = rect->right - rect->left;
1659 int h = rect->bottom - rect->top;
1660 int bmp_xoffset = 0;
1661 int left, top;
1662 BOOL flat_menu;
1663 HBITMAP hbmToDraw = lpitem->hbmp;
1664 bmp = hbmToDraw;
1665
1666 UserSystemParametersInfo(SPI_GETFLATMENU, 0, &flat_menu, 0);
1667
1668 /* Check if there is a magic menu item associated with this item */
1669 if (IS_MAGIC_BITMAP(hbmToDraw))
1670 {
1671 UINT flags = 0;
1672 RECT r;
1673
1674 r = *rect;
1675 switch ((INT_PTR)hbmToDraw)
1676 {
1677 case (INT_PTR)HBMMENU_SYSTEM:
1678 if (lpitem->dwItemData)
1679 {
1680 if (ValidateHwndNoErr((HWND)lpitem->dwItemData))
1681 {
1682 ERR("Get Item Data from this Window!!!\n");
1683 }
1684
1685 ERR("Draw Bitmap\n");
1686 bmp = (HBITMAP)lpitem->dwItemData;
1687 if (!GreGetObject( bmp, sizeof(bm), &bm )) return;
1688 }
1689 else
1690 {
1691 PCURICON_OBJECT pIcon = NULL;
1692 //if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
1693 //bmp = BmpSysMenu;
1694 //if (! GreGetObject(bmp, sizeof(bm), &bm)) return;
1695 /* only use right half of the bitmap */
1696 //bmp_xoffset = bm.bmWidth / 2;
1697 //bm.bmWidth -= bmp_xoffset;
1698 if (WndOwner)
1699 {
1700 pIcon = NC_IconForWindow(WndOwner);
1701 // FIXME: NC_IconForWindow should reference it for us */
1702 if (pIcon) UserReferenceObject(pIcon);
1703 }
1704 ERR("Draw ICON\n");
1705 if (pIcon)
1706 {
1707 LONG cx = UserGetSystemMetrics(SM_CXSMICON);
1708 LONG cy = UserGetSystemMetrics(SM_CYSMICON);
1709 LONG x = rect->left - cx/2 + 1 + (rect->bottom - rect->top)/2; // this is really what Window does
1710 LONG y = (rect->top + rect->bottom)/2 - cy/2; // center
1711 UserDrawIconEx(hdc, x, y, pIcon, cx, cy, 0, NULL, DI_NORMAL);
1712 UserDereferenceObject(pIcon);
1713 }
1714 return;
1715 }
1716 goto got_bitmap;
1717 case (INT_PTR)HBMMENU_MBAR_RESTORE:
1718 flags = DFCS_CAPTIONRESTORE;
1719 break;
1720 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
1721 r.right += 1;
1722 flags = DFCS_CAPTIONMIN;
1723 break;
1724 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
1725 r.right += 1;
1726 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
1727 break;
1728 case (INT_PTR)HBMMENU_MBAR_CLOSE:
1729 flags = DFCS_CAPTIONCLOSE;
1730 break;
1731 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
1732 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
1733 break;
1734 case (INT_PTR)HBMMENU_CALLBACK:
1735 {
1736 DRAWITEMSTRUCT drawItem;
1737 POINT origorg;
1738 drawItem.CtlType = ODT_MENU;
1739 drawItem.CtlID = 0;
1740 drawItem.itemID = lpitem->wID;
1741 drawItem.itemAction = odaction;
1742 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1743 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1744 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1745 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1746 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1747 drawItem.itemState |= (!(Menu->fFlags & MNF_UNDERLINE))?ODS_NOACCEL:0;
1748 drawItem.itemState |= (Menu->fFlags & MNF_INACTIVE)?ODS_INACTIVE:0;
1749 drawItem.hwndItem = (HWND)UserHMGetHandle(Menu);
1750 drawItem.hDC = hdc;
1751 drawItem.rcItem = *rect;
1752 drawItem.itemData = lpitem->dwItemData;
1753 /* some applications make this assumption on the DC's origin */
1754 GreSetViewportOrgEx( hdc, lpitem->xItem, lpitem->yItem, &origorg);
1755 RECTL_vOffsetRect(&drawItem.rcItem, -(LONG)lpitem->xItem, -(LONG)lpitem->yItem);
1756 co_IntSendMessage( UserHMGetHandle(WndOwner), WM_DRAWITEM, 0, (LPARAM)&drawItem);
1757 GreSetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1758 return;
1759 }
1760 break;
1761
1762 case (INT_PTR) HBMMENU_POPUP_CLOSE:
1763 case (INT_PTR) HBMMENU_POPUP_RESTORE:
1764 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
1765 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
1766 MENU_DrawPopupGlyph(hdc, &r, (INT_PTR)hbmToDraw, lpitem->fState & MF_GRAYED, lpitem->fState & MF_HILITE);
1767 return;
1768 }
1769 RECTL_vInflateRect(&r, -1, -1);
1770 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
1771 DrawFrameControl(hdc, &r, DFC_CAPTION, flags);
1772 return;
1773 }
1774
1775 if (!bmp || !GreGetObject( bmp, sizeof(bm), &bm )) return;
1776
1777 got_bitmap:
1778 hdcMem = NtGdiCreateCompatibleDC( hdc );
1779 NtGdiSelectBitmap( hdcMem, bmp );
1780 /* handle fontsize > bitmap_height */
1781 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
1782 left=rect->left;
1783 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
1784 if ((lpitem->fState & MF_HILITE) && lpitem->hbmp)
1785 IntGdiSetBkColor(hdc, IntGetSysColor(COLOR_HIGHLIGHT));
1786 if (MenuBar &&
1787 !flat_menu &&
1788 (lpitem->fState & (MF_HILITE | MF_GRAYED)) == MF_HILITE)
1789 {
1790 ++left;
1791 ++top;
1792 }
1793 NtGdiBitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop , 0, 0);
1794 IntGdiDeleteDC( hdcMem, FALSE );
1795 }
1796
1797 LONG
1798 IntGetDialogBaseUnits(VOID)
1799 {
1800 static DWORD units;
1801
1802 if (!units)
1803 {
1804 HDC hdc;
1805 SIZE size;
1806
1807 if ((hdc = UserGetDCEx(NULL, NULL, DCX_CACHE)))
1808 {
1809 size.cx = IntGetCharDimensions( hdc, NULL, (PDWORD)&size.cy );
1810 if (size.cx) units = MAKELONG( size.cx, size.cy );
1811 UserReleaseDC( 0, hdc, FALSE);
1812 }
1813 }
1814 return units;
1815 }
1816
1817
1818 /***********************************************************************
1819 * MenuCalcItemSize
1820 *
1821 * Calculate the size of the menu item and store it in lpitem->rect.
1822 */
1823 static void FASTCALL MENU_CalcItemSize( HDC hdc, PITEM lpitem, PMENU Menu, PWND pwndOwner,
1824 INT orgX, INT orgY, BOOL menuBar, BOOL textandbmp)
1825 {
1826 WCHAR *p;
1827 UINT check_bitmap_width = UserGetSystemMetrics( SM_CXMENUCHECK );
1828 UINT arrow_bitmap_width;
1829 RECT Rect;
1830 INT itemheight = 0;
1831
1832 TRACE("dc=%x owner=%x (%d,%d)\n", hdc, pwndOwner, orgX, orgY);
1833
1834 arrow_bitmap_width = gpsi->oembmi[OBI_MNARROW].cx;
1835
1836 MenuCharSize.cx = IntGetCharDimensions( hdc, NULL, (PDWORD)&MenuCharSize.cy );
1837
1838 RECTL_vSetRect( &Rect, orgX, orgY, orgX, orgY );
1839
1840 if (lpitem->fType & MF_OWNERDRAW)
1841 {
1842 MEASUREITEMSTRUCT mis;
1843 mis.CtlType = ODT_MENU;
1844 mis.CtlID = 0;
1845 mis.itemID = lpitem->wID;
1846 mis.itemData = lpitem->dwItemData;
1847 mis.itemHeight = HIWORD( IntGetDialogBaseUnits());
1848 mis.itemWidth = 0;
1849 co_IntSendMessage( UserHMGetHandle(pwndOwner), WM_MEASUREITEM, 0, (LPARAM)&mis );
1850 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1851 * width of a menufont character to the width of an owner-drawn menu.
1852 */
1853 Rect.right += mis.itemWidth + 2 * MenuCharSize.cx;
1854 if (menuBar) {
1855 /* under at least win95 you seem to be given a standard
1856 height for the menu and the height value is ignored */
1857 Rect.bottom += UserGetSystemMetrics(SM_CYMENUSIZE);
1858 } else
1859 Rect.bottom += mis.itemHeight;
1860 // Or this,
1861 //lpitem->cxBmp = mis.itemWidth;
1862 //lpitem->cyBmp = mis.itemHeight;
1863 TRACE("MF_OWNERDRAW Height %d Width %d\n",mis.itemHeight,mis.itemWidth);
1864 TRACE("MF_OWNERDRAW id=%04lx size=%dx%d cx %d cy %d\n",
1865 lpitem->wID, Rect.right-Rect.left,
1866 Rect.bottom-Rect.top, MenuCharSize.cx, MenuCharSize.cy);
1867
1868 lpitem->xItem = Rect.left;
1869 lpitem->yItem = Rect.top;
1870 lpitem->cxItem = Rect.right;
1871 lpitem->cyItem = Rect.bottom;
1872
1873 return;
1874 }
1875
1876 lpitem->xItem = orgX;
1877 lpitem->yItem = orgY;
1878 lpitem->cxItem = orgX;
1879 lpitem->cyItem = orgY;
1880
1881 if (lpitem->fType & MF_SEPARATOR)
1882 {
1883 lpitem->cyItem += UserGetSystemMetrics( SM_CYMENUSIZE)/2;//SEPARATOR_HEIGHT;
1884 if( !menuBar)
1885 lpitem->cxItem += arrow_bitmap_width + MenuCharSize.cx;
1886 return;
1887 }
1888
1889 lpitem->dxTab = 0;
1890
1891 if (lpitem->hbmp)
1892 {
1893 SIZE size;
1894
1895 if (!menuBar) {
1896 MENU_GetBitmapItemSize(lpitem, &size, pwndOwner );
1897 /* Keep the size of the bitmap in callback mode to be able
1898 * to draw it correctly */
1899 lpitem->cxBmp = size.cx;
1900 lpitem->cyBmp = size.cy;
1901 Menu->cxTextAlign = max(Menu->cxTextAlign, size.cx);
1902 lpitem->cxItem += size.cx + 2;
1903 itemheight = size.cy + 2;
1904
1905 if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
1906 lpitem->cxItem += 2 * check_bitmap_width;
1907 lpitem->cxItem += 4 + MenuCharSize.cx;
1908 lpitem->dxTab = lpitem->cxItem;
1909 lpitem->cxItem += arrow_bitmap_width;
1910 } else /* hbmpItem & MenuBar */ {
1911 MENU_GetBitmapItemSize(lpitem, &size, pwndOwner );
1912 lpitem->cxItem += size.cx;
1913 if( lpitem->Xlpstr) lpitem->cxItem += 2;
1914 itemheight = size.cy;
1915
1916 /* Special case: Minimize button doesn't have a space behind it. */
1917 if (lpitem->hbmp == (HBITMAP)HBMMENU_MBAR_MINIMIZE ||
1918 lpitem->hbmp == (HBITMAP)HBMMENU_MBAR_MINIMIZE_D)
1919 lpitem->cxItem -= 1;
1920 }
1921 }
1922 else if (!menuBar) {
1923 if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
1924 lpitem->cxItem += check_bitmap_width;
1925 lpitem->cxItem += 4 + MenuCharSize.cx;
1926 lpitem->dxTab = lpitem->cxItem;
1927 lpitem->cxItem += arrow_bitmap_width;
1928 }
1929
1930 /* it must be a text item - unless it's the system menu */
1931 if (!(lpitem->fType & MF_SYSMENU) && lpitem->Xlpstr) {
1932 HFONT hfontOld = NULL;
1933 RECT rc;// = lpitem->Rect;
1934 LONG txtheight, txtwidth;
1935
1936 rc.left = lpitem->xItem;
1937 rc.top = lpitem->yItem;
1938 rc.right = lpitem->cxItem; // Do this for now......
1939 rc.bottom = lpitem->cyItem;
1940
1941 if ( lpitem->fState & MFS_DEFAULT ) {
1942 hfontOld = NtGdiSelectFont( hdc, ghMenuFontBold );
1943 }
1944 if (menuBar) {
1945 txtheight = DrawTextW( hdc, lpitem->Xlpstr, -1, &rc, DT_SINGLELINE|DT_CALCRECT);
1946
1947 lpitem->cxItem += rc.right - rc.left;
1948 itemheight = max( max( itemheight, txtheight), UserGetSystemMetrics( SM_CYMENU) - 1);
1949
1950 lpitem->cxItem += 2 * MenuCharSize.cx;
1951 } else {
1952 if ((p = wcschr( lpitem->Xlpstr, '\t' )) != NULL) {
1953 RECT tmprc = rc;
1954 LONG tmpheight;
1955 int n = (int)( p - lpitem->Xlpstr);
1956 /* Item contains a tab (only meaningful in popup menus) */
1957 /* get text size before the tab */
1958 txtheight = DrawTextW( hdc, lpitem->Xlpstr, n, &rc,
1959 DT_SINGLELINE|DT_CALCRECT);
1960 txtwidth = rc.right - rc.left;
1961 p += 1; /* advance past the Tab */
1962 /* get text size after the tab */
1963 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1964 DT_SINGLELINE|DT_CALCRECT);
1965 lpitem->dxTab += txtwidth;
1966 txtheight = max( txtheight, tmpheight);
1967 txtwidth += MenuCharSize.cx + /* space for the tab */
1968 tmprc.right - tmprc.left; /* space for the short cut */
1969 } else {
1970 txtheight = DrawTextW( hdc, lpitem->Xlpstr, -1, &rc,
1971 DT_SINGLELINE|DT_CALCRECT);
1972 txtwidth = rc.right - rc.left;
1973 lpitem->dxTab += txtwidth;
1974 }
1975 lpitem->cxItem += 2 + txtwidth;
1976 itemheight = max( itemheight,
1977 max( txtheight + 2, MenuCharSize.cy + 4));
1978 }
1979 if (hfontOld)
1980 {
1981 NtGdiSelectFont (hdc, hfontOld);
1982 }
1983 } else if( menuBar) {
1984 itemheight = max( itemheight, UserGetSystemMetrics(SM_CYMENU)-1);
1985 }
1986 lpitem->cyItem += itemheight;
1987 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->xItem, lpitem->yItem, lpitem->cxItem, lpitem->cyItem);
1988 }
1989
1990 /***********************************************************************
1991 * MENU_GetMaxPopupHeight
1992 */
1993 static UINT
1994 MENU_GetMaxPopupHeight(PMENU lppop)
1995 {
1996 if (lppop->cyMax)
1997 {
1998 //ERR("MGMaxPH cyMax %d\n",lppop->cyMax);
1999 return lppop->cyMax;
2000 }
2001 //ERR("MGMaxPH SyMax %d\n",UserGetSystemMetrics(SM_CYSCREEN) - UserGetSystemMetrics(SM_CYBORDER));
2002 return UserGetSystemMetrics(SM_CYSCREEN) - UserGetSystemMetrics(SM_CYBORDER);
2003 }
2004
2005 /***********************************************************************
2006 * MenuPopupMenuCalcSize
2007 *
2008 * Calculate the size of a popup menu.
2009 */
2010 static void FASTCALL MENU_PopupMenuCalcSize(PMENU Menu, PWND WndOwner)
2011 {
2012 PITEM lpitem;
2013 HDC hdc;
2014 int start, i;
2015 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
2016 BOOL textandbmp = FALSE;
2017
2018 Menu->cxMenu = Menu->cyMenu = 0;
2019 if (Menu->cItems == 0) return;
2020
2021 hdc = UserGetDCEx(NULL, NULL, DCX_CACHE);
2022
2023 NtGdiSelectFont( hdc, ghMenuFont );
2024
2025 start = 0;
2026 maxX = 2 + 1;
2027
2028 Menu->cxTextAlign = 0;
2029
2030 while (start < Menu->cItems)
2031 {
2032 lpitem = &Menu->rgItems[start];
2033 orgX = maxX;
2034 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
2035 orgX += MENU_COL_SPACE;
2036 orgY = MENU_TOP_MARGIN;
2037
2038 maxTab = maxTabWidth = 0;
2039 /* Parse items until column break or end of menu */
2040 for (i = start; i < Menu->cItems; i++, lpitem++)
2041 {
2042 if (i != start &&
2043 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
2044
2045 MENU_CalcItemSize(hdc, lpitem, Menu, WndOwner, orgX, orgY, FALSE, textandbmp);
2046 maxX = max(maxX, lpitem->cxItem);
2047 orgY = lpitem->cyItem;
2048 if (IS_STRING_ITEM(lpitem->fType) && lpitem->dxTab )
2049 {
2050 maxTab = max( maxTab, lpitem->dxTab );
2051 maxTabWidth = max(maxTabWidth, lpitem->cxItem - lpitem->dxTab);
2052 }
2053 if( lpitem->Xlpstr && lpitem->hbmp) textandbmp = TRUE;
2054 }
2055
2056 /* Finish the column (set all items to the largest width found) */
2057 maxX = max( maxX, maxTab + maxTabWidth );
2058 for (lpitem = &Menu->rgItems[start]; start < i; start++, lpitem++)
2059 {
2060 lpitem->cxItem = maxX;
2061 if (IS_STRING_ITEM(lpitem->fType) && lpitem->dxTab)
2062 lpitem->dxTab = maxTab;
2063 }
2064 Menu->cyMenu = max(Menu->cyMenu, orgY);
2065 }
2066
2067 Menu->cxMenu = maxX;
2068 /* if none of the items have both text and bitmap then
2069 * the text and bitmaps are all aligned on the left. If there is at
2070 * least one item with both text and bitmap then bitmaps are
2071 * on the left and texts left aligned with the right hand side
2072 * of the bitmaps */
2073 if( !textandbmp) Menu->cxTextAlign = 0;
2074
2075 /* space for 3d border */
2076 Menu->cyMenu += MENU_BOTTOM_MARGIN;
2077 Menu->cxMenu += 2;
2078
2079 /* Adjust popup height if it exceeds maximum */
2080 maxHeight = MENU_GetMaxPopupHeight(Menu);
2081 Menu->iMaxTop = Menu->cyMenu - MENU_TOP_MARGIN;
2082 if (Menu->cyMenu >= maxHeight)
2083 {
2084 Menu->cyMenu = maxHeight;
2085 Menu->dwArrowsOn = 1;
2086 }
2087 else
2088 {
2089 Menu->dwArrowsOn = 0;
2090 }
2091 UserReleaseDC( 0, hdc, FALSE );
2092 }
2093
2094 /***********************************************************************
2095 * MENU_MenuBarCalcSize
2096 *
2097 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
2098 * height is off by 1 pixel which causes lengthy window relocations when
2099 * active document window is maximized/restored.
2100 *
2101 * Calculate the size of the menu bar.
2102 */
2103 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect, PMENU lppop, PWND pwndOwner )
2104 {
2105 ITEM *lpitem;
2106 UINT start, i, helpPos;
2107 int orgX, orgY, maxY;
2108
2109 if ((lprect == NULL) || (lppop == NULL)) return;
2110 if (lppop->cItems == 0) return;
2111 //TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
2112 lppop->cxMenu = lprect->right - lprect->left;
2113 lppop->cyMenu = 0;
2114 maxY = lprect->top;
2115 start = 0;
2116 helpPos = ~0U;
2117 lppop->cxTextAlign = 0;
2118 while (start < lppop->cItems)
2119 {
2120 lpitem = &lppop->rgItems[start];
2121 orgX = lprect->left;
2122 orgY = maxY;
2123
2124 /* Parse items until line break or end of menu */
2125 for (i = start; i < lppop->cItems; i++, lpitem++)
2126 {
2127 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
2128 if ((i != start) &&
2129 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
2130
2131 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
2132 //debug_print_menuitem (" item: ", lpitem, "");
2133 //MENU_CalcItemSize( hdc, lpitem, pwndOwner, orgX, orgY, TRUE, lppop );
2134 MENU_CalcItemSize(hdc, lpitem, lppop, pwndOwner, orgX, orgY, TRUE, FALSE);
2135
2136 if (lpitem->cxItem > lprect->right)
2137 {
2138 if (i != start) break;
2139 else lpitem->cxItem = lprect->right;
2140 }
2141 maxY = max( maxY, lpitem->cyItem );
2142 orgX = lpitem->cxItem;
2143 }
2144
2145 /* Finish the line (set all items to the largest height found) */
2146
2147 /* FIXME: Is this really needed? */ /*NO! it is not needed, why make the
2148 HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
2149 #if 0
2150 while (start < i) lppop->rgItems[start++].cyItem = maxY;
2151 #endif
2152 start = i; /* This works! */
2153 }
2154
2155 lprect->bottom = maxY + 1;
2156 lppop->cyMenu = lprect->bottom - lprect->top;
2157
2158 /* Flush right all items between the MF_RIGHTJUSTIFY and */
2159 /* the last item (if several lines, only move the last line) */
2160 if (helpPos == ~0U) return;
2161 lpitem = &lppop->rgItems[lppop->cItems-1];
2162 orgY = lpitem->yItem;
2163 orgX = lprect->right;
2164 for (i = lppop->cItems - 1; i >= helpPos; i--, lpitem--) {
2165 if (lpitem->yItem != orgY) break; /* Other line */
2166 if (lpitem->cxItem >= orgX) break; /* Too far right already */
2167 lpitem->xItem += orgX - lpitem->cxItem;
2168 lpitem->cxItem = orgX;
2169 orgX = lpitem->xItem;
2170 }
2171 }
2172
2173 /***********************************************************************
2174 * MENU_DrawScrollArrows
2175 *
2176 * Draw scroll arrows.
2177 */
2178 static void MENU_DrawScrollArrows(PMENU lppop, HDC hdc)
2179 {
2180 UINT arrow_bitmap_width, arrow_bitmap_height;
2181 RECT rect, dfcrc;
2182 UINT Flags = 0;
2183
2184 arrow_bitmap_width = gpsi->oembmi[OBI_DNARROW].cx;
2185 arrow_bitmap_height = gpsi->oembmi[OBI_DNARROW].cy;
2186
2187 rect.left = 0;
2188 rect.top = 0;
2189 rect.right = lppop->cxMenu;
2190 rect.bottom = arrow_bitmap_height;
2191 FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENU));
2192 dfcrc.left = (lppop->cxMenu - arrow_bitmap_width) / 2;
2193 dfcrc.top = 0;
2194 dfcrc.right = arrow_bitmap_width;
2195 dfcrc.bottom = arrow_bitmap_height;
2196 DrawFrameControl(hdc, &dfcrc, DFC_MENU, (lppop->iTop ? 0 : DFCS_INACTIVE)|DFCS_MENUARROWUP);
2197
2198 rect.top = lppop->cyMenu - arrow_bitmap_height;
2199 rect.bottom = lppop->cyMenu;
2200 FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENU));
2201 if (!(lppop->iTop < lppop->iMaxTop - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height)))
2202 Flags = DFCS_INACTIVE;
2203 dfcrc.left = (lppop->cxMenu - arrow_bitmap_width) / 2;
2204 dfcrc.top = lppop->cyMenu - arrow_bitmap_height;
2205 dfcrc.right = arrow_bitmap_width;
2206 dfcrc.bottom = lppop->cyMenu;
2207 DrawFrameControl(hdc, &dfcrc, DFC_MENU, Flags|DFCS_MENUARROWDOWN);
2208 }
2209
2210 /***********************************************************************
2211 * MenuDrawMenuItem
2212 *
2213 * Draw a single menu item.
2214 */
2215 static void FASTCALL MENU_DrawMenuItem(PWND Wnd, PMENU Menu, PWND WndOwner, HDC hdc,
2216 PITEM lpitem, UINT Height, BOOL menuBar, UINT odaction)
2217 {
2218 RECT rect;
2219 PWCHAR Text;
2220 BOOL flat_menu = FALSE;
2221 int bkgnd;
2222 UINT arrow_bitmap_width = 0;
2223 //RECT bmprc;
2224
2225 if (!menuBar) {
2226 arrow_bitmap_width = gpsi->oembmi[OBI_MNARROW].cx;
2227 }
2228
2229 if (lpitem->fType & MF_SYSMENU)
2230 {
2231 if (!(Wnd->style & WS_MINIMIZE))
2232 {
2233 NC_GetInsideRect(Wnd, &rect);
2234 UserDrawSysMenuButton(Wnd, hdc, &rect, lpitem->fState & (MF_HILITE | MF_MOUSESELECT));
2235 }
2236 return;
2237 }
2238
2239 UserSystemParametersInfo (SPI_GETFLATMENU, 0, &flat_menu, 0);
2240 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
2241
2242 /* Setup colors */
2243
2244 if (lpitem->fState & MF_HILITE)
2245 {
2246 if(menuBar && !flat_menu) {
2247 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_MENUTEXT));
2248 IntGdiSetBkColor(hdc, IntGetSysColor(COLOR_MENU));
2249 } else {
2250 if (lpitem->fState & MF_GRAYED)
2251 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_GRAYTEXT));
2252 else
2253 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_HIGHLIGHTTEXT));
2254 IntGdiSetBkColor(hdc, IntGetSysColor(COLOR_HIGHLIGHT));
2255 }
2256 }
2257 else
2258 {
2259 if (lpitem->fState & MF_GRAYED)
2260 IntGdiSetTextColor( hdc, IntGetSysColor( COLOR_GRAYTEXT ) );
2261 else
2262 IntGdiSetTextColor( hdc, IntGetSysColor( COLOR_MENUTEXT ) );
2263 IntGdiSetBkColor( hdc, IntGetSysColor( bkgnd ) );
2264 }
2265
2266 //TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->Rect));
2267 //rect = lpitem->Rect;
2268 rect.left = lpitem->xItem;
2269 rect.top = lpitem->yItem;
2270 rect.right = lpitem->cxItem; // Do this for now......
2271 rect.bottom = lpitem->cyItem;
2272
2273 MENU_AdjustMenuItemRect(Menu, &rect);
2274
2275 if (lpitem->fType & MF_OWNERDRAW)
2276 {
2277 /*
2278 ** Experimentation under Windows reveals that an owner-drawn
2279 ** menu is given the rectangle which includes the space it requested
2280 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
2281 ** and a popup-menu arrow. This is the value of lpitem->rect.
2282 ** Windows will leave all drawing to the application except for
2283 ** the popup-menu arrow. Windows always draws that itself, after
2284 ** the menu owner has finished drawing.
2285 */
2286 DRAWITEMSTRUCT dis;
2287 COLORREF old_bk, old_text;
2288
2289 dis.CtlType = ODT_MENU;
2290 dis.CtlID = 0;
2291 dis.itemID = lpitem->wID;
2292 dis.itemData = (DWORD)lpitem->dwItemData;
2293 dis.itemState = 0;
2294 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
2295 if (lpitem->fState & MF_DEFAULT) dis.itemState |= ODS_DEFAULT;
2296 if (lpitem->fState & MF_DISABLED) dis.itemState |= ODS_DISABLED;
2297 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED | ODS_DISABLED;
2298 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
2299 if (!(Menu->fFlags & MNF_UNDERLINE)) dis.itemState |= ODS_NOACCEL;
2300 if (Menu->fFlags & MNF_INACTIVE) dis.itemState |= ODS_INACTIVE;
2301 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
2302 dis.hwndItem = (HWND) UserHMGetHandle(Menu);
2303 dis.hDC = hdc;
2304 dis.rcItem = rect;
2305 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
2306 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", Wnd,
2307 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
2308 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
2309 dis.rcItem.bottom);
2310 TRACE("Ownerdraw: Width %d Height %d\n", dis.rcItem.right-dis.rcItem.left, dis.rcItem.bottom-dis.rcItem.top);
2311 old_bk = GreGetBkColor(hdc);
2312 old_text = GreGetTextColor(hdc);
2313 co_IntSendMessage(UserHMGetHandle(WndOwner), WM_DRAWITEM, 0, (LPARAM) &dis);
2314 IntGdiSetBkColor(hdc, old_bk);
2315 IntGdiSetTextColor(hdc, old_text);
2316 /* Draw the popup-menu arrow */
2317 if (!menuBar && lpitem->spSubMenu)
2318 {
2319 RECT rectTemp;
2320 RtlCopyMemory(&rectTemp, &rect, sizeof(RECT));
2321 rectTemp.left = rectTemp.right - UserGetSystemMetrics(SM_CXMENUCHECK);
2322 DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
2323 }
2324 return;
2325 }
2326
2327 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
2328
2329 if (lpitem->fState & MF_HILITE)
2330 {
2331 if (flat_menu)
2332 {
2333 RECTL_vInflateRect (&rect, -1, -1);
2334 FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENUHILIGHT));
2335 RECTL_vInflateRect (&rect, 1, 1);
2336 FrameRect(hdc, &rect, IntGetSysColorBrush(COLOR_HIGHLIGHT));
2337 }
2338 else
2339 {
2340 if (menuBar)
2341 {
2342 FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENU));
2343 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
2344 }
2345 else
2346 {
2347 FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_HIGHLIGHT));
2348 }
2349 }
2350 }
2351 else
2352 FillRect( hdc, &rect, IntGetSysColorBrush(bkgnd) );
2353
2354 IntGdiSetBkMode( hdc, TRANSPARENT );
2355
2356 /* vertical separator */
2357 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
2358 {
2359 HPEN oldPen;
2360 RECT rc = rect;
2361
2362 rc.left -= 3;//MENU_COL_SPACE / 2 + 1; == 3!!
2363 rc.top = 3;
2364 rc.bottom = Height - 3;
2365 if (flat_menu)
2366 {
2367 oldPen = NtGdiSelectPen( hdc, NtGdiGetStockObject(DC_PEN) );
2368 IntSetDCPenColor(hdc, IntGetSysColor(COLOR_BTNSHADOW));
2369 GreMoveTo( hdc, rc.left, rc.top, NULL );
2370 NtGdiLineTo( hdc, rc.left, rc.bottom );
2371 NtGdiSelectPen( hdc, oldPen );
2372 }
2373 else
2374 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
2375 }
2376
2377 /* horizontal separator */
2378 if (lpitem->fType & MF_SEPARATOR)
2379 {
2380 HPEN oldPen;
2381 RECT rc = rect;
2382
2383 rc.left++;
2384 rc.right--;
2385 rc.top = (rc.top + rc.bottom) / 2 - 1;
2386 if (flat_menu)
2387 {
2388 oldPen = NtGdiSelectPen( hdc, NtGdiGetStockObject(DC_PEN) );
2389 IntSetDCPenColor( hdc, IntGetSysColor(COLOR_BTNSHADOW));
2390 GreMoveTo( hdc, rc.left, rc.top, NULL );
2391 NtGdiLineTo( hdc, rc.right, rc.top );
2392 NtGdiSelectPen( hdc, oldPen );
2393 }
2394 else
2395 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
2396 return;
2397 }
2398 #if 0
2399 /* helper lines for debugging */
2400 /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
2401 FrameRect(hdc, &rect, NtGdiGetStockObject(BLACK_BRUSH));
2402 NtGdiSelectPen(hdc, NtGdiGetStockObject(DC_PEN));
2403 IntSetDCPenColor(hdc, IntGetSysColor(COLOR_WINDOWFRAME));
2404 GreMoveTo(hdc, rect.left, (rect.top + rect.bottom) / 2, NULL);
2405 NtGdiLineTo(hdc, rect.right, (rect.top + rect.bottom) / 2);
2406 #endif
2407 #if 0 // breaks mdi menu bar icons.
2408 if (lpitem->hbmp) {
2409 /* calculate the bitmap rectangle in coordinates relative
2410 * to the item rectangle */
2411 if( menuBar) {
2412 if( lpitem->hbmp == HBMMENU_CALLBACK)
2413 bmprc.left = 3;
2414 else
2415 bmprc.left = lpitem->Xlpstr ? MenuCharSize.cx : 0;
2416 }
2417 else if ((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK)
2418 bmprc.left = 4;
2419 else if ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)
2420 bmprc.left = 2;
2421 else
2422 bmprc.left = 4 + UserGetSystemMetrics(SM_CXMENUCHECK);
2423
2424 bmprc.right = bmprc.left + lpitem->cxBmp;
2425
2426 if( menuBar && !(lpitem->hbmp == HBMMENU_CALLBACK))
2427 bmprc.top = 0;
2428 else
2429 bmprc.top = (rect.bottom - rect.top - lpitem->cyBmp) / 2;
2430
2431 bmprc.bottom = bmprc.top + lpitem->cyBmp;
2432 }
2433 #endif
2434 if (!menuBar)
2435 {
2436 HBITMAP bm;
2437 INT y = rect.top + rect.bottom;
2438 RECT rc = rect;
2439 BOOL checked = FALSE;
2440 UINT check_bitmap_width = UserGetSystemMetrics( SM_CXMENUCHECK );
2441 UINT check_bitmap_height = UserGetSystemMetrics( SM_CYMENUCHECK );
2442 /* Draw the check mark
2443 *
2444 * FIXME:
2445 * Custom checkmark bitmaps are monochrome but not always 1bpp.
2446 */
2447 if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK)) {
2448 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hbmpChecked :
2449 lpitem->hbmpUnchecked;
2450 if (bm) /* we have a custom bitmap */
2451 {
2452 HDC hdcMem = NtGdiCreateCompatibleDC( hdc );
2453
2454 NtGdiSelectBitmap( hdcMem, bm );
2455 NtGdiBitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
2456 check_bitmap_width, check_bitmap_height,
2457 hdcMem, 0, 0, SRCCOPY, 0,0);
2458 IntGdiDeleteDC( hdcMem, FALSE );
2459 checked = TRUE;
2460 }
2461 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
2462 {
2463 RECT r;
2464 r = rect;
2465 r.right = r.left + check_bitmap_width;
2466 DrawFrameControl( hdc, &r, DFC_MENU,
2467 (lpitem->fType & MFT_RADIOCHECK) ?
2468 DFCS_MENUBULLET : DFCS_MENUCHECK);
2469 checked = TRUE;
2470 }
2471 }
2472 if ( lpitem->hbmp )//&& !( checked && ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)))
2473 {
2474 RECT bmpRect = rect;
2475 if (!((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP) && !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
2476 bmpRect.left += check_bitmap_width + 2;
2477 if (!(checked && ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)))
2478 {
2479 bmpRect.right = bmpRect.left + lpitem->cxBmp;
2480 MENU_DrawBitmapItem(hdc, lpitem, &bmpRect, Menu, WndOwner, odaction, menuBar);
2481 }
2482 }
2483 /* Draw the popup-menu arrow */
2484 if (lpitem->spSubMenu)
2485 {
2486 RECT rectTemp;
2487 RtlCopyMemory(&rectTemp, &rect, sizeof(RECT));
2488 rectTemp.left = rectTemp.right - check_bitmap_width;
2489 DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
2490 }
2491 rect.left += 4;
2492 if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK))
2493 rect.left += check_bitmap_width;
2494 rect.right -= arrow_bitmap_width;
2495 }
2496 else if( lpitem->hbmp)
2497 { /* Draw the bitmap */
2498 MENU_DrawBitmapItem(hdc, lpitem, &rect/*bmprc*/, Menu, WndOwner, odaction, menuBar);
2499 }
2500
2501 /* process text if present */
2502 if (lpitem->Xlpstr)
2503 {
2504 int i = 0;
2505 HFONT hfontOld = 0;
2506
2507 UINT uFormat = menuBar ?
2508 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
2509 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
2510
2511 if (((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP))
2512 rect.left += max(0, (int)(Menu->cxTextAlign - UserGetSystemMetrics(SM_CXMENUCHECK)));
2513 else
2514 rect.left += Menu->cxTextAlign;
2515
2516 if ( lpitem->fState & MFS_DEFAULT )
2517 {
2518 hfontOld = NtGdiSelectFont(hdc, ghMenuFontBold);
2519 }
2520
2521 if (menuBar) {
2522 if( lpitem->hbmp)
2523 rect.left += lpitem->cxBmp;
2524 if( !(lpitem->hbmp == HBMMENU_CALLBACK))
2525 rect.left += MenuCharSize.cx;
2526 rect.right -= MenuCharSize.cx;
2527 }
2528
2529 Text = lpitem->Xlpstr;
2530 if(Text)
2531 {
2532 for (i = 0; Text[i]; i++)
2533 if (Text[i] == L'\t' || Text[i] == L'\b')
2534 break;
2535 }
2536
2537 if (menuBar &&
2538 !flat_menu &&
2539 (lpitem->fState & (MF_HILITE | MF_GRAYED)) == MF_HILITE)
2540 {
2541 RECTL_vOffsetRect(&rect, +1, +1);
2542 }
2543
2544 if (!menuBar)
2545 --rect.bottom;
2546
2547 if(lpitem->fState & MF_GRAYED)
2548 {
2549 if (!(lpitem->fState & MF_HILITE) )
2550 {
2551 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
2552 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_BTNHIGHLIGHT));
2553 DrawTextW( hdc, Text, i, &rect, uFormat );
2554 --rect.left; --rect.top; --rect.right; --rect.bottom;
2555 }
2556 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_BTNSHADOW));
2557 }
2558 DrawTextW( hdc, Text, i, &rect, uFormat);
2559
2560 /* paint the shortcut text */
2561 if (!menuBar && L'\0' != Text[i]) /* There's a tab or flush-right char */
2562 {
2563 if (L'\t' == Text[i])
2564 {
2565 rect.left = lpitem->dxTab;
2566 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
2567 }
2568 else
2569 {
2570 rect.right = lpitem->dxTab;
2571 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
2572 }
2573
2574 if (lpitem->fState & MF_GRAYED)
2575 {
2576 if (!(lpitem->fState & MF_HILITE) )
2577 {
2578 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
2579 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_BTNHIGHLIGHT));
2580 DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat);
2581 --rect.left; --rect.top; --rect.right; --rect.bottom;
2582 }
2583 IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_BTNSHADOW));
2584 }
2585 DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat );
2586 }
2587
2588 if (!menuBar)
2589 ++rect.bottom;
2590
2591 if (menuBar &&
2592 !flat_menu &&
2593 (lpitem->fState & (MF_HILITE | MF_GRAYED)) == MF_HILITE)
2594 {
2595 RECTL_vOffsetRect(&rect, -1, -1);
2596 }
2597
2598 if (hfontOld)
2599 {
2600 NtGdiSelectFont (hdc, hfontOld);
2601 }
2602 }
2603 }
2604
2605 /***********************************************************************
2606 * MenuDrawPopupMenu
2607 *
2608 * Paint a popup menu.
2609 */
2610 static void FASTCALL MENU_DrawPopupMenu(PWND wnd, HDC hdc, PMENU menu )
2611 {
2612 HBRUSH hPrevBrush = 0, brush = IntGetSysColorBrush(COLOR_MENU);
2613 RECT rect;
2614
2615 TRACE("DPM wnd=%p dc=%p menu=%p\n", wnd, hdc, menu);
2616
2617 IntGetClientRect( wnd, &rect );
2618
2619 if (menu && menu->hbrBack) brush = menu->hbrBack;
2620 if((hPrevBrush = NtGdiSelectBrush( hdc, brush ))
2621 && (NtGdiSelectFont( hdc, ghMenuFont)))
2622 {
2623 HPEN hPrevPen;
2624
2625 NtGdiRectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
2626
2627 hPrevPen = NtGdiSelectPen( hdc, NtGdiGetStockObject( NULL_PEN ) );
2628 if ( hPrevPen )
2629 {
2630 BOOL flat_menu = FALSE;
2631
2632 UserSystemParametersInfo (SPI_GETFLATMENU, 0, &flat_menu, 0);
2633 if (flat_menu)
2634 FrameRect(hdc, &rect, IntGetSysColorBrush(COLOR_BTNSHADOW));
2635 else
2636 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
2637
2638 TRACE("hmenu %p Style %08x\n", UserHMGetHandle(menu), (menu->fFlags & MNS_STYLE_MASK));
2639 /* draw menu items */
2640 if (menu && menu->cItems)
2641 {
2642 ITEM *item;
2643 UINT u;
2644
2645 item = menu->rgItems;
2646 for( u = menu->cItems; u > 0; u--, item++)
2647 {
2648 MENU_DrawMenuItem(wnd, menu, menu->spwndNotify, hdc, item,
2649 menu->cyMenu, FALSE, ODA_DRAWENTIRE);
2650 }
2651 /* draw scroll arrows */
2652 if (menu->dwArrowsOn)
2653 {
2654 MENU_DrawScrollArrows(menu, hdc);
2655 }
2656 }
2657 }
2658 else
2659 {
2660 NtGdiSelectBrush( hdc, hPrevBrush );
2661 }
2662 }
2663 }
2664
2665 /**********************************************************************
2666 * MENU_IsMenuActive
2667 */
2668 PWND MENU_IsMenuActive(VOID)
2669 {
2670 return ValidateHwndNoErr(top_popup);
2671 }
2672
2673 /**********************************************************************
2674 * MENU_EndMenu
2675 *
2676 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2677 *
2678 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2679 */
2680 void MENU_EndMenu( PWND pwnd )
2681 {
2682 PMENU menu = NULL;
2683 menu = UserGetMenuObject(top_popup_hmenu);
2684 if ( menu && ( UserHMGetHandle(pwnd) == menu->hWnd || pwnd == menu->spwndNotify ) )
2685 {
2686 if (fInsideMenuLoop && top_popup)
2687 {
2688 fInsideMenuLoop = FALSE;
2689
2690 if (fInEndMenu)
2691 {
2692 ERR("Already in End loop\n");
2693 return;
2694 }
2695
2696 fInEndMenu = TRUE;
2697 UserPostMessage( top_popup, WM_CANCELMODE, 0, 0);
2698 }
2699 }
2700 }
2701
2702 DWORD WINAPI
2703 IntDrawMenuBarTemp(PWND pWnd, HDC hDC, LPRECT Rect, PMENU pMenu, HFONT Font)
2704 {
2705 UINT i;
2706 HFONT FontOld = NULL;
2707 BOOL flat_menu = FALSE;
2708
2709 UserSystemParametersInfo(SPI_GETFLATMENU, 0, &flat_menu, 0);
2710
2711 if (!pMenu)
2712 {
2713 pMenu = UserGetMenuObject(UlongToHandle(pWnd->IDMenu));
2714 }
2715
2716 if (!Font)
2717 {
2718 Font = ghMenuFont;
2719 }
2720
2721 if (Rect == NULL || !pMenu)
2722 {
2723 return UserGetSystemMetrics(SM_CYMENU);
2724 }
2725
2726 TRACE("(%x, %x, %p, %x, %x)\n", pWnd, hDC, Rect, pMenu, Font);
2727
2728 FontOld = NtGdiSelectFont(hDC, Font);
2729
2730 if (pMenu->cyMenu == 0)
2731 {
2732 MENU_MenuBarCalcSize(hDC, Rect, pMenu, pWnd);
2733 }
2734
2735 Rect->bottom = Rect->top + pMenu->cyMenu;
2736
2737 FillRect(hDC, Rect, IntGetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
2738
2739 NtGdiSelectPen(hDC, NtGdiGetStockObject(DC_PEN));
2740 IntSetDCPenColor(hDC, IntGetSysColor(COLOR_3DFACE));
2741 GreMoveTo(hDC, Rect->left, Rect->bottom - 1, NULL);
2742 NtGdiLineTo(hDC, Rect->right, Rect->bottom - 1);
2743
2744 if (pMenu->cItems == 0)
2745 {
2746 NtGdiSelectFont(hDC, FontOld);
2747 return UserGetSystemMetrics(SM_CYMENU);
2748 }
2749
2750 for (i = 0; i < pMenu->cItems; i++)
2751 {
2752 MENU_DrawMenuItem(pWnd, pMenu, pWnd, hDC, &pMenu->rgItems[i], pMenu->cyMenu, TRUE, ODA_DRAWENTIRE);
2753 }
2754
2755 NtGdiSelectFont(hDC, FontOld);
2756
2757 return pMenu->cyMenu;
2758 }
2759
2760 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, PWND pWnd, BOOL suppress_draw )
2761 {
2762 HFONT hfontOld = 0;
2763 PMENU lppop = UserGetMenuObject(UlongToHandle(pWnd->IDMenu));
2764
2765 if (lppop == NULL)
2766 {
2767 // No menu. Do not reserve any space
2768 return 0;
2769 }
2770
2771 if (lprect == NULL)
2772 {
2773 return UserGetSystemMetrics(SM_CYMENU);
2774 }
2775
2776 if (suppress_draw)
2777 {
2778 hfontOld = NtGdiSelectFont(hDC, ghMenuFont);
2779
2780 MENU_MenuBarCalcSize(hDC, lprect, lppop, pWnd);
2781
2782 lprect->bottom = lprect->top + lppop->cyMenu;
2783
2784 if (hfontOld) NtGdiSelectFont( hDC, hfontOld);
2785
2786 return lppop->cyMenu;
2787 }
2788 else
2789 {
2790 return IntDrawMenuBarTemp(pWnd, hDC, lprect, lppop, NULL);
2791 }
2792 }
2793
2794 /***********************************************************************
2795 * MENU_InitPopup
2796 *
2797 * Popup menu initialization before WM_ENTERMENULOOP.
2798 */
2799 static BOOL MENU_InitPopup( PWND pWndOwner, PMENU menu, UINT flags )
2800 {
2801 PWND pWndCreated;
2802 PPOPUPMENU pPopupMenu;
2803 CREATESTRUCTW Cs;
2804 LARGE_STRING WindowName;
2805 UNICODE_STRING ClassName;
2806 DWORD ex_style = WS_EX_TOOLWINDOW;
2807
2808 TRACE("owner=%p hmenu=%p\n", pWndOwner, menu);
2809
2810 menu->spwndNotify = pWndOwner;
2811
2812 if (flags & TPM_LAYOUTRTL || pWndOwner->ExStyle & WS_EX_LAYOUTRTL)
2813 ex_style = WS_EX_LAYOUTRTL;
2814
2815 ClassName.Buffer = WC_MENU;
2816 ClassName.Length = 0;
2817
2818 RtlZeroMemory(&WindowName, sizeof(WindowName));
2819 RtlZeroMemory(&Cs, sizeof(Cs));
2820 Cs.style = WS_POPUP;
2821 Cs.dwExStyle = ex_style;
2822 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
2823 Cs.lpszName = (LPCWSTR) &WindowName;
2824 Cs.lpszClass = (LPCWSTR) &ClassName;
2825 Cs.lpCreateParams = UserHMGetHandle(menu);
2826 Cs.hwndParent = UserHMGetHandle(pWndOwner);
2827
2828 /* NOTE: In Windows, top menu popup is not owned. */
2829 pWndCreated = co_UserCreateWindowEx( &Cs, &ClassName, &WindowName, NULL);
2830
2831 if( !pWndCreated ) return FALSE;
2832
2833 //
2834 // Setup pop up menu structure.
2835 //
2836 menu->hWnd = UserHMGetHandle(pWndCreated);
2837
2838 pPopupMenu = ((PMENUWND)pWndCreated)->ppopupmenu;
2839
2840 pPopupMenu->spwndActivePopup = pWndCreated; // top_popup = MenuInfo.Wnd or menu->hWnd
2841 pPopupMenu->spwndNotify = pWndOwner; // Same as MenuInfo.spwndNotify(which could be wrong) or menu->hwndOwner
2842 //pPopupMenu->spmenu = menu; Should be set up already from WM_CREATE!
2843
2844 pPopupMenu->fIsTrackPopup = !!(flags & TPM_POPUPMENU);
2845 pPopupMenu->fIsSysMenu = !!(flags & TPM_SYSTEM_MENU);
2846 pPopupMenu->fNoNotify = !!(flags & TPM_NONOTIFY);
2847 pPopupMenu->fRightButton = !!(flags & TPM_RIGHTBUTTON);
2848 pPopupMenu->fSynchronous = !!(flags & TPM_RETURNCMD);
2849
2850 if (pPopupMenu->fRightButton)
2851 pPopupMenu->fFirstClick = !!(UserGetKeyState(VK_RBUTTON) & 0x8000);
2852 else
2853 pPopupMenu->fFirstClick = !!(UserGetKeyState(VK_LBUTTON) & 0x8000);
2854
2855 if (gpsi->aiSysMet[SM_MENUDROPALIGNMENT] ||
2856 menu->fFlags & MNF_RTOL)
2857 {
2858 pPopupMenu->fDroppedLeft = TRUE;
2859 }
2860 return TRUE;
2861 }
2862
2863 /***********************************************************************
2864 * MenuShowPopup
2865 *
2866 * Display a popup menu.
2867 */
2868 static BOOL FASTCALL MENU_ShowPopup(PWND pwndOwner, PMENU menu, UINT id, UINT flags,
2869 INT x, INT y)
2870 {
2871 UINT width, height;
2872 POINT pt;
2873 PMONITOR monitor;
2874 PWND pWnd;
2875 USER_REFERENCE_ENTRY Ref;
2876 BOOL bIsPopup = (flags & TPM_POPUPMENU) != 0;
2877
2878 TRACE("owner=%p menu=%p id=0x%04x x=0x%04x y=0x%04x\n",
2879 pwndOwner, menu, id, x, y);
2880
2881 if (menu->iItem != NO_SELECTED_ITEM)
2882 {
2883 menu->rgItems[menu->iItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
2884 menu->iItem = NO_SELECTED_ITEM;
2885 }
2886
2887 menu->dwArrowsOn = 0;
2888 MENU_PopupMenuCalcSize(menu, pwndOwner);
2889
2890 /* adjust popup menu pos so that it fits within the desktop */
2891
2892 width = menu->cxMenu + UserGetSystemMetrics(SM_CXBORDER);
2893 height = menu->cyMenu + UserGetSystemMetrics(SM_CYBORDER);
2894
2895 /* FIXME: should use item rect */
2896 pt.x = x;
2897 pt.y = y;
2898 monitor = UserMonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
2899
2900 if (flags & TPM_LAYOUTRTL)
2901 flags ^= TPM_RIGHTALIGN;
2902
2903 if( flags & TPM_RIGHTALIGN ) x -= width;
2904 if( flags & TPM_CENTERALIGN ) x -= width / 2;
2905
2906 if( flags & TPM_BOTTOMALIGN ) y -= height;
2907 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
2908
2909 if( x + width > monitor->rcMonitor.right)
2910 {
2911 if( x + width > monitor->rcMonitor.right)
2912 {
2913 /* If we would flip around our origin, would we go off screen on the other side?
2914 Or is our origin itself too far to the right already? */
2915 if (!bIsPopup || x - width < monitor->rcMonitor.left || x > monitor->rcMonitor.right)
2916 x = monitor->rcMonitor.right - width;
2917 else
2918 x -= width;
2919 }
2920 }
2921 if( x < monitor->rcMonitor.left )
2922 {
2923 /* If we would flip around our origin, would we go off screen on the other side? */
2924 if (!bIsPopup || x + width > monitor->rcMonitor.right)
2925 x = monitor->rcMonitor.left;
2926 else
2927 x += width;
2928 }
2929
2930 if( y + height > monitor->rcMonitor.bottom)
2931 {
2932 if( y + height > monitor->rcMonitor.bottom)
2933 {
2934 /* If we would flip around our origin, would we go off screen on the other side?
2935 Or is our origin itself too far to the bottom already? */
2936 if (!bIsPopup || y - height < monitor->rcMonitor.top || y > monitor->rcMonitor.bottom)
2937 y = monitor->rcMonitor.bottom - height;
2938 else
2939 y -= height;
2940 }
2941 }
2942 if( y < monitor->rcMonitor.top )
2943 {
2944 /* If we would flip around our origin, would we go off screen on the other side? */
2945 if (!bIsPopup || y + height > monitor->rcMonitor.bottom)
2946 y = monitor->rcMonitor.top;
2947 else
2948 y += height;
2949 }
2950
2951 pWnd = ValidateHwndNoErr( menu->hWnd );
2952
2953 if (!pWnd)
2954 {
2955 ERR("menu->hWnd bad hwnd %p\n",menu->hWnd);
2956 return FALSE;
2957 }
2958
2959 if (!top_popup) {
2960 top_popup = menu->hWnd;
2961 top_popup_hmenu = UserHMGetHandle(menu);
2962 }
2963
2964 /* Display the window */
2965 UserRefObjectCo(pWnd, &Ref);
2966 co_WinPosSetWindowPos( pWnd, HWND_TOPMOST, x, y, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE);
2967
2968 co_IntUpdateWindows(pWnd, RDW_ALLCHILDREN, FALSE);
2969
2970 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, pWnd, OBJID_CLIENT, CHILDID_SELF, 0);
2971 UserDerefObjectCo(pWnd);
2972
2973 return TRUE;
2974 }
2975
2976 /***********************************************************************
2977 * MENU_EnsureMenuItemVisible
2978 */
2979 void MENU_EnsureMenuItemVisible(PMENU lppop, UINT wIndex, HDC hdc)
2980 {
2981 USER_REFERENCE_ENTRY Ref;
2982 if (lppop->dwArrowsOn)
2983 {
2984 ITEM *item = &lppop->rgItems[wIndex];
2985 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
2986 UINT nOldPos = lppop->iTop;
2987 RECT rc;
2988 UINT arrow_bitmap_height;
2989 PWND pWnd = ValidateHwndNoErr(lppop->hWnd);
2990
2991 IntGetClientRect(pWnd, &rc);
2992
2993 arrow_bitmap_height = gpsi->oembmi[OBI_DNARROW].cy;
2994
2995 rc.top += arrow_bitmap_height;
2996 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
2997
2998 nMaxHeight -= UserGetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
2999 UserRefObjectCo(pWnd, &Ref);
3000 if (item->cyItem > lppop->iTop + nMaxHeight)
3001 {
3002 lppop->iTop = item->cyItem - nMaxHeight;
3003 IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc);
3004 MENU_DrawScrollArrows(lppop, hdc);
3005 //ERR("Scroll Down iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
3006 }
3007 else if (item->yItem - MENU_TOP_MARGIN < lppop->iTop)
3008 {
3009 lppop->iTop = item->yItem - MENU_TOP_MARGIN;
3010 IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc);
3011 MENU_DrawScrollArrows(lppop, hdc);
3012 //ERR("Scroll Up iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight);
3013 }
3014 UserDerefObjectCo(pWnd);
3015 }
3016 }
3017
3018 /***********************************************************************
3019 * MenuSelectItem
3020 */
3021 static void FASTCALL MENU_SelectItem(PWND pwndOwner, PMENU menu, UINT wIndex,
3022 BOOL sendMenuSelect, PMENU topmenu)
3023 {
3024 HDC hdc;
3025 PWND pWnd;
3026
3027 TRACE("M_SI: owner=%p menu=%p index=0x%04x select=0x%04x\n", pwndOwner, menu, wIndex, sendMenuSelect);
3028
3029 if (!menu || !menu->cItems) return;
3030
3031 pWnd = ValidateHwndNoErr(menu->hWnd);
3032
3033 if (!pWnd) return;
3034
3035 if (menu->iItem == wIndex) return;
3036
3037 if (menu->fFlags & MNF_POPUP)
3038 hdc = UserGetDCEx(pWnd, 0, DCX_USESTYLE);
3039 else
3040 hdc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW);
3041
3042 if (!top_popup) {
3043 top_popup = menu->hWnd; //pPopupMenu->spwndActivePopup or
3044 //pPopupMenu->fIsTrackPopup set pPopupMenu->spwndPopupMenu;
3045 top_popup_hmenu = UserHMGetHandle(menu); //pPopupMenu->spmenu
3046 }
3047
3048 NtGdiSelectFont( hdc, ghMenuFont );
3049
3050 /* Clear previous highlighted item */
3051 if (menu->iItem != NO_SELECTED_ITEM)
3052 {
3053 menu->rgItems[menu->iItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
3054 MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc, &menu->rgItems[menu->iItem],
3055 menu->cyMenu, !(menu->fFlags & MNF_POPUP),
3056 ODA_SELECT);
3057 }
3058
3059 /* Highlight new item (if any) */
3060 menu->iItem = wIndex;
3061 if (menu->iItem != NO_SELECTED_ITEM)
3062 {
3063 if (!(menu->rgItems[wIndex].fType & MF_SEPARATOR))
3064 {
3065 menu->rgItems[wIndex].fState |= MF_HILITE;
3066 MENU_EnsureMenuItemVisible(menu, wIndex, hdc);
3067 MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc,
3068 &menu->rgItems[wIndex], menu->cyMenu, !(menu->fFlags & MNF_POPUP), ODA_SELECT);
3069 }
3070 if (sendMenuSelect)
3071 {
3072 ITEM *ip = &menu->rgItems[menu->iItem];
3073 WPARAM wParam = MAKEWPARAM( ip->spSubMenu ? wIndex : ip->wID,
3074 ip->fType | ip->fState |
3075 (ip->spSubMenu ? MF_POPUP : 0) |
3076 (menu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) );
3077
3078 co_IntSendMessage(UserHMGetHandle(pwndOwner), WM_MENUSELECT, wParam, (LPARAM) UserHMGetHandle(menu));
3079 }
3080 }
3081 else if (sendMenuSelect)
3082 {
3083 if (topmenu)
3084 {
3085 int pos;
3086 pos = MENU_FindSubMenu(&topmenu, menu);
3087 if (pos != NO_SELECTED_ITEM)
3088 {
3089 ITEM *ip = &topmenu->rgItems[pos];
3090 WPARAM wParam = MAKEWPARAM( Pos, ip->fType | ip->fState |
3091 (ip->spSubMenu ? MF_POPUP : 0) |
3092 (topmenu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) );
3093
3094 co_IntSendMessage(UserHMGetHandle(pwndOwner), WM_MENUSELECT, wParam, (LPARAM) UserHMGetHandle(topmenu));
3095 }
3096 }
3097 }
3098 UserReleaseDC(pWnd, hdc, FALSE);
3099 }
3100
3101 /***********************************************************************
3102 * MenuMoveSelection
3103 *
3104 * Moves currently selected item according to the Offset parameter.
3105 * If there is no selection then it should select the last item if
3106 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
3107 */
3108 static void FASTCALL MENU_MoveSelection(PWND pwndOwner, PMENU menu, INT offset)
3109 {
3110 INT i;
3111
3112 TRACE("pwnd=%x menu=%x off=0x%04x\n", pwndOwner, menu, offset);
3113
3114 if ((!menu) || (!menu->rgItems)) return;
3115
3116 if ( menu->iItem != NO_SELECTED_ITEM )
3117 {
3118 if ( menu->cItems == 1 )
3119 return;
3120 else
3121 for (i = menu->iItem + offset ; i >= 0 && i < menu->cItems
3122 ; i += offset)
3123 if (!(menu->rgItems[i].fType & MF_SEPARATOR))
3124 {
3125 MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 );
3126 return;
3127 }
3128 }
3129
3130 for ( i = (offset > 0) ? 0 : menu->cItems - 1;
3131 i >= 0 && i < menu->cItems ; i += offset)
3132 if (!(menu->rgItems[i].fType & MF_SEPARATOR))
3133 {
3134 MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 );
3135 return;
3136 }
3137 }
3138
3139 /***********************************************************************
3140 * MenuHideSubPopups
3141 *
3142 * Hide the sub-popup menus of this menu.
3143 */
3144 static void FASTCALL MENU_HideSubPopups(PWND pWndOwner, PMENU Menu,
3145 BOOL SendMenuSelect, UINT wFlags)
3146 {
3147 TRACE("owner=%x menu=%x 0x%04x\n", pWndOwner, Menu, SendMenuSelect);
3148
3149 if ( Menu && top_popup )
3150 {
3151 PITEM Item;
3152
3153 if (Menu->iItem != NO_SELECTED_ITEM)
3154 {
3155 Item = &Menu->rgItems[Menu->iItem];
3156 if (!(Item->spSubMenu) ||
3157 !(Item->fState & MF_MOUSESELECT)) return;
3158 Item->fState &= ~MF_MOUSESELECT;
3159 }
3160 else
3161 return;
3162
3163 if (Item->spSubMenu)
3164 {
3165 PWND pWnd;
3166 if (!VerifyMenu(Item->spSubMenu)) return;
3167 pWnd = ValidateHwndNoErr(Item->spSubMenu->hWnd);
3168 MENU_HideSubPopups(pWndOwner, Item->spSubMenu, FALSE, wFlags);
3169 MENU_SelectItem(pWndOwner, Item->spSubMenu, NO_SELECTED_ITEM, SendMenuSelect, NULL);
3170 TRACE("M_HSP top p hm %p pWndOwner IDMenu %p\n",top_popup_hmenu,pWndOwner->IDMenu);
3171 co_UserDestroyWindow(pWnd);
3172
3173 /* Native returns handle to destroyed window */
3174 if (!(wFlags & TPM_NONOTIFY))
3175 {
3176 co_IntSendMessage( UserHMGetHandle(pWndOwner), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(Item->spSubMenu),
3177 MAKELPARAM(0, IS_SYSTEM_MENU(Item->spSubMenu)) );
3178 }
3179 ////
3180 // Call WM_UNINITMENUPOPUP FIRST before destroy!!
3181 // Fixes todo_wine User32 test menu.c line 2239 GetMenuBarInfo callback....
3182 //
3183 Item->spSubMenu->hWnd = NULL;
3184 ////
3185 }
3186 }
3187 }
3188
3189 /***********************************************************************
3190 * MenuShowSubPopup
3191 *
3192 * Display the sub-menu of the selected item of this menu.
3193 * Return the handle of the submenu, or menu if no submenu to display.
3194 */
3195 static PMENU FASTCALL MENU_ShowSubPopup(PWND WndOwner, PMENU Menu, BOOL SelectFirst, UINT Flags)
3196 {
3197 RECT Rect;
3198 ITEM *Item;
3199 HDC Dc;
3200 PWND pWnd;
3201
3202 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, Menu, SelectFirst);
3203
3204 if (!Menu) return Menu;
3205
3206 if (Menu->iItem == NO_SELECTED_ITEM) return Menu;
3207
3208 Item = &Menu->rgItems[Menu->iItem];
3209 if (!(Item->spSubMenu) || (Item->fState & (MF_GRAYED | MF_DISABLED)))
3210 return Menu;
3211
3212 /* message must be sent before using item,
3213 because nearly everything may be changed by the application ! */
3214
3215 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3216 if (!(Flags & TPM_NONOTIFY))
3217 {
3218 co_IntSendMessage(UserHMGetHandle(WndOwner), WM_INITMENUPOPUP,
3219 (WPARAM) UserHMGetHandle(Item->spSubMenu),
3220 MAKELPARAM(Menu->iItem, IS_SYSTEM_MENU(Menu)));
3221 }
3222
3223 Item = &Menu->rgItems[Menu->iItem];
3224 //Rect = ItemInfo.Rect;
3225 Rect.left = Item->xItem;
3226 Rect.top = Item->yItem;
3227 Rect.right = Item->cxItem; // Do this for now......
3228 Rect.bottom = Item->cyItem;
3229
3230 pWnd = ValidateHwndNoErr(Menu->hWnd);
3231
3232 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
3233 if (!(Item->fState & MF_HILITE))
3234 {
3235 if (Menu->fFlags & MNF_POPUP) Dc = UserGetDCEx(pWnd, NULL, DCX_USESTYLE);
3236 else Dc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW);
3237
3238 NtGdiSelectFont(Dc, ghMenuFont);
3239
3240 Item->fState |= MF_HILITE;
3241 MENU_DrawMenuItem(pWnd, Menu, WndOwner, Dc, Item, Menu->cyMenu,
3242 !(Menu->fFlags & MNF_POPUP), ODA_DRAWENTIRE);
3243
3244 UserReleaseDC(pWnd, Dc, FALSE);
3245 }
3246
3247 if (!Item->yItem && !Item->xItem && !Item->cyItem && !Item->cxItem)
3248 {
3249 Item->xItem = Rect.left;
3250 Item->yItem = Rect.top;
3251 Item->cxItem = Rect.right; // Do this for now......
3252 Item->cyItem = Rect.bottom;
3253 }
3254 Item->fState |= MF_MOUSESELECT;
3255
3256 if (IS_SYSTEM_MENU(Menu))
3257 {
3258 MENU_InitSysMenuPopup(Item->spSubMenu, pWnd->style, pWnd->pcls->style, HTSYSMENU);
3259
3260 NC_GetSysPopupPos(pWnd, &Rect);
3261 if (Flags & TPM_LAYOUTRTL) Rect.left = Rect.right;
3262 Rect.top = Rect.bottom;
3263 Rect.right = UserGetSystemMetrics(SM_CXSIZE);
3264 Rect.bottom = UserGetSystemMetrics(SM_CYSIZE);
3265 }
3266 else
3267 {
3268 IntGetWindowRect(pWnd, &Rect);
3269 if (Menu->fFlags & MNF_POPUP)
3270 {
3271 RECT rc;
3272 rc.left = Item->xItem;
3273 rc.top = Item->yItem;
3274 rc.right = Item->cxItem; // Do this for now......
3275 rc.bottom = Item->cyItem;
3276
3277 MENU_AdjustMenuItemRect(Menu, &rc);
3278
3279 /* The first item in the popup menu has to be at the
3280 same y position as the focused menu item */
3281 if(Flags & TPM_LAYOUTRTL)
3282 Rect.left += UserGetSystemMetrics(SM_CXBORDER);
3283 else
3284 Rect.left += rc.right /*ItemInfo.Rect.right*/ - UserGetSystemMetrics(SM_CXBORDER);
3285 Rect.top += rc.top - MENU_TOP_MARGIN;//3;
3286 Rect.right = rc.left - rc.right + UserGetSystemMetrics(SM_CXBORDER);
3287 Rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN - MENU_BOTTOM_MARGIN/*2*/
3288 - UserGetSystemMetrics(SM_CYBORDER);
3289 }
3290 else
3291 {
3292 if(Flags & TPM_LAYOUTRTL)
3293 Rect.left += Rect.right - Item->xItem; //ItemInfo.Rect.left;
3294 else
3295 Rect.left += Item->xItem; //ItemInfo.Rect.left;
3296 Rect.top += Item->cyItem; //ItemInfo.Rect.bottom;
3297 Rect.right = Item->cxItem - Item->xItem; //ItemInfo.Rect.right - ItemInfo.Rect.left;
3298 Rect.bottom = Item->cyItem - Item->yItem; //ItemInfo.Rect.bottom - ItemInfo.Rect.top;
3299 }
3300 }
3301
3302 /* use default alignment for submenus */
3303 Flags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
3304
3305 MENU_InitPopup( WndOwner, Item->spSubMenu, Flags );
3306
3307 MENU_ShowPopup( WndOwner, Item->spSubMenu, Menu->iItem, Flags,
3308 Rect.left, Rect.top);
3309 if (SelectFirst)
3310 {
3311 MENU_MoveSelection(WndOwner, Item->spSubMenu, ITEM_NEXT);
3312 }
3313 return Item->spSubMenu;
3314 }
3315
3316 /***********************************************************************
3317 * MenuExecFocusedItem
3318 *
3319 * Execute a menu item (for instance when user pressed Enter).
3320 * Return the wID of the executed item. Otherwise, -1 indicating
3321 * that no menu item was executed, -2 if a popup is shown;
3322 * Have to receive the flags for the TrackPopupMenu options to avoid
3323 * sending unwanted message.
3324 *
3325 */
3326 static INT FASTCALL MENU_ExecFocusedItem(MTRACKER *pmt, PMENU Menu, UINT Flags)
3327 {
3328 PITEM Item;
3329
3330 TRACE("%p menu=%p\n", pmt, Menu);
3331
3332 if (!Menu || !Menu->cItems || Menu->iItem == NO_SELECTED_ITEM)
3333 {
3334 return -1;
3335 }
3336
3337 Item = &Menu->rgItems[Menu->iItem];
3338
3339 TRACE("%p %08x %p\n", Menu, Item->wID, Item->spSubMenu);
3340
3341 if (!(Item->spSubMenu))
3342 {
3343 if (!(Item->fState & (MF_GRAYED | MF_DISABLED)) && !(Item->fType & MF_SEPARATOR))
3344 {
3345 /* If TPM_RETURNCMD is set you return the id, but
3346 do not send a message to the owner */
3347 if (!(Flags & TPM_RETURNCMD))
3348 {
3349 if (Menu->fFlags & MNF_SYSMENU)
3350 {
3351 UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_SYSCOMMAND, Item->wID,
3352 MAKELPARAM((SHORT) pmt->Pt.x, (SHORT) pmt->Pt.y));
3353 }
3354 else
3355 {
3356 DWORD dwStyle = ((Menu->fFlags & MNS_STYLE_MASK) | ( pmt->TopMenu ? (pmt->TopMenu->fFlags & MNS_STYLE_MASK) : 0) );
3357
3358 if (dwStyle & MNS_NOTIFYBYPOS)
3359 UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_MENUCOMMAND, Menu->iItem, (LPARAM)UserHMGetHandle(Menu));
3360 else
3361 UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_COMMAND, Item->wID, 0);
3362 }
3363 }
3364 return Item->wID;
3365 }
3366 }
3367 else
3368 {
3369 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, Menu, TRUE, Flags);
3370 return -2;
3371 }
3372
3373 return -1;
3374 }
3375
3376 /***********************************************************************
3377 * MenuSwitchTracking
3378 *
3379 * Helper function for menu navigation routines.
3380 */
3381 static void FASTCALL MENU_SwitchTracking(MTRACKER* pmt, PMENU PtMenu, UINT Index, UINT wFlags)
3382 {
3383 TRACE("%x menu=%x 0x%04x\n", pmt, PtMenu, Index);
3384
3385 if ( pmt->TopMenu != PtMenu &&
3386 !((PtMenu->fFlags | pmt->TopMenu->fFlags) & MNF_POPUP) )
3387 {
3388 /* both are top level menus (system and menu-bar) */
3389 MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, wFlags);
3390 MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, NO_SELECTED_ITEM, FALSE, NULL);
3391 pmt->TopMenu = PtMenu;
3392 }
3393 else
3394 {
3395 MENU_HideSubPopups(pmt->OwnerWnd, PtMenu, FALSE, wFlags);
3396 }
3397
3398 MENU_SelectItem(pmt->OwnerWnd, PtMenu, Index, TRUE, NULL);
3399 }
3400
3401 /***********************************************************************
3402 * MenuButtonDown
3403 *
3404 * Return TRUE if we can go on with menu tracking.
3405 */
3406 static BOOL FASTCALL MENU_ButtonDown(MTRACKER* pmt, PMENU PtMenu, UINT Flags)
3407 {
3408 TRACE("%x PtMenu=%p\n", pmt, PtMenu);
3409
3410 if (PtMenu)
3411 {
3412 UINT id = 0;
3413 PITEM item;
3414 if (IS_SYSTEM_MENU(PtMenu))
3415 {
3416 item = PtMenu->rgItems;
3417 }
3418 else
3419 {
3420 item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &id );
3421 }
3422
3423 if (item)
3424 {
3425 if (PtMenu->iItem != id)
3426 MENU_SwitchTracking(pmt, PtMenu, id, Flags);
3427
3428 /* If the popup menu is not already "popped" */
3429 if (!(item->fState & MF_MOUSESELECT))
3430 {
3431 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags);
3432 }
3433
3434 return TRUE;
3435 }
3436 /* Else the click was on the menu bar, finish the tracking */
3437 }
3438 return FALSE;
3439 }
3440
3441 /***********************************************************************
3442 * MenuButtonUp
3443 *
3444 * Return the value of MenuExecFocusedItem if
3445 * the selected item was not a popup. Else open the popup.
3446 * A -1 return value indicates that we go on with menu tracking.
3447 *
3448 */
3449 static INT FASTCALL MENU_ButtonUp(MTRACKER *pmt, PMENU PtMenu, UINT Flags)
3450 {
3451 TRACE("%p pmenu=%x\n", pmt, PtMenu);
3452
3453 if (PtMenu)
3454 {
3455 UINT Id = 0;
3456 ITEM *item;
3457
3458 if ( IS_SYSTEM_MENU(PtMenu) )
3459 {
3460 item = PtMenu->rgItems;
3461 }
3462 else
3463 {
3464 item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &Id );
3465 }
3466
3467 if (item && ( PtMenu->iItem == Id))
3468 {
3469 if (!(item->spSubMenu))
3470 {
3471 INT ExecutedMenuId = MENU_ExecFocusedItem( pmt, PtMenu, Flags);
3472 if (ExecutedMenuId == -1 || ExecutedMenuId == -2) return -1;
3473 return ExecutedMenuId;
3474 }
3475
3476 /* If we are dealing with the menu bar */
3477 /* and this is a click on an already "popped" item: */
3478 /* Stop the menu tracking and close the opened submenus */
3479 if (pmt->TopMenu == PtMenu && PtMenu->TimeToHide)
3480 {
3481 return 0;
3482 }
3483 }
3484 if ( IntGetMenu(PtMenu->hWnd) == PtMenu )
3485 {
3486 PtMenu->TimeToHide = TRUE;
3487 }
3488 }
3489 return -1;
3490 }
3491
3492 /***********************************************************************
3493 * MenuPtMenu
3494 *
3495 * Walks menu chain trying to find a menu pt maps to.
3496 */
3497 static PMENU FASTCALL MENU_PtMenu(PMENU menu, POINT pt)
3498 {
3499 PITEM pItem;
3500 PMENU ret = NULL;
3501
3502 if (!menu) return NULL;
3503
3504 /* try subpopup first (if any) */
3505 if (menu->iItem != NO_SELECTED_ITEM)
3506 {
3507 pItem = menu->rgItems;
3508 if ( pItem ) pItem = &pItem[menu->iItem];
3509 if ( pItem && pItem->spSubMenu && pItem->fState & MF_MOUSESELECT)
3510 {
3511 ret = MENU_PtMenu( pItem->spSubMenu, pt);
3512 }
3513 }
3514
3515 /* check the current window (avoiding WM_HITTEST) */
3516 if (!ret)
3517 {
3518 PWND pWnd = ValidateHwndNoErr(menu->hWnd);
3519 INT ht = GetNCHitEx(pWnd, pt);
3520 if ( menu->fFlags & MNF_POPUP )
3521 {
3522 if (ht != HTNOWHERE && ht != HTERROR) ret = menu;
3523 }
3524 else if (ht == HTSYSMENU)
3525 ret = get_win_sys_menu(menu->hWnd);
3526 else if (ht == HTMENU)
3527 ret = IntGetMenu( menu->hWnd );
3528 }
3529 return ret;
3530 }
3531
3532 /***********************************************************************
3533 * MenuMouseMove
3534 *
3535 * Return TRUE if we can go on with menu tracking.
3536 */
3537 static BOOL FASTCALL MENU_MouseMove(MTRACKER *pmt, PMENU PtMenu, UINT Flags)
3538 {
3539 UINT Index = NO_SELECTED_ITEM;
3540
3541 if ( PtMenu )
3542 {
3543 if (IS_SYSTEM_MENU(PtMenu))
3544 {
3545 Index = 0;
3546 //// ReactOS only HACK: CORE-2338
3547 // Windows tracks mouse moves to the system menu but does not open it.
3548 // Only keyboard tracking can do that.
3549 //
3550 TRACE("SystemMenu\n");
3551 return TRUE; // Stay inside the Loop!
3552 }
3553 else
3554 MENU_FindItemByCoords( PtMenu, pmt->Pt, &Index );
3555 }
3556
3557 if (Index == NO_SELECTED_ITEM)
3558 {
3559 MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, NO_SELECTED_ITEM, TRUE, pmt->TopMenu);
3560 }
3561 else if (PtMenu->iItem != Index)
3562 {
3563 MENU_SwitchTracking(pmt, PtMenu, Index, Flags);
3564 pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags);
3565 }
3566 return TRUE;
3567 }
3568
3569 /***********************************************************************
3570 * MenuGetSubPopup
3571 *
3572 * Return the handle of the selected sub-popup menu (if any).
3573 */
3574 static PMENU MENU_GetSubPopup( PMENU menu )
3575 {
3576 ITEM *item;
3577
3578 if ((!menu) || (menu->iItem == NO_SELECTED_ITEM)) return 0;
3579
3580 item = &menu->rgItems[menu->iItem];
3581 if (item && (item->spSubMenu) && (item->fState & MF_MOUSESELECT))
3582 {
3583 return item->spSubMenu;
3584 }
3585 return 0;
3586 }
3587
3588 /***********************************************************************
3589 * MenuDoNextMenu
3590 *
3591 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
3592 */
3593 static LRESULT FASTCALL MENU_DoNextMenu(MTRACKER* pmt, UINT Vk, UINT wFlags)
3594 {
3595 BOOL atEnd = FALSE;
3596
3597 /* When skipping left, we need to do something special after the
3598 first menu. */
3599 if (Vk == VK_LEFT && pmt->TopMenu->iItem == 0)
3600 {
3601 atEnd = TRUE;
3602 }
3603 /* When skipping right, for the non-system menu, we need to
3604 handle the last non-special menu item (ie skip any window
3605 icons such as MDI maximize, restore or close) */
3606 else if ((Vk == VK_RIGHT) && !IS_SYSTEM_MENU(pmt->TopMenu))
3607 {
3608 UINT i = pmt->TopMenu->iItem + 1;
3609 while (i < pmt->TopMenu->cItems) {
3610 if ((pmt->TopMenu->rgItems[i].wID >= SC_SIZE &&
3611 pmt->TopMenu->rgItems[i].wID <= SC_RESTORE)) {
3612 i++;
3613 } else break;
3614 }
3615 if (i == pmt->TopMenu->cItems) {
3616 atEnd = TRUE;
3617 }
3618 }
3619 /* When skipping right, we need to cater for the system menu */
3620 else if ((Vk == VK_RIGHT) && IS_SYSTEM_MENU(pmt->TopMenu))
3621 {
3622 if (pmt->TopMenu->iItem == (pmt->TopMenu->cItems - 1)) {
3623 atEnd = TRUE;
3624 }
3625 }
3626
3627 if ( atEnd )
3628 {
3629 MDINEXTMENU NextMenu;
3630 PMENU MenuTmp;
3631 PWND pwndTemp;
3632 HMENU hNewMenu;
3633 HWND hNewWnd;
3634 UINT Id = 0;
3635
3636 MenuTmp = (IS_SYSTEM_MENU(pmt->TopMenu)) ? co_IntGetSubMenu(pmt->TopMenu, 0) : pmt->TopMenu;
3637 NextMenu.hmenuIn = UserHMGetHandle(MenuTmp);
3638 NextMenu.hmenuNext = NULL;
3639 NextMenu.hwndNext = NULL;
3640 co_IntSendMessage(UserHMGetHandle(pmt->OwnerWnd), WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
3641
3642 TRACE("%p [%p] -> %p [%p]\n",
3643 pmt->CurrentMenu, pmt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
3644
3645 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
3646 {
3647 hNewWnd = UserHMGetHandle(pmt->OwnerWnd);
3648 if (IS_SYSTEM_MENU(pmt->TopMenu))
3649 {
3650 /* switch to the menu bar */
3651
3652 if (pmt->OwnerWnd->style & WS_CHILD || !(MenuTmp = IntGetMenu(hNewWnd))) return FALSE;
3653
3654 if (Vk == VK_LEFT)
3655 {
3656 Id = MenuTmp->cItems - 1;
3657
3658 /* Skip backwards over any system predefined icons,
3659 eg. MDI close, restore etc icons */
3660 while ((Id > 0) &&
3661 (MenuTmp->rgItems[Id].wID >= SC_SIZE &&
3662 MenuTmp->rgItems[Id].wID <= SC_RESTORE)) Id--;
3663
3664 }
3665 hNewMenu = UserHMGetHandle(MenuTmp);
3666 }
3667 else if (pmt->OwnerWnd->style & WS_SYSMENU)
3668 {
3669 /* switch to the system menu */
3670 MenuTmp = get_win_sys_menu(hNewWnd);
3671 if (MenuTmp) hNewMenu = UserHMGetHandle(MenuTmp);
3672 }
3673 else
3674 return FALSE;
3675 }
3676 else /* application returned a new menu to switch to */
3677 {
3678 hNewMenu = NextMenu.hmenuNext;
3679 hNewWnd = NextMenu.hwndNext;
3680
3681 if ((MenuTmp = UserGetMenuObject(hNewMenu)) && (pwndTemp = ValidateHwndNoErr(hNewWnd)))
3682 {
3683 if ( pwndTemp->style & WS_SYSMENU && (get_win_sys_menu(hNewWnd) == MenuTmp) )
3684 {
3685 /* get the real system menu */
3686 MenuTmp = get_win_sys_menu(hNewWnd);
3687 hNewMenu = UserHMGetHandle(MenuTmp);
3688 }
3689 else if (pwndTemp->style & WS_CHILD || IntGetMenu(hNewWnd) != MenuTmp)
3690 {
3691 /* FIXME: Not sure what to do here;
3692 * perhaps try to track NewMenu as a popup? */
3693
3694 WARN(" -- got confused.\n");
3695 return FALSE;
3696 }
3697 }
3698 else return FALSE;
3699 }
3700
3701 if (hNewMenu != UserHMGetHandle(pmt->TopMenu))
3702 {
3703 MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3704
3705 if (pmt->CurrentMenu != pmt->TopMenu)
3706 MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, wFlags);
3707 }
3708
3709 if (hNewWnd != UserHMGetHandle(pmt->OwnerWnd))
3710 {