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