sync with trunk r46493
[reactos.git] / dll / win32 / user32 / windows / menu.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS user32.dll
4 * FILE: user32/windows/menu.c
5 * PURPOSE: Menus
6 *
7 * PROGRAMMERS: Casper S. Hornstrup
8 * James Tabor
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <user32.h>
14 #include <wine/debug.h>
15
16 LRESULT DefWndNCPaint(HWND hWnd, HRGN hRgn, BOOL Active);
17
18 WINE_DEFAULT_DEBUG_CHANNEL(menu);
19
20 /* internal popup menu window messages */
21
22 #define MM_SETMENUHANDLE (WM_USER + 0)
23 #define MM_GETMENUHANDLE (WM_USER + 1)
24
25 /* internal flags for menu tracking */
26
27 #define TF_ENDMENU 0x10000
28 #define TF_SUSPENDPOPUP 0x20000
29 #define TF_SKIPREMOVE 0x40000
30
31 #define ITEM_PREV -1
32 #define ITEM_NEXT 1
33
34 /* Internal MenuTrackMenu() flags */
35 #define TPM_INTERNAL 0xF0000000
36 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
37 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
38
39
40 #define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)
41
42 #define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK)
43
44 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
45 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
46 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
47
48 #define IS_SYSTEM_MENU(MenuInfo) \
49 (0 == ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
50
51 #define IS_SYSTEM_POPUP(MenuInfo) \
52 (0 != ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
53
54 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
55
56 /* Use global popup window because there's no way 2 menus can
57 * be tracked at the same time. */
58 static HWND top_popup;
59 static HMENU top_popup_hmenu;
60
61 /* Flag set by EndMenu() to force an exit from menu tracking */
62 static BOOL fEndMenu = FALSE;
63
64 #define MENU_ITEM_HBMP_SPACE (5)
65 #define MENU_BAR_ITEMS_SPACE (12)
66 #define SEPARATOR_HEIGHT (5)
67 #define MENU_TAB_SPACE (8)
68
69 #define MAKEINTATOMA(atom) ((LPCSTR)((ULONG_PTR)((WORD)(atom))))
70 #define MAKEINTATOMW(atom) ((LPCWSTR)((ULONG_PTR)((WORD)(atom))))
71 #define POPUPMENU_CLASS_ATOMA MAKEINTATOMA(32768) /* PopupMenu */
72 #define POPUPMENU_CLASS_ATOMW MAKEINTATOMW(32768) /* PopupMenu */
73
74 typedef struct
75 {
76 UINT TrackFlags;
77 HMENU CurrentMenu; /* current submenu (can be equal to hTopMenu)*/
78 HMENU TopMenu; /* initial menu */
79 HWND OwnerWnd; /* where notifications are sent */
80 POINT Pt;
81 } MTRACKER;
82
83
84 /*********************************************************************
85 * PopupMenu class descriptor
86 */
87 const struct builtin_class_descr POPUPMENU_builtin_class =
88 {
89 POPUPMENU_CLASS_ATOMW, /* name */
90 CS_SAVEBITS | CS_DBLCLKS, /* style */
91 (WNDPROC) NULL, /* FIXME - procA */
92 (WNDPROC) PopupMenuWndProcW, /* FIXME - procW */
93 sizeof(MENUINFO *), /* extra */
94 (LPCWSTR) IDC_ARROW, /* cursor */
95 (HBRUSH)(COLOR_MENU + 1) /* brush */
96 };
97
98 #ifndef GET_WORD
99 #define GET_WORD(ptr) (*(WORD *)(ptr))
100 #endif
101 #ifndef GET_DWORD
102 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
103 #endif
104
105 HFONT hMenuFont = NULL;
106 HFONT hMenuFontBold = NULL;
107
108 /* Dimension of the menu bitmaps */
109 static HBITMAP BmpSysMenu = NULL;
110
111 static SIZE MenuCharSize;
112
113 /***********************************************************************
114 * MenuGetRosMenuInfo
115 *
116 * Get full information about menu
117 */
118 static BOOL FASTCALL
119 MenuGetRosMenuInfo(PROSMENUINFO MenuInfo, HMENU Menu)
120 {
121 MenuInfo->cbSize = sizeof(ROSMENUINFO);
122 MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE;
123
124 return NtUserMenuInfo(Menu, MenuInfo, FALSE);
125 }
126
127 /***********************************************************************
128 * MenuSetRosMenuInfo
129 *
130 * Set full information about menu
131 */
132 static BOOL FASTCALL
133 MenuSetRosMenuInfo(PROSMENUINFO MenuInfo)
134 {
135 MenuInfo->cbSize = sizeof(ROSMENUINFO);
136 MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE;
137
138 return NtUserMenuInfo(MenuInfo->Self, MenuInfo, TRUE);
139 }
140
141 /***********************************************************************
142 * MenuInitRosMenuItemInfo
143 *
144 * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo
145 */
146 static VOID FASTCALL
147 MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
148 {
149 ZeroMemory(ItemInfo, sizeof(ROSMENUITEMINFO));
150 ItemInfo->cbSize = sizeof(ROSMENUITEMINFO);
151 }
152
153 /***********************************************************************
154 * MenuGetRosMenuItemInfo
155 *
156 * Get full information about a menu item
157 */
158 static BOOL FASTCALL
159 MenuGetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo)
160 {
161 UINT Save_Mask = ItemInfo->fMask; /* Save the org mask bits. */
162
163 if (ItemInfo->dwTypeData != NULL)
164 {
165 HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData);
166 }
167
168
169 ItemInfo->fMask = MIIM_BITMAP | MIIM_CHECKMARKS | MIIM_DATA | MIIM_FTYPE
170 | MIIM_ID | MIIM_STATE | MIIM_STRING | MIIM_SUBMENU | MIIM_TYPE;
171 ItemInfo->dwTypeData = NULL;
172
173 if (! NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, FALSE))
174 {
175 ItemInfo->fType = 0;
176 return FALSE;
177 }
178
179 if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING)
180 {
181 ItemInfo->cch++;
182 ItemInfo->dwTypeData = HeapAlloc(GetProcessHeap(), 0,
183 ItemInfo->cch * sizeof(WCHAR));
184 if (NULL == ItemInfo->dwTypeData)
185 {
186 return FALSE;
187 }
188
189 if (! NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, FALSE))
190 {
191 ItemInfo->fType = 0;
192 return FALSE;
193 }
194 ItemInfo->dwTypeData[ItemInfo->cch - 1] = UNICODE_NULL;
195 }
196 ItemInfo->fMask = Save_Mask;
197 return TRUE;
198 }
199
200 /***********************************************************************
201 * MenuSetRosMenuItemInfo
202 *
203 * Set selected information about a menu item, need to set the mask bits.
204 */
205 static BOOL FASTCALL
206 MenuSetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo)
207 {
208 BOOL Ret;
209
210 if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING &&
211 ItemInfo->dwTypeData != NULL)
212 {
213 ItemInfo->cch = strlenW(ItemInfo->dwTypeData);
214 }
215 Ret = NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, TRUE);
216
217 return Ret;
218 }
219
220 /***********************************************************************
221 * MenuCleanupRosMenuItemInfo
222 *
223 * Cleanup after use of MenuGet/SetRosMenuItemInfo
224 */
225 static VOID FASTCALL
226 MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
227 {
228 if (ItemInfo->dwTypeData != NULL)
229 {
230 HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData);
231 ItemInfo->dwTypeData = NULL;
232 }
233 }
234
235 /***********************************************************************
236 * MenuGetAllRosMenuItemInfo
237 *
238 * Get full information about all menu items
239 */
240 static INT FASTCALL
241 MenuGetAllRosMenuItemInfo(HMENU Menu, PROSMENUITEMINFO *ItemInfo)
242 {
243 DWORD BufSize;
244
245 BufSize = NtUserBuildMenuItemList(Menu, (VOID *) 1, 0, 0);
246 if (BufSize == (DWORD) -1 || BufSize == 0)
247 {
248 return -1;
249 }
250 *ItemInfo = HeapAlloc(GetProcessHeap(), 0, BufSize);
251 if (NULL == *ItemInfo)
252 {
253 return -1;
254 }
255
256 return NtUserBuildMenuItemList(Menu, *ItemInfo, BufSize, 0);
257 }
258
259 /***********************************************************************
260 * MenuCleanupAllRosMenuItemInfo
261 *
262 * Cleanup after use of MenuGetAllRosMenuItemInfo
263 */
264 static VOID FASTCALL
265 MenuCleanupAllRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
266 {
267 HeapFree(GetProcessHeap(), 0, ItemInfo);
268 }
269
270 /***********************************************************************
271 * MenuInitSysMenuPopup
272 *
273 * Grey the appropriate items in System menu.
274 */
275 void FASTCALL MenuInitSysMenuPopup(HMENU hmenu, DWORD style, DWORD clsStyle, LONG HitTest )
276 {
277 BOOL gray;
278 UINT DefItem;
279 #if 0
280 MENUITEMINFOW mii;
281 #endif
282
283 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
284 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
285 gray = ((style & WS_MAXIMIZE) != 0);
286 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
287 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
288 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
289 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
290 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
291 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
292 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
293 gray = (clsStyle & CS_NOCLOSE) != 0;
294
295 /* The menu item must keep its state if it's disabled */
296 if(gray)
297 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
298
299 /* Set default menu item */
300 if(style & WS_MINIMIZE) DefItem = SC_RESTORE;
301 else if(HitTest == HTCAPTION) DefItem = ((style & (WS_MAXIMIZE | WS_MINIMIZE)) ? SC_RESTORE : SC_MAXIMIZE);
302 else DefItem = SC_CLOSE;
303 #if 0
304 mii.cbSize = sizeof(MENUITEMINFOW);
305 mii.fMask |= MIIM_STATE;
306 if((DefItem != SC_CLOSE) && GetMenuItemInfoW(hmenu, DefItem, FALSE, &mii) &&
307 (mii.fState & (MFS_GRAYED | MFS_DISABLED))) DefItem = SC_CLOSE;
308 #endif
309 SetMenuDefaultItem(hmenu, DefItem, MF_BYCOMMAND);
310 }
311
312 /******************************************************************************
313 *
314 * UINT MenuGetStartOfNextColumn(
315 * PROSMENUINFO MenuInfo)
316 *
317 *****************************************************************************/
318 static UINT MenuGetStartOfNextColumn(
319 PROSMENUINFO MenuInfo)
320 {
321 PROSMENUITEMINFO MenuItems;
322 UINT i;
323
324 i = MenuInfo->FocusedItem;
325 if ( i == NO_SELECTED_ITEM )
326 return i;
327
328 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0)
329 return NO_SELECTED_ITEM;
330
331 for (i++ ; i < MenuInfo->MenuItemCount; i++)
332 if (0 != (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)))
333 return i;
334
335 return NO_SELECTED_ITEM;
336 }
337
338 /******************************************************************************
339 *
340 * UINT MenuGetStartOfPrevColumn(
341 * PROSMENUINFO MenuInfo)
342 *
343 *****************************************************************************/
344
345 static UINT FASTCALL MenuGetStartOfPrevColumn(
346 PROSMENUINFO MenuInfo)
347 {
348 PROSMENUITEMINFO MenuItems;
349 UINT i;
350
351 if (!MenuInfo->FocusedItem || MenuInfo->FocusedItem == NO_SELECTED_ITEM)
352 return NO_SELECTED_ITEM;
353
354 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0)
355 return NO_SELECTED_ITEM;
356
357 /* Find the start of the column */
358 for (i = MenuInfo->FocusedItem;
359 0 != i && 0 == (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
360 --i)
361 {
362 ; /* empty */
363 }
364
365 if (i == 0)
366 {
367 MenuCleanupAllRosMenuItemInfo(MenuItems);
368 return NO_SELECTED_ITEM;
369 }
370
371 for (--i; 0 != i; --i)
372 if (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
373 break;
374
375 MenuCleanupAllRosMenuItemInfo(MenuItems);
376 TRACE("ret %d.\n", i );
377
378 return i;
379 }
380
381 /***********************************************************************
382 * MenuFindSubMenu
383 *
384 * Find a Sub menu. Return the position of the submenu, and modifies
385 * *hmenu in case it is found in another sub-menu.
386 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
387 */
388 static UINT FASTCALL MenuFindSubMenu(HMENU *hmenu, HMENU hSubTarget )
389 {
390 ROSMENUINFO menu;
391 UINT i;
392 ROSMENUITEMINFO item;
393 if (((*hmenu)==(HMENU)0xffff) ||
394 (!MenuGetRosMenuInfo(&menu, *hmenu)))
395 return NO_SELECTED_ITEM;
396
397 MenuInitRosMenuItemInfo(&item);
398 for (i = 0; i < menu.MenuItemCount; i++) {
399 if (! MenuGetRosMenuItemInfo(menu.Self, i, &item))
400 {
401 MenuCleanupRosMenuItemInfo(&item);
402 return NO_SELECTED_ITEM;
403 }
404 if (!(item.fType & MF_POPUP)) continue;
405 if (item.hSubMenu == hSubTarget) {
406 MenuCleanupRosMenuItemInfo(&item);
407 return i;
408 }
409 else {
410 HMENU hsubmenu = item.hSubMenu;
411 UINT pos = MenuFindSubMenu(&hsubmenu, hSubTarget );
412 if (pos != NO_SELECTED_ITEM) {
413 *hmenu = hsubmenu;
414 return pos;
415 }
416 }
417 }
418 MenuCleanupRosMenuItemInfo(&item);
419 return NO_SELECTED_ITEM;
420 }
421
422 /***********************************************************************
423 * MenuLoadBitmaps
424 *
425 * Load the arrow bitmap. We can't do this from MenuInit since user32
426 * can also be used (and thus initialized) from text-mode.
427 */
428 static void FASTCALL
429 MenuLoadBitmaps(VOID)
430 {
431 /* Load system buttons bitmaps */
432 if (NULL == BmpSysMenu)
433 {
434 BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
435 }
436 }
437
438 /***********************************************************************
439 * MenuDrawPopupGlyph
440 *
441 * Draws popup magic glyphs (can be found in system menu).
442 */
443 static void FASTCALL
444 MenuDrawPopupGlyph(HDC dc, LPRECT r, INT_PTR popupMagic, BOOL inactive, BOOL hilite)
445 {
446 LOGFONTW lf;
447 HFONT hFont, hOldFont;
448 COLORREF clrsave;
449 INT bkmode;
450 TCHAR symbol;
451 switch (popupMagic)
452 {
453 case (INT_PTR) HBMMENU_POPUP_RESTORE:
454 symbol = '2';
455 break;
456 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
457 symbol = '0';
458 break;
459 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
460 symbol = '1';
461 break;
462 case (INT_PTR) HBMMENU_POPUP_CLOSE:
463 symbol = 'r';
464 break;
465 default:
466 ERR("Invalid popup magic bitmap %d\n", (int)popupMagic);
467 return;
468 }
469 ZeroMemory(&lf, sizeof(LOGFONTW));
470 InflateRect(r, -2, -2);
471 lf.lfHeight = r->bottom - r->top;
472 lf.lfWidth = 0;
473 lf.lfWeight = FW_NORMAL;
474 lf.lfCharSet = DEFAULT_CHARSET;
475 lstrcpy(lf.lfFaceName, TEXT("Marlett"));
476 hFont = CreateFontIndirect(&lf);
477 /* save font and text color */
478 hOldFont = SelectObject(dc, hFont);
479 clrsave = GetTextColor(dc);
480 bkmode = GetBkMode(dc);
481 /* set color and drawing mode */
482 SetBkMode(dc, TRANSPARENT);
483 if (inactive)
484 {
485 /* draw shadow */
486 if (!hilite)
487 {
488 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
489 TextOut(dc, r->left + 1, r->top + 1, &symbol, 1);
490 }
491 }
492 SetTextColor(dc, GetSysColor(inactive ? COLOR_GRAYTEXT : (hilite ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT)));
493 /* draw selected symbol */
494 TextOut(dc, r->left, r->top, &symbol, 1);
495 /* restore previous settings */
496 SetTextColor(dc, clrsave);
497 SelectObject(dc, hOldFont);
498 SetBkMode(dc, bkmode);
499 DeleteObject(hFont);
500 }
501
502 /***********************************************************************
503 * MenuFindItemByKey
504 *
505 * Find the menu item selected by a key press.
506 * Return item id, -1 if none, -2 if we should close the menu.
507 */
508 static UINT FASTCALL MenuFindItemByKey(HWND WndOwner, PROSMENUINFO MenuInfo,
509 WCHAR Key, BOOL ForceMenuChar)
510 {
511 ROSMENUINFO SysMenuInfo;
512 PROSMENUITEMINFO Items, ItemInfo;
513 LRESULT MenuChar;
514 UINT i;
515
516 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char) Key, Key, MenuInfo);
517
518 if (NULL == MenuInfo || ! IsMenu(MenuInfo->Self))
519 {
520 if (MenuGetRosMenuInfo(&SysMenuInfo, GetSystemMenu(WndOwner, FALSE)))
521 {
522 MenuInfo = &SysMenuInfo;
523 }
524 else
525 {
526 MenuInfo = NULL;
527 }
528 }
529
530 if (NULL != MenuInfo)
531 {
532 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &Items) <= 0)
533 {
534 return -1;
535 }
536 if (! ForceMenuChar)
537 {
538 Key = toupperW(Key);
539 ItemInfo = Items;
540 for (i = 0; i < MenuInfo->MenuItemCount; i++, ItemInfo++)
541 {
542 if ((ItemInfo->lpstr) && NULL != ItemInfo->dwTypeData)
543 {
544 WCHAR *p = (WCHAR *) ItemInfo->dwTypeData - 2;
545 do
546 {
547 p = strchrW(p + 2, '&');
548 }
549 while (NULL != p && L'&' == p[1]);
550 if (NULL != p && (toupperW(p[1]) == Key))
551 {
552 return i;
553 }
554 }
555 }
556 }
557
558 MenuChar = SendMessageW(WndOwner, WM_MENUCHAR,
559 MAKEWPARAM(Key, MenuInfo->Flags), (LPARAM) MenuInfo->Self);
560 if (2 == HIWORD(MenuChar))
561 {
562 return LOWORD(MenuChar);
563 }
564 if (1 == HIWORD(MenuChar))
565 {
566 return (UINT) (-2);
567 }
568 }
569
570 return (UINT)(-1);
571 }
572
573 /***********************************************************************
574 * MenuGetBitmapItemSize
575 *
576 * Get the size of a bitmap item.
577 */
578 static void FASTCALL MenuGetBitmapItemSize(PROSMENUITEMINFO lpitem, SIZE *size, HWND WndOwner)
579 {
580 BITMAP bm;
581 HBITMAP bmp = lpitem->hbmpItem;
582
583 size->cx = size->cy = 0;
584
585 /* check if there is a magic menu item associated with this item */
586 if (IS_MAGIC_BITMAP(bmp))
587 {
588 switch((INT_PTR) bmp)
589 {
590 case (INT_PTR)HBMMENU_CALLBACK:
591 {
592 MEASUREITEMSTRUCT measItem;
593 measItem.CtlType = ODT_MENU;
594 measItem.CtlID = 0;
595 measItem.itemID = lpitem->wID;
596 measItem.itemWidth = lpitem->Rect.right - lpitem->Rect.left;
597 measItem.itemHeight = lpitem->Rect.bottom - lpitem->Rect.top;
598 measItem.itemData = lpitem->dwItemData;
599 SendMessageW( WndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
600 size->cx = measItem.itemWidth;
601 size->cy = measItem.itemHeight;
602 return;
603 }
604 break;
605
606 case (INT_PTR) HBMMENU_SYSTEM:
607 if (0 != lpitem->dwItemData)
608 {
609 bmp = (HBITMAP) lpitem->dwItemData;
610 break;
611 }
612 /* fall through */
613 case (INT_PTR) HBMMENU_MBAR_RESTORE:
614 case (INT_PTR) HBMMENU_MBAR_MINIMIZE:
615 case (INT_PTR) HBMMENU_MBAR_CLOSE:
616 case (INT_PTR) HBMMENU_MBAR_MINIMIZE_D:
617 case (INT_PTR) HBMMENU_MBAR_CLOSE_D:
618 case (INT_PTR) HBMMENU_POPUP_CLOSE:
619 case (INT_PTR) HBMMENU_POPUP_RESTORE:
620 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
621 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
622 /* FIXME: Why we need to subtract these magic values? */
623 /* to make them smaller than the menu bar? */
624 size->cx = GetSystemMetrics(SM_CXSIZE) - 2;
625 size->cy = GetSystemMetrics(SM_CYSIZE) - 4;
626 return;
627 }
628 }
629
630 if (GetObjectW(bmp, sizeof(BITMAP), &bm))
631 {
632 size->cx = bm.bmWidth;
633 size->cy = bm.bmHeight;
634 }
635 }
636
637 /***********************************************************************
638 * MenuDrawBitmapItem
639 *
640 * Draw a bitmap item.
641 */
642 static void FASTCALL MenuDrawBitmapItem(HDC hdc, PROSMENUITEMINFO lpitem, const RECT *rect,
643 HMENU hmenu, HWND WndOwner, UINT odaction, BOOL MenuBar)
644 {
645 BITMAP bm;
646 DWORD rop;
647 HDC hdcMem;
648 HBITMAP bmp;
649 int w = rect->right - rect->left;
650 int h = rect->bottom - rect->top;
651 int bmp_xoffset = 0;
652 int left, top;
653 HBITMAP hbmToDraw = lpitem->hbmpItem;
654 bmp = hbmToDraw;
655
656 /* Check if there is a magic menu item associated with this item */
657 if (IS_MAGIC_BITMAP(hbmToDraw))
658 {
659 UINT flags = 0;
660 RECT r;
661
662 r = *rect;
663 switch ((INT_PTR)hbmToDraw)
664 {
665 case (INT_PTR)HBMMENU_SYSTEM:
666 if (lpitem->dwTypeData)
667 {
668 bmp = (HBITMAP)lpitem->dwTypeData;
669 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
670 }
671 else
672 {
673 if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
674 bmp = BmpSysMenu;
675 if (! GetObjectW(bmp, sizeof(bm), &bm)) return;
676 /* only use right half of the bitmap */
677 bmp_xoffset = bm.bmWidth / 2;
678 bm.bmWidth -= bmp_xoffset;
679 }
680 goto got_bitmap;
681 case (INT_PTR)HBMMENU_MBAR_RESTORE:
682 flags = DFCS_CAPTIONRESTORE;
683 break;
684 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
685 r.right += 1;
686 flags = DFCS_CAPTIONMIN;
687 break;
688 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
689 r.right += 1;
690 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
691 break;
692 case (INT_PTR)HBMMENU_MBAR_CLOSE:
693 flags = DFCS_CAPTIONCLOSE;
694 break;
695 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
696 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
697 break;
698 case (INT_PTR)HBMMENU_CALLBACK:
699 {
700 DRAWITEMSTRUCT drawItem;
701 POINT origorg;
702 drawItem.CtlType = ODT_MENU;
703 drawItem.CtlID = 0;
704 drawItem.itemID = lpitem->wID;
705 drawItem.itemAction = odaction;
706 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
707 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
708 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
709 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
710 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
711 drawItem.hwndItem = (HWND)hmenu;
712 drawItem.hDC = hdc;
713 drawItem.rcItem = *rect;
714 drawItem.itemData = lpitem->dwItemData;
715 /* some applications make this assumption on the DC's origin */
716 SetViewportOrgEx( hdc, lpitem->Rect.left, lpitem->Rect.top, &origorg);
717 OffsetRect( &drawItem.rcItem, - lpitem->Rect.left, - lpitem->Rect.top);
718 SendMessageW( WndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
719 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
720 return;
721 }
722 break;
723
724 case (INT_PTR) HBMMENU_POPUP_CLOSE:
725 case (INT_PTR) HBMMENU_POPUP_RESTORE:
726 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
727 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
728 MenuDrawPopupGlyph(hdc, &r, (INT_PTR)hbmToDraw, lpitem->fState & MF_GRAYED, lpitem->fState & MF_HILITE);
729 return;
730 }
731 InflateRect(&r, -1, -1);
732 if (0 != (lpitem->fState & MF_HILITE))
733 {
734 flags |= DFCS_PUSHED;
735 }
736 DrawFrameControl(hdc, &r, DFC_CAPTION, flags);
737 return;
738 }
739
740 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
741
742 got_bitmap:
743 hdcMem = CreateCompatibleDC( hdc );
744 SelectObject( hdcMem, bmp );
745
746 /* handle fontsize > bitmap_height */
747 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
748 left=rect->left;
749 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
750 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
751 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
752 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
753 DeleteDC( hdcMem );
754 }
755
756 /***********************************************************************
757 * MenuCalcItemSize
758 *
759 * Calculate the size of the menu item and store it in lpitem->rect.
760 */
761 static void FASTCALL MenuCalcItemSize( HDC hdc, PROSMENUITEMINFO lpitem, PROSMENUINFO MenuInfo, HWND hwndOwner,
762 INT orgX, INT orgY, BOOL menuBar)
763 {
764 WCHAR *p;
765 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
766 INT itemheight = 0;
767
768 TRACE("dc=%x owner=%x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
769
770 MenuCharSize.cx = GdiGetCharDimensions( hdc, NULL, &MenuCharSize.cy );
771
772 SetRect( &lpitem->Rect, orgX, orgY, orgX, orgY );
773
774 if (lpitem->fType & MF_OWNERDRAW)
775 {
776 MEASUREITEMSTRUCT mis;
777 mis.CtlType = ODT_MENU;
778 mis.CtlID = 0;
779 mis.itemID = lpitem->wID;
780 mis.itemData = lpitem->dwItemData;
781 mis.itemHeight = HIWORD( GetDialogBaseUnits());
782 mis.itemWidth = 0;
783 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
784 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
785 * width of a menufont character to the width of an owner-drawn menu.
786 */
787 lpitem->Rect.right += mis.itemWidth + 2 * MenuCharSize.cx;
788
789 if (menuBar) {
790 /* under at least win95 you seem to be given a standard
791 height for the menu and the height value is ignored */
792 lpitem->Rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
793 } else
794 lpitem->Rect.bottom += mis.itemHeight;
795
796 TRACE("id=%04lx size=%dx%d\n",
797 lpitem->wID, mis.itemWidth, mis.itemHeight);
798 return;
799 }
800
801 if (lpitem->fType & MF_SEPARATOR)
802 {
803 lpitem->Rect.bottom += SEPARATOR_HEIGHT;
804 if( !menuBar)
805 lpitem->Rect.right += check_bitmap_width + MenuCharSize.cx;
806 return;
807 }
808
809 lpitem->dxTab = 0;
810
811 if (lpitem->hbmpItem)
812 {
813 SIZE size;
814
815 if (!menuBar) {
816 MenuGetBitmapItemSize(lpitem, &size, hwndOwner );
817 /* Keep the size of the bitmap in callback mode to be able
818 * to draw it correctly */
819 lpitem->Rect.right = lpitem->Rect.left + size.cx;
820 if (MenuInfo->maxBmpSize.cx < abs(size.cx) + MENU_ITEM_HBMP_SPACE ||
821 MenuInfo->maxBmpSize.cy < abs(size.cy))
822 {
823 MenuInfo->maxBmpSize.cx = abs(size.cx) + MENU_ITEM_HBMP_SPACE;
824 MenuInfo->maxBmpSize.cy = abs(size.cy);
825 }
826 MenuSetRosMenuInfo(MenuInfo);
827 itemheight = size.cy + 2;
828
829 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
830 lpitem->Rect.right += 2 * check_bitmap_width;
831 lpitem->Rect.right += 4 + MenuCharSize.cx;
832 lpitem->dxTab = lpitem->Rect.right;
833 lpitem->Rect.right += check_bitmap_width;
834 } else /* hbmpItem & MenuBar */ {
835 MenuGetBitmapItemSize(lpitem, &size, hwndOwner );
836 lpitem->Rect.right += size.cx;
837 if( lpitem->lpstr) lpitem->Rect.right += 2;
838 itemheight = size.cy;
839
840 /* Special case: Minimize button doesn't have a space behind it. */
841 if (lpitem->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE ||
842 lpitem->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE_D)
843 lpitem->Rect.right -= 1;
844 }
845 }
846 else if (!menuBar) {
847 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
848 lpitem->Rect.right += check_bitmap_width;
849 lpitem->Rect.right += 4 + MenuCharSize.cx;
850 lpitem->dxTab = lpitem->Rect.right;
851 lpitem->Rect.right += check_bitmap_width;
852 }
853
854 /* it must be a text item - unless it's the system menu */
855 if (!(lpitem->fType & MF_SYSMENU) && lpitem->lpstr) {
856 HFONT hfontOld = NULL;
857 RECT rc = lpitem->Rect;
858 LONG txtheight, txtwidth;
859
860 if ( lpitem->fState & MFS_DEFAULT ) {
861 hfontOld = SelectObject( hdc, hMenuFontBold );
862 }
863 if (menuBar) {
864 txtheight = DrawTextW( hdc, lpitem->dwTypeData, -1, &rc,
865 DT_SINGLELINE|DT_CALCRECT);
866 lpitem->Rect.right += rc.right - rc.left;
867 itemheight = max( max( itemheight, txtheight),
868 GetSystemMetrics( SM_CYMENU) - 1);
869 lpitem->Rect.right += 2 * MenuCharSize.cx;
870 } else {
871 if ((p = strchrW( lpitem->dwTypeData, '\t' )) != NULL) {
872 RECT tmprc = rc;
873 LONG tmpheight;
874 int n = (int)( p - lpitem->dwTypeData);
875 /* Item contains a tab (only meaningful in popup menus) */
876 /* get text size before the tab */
877 txtheight = DrawTextW( hdc, lpitem->dwTypeData, n, &rc,
878 DT_SINGLELINE|DT_CALCRECT);
879 txtwidth = rc.right - rc.left;
880 p += 1; /* advance past the Tab */
881 /* get text size after the tab */
882 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
883 DT_SINGLELINE|DT_CALCRECT);
884 lpitem->dxTab += txtwidth;
885 txtheight = max( txtheight, tmpheight);
886 txtwidth += MenuCharSize.cx + /* space for the tab */
887 tmprc.right - tmprc.left; /* space for the short cut */
888 } else {
889 txtheight = DrawTextW( hdc, lpitem->dwTypeData, -1, &rc,
890 DT_SINGLELINE|DT_CALCRECT);
891 txtwidth = rc.right - rc.left;
892 lpitem->dxTab += txtwidth;
893 }
894 lpitem->Rect.right += 2 + txtwidth;
895 itemheight = max( itemheight,
896 max( txtheight + 2, MenuCharSize.cy + 4));
897 }
898 if (hfontOld) SelectObject (hdc, hfontOld);
899 } else if( menuBar) {
900 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
901 }
902 lpitem->Rect.bottom += itemheight;
903 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->Rect.left, lpitem->Rect.top, lpitem->Rect.right, lpitem->Rect.bottom);
904 }
905
906 /***********************************************************************
907 * MenuPopupMenuCalcSize
908 *
909 * Calculate the size of a popup menu.
910 */
911 static void FASTCALL MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo, HWND WndOwner)
912 {
913 ROSMENUITEMINFO lpitem;
914 HDC hdc;
915 int start, i;
916 int orgX, orgY, maxX, maxTab, maxTabWidth;
917
918 MenuInfo->Width = MenuInfo->Height = 0;
919 if (MenuInfo->MenuItemCount == 0)
920 {
921 MenuSetRosMenuInfo(MenuInfo);
922 return;
923 }
924
925 hdc = GetDC(NULL);
926 SelectObject( hdc, hMenuFont );
927
928 start = 0;
929 maxX = 2 + 1;
930
931 MenuInfo->maxBmpSize.cx = 0;
932 MenuInfo->maxBmpSize.cy = 0;
933
934 MenuInitRosMenuItemInfo(&lpitem);
935 while (start < MenuInfo->MenuItemCount)
936 {
937 orgX = maxX;
938 orgY = 2;
939
940 maxTab = maxTabWidth = 0;
941
942 /* Parse items until column break or end of menu */
943 for (i = start; i < MenuInfo->MenuItemCount; i++)
944 {
945 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i, &lpitem))
946 {
947 MenuCleanupRosMenuItemInfo(&lpitem);
948 MenuSetRosMenuInfo(MenuInfo);
949 return;
950 }
951 if (i != start &&
952 (lpitem.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
953
954 MenuCalcItemSize(hdc, &lpitem, MenuInfo, WndOwner, orgX, orgY, FALSE);
955 if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &lpitem))
956 {
957 MenuCleanupRosMenuItemInfo(&lpitem);
958 MenuSetRosMenuInfo(MenuInfo);
959 return;
960 }
961 // Not sure here,, The patch from wine removes this.
962 // if ((lpitem.fType & MF_MENUBARBREAK) != 0)
963 // {
964 // OrgX++;
965 // }
966 maxX = max(maxX, lpitem.Rect.right);
967 orgY = lpitem.Rect.bottom;
968 if ((lpitem.lpstr) && lpitem.dxTab )
969 {
970 maxTab = max( maxTab, lpitem.dxTab );
971 maxTabWidth = max(maxTabWidth, lpitem.Rect.right - lpitem.dxTab);
972 }
973 }
974
975 /* Finish the column (set all items to the largest width found) */
976 maxX = max( maxX, maxTab + maxTabWidth );
977 while (start < i)
978 {
979 if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &lpitem))
980 {
981 lpitem.Rect.right = maxX;
982 if ((lpitem.lpstr) && 0 != lpitem.dxTab)
983 {
984 lpitem.dxTab = maxTab;
985 }
986 MenuSetRosMenuItemInfo(MenuInfo->Self, start, &lpitem);
987 }
988 start++;
989 }
990 MenuInfo->Height = max(MenuInfo->Height, orgY);
991 }
992
993 MenuInfo->Width = maxX;
994
995 /* space for 3d border */
996 MenuInfo->Height += 2;
997 MenuInfo->Width += 2;
998
999 MenuCleanupRosMenuItemInfo(&lpitem);
1000 MenuSetRosMenuInfo(MenuInfo);
1001 ReleaseDC( 0, hdc );
1002 }
1003
1004 /***********************************************************************
1005 * MenuMenuBarCalcSize
1006 *
1007 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1008 * height is off by 1 pixel which causes lengthy window relocations when
1009 * active document window is maximized/restored.
1010 *
1011 * Calculate the size of the menu bar.
1012 */
1013 static void FASTCALL MenuMenuBarCalcSize( HDC hdc, LPRECT lprect,
1014 PROSMENUINFO MenuInfo, HWND hwndOwner )
1015 {
1016 ROSMENUITEMINFO ItemInfo;
1017 int start, i, orgX, orgY, maxY, helpPos;
1018
1019 if ((lprect == NULL) || (MenuInfo == NULL)) return;
1020 if (MenuInfo->MenuItemCount == 0) return;
1021 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n", lprect->left, lprect->top, lprect->right, lprect->bottom);
1022 MenuInfo->Width = lprect->right - lprect->left;
1023 MenuInfo->Height = 0;
1024 maxY = lprect->top + 1;
1025 start = 0;
1026 helpPos = -1;
1027
1028 MenuInfo->maxBmpSize.cx = 0;
1029 MenuInfo->maxBmpSize.cy = 0;
1030
1031 MenuInitRosMenuItemInfo(&ItemInfo);
1032 while (start < MenuInfo->MenuItemCount)
1033 {
1034 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo))
1035 {
1036 MenuCleanupRosMenuItemInfo(&ItemInfo);
1037 return;
1038 }
1039 orgX = lprect->left;
1040 orgY = maxY;
1041
1042 /* Parse items until line break or end of menu */
1043 for (i = start; i < MenuInfo->MenuItemCount; i++)
1044 {
1045 if ((helpPos == -1) && (ItemInfo.fType & MF_RIGHTJUSTIFY)) helpPos = i;
1046 if ((i != start) &&
1047 (ItemInfo.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1048
1049 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY);
1050 MenuCalcItemSize(hdc, &ItemInfo, MenuInfo, hwndOwner, orgX, orgY, TRUE);
1051 if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo))
1052 {
1053 MenuCleanupRosMenuItemInfo(&ItemInfo);
1054 return;
1055 }
1056
1057 if (ItemInfo.Rect.right > lprect->right)
1058 {
1059 if (i != start) break;
1060 else ItemInfo.Rect.right = lprect->right;
1061 }
1062 maxY = max( maxY, ItemInfo.Rect.bottom );
1063 orgX = ItemInfo.Rect.right;
1064 if (i + 1 < MenuInfo->MenuItemCount)
1065 {
1066 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i + 1, &ItemInfo))
1067 {
1068 MenuCleanupRosMenuItemInfo(&ItemInfo);
1069 return;
1070 }
1071 }
1072 }
1073
1074 /* FIXME: Is this really needed? */ /*NO! it is not needed, why make the
1075 HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
1076 #if 0
1077 /* Finish the line (set all items to the largest height found) */
1078 while (start < i)
1079 {
1080 if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo))
1081 {
1082 ItemInfo.Rect.bottom = maxY;
1083 MenuSetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo);
1084 }
1085 start++;
1086 }
1087 #else
1088 start = i; /* This works! */
1089 #endif
1090 }
1091
1092 lprect->bottom = maxY;
1093 MenuInfo->Height = lprect->bottom - lprect->top;
1094 MenuSetRosMenuInfo(MenuInfo);
1095
1096 if (helpPos != -1)
1097 {
1098 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1099 /* the last item (if several lines, only move the last line) */
1100 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->MenuItemCount - 1, &ItemInfo))
1101 {
1102 MenuCleanupRosMenuItemInfo(&ItemInfo);
1103 return;
1104 }
1105 orgY = ItemInfo.Rect.top;
1106 orgX = lprect->right;
1107 for (i = MenuInfo->MenuItemCount - 1; helpPos <= i; i--)
1108 {
1109 if (i < helpPos)
1110 {
1111 break; /* done */
1112 }
1113 if (ItemInfo.Rect.top != orgY)
1114 {
1115 break; /* Other line */
1116 }
1117 if (orgX <= ItemInfo.Rect.right)
1118 {
1119 break; /* Too far right already */
1120 }
1121 ItemInfo.Rect.left += orgX - ItemInfo.Rect.right;
1122 ItemInfo.Rect.right = orgX;
1123 orgX = ItemInfo.Rect.left;
1124 MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo);
1125 if (helpPos + 1 <= i &&
1126 ! MenuGetRosMenuItemInfo(MenuInfo->Self, i - 1, &ItemInfo))
1127 {
1128 MenuCleanupRosMenuItemInfo(&ItemInfo);
1129 return;
1130 }
1131 }
1132 }
1133
1134 MenuCleanupRosMenuItemInfo(&ItemInfo);
1135 }
1136
1137 /***********************************************************************
1138 * MenuDrawMenuItem
1139 *
1140 * Draw a single menu item.
1141 */
1142 static void FASTCALL MenuDrawMenuItem(HWND hWnd, PROSMENUINFO MenuInfo, HWND WndOwner, HDC hdc,
1143 PROSMENUITEMINFO lpitem, UINT Height, BOOL menuBar, UINT odaction)
1144 {
1145 RECT rect;
1146 PWCHAR Text;
1147 BOOL flat_menu = FALSE;
1148 int bkgnd;
1149 PWND Wnd = ValidateHwnd(hWnd);
1150
1151 if (!Wnd)
1152 return;
1153
1154 if (lpitem->fType & MF_SYSMENU)
1155 {
1156 if ( (Wnd->style & WS_MINIMIZE))
1157 {
1158 UserGetInsideRectNC(Wnd, &rect);
1159 UserDrawSysMenuButton(hWnd, hdc, &rect,
1160 lpitem->fState & (MF_HILITE | MF_MOUSESELECT));
1161 }
1162 return;
1163 }
1164
1165 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1166 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1167
1168 /* Setup colors */
1169
1170 if (lpitem->fState & MF_HILITE)
1171 {
1172 if(menuBar && !flat_menu) {
1173 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1174 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1175 } else {
1176 if (lpitem->fState & MF_GRAYED)
1177 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1178 else
1179 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1180 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1181 }
1182 }
1183 else
1184 {
1185 if (lpitem->fState & MF_GRAYED)
1186 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1187 else
1188 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1189 SetBkColor( hdc, GetSysColor( bkgnd ) );
1190 }
1191
1192 rect = lpitem->Rect;
1193
1194 if (lpitem->fType & MF_OWNERDRAW)
1195 {
1196 /*
1197 ** Experimentation under Windows reveals that an owner-drawn
1198 ** menu is given the rectangle which includes the space it requested
1199 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1200 ** and a popup-menu arrow. This is the value of lpitem->rect.
1201 ** Windows will leave all drawing to the application except for
1202 ** the popup-menu arrow. Windows always draws that itself, after
1203 ** the menu owner has finished drawing.
1204 */
1205 DRAWITEMSTRUCT dis;
1206
1207 dis.CtlType = ODT_MENU;
1208 dis.CtlID = 0;
1209 dis.itemID = lpitem->wID;
1210 dis.itemData = (DWORD)lpitem->dwItemData;
1211 dis.itemState = 0;
1212 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1213 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED | ODS_DISABLED;
1214 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1215 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1216 dis.hwndItem = (HWND) MenuInfo->Self;
1217 dis.hDC = hdc;
1218 dis.rcItem = rect;
1219 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1220 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hWnd,
1221 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1222 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1223 dis.rcItem.bottom);
1224 SendMessageW(WndOwner, WM_DRAWITEM, 0, (LPARAM) &dis);
1225 /* Draw the popup-menu arrow */
1226 if (lpitem->fType & MF_POPUP)
1227 {
1228 RECT rectTemp;
1229 CopyRect(&rectTemp, &rect);
1230 rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK);
1231 DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
1232 }
1233 return;
1234 }
1235
1236 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1237
1238 if (lpitem->fState & MF_HILITE)
1239 {
1240 if (flat_menu)
1241 {
1242 InflateRect (&rect, -1, -1);
1243 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1244 InflateRect (&rect, 1, 1);
1245 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1246 }
1247 else
1248 {
1249 if(menuBar)
1250 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1251 else
1252 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1253 }
1254 }
1255 else
1256 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1257
1258 SetBkMode( hdc, TRANSPARENT );
1259
1260 /* vertical separator */
1261 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1262 {
1263 HPEN oldPen;
1264 RECT rc = rect;
1265
1266 rc.left -= 3;
1267 rc.top = 3;
1268 rc.bottom = Height - 3;
1269 if (flat_menu)
1270 {
1271 oldPen = SelectObject( hdc, GetStockObject(DC_PEN) );
1272 SetDCPenColor(hdc, GetSysColor(COLOR_BTNSHADOW));
1273 MoveToEx( hdc, rc.left, rc.top, NULL );
1274 LineTo( hdc, rc.left, rc.bottom );
1275 SelectObject( hdc, oldPen );
1276 }
1277 else
1278 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1279 }
1280
1281 /* horizontal separator */
1282 if (lpitem->fType & MF_SEPARATOR)
1283 {
1284 HPEN oldPen;
1285 RECT rc = rect;
1286
1287 rc.left++;
1288 rc.right--;
1289 rc.top += SEPARATOR_HEIGHT / 2;
1290 if (flat_menu)
1291 {
1292 oldPen = SelectObject( hdc, GetStockObject(DC_PEN) );
1293 SetDCPenColor( hdc, GetSysColor(COLOR_BTNSHADOW));
1294 MoveToEx( hdc, rc.left, rc.top, NULL );
1295 LineTo( hdc, rc.right, rc.top );
1296 SelectObject( hdc, oldPen );
1297 }
1298 else
1299 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1300 return;
1301 }
1302
1303 #if 0
1304 /* helper lines for debugging */
1305 /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
1306 FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1307 SelectObject(hdc, GetStockObject(DC_PEN));
1308 SetDCPenColor(hdc, GetSysColor(COLOR_WINDOWFRAME));
1309 MoveToEx(hdc, rect.left, (rect.top + rect.bottom) / 2, NULL);
1310 LineTo(hdc, rect.right, (rect.top + rect.bottom) / 2);
1311 #endif
1312
1313 if (!menuBar)
1314 {
1315 HBITMAP bm;
1316 INT y = rect.top + rect.bottom;
1317 RECT rc = rect;
1318 int checked = FALSE;
1319 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1320 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1321 /* Draw the check mark
1322 *
1323 * FIXME:
1324 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1325 */
1326 if( !(MenuInfo->dwStyle & MNS_NOCHECK)) {
1327 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hbmpChecked :
1328 lpitem->hbmpUnchecked;
1329 if (bm) /* we have a custom bitmap */
1330 {
1331 HDC hdcMem = CreateCompatibleDC( hdc );
1332
1333 SelectObject( hdcMem, bm );
1334 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1335 check_bitmap_width, check_bitmap_height,
1336 hdcMem, 0, 0, SRCCOPY );
1337 DeleteDC( hdcMem );
1338 checked = TRUE;
1339 }
1340 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1341 {
1342 RECT r;
1343 CopyRect(&r, &rect);
1344 r.right = r.left + GetSystemMetrics(SM_CXMENUCHECK);
1345 DrawFrameControl( hdc, &r, DFC_MENU,
1346 (lpitem->fType & MFT_RADIOCHECK) ?
1347 DFCS_MENUBULLET : DFCS_MENUCHECK);
1348 checked = TRUE;
1349 }
1350 }
1351 if ( lpitem->hbmpItem )
1352 {
1353 RECT bmpRect;
1354 CopyRect(&bmpRect, &rect);
1355 if (!(MenuInfo->dwStyle & MNS_CHECKORBMP) && !(MenuInfo->dwStyle & MNS_NOCHECK))
1356 bmpRect.left += check_bitmap_width + 2;
1357 if (!(checked && (MenuInfo->dwStyle & MNS_CHECKORBMP)))
1358 {
1359 bmpRect.right = bmpRect.left + MenuInfo->maxBmpSize.cx;
1360 MenuDrawBitmapItem(hdc, lpitem, &bmpRect, MenuInfo->Self, WndOwner, odaction, menuBar);
1361 }
1362 }
1363 /* Draw the popup-menu arrow */
1364 if (lpitem->fType & MF_POPUP)
1365 {
1366 RECT rectTemp;
1367 CopyRect(&rectTemp, &rect);
1368 rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK);
1369 DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
1370 }
1371 rect.left += 4;
1372 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
1373 rect.left += check_bitmap_width;
1374 rect.right -= check_bitmap_width;
1375 }
1376 else if( lpitem->hbmpItem)
1377 { /* Draw the bitmap */
1378 MenuDrawBitmapItem(hdc, lpitem, &rect, MenuInfo->Self, WndOwner, odaction, menuBar);
1379 }
1380
1381 /* process text if present */
1382 if (lpitem->lpstr)
1383 {
1384 register int i = 0;
1385 HFONT hfontOld = 0;
1386
1387 UINT uFormat = menuBar ? DT_CENTER | DT_VCENTER | DT_SINGLELINE
1388 : DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1389
1390 if(MenuInfo->dwStyle & MNS_CHECKORBMP)
1391 rect.left += max(0, MenuInfo->maxBmpSize.cx - GetSystemMetrics(SM_CXMENUCHECK));
1392 else
1393 rect.left += MenuInfo->maxBmpSize.cx;
1394
1395 if ( lpitem->fState & MFS_DEFAULT )
1396 {
1397 hfontOld = SelectObject(hdc, hMenuFontBold);
1398 }
1399
1400 if (menuBar) {
1401 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1402 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1403 }
1404
1405 Text = (PWCHAR) lpitem->dwTypeData;
1406 if(Text)
1407 {
1408 for (i = 0; L'\0' != Text[i]; i++)
1409 if (Text[i] == L'\t' || Text[i] == L'\b')
1410 break;
1411 }
1412
1413 if(lpitem->fState & MF_GRAYED)
1414 {
1415 if (!(lpitem->fState & MF_HILITE) )
1416 {
1417 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1418 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1419 DrawTextW( hdc, Text, i, &rect, uFormat );
1420 --rect.left; --rect.top; --rect.right; --rect.bottom;
1421 }
1422 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1423 }
1424
1425 DrawTextW( hdc, Text, i, &rect, uFormat);
1426
1427 /* paint the shortcut text */
1428 if (!menuBar && L'\0' != Text[i]) /* There's a tab or flush-right char */
1429 {
1430 if (L'\t' == Text[i])
1431 {
1432 rect.left = lpitem->dxTab;
1433 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1434 }
1435 else
1436 {
1437 rect.right = lpitem->dxTab;
1438 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1439 }
1440
1441 if (lpitem->fState & MF_GRAYED)
1442 {
1443 if (!(lpitem->fState & MF_HILITE) )
1444 {
1445 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1446 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1447 DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat);
1448 --rect.left; --rect.top; --rect.right; --rect.bottom;
1449 }
1450 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1451 }
1452 DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat );
1453 }
1454
1455 if (hfontOld)
1456 SelectObject (hdc, hfontOld);
1457 }
1458 }
1459
1460 /***********************************************************************
1461 * MenuDrawPopupMenu
1462 *
1463 * Paint a popup menu.
1464 */
1465 static void FASTCALL MenuDrawPopupMenu(HWND hwnd, HDC hdc, HMENU hmenu )
1466 {
1467 HBRUSH hPrevBrush = 0;
1468 RECT rect;
1469
1470 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1471
1472 GetClientRect( hwnd, &rect );
1473
1474 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1475 && (SelectObject( hdc, hMenuFont)))
1476 {
1477 HPEN hPrevPen;
1478
1479 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1480
1481 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1482 if ( hPrevPen )
1483 {
1484 BOOL flat_menu = FALSE;
1485 ROSMENUINFO MenuInfo;
1486 ROSMENUITEMINFO ItemInfo;
1487
1488 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1489 if (flat_menu)
1490 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1491 else
1492 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1493
1494 /* draw menu items */
1495 if (MenuGetRosMenuInfo(&MenuInfo, hmenu) && MenuInfo.MenuItemCount)
1496 {
1497 UINT u;
1498
1499 MenuInitRosMenuItemInfo(&ItemInfo);
1500
1501 for (u = 0; u < MenuInfo.MenuItemCount; u++)
1502 {
1503 if (MenuGetRosMenuItemInfo(MenuInfo.Self, u, &ItemInfo))
1504 {
1505 MenuDrawMenuItem(hwnd, &MenuInfo, MenuInfo.WndOwner, hdc, &ItemInfo,
1506 MenuInfo.Height, FALSE, ODA_DRAWENTIRE);
1507 }
1508 }
1509
1510 MenuCleanupRosMenuItemInfo(&ItemInfo);
1511 }
1512 } else
1513 {
1514 SelectObject( hdc, hPrevBrush );
1515 }
1516 }
1517 }
1518
1519 /***********************************************************************
1520 * MenuDrawMenuBar
1521 *
1522 * Paint a menu bar. Returns the height of the menu bar.
1523 * called from [windows/nonclient.c]
1524 */
1525 UINT MenuDrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1526 BOOL suppress_draw)
1527 {
1528 ROSMENUINFO lppop;
1529 HFONT hfontOld = 0;
1530 HMENU hMenu = GetMenu(hwnd);
1531
1532 if (! MenuGetRosMenuInfo(&lppop, hMenu) || lprect == NULL)
1533 {
1534 return GetSystemMetrics(SM_CYMENU);
1535 }
1536
1537 if (suppress_draw)
1538 {
1539 hfontOld = SelectObject(hDC, hMenuFont);
1540
1541 MenuMenuBarCalcSize(hDC, lprect, &lppop, hwnd);
1542
1543 lprect->bottom = lprect->top + lppop.Height;
1544
1545 if (hfontOld) SelectObject( hDC, hfontOld);
1546 return lppop.Height;
1547 }
1548 else
1549 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1550 }
1551
1552 /***********************************************************************
1553 * MenuShowPopup
1554 *
1555 * Display a popup menu.
1556 */
1557 static BOOL FASTCALL MenuShowPopup(HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1558 INT x, INT y, INT xanchor, INT yanchor )
1559 {
1560 ROSMENUINFO MenuInfo;
1561 ROSMENUITEMINFO ItemInfo;
1562 UINT width, height;
1563 POINT pt;
1564 HMONITOR monitor;
1565 MONITORINFO info;
1566
1567 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1568 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1569
1570 if (! MenuGetRosMenuInfo(&MenuInfo, hmenu)) return FALSE;
1571 if (MenuInfo.FocusedItem != NO_SELECTED_ITEM)
1572 {
1573 MenuInitRosMenuItemInfo(&ItemInfo);
1574 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
1575 {
1576 ItemInfo.fMask |= MIIM_STATE;
1577 ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
1578 MenuSetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo);
1579 }
1580 MenuCleanupRosMenuItemInfo(&ItemInfo);
1581 MenuInfo.FocusedItem = NO_SELECTED_ITEM;
1582 }
1583
1584 /* store the owner for DrawItem */
1585 MenuInfo.WndOwner = hwndOwner;
1586 MenuSetRosMenuInfo(&MenuInfo);
1587
1588 MenuPopupMenuCalcSize(&MenuInfo, hwndOwner);
1589
1590 /* adjust popup menu pos so that it fits within the desktop */
1591
1592 width = MenuInfo.Width + GetSystemMetrics(SM_CXBORDER);
1593 height = MenuInfo.Height + GetSystemMetrics(SM_CYBORDER);
1594
1595 /* FIXME: should use item rect */
1596 pt.x = x;
1597 pt.y = y;
1598 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1599 info.cbSize = sizeof(info);
1600 GetMonitorInfoW( monitor, &info );
1601
1602 if( flags & TPM_RIGHTALIGN ) x -= width;
1603 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1604
1605 if( flags & TPM_BOTTOMALIGN ) y -= height;
1606 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1607
1608 if( x + width > info.rcWork.right)
1609 {
1610 if( xanchor && x >= width - xanchor )
1611 x -= width - xanchor;
1612
1613 if( x + width > info.rcWork.right)
1614 x = info.rcWork.right - width;
1615 }
1616 if( x < info.rcWork.left ) x = info.rcWork.left;
1617
1618 if( y + height > info.rcWork.bottom)
1619 {
1620 if( yanchor && y >= height + yanchor )
1621 y -= height + yanchor;
1622
1623 if( y + height > info.rcWork.bottom)
1624 y = info.rcWork.bottom - height;
1625 }
1626 if( y < info.rcWork.top ) y = info.rcWork.top;
1627
1628 /* NOTE: In Windows, top menu popup is not owned. */
1629 MenuInfo.Wnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1630 WS_POPUP, x, y, width, height,
1631 hwndOwner, 0, (HINSTANCE) GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1632 (LPVOID) MenuInfo.Self);
1633 if ( !MenuInfo.Wnd || ! MenuSetRosMenuInfo(&MenuInfo)) return FALSE;
1634 if (!top_popup) {
1635 top_popup = MenuInfo.Wnd;
1636 top_popup_hmenu = hmenu;
1637 }
1638
1639 /* Display the window */
1640
1641 SetWindowPos( MenuInfo.Wnd, HWND_TOPMOST, 0, 0, 0, 0,
1642 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
1643 UpdateWindow( MenuInfo.Wnd );
1644 return TRUE;
1645 }
1646
1647
1648 /***********************************************************************
1649 * MenuSelectItem
1650 */
1651 static void FASTCALL MenuSelectItem(HWND hwndOwner, PROSMENUINFO hmenu, UINT wIndex,
1652 BOOL sendMenuSelect, HMENU topmenu)
1653 {
1654 ROSMENUITEMINFO ItemInfo;
1655 ROSMENUINFO TopMenuInfo;
1656 HDC hdc;
1657
1658 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1659
1660 if (!hmenu || !hmenu->MenuItemCount || !hmenu->Wnd) return;
1661 if (hmenu->FocusedItem == wIndex) return;
1662 if (hmenu->Flags & MF_POPUP) hdc = GetDC(hmenu->Wnd);
1663 else hdc = GetDCEx(hmenu->Wnd, 0, DCX_CACHE | DCX_WINDOW);
1664 if (!top_popup) {
1665 top_popup = hmenu->Wnd;
1666 top_popup_hmenu = hmenu->Self;
1667 }
1668
1669 SelectObject( hdc, hMenuFont );
1670
1671 MenuInitRosMenuItemInfo(&ItemInfo);
1672
1673 /* Clear previous highlighted item */
1674 if (hmenu->FocusedItem != NO_SELECTED_ITEM)
1675 {
1676 if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->FocusedItem, &ItemInfo))
1677 {
1678 ItemInfo.fMask |= MIIM_STATE;
1679 ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
1680 MenuSetRosMenuItemInfo(hmenu->Self, hmenu->FocusedItem, &ItemInfo);
1681 }
1682 MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc, &ItemInfo,
1683 hmenu->Height, ! (hmenu->Flags & MF_POPUP),
1684 ODA_SELECT);
1685 }
1686
1687 /* Highlight new item (if any) */
1688 hmenu->FocusedItem = wIndex;
1689 MenuSetRosMenuInfo(hmenu);
1690 if (hmenu->FocusedItem != NO_SELECTED_ITEM)
1691 {
1692 if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->FocusedItem, &ItemInfo))
1693 {
1694 if (!(ItemInfo.fType & MF_SEPARATOR))
1695 {
1696 ItemInfo.fMask |= MIIM_STATE;
1697 ItemInfo.fState |= MF_HILITE;
1698 MenuSetRosMenuItemInfo(hmenu->Self, hmenu->FocusedItem, &ItemInfo);
1699 MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc,
1700 &ItemInfo, hmenu->Height, ! (hmenu->Flags & MF_POPUP),
1701 ODA_SELECT);
1702 }
1703 if (sendMenuSelect)
1704 {
1705 SendMessageW(hwndOwner, WM_MENUSELECT,
1706 MAKELONG(ItemInfo.fType & MF_POPUP ? wIndex : ItemInfo.wID,
1707 ItemInfo.fType | ItemInfo.fState | MF_MOUSESELECT |
1708 (hmenu->Flags & MF_SYSMENU)), (LPARAM) hmenu->Self);
1709 }
1710 }
1711 }
1712 else if (sendMenuSelect) {
1713 if(topmenu) {
1714 int pos;
1715 pos = MenuFindSubMenu(&topmenu, hmenu->Self);
1716 if (pos != NO_SELECTED_ITEM)
1717 {
1718 if (MenuGetRosMenuInfo(&TopMenuInfo, topmenu)
1719 && MenuGetRosMenuItemInfo(topmenu, pos, &ItemInfo))
1720 {
1721 SendMessageW(hwndOwner, WM_MENUSELECT,
1722 MAKELONG(Pos, ItemInfo.fType | ItemInfo.fState
1723 | MF_MOUSESELECT
1724 | (TopMenuInfo.Flags & MF_SYSMENU)),
1725 (LPARAM) topmenu);
1726 }
1727 }
1728 }
1729 }
1730 MenuCleanupRosMenuItemInfo(&ItemInfo);
1731 ReleaseDC(hmenu->Wnd, hdc);
1732 }
1733
1734 /***********************************************************************
1735 * MenuMoveSelection
1736 *
1737 * Moves currently selected item according to the Offset parameter.
1738 * If there is no selection then it should select the last item if
1739 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
1740 */
1741 static void FASTCALL
1742 MenuMoveSelection(HWND WndOwner, PROSMENUINFO MenuInfo, INT Offset)
1743 {
1744 INT i;
1745 ROSMENUITEMINFO ItemInfo;
1746 INT OrigPos;
1747
1748 TRACE("hwnd=%x menu=%x off=0x%04x\n", WndOwner, MenuInfo, Offset);
1749
1750 /* Prevent looping */
1751 if (0 == MenuInfo->MenuItemCount || 0 == Offset)
1752 return;
1753 else if (Offset < -1)
1754 Offset = -1;
1755 else if (Offset > 1)
1756 Offset = 1;
1757
1758 MenuInitRosMenuItemInfo(&ItemInfo);
1759
1760 OrigPos = MenuInfo->FocusedItem;
1761 if (OrigPos == NO_SELECTED_ITEM) /* NO_SELECTED_ITEM is not -1 ! */
1762 {
1763 OrigPos = 0;
1764 i = -1;
1765 }
1766 else
1767 {
1768 i = MenuInfo->FocusedItem;
1769 }
1770
1771 do
1772 {
1773 /* Step */
1774 i += Offset;
1775 /* Clip and wrap around */
1776 if (i < 0)
1777 {
1778 i = MenuInfo->MenuItemCount - 1;
1779 }
1780 else if (i >= MenuInfo->MenuItemCount)
1781 {
1782 i = 0;
1783 }
1784 /* If this is a good candidate; */
1785 if (MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo) &&
1786 0 == (ItemInfo.fType & MF_SEPARATOR))
1787 {
1788 MenuSelectItem(WndOwner, MenuInfo, i, TRUE, NULL);
1789 MenuCleanupRosMenuItemInfo(&ItemInfo);
1790 return;
1791 }
1792 } while (i != OrigPos);
1793
1794 /* Not found */
1795 MenuCleanupRosMenuItemInfo(&ItemInfo);
1796 }
1797
1798 LRESULT WINAPI PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
1799 {
1800 TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
1801
1802 switch(Message)
1803 {
1804 case WM_CREATE:
1805 {
1806 CREATESTRUCTA *cs = (CREATESTRUCTA *) lParam;
1807 SetWindowLongPtrA(Wnd, 0, (LONG) cs->lpCreateParams);
1808 return 0;
1809 }
1810
1811 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1812 return MA_NOACTIVATE;
1813
1814 case WM_PAINT:
1815 {
1816 PAINTSTRUCT ps;
1817 BeginPaint(Wnd, &ps);
1818 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrA(Wnd, 0));
1819 EndPaint(Wnd, &ps);
1820 return 0;
1821 }
1822
1823 case WM_PRINTCLIENT:
1824 {
1825 MenuDrawPopupMenu( Wnd, (HDC)wParam,
1826 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
1827 return 0;
1828 }
1829
1830 case WM_ERASEBKGND:
1831 return 1;
1832
1833 case WM_DESTROY:
1834 /* zero out global pointer in case resident popup window was destroyed. */
1835 if (Wnd == top_popup)
1836 {
1837 top_popup = NULL;
1838 top_popup_hmenu = NULL;
1839 }
1840 break;
1841
1842 case WM_SHOWWINDOW:
1843 if (0 != wParam)
1844 {
1845 if (0 == GetWindowLongPtrA(Wnd, 0))
1846 {
1847 OutputDebugStringA("no menu to display\n");
1848 }
1849 }
1850 else
1851 {
1852 SetWindowLongPtrA(Wnd, 0, 0);
1853 }
1854 break;
1855
1856 case MM_SETMENUHANDLE:
1857 SetWindowLongPtrA(Wnd, 0, wParam);
1858 break;
1859
1860 case MM_GETMENUHANDLE:
1861 case MN_GETHMENU:
1862 return GetWindowLongPtrA(Wnd, 0);
1863
1864 default:
1865 return DefWindowProcA(Wnd, Message, wParam, lParam);
1866 }
1867 return 0;
1868 }
1869
1870 LRESULT WINAPI
1871 PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
1872 {
1873 TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
1874
1875 switch(Message)
1876 {
1877 case WM_CREATE:
1878 {
1879 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
1880 SetWindowLongPtrW(Wnd, 0, (LONG) cs->lpCreateParams);
1881 return 0;
1882 }
1883
1884 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1885 return MA_NOACTIVATE;
1886
1887 case WM_PAINT:
1888 {
1889 PAINTSTRUCT ps;
1890 BeginPaint(Wnd, &ps);
1891 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrW(Wnd, 0));
1892 EndPaint(Wnd, &ps);
1893 return 0;
1894 }
1895
1896 case WM_PRINTCLIENT:
1897 {
1898 MenuDrawPopupMenu( Wnd, (HDC)wParam,
1899 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
1900 return 0;
1901 }
1902
1903 case WM_ERASEBKGND:
1904 return 1;
1905
1906 case WM_DESTROY:
1907 /* zero out global pointer in case resident popup window was destroyed. */
1908 if (Wnd == top_popup)
1909 {
1910 top_popup = NULL;
1911 top_popup_hmenu = NULL;
1912 }
1913 break;
1914
1915 case WM_SHOWWINDOW:
1916 if (0 != wParam)
1917 {
1918 if (0 == GetWindowLongPtrW(Wnd, 0))
1919 {
1920 OutputDebugStringA("no menu to display\n");
1921 }
1922 }
1923 else
1924 {
1925 SetWindowLongPtrW(Wnd, 0, 0);
1926 }
1927 break;
1928
1929 case MM_SETMENUHANDLE:
1930 SetWindowLongPtrW(Wnd, 0, wParam);
1931 break;
1932
1933 case MM_GETMENUHANDLE:
1934 case MN_GETHMENU:
1935 return GetWindowLongPtrW(Wnd, 0);
1936
1937 default:
1938 return DefWindowProcW(Wnd, Message, wParam, lParam);
1939 }
1940
1941 return 0;
1942 }
1943
1944 /**********************************************************************
1945 * MENU_ParseResource
1946 *
1947 * Parse a standard menu resource and add items to the menu.
1948 * Return a pointer to the end of the resource.
1949 *
1950 * NOTE: flags is equivalent to the mtOption field
1951 */
1952 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1953 {
1954 WORD flags, id = 0;
1955 HMENU hSubMenu;
1956 LPCSTR str;
1957 BOOL end = FALSE;
1958
1959 do
1960 {
1961 flags = GET_WORD(res);
1962
1963 /* remove MF_END flag before passing it to AppendMenu()! */
1964 end = (flags & MF_END);
1965 if(end) flags ^= MF_END;
1966
1967 res += sizeof(WORD);
1968 if(!(flags & MF_POPUP))
1969 {
1970 id = GET_WORD(res);
1971 res += sizeof(WORD);
1972 }
1973 str = res;
1974 if(!unicode)
1975 res += strlen(str) + 1;
1976 else
1977 res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1978 if (flags & MF_POPUP)
1979 {
1980 hSubMenu = CreatePopupMenu();
1981 if(!hSubMenu) return NULL;
1982 if(!(res = MENU_ParseResource(res, hSubMenu, unicode)))
1983 return NULL;
1984 if(!unicode)
1985 AppendMenuA(hMenu, flags, (UINT)hSubMenu, str);
1986 else
1987 AppendMenuW(hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str);
1988 }
1989 else /* Not a popup */
1990 {
1991 if(!unicode)
1992 {
1993 if (*str == 0)
1994 flags = MF_SEPARATOR;
1995 }
1996 else
1997 {
1998 if (*(LPCWSTR)str == 0)
1999 flags = MF_SEPARATOR;
2000 }
2001
2002 if (flags & MF_SEPARATOR)
2003 {
2004 if (!(flags & (MF_GRAYED | MF_DISABLED)))
2005 flags |= MF_GRAYED | MF_DISABLED;
2006 }
2007
2008 if(!unicode)
2009 AppendMenuA(hMenu, flags, id, *str ? str : NULL);
2010 else
2011 AppendMenuW(hMenu, flags, id,
2012 *(LPCWSTR)str ? (LPCWSTR)str : NULL);
2013 }
2014 } while(!end);
2015 return res;
2016 }
2017
2018
2019 /**********************************************************************
2020 * MENUEX_ParseResource
2021 *
2022 * Parse an extended menu resource and add items to the menu.
2023 * Return a pointer to the end of the resource.
2024 */
2025 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2026 {
2027 WORD resinfo;
2028 do {
2029 MENUITEMINFOW mii;
2030
2031 mii.cbSize = sizeof(mii);
2032 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
2033 mii.fType = GET_DWORD(res);
2034 res += sizeof(DWORD);
2035 mii.fState = GET_DWORD(res);
2036 res += sizeof(DWORD);
2037 mii.wID = GET_DWORD(res);
2038 res += sizeof(DWORD);
2039 resinfo = GET_WORD(res);
2040 res += sizeof(WORD);
2041 /* Align the text on a word boundary. */
2042 res += (~((UINT_PTR)res - 1)) & 1;
2043 mii.dwTypeData = (LPWSTR) res;
2044 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2045 /* Align the following fields on a dword boundary. */
2046 res += (~((UINT_PTR)res - 1)) & 3;
2047
2048 TRACE("Menu item: [%08x,%08x,%04x,%04x,%S]\n",
2049 mii.fType, mii.fState, mii.wID, resinfo, mii.dwTypeData);
2050
2051 if (resinfo & 1) { /* Pop-up? */
2052 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2053 res += sizeof(DWORD);
2054 mii.hSubMenu = CreatePopupMenu();
2055 if (!mii.hSubMenu)
2056 return NULL;
2057 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2058 DestroyMenu(mii.hSubMenu);
2059 return NULL;
2060 }
2061 mii.fMask |= MIIM_SUBMENU;
2062 mii.fType |= MF_POPUP;
2063 mii.wID = (UINT) mii.hSubMenu;
2064 }
2065 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2066 {
2067 mii.fType |= MF_SEPARATOR;
2068 }
2069 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2070 } while (!(resinfo & MF_END));
2071 return res;
2072 }
2073
2074 NTSTATUS WINAPI
2075 User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength)
2076 {
2077 HMENU hmenu = LoadMenuW(User32Instance, L"SYSMENU");
2078 LRESULT Result = (LRESULT)hmenu;
2079 MENUINFO menuinfo = {0};
2080 MENUITEMINFOW info = {0};
2081
2082 // removing space for checkboxes from menu
2083 menuinfo.cbSize = sizeof(menuinfo);
2084 menuinfo.fMask = MIM_STYLE;
2085 GetMenuInfo(hmenu, &menuinfo);
2086 menuinfo.dwStyle |= MNS_NOCHECK;
2087 SetMenuInfo(hmenu, &menuinfo);
2088
2089 // adding bitmaps to menu items
2090 info.cbSize = sizeof(info);
2091 info.fMask |= MIIM_BITMAP;
2092 info.hbmpItem = HBMMENU_POPUP_MINIMIZE;
2093 SetMenuItemInfoW(hmenu, SC_MINIMIZE, FALSE, &info);
2094 info.hbmpItem = HBMMENU_POPUP_RESTORE;
2095 SetMenuItemInfoW(hmenu, SC_RESTORE, FALSE, &info);
2096 info.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
2097 SetMenuItemInfoW(hmenu, SC_MAXIMIZE, FALSE, &info);
2098 info.hbmpItem = HBMMENU_POPUP_CLOSE;
2099 SetMenuItemInfoW(hmenu, SC_CLOSE, FALSE, &info);
2100
2101 return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS));
2102 }
2103
2104
2105 BOOL
2106 MenuInit(VOID)
2107 {
2108 NONCLIENTMETRICSW ncm;
2109
2110 /* get the menu font */
2111 if(!hMenuFont || !hMenuFontBold)
2112 {
2113 ncm.cbSize = sizeof(ncm);
2114 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
2115 {
2116 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
2117 return FALSE;
2118 }
2119
2120 hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont);
2121 if(hMenuFont == NULL)
2122 {
2123 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
2124 return FALSE;
2125 }
2126
2127 ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
2128 hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont);
2129 if(hMenuFontBold == NULL)
2130 {
2131 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
2132 DeleteObject(hMenuFont);
2133 hMenuFont = NULL;
2134 return FALSE;
2135 }
2136 }
2137
2138 return TRUE;
2139 }
2140
2141 VOID
2142 MenuCleanup(VOID)
2143 {
2144 if (hMenuFont)
2145 {
2146 DeleteObject(hMenuFont);
2147 hMenuFont = NULL;
2148 }
2149
2150 if (hMenuFontBold)
2151 {
2152 DeleteObject(hMenuFontBold);
2153 hMenuFontBold = NULL;
2154 }
2155 }
2156
2157 /***********************************************************************
2158 * DrawMenuBarTemp (USER32.@)
2159 *
2160 * UNDOCUMENTED !!
2161 *
2162 * called by W98SE desk.cpl Control Panel Applet
2163 *
2164 * Not 100% sure about the param names, but close.
2165 *
2166 * @implemented
2167 */
2168 DWORD WINAPI
2169 DrawMenuBarTemp(HWND Wnd, HDC DC, LPRECT Rect, HMENU Menu, HFONT Font)
2170 {
2171 ROSMENUINFO MenuInfo;
2172 ROSMENUITEMINFO ItemInfo;
2173 UINT i;
2174 HFONT FontOld = NULL;
2175 BOOL flat_menu = FALSE;
2176
2177 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
2178
2179 if (NULL == Menu)
2180 {
2181 Menu = GetMenu(Wnd);
2182 }
2183
2184 if (NULL == Font)
2185 {
2186 Font = hMenuFont;
2187 }
2188
2189 if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
2190 {
2191 return GetSystemMetrics(SM_CYMENU);
2192 }
2193
2194 TRACE("(%x, %x, %p, %x, %x)\n", Wnd, DC, Rect, Menu, Font);
2195
2196 FontOld = SelectObject(DC, Font);
2197
2198 if (0 == MenuInfo.Height)
2199 {
2200 MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
2201 }
2202
2203 Rect->bottom = Rect->top + MenuInfo.Height;
2204
2205 FillRect(DC, Rect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
2206
2207 SelectObject(DC, GetStockObject(DC_PEN));
2208 SetDCPenColor(DC, GetSysColor(COLOR_3DFACE));
2209 MoveToEx(DC, Rect->left, Rect->bottom, NULL);
2210 LineTo(DC, Rect->right, Rect->bottom);
2211
2212 if (0 == MenuInfo.MenuItemCount)
2213 {
2214 SelectObject(DC, FontOld);
2215 return GetSystemMetrics(SM_CYMENU);
2216 }
2217
2218 MenuInitRosMenuItemInfo(&ItemInfo);
2219 for (i = 0; i < MenuInfo.MenuItemCount; i++)
2220 {
2221 if (MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
2222 {
2223 MenuDrawMenuItem(Wnd, &MenuInfo, Wnd, DC, &ItemInfo,
2224 MenuInfo.Height, TRUE, ODA_DRAWENTIRE);
2225 }
2226 }
2227 MenuCleanupRosMenuItemInfo(&ItemInfo);
2228
2229 SelectObject(DC, FontOld);
2230
2231 return MenuInfo.Height;
2232 }
2233
2234 /***********************************************************************
2235 * MenuShowSubPopup
2236 *
2237 * Display the sub-menu of the selected item of this menu.
2238 * Return the handle of the submenu, or menu if no submenu to display.
2239 */
2240 static HMENU FASTCALL
2241 MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags)
2242 {
2243 extern void FASTCALL NcGetSysPopupPos(HWND Wnd, RECT *Rect);
2244 RECT Rect;
2245 ROSMENUITEMINFO ItemInfo;
2246 ROSMENUINFO SubMenuInfo;
2247 HDC Dc;
2248 HMENU Ret;
2249
2250 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst);
2251
2252 if (NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2253 {
2254 return MenuInfo->Self;
2255 }
2256
2257 MenuInitRosMenuItemInfo(&ItemInfo);
2258 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2259 {
2260 MenuCleanupRosMenuItemInfo(&ItemInfo);
2261 return MenuInfo->Self;
2262 }
2263 if (0 == (ItemInfo.fType & MF_POPUP) || 0 != (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)))
2264 {
2265 MenuCleanupRosMenuItemInfo(&ItemInfo);
2266 return MenuInfo->Self;
2267 }
2268
2269 /* message must be sent before using item,
2270 because nearly everything may be changed by the application ! */
2271
2272 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2273 if (0 == (Flags & TPM_NONOTIFY))
2274 {
2275 SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu,
2276 MAKELONG(MenuInfo->FocusedItem, IS_SYSTEM_MENU(MenuInfo)));
2277 }
2278
2279 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2280 {
2281 MenuCleanupRosMenuItemInfo(&ItemInfo);
2282 return MenuInfo->Self;
2283 }
2284 Rect = ItemInfo.Rect;
2285
2286 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2287 if (0 == (ItemInfo.fState & MF_HILITE))
2288 {
2289 if (0 != (MenuInfo->Flags & MF_POPUP))
2290 {
2291 Dc = GetDC(MenuInfo->Wnd);
2292 }
2293 else
2294 {
2295 Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
2296 }
2297
2298 SelectObject(Dc, hMenuFont);
2299 ItemInfo.fMask |= MIIM_STATE;
2300 ItemInfo.fState |= MF_HILITE;
2301 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2302 MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->Height,
2303 ! (MenuInfo->Flags & MF_POPUP), ODA_DRAWENTIRE);
2304 ReleaseDC(MenuInfo->Wnd, Dc);
2305 }
2306
2307 if (0 == ItemInfo.Rect.top && 0 == ItemInfo.Rect.left
2308 && 0 == ItemInfo.Rect.bottom && 0 == ItemInfo.Rect.right)
2309 {
2310 ItemInfo.Rect = Rect;
2311 }
2312
2313 ItemInfo.fMask |= MIIM_STATE;
2314 ItemInfo.fState |= MF_MOUSESELECT;
2315 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2316
2317 if (IS_SYSTEM_MENU(MenuInfo))
2318 {
2319 MenuInitSysMenuPopup(ItemInfo.hSubMenu, GetWindowLongPtrW(MenuInfo->Wnd, GWL_STYLE),
2320 GetClassLongPtrW(MenuInfo->Wnd, GCL_STYLE), HTSYSMENU);
2321
2322 NcGetSysPopupPos(MenuInfo->Wnd, &Rect);
2323 Rect.top = Rect.bottom;
2324 Rect.right = GetSystemMetrics(SM_CXSIZE);
2325 Rect.bottom = GetSystemMetrics(SM_CYSIZE);
2326 }
2327 else
2328 {
2329 GetWindowRect(MenuInfo->Wnd, &Rect);
2330 if (0 != (MenuInfo->Flags & MF_POPUP))
2331 {
2332 Rect.left += ItemInfo.Rect.right - GetSystemMetrics(SM_CXBORDER);
2333 Rect.top += ItemInfo.Rect.top - 3;
2334 Rect.right = ItemInfo.Rect.left - ItemInfo.Rect.right + GetSystemMetrics(SM_CXBORDER);
2335 Rect.bottom = ItemInfo.Rect.top - ItemInfo.Rect.bottom - 3 - 2
2336 - GetSystemMetrics(SM_CYBORDER);
2337 }
2338 else
2339 {
2340 Rect.left += ItemInfo.Rect.left;
2341 Rect.top += ItemInfo.Rect.bottom;
2342 Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left;
2343 Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top;
2344 }
2345 }
2346
2347 MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->FocusedItem, Flags,
2348 Rect.left, Rect.top, Rect.right, Rect.bottom );
2349 if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2350 {
2351 MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT);
2352 }
2353
2354 Ret = ItemInfo.hSubMenu;
2355 MenuCleanupRosMenuItemInfo(&ItemInfo);
2356
2357 return Ret;
2358 }
2359
2360 /**********************************************************************
2361 * MENU_EndMenu
2362 *
2363 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2364 *
2365 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2366 */
2367 void MENU_EndMenu( HWND hwnd )
2368 {
2369 ROSMENUINFO MenuInfo;
2370 BOOL Ret = FALSE;
2371 if (top_popup_hmenu)
2372 Ret = MenuGetRosMenuInfo(&MenuInfo, top_popup_hmenu);
2373 if (Ret && hwnd == MenuInfo.WndOwner) EndMenu();
2374 }
2375
2376 /***********************************************************************
2377 * MenuHideSubPopups
2378 *
2379 * Hide the sub-popup menus of this menu.
2380 */
2381 static void FASTCALL
2382 MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo,
2383 BOOL SendMenuSelect, UINT wFlags)
2384 {
2385 ROSMENUINFO SubMenuInfo;
2386 ROSMENUITEMINFO ItemInfo;
2387
2388 TRACE("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect);
2389
2390 if (NULL != MenuInfo && NULL != top_popup && NO_SELECTED_ITEM != MenuInfo->FocusedItem)
2391 {
2392 MenuInitRosMenuItemInfo(&ItemInfo);
2393 ItemInfo.fMask |= MIIM_FTYPE | MIIM_STATE;
2394 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo)
2395 || 0 == (ItemInfo.fType & MF_POPUP)
2396 || 0 == (ItemInfo.fState & MF_MOUSESELECT))
2397 {
2398 MenuCleanupRosMenuItemInfo(&ItemInfo);
2399 return;
2400 }
2401 ItemInfo.fState &= ~MF_MOUSESELECT;
2402 ItemInfo.fMask |= MIIM_STATE;
2403 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2404 if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2405 {
2406 MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE, wFlags);
2407 MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL);
2408 DestroyWindow(SubMenuInfo.Wnd);
2409 SubMenuInfo.Wnd = NULL;
2410 MenuSetRosMenuInfo(&SubMenuInfo);
2411
2412 if (!(wFlags & TPM_NONOTIFY))
2413 SendMessageW( WndOwner, WM_UNINITMENUPOPUP, (WPARAM)ItemInfo.hSubMenu,
2414 MAKELPARAM(0, IS_SYSTEM_MENU(&SubMenuInfo)) );
2415 }
2416 }
2417 }
2418
2419 /***********************************************************************
2420 * MenuSwitchTracking
2421 *
2422 * Helper function for menu navigation routines.
2423 */
2424 static void FASTCALL
2425 MenuSwitchTracking(MTRACKER* Mt, PROSMENUINFO PtMenuInfo, UINT Index, UINT wFlags)
2426 {
2427 ROSMENUINFO TopMenuInfo;
2428
2429 TRACE("%x menu=%x 0x%04x\n", Mt, PtMenuInfo->Self, Index);
2430
2431 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu) &&
2432 Mt->TopMenu != PtMenuInfo->Self &&
2433 0 == ((PtMenuInfo->Flags | TopMenuInfo.Flags) & MF_POPUP))
2434 {
2435 /* both are top level menus (system and menu-bar) */
2436 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
2437 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL);
2438 Mt->TopMenu = PtMenuInfo->Self;
2439 }
2440 else
2441 {
2442 MenuHideSubPopups(Mt->OwnerWnd, PtMenuInfo, FALSE, wFlags);
2443 }
2444
2445 MenuSelectItem(Mt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL);
2446 }
2447
2448 /***********************************************************************
2449 * MenuExecFocusedItem
2450 *
2451 * Execute a menu item (for instance when user pressed Enter).
2452 * Return the wID of the executed item. Otherwise, -1 indicating
2453 * that no menu item was executed, -2 if a popup is shown;
2454 * Have to receive the flags for the TrackPopupMenu options to avoid
2455 * sending unwanted message.
2456 *
2457 */
2458 static INT FASTCALL
2459 MenuExecFocusedItem(MTRACKER *Mt, PROSMENUINFO MenuInfo, UINT Flags)
2460 {
2461 ROSMENUITEMINFO ItemInfo;
2462 UINT wID;
2463
2464 TRACE("%p menu=%p\n", Mt, MenuInfo);
2465
2466 if (0 == MenuInfo->MenuItemCount || NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2467 {
2468 return -1;
2469 }
2470
2471 MenuInitRosMenuItemInfo(&ItemInfo);
2472 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2473 {
2474 MenuCleanupRosMenuItemInfo(&ItemInfo);
2475 return -1;
2476 }
2477
2478 TRACE("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu);
2479
2480 if (0 == (ItemInfo.fType & MF_POPUP))
2481 {
2482 if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))
2483 && 0 == (ItemInfo.fType & MF_SEPARATOR))
2484 {
2485 /* If TPM_RETURNCMD is set you return the id, but
2486 do not send a message to the owner */
2487 if (0 == (Flags & TPM_RETURNCMD))
2488 {
2489 if (0 != (MenuInfo->Flags & MF_SYSMENU))
2490 {
2491 PostMessageW(Mt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID,
2492 MAKELPARAM((SHORT) Mt->Pt.x, (SHORT) Mt->Pt.y));
2493 }
2494 else
2495 {
2496 if (MenuInfo->dwStyle & MNS_NOTIFYBYPOS)
2497 PostMessageW(Mt->OwnerWnd, WM_MENUCOMMAND,
2498 MenuInfo->FocusedItem,
2499 (LPARAM)MenuInfo->Self);
2500 else
2501 PostMessageW(Mt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0);
2502 }
2503 }
2504 wID = ItemInfo.wID;
2505 MenuCleanupRosMenuItemInfo(&ItemInfo);
2506 return wID;
2507 }
2508 }
2509 else
2510 {
2511 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, MenuInfo, TRUE, Flags);
2512 return -2;
2513 }
2514
2515 return -1;
2516 }
2517
2518 /***********************************************************************
2519 * MenuButtonDown
2520 *
2521 * Return TRUE if we can go on with menu tracking.
2522 */
2523 static BOOL FASTCALL
2524 MenuButtonDown(MTRACKER* Mt, HMENU PtMenu, UINT Flags)
2525 {
2526 int Index;
2527 ROSMENUINFO MenuInfo;
2528 ROSMENUITEMINFO Item;
2529
2530 TRACE("%x PtMenu=%p\n", Mt, PtMenu);
2531
2532 if (NULL != PtMenu)
2533 {
2534 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2535 {
2536 return FALSE;
2537 }
2538 if (IS_SYSTEM_MENU(&MenuInfo))
2539 {
2540 Index = 0;
2541 }
2542 else
2543 {
2544 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2545 }
2546 MenuInitRosMenuItemInfo(&Item);
2547 if (NO_SELECTED_ITEM == Index || ! MenuGetRosMenuItemInfo(PtMenu, Index, &Item))
2548 {
2549 MenuCleanupRosMenuItemInfo(&Item);
2550 return FALSE;
2551 }
2552
2553 if (!(Item.fType & MF_SEPARATOR) &&
2554 !(Item.fState & (MFS_DISABLED | MFS_GRAYED)) )
2555 {
2556 if (MenuInfo.FocusedItem != Index)
2557 {
2558 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
2559 }
2560
2561 /* If the popup menu is not already "popped" */
2562 if (0 == (Item.fState & MF_MOUSESELECT))
2563 {
2564 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2565 }
2566 }
2567
2568 MenuCleanupRosMenuItemInfo(&Item);
2569
2570 return TRUE;
2571 }
2572
2573 /* else the click was on the menu bar, finish the tracking */
2574
2575 return FALSE;
2576 }
2577
2578 /***********************************************************************
2579 * MenuButtonUp
2580 *
2581 * Return the value of MenuExecFocusedItem if
2582 * the selected item was not a popup. Else open the popup.
2583 * A -1 return value indicates that we go on with menu tracking.
2584 *
2585 */
2586 static INT FASTCALL
2587 MenuButtonUp(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2588 {
2589 UINT Id;
2590 ROSMENUINFO MenuInfo;
2591 ROSMENUITEMINFO ItemInfo;
2592
2593 TRACE("%p hmenu=%x\n", Mt, PtMenu);
2594
2595 if (NULL != PtMenu)
2596 {
2597 Id = 0;
2598 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2599 {
2600 return -1;
2601 }
2602
2603 if (! IS_SYSTEM_MENU(&MenuInfo))
2604 {
2605 Id = NtUserMenuItemFromPoint(Mt->OwnerWnd, MenuInfo.Self, Mt->Pt.x, Mt->Pt.y);
2606 }
2607 MenuInitRosMenuItemInfo(&ItemInfo);
2608 if (0 <= Id && MenuGetRosMenuItemInfo(MenuInfo.Self, Id, &ItemInfo) &&
2609 MenuInfo.FocusedItem == Id)
2610 {
2611 if (0 == (ItemInfo.fType & MF_POPUP))
2612 {
2613 INT ExecutedMenuId = MenuExecFocusedItem(Mt, &MenuInfo, Flags);
2614 MenuCleanupRosMenuItemInfo(&ItemInfo);
2615 return (ExecutedMenuId < 0) ? -1 : ExecutedMenuId;
2616 }
2617 MenuCleanupRosMenuItemInfo(&ItemInfo);
2618
2619 /* If we are dealing with the top-level menu */
2620 /* and this is a click on an already "popped" item: */
2621 /* Stop the menu tracking and close the opened submenus */
2622 if (Mt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide)
2623 {
2624 MenuCleanupRosMenuItemInfo(&ItemInfo);
2625 return 0;
2626 }
2627 }
2628 MenuCleanupRosMenuItemInfo(&ItemInfo);
2629 MenuInfo.TimeToHide = TRUE;
2630 MenuSetRosMenuInfo(&MenuInfo);
2631 }
2632
2633 return -1;
2634 }
2635
2636 /***********************************************************************
2637 * MenuPtMenu
2638 *
2639 * Walks menu chain trying to find a menu pt maps to.
2640 */
2641 static HMENU FASTCALL
2642 MenuPtMenu(HMENU Menu, POINT Pt)
2643 {
2644 extern LRESULT DefWndNCHitTest(HWND hWnd, POINT Point);
2645 ROSMENUINFO MenuInfo;
2646 ROSMENUITEMINFO ItemInfo;
2647 HMENU Ret = NULL;
2648 INT Ht;
2649
2650 if (! MenuGetRosMenuInfo(&MenuInfo, Menu))
2651 {
2652 return NULL;
2653 }
2654
2655 /* try subpopup first (if any) */
2656 if (NO_SELECTED_ITEM != MenuInfo.FocusedItem)
2657 {
2658 MenuInitRosMenuItemInfo(&ItemInfo);
2659 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo) &&
2660 0 != (ItemInfo.fType & MF_POPUP) &&
2661 0 != (ItemInfo.fState & MF_MOUSESELECT))
2662 {
2663 Ret = MenuPtMenu(ItemInfo.hSubMenu, Pt);
2664 if (NULL != Ret)
2665 {
2666 MenuCleanupRosMenuItemInfo(&ItemInfo);
2667 return Ret;
2668 }
2669 }
2670 MenuCleanupRosMenuItemInfo(&ItemInfo);
2671 }
2672
2673 /* check the current window (avoiding WM_HITTEST) */
2674 Ht = DefWndNCHitTest(MenuInfo.Wnd, Pt);
2675 if (0 != (MenuInfo.Flags & MF_POPUP))
2676 {
2677 if (HTNOWHERE != Ht && HTERROR != Ht)
2678 {
2679 Ret = Menu;
2680 }
2681 }
2682 else if (HTSYSMENU == Ht)
2683 {
2684 Ret = NtUserGetSystemMenu(MenuInfo.Wnd, FALSE);
2685 }
2686 else if (HTMENU == Ht)
2687 {
2688 Ret = GetMenu(MenuInfo.Wnd);
2689 }
2690
2691 return Ret;
2692 }
2693
2694 /***********************************************************************
2695 * MenuMouseMove
2696 *
2697 * Return TRUE if we can go on with menu tracking.
2698 */
2699 static BOOL FASTCALL
2700 MenuMouseMove(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2701 {
2702 UINT Index;
2703 ROSMENUINFO MenuInfo;
2704 ROSMENUITEMINFO ItemInfo;
2705
2706 if (NULL != PtMenu)
2707 {
2708 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2709 {
2710 return TRUE;
2711 }
2712 if (IS_SYSTEM_MENU(&MenuInfo))
2713 {
2714 Index = 0;
2715 }
2716 else
2717 {
2718 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2719 }
2720 }
2721 else
2722 {
2723 Index = NO_SELECTED_ITEM;
2724 }
2725
2726 if (NO_SELECTED_ITEM == Index)
2727 {
2728 if (Mt->CurrentMenu == MenuInfo.Self ||
2729 MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
2730 {
2731 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
2732 TRUE, Mt->TopMenu);
2733 }
2734 }
2735 else if (MenuInfo.FocusedItem != Index)
2736 {
2737 MenuInitRosMenuItemInfo(&ItemInfo);
2738 if (MenuGetRosMenuItemInfo(MenuInfo.Self, Index, &ItemInfo) &&
2739 !(ItemInfo.fType & MF_SEPARATOR))
2740 {
2741 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
2742 if (!(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED)))
2743 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2744 }
2745 MenuCleanupRosMenuItemInfo(&ItemInfo);
2746 }
2747
2748 return TRUE;
2749 }
2750
2751 /***********************************************************************
2752 * MenuGetSubPopup
2753 *
2754 * Return the handle of the selected sub-popup menu (if any).
2755 */
2756 static HMENU FASTCALL
2757 MenuGetSubPopup(HMENU Menu)
2758 {
2759 ROSMENUINFO MenuInfo;
2760 ROSMENUITEMINFO ItemInfo;
2761
2762 if (! MenuGetRosMenuInfo(&MenuInfo, Menu)
2763 || NO_SELECTED_ITEM == MenuInfo.FocusedItem)
2764 {
2765 return NULL;
2766 }
2767
2768 MenuInitRosMenuItemInfo(&ItemInfo);
2769 if (! MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
2770 {
2771 MenuCleanupRosMenuItemInfo(&ItemInfo);
2772 return NULL;
2773 }
2774 if (0 != (ItemInfo.fType & MF_POPUP) && 0 != (ItemInfo.fState & MF_MOUSESELECT))
2775 {
2776 MenuCleanupRosMenuItemInfo(&ItemInfo);
2777 return ItemInfo.hSubMenu;
2778 }
2779
2780 MenuCleanupRosMenuItemInfo(&ItemInfo);
2781 return NULL;
2782 }
2783
2784 /***********************************************************************
2785 * MenuDoNextMenu
2786 *
2787 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2788 */
2789 static LRESULT FASTCALL
2790 MenuDoNextMenu(MTRACKER* Mt, UINT Vk, UINT wFlags)
2791 {
2792 ROSMENUINFO TopMenuInfo;
2793 ROSMENUINFO MenuInfo;
2794
2795 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2796 {
2797 return (LRESULT) FALSE;
2798 }
2799
2800 if ((VK_LEFT == Vk && 0 == TopMenuInfo.FocusedItem)
2801 || (VK_RIGHT == Vk && TopMenuInfo.FocusedItem == TopMenuInfo.MenuItemCount - 1))
2802 {
2803 MDINEXTMENU NextMenu;
2804 HMENU NewMenu;
2805 HWND NewWnd;
2806 UINT Id = 0;
2807
2808 NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(Mt->TopMenu, 0) : Mt->TopMenu;
2809 NextMenu.hmenuNext = NULL;
2810 NextMenu.hwndNext = NULL;
2811 SendMessageW(Mt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
2812
2813 TRACE("%p [%p] -> %p [%p]\n",
2814 Mt->CurrentMenu, Mt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
2815
2816 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
2817 {
2818 DWORD Style = GetWindowLongPtrW(Mt->OwnerWnd, GWL_STYLE);
2819 NewWnd = Mt->OwnerWnd;
2820 if (IS_SYSTEM_MENU(&TopMenuInfo))
2821 {
2822 /* switch to the menu bar */
2823
2824 if (0 != (Style & WS_CHILD)
2825 || NULL == (NewMenu = GetMenu(NewWnd)))
2826 {
2827 return FALSE;
2828 }
2829
2830 if (VK_LEFT == Vk)
2831 {
2832 if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu))
2833 {
2834 return FALSE;
2835 }
2836 Id = MenuInfo.MenuItemCount - 1;
2837 }
2838 }
2839 else if (0 != (Style & WS_SYSMENU))
2840 {
2841 /* switch to the system menu */
2842 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2843 }
2844 else
2845 {
2846 return FALSE;
2847 }
2848 }
2849 else /* application returned a new menu to switch to */
2850 {
2851 NewMenu = NextMenu.hmenuNext;
2852 NewWnd = NextMenu.hwndNext;
2853
2854 if (IsMenu(NewMenu) && IsWindow(NewWnd))
2855 {
2856 DWORD Style = GetWindowLongPtrW(NewWnd, GWL_STYLE);
2857
2858 if (0 != (Style & WS_SYSMENU)
2859 && GetSystemMenu(NewWnd, FALSE) == NewMenu)
2860 {
2861 /* get the real system menu */
2862 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2863 }
2864 else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu)
2865 {
2866 /* FIXME: Not sure what to do here;
2867 * perhaps try to track NewMenu as a popup? */
2868
2869 WARN(" -- got confused.\n");
2870 return FALSE;
2871 }
2872 }
2873 else
2874 {
2875 return FALSE;
2876 }
2877 }
2878
2879 if (NewMenu != Mt->TopMenu)
2880 {
2881 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM,
2882 FALSE, 0 );
2883 if (Mt->CurrentMenu != Mt->TopMenu)
2884 {
2885 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
2886 }
2887 }
2888
2889 if (NewWnd != Mt->OwnerWnd)
2890 {
2891 Mt->OwnerWnd = NewWnd;
2892 SetCapture(Mt->OwnerWnd);
2893 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, Mt->OwnerWnd);
2894 }
2895
2896 Mt->TopMenu = Mt->CurrentMenu = NewMenu; /* all subpopups are hidden */
2897 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2898 {
2899 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0);
2900 }
2901
2902 return TRUE;
2903 }
2904
2905 return FALSE;
2906 }
2907
2908 /***********************************************************************
2909 * MenuSuspendPopup
2910 *
2911 * The idea is not to show the popup if the next input message is
2912 * going to hide it anyway.
2913 */
2914 static BOOL FASTCALL
2915 MenuSuspendPopup(MTRACKER* Mt, UINT Message)
2916 {
2917 MSG Msg;
2918
2919 Msg.hwnd = Mt->OwnerWnd;
2920
2921 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2922 Mt->TrackFlags |= TF_SKIPREMOVE;
2923
2924 switch (Message)
2925 {
2926 case WM_KEYDOWN:
2927 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2928 if (WM_KEYUP == Msg.message || WM_PAINT == Msg.message)
2929 {
2930 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2931 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2932 if (WM_KEYDOWN == Msg.message
2933 && (VK_LEFT == Msg.wParam || VK_RIGHT == Msg.wParam))
2934 {
2935 Mt->TrackFlags |= TF_SUSPENDPOPUP;
2936 return TRUE;
2937 }
2938 }
2939 break;
2940 }
2941
2942 /* failures go through this */
2943 Mt->TrackFlags &= ~TF_SUSPENDPOPUP;
2944
2945 return FALSE;
2946 }
2947
2948 /***********************************************************************
2949 * MenuKeyEscape
2950 *
2951 * Handle a VK_ESCAPE key event in a menu.
2952 */
2953 static BOOL FASTCALL
2954 MenuKeyEscape(MTRACKER *Mt, UINT Flags)
2955 {
2956 BOOL EndMenu = TRUE;
2957 ROSMENUINFO MenuInfo;
2958 HMENU MenuTmp, MenuPrev;
2959
2960 if (Mt->CurrentMenu != Mt->TopMenu)
2961 {
2962 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)
2963 && 0 != (MenuInfo.Flags & MF_POPUP))
2964 {
2965 MenuPrev = MenuTmp = Mt->TopMenu;
2966
2967 /* close topmost popup */
2968 while (MenuTmp != Mt->CurrentMenu)
2969 {
2970 MenuPrev = MenuTmp;
2971 MenuTmp = MenuGetSubPopup(MenuPrev);
2972 }
2973
2974 if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev))
2975 {
2976 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, TRUE, Flags);
2977 }
2978 Mt->CurrentMenu = MenuPrev;
2979 EndMenu = FALSE;
2980 }
2981 }
2982
2983 return EndMenu;
2984 }
2985
2986 /***********************************************************************
2987 * MenuKeyLeft
2988 *
2989 * Handle a VK_LEFT key event in a menu.
2990 */
2991 static void FASTCALL
2992 MenuKeyLeft(MTRACKER* Mt, UINT Flags)
2993 {
2994 ROSMENUINFO MenuInfo;
2995 ROSMENUINFO TopMenuInfo;
2996 ROSMENUINFO PrevMenuInfo;
2997 HMENU MenuTmp, MenuPrev;
2998 UINT PrevCol;
2999
3000 MenuPrev = MenuTmp = Mt->TopMenu;
3001
3002 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3003 {
3004 return;
3005 }
3006
3007 /* Try to move 1 column left (if possible) */
3008 if (NO_SELECTED_ITEM != (PrevCol = MenuGetStartOfPrevColumn(&MenuInfo)))
3009 {
3010 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3011 {
3012 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0);
3013 }
3014 return;
3015 }
3016
3017 /* close topmost popup */
3018 while (MenuTmp != Mt->CurrentMenu)
3019 {
3020 MenuPrev = MenuTmp;
3021 MenuTmp = MenuGetSubPopup(MenuPrev);
3022 }
3023
3024 if (! MenuGetRosMenuInfo(&PrevMenuInfo, MenuPrev))
3025 {
3026 return;
3027 }
3028 MenuHideSubPopups(Mt->OwnerWnd, &PrevMenuInfo, TRUE, Flags);
3029 Mt->CurrentMenu = MenuPrev;
3030
3031 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3032 {
3033 return;
3034 }
3035 if ((MenuPrev == Mt->TopMenu) && 0 == (TopMenuInfo.Flags & MF_POPUP))
3036 {
3037 /* move menu bar selection if no more popups are left */
3038
3039 if (! MenuDoNextMenu(Mt, VK_LEFT, Flags))
3040 {
3041 MenuMoveSelection(Mt->OwnerWnd, &TopMenuInfo, ITEM_PREV);
3042 }
3043
3044 if (MenuPrev != MenuTmp || 0 != (Mt->TrackFlags & TF_SUSPENDPOPUP))
3045 {
3046 /* A sublevel menu was displayed - display the next one
3047 * unless there is another displacement coming up */
3048
3049 if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
3050 && MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3051 {
3052 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &TopMenuInfo,
3053 TRUE, Flags);
3054 }
3055 }
3056 }
3057 }
3058
3059 /***********************************************************************
3060 * MenuKeyRight
3061 *
3062 * Handle a VK_RIGHT key event in a menu.
3063 */
3064 static void FASTCALL MenuKeyRight(MTRACKER *Mt, UINT Flags)
3065 {
3066 HMENU hmenutmp;
3067 ROSMENUINFO MenuInfo;
3068 ROSMENUINFO CurrentMenuInfo;
3069 UINT NextCol;
3070
3071 TRACE("MenuKeyRight called, cur %p, top %p.\n",
3072 Mt->CurrentMenu, Mt->TopMenu);
3073
3074 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu)) return;
3075 if ((MenuInfo.Flags & MF_POPUP) || (Mt->CurrentMenu != Mt->TopMenu))
3076 {
3077 /* If already displaying a popup, try to display sub-popup */
3078
3079 hmenutmp = Mt->CurrentMenu;
3080 if (MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
3081 {
3082 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &CurrentMenuInfo, TRUE, Flags);
3083 }
3084
3085 /* if subpopup was displayed then we are done */
3086 if (hmenutmp != Mt->CurrentMenu) return;
3087 }
3088
3089 if (! MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
3090 {
3091 return;
3092 }
3093
3094 /* Check to see if there's another column */
3095 if (NO_SELECTED_ITEM != (NextCol = MenuGetStartOfNextColumn(&CurrentMenuInfo)))
3096 {
3097 TRACE("Going to %d.\n", NextCol);
3098 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3099 {
3100 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NextCol, TRUE, 0);
3101 }
3102 return;
3103 }
3104
3105 if (0 == (MenuInfo.Flags & MF_POPUP)) /* menu bar tracking */
3106 {
3107 if (Mt->CurrentMenu != Mt->TopMenu)
3108 {
3109 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
3110 hmenutmp = Mt->CurrentMenu = Mt->TopMenu;
3111 }
3112 else
3113 {
3114 hmenutmp = NULL;
3115 }
3116
3117 /* try to move to the next item */
3118 if ( !MenuDoNextMenu(Mt, VK_RIGHT, Flags))
3119 MenuMoveSelection(Mt->OwnerWnd, &MenuInfo, ITEM_NEXT);
3120
3121 if ( hmenutmp || Mt->TrackFlags & TF_SUSPENDPOPUP )
3122 {
3123 if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
3124 && MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu))
3125 {
3126 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo,
3127 TRUE, Flags);
3128 }
3129 }
3130 }
3131 }
3132
3133 /***********************************************************************
3134 * MenuTrackMenu
3135 *
3136 * Menu tracking code.
3137 */
3138 static INT FASTCALL MenuTrackMenu(HMENU hmenu, UINT wFlags, INT x, INT y,
3139 HWND hwnd, const RECT *lprect )
3140 {
3141 MSG msg;
3142 ROSMENUINFO MenuInfo;
3143 ROSMENUITEMINFO ItemInfo;
3144 BOOL fRemove;
3145 INT executedMenuId = -1;
3146 MTRACKER mt;
3147 BOOL enterIdleSent = FALSE;
3148
3149 mt.TrackFlags = 0;
3150 mt.CurrentMenu = hmenu;
3151 mt.TopMenu = hmenu;
3152 mt.OwnerWnd = hwnd;
3153 mt.Pt.x = x;
3154 mt.Pt.y = y;
3155
3156 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n",
3157 hmenu, wFlags, x, y, hwnd, lprect ? lprect->left : 0, lprect ? lprect->top : 0,
3158 lprect ? lprect->right : 0, lprect ? lprect->bottom : 0);
3159
3160 if (!IsMenu(hmenu))
3161 {
3162 WARN("Invalid menu handle %p\n", hmenu);
3163 SetLastError( ERROR_INVALID_MENU_HANDLE );
3164 return FALSE;
3165 }
3166
3167 fEndMenu = FALSE;
3168 if (! MenuGetRosMenuInfo(&MenuInfo, hmenu))
3169 {
3170 return FALSE;
3171 }
3172
3173 if (wFlags & TPM_BUTTONDOWN)
3174 {
3175 /* Get the result in order to start the tracking or not */
3176 fRemove = MenuButtonDown( &mt, hmenu, wFlags );
3177 fEndMenu = !fRemove;
3178 }
3179
3180 SetCapture(mt.OwnerWnd);
3181 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, mt.OwnerWnd);
3182
3183 ERR("MenuTrackMenu 1\n");
3184 while (! fEndMenu)
3185 {
3186 PVOID menu = ValidateHandle(mt.CurrentMenu, VALIDATE_TYPE_MENU);
3187 if (!menu) /* sometimes happens if I do a window manager close */