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 */
3545 static BOOL FASTCALL MenuInitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3546 {
3547 ROSMENUINFO MenuInfo;
3548
3549 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3550
3551 HideCaret(0);
3552
3553 /* This makes the menus of applications built with Delphi work.
3554 * It also enables menus to be displayed in more than one window,
3555 * but there are some bugs left that need to be fixed in this case.
3556 */
3557 if (MenuGetRosMenuInfo(&MenuInfo, hMenu))
3558 {
3559 MenuInfo.Wnd = hWnd;
3560 MenuSetRosMenuInfo(&MenuInfo);
3561 }
3562
3563 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3564 if (!(wFlags & TPM_NONOTIFY))
3565 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3566
3567 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3568
3569 if (!(wFlags & TPM_NONOTIFY))
3570 {
3571 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3572 /* If an app changed/recreated menu bar entries in WM_INITMENU
3573 * menu sizes will be recalculated once the menu created/shown.
3574 */
3575
3576 if (!MenuInfo.Height)
3577 {
3578 /* app changed/recreated menu bar entries in WM_INITMENU
3579 Recalculate menu sizes else clicks will not work */
3580 SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3581 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3582
3583 }
3584 }
3585
3586 IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART,
3587 hWnd,
3588 MenuInfo.Flags & MF_SYSMENU ? OBJID_SYSMENU : OBJID_MENU,
3589 CHILDID_SELF, 0);
3590 return TRUE;
3591 }
3592 /***********************************************************************
3593 * MenuExitTracking
3594 */
3595 static BOOL FASTCALL MenuExitTracking(HWND hWnd, BOOL bPopup)
3596 {
3597 TRACE("hwnd=%p\n", hWnd);
3598
3599 IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, hWnd, OBJID_WINDOW, CHILDID_SELF, 0);
3600 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3601 ShowCaret(0);
3602 top_popup = 0;
3603 top_popup_hmenu = NULL;
3604 return TRUE;
3605 }
3606
3607 /***********************************************************************
3608 * MenuTrackMouseMenuBar
3609 *
3610 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3611 */
3612 VOID MenuTrackMouseMenuBar( HWND hWnd, ULONG ht, POINT pt)
3613 {
3614 HMENU hMenu = (ht == HTSYSMENU) ? NtUserGetSystemMenu( hWnd, FALSE) : GetMenu(hWnd);
3615 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3616
3617 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
3618
3619 if (IsMenu(hMenu))
3620 {
3621 /* map point to parent client coordinates */
3622 HWND Parent = GetAncestor(hWnd, GA_PARENT );
3623 if (Parent != GetDesktopWindow())
3624 {
3625 ScreenToClient(Parent, &pt);
3626 }
3627
3628 MenuInitTracking(hWnd, hMenu, FALSE, wFlags);
3629 MenuTrackMenu(hMenu, wFlags, pt.x, pt.y, hWnd, NULL);
3630 MenuExitTracking(hWnd, FALSE);
3631 }
3632 }
3633
3634
3635 /***********************************************************************
3636 * MenuTrackKbdMenuBar
3637 *
3638 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3639 */
3640 VOID MenuTrackKbdMenuBar(HWND hwnd, UINT wParam, WCHAR wChar)
3641 {
3642 UINT uItem = NO_SELECTED_ITEM;
3643 HMENU hTrackMenu;
3644 ROSMENUINFO MenuInfo;
3645 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3646
3647 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3648
3649 /* find window that has a menu */
3650
3651 while (!((GetWindowLongPtrW( hwnd, GWL_STYLE ) &
3652 (WS_CHILD | WS_POPUP)) != WS_CHILD))
3653 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3654
3655 /* check if we have to track a system menu */
3656
3657 hTrackMenu = GetMenu( hwnd );
3658 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3659 {
3660 if (!(GetWindowLongPtrW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3661 hTrackMenu = NtUserGetSystemMenu(hwnd, FALSE);
3662 uItem = 0;
3663 wParam |= HTSYSMENU; /* prevent item lookup */
3664 }
3665
3666 if (!IsMenu( hTrackMenu )) return;
3667
3668 MenuInitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3669
3670 if (! MenuGetRosMenuInfo(&MenuInfo, hTrackMenu))
3671 {
3672 goto track_menu;
3673 }
3674
3675 if( wChar && wChar != ' ' )
3676 {
3677 uItem = MenuFindItemByKey( hwnd, &MenuInfo, wChar, (wParam & HTSYSMENU) );
3678 if ( uItem >= (UINT)(-2) )
3679 {
3680 if( uItem == (UINT)(-1) ) MessageBeep(0);
3681 /* schedule end of menu tracking */
3682 wFlags |= TF_ENDMENU;
3683 goto track_menu;
3684 }
3685 }
3686
3687 MenuSelectItem( hwnd, &MenuInfo, uItem, TRUE, 0 );
3688
3689 if (!(wParam & HTSYSMENU) || wChar == ' ')
3690 {
3691 if( uItem == NO_SELECTED_ITEM )
3692 MenuMoveSelection( hwnd, &MenuInfo, ITEM_NEXT );
3693 else
3694 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3695 }
3696
3697 track_menu:
3698 MenuTrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3699 MenuExitTracking( hwnd, FALSE );
3700 }
3701
3702 /**********************************************************************
3703 * TrackPopupMenuEx (USER32.@)
3704 */
3705 BOOL WINAPI TrackPopupMenuEx( HMENU Menu, UINT Flags, int x, int y,
3706 HWND Wnd, LPTPMPARAMS Tpm)
3707 {
3708 BOOL ret = FALSE;
3709 ROSMENUINFO MenuInfo;
3710
3711 if (!IsMenu(Menu))
3712 {
3713 SetLastError( ERROR_INVALID_MENU_HANDLE );
3714 return FALSE;
3715 }
3716
3717 /* ReactOS Check */
3718 if (!ValidateHwnd(Wnd))
3719 {
3720 return FALSE;
3721 }
3722
3723 MenuGetRosMenuInfo(&MenuInfo, Menu);
3724 if (IsWindow(MenuInfo.Wnd))
3725 {
3726 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3727 return FALSE;
3728 }
3729
3730 MenuInitTracking(Wnd, Menu, TRUE, Flags);
3731
3732 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3733 if (!(Flags & TPM_NONOTIFY))
3734 SendMessageW(Wnd, WM_INITMENUPOPUP, (WPARAM) Menu, 0);
3735
3736 if (MenuShowPopup(Wnd, Menu, 0, Flags, x, y, 0, 0 ))
3737 ret = MenuTrackMenu(Menu, Flags | TPM_POPUPMENU, 0, 0, Wnd,
3738 Tpm ? &Tpm->rcExclude : NULL);
3739 MenuExitTracking(Wnd, TRUE);
3740 return ret;
3741 }
3742
3743 /**********************************************************************
3744 * TrackPopupMenu (USER32.@)
3745 */
3746 BOOL WINAPI TrackPopupMenu( HMENU Menu, UINT Flags, int x, int y,
3747 int Reserved, HWND Wnd, CONST RECT *Rect)
3748 {
3749 return TrackPopupMenuEx( Menu, Flags, x, y, Wnd, NULL);
3750 }
3751
3752 /*
3753 * From MSDN:
3754 * The MFT_BITMAP, MFT_SEPARATOR, and MFT_STRING values cannot be combined
3755 * with one another. Also MFT_OWNERDRAW. Set fMask to MIIM_TYPE to use fType.
3756 *
3757 * Windows 2K/XP: fType is used only if fMask has a value of MIIM_FTYPE.
3758 *
3759 * MIIM_TYPE: Retrieves or sets the fType and dwTypeData members. Windows
3760 * 2K/XP: MIIM_TYPE is replaced by MIIM_BITMAP, MIIM_FTYPE, and MIIM_STRING.
3761 * MFT_STRING is replaced by MIIM_STRING.
3762 * (So, I guess we should use MIIM_STRING only for strings?)
3763 *
3764 * MIIM_FTYPE: Windows 2K/Windows XP: Retrieves or sets the fType member.
3765 *
3766 * Based on wine, SetMenuItemInfo_common:
3767 * 1) set MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP any one with MIIM_TYPE,
3768 * it will result in a error.
3769 * 2) set menu mask to MIIM_FTYPE and MFT_BITMAP ftype it will result in a error.
3770 * These conditions are addressed in Win32k IntSetMenuItemInfo.
3771 *
3772 */
3773 static
3774 BOOL
3775 FASTCALL
3776 MenuSetItemData(
3777 LPMENUITEMINFOW mii,
3778 UINT Flags,
3779 UINT_PTR IDNewItem,
3780 LPCWSTR NewItem,
3781 BOOL Unicode)
3782 {
3783 /*
3784 * Let us assume MIIM_FTYPE is set and building a new menu item structure.
3785 */
3786 if(Flags & MF_BITMAP)
3787 {
3788 mii->fMask |= MIIM_BITMAP; /* Use the new way of seting hbmpItem.*/
3789 mii->hbmpItem = (HBITMAP) NewItem;
3790
3791 if (Flags & MF_HELP)
3792 {
3793 /* increase ident */
3794 mii->fType |= MF_HELP;
3795 }
3796 }
3797 else if(Flags & MF_OWNERDRAW)
3798 {
3799 mii->fType |= MFT_OWNERDRAW;
3800 mii->fMask |= MIIM_DATA;
3801 mii->dwItemData = (DWORD_PTR) NewItem;
3802 }
3803 else if (Flags & MF_SEPARATOR)
3804 {
3805 mii->fType |= MFT_SEPARATOR;
3806 if (!(Flags & (MF_GRAYED|MF_DISABLED)))
3807 Flags |= MF_GRAYED|MF_DISABLED;
3808 }
3809 else /* Default action MF_STRING. */
3810 {
3811 /* Item beginning with a backspace is a help item */
3812 if (NewItem != NULL)
3813 {
3814 if (Unicode)
3815 {
3816 if (*NewItem == '\b')
3817 {
3818 mii->fType |= MF_HELP;
3819 NewItem++;
3820 }
3821 }
3822 else
3823 {
3824 LPCSTR NewItemA = (LPCSTR) NewItem;
3825 if (*NewItemA == '\b')
3826 {
3827 mii->fType |= MF_HELP;
3828 NewItemA++;
3829 NewItem = (LPCWSTR) NewItemA;
3830 }
3831 }
3832
3833 if (Flags & MF_HELP)
3834 mii->fType |= MF_HELP;
3835 mii->fMask |= MIIM_STRING;
3836 mii->fType |= MFT_STRING; /* Zero */
3837 mii->dwTypeData = (LPWSTR)NewItem;
3838 if (Unicode)
3839 mii->cch = (NULL == NewItem ? 0 : strlenW(NewItem));
3840 else
3841 mii->cch = (NULL == NewItem ? 0 : strlen((LPCSTR)NewItem));
3842 }
3843 else
3844 {
3845 mii->fType |= MFT_SEPARATOR;
3846 if (!(Flags & (MF_GRAYED|MF_DISABLED)))
3847 Flags |= MF_GRAYED|MF_DISABLED;
3848 }
3849 }
3850
3851 if(Flags & MF_RIGHTJUSTIFY) /* Same as MF_HELP */
3852 {
3853 mii->fType |= MFT_RIGHTJUSTIFY;
3854 }
3855
3856 if(Flags & MF_MENUBREAK)
3857 {
3858 mii->fType |= MFT_MENUBREAK;
3859 }
3860 else if(Flags & MF_MENUBARBREAK)
3861 {
3862 mii->fType |= MFT_MENUBARBREAK;
3863 }
3864
3865 if(Flags & MF_GRAYED || Flags & MF_DISABLED)
3866 {
3867 if (Flags & MF_GRAYED)
3868 mii->fState |= MF_GRAYED;
3869
3870 if (Flags & MF_DISABLED)
3871 mii->fState |= MF_DISABLED;
3872
3873 mii->fMask |= MIIM_STATE;
3874 }
3875 else if (Flags & MF_HILITE)
3876 {
3877 mii->fState |= MF_HILITE;
3878 mii->fMask |= MIIM_STATE;
3879 }
3880 else /* default state */
3881 {
3882 mii->fState |= MFS_ENABLED;
3883 mii->fMask |= MIIM_STATE;
3884 }
3885
3886 if(Flags & MF_POPUP)
3887 {
3888 mii->fType |= MF_POPUP;
3889 mii->fMask |= MIIM_SUBMENU;
3890 mii->hSubMenu = (HMENU)IDNewItem;
3891 }
3892 else
3893 {
3894 mii->fMask |= MIIM_ID;
3895 mii->wID = (UINT)IDNewItem;
3896 }
3897 return TRUE;
3898 }
3899
3900 NTSTATUS WINAPI
3901 User32CallLoadMenuFromKernel(PVOID Arguments, ULONG ArgumentLength)
3902 {
3903 PLOADMENU_CALLBACK_ARGUMENTS Common;
3904 LRESULT Result;
3905
3906 Common = (PLOADMENU_CALLBACK_ARGUMENTS) Arguments;
3907
3908 Result = (LRESULT)LoadMenuW( Common->hModule,
3909 IS_INTRESOURCE(Common->MenuName[0]) ?
3910 MAKEINTRESOURCE(Common->MenuName[0]) :
3911 (LPCWSTR)&Common->MenuName);
3912
3913 return ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS);
3914 }
3915
3916
3917 /* FUNCTIONS *****************************************************************/
3918
3919 /*static BOOL
3920 MenuIsStringItem(ULONG TypeData)
3921 {
3922 return(MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType));
3923 }*/
3924
3925
3926 /*
3927 * @implemented
3928 */
3929 BOOL WINAPI
3930 AppendMenuA(HMENU hMenu,
3931 UINT uFlags,
3932 UINT_PTR uIDNewItem,
3933 LPCSTR lpNewItem)
3934 {
3935 return(InsertMenuA(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
3936 lpNewItem));
3937 }
3938
3939
3940 /*
3941 * @implemented
3942 */
3943 BOOL WINAPI
3944 AppendMenuW(HMENU hMenu,
3945 UINT uFlags,
3946 UINT_PTR uIDNewItem,
3947 LPCWSTR lpNewItem)
3948 {
3949 return(InsertMenuW(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
3950 lpNewItem));
3951 }
3952
3953
3954 /*
3955 * @implemented
3956 */
3957 DWORD WINAPI
3958 CheckMenuItem(HMENU hmenu,
3959 UINT uIDCheckItem,
3960 UINT uCheck)
3961 {
3962 return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck);
3963 }
3964
3965 static
3966 BOOL
3967 MenuCheckMenuRadioItem(HMENU hMenu, UINT idFirst, UINT idLast, UINT idCheck, UINT uFlags, BOOL bCheck, PUINT pChecked, PUINT pUnchecked, PUINT pMenuChanged)
3968 {
3969 UINT ItemCount, i;
3970 PROSMENUITEMINFO Items = NULL;
3971 UINT cChecked, cUnchecked;
3972 BOOL bRet = TRUE;
3973 //ROSMENUINFO mi;
3974
3975 if(idFirst > idLast)
3976 return FALSE;
3977
3978 ItemCount = GetMenuItemCount(hMenu);
3979
3980 //mi.cbSize = sizeof(ROSMENUINFO);
3981 //if(!NtUserMenuInfo(hmenu, &mi, FALSE)) return ret;
3982
3983
3984 if(MenuGetAllRosMenuItemInfo(hMenu, &Items) <= 0)
3985 {
3986 ERR("MenuGetAllRosMenuItemInfo failed\n");
3987 return FALSE;
3988 }
3989
3990 cChecked = cUnchecked = 0;
3991
3992 for (i = 0 ; i < ItemCount; i++)
3993 {
3994 BOOL check = FALSE;
3995 if (0 != (Items[i].fType & MF_MENUBARBREAK)) continue;
3996 if (0 != (Items[i].fType & MF_SEPARATOR)) continue;
3997
3998 if ((Items[i].fType & MF_POPUP) && (uFlags == MF_BYCOMMAND))
3999 {
4000 MenuCheckMenuRadioItem(Items[i].hSubMenu, idFirst, idLast, idCheck, uFlags, bCheck, pChecked, pUnchecked, pMenuChanged);
4001 continue;
4002 }
4003 if (uFlags & MF_BYPOSITION)
4004 {
4005 if (i < idFirst || i > idLast)
4006 continue;
4007
4008 if (i == idCheck)
4009 {
4010 cChecked++;
4011 check = TRUE;
4012 }
4013 else
4014 {
4015 cUnchecked++;
4016 }
4017 }
4018 else
4019 {
4020 if (Items[i].wID < idFirst || Items[i].wID > idLast)
4021 continue;
4022
4023 if (Items[i].wID == idCheck)
4024 {
4025 cChecked++;
4026 check = TRUE;
4027 }
4028 else
4029 {
4030 cUnchecked++;
4031 }
4032 }
4033
4034 if (!bCheck)
4035 continue;
4036
4037 Items[i].fMask = MIIM_STATE | MIIM_FTYPE;
4038 if (check)
4039 {
4040 Items[i].fType |= MFT_RADIOCHECK;
4041 Items[i].fState |= MFS_CHECKED;
4042 }
4043 else
4044 {
4045 Items[i].fState &= ~MFS_CHECKED;
4046 }
4047
4048 if(!MenuSetRosMenuItemInfo(hMenu, i ,&Items[i]))
4049 {
4050 ERR("MenuSetRosMenuItemInfo failed\n");
4051 bRet = FALSE;
4052 break;
4053 }
4054 }
4055 HeapFree(GetProcessHeap(), 0, Items);
4056
4057 *pChecked += cChecked;
4058 *pUnchecked += cUnchecked;
4059
4060 if (cChecked || cUnchecked)
4061 (*pMenuChanged)++;
4062
4063 return bRet;
4064 }
4065
4066 /*
4067 * @implemented
4068 */
4069 BOOL WINAPI
4070 CheckMenuRadioItem(HMENU hmenu,
4071 UINT idFirst,
4072 UINT idLast,
4073 UINT idCheck,
4074 UINT uFlags)
4075 {
4076 UINT cChecked = 0;
4077 UINT cUnchecked = 0;
4078 UINT cMenuChanged = 0;
4079
4080 if (!MenuCheckMenuRadioItem(hmenu, idFirst, idLast, idCheck, uFlags, FALSE, &cChecked, &cUnchecked, &cMenuChanged))
4081 return FALSE;
4082
4083 if (cMenuChanged > 1)
4084 return FALSE;
4085
4086 cMenuChanged = 0;
4087 cChecked = 0;
4088 cUnchecked = 0;
4089
4090 if (!MenuCheckMenuRadioItem(hmenu, idFirst, idLast, idCheck, uFlags, TRUE, &cChecked, &cUnchecked, &cMenuChanged))
4091 return FALSE;
4092
4093 return (cChecked != 0);
4094 }
4095
4096
4097 /*
4098 * @implemented
4099 */
4100 HMENU WINAPI
4101 CreateMenu(VOID)
4102 {
4103 MenuLoadBitmaps();
4104 return NtUserxCreateMenu();
4105 }
4106
4107
4108 /*
4109 * @implemented
4110 */
4111 HMENU WINAPI
4112 CreatePopupMenu(VOID)
4113 {
4114 MenuLoadBitmaps();
4115 return NtUserxCreatePopupMenu();
4116 }
4117
4118
4119 /*
4120 * @implemented
4121 */
4122 BOOL WINAPI
4123 DrawMenuBar(HWND hWnd)
4124 {
4125 // return NtUserxDrawMenuBar(hWnd);
4126 ROSMENUINFO MenuInfo;
4127 HMENU hMenu;
4128 hMenu = GetMenu(hWnd);
4129 if (!hMenu)
4130 return FALSE;
4131 MenuGetRosMenuInfo(&MenuInfo, hMenu);
4132 MenuInfo.Height = 0; // make sure to recalc size
4133 MenuSetRosMenuInfo(&MenuInfo);
4134
4135 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4136 SWP_NOZORDER | SWP_FRAMECHANGED );
4137 return TRUE;
4138 }
4139
4140 /*
4141 * @implemented
4142 */
4143 BOOL WINAPI
4144 EnableMenuItem(HMENU hMenu,
4145 UINT uIDEnableItem,
4146 UINT uEnable)
4147 {
4148 return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable);
4149 }
4150
4151 /*
4152 * @implemented
4153 */
4154 BOOL WINAPI
4155 EndMenu(VOID)
4156 {
4157 GUITHREADINFO guii;
4158 guii.cbSize = sizeof(GUITHREADINFO);
4159 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii) && guii.hwndMenuOwner)
4160 {
4161 if (!fEndMenu &&
4162 top_popup &&
4163 guii.hwndMenuOwner != top_popup )
4164 {
4165 ERR("Capture GUI pti hWnd does not match top_popup!\n");
4166 }
4167 }
4168
4169 /* if we are in the menu code, and it is active */
4170 if (!fEndMenu && top_popup)
4171 {
4172 /* terminate the menu handling code */
4173 fEndMenu = TRUE;
4174
4175 /* needs to be posted to wakeup the internal menu handler */
4176 /* which will now terminate the menu, in the event that */
4177 /* the main window was minimized, or lost focus, so we */
4178 /* don't end up with an orphaned menu */
4179 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4180 }
4181 return TRUE;
4182 }
4183
4184 // So this one maybe one day it will be a callback!
4185 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
4186 UINT wHilite )
4187 {
4188 ROSMENUINFO MenuInfo;
4189 ROSMENUITEMINFO mii;
4190 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
4191 if (!hWnd)
4192 {
4193 SetLastError(ERROR_INVALID_WINDOW_HANDLE);
4194 return FALSE;
4195 }
4196 if (!NtUserMenuItemInfo(hMenu, wItemID, wHilite, &mii, FALSE)) return FALSE;
4197 if (!NtUserMenuInfo(hMenu, &MenuInfo, FALSE)) return FALSE;
4198 if (MenuInfo.FocusedItem == wItemID) return TRUE;
4199 MenuHideSubPopups( hWnd, &MenuInfo, FALSE, 0 );
4200 MenuSelectItem( hWnd, &MenuInfo, wItemID, TRUE, 0 );
4201 return TRUE;
4202 }
4203
4204 /*
4205 * @implemented
4206 */
4207 HMENU WINAPI
4208 GetMenu(HWND hWnd)
4209 {
4210 PWND Wnd = ValidateHwnd(hWnd);
4211
4212 if (!Wnd)
4213 return NULL;
4214
4215 return UlongToHandle(Wnd->IDMenu);
4216 }
4217
4218
4219 /*
4220 * @implemented
4221 */
4222 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4223 {
4224 BOOL Ret;
4225 Ret = NtUserGetMenuBarInfo( hwnd, idObject, idItem, pmbi);
4226 // Reason to move to server side!!!!!
4227 if (!Ret) return Ret;
4228 // EL HAXZO!!!
4229 pmbi->fBarFocused = top_popup_hmenu == pmbi->hMenu;
4230 if (!idItem)
4231 {
4232 pmbi->fFocused = pmbi->fBarFocused;
4233 }
4234
4235 return TRUE;
4236 }
4237
4238
4239 /*
4240 * @implemented
4241 */
4242 LONG WINAPI
4243 GetMenuCheckMarkDimensions(VOID)
4244 {
4245 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK),
4246 GetSystemMetrics(SM_CYMENUCHECK)));
4247 }
4248
4249
4250 /*
4251 * @implemented
4252 */
4253 UINT WINAPI
4254 GetMenuDefaultItem(HMENU hMenu,
4255 UINT fByPos,
4256 UINT gmdiFlags)
4257 {
4258 return NtUserGetMenuDefaultItem(hMenu, fByPos, gmdiFlags);
4259 }
4260
4261
4262 /*
4263 * @implemented
4264 */
4265 BOOL WINAPI
4266 GetMenuInfo(HMENU hmenu,
4267 LPMENUINFO lpcmi)
4268 {
4269 ROSMENUINFO mi;
4270 BOOL res = FALSE;
4271
4272 if(!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
4273 return FALSE;
4274
4275 RtlZeroMemory(&mi, sizeof(MENUINFO));
4276 mi.cbSize = sizeof(MENUINFO);
4277 mi.fMask = lpcmi->fMask;
4278
4279 res = NtUserMenuInfo(hmenu, &mi, FALSE);
4280
4281 memcpy(lpcmi, &mi, sizeof(MENUINFO));
4282 return res;
4283 }
4284
4285
4286 /*
4287 * @implemented
4288 */
4289 int WINAPI
4290 GetMenuItemCount(HMENU Menu)
4291 {
4292 ROSMENUINFO MenuInfo;
4293
4294 return MenuGetRosMenuInfo(&MenuInfo, Menu) ? MenuInfo.MenuItemCount : 0;
4295 }
4296
4297
4298 /*
4299 * @implemented
4300 */
4301 UINT WINAPI
4302 GetMenuItemID(HMENU hMenu,
4303 int nPos)
4304 {
4305 ROSMENUITEMINFO mii;
4306
4307 mii.cbSize = sizeof(MENUITEMINFOW);
4308 mii.fMask = MIIM_ID | MIIM_SUBMENU;
4309
4310 if (! NtUserMenuItemInfo(hMenu, nPos, MF_BYPOSITION, &mii, FALSE))
4311 {
4312 return -1;
4313 }
4314
4315 if (NULL != mii.hSubMenu)
4316 {
4317 return -1;
4318 }
4319 if (0 == mii.wID)
4320 {
4321 return -1;
4322 }
4323
4324 return mii.wID;
4325 }
4326
4327
4328 /*
4329 * @implemented
4330 */
4331 BOOL WINAPI
4332 GetMenuItemInfoA(
4333 HMENU Menu,
4334 UINT Item,
4335 BOOL ByPosition,
4336 LPMENUITEMINFOA mii)
4337 {
4338 MENUITEMINFOW miiW;
4339 LPSTR AnsiBuffer;
4340 INT Count;
4341
4342 if (mii->cbSize != sizeof(MENUITEMINFOA) &&
4343 mii->cbSize != sizeof(MENUITEMINFOA) - sizeof(HBITMAP))
4344 {
4345 SetLastError(ERROR_INVALID_PARAMETER);
4346 return FALSE;
4347 }
4348
4349 if(!(mii->fMask & (MIIM_TYPE | MIIM_STRING)))
4350 {
4351 /* No text requested, just pass on */
4352 return NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) mii, FALSE);
4353 }
4354
4355 AnsiBuffer = mii->dwTypeData;
4356 Count = miiW.cch = mii->cch;
4357 RtlCopyMemory(&miiW, mii, mii->cbSize);
4358 miiW.dwTypeData = 0;
4359
4360 if (AnsiBuffer)
4361 {
4362 miiW.dwTypeData = RtlAllocateHeap(GetProcessHeap(), 0,
4363 miiW.cch * sizeof(WCHAR));
4364 if (miiW.dwTypeData == NULL) return FALSE;
4365 miiW.dwTypeData[0] = 0;
4366 }
4367
4368 if (!NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO)&miiW, FALSE))
4369 {
4370 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4371 return FALSE;
4372 }
4373
4374 RtlCopyMemory(mii, &miiW, miiW.cbSize);
4375
4376 if (!AnsiBuffer || !Count)
4377 {
4378 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4379 mii->dwTypeData = AnsiBuffer;
4380 mii->cch = miiW.cch;
4381 return TRUE;
4382 }
4383
4384 if ((miiW.fMask & MIIM_STRING) || (IS_STRING_ITEM(miiW.fType)))
4385 {
4386 if (miiW.cch)
4387 {
4388 if (!WideCharToMultiByte(CP_ACP, 0, miiW.dwTypeData, miiW.cch, AnsiBuffer, mii->cch, NULL, NULL))
4389 {
4390 AnsiBuffer[0] = 0;
4391 }
4392 if (Count > miiW.cch)
4393 {
4394 AnsiBuffer[miiW.cch] = 0;
4395 }
4396 mii->cch = miiW.cch;
4397 }
4398 }
4399 else
4400 {
4401 AnsiBuffer[0] = 0;
4402 }
4403
4404 RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4405 mii->dwTypeData = AnsiBuffer;
4406
4407 return TRUE;
4408 }
4409
4410
4411 /*
4412 * @implemented
4413 */
4414 BOOL WINAPI
4415 GetMenuItemInfoW(
4416 HMENU Menu,
4417 UINT Item,
4418 BOOL ByPosition,
4419 LPMENUITEMINFOW mii)
4420 {
4421 MENUITEMINFOW miiW;
4422 LPWSTR String;
4423 INT Count;
4424
4425 if (mii->cbSize != sizeof(MENUITEMINFOW) &&
4426 mii->cbSize != sizeof(MENUITEMINFOW) - sizeof(HBITMAP))
4427 {
4428 SetLastError(ERROR_INVALID_PARAMETER);
4429 return FALSE;
4430 }
4431
4432 if(!(mii->fMask & (MIIM_TYPE | MIIM_STRING)))
4433 {
4434 /* No text requested, just pass on */
4435 return NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) mii, FALSE);
4436 }
4437
4438 String = mii->dwTypeData;
4439 Count = mii->cch;
4440 RtlCopyMemory(&miiW, mii, mii->cbSize);
4441 miiW.dwTypeData = 0;
4442
4443 if (String)
4444 {
4445 miiW.dwTypeData = RtlAllocateHeap(GetProcessHeap(), 0,
4446 miiW.cch * sizeof(WCHAR));
4447 if (miiW.dwTypeData == NULL) return FALSE;
4448 miiW.dwTypeData[0] = 0;
4449 }
4450
4451 if (!NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) &miiW, FALSE))
4452 {
4453 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4454 return FALSE;
4455 }
4456
4457 RtlCopyMemory(mii, &miiW, miiW.cbSize); // Okay to over write user data.
4458
4459 if (!String || !Count)
4460 {
4461 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4462 mii->dwTypeData = String; // may not be zero.
4463 mii->cch = miiW.cch;
4464 return TRUE;
4465 }
4466
4467 if ((miiW.fMask & MIIM_STRING) || (IS_STRING_ITEM(miiW.fType)))
4468 {
4469 lstrcpynW( String, miiW.dwTypeData, Count );
4470 }
4471
4472 RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4473 mii->dwTypeData = String;
4474 mii->cch = strlenW(String);
4475 return TRUE;
4476 }
4477
4478
4479 /*
4480 * @implemented
4481 */
4482 UINT
4483 WINAPI
4484 GetMenuState(
4485 HMENU hMenu,
4486 UINT uId,
4487 UINT uFlags)
4488 {
4489 ROSMENUINFO MenuInfo;
4490 ROSMENUITEMINFO mii;
4491 memset( &mii, 0, sizeof(mii) );
4492 mii.cbSize = sizeof(MENUITEMINFOW);
4493 mii.fMask = MIIM_STATE | MIIM_FTYPE | MIIM_SUBMENU;
4494
4495 SetLastError(0);
4496 if(NtUserMenuItemInfo(hMenu, uId, uFlags, &mii, FALSE))
4497 {
4498 UINT nSubItems = 0;
4499 if(mii.hSubMenu)
4500 {
4501 if (! MenuGetRosMenuInfo(&MenuInfo, mii.hSubMenu))
4502 {
4503 return (UINT) -1;
4504 }
4505 nSubItems = MenuInfo.MenuItemCount;
4506
4507 /* FIXME - ported from wine, does that work (0xff)? */
4508 if(GetLastError() != ERROR_INVALID_MENU_HANDLE)
4509 return (nSubItems << 8) | ((mii.fState | mii.fType) & 0xff);
4510
4511 return (UINT)-1; /* Invalid submenu */
4512 }
4513
4514 /* FIXME - ported from wine, does that work? */
4515 return (mii.fType | mii.fState);
4516 }
4517
4518 return (UINT)-1;
4519 }
4520
4521
4522 /*
4523 * @implemented
4524 */
4525 int
4526 WINAPI
4527 GetMenuStringA(
4528 HMENU hMenu,
4529 UINT uIDItem,
4530 LPSTR lpString,
4531 int nMaxCount,
4532 UINT uFlag)
4533 {
4534 MENUITEMINFOA mii;
4535 memset( &mii, 0, sizeof(mii) );
4536 mii.dwTypeData = lpString;
4537 mii.fMask = MIIM_STRING | MIIM_FTYPE;
4538 mii.fType = MFT_STRING;
4539 mii.cbSize = sizeof(MENUITEMINFOA);
4540 mii.cch = nMaxCount;
4541
4542 if(!(GetMenuItemInfoA( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&mii)))
4543 return 0;
4544 else
4545 return mii.cch;
4546 }
4547
4548
4549 /*
4550 * @implemented
4551 */
4552 int
4553 WINAPI
4554 GetMenuStringW(
4555 HMENU hMenu,
4556 UINT uIDItem,
4557 LPWSTR lpString,
4558 int nMaxCount,
4559 UINT uFlag)
4560 {
4561 MENUITEMINFOW miiW;
4562 memset( &miiW, 0, sizeof(miiW) );
4563 miiW.dwTypeData = lpString;
4564 miiW.fMask = MIIM_STRING | MIIM_FTYPE;
4565 miiW.fType = MFT_STRING;
4566 miiW.cbSize = sizeof(MENUITEMINFOW);
4567 miiW.cch = nMaxCount;
4568
4569 if(!(GetMenuItemInfoW( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&miiW)))
4570 return 0;
4571 else
4572 return miiW.cch;
4573 }
4574
4575
4576 /*
4577 * @implemented
4578 */
4579 HMENU
4580 WINAPI
4581 GetSubMenu(
4582 HMENU hMenu,
4583 int nPos)
4584 {
4585 ROSMENUITEMINFO mi;
4586
4587 mi.cbSize = sizeof(MENUITEMINFOW);
4588 mi.fMask = MIIM_SUBMENU;
4589
4590 if (NtUserMenuItemInfo(hMenu, (UINT)nPos, MF_BYPOSITION, &mi, FALSE))
4591 {
4592 return IsMenu(mi.hSubMenu) ? mi.hSubMenu : NULL;
4593 }
4594
4595 return NULL;
4596 }
4597
4598 /*
4599 * @implemented
4600 */
4601 HMENU
4602 WINAPI
4603 GetSystemMenu(
4604 HWND hWnd,
4605 BOOL bRevert)
4606 {
4607 HMENU TopMenu;
4608
4609 TopMenu = NtUserGetSystemMenu(hWnd, bRevert);
4610
4611 return NULL == TopMenu ? NULL : GetSubMenu(TopMenu, 0);
4612 }
4613
4614
4615 /*
4616 * @implemented
4617 */
4618 BOOL
4619 WINAPI
4620 InsertMenuA(
4621 HMENU hMenu,
4622 UINT uPosition,
4623 UINT uFlags,
4624 UINT_PTR uIDNewItem,
4625 LPCSTR lpNewItem)
4626 {
4627 MENUITEMINFOA mii;
4628 memset( &mii, 0, sizeof(mii) );
4629 mii.cbSize = sizeof(MENUITEMINFOA);
4630 mii.fMask = MIIM_FTYPE;
4631
4632 MenuSetItemData((LPMENUITEMINFOW) &mii,
4633 uFlags,
4634 uIDNewItem,
4635 (LPCWSTR) lpNewItem,
4636 FALSE);
4637
4638 return InsertMenuItemA(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii);
4639 }
4640
4641
4642
4643 /*
4644 * @implemented
4645 */
4646 BOOL
4647 WINAPI
4648 InsertMenuItemA(
4649 HMENU hMenu,
4650 UINT uItem,
4651 BOOL fByPosition,
4652 LPCMENUITEMINFOA lpmii)
4653 {
4654 MENUITEMINFOW mi;
4655 UNICODE_STRING MenuText;
4656 BOOL res = FALSE;
4657 BOOL CleanHeap = FALSE;
4658
4659 if((lpmii->cbSize == sizeof(MENUITEMINFOA)) ||
4660 (lpmii->cbSize == sizeof(MENUITEMINFOA) - sizeof(HBITMAP)))
4661 {
4662 RtlCopyMemory ( &mi, lpmii, lpmii->cbSize );
4663
4664 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
4665 {
4666 mi.cbSize = sizeof( MENUITEMINFOW);
4667 mi.hbmpItem = NULL;
4668 }
4669 /* copy the text string */
4670 if (((mi.fMask & MIIM_STRING) ||
4671 ((mi.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mi.fType) == MF_STRING)))
4672 && mi.dwTypeData != NULL)
4673 {
4674 if (!RtlCreateUnicodeStringFromAsciiz(&MenuText, (LPSTR)mi.dwTypeData))
4675 {
4676 SetLastError (ERROR_NOT_ENOUGH_MEMORY);
4677 return FALSE;
4678 }
4679 mi.dwTypeData = MenuText.Buffer;
4680 mi.cch = MenuText.Length / sizeof(WCHAR);
4681 CleanHeap = TRUE;
4682 }
4683 res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mi, NULL);
4684
4685 if ( CleanHeap ) RtlFreeUnicodeString ( &MenuText );
4686 }
4687 return res;
4688 }
4689
4690
4691 /*
4692 * @implemented
4693 */
4694 BOOL
4695 WINAPI
4696 InsertMenuItemW(
4697 HMENU hMenu,
4698 UINT uItem,
4699 BOOL fByPosition,
4700 LPCMENUITEMINFOW lpmii)
4701 {
4702 MENUITEMINFOW mi;
4703 UNICODE_STRING MenuText;
4704 BOOL res = FALSE;
4705
4706 /* while we could just pass 'lpmii' to win32k, we make a copy so that
4707 if a bad user passes bad data, we crash his process instead of the
4708 entire kernel */
4709
4710 if((lpmii->cbSize == sizeof(MENUITEMINFOW)) ||
4711 (lpmii->cbSize == sizeof(MENUITEMINFOW) - sizeof(HBITMAP)))
4712 {
4713 RtlCopyMemory(&mi, lpmii, lpmii->cbSize);
4714
4715 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
4716 {
4717 mi.cbSize = sizeof( MENUITEMINFOW);
4718 mi.hbmpItem = NULL;
4719 }
4720 /* copy the text string */
4721 if (((mi.fMask & MIIM_STRING) ||
4722 ((mi.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mi.fType) == MF_STRING)))
4723 && mi.dwTypeData != NULL)
4724 {
4725 RtlInitUnicodeString(&MenuText, (PWSTR)lpmii->dwTypeData);
4726 mi.dwTypeData = MenuText.Buffer;
4727 mi.cch = MenuText.Length / sizeof(WCHAR);
4728 }
4729 res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mi, NULL);
4730 }
4731 return res;
4732 }
4733
4734
4735 /*
4736 * @implemented
4737 */
4738 BOOL
4739 WINAPI
4740 InsertMenuW(
4741 HMENU hMenu,
4742 UINT uPosition,
4743 UINT uFlags,
4744 UINT_PTR uIDNewItem,
4745 LPCWSTR lpNewItem)
4746 {
4747 MENUITEMINFOW mii;
4748 memset( &mii, 0, sizeof(mii) );
4749 mii.cbSize = sizeof(MENUITEMINFOW);
4750 mii.fMask = MIIM_FTYPE;
4751
4752 MenuSetItemData( &mii,
4753 uFlags,
4754 uIDNewItem,
4755 lpNewItem,
4756 TRUE);
4757
4758 return InsertMenuItemW(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii);
4759 }
4760
4761
4762 /*
4763 * @implemented
4764 */
4765 BOOL
4766 WINAPI
4767 IsMenu(
4768 HMENU Menu)
4769 {
4770 if (ValidateHandle(Menu, TYPE_MENU)) return TRUE;
4771 return FALSE;
4772 }
4773
4774
4775 /*
4776 * @implemented
4777 */
4778 HMENU WINAPI
4779 LoadMenuA(HINSTANCE hInstance,
4780 LPCSTR lpMenuName)
4781 {
4782 HANDLE Resource = FindResourceA(hInstance, lpMenuName, MAKEINTRESOURCEA(4));
4783 if (Resource == NULL)
4784 {
4785 return(NULL);
4786 }
4787 return(LoadMenuIndirectA((PVOID)LoadResource(hInstance, Resource)));
4788 }
4789
4790
4791 /*
4792 * @implemented
4793 */
4794 HMENU WINAPI
4795 LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate)
4796 {
4797 return(LoadMenuIndirectW(lpMenuTemplate));
4798 }
4799
4800
4801 /*
4802 * @implemented
4803 */
4804 HMENU WINAPI
4805 LoadMenuIndirectW(CONST MENUTEMPLATE *lpMenuTemplate)
4806 {
4807 HMENU hMenu;
4808 WORD version, offset;
4809 LPCSTR p = (LPCSTR)lpMenuTemplate;
4810
4811 version = GET_WORD(p);
4812 p += sizeof(WORD);
4813
4814 switch (version)
4815 {
4816 case 0: /* standard format is version of 0 */
4817 offset = GET_WORD(p);
4818 p += sizeof(WORD) + offset;
4819 if (!(hMenu = CreateMenu())) return 0;
4820 if (!MENU_ParseResource(p, hMenu, TRUE))
4821 {
4822 DestroyMenu(hMenu);
4823 return 0;
4824 }
4825 return hMenu;
4826 case 1: /* extended format is version of 1 */
4827 offset = GET_WORD(p);
4828 p += sizeof(WORD) + offset;
4829 if (!(hMenu = CreateMenu())) return 0;
4830 if (!MENUEX_ParseResource(p, hMenu))
4831 {
4832 DestroyMenu( hMenu );
4833 return 0;
4834 }
4835 return hMenu;
4836 default:
4837 ERR("Menu template version %d not supported.\n", version);
4838 return 0;
4839 }
4840 }
4841
4842
4843 /*
4844 * @implemented
4845 */
4846 HMENU WINAPI
4847 LoadMenuW(HINSTANCE hInstance,
4848 LPCWSTR lpMenuName)
4849 {
4850 HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU);
4851 if (Resource == NULL)
4852 {
4853 return(NULL);
4854 }
4855 return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource)));
4856 }
4857
4858
4859 /*
4860 * @implemented
4861 */
4862 int
4863 WINAPI
4864 MenuItemFromPoint(
4865 HWND hWnd,
4866 HMENU hMenu,
4867 POINT ptScreen)
4868 {
4869 return NtUserMenuItemFromPoint(hWnd, hMenu, ptScreen.x, ptScreen.y);
4870 }
4871
4872
4873 /*
4874 * @implemented
4875 */
4876 BOOL
4877 WINAPI
4878 ModifyMenuA(
4879 HMENU hMnu,
4880 UINT uPosition,
4881 UINT uFlags,
4882 UINT_PTR uIDNewItem,
4883 LPCSTR lpNewItem)
4884 {
4885 ROSMENUINFO mi;
4886 ROSMENUITEMINFO rmii;
4887 MENUITEMINFOA mii;
4888 memset( &mii, 0, sizeof(mii) );
4889 mii.cbSize = sizeof(MENUITEMINFOA);
4890 mii.fMask = MIIM_FTYPE;
4891
4892 if (!MenuGetRosMenuInfo( &mi, hMnu )) return FALSE;
4893
4894 mi.Height = 0;
4895
4896 if (!MenuSetRosMenuInfo( &mi )) return FALSE;
4897
4898 MenuInitRosMenuItemInfo( &rmii );
4899
4900 if(!MenuGetRosMenuItemInfo( hMnu, uPosition, &rmii)) return FALSE;
4901
4902 if ((rmii.fType & MF_POPUP) && (uFlags & MF_POPUP) && (rmii.hSubMenu != (HMENU)uIDNewItem))
4903 NtUserDestroyMenu( rmii.hSubMenu ); /* ModifyMenu() spec */
4904
4905 MenuCleanupRosMenuItemInfo( &rmii );
4906
4907 MenuSetItemData((LPMENUITEMINFOW) &mii,
4908 uFlags,
4909 uIDNewItem,
4910 (LPCWSTR) lpNewItem,
4911 FALSE);
4912
4913 return SetMenuItemInfoA( hMnu,
4914 uPosition,
4915 (BOOL)(MF_BYPOSITION & uFlags),
4916 &mii);
4917 }
4918
4919
4920 /*
4921 * @implemented
4922 */
4923 BOOL
4924 WINAPI
4925 ModifyMenuW(
4926 HMENU hMnu,
4927 UINT uPosition,
4928 UINT uFlags,
4929 UINT_PTR uIDNewItem,
4930 LPCWSTR lpNewItem)
4931 {
4932 ROSMENUINFO mi;
4933 ROSMENUITEMINFO rmii;
4934 MENUITEMINFOW mii;
4935 memset ( &mii, 0, sizeof(mii) );
4936 mii.cbSize = sizeof(MENUITEMINFOW);
4937 mii.fMask = MIIM_FTYPE;
4938
4939 if (!MenuGetRosMenuInfo( &mi, hMnu )) return FALSE;
4940
4941 mi.Height = 0; // Force size recalculation.
4942
4943 if (!MenuSetRosMenuInfo( &mi )) return FALSE;
4944
4945 MenuInitRosMenuItemInfo( &rmii );
4946
4947 if(!MenuGetRosMenuItemInfo( hMnu, uPosition, &rmii)) return FALSE;
4948
4949 if ((rmii.fType & MF_POPUP) && (uFlags & MF_POPUP) && (rmii.hSubMenu != (HMENU)uIDNewItem))
4950 NtUserDestroyMenu( rmii.hSubMenu ); /* ModifyMenu() spec */
4951
4952 MenuCleanupRosMenuItemInfo( &rmii );
4953
4954 /* Init new data for this menu item */
4955 MenuSetItemData( &mii,
4956 uFlags,
4957 uIDNewItem,
4958 lpNewItem,
4959 TRUE);
4960
4961 /* Now, make Win32k IntSetMenuItemInfo handle the changes to this menu item. */
4962 return SetMenuItemInfoW( hMnu,
4963 uPosition,
4964 (BOOL)(MF_BYPOSITION & uFlags),
4965 &mii);
4966 }
4967
4968
4969 /*
4970 * @implemented
4971 */
4972 BOOL WINAPI
4973 SetMenu(HWND hWnd,
4974 HMENU hMenu)
4975 {
4976 return NtUserSetMenu(hWnd, hMenu, TRUE);
4977 }
4978
4979
4980 /*
4981 * @implemented
4982 */
4983 BOOL
4984 WINAPI
4985 SetMenuInfo(
4986 HMENU hmenu,
4987 LPCMENUINFO lpcmi)
4988 {
4989 ROSMENUINFO mi;
4990 BOOL res = FALSE;
4991
4992 if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
4993 {
4994 SetLastError(ERROR_INVALID_PARAMETER);
4995 return res;
4996 }
4997
4998 memcpy(&mi, lpcmi, sizeof(MENUINFO));
4999 return NtUserMenuInfo(hmenu, &mi, TRUE);
5000 }
5001
5002
5003 /*
5004 * @implemented
5005 */
5006 BOOL
5007 WINAPI
5008 SetMenuItemBitmaps(
5009 HMENU hMenu,
5010 UINT uPosition,
5011 UINT uFlags,
5012 HBITMAP hBitmapUnchecked,
5013 HBITMAP hBitmapChecked)
5014 {
5015 ROSMENUITEMINFO uItem;
5016 memset ( &uItem, 0, sizeof(uItem) );
5017 uItem.fMask = MIIM_STATE | MIIM_BITMAP;
5018
5019 if(!(NtUserMenuItemInfo(hMenu, uPosition,
5020 (BOOL)(MF_BYPOSITION & uFlags), &uItem, FALSE))) return FALSE;
5021
5022 if (!hBitmapChecked && !hBitmapUnchecked)
5023 {
5024 uItem.fState &= ~MF_USECHECKBITMAPS;
5025 }
5026 else /* Install new bitmaps */
5027 {
5028 uItem.hbmpChecked = hBitmapChecked;
5029 uItem.hbmpUnchecked = hBitmapUnchecked;
5030 uItem.fState |= MF_USECHECKBITMAPS;
5031 }
5032 return NtUserMenuItemInfo(hMenu, uPosition,
5033 (BOOL)(MF_BYPOSITION & uFlags), &uItem, TRUE);
5034 }
5035
5036
5037 /*
5038 * @implemented
5039 */
5040 BOOL
5041 WINAPI
5042 SetMenuItemInfoA(
5043 HMENU hMenu,
5044 UINT uItem,
5045 BOOL fByPosition,
5046 LPCMENUITEMINFOA lpmii)
5047 {
5048 MENUITEMINFOW MenuItemInfoW;
5049 UNICODE_STRING UnicodeString;
5050 NTSTATUS Status;
5051 ULONG Result = FALSE;
5052
5053 RtlCopyMemory(&MenuItemInfoW, lpmii, min(lpmii->cbSize, sizeof(MENUITEMINFOW)));
5054
5055 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
5056 {
5057 MenuItemInfoW.cbSize = sizeof( MENUITEMINFOW);
5058 MenuItemInfoW.hbmpItem = NULL;
5059 }
5060 /*
5061 * MIIM_STRING == good
5062 * MIIM_TYPE & MFT_STRING == good
5063 * MIIM_STRING & MFT_STRING == good
5064 * MIIM_STRING & MFT_OWNERSRAW == good
5065 */
5066 if (((MenuItemInfoW.fMask & MIIM_STRING) ||
5067 ((MenuItemInfoW.fMask & MIIM_TYPE) &&
5068 (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
5069 && MenuItemInfoW.dwTypeData != NULL)
5070 {
5071 /* cch is ignored when the content of a menu item is set by calling SetMenuItemInfo. */
5072 Status = RtlCreateUnicodeStringFromAsciiz(&UnicodeString,
5073 (LPSTR)MenuItemInfoW.dwTypeData);
5074 if (!NT_SUCCESS (Status))
5075 {
5076 SetLastError (RtlNtStatusToDosError(Status));
5077 return FALSE;
5078 }
5079 MenuItemInfoW.dwTypeData = UnicodeString.Buffer;
5080 MenuItemInfoW.cch = UnicodeString.Length / sizeof(WCHAR);
5081 }
5082 else
5083 {
5084 UnicodeString.Buffer = NULL;
5085 }
5086
5087 Result = NtUserMenuItemInfo(hMenu, uItem, fByPosition,
5088 (PROSMENUITEMINFO)&MenuItemInfoW, TRUE);
5089
5090 if (UnicodeString.Buffer != NULL)
5091 {
5092 RtlFreeUnicodeString(&UnicodeString);
5093 }
5094
5095 return Result;
5096 }
5097
5098
5099 /*
5100 * @implemented
5101 */
5102 BOOL
5103 WINAPI
5104 SetMenuItemInfoW(
5105 HMENU hMenu,
5106 UINT uItem,
5107 BOOL fByPosition,
5108 LPCMENUITEMINFOW lpmii)
5109 {
5110 MENUITEMINFOW MenuItemInfoW;
5111 ULONG Result;
5112
5113 RtlCopyMemory(&MenuItemInfoW, lpmii, min(lpmii->cbSize, sizeof(MENUITEMINFOW)));
5114
5115 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
5116 {
5117 MenuItemInfoW.cbSize = sizeof( MENUITEMINFOW);
5118 MenuItemInfoW.hbmpItem = NULL;
5119 }
5120
5121 if (((MenuItemInfoW.fMask & MIIM_STRING) ||
5122 ((MenuItemInfoW.fMask & MIIM_TYPE) &&
5123 (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
5124 && MenuItemInfoW.dwTypeData != NULL)
5125 {
5126 MenuItemInfoW.cch = strlenW(MenuItemInfoW.dwTypeData);
5127 }
5128 Result = NtUserMenuItemInfo(hMenu, uItem, fByPosition,
5129 (PROSMENUITEMINFO)&MenuItemInfoW, TRUE);
5130
5131 return Result;
5132 }
5133
5134 /*
5135 * @implemented
5136 */
5137 BOOL
5138 WINAPI
5139 SetSystemMenu (
5140 HWND hwnd,
5141 HMENU hMenu)
5142 {
5143 if(!hwnd)
5144 {
5145 SetLastError(ERROR_INVALID_WINDOW_HANDLE);
5146 return FALSE;
5147 }
5148 if(!hMenu)
5149 {
5150 SetLastError(ERROR_INVALID_MENU_HANDLE);
5151 return FALSE;
5152 }
5153 return NtUserSetSystemMenu(hwnd, hMenu);
5154 }
5155
5156 //
5157 // Example for the Win32/User32 rewrite.
5158 // Def = TrackPopupMenuEx@24=NtUserTrackPopupMenuEx@24
5159 //
5160 //
5161 BOOL
5162 WINAPI
5163 NEWTrackPopupMenu(
5164 HMENU Menu,
5165 UINT Flags,
5166 int x,
5167 int y,
5168 int Reserved,
5169 HWND Wnd,
5170 CONST RECT *Rect)
5171 {
5172 return NtUserTrackPopupMenuEx( Menu,
5173 Flags,
5174 x,
5175 y,
5176 Wnd,
5177 NULL); // LPTPMPARAMS is null
5178 }
5179
5180
5181 /*
5182 * @implemented
5183 */
5184 DWORD
5185 WINAPI
5186 GetMenuContextHelpId(HMENU hmenu)
5187 {
5188 ROSMENUINFO mi;
5189 mi.cbSize = sizeof(ROSMENUINFO);
5190 mi.fMask = MIM_HELPID;
5191
5192 if(NtUserMenuInfo(hmenu, &mi, FALSE))
5193 {
5194 return mi.dwContextHelpID;
5195 }
5196 return 0;
5197 }
5198
5199 /*
5200 * @unimplemented
5201 */
5202 BOOL
5203 WINAPI
5204 MenuWindowProcA(
5205 HWND hWnd,
5206 ULONG_PTR Result,
5207 UINT Msg,
5208 WPARAM wParam,
5209 LPARAM lParam
5210 )
5211 {
5212 if ( Msg < WM_USER)
5213 {
5214 LRESULT lResult;
5215 lResult = PopupMenuWndProcA(hWnd, Msg, wParam, lParam );
5216 if (Result)
5217 {
5218 Result = (ULONG_PTR)lResult;
5219 return TRUE;
5220 }
5221 return FALSE;
5222 }
5223 return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, TRUE);
5224
5225 }
5226
5227 /*
5228 * @unimplemented
5229 */
5230 BOOL
5231 WINAPI
5232 MenuWindowProcW(
5233 HWND hWnd,
5234 ULONG_PTR Result,
5235 UINT Msg,
5236 WPARAM wParam,
5237 LPARAM lParam
5238 )
5239 {
5240 if ( Msg < WM_USER)
5241 {
5242 LRESULT lResult;
5243 lResult = PopupMenuWndProcW(hWnd, Msg, wParam, lParam );
5244 if (Result)
5245 {
5246 Result = (ULONG_PTR)lResult;
5247 return TRUE;
5248 }
5249 return FALSE;
5250 }
5251 return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, FALSE);
5252 }
5253
5254 /*
5255 * @implemented
5256 */
5257 BOOL
5258 WINAPI
5259 ChangeMenuW(
5260 HMENU hMenu,
5261 UINT cmd,
5262 LPCWSTR lpszNewItem,
5263 UINT cmdInsert,
5264 UINT flags)
5265 {
5266 /*
5267 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5268 for MF_DELETE. We should check the parameters for all others
5269 MF_* actions also (anybody got a doc on ChangeMenu?).
5270 */
5271
5272 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
5273 {
5274 case MF_APPEND :
5275 return AppendMenuW(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
5276
5277 case MF_DELETE :
5278 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
5279
5280 case MF_CHANGE :
5281 return ModifyMenuW(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
5282
5283 case MF_REMOVE :
5284 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
5285 flags &~ MF_REMOVE);
5286
5287 default : /* MF_INSERT */
5288 return InsertMenuW(hMenu, cmd, flags, cmdInsert, lpszNewItem);
5289 };
5290 }
5291
5292 /*
5293 * @implemented
5294 */
5295 BOOL
5296 WINAPI
5297 ChangeMenuA(
5298 HMENU hMenu,
5299 UINT cmd,
5300 LPCSTR lpszNewItem,
5301 UINT cmdInsert,
5302 UINT flags)
5303 {
5304 /*
5305 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5306 for MF_DELETE. We should check the parameters for all others
5307 MF_* actions also (anybody got a doc on ChangeMenu?).
5308 */
5309
5310 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
5311 {
5312 case MF_APPEND :
5313 return AppendMenuA(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
5314
5315 case MF_DELETE :
5316 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
5317
5318 case MF_CHANGE :
5319 return ModifyMenuA(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
5320
5321 case MF_REMOVE :
5322 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
5323 flags &~ MF_REMOVE);
5324
5325 default : /* MF_INSERT */
5326 return InsertMenuA(hMenu, cmd, flags, cmdInsert, lpszNewItem);
5327 };
5328 }
5329
5330
5331
5332
5333