768cf65c3f3f089d2f2a73559e754415ff9ab2dc
[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 LRESULT WINAPI PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
1796 {
1797 TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
1798
1799 switch(Message)
1800 {
1801 case WM_CREATE:
1802 {
1803 CREATESTRUCTA *cs = (CREATESTRUCTA *) lParam;
1804 SetWindowLongPtrA(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
1805 return 0;
1806 }
1807
1808 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1809 return MA_NOACTIVATE;
1810
1811 case WM_PAINT:
1812 {
1813 PAINTSTRUCT ps;
1814 BeginPaint(Wnd, &ps);
1815 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrA(Wnd, 0));
1816 EndPaint(Wnd, &ps);
1817 return 0;
1818 }
1819
1820 case WM_PRINTCLIENT:
1821 {
1822 MenuDrawPopupMenu( Wnd, (HDC)wParam,
1823 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
1824 return 0;
1825 }
1826
1827 case WM_ERASEBKGND:
1828 return 1;
1829
1830 case WM_DESTROY:
1831 /* zero out global pointer in case resident popup window was destroyed. */
1832 if (Wnd == top_popup)
1833 {
1834 top_popup = NULL;
1835 top_popup_hmenu = NULL;
1836 }
1837 break;
1838
1839 case WM_SHOWWINDOW:
1840 if (0 != wParam)
1841 {
1842 if (0 == GetWindowLongPtrA(Wnd, 0))
1843 {
1844 OutputDebugStringA("no menu to display\n");
1845 }
1846 }
1847 else
1848 {
1849 SetWindowLongPtrA(Wnd, 0, 0);
1850 }
1851 break;
1852
1853 case MM_SETMENUHANDLE:
1854 SetWindowLongPtrA(Wnd, 0, wParam);
1855 break;
1856
1857 case MM_GETMENUHANDLE:
1858 case MN_GETHMENU:
1859 return GetWindowLongPtrA(Wnd, 0);
1860
1861 default:
1862 return DefWindowProcA(Wnd, Message, wParam, lParam);
1863 }
1864 return 0;
1865 }
1866
1867 LRESULT WINAPI
1868 PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
1869 {
1870 TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
1871
1872 switch(Message)
1873 {
1874 case WM_CREATE:
1875 {
1876 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
1877 SetWindowLongPtrW(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
1878 return 0;
1879 }
1880
1881 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1882 return MA_NOACTIVATE;
1883
1884 case WM_PAINT:
1885 {
1886 PAINTSTRUCT ps;
1887 BeginPaint(Wnd, &ps);
1888 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrW(Wnd, 0));
1889 EndPaint(Wnd, &ps);
1890 return 0;
1891 }
1892
1893 case WM_PRINTCLIENT:
1894 {
1895 MenuDrawPopupMenu( Wnd, (HDC)wParam,
1896 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
1897 return 0;
1898 }
1899
1900 case WM_ERASEBKGND:
1901 return 1;
1902
1903 case WM_DESTROY:
1904 /* zero out global pointer in case resident popup window was destroyed. */
1905 if (Wnd == top_popup)
1906 {
1907 top_popup = NULL;
1908 top_popup_hmenu = NULL;
1909 }
1910 break;
1911
1912 case WM_SHOWWINDOW:
1913 if (0 != wParam)
1914 {
1915 if (0 == GetWindowLongPtrW(Wnd, 0))
1916 {
1917 OutputDebugStringA("no menu to display\n");
1918 }
1919 }
1920 else
1921 {
1922 SetWindowLongPtrW(Wnd, 0, 0);
1923 }
1924 break;
1925
1926 case MM_SETMENUHANDLE:
1927 SetWindowLongPtrW(Wnd, 0, wParam);
1928 break;
1929
1930 case MM_GETMENUHANDLE:
1931 case MN_GETHMENU:
1932 return GetWindowLongPtrW(Wnd, 0);
1933
1934 default:
1935 return DefWindowProcW(Wnd, Message, wParam, lParam);
1936 }
1937
1938 return 0;
1939 }
1940
1941 /**********************************************************************
1942 * MENU_ParseResource
1943 *
1944 * Parse a standard menu resource and add items to the menu.
1945 * Return a pointer to the end of the resource.
1946 *
1947 * NOTE: flags is equivalent to the mtOption field
1948 */
1949 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1950 {
1951 WORD flags, id = 0;
1952 HMENU hSubMenu;
1953 LPCSTR str;
1954 BOOL end = FALSE;
1955
1956 do
1957 {
1958 flags = GET_WORD(res);
1959
1960 /* remove MF_END flag before passing it to AppendMenu()! */
1961 end = (flags & MF_END);
1962 if(end) flags ^= MF_END;
1963
1964 res += sizeof(WORD);
1965 if(!(flags & MF_POPUP))
1966 {
1967 id = GET_WORD(res);
1968 res += sizeof(WORD);
1969 }
1970 str = res;
1971 if(!unicode)
1972 res += strlen(str) + 1;
1973 else
1974 res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1975 if (flags & MF_POPUP)
1976 {
1977 hSubMenu = CreatePopupMenu();
1978 if(!hSubMenu) return NULL;
1979 if(!(res = MENU_ParseResource(res, hSubMenu, unicode)))
1980 return NULL;
1981 if(!unicode)
1982 AppendMenuA(hMenu, flags, (UINT)hSubMenu, str);
1983 else
1984 AppendMenuW(hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str);
1985 }
1986 else /* Not a popup */
1987 {
1988 if(!unicode)
1989 {
1990 if (*str == 0)
1991 flags = MF_SEPARATOR;
1992 }
1993 else
1994 {
1995 if (*(LPCWSTR)str == 0)
1996 flags = MF_SEPARATOR;
1997 }
1998
1999 if (flags & MF_SEPARATOR)
2000 {
2001 if (!(flags & (MF_GRAYED | MF_DISABLED)))
2002 flags |= MF_GRAYED | MF_DISABLED;
2003 }
2004
2005 if(!unicode)
2006 AppendMenuA(hMenu, flags, id, *str ? str : NULL);
2007 else
2008 AppendMenuW(hMenu, flags, id,
2009 *(LPCWSTR)str ? (LPCWSTR)str : NULL);
2010 }
2011 } while(!end);
2012 return res;
2013 }
2014
2015
2016 /**********************************************************************
2017 * MENUEX_ParseResource
2018 *
2019 * Parse an extended menu resource and add items to the menu.
2020 * Return a pointer to the end of the resource.
2021 */
2022 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2023 {
2024 WORD resinfo;
2025 do {
2026 MENUITEMINFOW mii;
2027
2028 mii.cbSize = sizeof(mii);
2029 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
2030 mii.fType = GET_DWORD(res);
2031 res += sizeof(DWORD);
2032 mii.fState = GET_DWORD(res);
2033 res += sizeof(DWORD);
2034 mii.wID = GET_DWORD(res);
2035 res += sizeof(DWORD);
2036 resinfo = GET_WORD(res);
2037 res += sizeof(WORD);
2038 /* Align the text on a word boundary. */
2039 res += (~((UINT_PTR)res - 1)) & 1;
2040 mii.dwTypeData = (LPWSTR) res;
2041 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2042 /* Align the following fields on a dword boundary. */
2043 res += (~((UINT_PTR)res - 1)) & 3;
2044
2045 TRACE("Menu item: [%08x,%08x,%04x,%04x,%S]\n",
2046 mii.fType, mii.fState, mii.wID, resinfo, mii.dwTypeData);
2047
2048 if (resinfo & 1) { /* Pop-up? */
2049 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2050 res += sizeof(DWORD);
2051 mii.hSubMenu = CreatePopupMenu();
2052 if (!mii.hSubMenu)
2053 return NULL;
2054 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2055 DestroyMenu(mii.hSubMenu);
2056 return NULL;
2057 }
2058 mii.fMask |= MIIM_SUBMENU;
2059 mii.fType |= MF_POPUP;
2060 mii.wID = (UINT) mii.hSubMenu;
2061 }
2062 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2063 {
2064 mii.fType |= MF_SEPARATOR;
2065 }
2066 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2067 } while (!(resinfo & MF_END));
2068 return res;
2069 }
2070
2071 NTSTATUS WINAPI
2072 User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength)
2073 {
2074 HMENU hmenu = LoadMenuW(User32Instance, L"SYSMENU");
2075 LRESULT Result = (LRESULT)hmenu;
2076 MENUINFO menuinfo = {0};
2077 MENUITEMINFOW info = {0};
2078
2079 // removing space for checkboxes from menu
2080 menuinfo.cbSize = sizeof(menuinfo);
2081 menuinfo.fMask = MIM_STYLE;
2082 GetMenuInfo(hmenu, &menuinfo);
2083 menuinfo.dwStyle |= MNS_NOCHECK;
2084 SetMenuInfo(hmenu, &menuinfo);
2085
2086 // adding bitmaps to menu items
2087 info.cbSize = sizeof(info);
2088 info.fMask |= MIIM_BITMAP;
2089 info.hbmpItem = HBMMENU_POPUP_MINIMIZE;
2090 SetMenuItemInfoW(hmenu, SC_MINIMIZE, FALSE, &info);
2091 info.hbmpItem = HBMMENU_POPUP_RESTORE;
2092 SetMenuItemInfoW(hmenu, SC_RESTORE, FALSE, &info);
2093 info.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
2094 SetMenuItemInfoW(hmenu, SC_MAXIMIZE, FALSE, &info);
2095 info.hbmpItem = HBMMENU_POPUP_CLOSE;
2096 SetMenuItemInfoW(hmenu, SC_CLOSE, FALSE, &info);
2097
2098 return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS));
2099 }
2100
2101
2102 BOOL
2103 MenuInit(VOID)
2104 {
2105 NONCLIENTMETRICSW ncm;
2106
2107 /* get the menu font */
2108 if(!hMenuFont || !hMenuFontBold)
2109 {
2110 ncm.cbSize = sizeof(ncm);
2111 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
2112 {
2113 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
2114 return FALSE;
2115 }
2116
2117 hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont);
2118 if(hMenuFont == NULL)
2119 {
2120 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
2121 return FALSE;
2122 }
2123
2124 ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
2125 hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont);
2126 if(hMenuFontBold == NULL)
2127 {
2128 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
2129 DeleteObject(hMenuFont);
2130 hMenuFont = NULL;
2131 return FALSE;
2132 }
2133 }
2134
2135 return TRUE;
2136 }
2137
2138 VOID
2139 MenuCleanup(VOID)
2140 {
2141 if (hMenuFont)
2142 {
2143 DeleteObject(hMenuFont);
2144 hMenuFont = NULL;
2145 }
2146
2147 if (hMenuFontBold)
2148 {
2149 DeleteObject(hMenuFontBold);
2150 hMenuFontBold = NULL;
2151 }
2152 }
2153
2154 /***********************************************************************
2155 * DrawMenuBarTemp (USER32.@)
2156 *
2157 * UNDOCUMENTED !!
2158 *
2159 * called by W98SE desk.cpl Control Panel Applet
2160 *
2161 * Not 100% sure about the param names, but close.
2162 *
2163 * @implemented
2164 */
2165 DWORD WINAPI
2166 DrawMenuBarTemp(HWND Wnd, HDC DC, LPRECT Rect, HMENU Menu, HFONT Font)
2167 {
2168 ROSMENUINFO MenuInfo;
2169 ROSMENUITEMINFO ItemInfo;
2170 UINT i;
2171 HFONT FontOld = NULL;
2172 BOOL flat_menu = FALSE;
2173
2174 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
2175
2176 if (NULL == Menu)
2177 {
2178 Menu = GetMenu(Wnd);
2179 }
2180
2181 if (NULL == Font)
2182 {
2183 Font = hMenuFont;
2184 }
2185
2186 if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
2187 {
2188 return GetSystemMetrics(SM_CYMENU);
2189 }
2190
2191 TRACE("(%x, %x, %p, %x, %x)\n", Wnd, DC, Rect, Menu, Font);
2192
2193 FontOld = SelectObject(DC, Font);
2194
2195 if (0 == MenuInfo.Height)
2196 {
2197 MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
2198 }
2199
2200 Rect->bottom = Rect->top + MenuInfo.Height;
2201
2202 FillRect(DC, Rect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
2203
2204 SelectObject(DC, GetStockObject(DC_PEN));
2205 SetDCPenColor(DC, GetSysColor(COLOR_3DFACE));
2206 MoveToEx(DC, Rect->left, Rect->bottom - 1, NULL);
2207 LineTo(DC, Rect->right, Rect->bottom - 1);
2208
2209 if (0 == MenuInfo.MenuItemCount)
2210 {
2211 SelectObject(DC, FontOld);
2212 return GetSystemMetrics(SM_CYMENU);
2213 }
2214
2215 MenuInitRosMenuItemInfo(&ItemInfo);
2216 for (i = 0; i < MenuInfo.MenuItemCount; i++)
2217 {
2218 if (MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
2219 {
2220 MenuDrawMenuItem(Wnd, &MenuInfo, Wnd, DC, &ItemInfo,
2221 MenuInfo.Height, TRUE, ODA_DRAWENTIRE);
2222 }
2223 }
2224 MenuCleanupRosMenuItemInfo(&ItemInfo);
2225
2226 SelectObject(DC, FontOld);
2227
2228 return MenuInfo.Height;
2229 }
2230
2231 /***********************************************************************
2232 * MenuShowSubPopup
2233 *
2234 * Display the sub-menu of the selected item of this menu.
2235 * Return the handle of the submenu, or menu if no submenu to display.
2236 */
2237 static HMENU FASTCALL
2238 MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags)
2239 {
2240 extern void FASTCALL NcGetSysPopupPos(HWND Wnd, RECT *Rect);
2241 RECT Rect;
2242 ROSMENUITEMINFO ItemInfo;
2243 ROSMENUINFO SubMenuInfo;
2244 HDC Dc;
2245 HMENU Ret;
2246
2247 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst);
2248
2249 if (NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2250 {
2251 return MenuInfo->Self;
2252 }
2253
2254 MenuInitRosMenuItemInfo(&ItemInfo);
2255 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2256 {
2257 MenuCleanupRosMenuItemInfo(&ItemInfo);
2258 return MenuInfo->Self;
2259 }
2260 if (0 == (ItemInfo.fType & MF_POPUP) || 0 != (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)))
2261 {
2262 MenuCleanupRosMenuItemInfo(&ItemInfo);
2263 return MenuInfo->Self;
2264 }
2265
2266 /* message must be sent before using item,
2267 because nearly everything may be changed by the application ! */
2268
2269 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2270 if (0 == (Flags & TPM_NONOTIFY))
2271 {
2272 SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu,
2273 MAKELONG(MenuInfo->FocusedItem, IS_SYSTEM_MENU(MenuInfo)));
2274 }
2275
2276 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2277 {
2278 MenuCleanupRosMenuItemInfo(&ItemInfo);
2279 return MenuInfo->Self;
2280 }
2281 Rect = ItemInfo.Rect;
2282
2283 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2284 if (0 == (ItemInfo.fState & MF_HILITE))
2285 {
2286 if (0 != (MenuInfo->Flags & MF_POPUP))
2287 {
2288 Dc = GetDC(MenuInfo->Wnd);
2289 }
2290 else
2291 {
2292 Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
2293 }
2294
2295 SelectObject(Dc, hMenuFont);
2296 ItemInfo.fMask |= MIIM_STATE;
2297 ItemInfo.fState |= MF_HILITE;
2298 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2299 MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->Height,
2300 ! (MenuInfo->Flags & MF_POPUP), ODA_DRAWENTIRE);
2301 ReleaseDC(MenuInfo->Wnd, Dc);
2302 }
2303
2304 if (0 == ItemInfo.Rect.top && 0 == ItemInfo.Rect.left
2305 && 0 == ItemInfo.Rect.bottom && 0 == ItemInfo.Rect.right)
2306 {
2307 ItemInfo.Rect = Rect;
2308 }
2309
2310 ItemInfo.fMask |= MIIM_STATE;
2311 ItemInfo.fState |= MF_MOUSESELECT;
2312 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2313
2314 if (IS_SYSTEM_MENU(MenuInfo))
2315 {
2316 MenuInitSysMenuPopup(ItemInfo.hSubMenu, GetWindowLongPtrW(MenuInfo->Wnd, GWL_STYLE),
2317 GetClassLongPtrW(MenuInfo->Wnd, GCL_STYLE), HTSYSMENU);
2318
2319 NcGetSysPopupPos(MenuInfo->Wnd, &Rect);
2320 Rect.top = Rect.bottom;
2321 Rect.right = GetSystemMetrics(SM_CXSIZE);
2322 Rect.bottom = GetSystemMetrics(SM_CYSIZE);
2323 }
2324 else
2325 {
2326 GetWindowRect(MenuInfo->Wnd, &Rect);
2327 if (0 != (MenuInfo->Flags & MF_POPUP))
2328 {
2329 Rect.left += ItemInfo.Rect.right - GetSystemMetrics(SM_CXBORDER);
2330 Rect.top += ItemInfo.Rect.top - 3;
2331 Rect.right = ItemInfo.Rect.left - ItemInfo.Rect.right + GetSystemMetrics(SM_CXBORDER);
2332 Rect.bottom = ItemInfo.Rect.top - ItemInfo.Rect.bottom - 3 - 2
2333 - GetSystemMetrics(SM_CYBORDER);
2334 }
2335 else
2336 {
2337 Rect.left += ItemInfo.Rect.left;
2338 Rect.top += ItemInfo.Rect.bottom;
2339 Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left;
2340 Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top;
2341 }
2342 }
2343
2344 MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->FocusedItem, Flags,
2345 Rect.left, Rect.top, Rect.right, Rect.bottom );
2346 if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2347 {
2348 MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT);
2349 }
2350
2351 Ret = ItemInfo.hSubMenu;
2352 MenuCleanupRosMenuItemInfo(&ItemInfo);
2353
2354 return Ret;
2355 }
2356
2357 /**********************************************************************
2358 * MENU_EndMenu
2359 *
2360 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2361 *
2362 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2363 */
2364 void MENU_EndMenu( HWND hwnd )
2365 {
2366 ROSMENUINFO MenuInfo;
2367 BOOL Ret = FALSE;
2368 if (top_popup_hmenu)
2369 Ret = MenuGetRosMenuInfo(&MenuInfo, top_popup_hmenu);
2370 if (Ret && hwnd == MenuInfo.WndOwner) EndMenu();
2371 }
2372
2373 /***********************************************************************
2374 * MenuHideSubPopups
2375 *
2376 * Hide the sub-popup menus of this menu.
2377 */
2378 static void FASTCALL
2379 MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo,
2380 BOOL SendMenuSelect, UINT wFlags)
2381 {
2382 ROSMENUINFO SubMenuInfo;
2383 ROSMENUITEMINFO ItemInfo;
2384
2385 TRACE("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect);
2386
2387 if (NULL != MenuInfo && NULL != top_popup && NO_SELECTED_ITEM != MenuInfo->FocusedItem)
2388 {
2389 MenuInitRosMenuItemInfo(&ItemInfo);
2390 ItemInfo.fMask |= MIIM_FTYPE | MIIM_STATE;
2391 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo)
2392 || 0 == (ItemInfo.fType & MF_POPUP)
2393 || 0 == (ItemInfo.fState & MF_MOUSESELECT))
2394 {
2395 MenuCleanupRosMenuItemInfo(&ItemInfo);
2396 return;
2397 }
2398 ItemInfo.fState &= ~MF_MOUSESELECT;
2399 ItemInfo.fMask |= MIIM_STATE;
2400 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2401 if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2402 {
2403 MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE, wFlags);
2404 MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL);
2405 DestroyWindow(SubMenuInfo.Wnd);
2406 SubMenuInfo.Wnd = NULL;
2407 MenuSetRosMenuInfo(&SubMenuInfo);
2408
2409 if (!(wFlags & TPM_NONOTIFY))
2410 SendMessageW( WndOwner, WM_UNINITMENUPOPUP, (WPARAM)ItemInfo.hSubMenu,
2411 MAKELPARAM(0, IS_SYSTEM_MENU(&SubMenuInfo)) );
2412 }
2413 }
2414 }
2415
2416 /***********************************************************************
2417 * MenuSwitchTracking
2418 *
2419 * Helper function for menu navigation routines.
2420 */
2421 static void FASTCALL
2422 MenuSwitchTracking(MTRACKER* Mt, PROSMENUINFO PtMenuInfo, UINT Index, UINT wFlags)
2423 {
2424 ROSMENUINFO TopMenuInfo;
2425
2426 TRACE("%x menu=%x 0x%04x\n", Mt, PtMenuInfo->Self, Index);
2427
2428 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu) &&
2429 Mt->TopMenu != PtMenuInfo->Self &&
2430 0 == ((PtMenuInfo->Flags | TopMenuInfo.Flags) & MF_POPUP))
2431 {
2432 /* both are top level menus (system and menu-bar) */
2433 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
2434 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL);
2435 Mt->TopMenu = PtMenuInfo->Self;
2436 }
2437 else
2438 {
2439 MenuHideSubPopups(Mt->OwnerWnd, PtMenuInfo, FALSE, wFlags);
2440 }
2441
2442 MenuSelectItem(Mt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL);
2443 }
2444
2445 /***********************************************************************
2446 * MenuExecFocusedItem
2447 *
2448 * Execute a menu item (for instance when user pressed Enter).
2449 * Return the wID of the executed item. Otherwise, -1 indicating
2450 * that no menu item was executed, -2 if a popup is shown;
2451 * Have to receive the flags for the TrackPopupMenu options to avoid
2452 * sending unwanted message.
2453 *
2454 */
2455 static INT FASTCALL
2456 MenuExecFocusedItem(MTRACKER *Mt, PROSMENUINFO MenuInfo, UINT Flags)
2457 {
2458 ROSMENUITEMINFO ItemInfo;
2459 UINT wID;
2460
2461 TRACE("%p menu=%p\n", Mt, MenuInfo);
2462
2463 if (0 == MenuInfo->MenuItemCount || NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2464 {
2465 return -1;
2466 }
2467
2468 MenuInitRosMenuItemInfo(&ItemInfo);
2469 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2470 {
2471 MenuCleanupRosMenuItemInfo(&ItemInfo);
2472 return -1;
2473 }
2474
2475 TRACE("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu);
2476
2477 if (0 == (ItemInfo.fType & MF_POPUP))
2478 {
2479 if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))
2480 && 0 == (ItemInfo.fType & MF_SEPARATOR))
2481 {
2482 /* If TPM_RETURNCMD is set you return the id, but
2483 do not send a message to the owner */
2484 if (0 == (Flags & TPM_RETURNCMD))
2485 {
2486 if (0 != (MenuInfo->Flags & MF_SYSMENU))
2487 {
2488 PostMessageW(Mt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID,
2489 MAKELPARAM((SHORT) Mt->Pt.x, (SHORT) Mt->Pt.y));
2490 }
2491 else
2492 {
2493 if (MenuInfo->dwStyle & MNS_NOTIFYBYPOS)
2494 PostMessageW(Mt->OwnerWnd, WM_MENUCOMMAND,
2495 MenuInfo->FocusedItem,
2496 (LPARAM)MenuInfo->Self);
2497 else
2498 PostMessageW(Mt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0);
2499 }
2500 }
2501 wID = ItemInfo.wID;
2502 MenuCleanupRosMenuItemInfo(&ItemInfo);
2503 return wID;
2504 }
2505 }
2506 else
2507 {
2508 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, MenuInfo, TRUE, Flags);
2509 return -2;
2510 }
2511
2512 return -1;
2513 }
2514
2515 /***********************************************************************
2516 * MenuButtonDown
2517 *
2518 * Return TRUE if we can go on with menu tracking.
2519 */
2520 static BOOL FASTCALL
2521 MenuButtonDown(MTRACKER* Mt, HMENU PtMenu, UINT Flags)
2522 {
2523 int Index;
2524 ROSMENUINFO MenuInfo;
2525 ROSMENUITEMINFO Item;
2526
2527 TRACE("%x PtMenu=%p\n", Mt, PtMenu);
2528
2529 if (NULL != PtMenu)
2530 {
2531 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2532 {
2533 return FALSE;
2534 }
2535 if (IS_SYSTEM_MENU(&MenuInfo))
2536 {
2537 Index = 0;
2538 }
2539 else
2540 {
2541 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2542 }
2543 MenuInitRosMenuItemInfo(&Item);
2544 if (NO_SELECTED_ITEM == Index || ! MenuGetRosMenuItemInfo(PtMenu, Index, &Item))
2545 {
2546 MenuCleanupRosMenuItemInfo(&Item);
2547 return FALSE;
2548 }
2549
2550 if (!(Item.fType & MF_SEPARATOR) &&
2551 !(Item.fState & (MFS_DISABLED | MFS_GRAYED)) )
2552 {
2553 if (MenuInfo.FocusedItem != Index)
2554 {
2555 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
2556 }
2557
2558 /* If the popup menu is not already "popped" */
2559 if (0 == (Item.fState & MF_MOUSESELECT))
2560 {
2561 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2562 }
2563 }
2564
2565 MenuCleanupRosMenuItemInfo(&Item);
2566
2567 return TRUE;
2568 }
2569
2570 /* else the click was on the menu bar, finish the tracking */
2571
2572 return FALSE;
2573 }
2574
2575 /***********************************************************************
2576 * MenuButtonUp
2577 *
2578 * Return the value of MenuExecFocusedItem if
2579 * the selected item was not a popup. Else open the popup.
2580 * A -1 return value indicates that we go on with menu tracking.
2581 *
2582 */
2583 static INT FASTCALL
2584 MenuButtonUp(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2585 {
2586 UINT Id;
2587 ROSMENUINFO MenuInfo;
2588 ROSMENUITEMINFO ItemInfo;
2589
2590 TRACE("%p hmenu=%x\n", Mt, PtMenu);
2591
2592 if (NULL != PtMenu)
2593 {
2594 Id = 0;
2595 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2596 {
2597 return -1;
2598 }
2599
2600 if (! IS_SYSTEM_MENU(&MenuInfo))
2601 {
2602 Id = NtUserMenuItemFromPoint(Mt->OwnerWnd, MenuInfo.Self, Mt->Pt.x, Mt->Pt.y);
2603 }
2604 MenuInitRosMenuItemInfo(&ItemInfo);
2605 if (0 <= Id && MenuGetRosMenuItemInfo(MenuInfo.Self, Id, &ItemInfo) &&
2606 MenuInfo.FocusedItem == Id)
2607 {
2608 if (0 == (ItemInfo.fType & MF_POPUP))
2609 {
2610 INT ExecutedMenuId = MenuExecFocusedItem(Mt, &MenuInfo, Flags);
2611 MenuCleanupRosMenuItemInfo(&ItemInfo);
2612 return (ExecutedMenuId < 0) ? -1 : ExecutedMenuId;
2613 }
2614 MenuCleanupRosMenuItemInfo(&ItemInfo);
2615
2616 /* If we are dealing with the top-level menu */
2617 /* and this is a click on an already "popped" item: */
2618 /* Stop the menu tracking and close the opened submenus */
2619 if (Mt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide)
2620 {
2621 MenuCleanupRosMenuItemInfo(&ItemInfo);
2622 return 0;
2623 }
2624 }
2625 MenuCleanupRosMenuItemInfo(&ItemInfo);
2626 MenuInfo.TimeToHide = TRUE;
2627 MenuSetRosMenuInfo(&MenuInfo);
2628 }
2629
2630 return -1;
2631 }
2632
2633 /***********************************************************************
2634 * MenuPtMenu
2635 *
2636 * Walks menu chain trying to find a menu pt maps to.
2637 */
2638 static HMENU FASTCALL
2639 MenuPtMenu(HMENU Menu, POINT Pt)
2640 {
2641 extern LRESULT DefWndNCHitTest(HWND hWnd, POINT Point);
2642 ROSMENUINFO MenuInfo;
2643 ROSMENUITEMINFO ItemInfo;
2644 HMENU Ret = NULL;
2645 INT Ht;
2646
2647 if (! MenuGetRosMenuInfo(&MenuInfo, Menu))
2648 {
2649 return NULL;
2650 }
2651
2652 /* try subpopup first (if any) */
2653 if (NO_SELECTED_ITEM != MenuInfo.FocusedItem)
2654 {
2655 MenuInitRosMenuItemInfo(&ItemInfo);
2656 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo) &&
2657 0 != (ItemInfo.fType & MF_POPUP) &&
2658 0 != (ItemInfo.fState & MF_MOUSESELECT))
2659 {
2660 Ret = MenuPtMenu(ItemInfo.hSubMenu, Pt);
2661 if (NULL != Ret)
2662 {
2663 MenuCleanupRosMenuItemInfo(&ItemInfo);
2664 return Ret;
2665 }
2666 }
2667 MenuCleanupRosMenuItemInfo(&ItemInfo);
2668 }
2669
2670 /* check the current window (avoiding WM_HITTEST) */
2671 Ht = DefWndNCHitTest(MenuInfo.Wnd, Pt);
2672 if (0 != (MenuInfo.Flags & MF_POPUP))
2673 {
2674 if (HTNOWHERE != Ht && HTERROR != Ht)
2675 {
2676 Ret = Menu;
2677 }
2678 }
2679 else if (HTSYSMENU == Ht)
2680 {
2681 Ret = NtUserGetSystemMenu(MenuInfo.Wnd, FALSE);
2682 }
2683 else if (HTMENU == Ht)
2684 {
2685 Ret = GetMenu(MenuInfo.Wnd);
2686 }
2687
2688 return Ret;
2689 }
2690
2691 /***********************************************************************
2692 * MenuMouseMove
2693 *
2694 * Return TRUE if we can go on with menu tracking.
2695 */
2696 static BOOL FASTCALL
2697 MenuMouseMove(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2698 {
2699 UINT Index;
2700 ROSMENUINFO MenuInfo;
2701 ROSMENUITEMINFO ItemInfo;
2702
2703 if (NULL != PtMenu)
2704 {
2705 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2706 {
2707 return TRUE;
2708 }
2709 if (IS_SYSTEM_MENU(&MenuInfo))
2710 {
2711 Index = 0;
2712 }
2713 else
2714 {
2715 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2716 }
2717 }
2718 else
2719 {
2720 Index = NO_SELECTED_ITEM;
2721 }
2722
2723 if (NO_SELECTED_ITEM == Index)
2724 {
2725 if (Mt->CurrentMenu == MenuInfo.Self ||
2726 MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
2727 {
2728 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
2729 TRUE, Mt->TopMenu);
2730 }
2731 }
2732 else if (MenuInfo.FocusedItem != Index)
2733 {
2734 MenuInitRosMenuItemInfo(&ItemInfo);
2735 if (MenuGetRosMenuItemInfo(MenuInfo.Self, Index, &ItemInfo) &&
2736 !(ItemInfo.fType & MF_SEPARATOR))
2737 {
2738 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
2739 if (!(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED)))
2740 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2741 }
2742 MenuCleanupRosMenuItemInfo(&ItemInfo);
2743 }
2744
2745 return TRUE;
2746 }
2747
2748 /***********************************************************************
2749 * MenuGetSubPopup
2750 *
2751 * Return the handle of the selected sub-popup menu (if any).
2752 */
2753 static HMENU FASTCALL
2754 MenuGetSubPopup(HMENU Menu)
2755 {
2756 ROSMENUINFO MenuInfo;
2757 ROSMENUITEMINFO ItemInfo;
2758
2759 if (! MenuGetRosMenuInfo(&MenuInfo, Menu)
2760 || NO_SELECTED_ITEM == MenuInfo.FocusedItem)
2761 {
2762 return NULL;
2763 }
2764
2765 MenuInitRosMenuItemInfo(&ItemInfo);
2766 if (! MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
2767 {
2768 MenuCleanupRosMenuItemInfo(&ItemInfo);
2769 return NULL;
2770 }
2771 if (0 != (ItemInfo.fType & MF_POPUP) && 0 != (ItemInfo.fState & MF_MOUSESELECT))
2772 {
2773 MenuCleanupRosMenuItemInfo(&ItemInfo);
2774 return ItemInfo.hSubMenu;
2775 }
2776
2777 MenuCleanupRosMenuItemInfo(&ItemInfo);
2778 return NULL;
2779 }
2780
2781 /***********************************************************************
2782 * MenuDoNextMenu
2783 *
2784 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2785 */
2786 static LRESULT FASTCALL
2787 MenuDoNextMenu(MTRACKER* Mt, UINT Vk, UINT wFlags)
2788 {
2789 ROSMENUINFO TopMenuInfo;
2790 ROSMENUINFO MenuInfo;
2791
2792 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2793 {
2794 return (LRESULT) FALSE;
2795 }
2796
2797 if ((VK_LEFT == Vk && 0 == TopMenuInfo.FocusedItem)
2798 || (VK_RIGHT == Vk && TopMenuInfo.FocusedItem == TopMenuInfo.MenuItemCount - 1))
2799 {
2800 MDINEXTMENU NextMenu;
2801 HMENU NewMenu;
2802 HWND NewWnd;
2803 UINT Id = 0;
2804
2805 NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(Mt->TopMenu, 0) : Mt->TopMenu;
2806 NextMenu.hmenuNext = NULL;
2807 NextMenu.hwndNext = NULL;
2808 SendMessageW(Mt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
2809
2810 TRACE("%p [%p] -> %p [%p]\n",
2811 Mt->CurrentMenu, Mt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
2812
2813 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
2814 {
2815 DWORD Style = GetWindowLongPtrW(Mt->OwnerWnd, GWL_STYLE);
2816 NewWnd = Mt->OwnerWnd;
2817 if (IS_SYSTEM_MENU(&TopMenuInfo))
2818 {
2819 /* switch to the menu bar */
2820
2821 if (0 != (Style & WS_CHILD)
2822 || NULL == (NewMenu = GetMenu(NewWnd)))
2823 {
2824 return FALSE;
2825 }
2826
2827 if (VK_LEFT == Vk)
2828 {
2829 if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu))
2830 {
2831 return FALSE;
2832 }
2833 Id = MenuInfo.MenuItemCount - 1;
2834 }
2835 }
2836 else if (0 != (Style & WS_SYSMENU))
2837 {
2838 /* switch to the system menu */
2839 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2840 }
2841 else
2842 {
2843 return FALSE;
2844 }
2845 }
2846 else /* application returned a new menu to switch to */
2847 {
2848 NewMenu = NextMenu.hmenuNext;
2849 NewWnd = NextMenu.hwndNext;
2850
2851 if (IsMenu(NewMenu) && IsWindow(NewWnd))
2852 {
2853 DWORD Style = GetWindowLongPtrW(NewWnd, GWL_STYLE);
2854
2855 if (0 != (Style & WS_SYSMENU)
2856 && GetSystemMenu(NewWnd, FALSE) == NewMenu)
2857 {
2858 /* get the real system menu */
2859 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2860 }
2861 else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu)
2862 {
2863 /* FIXME: Not sure what to do here;
2864 * perhaps try to track NewMenu as a popup? */
2865
2866 WARN(" -- got confused.\n");
2867 return FALSE;
2868 }
2869 }
2870 else
2871 {
2872 return FALSE;
2873 }
2874 }
2875
2876 if (NewMenu != Mt->TopMenu)
2877 {
2878 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM,
2879 FALSE, 0 );
2880 if (Mt->CurrentMenu != Mt->TopMenu)
2881 {
2882 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
2883 }
2884 }
2885
2886 if (NewWnd != Mt->OwnerWnd)
2887 {
2888 Mt->OwnerWnd = NewWnd;
2889 SetCapture(Mt->OwnerWnd);
2890 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, Mt->OwnerWnd);
2891 }
2892
2893 Mt->TopMenu = Mt->CurrentMenu = NewMenu; /* all subpopups are hidden */
2894 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2895 {
2896 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0);
2897 }
2898
2899 return TRUE;
2900 }
2901
2902 return FALSE;
2903 }
2904
2905 /***********************************************************************
2906 * MenuSuspendPopup
2907 *
2908 * The idea is not to show the popup if the next input message is
2909 * going to hide it anyway.
2910 */
2911 static BOOL FASTCALL
2912 MenuSuspendPopup(MTRACKER* Mt, UINT Message)
2913 {
2914 MSG Msg;
2915
2916 Msg.hwnd = Mt->OwnerWnd;
2917
2918 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2919 Mt->TrackFlags |= TF_SKIPREMOVE;
2920
2921 switch (Message)
2922 {
2923 case WM_KEYDOWN:
2924 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2925 if (WM_KEYUP == Msg.message || WM_PAINT == Msg.message)
2926 {
2927 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2928 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2929 if (WM_KEYDOWN == Msg.message
2930 && (VK_LEFT == Msg.wParam || VK_RIGHT == Msg.wParam))
2931 {
2932 Mt->TrackFlags |= TF_SUSPENDPOPUP;
2933 return TRUE;
2934 }
2935 }
2936 break;
2937 }
2938
2939 /* failures go through this */
2940 Mt->TrackFlags &= ~TF_SUSPENDPOPUP;
2941
2942 return FALSE;
2943 }
2944
2945 /***********************************************************************
2946 * MenuKeyEscape
2947 *
2948 * Handle a VK_ESCAPE key event in a menu.
2949 */
2950 static BOOL FASTCALL
2951 MenuKeyEscape(MTRACKER *Mt, UINT Flags)
2952 {
2953 BOOL EndMenu = TRUE;
2954 ROSMENUINFO MenuInfo;
2955 HMENU MenuTmp, MenuPrev;
2956
2957 if (Mt->CurrentMenu != Mt->TopMenu)
2958 {
2959 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)
2960 && 0 != (MenuInfo.Flags & MF_POPUP))
2961 {
2962 MenuPrev = MenuTmp = Mt->TopMenu;
2963
2964 /* close topmost popup */
2965 while (MenuTmp != Mt->CurrentMenu)
2966 {
2967 MenuPrev = MenuTmp;
2968 MenuTmp = MenuGetSubPopup(MenuPrev);
2969 }
2970
2971 if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev))
2972 {
2973 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, TRUE, Flags);
2974 }
2975 Mt->CurrentMenu = MenuPrev;
2976 EndMenu = FALSE;
2977 }
2978 }
2979
2980 return EndMenu;
2981 }
2982
2983 /***********************************************************************
2984 * MenuKeyLeft
2985 *
2986 * Handle a VK_LEFT key event in a menu.
2987 */
2988 static void FASTCALL
2989 MenuKeyLeft(MTRACKER* Mt, UINT Flags)
2990 {
2991 ROSMENUINFO MenuInfo;
2992 ROSMENUINFO TopMenuInfo;
2993 ROSMENUINFO PrevMenuInfo;
2994 HMENU MenuTmp, MenuPrev;
2995 UINT PrevCol;
2996
2997 MenuPrev = MenuTmp = Mt->TopMenu;
2998
2999 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3000 {
3001 return;
3002 }
3003
3004 /* Try to move 1 column left (if possible) */
3005 if (NO_SELECTED_ITEM != (PrevCol = MenuGetStartOfPrevColumn(&MenuInfo)))
3006 {
3007 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3008 {
3009 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0);
3010 }
3011 return;
3012 }
3013
3014 /* close topmost popup */
3015 while (MenuTmp != Mt->CurrentMenu)
3016 {
3017 MenuPrev = MenuTmp;
3018 MenuTmp = MenuGetSubPopup(MenuPrev);
3019 }
3020
3021 if (! MenuGetRosMenuInfo(&PrevMenuInfo, MenuPrev))
3022 {
3023 return;
3024 }
3025 MenuHideSubPopups(Mt->OwnerWnd, &PrevMenuInfo, TRUE, Flags);
3026 Mt->CurrentMenu = MenuPrev;
3027
3028 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3029 {
3030 return;
3031 }
3032 if ((MenuPrev == Mt->TopMenu) && 0 == (TopMenuInfo.Flags & MF_POPUP))
3033 {
3034 /* move menu bar selection if no more popups are left */
3035
3036 if (! MenuDoNextMenu(Mt, VK_LEFT, Flags))
3037 {
3038 MenuMoveSelection(Mt->OwnerWnd, &TopMenuInfo, ITEM_PREV);
3039 }
3040
3041 if (MenuPrev != MenuTmp || 0 != (Mt->TrackFlags & TF_SUSPENDPOPUP))
3042 {
3043 /* A sublevel menu was displayed - display the next one
3044 * unless there is another displacement coming up */
3045
3046 if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
3047 && MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3048 {
3049 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &TopMenuInfo,
3050 TRUE, Flags);
3051 }
3052 }
3053 }
3054 }
3055
3056 /***********************************************************************
3057 * MenuKeyRight
3058 *
3059 * Handle a VK_RIGHT key event in a menu.
3060 */
3061 static void FASTCALL MenuKeyRight(MTRACKER *Mt, UINT Flags)
3062 {
3063 HMENU hmenutmp;
3064 ROSMENUINFO MenuInfo;
3065 ROSMENUINFO CurrentMenuInfo;
3066 UINT NextCol;
3067
3068 TRACE("MenuKeyRight called, cur %p, top %p.\n",
3069 Mt->CurrentMenu, Mt->TopMenu);
3070
3071 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu)) return;
3072 if ((MenuInfo.Flags & MF_POPUP) || (Mt->CurrentMenu != Mt->TopMenu))
3073 {
3074 /* If already displaying a popup, try to display sub-popup */
3075
3076 hmenutmp = Mt->CurrentMenu;
3077 if (MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
3078 {
3079 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &CurrentMenuInfo, TRUE, Flags);
3080 }
3081
3082 /* if subpopup was displayed then we are done */
3083 if (hmenutmp != Mt->CurrentMenu) return;
3084 }
3085
3086 if (! MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
3087 {
3088 return;
3089 }
3090
3091 /* Check to see if there's another column */
3092 if (NO_SELECTED_ITEM != (NextCol = MenuGetStartOfNextColumn(&CurrentMenuInfo)))
3093 {
3094 TRACE("Going to %d.\n", NextCol);
3095 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3096 {
3097 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NextCol, TRUE, 0);
3098 }
3099 return;
3100 }
3101
3102 if (0 == (MenuInfo.Flags & MF_POPUP)) /* menu bar tracking */
3103 {
3104 if (Mt->CurrentMenu != Mt->TopMenu)
3105 {
3106 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
3107 hmenutmp = Mt->CurrentMenu = Mt->TopMenu;
3108 }
3109 else
3110 {
3111 hmenutmp = NULL;
3112 }
3113
3114 /* try to move to the next item */
3115 if ( !MenuDoNextMenu(Mt, VK_RIGHT, Flags))
3116 MenuMoveSelection(Mt->OwnerWnd, &MenuInfo, ITEM_NEXT);
3117
3118 if ( hmenutmp || Mt->TrackFlags & TF_SUSPENDPOPUP )
3119 {
3120 if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
3121 && MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu))
3122 {
3123 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo,
3124 TRUE, Flags);
3125 }
3126 }
3127 }
3128 }
3129
3130 /***********************************************************************
3131 * MenuTrackMenu
3132 *
3133 * Menu tracking code.
3134 */
3135 static INT FASTCALL MenuTrackMenu(HMENU hmenu, UINT wFlags, INT x, INT y,
3136 HWND hwnd, const RECT *lprect )
3137 {
3138 MSG msg;
3139 ROSMENUINFO MenuInfo;
3140 ROSMENUITEMINFO ItemInfo;
3141 BOOL fRemove;
3142 INT executedMenuId = -1;
3143 MTRACKER mt;
3144 BOOL enterIdleSent = FALSE;
3145
3146 mt.TrackFlags = 0;
3147 mt.CurrentMenu = hmenu;
3148 mt.TopMenu = hmenu;
3149 mt.OwnerWnd = hwnd;
3150 mt.Pt.x = x;
3151 mt.Pt.y = y;
3152
3153 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n",
3154 hmenu, wFlags, x, y, hwnd, lprect ? lprect->left : 0, lprect ? lprect->top : 0,
3155 lprect ? lprect->right : 0, lprect ? lprect->bottom : 0);
3156
3157 if (!IsMenu(hmenu))
3158 {
3159 WARN("Invalid menu handle %p\n", hmenu);
3160 SetLastError( ERROR_INVALID_MENU_HANDLE );
3161 return FALSE;
3162 }
3163
3164 fEndMenu = FALSE;
3165 if (! MenuGetRosMenuInfo(&MenuInfo, hmenu))
3166 {
3167 return FALSE;
3168 }
3169
3170 if (wFlags & TPM_BUTTONDOWN)
3171 {
3172 /* Get the result in order to start the tracking or not */
3173 fRemove = MenuButtonDown( &mt, hmenu, wFlags );
3174 fEndMenu = !fRemove;
3175 }
3176
3177 SetCapture(mt.OwnerWnd);
3178 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, mt.OwnerWnd);
3179
3180 FIXME("MenuTrackMenu 1\n");
3181 while (! fEndMenu)
3182 {
3183 PVOID menu = ValidateHandle(mt.CurrentMenu, VALIDATE_TYPE_MENU);
3184 if (!menu) /* sometimes happens if I do a window manager close */
3185 break;
3186
3187 /* we have to keep the message in the queue until it's
3188 * clear that menu loop is not over yet. */
3189
3190 for (;;)
3191 {
3192 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3193 {
3194 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3195 /* remove the message from the queue */
3196 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3197 }
3198 else
3199 {
3200 if (!enterIdleSent)
3201 {
3202 HWND win = MenuInfo.Flags & MF_POPUP ? MenuInfo.Wnd : NULL;
3203 enterIdleSent = TRUE;
3204 SendMessageW( mt.OwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM) win);
3205 }
3206 WaitMessage();
3207 }
3208 //FIXME("MenuTrackMenu loop 1\n");
3209 }
3210
3211 /* check if EndMenu() tried to cancel us, by posting this message */
3212 if (msg.message == WM_CANCELMODE)
3213 {
3214 /* we are now out of the loop */
3215 fEndMenu = TRUE;
3216
3217 /* remove the message from the queue */
3218 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3219
3220 /* break out of internal loop, ala ESCAPE */
3221 break;
3222 }
3223
3224 TranslateMessage( &msg );
3225 mt.Pt = msg.pt;
3226
3227 if ( (msg.hwnd == MenuInfo.Wnd) || (msg.message!=WM_TIMER) )
3228 enterIdleSent=FALSE;
3229
3230 fRemove = FALSE;
3231 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3232 {
3233 /*
3234 * Use the mouse coordinates in lParam instead of those in the MSG
3235 * struct to properly handle synthetic messages. They are already
3236 * in screen coordinates.
3237 */
3238 mt.Pt.x = (short)LOWORD(msg.lParam);
3239 mt.Pt.y = (short)HIWORD(msg.lParam);
3240
3241 /* Find a menu for this mouse event */
3242 hmenu = MenuPtMenu(mt.TopMenu, mt.Pt);
3243
3244 switch(msg.message)
3245 {
3246 /* no WM_NC... messages in captured state */
3247
3248 case WM_RBUTTONDBLCLK:
3249 case WM_RBUTTONDOWN:
3250 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3251 /* fall through */
3252 case WM_LBUTTONDBLCLK:
3253 case WM_LBUTTONDOWN:
3254 /* If the message belongs to the menu, removes it from the queue */
3255 /* Else, end menu tracking */
3256 fRemove = MenuButtonDown(&mt, hmenu, wFlags);
3257 fEndMenu = !fRemove;
3258 break;
3259
3260 case WM_RBUTTONUP:
3261 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3262 /* fall through */
3263 case WM_LBUTTONUP:
3264 /* Check if a menu was selected by the mouse */
3265 if (hmenu)
3266 {
3267 executedMenuId = MenuButtonUp( &mt, hmenu, wFlags);
3268 TRACE("executedMenuId %d\n", executedMenuId);
3269
3270 /* End the loop if executedMenuId is an item ID */
3271 /* or if the job was done (executedMenuId = 0). */
3272 fEndMenu = fRemove = (executedMenuId != -1);
3273 }
3274 /* No menu was selected by the mouse */
3275 /* if the function was called by TrackPopupMenu, continue
3276 with the menu tracking. If not, stop it */
3277 else
3278 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3279
3280 break;
3281
3282 case WM_MOUSEMOVE:
3283 /* the selected menu item must be changed every time */
3284 /* the mouse moves. */
3285
3286 if (hmenu)
3287 fEndMenu |= !MenuMouseMove( &mt, hmenu, wFlags );
3288
3289 } /* switch(msg.message) - mouse */
3290 }
3291 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3292 {
3293 fRemove = TRUE; /* Keyboard messages are always removed */
3294 switch(msg.message)
3295 {
3296 case WM_KEYDOWN:
3297 case WM_SYSKEYDOWN:
3298 switch(msg.wParam)
3299 {
3300 case VK_MENU:
3301 case VK_F10:
3302 fEndMenu = TRUE;
3303 break;
3304
3305 case VK_HOME:
3306 case VK_END:
3307 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3308 {
3309 MenuSelectItem(mt.OwnerWnd, &MenuInfo,
3310 NO_SELECTED_ITEM, FALSE, 0 );
3311 MenuMoveSelection(mt.OwnerWnd, &MenuInfo,
3312 VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV);
3313 }
3314 break;
3315
3316 case VK_UP:
3317 case VK_DOWN: /* If on menu bar, pull-down the menu */
3318 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3319 {
3320 if (!(MenuInfo.Flags & MF_POPUP))
3321 {
3322 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
3323 mt.CurrentMenu = MenuShowSubPopup(mt.OwnerWnd, &MenuInfo, TRUE, wFlags);
3324 }
3325 else /* otherwise try to move selection */
3326 MenuMoveSelection(mt.OwnerWnd, &MenuInfo,
3327 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3328 }
3329 break;
3330
3331 case VK_LEFT:
3332 MenuKeyLeft( &mt, wFlags );
3333 break;
3334
3335 case VK_RIGHT:
3336 MenuKeyRight( &mt, wFlags );
3337 break;
3338
3339 case VK_ESCAPE:
3340 fEndMenu = MenuKeyEscape(&mt, wFlags);
3341 break;
3342
3343 case VK_F1:
3344 {
3345 HELPINFO hi;
3346 hi.cbSize = sizeof(HELPINFO);
3347 hi.iContextType = HELPINFO_MENUITEM;
3348 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3349 {
3350 if (MenuInfo.FocusedItem == NO_SELECTED_ITEM)
3351 hi.iCtrlId = 0;
3352 else
3353 {
3354 MenuInitRosMenuItemInfo(&ItemInfo);
3355 if (MenuGetRosMenuItemInfo(MenuInfo.Self,
3356 MenuInfo.FocusedItem,
3357 &ItemInfo))
3358 {
3359 hi.iCtrlId = ItemInfo.wID;
3360 }
3361 else
3362 {
3363 hi.iCtrlId = 0;
3364 }
3365 MenuCleanupRosMenuItemInfo(&ItemInfo);
3366 }
3367 }
3368 hi.hItemHandle = hmenu;
3369 hi.dwContextId = MenuInfo.dwContextHelpID;
3370 hi.MousePos = msg.pt;
3371 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3372 break;
3373 }
3374
3375 default:
3376 break;
3377 }
3378 break; /* WM_KEYDOWN */
3379
3380 case WM_CHAR:
3381 case WM_SYSCHAR:
3382 {
3383 UINT pos;
3384
3385 if (! MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu)) break;
3386 if (msg.wParam == L'\r' || msg.wParam == L' ')
3387 {
3388 executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags);
3389 fEndMenu = (executedMenuId != -2);
3390 break;
3391 }
3392
3393 /* Hack to avoid control chars. */
3394 /* We will find a better way real soon... */
3395 if (msg.wParam < 32) break;
3396
3397 pos = MenuFindItemByKey(mt.OwnerWnd, &MenuInfo,
3398 LOWORD(msg.wParam), FALSE);
3399 if (pos == (UINT)-2) fEndMenu = TRUE;
3400 else if (pos == (UINT)-1) MessageBeep(0);
3401 else
3402 {
3403 MenuSelectItem(mt.OwnerWnd, &MenuInfo, pos,
3404 TRUE, 0);
3405 executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags);
3406 fEndMenu = (executedMenuId != -2);
3407 }
3408 }
3409 break;
3410 } /* switch(msg.message) - kbd */
3411 }
3412 else
3413 {
3414 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3415 DispatchMessageW( &msg );
3416 //FIXME("MenuTrackMenu loop 2\n");
3417 continue;
3418 }
3419
3420 if (!fEndMenu) fRemove = TRUE;
3421
3422 /* finally remove message from the queue */
3423
3424 if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) )
3425 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3426 else mt.TrackFlags &= ~TF_SKIPREMOVE;
3427 //FIXME("MenuTrackMenu loop 3\n");
3428 }
3429 FIXME("MenuTrackMenu 2\n");
3430
3431 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, NULL);
3432 SetCapture(NULL); /* release the capture */
3433
3434 /* If dropdown is still painted and the close box is clicked on
3435 then the menu will be destroyed as part of the DispatchMessage above.
3436 This will then invalidate the menu handle in mt.hTopMenu. We should
3437 check for this first. */
3438 if( IsMenu( mt.TopMenu ) )
3439 {
3440 if (IsWindow(mt.OwnerWnd))
3441 {
3442 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
3443 {
3444 MenuHideSubPopups(mt.OwnerWnd, &MenuInfo, FALSE, wFlags);
3445
3446 if (MenuInfo.Flags & MF_POPUP)
3447 {
3448 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0);
3449 DestroyWindow(MenuInfo.Wnd);
3450 MenuInfo.Wnd = NULL;
3451
3452 if (!(MenuInfo.Flags & TPM_NONOTIFY))
3453 SendMessageW( mt.OwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.TopMenu,
3454 MAKELPARAM(0, IS_SYSTEM_MENU(&MenuInfo)) );
3455
3456 }
3457 MenuSelectItem( mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, 0 );
3458 }
3459
3460 SendMessageW( mt.OwnerWnd, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0 );
3461 }
3462
3463 /* Reset the variable for hiding menu */
3464 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
3465 {
3466 MenuInfo.TimeToHide = FALSE;
3467 MenuSetRosMenuInfo(&MenuInfo);
3468 }
3469 }
3470
3471 /* The return value is only used by TrackPopupMenu */
3472 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3473 if (executedMenuId == -1) executedMenuId = 0;
3474 return executedMenuId;
3475 }
3476
3477 /***********************************************************************
3478 * MenuInitTracking
3479 */
3480 static BOOL FASTCALL MenuInitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3481 {
3482 ROSMENUINFO MenuInfo;
3483
3484 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3485
3486 HideCaret(0);
3487
3488 MenuGetRosMenuInfo(&MenuInfo, hMenu);
3489 /* This makes the menus of applications built with Delphi work.
3490 * It also enables menus to be displayed in more than one window,
3491 * but there are some bugs left that need to be fixed in this case.
3492 */
3493 if(MenuInfo.Self == hMenu)
3494 {
3495 MenuInfo.Wnd = hWnd;
3496 MenuSetRosMenuInfo(&MenuInfo);
3497 }
3498
3499 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3500 if (!(wFlags & TPM_NONOTIFY))
3501 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3502
3503 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3504
3505 if (!(wFlags & TPM_NONOTIFY))
3506 {
3507 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3508 /* If an app changed/recreated menu bar entries in WM_INITMENU
3509 * menu sizes will be recalculated once the menu created/shown.
3510 */
3511
3512 if (!MenuInfo.Height)
3513 {
3514 /* app changed/recreated menu bar entries in WM_INITMENU
3515 Recalculate menu sizes else clicks will not work */
3516 SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3517 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3518
3519 }
3520 }
3521
3522 IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART,
3523 hWnd,
3524 MenuInfo.Flags & MF_SYSMENU ? OBJID_SYSMENU : OBJID_MENU,
3525 CHILDID_SELF, 0);
3526 return TRUE;
3527 }
3528 /***********************************************************************
3529 * MenuExitTracking
3530 */
3531 static BOOL FASTCALL MenuExitTracking(HWND hWnd, BOOL bPopup)
3532 {
3533 TRACE("hwnd=%p\n", hWnd);
3534
3535 IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, hWnd, OBJID_WINDOW, CHILDID_SELF, 0);
3536 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3537 ShowCaret(0);
3538 top_popup = 0;
3539 top_popup_hmenu = NULL;
3540 return TRUE;
3541 }
3542
3543 /***********************************************************************
3544 * MenuTrackMouseMenuBar
3545 *
3546 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3547 */
3548 VOID MenuTrackMouseMenuBar( HWND hWnd, ULONG ht, POINT pt)
3549 {
3550 HMENU hMenu = (ht == HTSYSMENU) ? NtUserGetSystemMenu( hWnd, FALSE) : GetMenu(hWnd);
3551 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3552
3553 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
3554
3555 if (IsMenu(hMenu))
3556 {
3557 /* map point to parent client coordinates */
3558 HWND Parent = GetAncestor(hWnd, GA_PARENT );
3559 if (Parent != GetDesktopWindow())
3560 {
3561 ScreenToClient(Parent, &pt);
3562 }
3563
3564 MenuInitTracking(hWnd, hMenu, FALSE, wFlags);
3565 MenuTrackMenu(hMenu, wFlags, pt.x, pt.y, hWnd, NULL);
3566 MenuExitTracking(hWnd, FALSE);
3567 }
3568 }
3569
3570
3571 /***********************************************************************
3572 * MenuTrackKbdMenuBar
3573 *
3574 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3575 */
3576 VOID MenuTrackKbdMenuBar(HWND hwnd, UINT wParam, WCHAR wChar)
3577 {
3578 UINT uItem = NO_SELECTED_ITEM;