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