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