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