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