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