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