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