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