[USERENV][USER32]
[reactos.git] / reactos / win32ss / user / 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 #ifdef __REACTOS__
1795 PWND pWnd;
1796
1797 pWnd = ValidateHwnd(Wnd);
1798 if (pWnd)
1799 {
1800 if (!pWnd->fnid)
1801 {
1802 NtUserSetWindowFNID(Wnd, FNID_MENU);
1803 }
1804 else
1805 {
1806 if (pWnd->fnid != FNID_MENU)
1807 {
1808 ERR("Wrong window class for Menu!\n");
1809 return 0;
1810 }
1811 }
1812 }
1813 #endif
1814
1815 TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
1816
1817 switch(Message)
1818 {
1819 case WM_CREATE:
1820 {
1821 CREATESTRUCTA *cs = (CREATESTRUCTA *) lParam;
1822 SetWindowLongPtrA(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
1823 return 0;
1824 }
1825
1826 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1827 return MA_NOACTIVATE;
1828
1829 case WM_PAINT:
1830 {
1831 PAINTSTRUCT ps;
1832 BeginPaint(Wnd, &ps);
1833 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrA(Wnd, 0));
1834 EndPaint(Wnd, &ps);
1835 return 0;
1836 }
1837
1838 case WM_PRINTCLIENT:
1839 {
1840 MenuDrawPopupMenu( Wnd, (HDC)wParam,
1841 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
1842 return 0;
1843 }
1844
1845 case WM_ERASEBKGND:
1846 return 1;
1847
1848 case WM_DESTROY:
1849 /* zero out global pointer in case resident popup window was destroyed. */
1850 if (Wnd == top_popup)
1851 {
1852 top_popup = NULL;
1853 top_popup_hmenu = NULL;
1854 }
1855 break;
1856
1857 #ifdef __REACTOS__
1858 case WM_NCDESTROY:
1859 NtUserSetWindowFNID(Wnd, FNID_DESTROY);
1860 break;
1861 #endif
1862
1863 case WM_SHOWWINDOW:
1864 if (0 != wParam)
1865 {
1866 if (0 == GetWindowLongPtrA(Wnd, 0))
1867 {
1868 OutputDebugStringA("no menu to display\n");
1869 }
1870 }
1871 else
1872 {
1873 SetWindowLongPtrA(Wnd, 0, 0);
1874 }
1875 break;
1876
1877 case MM_SETMENUHANDLE:
1878 SetWindowLongPtrA(Wnd, 0, wParam);
1879 break;
1880
1881 case MM_GETMENUHANDLE:
1882 case MN_GETHMENU:
1883 return GetWindowLongPtrA(Wnd, 0);
1884
1885 default:
1886 return DefWindowProcA(Wnd, Message, wParam, lParam);
1887 }
1888 return 0;
1889 }
1890
1891 LRESULT WINAPI
1892 PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
1893 {
1894 #ifdef __REACTOS__ // Do this now, remove after Server side is fixed.
1895 PWND pWnd;
1896
1897 pWnd = ValidateHwnd(Wnd);
1898 if (pWnd)
1899 {
1900 if (!pWnd->fnid)
1901 {
1902 if (Message != WM_NCCREATE)
1903 {
1904 return DefWindowProcW(Wnd, Message, wParam, lParam);
1905 }
1906 NtUserSetWindowFNID(Wnd, FNID_MENU);
1907 }
1908 else
1909 {
1910 if (pWnd->fnid != FNID_MENU)
1911 {
1912 ERR("Wrong window class for Menu!\n");
1913 return 0;
1914 }
1915 }
1916 }
1917 #endif
1918
1919 TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
1920
1921 switch(Message)
1922 {
1923 case WM_CREATE:
1924 {
1925 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
1926 SetWindowLongPtrW(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
1927 return 0;
1928 }
1929
1930 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1931 return MA_NOACTIVATE;
1932
1933 case WM_PAINT:
1934 {
1935 PAINTSTRUCT ps;
1936 BeginPaint(Wnd, &ps);
1937 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrW(Wnd, 0));
1938 EndPaint(Wnd, &ps);
1939 return 0;
1940 }
1941
1942 case WM_PRINTCLIENT:
1943 {
1944 MenuDrawPopupMenu( Wnd, (HDC)wParam,
1945 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
1946 return 0;
1947 }
1948
1949 case WM_ERASEBKGND:
1950 return 1;
1951
1952 case WM_DESTROY:
1953 /* zero out global pointer in case resident popup window was destroyed. */
1954 if (Wnd == top_popup)
1955 {
1956 top_popup = NULL;
1957 top_popup_hmenu = NULL;
1958 }
1959 break;
1960
1961 case WM_SHOWWINDOW:
1962 if (0 != wParam)
1963 {
1964 if (0 == GetWindowLongPtrW(Wnd, 0))
1965 {
1966 OutputDebugStringA("no menu to display\n");
1967 }
1968 }
1969 else
1970 {
1971 SetWindowLongPtrW(Wnd, 0, 0);
1972 }
1973 break;
1974
1975 case MM_SETMENUHANDLE:
1976 SetWindowLongPtrW(Wnd, 0, wParam);
1977 break;
1978
1979 case MM_GETMENUHANDLE:
1980 case MN_GETHMENU:
1981 return GetWindowLongPtrW(Wnd, 0);
1982
1983 default:
1984 return DefWindowProcW(Wnd, Message, wParam, lParam);
1985 }
1986
1987 return 0;
1988 }
1989
1990 /**********************************************************************
1991 * MENU_ParseResource
1992 *
1993 * Parse a standard menu resource and add items to the menu.
1994 * Return a pointer to the end of the resource.
1995 *
1996 * NOTE: flags is equivalent to the mtOption field
1997 */
1998 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1999 {
2000 WORD flags, id = 0;
2001 HMENU hSubMenu;
2002 LPCSTR str;
2003 BOOL end = FALSE;
2004
2005 do
2006 {
2007 flags = GET_WORD(res);
2008
2009 /* remove MF_END flag before passing it to AppendMenu()! */
2010 end = (flags & MF_END);
2011 if(end) flags ^= MF_END;
2012
2013 res += sizeof(WORD);
2014 if(!(flags & MF_POPUP))
2015 {
2016 id = GET_WORD(res);
2017 res += sizeof(WORD);
2018 }
2019 str = res;
2020 if(!unicode)
2021 res += strlen(str) + 1;
2022 else
2023 res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
2024 if (flags & MF_POPUP)
2025 {
2026 hSubMenu = CreatePopupMenu();
2027 if(!hSubMenu) return NULL;
2028 if(!(res = MENU_ParseResource(res, hSubMenu, unicode)))
2029 return NULL;
2030 if(!unicode)
2031 AppendMenuA(hMenu, flags, (UINT)hSubMenu, str);
2032 else
2033 AppendMenuW(hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str);
2034 }
2035 else /* Not a popup */
2036 {
2037 if(!unicode)
2038 {
2039 if (*str == 0)
2040 flags = MF_SEPARATOR;
2041 }
2042 else
2043 {
2044 if (*(LPCWSTR)str == 0)
2045 flags = MF_SEPARATOR;
2046 }
2047
2048 if (flags & MF_SEPARATOR)
2049 {
2050 if (!(flags & (MF_GRAYED | MF_DISABLED)))
2051 flags |= MF_GRAYED | MF_DISABLED;
2052 }
2053
2054 if(!unicode)
2055 AppendMenuA(hMenu, flags, id, *str ? str : NULL);
2056 else
2057 AppendMenuW(hMenu, flags, id,
2058 *(LPCWSTR)str ? (LPCWSTR)str : NULL);
2059 }
2060 } while(!end);
2061 return res;
2062 }
2063
2064
2065 /**********************************************************************
2066 * MENUEX_ParseResource
2067 *
2068 * Parse an extended menu resource and add items to the menu.
2069 * Return a pointer to the end of the resource.
2070 */
2071 static LPCSTR MENUEX_ParseResource(LPCSTR res, HMENU hMenu)
2072 {
2073 WORD resinfo;
2074 MENUITEMINFOW mii;
2075
2076 do
2077 {
2078 mii.cbSize = sizeof(mii);
2079 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2080 mii.fType = GET_DWORD(res);
2081 res += sizeof(DWORD);
2082 mii.fState = GET_DWORD(res);
2083 res += sizeof(DWORD);
2084 mii.wID = GET_DWORD(res);
2085 res += sizeof(DWORD);
2086 resinfo = GET_WORD(res);
2087 res += sizeof(WORD);
2088 /* Align the text on a word boundary. */
2089 res += (~((UINT_PTR)res - 1)) & 1;
2090 mii.dwTypeData = (LPWSTR)res;
2091 mii.cch = strlenW(mii.dwTypeData);
2092 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2093 /* Align the following fields on a dword boundary. */
2094 res += (~((UINT_PTR)res - 1)) & 3;
2095
2096 TRACE("Menu item: [%08x,%08x,%04x,%04x,%S]\n",
2097 mii.fType, mii.fState, mii.wID, resinfo, mii.dwTypeData);
2098
2099 if (resinfo & 1) /* Pop-up? */
2100 {
2101 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2102 res += sizeof(DWORD);
2103 mii.hSubMenu = CreatePopupMenu();
2104 if (!mii.hSubMenu)
2105 {
2106 ERR("CreatePopupMenu failed\n");
2107 return NULL;
2108 }
2109
2110 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu)))
2111 {
2112 ERR("MENUEX_ParseResource failed\n");
2113 DestroyMenu(mii.hSubMenu);
2114 return NULL;
2115 }
2116 mii.fMask |= MIIM_SUBMENU;
2117 mii.fType |= MF_POPUP;
2118 mii.wID = (UINT)mii.hSubMenu;
2119 }
2120 else if (!mii.dwTypeData[0])
2121 mii.fType |= MF_SEPARATOR;
2122
2123 if (!InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii))
2124 ERR("InsertMenuItemW failed\n");
2125 } while (!(resinfo & MF_END));
2126 return res;
2127 }
2128
2129 NTSTATUS WINAPI
2130 User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength)
2131 {
2132 HMENU hmenu = LoadMenuW(User32Instance, L"SYSMENU");
2133 LRESULT Result = (LRESULT)hmenu;
2134 MENUINFO menuinfo = {0};
2135 MENUITEMINFOW info = {0};
2136
2137 // removing space for checkboxes from menu
2138 menuinfo.cbSize = sizeof(menuinfo);
2139 menuinfo.fMask = MIM_STYLE;
2140 GetMenuInfo(hmenu, &menuinfo);
2141 menuinfo.dwStyle |= MNS_NOCHECK;
2142 SetMenuInfo(hmenu, &menuinfo);
2143
2144 // adding bitmaps to menu items
2145 info.cbSize = sizeof(info);
2146 info.fMask |= MIIM_BITMAP;
2147 info.hbmpItem = HBMMENU_POPUP_MINIMIZE;
2148 SetMenuItemInfoW(hmenu, SC_MINIMIZE, FALSE, &info);
2149 info.hbmpItem = HBMMENU_POPUP_RESTORE;
2150 SetMenuItemInfoW(hmenu, SC_RESTORE, FALSE, &info);
2151 info.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
2152 SetMenuItemInfoW(hmenu, SC_MAXIMIZE, FALSE, &info);
2153 info.hbmpItem = HBMMENU_POPUP_CLOSE;
2154 SetMenuItemInfoW(hmenu, SC_CLOSE, FALSE, &info);
2155
2156 return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS));
2157 }
2158
2159
2160 BOOL
2161 MenuInit(VOID)
2162 {
2163 NONCLIENTMETRICSW ncm;
2164
2165 /* get the menu font */
2166 if(!hMenuFont || !hMenuFontBold)
2167 {
2168 ncm.cbSize = sizeof(ncm);
2169 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
2170 {
2171 ERR("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
2172 return FALSE;
2173 }
2174
2175 hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont);
2176 if(hMenuFont == NULL)
2177 {
2178 ERR("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
2179 return FALSE;
2180 }
2181
2182 ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
2183 hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont);
2184 if(hMenuFontBold == NULL)
2185 {
2186 ERR("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
2187 DeleteObject(hMenuFont);
2188 hMenuFont = NULL;
2189 return FALSE;
2190 }
2191 }
2192
2193 return TRUE;
2194 }
2195
2196 VOID
2197 MenuCleanup(VOID)
2198 {
2199 if (hMenuFont)
2200 {
2201 DeleteObject(hMenuFont);
2202 hMenuFont = NULL;
2203 }
2204
2205 if (hMenuFontBold)
2206 {
2207 DeleteObject(hMenuFontBold);
2208 hMenuFontBold = NULL;
2209 }
2210 }
2211
2212 /***********************************************************************
2213 * DrawMenuBarTemp (USER32.@)
2214 *
2215 * UNDOCUMENTED !!
2216 *
2217 * called by W98SE desk.cpl Control Panel Applet
2218 *
2219 * Not 100% sure about the param names, but close.
2220 *
2221 * @implemented
2222 */
2223 DWORD WINAPI
2224 DrawMenuBarTemp(HWND Wnd, HDC DC, LPRECT Rect, HMENU Menu, HFONT Font)
2225 {
2226 ROSMENUINFO MenuInfo;
2227 ROSMENUITEMINFO ItemInfo;
2228 UINT i;
2229 HFONT FontOld = NULL;
2230 BOOL flat_menu = FALSE;
2231
2232 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
2233
2234 if (NULL == Menu)
2235 {
2236 Menu = GetMenu(Wnd);
2237 }
2238
2239 if (NULL == Font)
2240 {
2241 Font = hMenuFont;
2242 }
2243
2244 if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
2245 {
2246 return GetSystemMetrics(SM_CYMENU);
2247 }
2248
2249 TRACE("(%x, %x, %p, %x, %x)\n", Wnd, DC, Rect, Menu, Font);
2250
2251 FontOld = SelectObject(DC, Font);
2252
2253 if (0 == MenuInfo.Height)
2254 {
2255 MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
2256 }
2257
2258 Rect->bottom = Rect->top + MenuInfo.Height;
2259
2260 FillRect(DC, Rect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
2261
2262 SelectObject(DC, GetStockObject(DC_PEN));
2263 SetDCPenColor(DC, GetSysColor(COLOR_3DFACE));
2264 MoveToEx(DC, Rect->left, Rect->bottom - 1, NULL);
2265 LineTo(DC, Rect->right, Rect->bottom - 1);
2266
2267 if (0 == MenuInfo.MenuItemCount)
2268 {
2269 SelectObject(DC, FontOld);
2270 return GetSystemMetrics(SM_CYMENU);
2271 }
2272
2273 MenuInitRosMenuItemInfo(&ItemInfo);
2274 for (i = 0; i < MenuInfo.MenuItemCount; i++)
2275 {
2276 if (MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
2277 {
2278 MenuDrawMenuItem(Wnd, &MenuInfo, Wnd, DC, &ItemInfo,
2279 MenuInfo.Height, TRUE, ODA_DRAWENTIRE);
2280 }
2281 }
2282 MenuCleanupRosMenuItemInfo(&ItemInfo);
2283
2284 SelectObject(DC, FontOld);
2285
2286 return MenuInfo.Height;
2287 }
2288
2289 /***********************************************************************
2290 * MenuShowSubPopup
2291 *
2292 * Display the sub-menu of the selected item of this menu.
2293 * Return the handle of the submenu, or menu if no submenu to display.
2294 */
2295 static HMENU FASTCALL
2296 MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags)
2297 {
2298 extern void FASTCALL NcGetSysPopupPos(HWND Wnd, RECT *Rect);
2299 RECT Rect;
2300 ROSMENUITEMINFO ItemInfo;
2301 ROSMENUINFO SubMenuInfo;
2302 HDC Dc;
2303 HMENU Ret;
2304
2305 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst);
2306
2307 if (NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2308 {
2309 return MenuInfo->Self;
2310 }
2311
2312 MenuInitRosMenuItemInfo(&ItemInfo);
2313 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2314 {
2315 MenuCleanupRosMenuItemInfo(&ItemInfo);
2316 return MenuInfo->Self;
2317 }
2318 if (0 == (ItemInfo.fType & MF_POPUP) || 0 != (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)))
2319 {
2320 MenuCleanupRosMenuItemInfo(&ItemInfo);
2321 return MenuInfo->Self;
2322 }
2323
2324 /* message must be sent before using item,
2325 because nearly everything may be changed by the application ! */
2326
2327 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2328 if (0 == (Flags & TPM_NONOTIFY))
2329 {
2330 SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu,
2331 MAKELONG(MenuInfo->FocusedItem, IS_SYSTEM_MENU(MenuInfo)));
2332 }
2333
2334 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2335 {
2336 MenuCleanupRosMenuItemInfo(&ItemInfo);
2337 return MenuInfo->Self;
2338 }
2339 Rect = ItemInfo.Rect;
2340
2341 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2342 if (0 == (ItemInfo.fState & MF_HILITE))
2343 {
2344 if (0 != (MenuInfo->Flags & MF_POPUP))
2345 {
2346 Dc = GetDC(MenuInfo->Wnd);
2347 }
2348 else
2349 {
2350 Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
2351 }
2352
2353 SelectObject(Dc, hMenuFont);
2354 ItemInfo.fMask |= MIIM_STATE;
2355 ItemInfo.fState |= MF_HILITE;
2356 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2357 MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->Height,
2358 ! (MenuInfo->Flags & MF_POPUP), ODA_DRAWENTIRE);
2359 ReleaseDC(MenuInfo->Wnd, Dc);
2360 }
2361
2362 if (0 == ItemInfo.Rect.top && 0 == ItemInfo.Rect.left
2363 && 0 == ItemInfo.Rect.bottom && 0 == ItemInfo.Rect.right)
2364 {
2365 ItemInfo.Rect = Rect;
2366 }
2367
2368 ItemInfo.fMask |= MIIM_STATE;
2369 ItemInfo.fState |= MF_MOUSESELECT;
2370 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2371
2372 if (IS_SYSTEM_MENU(MenuInfo))
2373 {
2374 MenuInitSysMenuPopup(ItemInfo.hSubMenu, GetWindowLongPtrW(MenuInfo->Wnd, GWL_STYLE),
2375 GetClassLongPtrW(MenuInfo->Wnd, GCL_STYLE), HTSYSMENU);
2376
2377 NcGetSysPopupPos(MenuInfo->Wnd, &Rect);
2378 Rect.top = Rect.bottom;
2379 Rect.right = GetSystemMetrics(SM_CXSIZE);
2380 Rect.bottom = GetSystemMetrics(SM_CYSIZE);
2381 }
2382 else
2383 {
2384 GetWindowRect(MenuInfo->Wnd, &Rect);
2385 if (0 != (MenuInfo->Flags & MF_POPUP))
2386 {
2387 Rect.left += ItemInfo.Rect.right - GetSystemMetrics(SM_CXBORDER);
2388 Rect.top += ItemInfo.Rect.top - 3;
2389 Rect.right = ItemInfo.Rect.left - ItemInfo.Rect.right + GetSystemMetrics(SM_CXBORDER);
2390 Rect.bottom = ItemInfo.Rect.top - ItemInfo.Rect.bottom - 3 - 2
2391 - GetSystemMetrics(SM_CYBORDER);
2392 }
2393 else
2394 {
2395 Rect.left += ItemInfo.Rect.left;
2396 Rect.top += ItemInfo.Rect.bottom;
2397 Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left;
2398 Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top;
2399 }
2400 }
2401
2402 MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->FocusedItem, Flags,
2403 Rect.left, Rect.top, Rect.right, Rect.bottom );
2404 if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2405 {
2406 MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT);
2407 }
2408
2409 Ret = ItemInfo.hSubMenu;
2410 MenuCleanupRosMenuItemInfo(&ItemInfo);
2411
2412 return Ret;
2413 }
2414
2415 /**********************************************************************
2416 * MENU_EndMenu
2417 *
2418 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2419 *
2420 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2421 */
2422 void MENU_EndMenu( HWND hwnd )
2423 {
2424 ROSMENUINFO MenuInfo;
2425 BOOL Ret = FALSE;
2426 if (top_popup_hmenu)
2427 Ret = MenuGetRosMenuInfo(&MenuInfo, top_popup_hmenu);
2428 if (Ret && hwnd == MenuInfo.WndOwner) EndMenu();
2429 }
2430
2431 /***********************************************************************
2432 * MenuHideSubPopups
2433 *
2434 * Hide the sub-popup menus of this menu.
2435 */
2436 static void FASTCALL
2437 MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo,
2438 BOOL SendMenuSelect, UINT wFlags)
2439 {
2440 ROSMENUINFO SubMenuInfo;
2441 ROSMENUITEMINFO ItemInfo;
2442
2443 TRACE("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect);
2444
2445 if (NULL != MenuInfo && NULL != top_popup && NO_SELECTED_ITEM != MenuInfo->FocusedItem)
2446 {
2447 MenuInitRosMenuItemInfo(&ItemInfo);
2448 ItemInfo.fMask |= MIIM_FTYPE | MIIM_STATE;
2449 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo)
2450 || 0 == (ItemInfo.fType & MF_POPUP)
2451 || 0 == (ItemInfo.fState & MF_MOUSESELECT))
2452 {
2453 MenuCleanupRosMenuItemInfo(&ItemInfo);
2454 return;
2455 }
2456 ItemInfo.fState &= ~MF_MOUSESELECT;
2457 ItemInfo.fMask |= MIIM_STATE;
2458 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2459 if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2460 {
2461 MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE, wFlags);
2462 MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL);
2463 DestroyWindow(SubMenuInfo.Wnd);
2464 SubMenuInfo.Wnd = NULL;
2465 MenuSetRosMenuInfo(&SubMenuInfo);
2466
2467 if (!(wFlags & TPM_NONOTIFY))
2468 SendMessageW( WndOwner, WM_UNINITMENUPOPUP, (WPARAM)ItemInfo.hSubMenu,
2469 MAKELPARAM(0, IS_SYSTEM_MENU(&SubMenuInfo)) );
2470 }
2471 }
2472 }
2473
2474 /***********************************************************************
2475 * MenuSwitchTracking
2476 *
2477 * Helper function for menu navigation routines.
2478 */
2479 static void FASTCALL
2480 MenuSwitchTracking(MTRACKER* Mt, PROSMENUINFO PtMenuInfo, UINT Index, UINT wFlags)
2481 {
2482 ROSMENUINFO TopMenuInfo;
2483
2484 TRACE("%x menu=%x 0x%04x\n", Mt, PtMenuInfo->Self, Index);
2485
2486 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu) &&
2487 Mt->TopMenu != PtMenuInfo->Self &&
2488 0 == ((PtMenuInfo->Flags | TopMenuInfo.Flags) & MF_POPUP))
2489 {
2490 /* both are top level menus (system and menu-bar) */
2491 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
2492 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL);
2493 Mt->TopMenu = PtMenuInfo->Self;
2494 }
2495 else
2496 {
2497 MenuHideSubPopups(Mt->OwnerWnd, PtMenuInfo, FALSE, wFlags);
2498 }
2499
2500 MenuSelectItem(Mt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL);
2501 }
2502
2503 /***********************************************************************
2504 * MenuExecFocusedItem
2505 *
2506 * Execute a menu item (for instance when user pressed Enter).
2507 * Return the wID of the executed item. Otherwise, -1 indicating
2508 * that no menu item was executed, -2 if a popup is shown;
2509 * Have to receive the flags for the TrackPopupMenu options to avoid
2510 * sending unwanted message.
2511 *
2512 */
2513 static INT FASTCALL
2514 MenuExecFocusedItem(MTRACKER *Mt, PROSMENUINFO MenuInfo, UINT Flags)
2515 {
2516 ROSMENUITEMINFO ItemInfo;
2517 UINT wID;
2518
2519 TRACE("%p menu=%p\n", Mt, MenuInfo);
2520
2521 if (0 == MenuInfo->MenuItemCount || NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2522 {
2523 return -1;
2524 }
2525
2526 MenuInitRosMenuItemInfo(&ItemInfo);
2527 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2528 {
2529 MenuCleanupRosMenuItemInfo(&ItemInfo);
2530 return -1;
2531 }
2532
2533 TRACE("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu);
2534
2535 if (0 == (ItemInfo.fType & MF_POPUP))
2536 {
2537 if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))
2538 && 0 == (ItemInfo.fType & MF_SEPARATOR))
2539 {
2540 /* If TPM_RETURNCMD is set you return the id, but
2541 do not send a message to the owner */
2542 if (0 == (Flags & TPM_RETURNCMD))
2543 {
2544 if (0 != (MenuInfo->Flags & MF_SYSMENU))
2545 {
2546 PostMessageW(Mt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID,
2547 MAKELPARAM((SHORT) Mt->Pt.x, (SHORT) Mt->Pt.y));
2548 }
2549 else
2550 {
2551 if (MenuInfo->dwStyle & MNS_NOTIFYBYPOS)
2552 PostMessageW(Mt->OwnerWnd, WM_MENUCOMMAND,
2553 MenuInfo->FocusedItem,
2554 (LPARAM)MenuInfo->Self);
2555 else
2556 PostMessageW(Mt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0);
2557 }
2558 }
2559 wID = ItemInfo.wID;
2560 MenuCleanupRosMenuItemInfo(&ItemInfo);
2561 return wID;
2562 }
2563 }
2564 else
2565 {
2566 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, MenuInfo, TRUE, Flags);
2567 return -2;
2568 }
2569
2570 return -1;
2571 }
2572
2573 /***********************************************************************
2574 * MenuButtonDown
2575 *
2576 * Return TRUE if we can go on with menu tracking.
2577 */
2578 static BOOL FASTCALL
2579 MenuButtonDown(MTRACKER* Mt, HMENU PtMenu, UINT Flags)
2580 {
2581 int Index;
2582 ROSMENUINFO MenuInfo;
2583 ROSMENUITEMINFO Item;
2584
2585 TRACE("%x PtMenu=%p\n", Mt, PtMenu);
2586
2587 if (NULL != PtMenu)
2588 {
2589 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2590 {
2591 return FALSE;
2592 }
2593 if (IS_SYSTEM_MENU(&MenuInfo))
2594 {
2595 Index = 0;
2596 }
2597 else
2598 {
2599 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2600 }
2601 MenuInitRosMenuItemInfo(&Item);
2602 if (NO_SELECTED_ITEM == Index || ! MenuGetRosMenuItemInfo(PtMenu, Index, &Item))
2603 {
2604 MenuCleanupRosMenuItemInfo(&Item);
2605 return FALSE;
2606 }
2607
2608 if (!(Item.fType & MF_SEPARATOR) &&
2609 !(Item.fState & (MFS_DISABLED | MFS_GRAYED)) )
2610 {
2611 if (MenuInfo.FocusedItem != Index)
2612 {
2613 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
2614 }
2615
2616 /* If the popup menu is not already "popped" */
2617 if (0 == (Item.fState & MF_MOUSESELECT))
2618 {
2619 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2620 }
2621 }
2622
2623 MenuCleanupRosMenuItemInfo(&Item);
2624
2625 return TRUE;
2626 }
2627
2628 /* else the click was on the menu bar, finish the tracking */
2629
2630 return FALSE;
2631 }
2632
2633 /***********************************************************************
2634 * MenuButtonUp
2635 *
2636 * Return the value of MenuExecFocusedItem if
2637 * the selected item was not a popup. Else open the popup.
2638 * A -1 return value indicates that we go on with menu tracking.
2639 *
2640 */
2641 static INT FASTCALL
2642 MenuButtonUp(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2643 {
2644 INT Id;
2645 ROSMENUINFO MenuInfo;
2646 ROSMENUITEMINFO ItemInfo;
2647
2648 TRACE("%p hmenu=%x\n", Mt, PtMenu);
2649
2650 if (NULL != PtMenu)
2651 {
2652 Id = 0;
2653 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2654 {
2655 return -1;
2656 }
2657
2658 if (! IS_SYSTEM_MENU(&MenuInfo))
2659 {
2660 Id = NtUserMenuItemFromPoint(Mt->OwnerWnd, MenuInfo.Self, Mt->Pt.x, Mt->Pt.y);
2661 }
2662 MenuInitRosMenuItemInfo(&ItemInfo);
2663 if (0 <= Id && MenuGetRosMenuItemInfo(MenuInfo.Self, Id, &ItemInfo) &&
2664 MenuInfo.FocusedItem == Id)
2665 {
2666 if (0 == (ItemInfo.fType & MF_POPUP))
2667 {
2668 INT ExecutedMenuId = MenuExecFocusedItem(Mt, &MenuInfo, Flags);
2669 MenuCleanupRosMenuItemInfo(&ItemInfo);
2670 return (ExecutedMenuId < 0) ? -1 : ExecutedMenuId;
2671 }
2672 MenuCleanupRosMenuItemInfo(&ItemInfo);
2673
2674 /* If we are dealing with the top-level menu */
2675 /* and this is a click on an already "popped" item: */
2676 /* Stop the menu tracking and close the opened submenus */
2677 if (Mt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide)
2678 {
2679 MenuCleanupRosMenuItemInfo(&ItemInfo);
2680 return 0;
2681 }
2682 }
2683 MenuCleanupRosMenuItemInfo(&ItemInfo);
2684 MenuInfo.TimeToHide = TRUE;
2685 MenuSetRosMenuInfo(&MenuInfo);
2686 }
2687
2688 return -1;
2689 }
2690
2691 /***********************************************************************
2692 * MenuPtMenu
2693 *
2694 * Walks menu chain trying to find a menu pt maps to.
2695 */
2696 static HMENU FASTCALL
2697 MenuPtMenu(HMENU Menu, POINT Pt)
2698 {
2699 extern LRESULT DefWndNCHitTest(HWND hWnd, POINT Point);
2700 ROSMENUINFO MenuInfo;
2701 ROSMENUITEMINFO ItemInfo;
2702 HMENU Ret = NULL;
2703 INT Ht;
2704
2705 if (! MenuGetRosMenuInfo(&MenuInfo, Menu))
2706 {
2707 return NULL;
2708 }
2709
2710 /* try subpopup first (if any) */
2711 if (NO_SELECTED_ITEM != MenuInfo.FocusedItem)
2712 {
2713 MenuInitRosMenuItemInfo(&ItemInfo);
2714 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo) &&
2715 0 != (ItemInfo.fType & MF_POPUP) &&
2716 0 != (ItemInfo.fState & MF_MOUSESELECT))
2717 {
2718 Ret = MenuPtMenu(ItemInfo.hSubMenu, Pt);
2719 if (NULL != Ret)
2720 {
2721 MenuCleanupRosMenuItemInfo(&ItemInfo);
2722 return Ret;
2723 }
2724 }
2725 MenuCleanupRosMenuItemInfo(&ItemInfo);
2726 }
2727
2728 /* check the current window (avoiding WM_HITTEST) */
2729 Ht = DefWndNCHitTest(MenuInfo.Wnd, Pt);
2730 if (0 != (MenuInfo.Flags & MF_POPUP))
2731 {
2732 if (HTNOWHERE != Ht && HTERROR != Ht)
2733 {
2734 Ret = Menu;
2735 }
2736 }
2737 else if (HTSYSMENU == Ht)
2738 {
2739 Ret = NtUserGetSystemMenu(MenuInfo.Wnd, FALSE);
2740 }
2741 else if (HTMENU == Ht)
2742 {
2743 Ret = GetMenu(MenuInfo.Wnd);
2744 }
2745
2746 return Ret;
2747 }
2748
2749 /***********************************************************************
2750 * MenuMouseMove
2751 *
2752 * Return TRUE if we can go on with menu tracking.
2753 */
2754 static BOOL FASTCALL
2755 MenuMouseMove(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2756 {
2757 INT Index;
2758 ROSMENUINFO MenuInfo;
2759 ROSMENUITEMINFO ItemInfo;
2760
2761 if (NULL != PtMenu)
2762 {
2763 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2764 {
2765 return TRUE;
2766 }
2767 if (IS_SYSTEM_MENU(&MenuInfo))
2768 {
2769 Index = 0;
2770 }
2771 else
2772 {
2773 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2774 }
2775 }
2776 else
2777 {
2778 Index = NO_SELECTED_ITEM;
2779 }
2780
2781 if (NO_SELECTED_ITEM == Index)
2782 {
2783 if (Mt->CurrentMenu == MenuInfo.Self ||
2784 MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
2785 {
2786 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
2787 TRUE, Mt->TopMenu);
2788 }
2789 }
2790 else if (MenuInfo.FocusedItem != Index)
2791 {
2792 MenuInitRosMenuItemInfo(&ItemInfo);
2793 if (MenuGetRosMenuItemInfo(MenuInfo.Self, Index, &ItemInfo) &&
2794 !(ItemInfo.fType & MF_SEPARATOR))
2795 {
2796 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
2797 if (!(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED)))
2798 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2799 }
2800 MenuCleanupRosMenuItemInfo(&ItemInfo);
2801 }
2802
2803 return TRUE;
2804 }
2805
2806 /***********************************************************************
2807 * MenuGetSubPopup
2808 *
2809 * Return the handle of the selected sub-popup menu (if any).
2810 */
2811 static HMENU FASTCALL
2812 MenuGetSubPopup(HMENU Menu)
2813 {
2814 ROSMENUINFO MenuInfo;
2815 ROSMENUITEMINFO ItemInfo;
2816
2817 if (! MenuGetRosMenuInfo(&MenuInfo, Menu)
2818 || NO_SELECTED_ITEM == MenuInfo.FocusedItem)
2819 {
2820 return NULL;
2821 }
2822
2823 MenuInitRosMenuItemInfo(&ItemInfo);
2824 if (! MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
2825 {
2826 MenuCleanupRosMenuItemInfo(&ItemInfo);
2827 return NULL;
2828 }
2829 if (0 != (ItemInfo.fType & MF_POPUP) && 0 != (ItemInfo.fState & MF_MOUSESELECT))
2830 {
2831 MenuCleanupRosMenuItemInfo(&ItemInfo);
2832 return ItemInfo.hSubMenu;
2833 }
2834
2835 MenuCleanupRosMenuItemInfo(&ItemInfo);
2836 return NULL;
2837 }
2838
2839 /***********************************************************************
2840 * MenuDoNextMenu
2841 *
2842 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2843 */
2844 static LRESULT FASTCALL
2845 MenuDoNextMenu(MTRACKER* Mt, UINT Vk, UINT wFlags)
2846 {
2847 ROSMENUINFO TopMenuInfo;
2848 ROSMENUINFO MenuInfo;
2849
2850 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2851 {
2852 return (LRESULT) FALSE;
2853 }
2854
2855 if ((VK_LEFT == Vk && 0 == TopMenuInfo.FocusedItem)
2856 || (VK_RIGHT == Vk && TopMenuInfo.FocusedItem == TopMenuInfo.MenuItemCount - 1))
2857 {
2858 MDINEXTMENU NextMenu;
2859 HMENU NewMenu;
2860 HWND NewWnd;
2861 UINT Id = 0;
2862
2863 NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(Mt->TopMenu, 0) : Mt->TopMenu;
2864 NextMenu.hmenuNext = NULL;
2865 NextMenu.hwndNext = NULL;
2866 SendMessageW(Mt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
2867
2868 TRACE("%p [%p] -> %p [%p]\n",
2869 Mt->CurrentMenu, Mt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
2870
2871 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
2872 {
2873 DWORD Style = GetWindowLongPtrW(Mt->OwnerWnd, GWL_STYLE);
2874 NewWnd = Mt->OwnerWnd;
2875 if (IS_SYSTEM_MENU(&TopMenuInfo))
2876 {
2877 /* switch to the menu bar */
2878
2879 if (0 != (Style & WS_CHILD)
2880 || NULL == (NewMenu = GetMenu(NewWnd)))
2881 {
2882 return FALSE;
2883 }
2884
2885 if (VK_LEFT == Vk)
2886 {
2887 if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu))
2888 {
2889 return FALSE;
2890 }
2891 Id = MenuInfo.MenuItemCount - 1;
2892 }
2893 }
2894 else if (0 != (Style & WS_SYSMENU))
2895 {
2896 /* switch to the system menu */
2897 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2898 }
2899 else
2900 {
2901 return FALSE;
2902 }
2903 }
2904 else /* application returned a new menu to switch to */
2905 {
2906 NewMenu = NextMenu.hmenuNext;
2907 NewWnd = NextMenu.hwndNext;
2908
2909 if (IsMenu(NewMenu) && IsWindow(NewWnd))
2910 {
2911 DWORD Style = GetWindowLongPtrW(NewWnd, GWL_STYLE);
2912
2913 if (0 != (Style & WS_SYSMENU)
2914 && GetSystemMenu(NewWnd, FALSE) == NewMenu)
2915 {
2916 /* get the real system menu */
2917 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2918 }
2919 else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu)
2920 {
2921 /* FIXME: Not sure what to do here;
2922 * perhaps try to track NewMenu as a popup? */
2923
2924 WARN(" -- got confused.\n");
2925 return FALSE;
2926 }
2927 }
2928 else
2929 {
2930 return FALSE;
2931 }
2932 }
2933
2934 if (NewMenu != Mt->TopMenu)
2935 {
2936 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM,
2937 FALSE, 0 );
2938 if (Mt->CurrentMenu != Mt->TopMenu)
2939 {
2940 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
2941 }
2942 }
2943
2944 if (NewWnd != Mt->OwnerWnd)
2945 {
2946 Mt->OwnerWnd = NewWnd;
2947 NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, Mt->OwnerWnd); // 1
2948 SetCapture(Mt->OwnerWnd); // 2
2949 }
2950
2951 Mt->TopMenu = Mt->CurrentMenu = NewMenu; /* all subpopups are hidden */
2952 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2953 {
2954 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0);
2955 }
2956
2957 return TRUE;
2958 }
2959
2960 return FALSE;
2961 }
2962
2963 /***********************************************************************
2964 * MenuSuspendPopup
2965 *
2966 * The idea is not to show the popup if the next input message is
2967 * going to hide it anyway.
2968 */
2969 static BOOL FASTCALL
2970 MenuSuspendPopup(MTRACKER* Mt, UINT uMsg)
2971 {
2972 MSG msg;
2973
2974 msg.hwnd = Mt->OwnerWnd;
2975
2976 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE); // ported incorrectly since 8317 GvG
2977 // Mt->TrackFlags |= TF_SKIPREMOVE; // This sends TrackMenu into a loop with arrow keys!!!!
2978
2979 switch( uMsg )
2980 {
2981 case WM_KEYDOWN:
2982 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2983 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2984 {
2985 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2986 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2987 if( msg.message == WM_KEYDOWN &&
2988 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2989 {
2990 Mt->TrackFlags |= TF_SUSPENDPOPUP;
2991 return TRUE;
2992 }
2993 }
2994 break;
2995 }
2996 /* failures go through this */
2997 Mt->TrackFlags &= ~TF_SUSPENDPOPUP;
2998 return FALSE;
2999 }
3000
3001 /***********************************************************************
3002 * MenuKeyEscape
3003 *
3004 * Handle a VK_ESCAPE key event in a menu.
3005 */
3006 static BOOL FASTCALL
3007 MenuKeyEscape(MTRACKER *Mt, UINT Flags)
3008 {
3009 BOOL EndMenu = TRUE;
3010 ROSMENUINFO MenuInfo;
3011 HMENU MenuTmp, MenuPrev;
3012
3013 if (Mt->CurrentMenu != Mt->TopMenu)
3014 {
3015 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)
3016 && 0 != (MenuInfo.Flags & MF_POPUP))
3017 {
3018 MenuPrev = MenuTmp = Mt->TopMenu;
3019
3020 /* close topmost popup */
3021 while (MenuTmp != Mt->CurrentMenu)
3022 {
3023 MenuPrev = MenuTmp;
3024 MenuTmp = MenuGetSubPopup(MenuPrev);
3025 }
3026
3027 if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev))
3028 {
3029 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, TRUE, Flags);
3030 }
3031 Mt->CurrentMenu = MenuPrev;
3032 EndMenu = FALSE;
3033 }
3034 }
3035
3036 return EndMenu;
3037 }
3038
3039 /***********************************************************************
3040 * MenuKeyLeft
3041 *
3042 * Handle a VK_LEFT key event in a menu.
3043 */
3044 static void FASTCALL
3045 MenuKeyLeft(MTRACKER* Mt, UINT Flags)
3046 {
3047 ROSMENUINFO MenuInfo;
3048 ROSMENUINFO TopMenuInfo;
3049 ROSMENUINFO PrevMenuInfo;
3050 HMENU MenuTmp, MenuPrev;
3051 UINT PrevCol;
3052
3053 MenuPrev = MenuTmp = Mt->TopMenu;
3054
3055 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3056 {
3057 return;
3058 }
3059
3060 /* Try to move 1 column left (if possible) */
3061 if ( (PrevCol = MenuGetStartOfPrevColumn(&MenuInfo)) != NO_SELECTED_ITEM)
3062 {
3063 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3064 {
3065 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0);
3066 }
3067 return;
3068 }
3069
3070 /* close topmost popup */
3071 while (MenuTmp != Mt->CurrentMenu)
3072 {
3073 MenuPrev = MenuTmp;
3074 MenuTmp = MenuGetSubPopup(MenuPrev);
3075 }
3076
3077 if (! MenuGetRosMenuInfo(&PrevMenuInfo, MenuPrev))
3078 {
3079 return;
3080 }
3081 MenuHideSubPopups(Mt->OwnerWnd, &PrevMenuInfo, TRUE, Flags);
3082 Mt->CurrentMenu = MenuPrev;
3083
3084 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))