sync with trunk (r49238)
[reactos.git] / dll / win32 / user32 / windows / menu.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS user32.dll
4 * FILE: user32/windows/menu.c
5 * PURPOSE: Menus
6 *
7 * PROGRAMMERS: Casper S. Hornstrup
8 * James Tabor
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <user32.h>
14 #include <wine/debug.h>
15
16 LRESULT DefWndNCPaint(HWND hWnd, HRGN hRgn, BOOL Active);
17
18 WINE_DEFAULT_DEBUG_CHANNEL(menu);
19
20 /* internal popup menu window messages */
21
22 #define MM_SETMENUHANDLE (WM_USER + 0)
23 #define MM_GETMENUHANDLE (WM_USER + 1)
24
25 /* internal flags for menu tracking */
26
27 #define TF_ENDMENU 0x10000
28 #define TF_SUSPENDPOPUP 0x20000
29 #define TF_SKIPREMOVE 0x40000
30
31 #define ITEM_PREV -1
32 #define ITEM_NEXT 1
33
34 /* Internal MenuTrackMenu() flags */
35 #define TPM_INTERNAL 0xF0000000
36 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
37 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
38
39
40 #define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)
41
42 #define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK)
43
44 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
45 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
46 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
47
48 #define IS_SYSTEM_MENU(MenuInfo) \
49 (0 == ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
50
51 #define IS_SYSTEM_POPUP(MenuInfo) \
52 (0 != ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
53
54 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
55
56 /* Use global popup window because there's no way 2 menus can
57 * be tracked at the same time. */
58 static HWND top_popup;
59 static HMENU top_popup_hmenu;
60
61 /* Flag set by EndMenu() to force an exit from menu tracking */
62 static BOOL fEndMenu = FALSE;
63
64 #define MENU_ITEM_HBMP_SPACE (5)
65 #define MENU_BAR_ITEMS_SPACE (12)
66 #define SEPARATOR_HEIGHT (5)
67 #define MENU_TAB_SPACE (8)
68
69 #define MAKEINTATOMA(atom) ((LPCSTR)((ULONG_PTR)((WORD)(atom))))
70 #define MAKEINTATOMW(atom) ((LPCWSTR)((ULONG_PTR)((WORD)(atom))))
71 #define POPUPMENU_CLASS_ATOMA MAKEINTATOMA(32768) /* PopupMenu */
72 #define POPUPMENU_CLASS_ATOMW MAKEINTATOMW(32768) /* PopupMenu */
73
74 typedef struct
75 {
76 UINT TrackFlags;
77 HMENU CurrentMenu; /* current submenu (can be equal to hTopMenu)*/
78 HMENU TopMenu; /* initial menu */
79 HWND OwnerWnd; /* where notifications are sent */
80 POINT Pt;
81 } MTRACKER;
82
83
84 /*********************************************************************
85 * PopupMenu class descriptor
86 */
87 const struct builtin_class_descr POPUPMENU_builtin_class =
88 {
89 POPUPMENU_CLASS_ATOMW, /* name */
90 CS_SAVEBITS | CS_DBLCLKS, /* style */
91 (WNDPROC) NULL, /* FIXME - procA */
92 (WNDPROC) PopupMenuWndProcW, /* FIXME - procW */
93 sizeof(MENUINFO *), /* extra */
94 (LPCWSTR) IDC_ARROW, /* cursor */
95 (HBRUSH)(COLOR_MENU + 1) /* brush */
96 };
97
98 #ifndef GET_WORD
99 #define GET_WORD(ptr) (*(WORD *)(ptr))
100 #endif
101 #ifndef GET_DWORD
102 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
103 #endif
104
105 HFONT hMenuFont = NULL;
106 HFONT hMenuFontBold = NULL;
107
108 /* Dimension of the menu bitmaps */
109 static HBITMAP BmpSysMenu = NULL;
110
111 static SIZE MenuCharSize;
112
113 /***********************************************************************
114 * MenuGetRosMenuInfo
115 *
116 * Get full information about menu
117 */
118 static BOOL FASTCALL
119 MenuGetRosMenuInfo(PROSMENUINFO MenuInfo, HMENU Menu)
120 {
121 MenuInfo->cbSize = sizeof(ROSMENUINFO);
122 MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE;
123
124 return NtUserMenuInfo(Menu, MenuInfo, FALSE);
125 }
126
127 /***********************************************************************
128 * MenuSetRosMenuInfo
129 *
130 * Set full information about menu
131 */
132 static BOOL FASTCALL
133 MenuSetRosMenuInfo(PROSMENUINFO MenuInfo)
134 {
135 MenuInfo->cbSize = sizeof(ROSMENUINFO);
136 MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE;
137
138 return NtUserMenuInfo(MenuInfo->Self, MenuInfo, TRUE);
139 }
140
141 /***********************************************************************
142 * MenuInitRosMenuItemInfo
143 *
144 * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo
145 */
146 static VOID FASTCALL
147 MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
148 {
149 ZeroMemory(ItemInfo, sizeof(ROSMENUITEMINFO));
150 ItemInfo->cbSize = sizeof(ROSMENUITEMINFO);
151 }
152
153 /***********************************************************************
154 * MenuGetRosMenuItemInfo
155 *
156 * Get full information about a menu item
157 */
158 static BOOL FASTCALL
159 MenuGetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo)
160 {
161 UINT Save_Mask = ItemInfo->fMask; /* Save the org mask bits. */
162
163 if (ItemInfo->dwTypeData != NULL)
164 {
165 HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData);
166 }
167
168
169 ItemInfo->fMask = MIIM_BITMAP | MIIM_CHECKMARKS | MIIM_DATA | MIIM_FTYPE
170 | MIIM_ID | MIIM_STATE | MIIM_STRING | MIIM_SUBMENU | MIIM_TYPE;
171 ItemInfo->dwTypeData = NULL;
172
173 if (! NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, FALSE))
174 {
175 ItemInfo->fType = 0;
176 return FALSE;
177 }
178
179 if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING)
180 {
181 ItemInfo->cch++;
182 ItemInfo->dwTypeData = HeapAlloc(GetProcessHeap(), 0,
183 ItemInfo->cch * sizeof(WCHAR));
184 if (NULL == ItemInfo->dwTypeData)
185 {
186 return FALSE;
187 }
188
189 if (! NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, FALSE))
190 {
191 ItemInfo->fType = 0;
192 return FALSE;
193 }
194 ItemInfo->dwTypeData[ItemInfo->cch - 1] = UNICODE_NULL;
195 }
196 ItemInfo->fMask = Save_Mask;
197 return TRUE;
198 }
199
200 /***********************************************************************
201 * MenuSetRosMenuItemInfo
202 *
203 * Set selected information about a menu item, need to set the mask bits.
204 */
205 static BOOL FASTCALL
206 MenuSetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo)
207 {
208 BOOL Ret;
209
210 if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING &&
211 ItemInfo->dwTypeData != NULL)
212 {
213 ItemInfo->cch = strlenW(ItemInfo->dwTypeData);
214 }
215 Ret = NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, TRUE);
216
217 return Ret;
218 }
219
220 /***********************************************************************
221 * MenuCleanupRosMenuItemInfo
222 *
223 * Cleanup after use of MenuGet/SetRosMenuItemInfo
224 */
225 static VOID FASTCALL
226 MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
227 {
228 if (ItemInfo->dwTypeData != NULL)
229 {
230 HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData);
231 ItemInfo->dwTypeData = NULL;
232 }
233 }
234
235 /***********************************************************************
236 * MenuGetAllRosMenuItemInfo
237 *
238 * Get full information about all menu items
239 */
240 static INT FASTCALL
241 MenuGetAllRosMenuItemInfo(HMENU Menu, PROSMENUITEMINFO *ItemInfo)
242 {
243 DWORD BufSize;
244
245 BufSize = NtUserBuildMenuItemList(Menu, (VOID *) 1, 0, 0);
246 if (BufSize == (DWORD) -1 || BufSize == 0)
247 {
248 return -1;
249 }
250 *ItemInfo = HeapAlloc(GetProcessHeap(), 0, BufSize);
251 if (NULL == *ItemInfo)
252 {
253 return -1;
254 }
255
256 return NtUserBuildMenuItemList(Menu, *ItemInfo, BufSize, 0);
257 }
258
259 /***********************************************************************
260 * MenuCleanupAllRosMenuItemInfo
261 *
262 * Cleanup after use of MenuGetAllRosMenuItemInfo
263 */
264 static VOID FASTCALL
265 MenuCleanupAllRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
266 {
267 HeapFree(GetProcessHeap(), 0, ItemInfo);
268 }
269
270 /***********************************************************************
271 * MenuInitSysMenuPopup
272 *
273 * Grey the appropriate items in System menu.
274 */
275 void FASTCALL MenuInitSysMenuPopup(HMENU hmenu, DWORD style, DWORD clsStyle, LONG HitTest )
276 {
277 BOOL gray;
278 UINT DefItem;
279 #if 0
280 MENUITEMINFOW mii;
281 #endif
282
283 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
284 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
285 gray = ((style & WS_MAXIMIZE) != 0);
286 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
287 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
288 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
289 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
290 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
291 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
292 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
293 gray = (clsStyle & CS_NOCLOSE) != 0;
294
295 /* The menu item must keep its state if it's disabled */
296 if(gray)
297 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
298
299 /* Set default menu item */
300 if(style & WS_MINIMIZE) DefItem = SC_RESTORE;
301 else if(HitTest == HTCAPTION) DefItem = ((style & (WS_MAXIMIZE | WS_MINIMIZE)) ? SC_RESTORE : SC_MAXIMIZE);
302 else DefItem = SC_CLOSE;
303 #if 0
304 mii.cbSize = sizeof(MENUITEMINFOW);
305 mii.fMask |= MIIM_STATE;
306 if((DefItem != SC_CLOSE) && GetMenuItemInfoW(hmenu, DefItem, FALSE, &mii) &&
307 (mii.fState & (MFS_GRAYED | MFS_DISABLED))) DefItem = SC_CLOSE;
308 #endif
309 SetMenuDefaultItem(hmenu, DefItem, MF_BYCOMMAND);
310 }
311
312 /******************************************************************************
313 *
314 * UINT MenuGetStartOfNextColumn(
315 * PROSMENUINFO MenuInfo)
316 *
317 *****************************************************************************/
318 static UINT MenuGetStartOfNextColumn(
319 PROSMENUINFO MenuInfo)
320 {
321 PROSMENUITEMINFO MenuItems;
322 UINT i;
323
324 i = MenuInfo->FocusedItem;
325 if ( i == NO_SELECTED_ITEM )
326 return i;
327
328 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0)
329 return NO_SELECTED_ITEM;
330
331 for (i++ ; i < MenuInfo->MenuItemCount; i++)
332 if (0 != (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)))
333 return i;
334
335 return NO_SELECTED_ITEM;
336 }
337
338 /******************************************************************************
339 *
340 * UINT MenuGetStartOfPrevColumn(
341 * PROSMENUINFO MenuInfo)
342 *
343 *****************************************************************************/
344
345 static UINT FASTCALL MenuGetStartOfPrevColumn(
346 PROSMENUINFO MenuInfo)
347 {
348 PROSMENUITEMINFO MenuItems;
349 UINT i;
350
351 if (!MenuInfo->FocusedItem || MenuInfo->FocusedItem == NO_SELECTED_ITEM)
352 return NO_SELECTED_ITEM;
353
354 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0)
355 return NO_SELECTED_ITEM;
356
357 /* Find the start of the column */
358 for (i = MenuInfo->FocusedItem;
359 0 != i && 0 == (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
360 --i)
361 {
362 ; /* empty */
363 }
364
365 if (i == 0)
366 {
367 MenuCleanupAllRosMenuItemInfo(MenuItems);
368 return NO_SELECTED_ITEM;
369 }
370
371 for (--i; 0 != i; --i)
372 if (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
373 break;
374
375 MenuCleanupAllRosMenuItemInfo(MenuItems);
376 TRACE("ret %d.\n", i );
377
378 return i;
379 }
380
381 /***********************************************************************
382 * MenuFindSubMenu
383 *
384 * Find a Sub menu. Return the position of the submenu, and modifies
385 * *hmenu in case it is found in another sub-menu.
386 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
387 */
388 static UINT FASTCALL MenuFindSubMenu(HMENU *hmenu, HMENU hSubTarget )
389 {
390 ROSMENUINFO menu;
391 UINT i;
392 ROSMENUITEMINFO item;
393 if (((*hmenu)==(HMENU)0xffff) ||
394 (!MenuGetRosMenuInfo(&menu, *hmenu)))
395 return NO_SELECTED_ITEM;
396
397 MenuInitRosMenuItemInfo(&item);
398 for (i = 0; i < menu.MenuItemCount; i++) {
399 if (! MenuGetRosMenuItemInfo(menu.Self, i, &item))
400 {
401 MenuCleanupRosMenuItemInfo(&item);
402 return NO_SELECTED_ITEM;
403 }
404 if (!(item.fType & MF_POPUP)) continue;
405 if (item.hSubMenu == hSubTarget) {
406 MenuCleanupRosMenuItemInfo(&item);
407 return i;
408 }
409 else {
410 HMENU hsubmenu = item.hSubMenu;
411 UINT pos = MenuFindSubMenu(&hsubmenu, hSubTarget );
412 if (pos != NO_SELECTED_ITEM) {
413 *hmenu = hsubmenu;
414 return pos;
415 }
416 }
417 }
418 MenuCleanupRosMenuItemInfo(&item);
419 return NO_SELECTED_ITEM;
420 }
421
422 /***********************************************************************
423 * MenuLoadBitmaps
424 *
425 * Load the arrow bitmap. We can't do this from MenuInit since user32
426 * can also be used (and thus initialized) from text-mode.
427 */
428 static void FASTCALL
429 MenuLoadBitmaps(VOID)
430 {
431 /* Load system buttons bitmaps */
432 if (NULL == BmpSysMenu)
433 {
434 BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
435 }
436 }
437
438 /***********************************************************************
439 * MenuDrawPopupGlyph
440 *
441 * Draws popup magic glyphs (can be found in system menu).
442 */
443 static void FASTCALL
444 MenuDrawPopupGlyph(HDC dc, LPRECT r, INT_PTR popupMagic, BOOL inactive, BOOL hilite)
445 {
446 LOGFONTW lf;
447 HFONT hFont, hOldFont;
448 COLORREF clrsave;
449 INT bkmode;
450 TCHAR symbol;
451 switch (popupMagic)
452 {
453 case (INT_PTR) HBMMENU_POPUP_RESTORE:
454 symbol = '2';
455 break;
456 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
457 symbol = '0';
458 break;
459 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
460 symbol = '1';
461 break;
462 case (INT_PTR) HBMMENU_POPUP_CLOSE:
463 symbol = 'r';
464 break;
465 default:
466 ERR("Invalid popup magic bitmap %d\n", (int)popupMagic);
467 return;
468 }
469 ZeroMemory(&lf, sizeof(LOGFONTW));
470 InflateRect(r, -2, -2);
471 lf.lfHeight = r->bottom - r->top;
472 lf.lfWidth = 0;
473 lf.lfWeight = FW_NORMAL;
474 lf.lfCharSet = DEFAULT_CHARSET;
475 lstrcpy(lf.lfFaceName, TEXT("Marlett"));
476 hFont = CreateFontIndirect(&lf);
477 /* save font and text color */
478 hOldFont = SelectObject(dc, hFont);
479 clrsave = GetTextColor(dc);
480 bkmode = GetBkMode(dc);
481 /* set color and drawing mode */
482 SetBkMode(dc, TRANSPARENT);
483 if (inactive)
484 {
485 /* draw shadow */
486 if (!hilite)
487 {
488 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
489 TextOut(dc, r->left + 1, r->top + 1, &symbol, 1);
490 }
491 }
492 SetTextColor(dc, GetSysColor(inactive ? COLOR_GRAYTEXT : (hilite ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT)));
493 /* draw selected symbol */
494 TextOut(dc, r->left, r->top, &symbol, 1);
495 /* restore previous settings */
496 SetTextColor(dc, clrsave);
497 SelectObject(dc, hOldFont);
498 SetBkMode(dc, bkmode);
499 DeleteObject(hFont);
500 }
501
502 /***********************************************************************
503 * MenuFindItemByKey
504 *
505 * Find the menu item selected by a key press.
506 * Return item id, -1 if none, -2 if we should close the menu.
507 */
508 static UINT FASTCALL MenuFindItemByKey(HWND WndOwner, PROSMENUINFO MenuInfo,
509 WCHAR Key, BOOL ForceMenuChar)
510 {
511 ROSMENUINFO SysMenuInfo;
512 PROSMENUITEMINFO Items, ItemInfo;
513 LRESULT MenuChar;
514 UINT i;
515
516 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char) Key, Key, MenuInfo);
517
518 if (NULL == MenuInfo || ! IsMenu(MenuInfo->Self))
519 {
520 if (MenuGetRosMenuInfo(&SysMenuInfo, GetSystemMenu(WndOwner, FALSE)))
521 {
522 MenuInfo = &SysMenuInfo;
523 }
524 else
525 {
526 MenuInfo = NULL;
527 }
528 }
529
530 if (NULL != MenuInfo)
531 {
532 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &Items) <= 0)
533 {
534 return -1;
535 }
536 if (! ForceMenuChar)
537 {
538 Key = toupperW(Key);
539 ItemInfo = Items;
540 for (i = 0; i < MenuInfo->MenuItemCount; i++, ItemInfo++)
541 {
542 if ((ItemInfo->lpstr) && NULL != ItemInfo->dwTypeData)
543 {
544 WCHAR *p = (WCHAR *) ItemInfo->dwTypeData - 2;
545 do
546 {
547 p = strchrW(p + 2, '&');
548 }
549 while (NULL != p && L'&' == p[1]);
550 if (NULL != p && (toupperW(p[1]) == Key))
551 {
552 return i;
553 }
554 }
555 }
556 }
557
558 MenuChar = SendMessageW(WndOwner, WM_MENUCHAR,
559 MAKEWPARAM(Key, MenuInfo->Flags), (LPARAM) MenuInfo->Self);
560 if (2 == HIWORD(MenuChar))
561 {
562 return LOWORD(MenuChar);
563 }
564 if (1 == HIWORD(MenuChar))
565 {
566 return (UINT) (-2);
567 }
568 }
569
570 return (UINT)(-1);
571 }
572
573 /***********************************************************************
574 * MenuGetBitmapItemSize
575 *
576 * Get the size of a bitmap item.
577 */
578 static void FASTCALL MenuGetBitmapItemSize(PROSMENUITEMINFO lpitem, SIZE *size, HWND WndOwner)
579 {
580 BITMAP bm;
581 HBITMAP bmp = lpitem->hbmpItem;
582
583 size->cx = size->cy = 0;
584
585 /* check if there is a magic menu item associated with this item */
586 if (IS_MAGIC_BITMAP(bmp))
587 {
588 switch((INT_PTR) bmp)
589 {
590 case (INT_PTR)HBMMENU_CALLBACK:
591 {
592 MEASUREITEMSTRUCT measItem;
593 measItem.CtlType = ODT_MENU;
594 measItem.CtlID = 0;
595 measItem.itemID = lpitem->wID;
596 measItem.itemWidth = lpitem->Rect.right - lpitem->Rect.left;
597 measItem.itemHeight = lpitem->Rect.bottom - lpitem->Rect.top;
598 measItem.itemData = lpitem->dwItemData;
599 SendMessageW( WndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
600 size->cx = measItem.itemWidth;
601 size->cy = measItem.itemHeight;
602 return;
603 }
604 break;
605
606 case (INT_PTR) HBMMENU_SYSTEM:
607 if (0 != lpitem->dwItemData)
608 {
609 bmp = (HBITMAP) lpitem->dwItemData;
610 break;
611 }
612 /* fall through */
613 case (INT_PTR) HBMMENU_MBAR_RESTORE:
614 case (INT_PTR) HBMMENU_MBAR_MINIMIZE:
615 case (INT_PTR) HBMMENU_MBAR_CLOSE:
616 case (INT_PTR) HBMMENU_MBAR_MINIMIZE_D:
617 case (INT_PTR) HBMMENU_MBAR_CLOSE_D:
618 case (INT_PTR) HBMMENU_POPUP_CLOSE:
619 case (INT_PTR) HBMMENU_POPUP_RESTORE:
620 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
621 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
622 /* FIXME: Why we need to subtract these magic values? */
623 /* to make them smaller than the menu bar? */
624 size->cx = GetSystemMetrics(SM_CXSIZE) - 2;
625 size->cy = GetSystemMetrics(SM_CYSIZE) - 4;
626 return;
627 }
628 }
629
630 if (GetObjectW(bmp, sizeof(BITMAP), &bm))
631 {
632 size->cx = bm.bmWidth;
633 size->cy = bm.bmHeight;
634 }
635 }
636
637 /***********************************************************************
638 * MenuDrawBitmapItem
639 *
640 * Draw a bitmap item.
641 */
642 static void FASTCALL MenuDrawBitmapItem(HDC hdc, PROSMENUITEMINFO lpitem, const RECT *rect,
643 HMENU hmenu, HWND WndOwner, UINT odaction, BOOL MenuBar)
644 {
645 BITMAP bm;
646 DWORD rop;
647 HDC hdcMem;
648 HBITMAP bmp;
649 int w = rect->right - rect->left;
650 int h = rect->bottom - rect->top;
651 int bmp_xoffset = 0;
652 int left, top;
653 HBITMAP hbmToDraw = lpitem->hbmpItem;
654 bmp = hbmToDraw;
655
656 /* Check if there is a magic menu item associated with this item */
657 if (IS_MAGIC_BITMAP(hbmToDraw))
658 {
659 UINT flags = 0;
660 RECT r;
661
662 r = *rect;
663 switch ((INT_PTR)hbmToDraw)
664 {
665 case (INT_PTR)HBMMENU_SYSTEM:
666 if (lpitem->dwTypeData)
667 {
668 bmp = (HBITMAP)lpitem->dwTypeData;
669 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
670 }
671 else
672 {
673 if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
674 bmp = BmpSysMenu;
675 if (! GetObjectW(bmp, sizeof(bm), &bm)) return;
676 /* only use right half of the bitmap */
677 bmp_xoffset = bm.bmWidth / 2;
678 bm.bmWidth -= bmp_xoffset;
679 }
680 goto got_bitmap;
681 case (INT_PTR)HBMMENU_MBAR_RESTORE:
682 flags = DFCS_CAPTIONRESTORE;
683 break;
684 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
685 r.right += 1;
686 flags = DFCS_CAPTIONMIN;
687 break;
688 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
689 r.right += 1;
690 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
691 break;
692 case (INT_PTR)HBMMENU_MBAR_CLOSE:
693 flags = DFCS_CAPTIONCLOSE;
694 break;
695 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
696 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
697 break;
698 case (INT_PTR)HBMMENU_CALLBACK:
699 {
700 DRAWITEMSTRUCT drawItem;
701 POINT origorg;
702 drawItem.CtlType = ODT_MENU;
703 drawItem.CtlID = 0;
704 drawItem.itemID = lpitem->wID;
705 drawItem.itemAction = odaction;
706 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
707 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
708 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
709 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
710 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
711 drawItem.hwndItem = (HWND)hmenu;
712 drawItem.hDC = hdc;
713 drawItem.rcItem = *rect;
714 drawItem.itemData = lpitem->dwItemData;
715 /* some applications make this assumption on the DC's origin */
716 SetViewportOrgEx( hdc, lpitem->Rect.left, lpitem->Rect.top, &origorg);
717 OffsetRect( &drawItem.rcItem, - lpitem->Rect.left, - lpitem->Rect.top);
718 SendMessageW( WndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
719 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
720 return;
721 }
722 break;
723
724 case (INT_PTR) HBMMENU_POPUP_CLOSE:
725 case (INT_PTR) HBMMENU_POPUP_RESTORE:
726 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
727 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
728 MenuDrawPopupGlyph(hdc, &r, (INT_PTR)hbmToDraw, lpitem->fState & MF_GRAYED, lpitem->fState & MF_HILITE);
729 return;
730 }
731 InflateRect(&r, -1, -1);
732 if (0 != (lpitem->fState & MF_HILITE))
733 {
734 flags |= DFCS_PUSHED;
735 }
736 DrawFrameControl(hdc, &r, DFC_CAPTION, flags);
737 return;
738 }
739
740 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
741
742 got_bitmap:
743 hdcMem = CreateCompatibleDC( hdc );
744 SelectObject( hdcMem, bmp );
745
746 /* handle fontsize > bitmap_height */
747 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
748 left=rect->left;
749 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
750 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
751 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
752 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
753 DeleteDC( hdcMem );
754 }
755
756 /***********************************************************************
757 * MenuCalcItemSize
758 *
759 * Calculate the size of the menu item and store it in lpitem->rect.
760 */
761 static void FASTCALL MenuCalcItemSize( HDC hdc, PROSMENUITEMINFO lpitem, PROSMENUINFO MenuInfo, HWND hwndOwner,
762 INT orgX, INT orgY, BOOL menuBar)
763 {
764 WCHAR *p;
765 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
766 INT itemheight = 0;
767
768 TRACE("dc=%x owner=%x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
769
770 MenuCharSize.cx = GdiGetCharDimensions( hdc, NULL, &MenuCharSize.cy );
771
772 SetRect( &lpitem->Rect, orgX, orgY, orgX, orgY );
773
774 if (lpitem->fType & MF_OWNERDRAW)
775 {
776 MEASUREITEMSTRUCT mis;
777 mis.CtlType = ODT_MENU;
778 mis.CtlID = 0;
779 mis.itemID = lpitem->wID;
780 mis.itemData = lpitem->dwItemData;
781 mis.itemHeight = HIWORD( GetDialogBaseUnits());
782 mis.itemWidth = 0;
783 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
784 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
785 * width of a menufont character to the width of an owner-drawn menu.
786 */
787 lpitem->Rect.right += mis.itemWidth + 2 * MenuCharSize.cx;
788
789 if (menuBar) {
790 /* under at least win95 you seem to be given a standard
791 height for the menu and the height value is ignored */
792 lpitem->Rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
793 } else
794 lpitem->Rect.bottom += mis.itemHeight;
795
796 TRACE("id=%04lx size=%dx%d\n",
797 lpitem->wID, mis.itemWidth, mis.itemHeight);
798 return;
799 }
800
801 if (lpitem->fType & MF_SEPARATOR)
802 {
803 lpitem->Rect.bottom += SEPARATOR_HEIGHT;
804 if( !menuBar)
805 lpitem->Rect.right += check_bitmap_width + MenuCharSize.cx;
806 return;
807 }
808
809 lpitem->dxTab = 0;
810
811 if (lpitem->hbmpItem)
812 {
813 SIZE size;
814
815 if (!menuBar) {
816 MenuGetBitmapItemSize(lpitem, &size, hwndOwner );
817 /* Keep the size of the bitmap in callback mode to be able
818 * to draw it correctly */
819 lpitem->Rect.right = lpitem->Rect.left + size.cx;
820 if (MenuInfo->maxBmpSize.cx < abs(size.cx) + MENU_ITEM_HBMP_SPACE ||
821 MenuInfo->maxBmpSize.cy < abs(size.cy))
822 {
823 MenuInfo->maxBmpSize.cx = abs(size.cx) + MENU_ITEM_HBMP_SPACE;
824 MenuInfo->maxBmpSize.cy = abs(size.cy);
825 }
826 MenuSetRosMenuInfo(MenuInfo);
827 itemheight = size.cy + 2;
828
829 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
830 lpitem->Rect.right += 2 * check_bitmap_width;
831 lpitem->Rect.right += 4 + MenuCharSize.cx;
832 lpitem->dxTab = lpitem->Rect.right;
833 lpitem->Rect.right += check_bitmap_width;
834 } else /* hbmpItem & MenuBar */ {
835 MenuGetBitmapItemSize(lpitem, &size, hwndOwner );
836 lpitem->Rect.right += size.cx;
837 if( lpitem->lpstr) lpitem->Rect.right += 2;
838 itemheight = size.cy;
839
840 /* Special case: Minimize button doesn't have a space behind it. */
841 if (lpitem->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE ||
842 lpitem->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE_D)
843 lpitem->Rect.right -= 1;
844 }
845 }
846 else if (!menuBar) {
847 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
848 lpitem->Rect.right += check_bitmap_width;
849 lpitem->Rect.right += 4 + MenuCharSize.cx;
850 lpitem->dxTab = lpitem->Rect.right;
851 lpitem->Rect.right += check_bitmap_width;
852 }
853
854 /* it must be a text item - unless it's the system menu */
855 if (!(lpitem->fType & MF_SYSMENU) && lpitem->lpstr) {
856 HFONT hfontOld = NULL;
857 RECT rc = lpitem->Rect;
858 LONG txtheight, txtwidth;
859
860 if ( lpitem->fState & MFS_DEFAULT ) {
861 hfontOld = SelectObject( hdc, hMenuFontBold );
862 }
863 if (menuBar) {
864 txtheight = DrawTextW( hdc, lpitem->dwTypeData, -1, &rc,
865 DT_SINGLELINE|DT_CALCRECT);
866 lpitem->Rect.right += rc.right - rc.left;
867 itemheight = max( max( itemheight, txtheight),
868 GetSystemMetrics( SM_CYMENU) - 1);
869 lpitem->Rect.right += 2 * MenuCharSize.cx;
870 } else {
871 if ((p = strchrW( lpitem->dwTypeData, '\t' )) != NULL) {
872 RECT tmprc = rc;
873 LONG tmpheight;
874 int n = (int)( p - lpitem->dwTypeData);
875 /* Item contains a tab (only meaningful in popup menus) */
876 /* get text size before the tab */
877 txtheight = DrawTextW( hdc, lpitem->dwTypeData, n, &rc,
878 DT_SINGLELINE|DT_CALCRECT);
879 txtwidth = rc.right - rc.left;
880 p += 1; /* advance past the Tab */
881 /* get text size after the tab */
882 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
883 DT_SINGLELINE|DT_CALCRECT);
884 lpitem->dxTab += txtwidth;
885 txtheight = max( txtheight, tmpheight);
886 txtwidth += MenuCharSize.cx + /* space for the tab */
887 tmprc.right - tmprc.left; /* space for the short cut */
888 } else {
889 txtheight = DrawTextW( hdc, lpitem->dwTypeData, -1, &rc,
890 DT_SINGLELINE|DT_CALCRECT);
891 txtwidth = rc.right - rc.left;
892 lpitem->dxTab += txtwidth;
893 }
894 lpitem->Rect.right += 2 + txtwidth;
895 itemheight = max( itemheight,
896 max( txtheight + 2, MenuCharSize.cy + 4));
897 }
898 if (hfontOld) SelectObject (hdc, hfontOld);
899 } else if( menuBar) {
900 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
901 }
902 lpitem->Rect.bottom += itemheight;
903 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->Rect.left, lpitem->Rect.top, lpitem->Rect.right, lpitem->Rect.bottom);
904 }
905
906 /***********************************************************************
907 * MenuPopupMenuCalcSize
908 *
909 * Calculate the size of a popup menu.
910 */
911 static void FASTCALL MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo, HWND WndOwner)
912 {
913 ROSMENUITEMINFO lpitem;
914 HDC hdc;
915 int start, i;
916 int orgX, orgY, maxX, maxTab, maxTabWidth;
917
918 MenuInfo->Width = MenuInfo->Height = 0;
919 if (MenuInfo->MenuItemCount == 0)
920 {
921 MenuSetRosMenuInfo(MenuInfo);
922 return;
923 }
924
925 hdc = GetDC(NULL);
926 SelectObject( hdc, hMenuFont );
927
928 start = 0;
929 maxX = 2 + 1;
930
931 MenuInfo->maxBmpSize.cx = 0;
932 MenuInfo->maxBmpSize.cy = 0;
933
934 MenuInitRosMenuItemInfo(&lpitem);
935 while (start < MenuInfo->MenuItemCount)
936 {
937 orgX = maxX;
938 orgY = 2;
939
940 maxTab = maxTabWidth = 0;
941
942 /* Parse items until column break or end of menu */
943 for (i = start; i < MenuInfo->MenuItemCount; i++)
944 {
945 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i, &lpitem))
946 {
947 MenuCleanupRosMenuItemInfo(&lpitem);
948 MenuSetRosMenuInfo(MenuInfo);
949 return;
950 }
951 if (i != start &&
952 (lpitem.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
953
954 MenuCalcItemSize(hdc, &lpitem, MenuInfo, WndOwner, orgX, orgY, FALSE);
955 if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &lpitem))
956 {
957 MenuCleanupRosMenuItemInfo(&lpitem);
958 MenuSetRosMenuInfo(MenuInfo);
959 return;
960 }
961 // Not sure here,, The patch from wine removes this.
962 // if ((lpitem.fType & MF_MENUBARBREAK) != 0)
963 // {
964 // OrgX++;
965 // }
966 maxX = max(maxX, lpitem.Rect.right);
967 orgY = lpitem.Rect.bottom;
968 if ((lpitem.lpstr) && lpitem.dxTab )
969 {
970 maxTab = max( maxTab, lpitem.dxTab );
971 maxTabWidth = max(maxTabWidth, lpitem.Rect.right - lpitem.dxTab);
972 }
973 }
974
975 /* Finish the column (set all items to the largest width found) */
976 maxX = max( maxX, maxTab + maxTabWidth );
977 while (start < i)
978 {
979 if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &lpitem))
980 {
981 lpitem.Rect.right = maxX;
982 if ((lpitem.lpstr) && 0 != lpitem.dxTab)
983 {
984 lpitem.dxTab = maxTab;
985 }
986 MenuSetRosMenuItemInfo(MenuInfo->Self, start, &lpitem);
987 }
988 start++;
989 }
990 MenuInfo->Height = max(MenuInfo->Height, orgY);
991 }
992
993 MenuInfo->Width = maxX;
994
995 /* space for 3d border */
996 MenuInfo->Height += 2;
997 MenuInfo->Width += 2;
998
999 MenuCleanupRosMenuItemInfo(&lpitem);
1000 MenuSetRosMenuInfo(MenuInfo);
1001 ReleaseDC( 0, hdc );
1002 }
1003
1004 /***********************************************************************
1005 * MenuMenuBarCalcSize
1006 *
1007 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1008 * height is off by 1 pixel which causes lengthy window relocations when
1009 * active document window is maximized/restored.
1010 *
1011 * Calculate the size of the menu bar.
1012 */
1013 static void FASTCALL MenuMenuBarCalcSize( HDC hdc, LPRECT lprect,
1014 PROSMENUINFO MenuInfo, HWND hwndOwner )
1015 {
1016 ROSMENUITEMINFO ItemInfo;
1017 int start, i, orgX, orgY, maxY, helpPos;
1018
1019 if ((lprect == NULL) || (MenuInfo == NULL)) return;
1020 if (MenuInfo->MenuItemCount == 0) return;
1021 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n", lprect->left, lprect->top, lprect->right, lprect->bottom);
1022 MenuInfo->Width = lprect->right - lprect->left;
1023 MenuInfo->Height = 0;
1024 maxY = lprect->top + 1;
1025 start = 0;
1026 helpPos = -1;
1027
1028 MenuInfo->maxBmpSize.cx = 0;
1029 MenuInfo->maxBmpSize.cy = 0;
1030
1031 MenuInitRosMenuItemInfo(&ItemInfo);
1032 while (start < MenuInfo->MenuItemCount)
1033 {
1034 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo))
1035 {
1036 MenuCleanupRosMenuItemInfo(&ItemInfo);
1037 return;
1038 }
1039 orgX = lprect->left;
1040 orgY = maxY;
1041
1042 /* Parse items until line break or end of menu */
1043 for (i = start; i < MenuInfo->MenuItemCount; i++)
1044 {
1045 if ((helpPos == -1) && (ItemInfo.fType & MF_RIGHTJUSTIFY)) helpPos = i;
1046 if ((i != start) &&
1047 (ItemInfo.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1048
1049 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY);
1050 MenuCalcItemSize(hdc, &ItemInfo, MenuInfo, hwndOwner, orgX, orgY, TRUE);
1051 if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo))
1052 {
1053 MenuCleanupRosMenuItemInfo(&ItemInfo);
1054 return;
1055 }
1056
1057 if (ItemInfo.Rect.right > lprect->right)
1058 {
1059 if (i != start) break;
1060 else ItemInfo.Rect.right = lprect->right;
1061 }
1062 maxY = max( maxY, ItemInfo.Rect.bottom );
1063 orgX = ItemInfo.Rect.right;
1064 if (i + 1 < MenuInfo->MenuItemCount)
1065 {
1066 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i + 1, &ItemInfo))
1067 {
1068 MenuCleanupRosMenuItemInfo(&ItemInfo);
1069 return;
1070 }
1071 }
1072 }
1073
1074 /* FIXME: Is this really needed? */ /*NO! it is not needed, why make the
1075 HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
1076 #if 0
1077 /* Finish the line (set all items to the largest height found) */
1078 while (start < i)
1079 {
1080 if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo))
1081 {
1082 ItemInfo.Rect.bottom = maxY;
1083 MenuSetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo);
1084 }
1085 start++;
1086 }
1087 #else
1088 start = i; /* This works! */
1089 #endif
1090 }
1091
1092 lprect->bottom = maxY;
1093 MenuInfo->Height = lprect->bottom - lprect->top;
1094 MenuSetRosMenuInfo(MenuInfo);
1095
1096 if (helpPos != -1)
1097 {
1098 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1099 /* the last item (if several lines, only move the last line) */
1100 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->MenuItemCount - 1, &ItemInfo))
1101 {
1102 MenuCleanupRosMenuItemInfo(&ItemInfo);
1103 return;
1104 }
1105 orgY = ItemInfo.Rect.top;
1106 orgX = lprect->right;
1107 for (i = MenuInfo->MenuItemCount - 1; helpPos <= i; i--)
1108 {
1109 if (i < helpPos)
1110 {
1111 break; /* done */
1112 }
1113 if (ItemInfo.Rect.top != orgY)
1114 {
1115 break; /* Other line */
1116 }
1117 if (orgX <= ItemInfo.Rect.right)
1118 {
1119 break; /* Too far right already */
1120 }
1121 ItemInfo.Rect.left += orgX - ItemInfo.Rect.right;
1122 ItemInfo.Rect.right = orgX;
1123 orgX = ItemInfo.Rect.left;
1124 MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo);
1125 if (helpPos + 1 <= i &&
1126 ! MenuGetRosMenuItemInfo(MenuInfo->Self, i - 1, &ItemInfo))
1127 {
1128 MenuCleanupRosMenuItemInfo(&ItemInfo);
1129 return;
1130 }
1131 }
1132 }
1133
1134 MenuCleanupRosMenuItemInfo(&ItemInfo);
1135 }
1136
1137 /***********************************************************************
1138 * MenuDrawMenuItem
1139 *
1140 * Draw a single menu item.
1141 */
1142 static void FASTCALL MenuDrawMenuItem(HWND hWnd, PROSMENUINFO MenuInfo, HWND WndOwner, HDC hdc,
1143 PROSMENUITEMINFO lpitem, UINT Height, BOOL menuBar, UINT odaction)
1144 {
1145 RECT rect;
1146 PWCHAR Text;
1147 BOOL flat_menu = FALSE;
1148 int bkgnd;
1149 PWND Wnd = ValidateHwnd(hWnd);
1150
1151 if (!Wnd)
1152 return;
1153
1154 if (lpitem->fType & MF_SYSMENU)
1155 {
1156 if ( (Wnd->style & WS_MINIMIZE))
1157 {
1158 UserGetInsideRectNC(Wnd, &rect);
1159 UserDrawSysMenuButton(hWnd, hdc, &rect,
1160 lpitem->fState & (MF_HILITE | MF_MOUSESELECT));
1161 }
1162 return;
1163 }
1164
1165 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1166 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1167
1168 /* Setup colors */
1169
1170 if (lpitem->fState & MF_HILITE)
1171 {
1172 if(menuBar && !flat_menu) {
1173 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1174 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1175 } else {
1176 if (lpitem->fState & MF_GRAYED)
1177 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1178 else
1179 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1180 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1181 }
1182 }
1183 else
1184 {
1185 if (lpitem->fState & MF_GRAYED)
1186 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1187 else
1188 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1189 SetBkColor( hdc, GetSysColor( bkgnd ) );
1190 }
1191
1192 rect = lpitem->Rect;
1193
1194 if (lpitem->fType & MF_OWNERDRAW)
1195 {
1196 /*
1197 ** Experimentation under Windows reveals that an owner-drawn
1198 ** menu is given the rectangle which includes the space it requested
1199 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1200 ** and a popup-menu arrow. This is the value of lpitem->rect.
1201 ** Windows will leave all drawing to the application except for
1202 ** the popup-menu arrow. Windows always draws that itself, after
1203 ** the menu owner has finished drawing.
1204 */
1205 DRAWITEMSTRUCT dis;
1206
1207 dis.CtlType = ODT_MENU;
1208 dis.CtlID = 0;
1209 dis.itemID = lpitem->wID;
1210 dis.itemData = (DWORD)lpitem->dwItemData;
1211 dis.itemState = 0;
1212 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1213 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED | ODS_DISABLED;
1214 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1215 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1216 dis.hwndItem = (HWND) MenuInfo->Self;
1217 dis.hDC = hdc;
1218 dis.rcItem = rect;
1219 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1220 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hWnd,
1221 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1222 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1223 dis.rcItem.bottom);
1224 SendMessageW(WndOwner, WM_DRAWITEM, 0, (LPARAM) &dis);
1225 /* Draw the popup-menu arrow */
1226 if (lpitem->fType & MF_POPUP)
1227 {
1228 RECT rectTemp;
1229 CopyRect(&rectTemp, &rect);
1230 rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK);
1231 DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
1232 }
1233 return;
1234 }
1235
1236 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1237
1238 if (lpitem->fState & MF_HILITE)
1239 {
1240 if (flat_menu)
1241 {
1242 InflateRect (&rect, -1, -1);
1243 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1244 InflateRect (&rect, 1, 1);
1245 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1246 }
1247 else
1248 {
1249 if(menuBar)
1250 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1251 else
1252 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1253 }
1254 }
1255 else
1256 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1257
1258 SetBkMode( hdc, TRANSPARENT );
1259
1260 /* vertical separator */
1261 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1262 {
1263 HPEN oldPen;
1264 RECT rc = rect;
1265
1266 rc.left -= 3;
1267 rc.top = 3;
1268 rc.bottom = Height - 3;
1269 if (flat_menu)
1270 {
1271 oldPen = SelectObject( hdc, GetStockObject(DC_PEN) );
1272 SetDCPenColor(hdc, GetSysColor(COLOR_BTNSHADOW));
1273 MoveToEx( hdc, rc.left, rc.top, NULL );
1274 LineTo( hdc, rc.left, rc.bottom );
1275 SelectObject( hdc, oldPen );
1276 }
1277 else
1278 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1279 }
1280
1281 /* horizontal separator */
1282 if (lpitem->fType & MF_SEPARATOR)
1283 {
1284 HPEN oldPen;
1285 RECT rc = rect;
1286
1287 rc.left++;
1288 rc.right--;
1289 rc.top += SEPARATOR_HEIGHT / 2;
1290 if (flat_menu)
1291 {
1292 oldPen = SelectObject( hdc, GetStockObject(DC_PEN) );
1293 SetDCPenColor( hdc, GetSysColor(COLOR_BTNSHADOW));
1294 MoveToEx( hdc, rc.left, rc.top, NULL );
1295 LineTo( hdc, rc.right, rc.top );
1296 SelectObject( hdc, oldPen );
1297 }
1298 else
1299 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1300 return;
1301 }
1302
1303 #if 0
1304 /* helper lines for debugging */
1305 /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
1306 FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1307 SelectObject(hdc, GetStockObject(DC_PEN));
1308 SetDCPenColor(hdc, GetSysColor(COLOR_WINDOWFRAME));
1309 MoveToEx(hdc, rect.left, (rect.top + rect.bottom) / 2, NULL);
1310 LineTo(hdc, rect.right, (rect.top + rect.bottom) / 2);
1311 #endif
1312
1313 if (!menuBar)
1314 {
1315 HBITMAP bm;
1316 INT y = rect.top + rect.bottom;
1317 RECT rc = rect;
1318 int checked = FALSE;
1319 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1320 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1321 /* Draw the check mark
1322 *
1323 * FIXME:
1324 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1325 */
1326 if( !(MenuInfo->dwStyle & MNS_NOCHECK)) {
1327 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hbmpChecked :
1328 lpitem->hbmpUnchecked;
1329 if (bm) /* we have a custom bitmap */
1330 {
1331 HDC hdcMem = CreateCompatibleDC( hdc );
1332
1333 SelectObject( hdcMem, bm );
1334 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1335 check_bitmap_width, check_bitmap_height,
1336 hdcMem, 0, 0, SRCCOPY );
1337 DeleteDC( hdcMem );
1338 checked = TRUE;
1339 }
1340 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1341 {
1342 RECT r;
1343 CopyRect(&r, &rect);
1344 r.right = r.left + GetSystemMetrics(SM_CXMENUCHECK);
1345 DrawFrameControl( hdc, &r, DFC_MENU,
1346 (lpitem->fType & MFT_RADIOCHECK) ?
1347 DFCS_MENUBULLET : DFCS_MENUCHECK);
1348 checked = TRUE;
1349 }
1350 }
1351 if ( lpitem->hbmpItem )
1352 {
1353 RECT bmpRect;
1354 CopyRect(&bmpRect, &rect);
1355 if (!(MenuInfo->dwStyle & MNS_CHECKORBMP) && !(MenuInfo->dwStyle & MNS_NOCHECK))
1356 bmpRect.left += check_bitmap_width + 2;
1357 if (!(checked && (MenuInfo->dwStyle & MNS_CHECKORBMP)))
1358 {
1359 bmpRect.right = bmpRect.left + MenuInfo->maxBmpSize.cx;
1360 MenuDrawBitmapItem(hdc, lpitem, &bmpRect, MenuInfo->Self, WndOwner, odaction, menuBar);
1361 }
1362 }
1363 /* Draw the popup-menu arrow */
1364 if (lpitem->fType & MF_POPUP)
1365 {
1366 RECT rectTemp;
1367 CopyRect(&rectTemp, &rect);
1368 rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK);
1369 DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
1370 }
1371 rect.left += 4;
1372 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
1373 rect.left += check_bitmap_width;
1374 rect.right -= check_bitmap_width;
1375 }
1376 else if( lpitem->hbmpItem)
1377 { /* Draw the bitmap */
1378 MenuDrawBitmapItem(hdc, lpitem, &rect, MenuInfo->Self, WndOwner, odaction, menuBar);
1379 }
1380
1381 /* process text if present */
1382 if (lpitem->lpstr)
1383 {
1384 register int i = 0;
1385 HFONT hfontOld = 0;
1386
1387 UINT uFormat = menuBar ? DT_CENTER | DT_VCENTER | DT_SINGLELINE
1388 : DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1389
1390 if(MenuInfo->dwStyle & MNS_CHECKORBMP)
1391 rect.left += max(0, MenuInfo->maxBmpSize.cx - GetSystemMetrics(SM_CXMENUCHECK));
1392 else
1393 rect.left += MenuInfo->maxBmpSize.cx;
1394
1395 if ( lpitem->fState & MFS_DEFAULT )
1396 {
1397 hfontOld = SelectObject(hdc, hMenuFontBold);
1398 }
1399
1400 if (menuBar) {
1401 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1402 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1403 }
1404
1405 Text = (PWCHAR) lpitem->dwTypeData;
1406 if(Text)
1407 {
1408 for (i = 0; L'\0' != Text[i]; i++)
1409 if (Text[i] == L'\t' || Text[i] == L'\b')
1410 break;
1411 }
1412
1413 if(lpitem->fState & MF_GRAYED)
1414 {
1415 if (!(lpitem->fState & MF_HILITE) )
1416 {
1417 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1418 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1419 DrawTextW( hdc, Text, i, &rect, uFormat );
1420 --rect.left; --rect.top; --rect.right; --rect.bottom;
1421 }
1422 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1423 }
1424
1425 DrawTextW( hdc, Text, i, &rect, uFormat);
1426
1427 /* paint the shortcut text */
1428 if (!menuBar && L'\0' != Text[i]) /* There's a tab or flush-right char */
1429 {
1430 if (L'\t' == Text[i])
1431 {
1432 rect.left = lpitem->dxTab;
1433 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1434 }
1435 else
1436 {
1437 rect.right = lpitem->dxTab;
1438 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1439 }
1440
1441 if (lpitem->fState & MF_GRAYED)
1442 {
1443 if (!(lpitem->fState & MF_HILITE) )
1444 {
1445 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1446 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1447 DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat);
1448 --rect.left; --rect.top; --rect.right; --rect.bottom;
1449 }
1450 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1451 }
1452 DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat );
1453 }
1454
1455 if (hfontOld)
1456 SelectObject (hdc, hfontOld);
1457 }
1458 }
1459
1460 /***********************************************************************
1461 * MenuDrawPopupMenu
1462 *
1463 * Paint a popup menu.
1464 */
1465 static void FASTCALL MenuDrawPopupMenu(HWND hwnd, HDC hdc, HMENU hmenu )
1466 {
1467 HBRUSH hPrevBrush = 0;
1468 RECT rect;
1469
1470 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1471
1472 GetClientRect( hwnd, &rect );
1473
1474 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1475 && (SelectObject( hdc, hMenuFont)))
1476 {
1477 HPEN hPrevPen;
1478
1479 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1480
1481 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1482 if ( hPrevPen )
1483 {
1484 BOOL flat_menu = FALSE;
1485 ROSMENUINFO MenuInfo;
1486 ROSMENUITEMINFO ItemInfo;
1487
1488 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1489 if (flat_menu)
1490 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1491 else
1492 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1493
1494 /* draw menu items */
1495 if (MenuGetRosMenuInfo(&MenuInfo, hmenu) && MenuInfo.MenuItemCount)
1496 {
1497 UINT u;
1498
1499 MenuInitRosMenuItemInfo(&ItemInfo);
1500
1501 for (u = 0; u < MenuInfo.MenuItemCount; u++)
1502 {
1503 if (MenuGetRosMenuItemInfo(MenuInfo.Self, u, &ItemInfo))
1504 {
1505 MenuDrawMenuItem(hwnd, &MenuInfo, MenuInfo.WndOwner, hdc, &ItemInfo,
1506 MenuInfo.Height, FALSE, ODA_DRAWENTIRE);
1507 }
1508 }
1509
1510 MenuCleanupRosMenuItemInfo(&ItemInfo);
1511 }
1512 } else
1513 {
1514 SelectObject( hdc, hPrevBrush );
1515 }
1516 }
1517 }
1518
1519 /***********************************************************************
1520 * MenuDrawMenuBar
1521 *
1522 * Paint a menu bar. Returns the height of the menu bar.
1523 * called from [windows/nonclient.c]
1524 */
1525 UINT MenuDrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1526 BOOL suppress_draw)
1527 {
1528 ROSMENUINFO lppop;
1529 HFONT hfontOld = 0;
1530 HMENU hMenu = GetMenu(hwnd);
1531
1532 if (! MenuGetRosMenuInfo(&lppop, hMenu) || lprect == NULL)
1533 {
1534 return GetSystemMetrics(SM_CYMENU);
1535 }
1536
1537 if (suppress_draw)
1538 {
1539 hfontOld = SelectObject(hDC, hMenuFont);
1540
1541 MenuMenuBarCalcSize(hDC, lprect, &lppop, hwnd);
1542
1543 lprect->bottom = lprect->top + lppop.Height;
1544
1545 if (hfontOld) SelectObject( hDC, hfontOld);
1546 return lppop.Height;
1547 }
1548 else
1549 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1550 }
1551
1552 /***********************************************************************
1553 * MenuShowPopup
1554 *
1555 * Display a popup menu.
1556 */
1557 static BOOL FASTCALL MenuShowPopup(HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1558 INT x, INT y, INT xanchor, INT yanchor )
1559 {
1560 ROSMENUINFO MenuInfo;
1561 ROSMENUITEMINFO ItemInfo;
1562 UINT width, height;
1563 POINT pt;
1564 HMONITOR monitor;
1565 MONITORINFO info;
1566
1567 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1568 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1569
1570 if (! MenuGetRosMenuInfo(&MenuInfo, hmenu)) return FALSE;
1571 if (MenuInfo.FocusedItem != NO_SELECTED_ITEM)
1572 {
1573 MenuInitRosMenuItemInfo(&ItemInfo);
1574 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
1575 {
1576 ItemInfo.fMask |= MIIM_STATE;
1577 ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
1578 MenuSetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo);
1579 }
1580 MenuCleanupRosMenuItemInfo(&ItemInfo);
1581 MenuInfo.FocusedItem = NO_SELECTED_ITEM;
1582 }
1583
1584 /* store the owner for DrawItem */
1585 MenuInfo.WndOwner = hwndOwner;
1586 MenuSetRosMenuInfo(&MenuInfo);
1587
1588 MenuPopupMenuCalcSize(&MenuInfo, hwndOwner);
1589
1590 /* adjust popup menu pos so that it fits within the desktop */
1591
1592 width = MenuInfo.Width + GetSystemMetrics(SM_CXBORDER);
1593 height = MenuInfo.Height + GetSystemMetrics(SM_CYBORDER);
1594
1595 /* FIXME: should use item rect */
1596 pt.x = x;
1597 pt.y = y;
1598 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1599 info.cbSize = sizeof(info);
1600 GetMonitorInfoW( monitor, &info );
1601
1602 if( flags & TPM_RIGHTALIGN ) x -= width;
1603 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1604
1605 if( flags & TPM_BOTTOMALIGN ) y -= height;
1606 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1607
1608 if( x + width > info.rcMonitor.right)
1609 {
1610 if( xanchor && x >= width - xanchor )
1611 x -= width - xanchor;
1612
1613 if( x + width > info.rcMonitor.right)
1614 x = info.rcMonitor.right - width;
1615 }
1616 if( x < info.rcMonitor.left ) x = info.rcMonitor.left;
1617
1618 if( y + height > info.rcMonitor.bottom)
1619 {
1620 if( yanchor && y >= height + yanchor )
1621 y -= height + yanchor;
1622
1623 if( y + height > info.rcMonitor.bottom)
1624 y = info.rcMonitor.bottom - height;
1625 }
1626 if( y < info.rcMonitor.top ) y = info.rcMonitor.top;
1627
1628 /* NOTE: In Windows, top menu popup is not owned. */
1629 MenuInfo.Wnd = CreateWindowExW( 0, POPUPMENU_CLASS_ATOMW, NULL,
1630 WS_POPUP, x, y, width, height,
1631 hwndOwner, 0, (HINSTANCE) GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1632 (LPVOID) MenuInfo.Self);
1633 if ( !MenuInfo.Wnd || ! MenuSetRosMenuInfo(&MenuInfo)) return FALSE;
1634 if (!top_popup) {
1635 top_popup = MenuInfo.Wnd;
1636 top_popup_hmenu = hmenu;
1637 }
1638
1639 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0);
1640
1641 /* Display the window */
1642
1643 SetWindowPos( MenuInfo.Wnd, HWND_TOPMOST, 0, 0, 0, 0,
1644 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
1645 UpdateWindow( MenuInfo.Wnd );
1646 return TRUE;
1647 }
1648
1649
1650 /***********************************************************************
1651 * MenuSelectItem
1652 */
1653 static void FASTCALL MenuSelectItem(HWND hwndOwner, PROSMENUINFO hmenu, UINT wIndex,
1654 BOOL sendMenuSelect, HMENU topmenu)
1655 {
1656 ROSMENUITEMINFO ItemInfo;
1657 ROSMENUINFO TopMenuInfo;
1658 HDC hdc;
1659
1660 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1661
1662 if (!hmenu || !hmenu->MenuItemCount || !hmenu->Wnd) return;
1663 if (hmenu->FocusedItem == wIndex) return;
1664 if (hmenu->Flags & MF_POPUP) hdc = GetDC(hmenu->Wnd);
1665 else hdc = GetDCEx(hmenu->Wnd, 0, DCX_CACHE | DCX_WINDOW);
1666 if (!top_popup) {
1667 top_popup = hmenu->Wnd;
1668 top_popup_hmenu = hmenu->Self;
1669 }
1670
1671 SelectObject( hdc, hMenuFont );
1672
1673 MenuInitRosMenuItemInfo(&ItemInfo);
1674
1675 /* Clear previous highlighted item */
1676 if (hmenu->FocusedItem != NO_SELECTED_ITEM)
1677 {
1678 if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->FocusedItem, &ItemInfo))
1679 {
1680 ItemInfo.fMask |= MIIM_STATE;
1681 ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
1682 MenuSetRosMenuItemInfo(hmenu->Self, hmenu->FocusedItem, &ItemInfo);
1683 }
1684 MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc, &ItemInfo,
1685 hmenu->Height, ! (hmenu->Flags & MF_POPUP),
1686 ODA_SELECT);
1687 }
1688
1689 /* Highlight new item (if any) */
1690 hmenu->FocusedItem = wIndex;
1691 MenuSetRosMenuInfo(hmenu);
1692 if (hmenu->FocusedItem != NO_SELECTED_ITEM)
1693 {
1694 if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->FocusedItem, &ItemInfo))
1695 {
1696 if (!(ItemInfo.fType & MF_SEPARATOR))
1697 {
1698 ItemInfo.fMask |= MIIM_STATE;
1699 ItemInfo.fState |= MF_HILITE;
1700 MenuSetRosMenuItemInfo(hmenu->Self, hmenu->FocusedItem, &ItemInfo);
1701 MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc,
1702 &ItemInfo, hmenu->Height, ! (hmenu->Flags & MF_POPUP),
1703 ODA_SELECT);
1704 }
1705 if (sendMenuSelect)
1706 {
1707 SendMessageW(hwndOwner, WM_MENUSELECT,
1708 MAKELONG(ItemInfo.fType & MF_POPUP ? wIndex : ItemInfo.wID,
1709 ItemInfo.fType | ItemInfo.fState | MF_MOUSESELECT |
1710 (hmenu->Flags & MF_SYSMENU)), (LPARAM) hmenu->Self);
1711 }
1712 }
1713 }
1714 else if (sendMenuSelect) {
1715 if(topmenu) {
1716 int pos;
1717 pos = MenuFindSubMenu(&topmenu, hmenu->Self);
1718 if (pos != NO_SELECTED_ITEM)
1719 {
1720 if (MenuGetRosMenuInfo(&TopMenuInfo, topmenu)
1721 && MenuGetRosMenuItemInfo(topmenu, pos, &ItemInfo))
1722 {
1723 SendMessageW(hwndOwner, WM_MENUSELECT,
1724 MAKELONG(Pos, ItemInfo.fType | ItemInfo.fState
1725 | MF_MOUSESELECT
1726 | (TopMenuInfo.Flags & MF_SYSMENU)),
1727 (LPARAM) topmenu);
1728 }
1729 }
1730 }
1731 }
1732 MenuCleanupRosMenuItemInfo(&ItemInfo);
1733 ReleaseDC(hmenu->Wnd, hdc);
1734 }
1735
1736 /***********************************************************************
1737 * MenuMoveSelection
1738 *
1739 * Moves currently selected item according to the Offset parameter.
1740 * If there is no selection then it should select the last item if
1741 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
1742 */
1743 static void FASTCALL
1744 MenuMoveSelection(HWND WndOwner, PROSMENUINFO MenuInfo, INT Offset)
1745 {
1746 INT i;
1747 ROSMENUITEMINFO ItemInfo;
1748 INT OrigPos;
1749
1750 TRACE("hwnd=%x menu=%x off=0x%04x\n", WndOwner, MenuInfo, Offset);
1751
1752 /* Prevent looping */
1753 if (0 == MenuInfo->MenuItemCount || 0 == Offset)
1754 return;
1755 else if (Offset < -1)
1756 Offset = -1;
1757 else if (Offset > 1)
1758 Offset = 1;
1759
1760 MenuInitRosMenuItemInfo(&ItemInfo);
1761
1762 OrigPos = MenuInfo->FocusedItem;
1763 if (OrigPos == NO_SELECTED_ITEM) /* NO_SELECTED_ITEM is not -1 ! */
1764 {
1765 OrigPos = 0;
1766 i = -1;
1767 }
1768 else
1769 {
1770 i = MenuInfo->FocusedItem;
1771 }
1772
1773 do
1774 {
1775 /* Step */
1776 i += Offset;
1777 /* Clip and wrap around */
1778 if (i < 0)
1779 {
1780 i = MenuInfo->MenuItemCount - 1;
1781 }
1782 else if (i >= MenuInfo->MenuItemCount)
1783 {
1784 i = 0;
1785 }
1786 /* If this is a good candidate; */
1787 if (MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo) &&
1788 0 == (ItemInfo.fType & MF_SEPARATOR))
1789 {
1790 MenuSelectItem(WndOwner, MenuInfo, i, TRUE, NULL);
1791 MenuCleanupRosMenuItemInfo(&ItemInfo);
1792 return;
1793 }
1794 } while (i != OrigPos);
1795
1796 /* Not found */
1797 MenuCleanupRosMenuItemInfo(&ItemInfo);
1798 }
1799
1800 LRESULT WINAPI PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
1801 {
1802 TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
1803
1804 switch(Message)
1805 {
1806 case WM_CREATE:
1807 {
1808 CREATESTRUCTA *cs = (CREATESTRUCTA *) lParam;
1809 SetWindowLongPtrA(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
1810 return 0;
1811 }
1812
1813 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1814 return MA_NOACTIVATE;
1815
1816 case WM_PAINT:
1817 {
1818 PAINTSTRUCT ps;
1819 BeginPaint(Wnd, &ps);
1820 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrA(Wnd, 0));
1821 EndPaint(Wnd, &ps);
1822 return 0;
1823 }
1824
1825 case WM_PRINTCLIENT:
1826 {
1827 MenuDrawPopupMenu( Wnd, (HDC)wParam,
1828 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
1829 return 0;
1830 }
1831
1832 case WM_ERASEBKGND:
1833 return 1;
1834
1835 case WM_DESTROY:
1836 /* zero out global pointer in case resident popup window was destroyed. */
1837 if (Wnd == top_popup)
1838 {
1839 top_popup = NULL;
1840 top_popup_hmenu = NULL;
1841 }
1842 break;
1843
1844 case WM_SHOWWINDOW:
1845 if (0 != wParam)
1846 {
1847 if (0 == GetWindowLongPtrA(Wnd, 0))
1848 {
1849 OutputDebugStringA("no menu to display\n");
1850 }
1851 }
1852 else
1853 {
1854 SetWindowLongPtrA(Wnd, 0, 0);
1855 }
1856 break;
1857
1858 case MM_SETMENUHANDLE:
1859 SetWindowLongPtrA(Wnd, 0, wParam);
1860 break;
1861
1862 case MM_GETMENUHANDLE:
1863 case MN_GETHMENU:
1864 return GetWindowLongPtrA(Wnd, 0);
1865
1866 default:
1867 return DefWindowProcA(Wnd, Message, wParam, lParam);
1868 }
1869 return 0;
1870 }
1871
1872 LRESULT WINAPI
1873 PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
1874 {
1875 TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
1876
1877 switch(Message)
1878 {
1879 case WM_CREATE:
1880 {
1881 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
1882 SetWindowLongPtrW(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
1883 return 0;
1884 }
1885
1886 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1887 return MA_NOACTIVATE;
1888
1889 case WM_PAINT:
1890 {
1891 PAINTSTRUCT ps;
1892 BeginPaint(Wnd, &ps);
1893 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrW(Wnd, 0));
1894 EndPaint(Wnd, &ps);
1895 return 0;
1896 }
1897
1898 case WM_PRINTCLIENT:
1899 {
1900 MenuDrawPopupMenu( Wnd, (HDC)wParam,
1901 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
1902 return 0;
1903 }
1904
1905 case WM_ERASEBKGND:
1906 return 1;
1907
1908 case WM_DESTROY:
1909 /* zero out global pointer in case resident popup window was destroyed. */
1910 if (Wnd == top_popup)
1911 {
1912 top_popup = NULL;
1913 top_popup_hmenu = NULL;
1914 }
1915 break;
1916
1917 case WM_SHOWWINDOW:
1918 if (0 != wParam)
1919 {
1920 if (0 == GetWindowLongPtrW(Wnd, 0))
1921 {
1922 OutputDebugStringA("no menu to display\n");
1923 }
1924 }
1925 else
1926 {
1927 SetWindowLongPtrW(Wnd, 0, 0);
1928 }
1929 break;
1930
1931 case MM_SETMENUHANDLE:
1932 SetWindowLongPtrW(Wnd, 0, wParam);
1933 break;
1934
1935 case MM_GETMENUHANDLE:
1936 case MN_GETHMENU:
1937 return GetWindowLongPtrW(Wnd, 0);
1938
1939 default:
1940 return DefWindowProcW(Wnd, Message, wParam, lParam);
1941 }
1942
1943 return 0;
1944 }
1945
1946 /**********************************************************************
1947 * MENU_ParseResource
1948 *
1949 * Parse a standard menu resource and add items to the menu.
1950 * Return a pointer to the end of the resource.
1951 *
1952 * NOTE: flags is equivalent to the mtOption field
1953 */
1954 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1955 {
1956 WORD flags, id = 0;
1957 HMENU hSubMenu;
1958 LPCSTR str;
1959 BOOL end = FALSE;
1960
1961 do
1962 {
1963 flags = GET_WORD(res);
1964
1965 /* remove MF_END flag before passing it to AppendMenu()! */
1966 end = (flags & MF_END);
1967 if(end) flags ^= MF_END;
1968
1969 res += sizeof(WORD);
1970 if(!(flags & MF_POPUP))
1971 {
1972 id = GET_WORD(res);
1973 res += sizeof(WORD);
1974 }
1975 str = res;
1976 if(!unicode)
1977 res += strlen(str) + 1;
1978 else
1979 res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1980 if (flags & MF_POPUP)
1981 {
1982 hSubMenu = CreatePopupMenu();
1983 if(!hSubMenu) return NULL;
1984 if(!(res = MENU_ParseResource(res, hSubMenu, unicode)))
1985 return NULL;
1986 if(!unicode)
1987 AppendMenuA(hMenu, flags, (UINT)hSubMenu, str);
1988 else
1989 AppendMenuW(hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str);
1990 }
1991 else /* Not a popup */
1992 {
1993 if(!unicode)
1994 {
1995 if (*str == 0)
1996 flags = MF_SEPARATOR;
1997 }
1998 else
1999 {
2000 if (*(LPCWSTR)str == 0)
2001 flags = MF_SEPARATOR;
2002 }
2003
2004 if (flags & MF_SEPARATOR)
2005 {
2006 if (!(flags & (MF_GRAYED | MF_DISABLED)))
2007 flags |= MF_GRAYED | MF_DISABLED;
2008 }
2009
2010 if(!unicode)
2011 AppendMenuA(hMenu, flags, id, *str ? str : NULL);
2012 else
2013 AppendMenuW(hMenu, flags, id,
2014 *(LPCWSTR)str ? (LPCWSTR)str : NULL);
2015 }
2016 } while(!end);
2017 return res;
2018 }
2019
2020
2021 /**********************************************************************
2022 * MENUEX_ParseResource
2023 *
2024 * Parse an extended menu resource and add items to the menu.
2025 * Return a pointer to the end of the resource.
2026 */
2027 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2028 {
2029 WORD resinfo;
2030 do {
2031 MENUITEMINFOW mii;
2032
2033 mii.cbSize = sizeof(mii);
2034 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
2035 mii.fType = GET_DWORD(res);
2036 res += sizeof(DWORD);
2037 mii.fState = GET_DWORD(res);
2038 res += sizeof(DWORD);
2039 mii.wID = GET_DWORD(res);
2040 res += sizeof(DWORD);
2041 resinfo = GET_WORD(res);
2042 res += sizeof(WORD);
2043 /* Align the text on a word boundary. */
2044 res += (~((UINT_PTR)res - 1)) & 1;
2045 mii.dwTypeData = (LPWSTR) res;
2046 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2047 /* Align the following fields on a dword boundary. */
2048 res += (~((UINT_PTR)res - 1)) & 3;
2049
2050 TRACE("Menu item: [%08x,%08x,%04x,%04x,%S]\n",
2051 mii.fType, mii.fState, mii.wID, resinfo, mii.dwTypeData);
2052
2053 if (resinfo & 1) { /* Pop-up? */
2054 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2055 res += sizeof(DWORD);
2056 mii.hSubMenu = CreatePopupMenu();
2057 if (!mii.hSubMenu)
2058 return NULL;
2059 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2060 DestroyMenu(mii.hSubMenu);
2061 return NULL;
2062 }
2063 mii.fMask |= MIIM_SUBMENU;
2064 mii.fType |= MF_POPUP;
2065 mii.wID = (UINT) mii.hSubMenu;
2066 }
2067 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2068 {
2069 mii.fType |= MF_SEPARATOR;
2070 }
2071 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2072 } while (!(resinfo & MF_END));
2073 return res;
2074 }
2075
2076 NTSTATUS WINAPI
2077 User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength)
2078 {
2079 HMENU hmenu = LoadMenuW(User32Instance, L"SYSMENU");
2080 LRESULT Result = (LRESULT)hmenu;
2081 MENUINFO menuinfo = {0};
2082 MENUITEMINFOW info = {0};
2083
2084 // removing space for checkboxes from menu
2085 menuinfo.cbSize = sizeof(menuinfo);
2086 menuinfo.fMask = MIM_STYLE;
2087 GetMenuInfo(hmenu, &menuinfo);
2088 menuinfo.dwStyle |= MNS_NOCHECK;
2089 SetMenuInfo(hmenu, &menuinfo);
2090
2091 // adding bitmaps to menu items
2092 info.cbSize = sizeof(info);
2093 info.fMask |= MIIM_BITMAP;
2094 info.hbmpItem = HBMMENU_POPUP_MINIMIZE;
2095 SetMenuItemInfoW(hmenu, SC_MINIMIZE, FALSE, &info);
2096 info.hbmpItem = HBMMENU_POPUP_RESTORE;
2097 SetMenuItemInfoW(hmenu, SC_RESTORE, FALSE, &info);
2098 info.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
2099 SetMenuItemInfoW(hmenu, SC_MAXIMIZE, FALSE, &info);
2100 info.hbmpItem = HBMMENU_POPUP_CLOSE;
2101 SetMenuItemInfoW(hmenu, SC_CLOSE, FALSE, &info);
2102
2103 return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS));
2104 }
2105
2106
2107 BOOL
2108 MenuInit(VOID)
2109 {
2110 NONCLIENTMETRICSW ncm;
2111
2112 /* get the menu font */
2113 if(!hMenuFont || !hMenuFontBold)
2114 {
2115 ncm.cbSize = sizeof(ncm);
2116 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
2117 {
2118 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
2119 return FALSE;
2120 }
2121
2122 hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont);
2123 if(hMenuFont == NULL)
2124 {
2125 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
2126 return FALSE;
2127 }
2128
2129 ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
2130 hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont);
2131 if(hMenuFontBold == NULL)
2132 {
2133 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
2134 DeleteObject(hMenuFont);
2135 hMenuFont = NULL;
2136 return FALSE;
2137 }
2138 }
2139
2140 return TRUE;
2141 }
2142
2143 VOID
2144 MenuCleanup(VOID)
2145 {
2146 if (hMenuFont)
2147 {
2148 DeleteObject(hMenuFont);
2149 hMenuFont = NULL;
2150 }
2151
2152 if (hMenuFontBold)
2153 {
2154 DeleteObject(hMenuFontBold);
2155 hMenuFontBold = NULL;
2156 }
2157 }
2158
2159 /***********************************************************************
2160 * DrawMenuBarTemp (USER32.@)
2161 *
2162 * UNDOCUMENTED !!
2163 *
2164 * called by W98SE desk.cpl Control Panel Applet
2165 *
2166 * Not 100% sure about the param names, but close.
2167 *
2168 * @implemented
2169 */
2170 DWORD WINAPI
2171 DrawMenuBarTemp(HWND Wnd, HDC DC, LPRECT Rect, HMENU Menu, HFONT Font)
2172 {
2173 ROSMENUINFO MenuInfo;
2174 ROSMENUITEMINFO ItemInfo;
2175 UINT i;
2176 HFONT FontOld = NULL;
2177 BOOL flat_menu = FALSE;
2178
2179 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
2180
2181 if (NULL == Menu)
2182 {
2183 Menu = GetMenu(Wnd);
2184 }
2185
2186 if (NULL == Font)
2187 {
2188 Font = hMenuFont;
2189 }
2190
2191 if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
2192 {
2193 return GetSystemMetrics(SM_CYMENU);
2194 }
2195
2196 TRACE("(%x, %x, %p, %x, %x)\n", Wnd, DC, Rect, Menu, Font);
2197
2198 FontOld = SelectObject(DC, Font);
2199
2200 if (0 == MenuInfo.Height)
2201 {
2202 MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
2203 }
2204
2205 Rect->bottom = Rect->top + MenuInfo.Height;
2206
2207 FillRect(DC, Rect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
2208
2209 SelectObject(DC, GetStockObject(DC_PEN));
2210 SetDCPenColor(DC, GetSysColor(COLOR_3DFACE));
2211 MoveToEx(DC, Rect->left, Rect->bottom - 1, NULL);
2212 LineTo(DC, Rect->right, Rect->bottom - 1);
2213
2214 if (0 == MenuInfo.MenuItemCount)
2215 {
2216 SelectObject(DC, FontOld);
2217 return GetSystemMetrics(SM_CYMENU);
2218 }
2219
2220 MenuInitRosMenuItemInfo(&ItemInfo);
2221 for (i = 0; i < MenuInfo.MenuItemCount; i++)
2222 {
2223 if (MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
2224 {
2225 MenuDrawMenuItem(Wnd, &MenuInfo, Wnd, DC, &ItemInfo,
2226 MenuInfo.Height, TRUE, ODA_DRAWENTIRE);
2227 }
2228 }
2229 MenuCleanupRosMenuItemInfo(&ItemInfo);
2230
2231 SelectObject(DC, FontOld);
2232
2233 return MenuInfo.Height;
2234 }
2235
2236 /***********************************************************************
2237 * MenuShowSubPopup
2238 *
2239 * Display the sub-menu of the selected item of this menu.
2240 * Return the handle of the submenu, or menu if no submenu to display.
2241 */
2242 static HMENU FASTCALL
2243 MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags)
2244 {
2245 extern void FASTCALL NcGetSysPopupPos(HWND Wnd, RECT *Rect);
2246 RECT Rect;
2247 ROSMENUITEMINFO ItemInfo;
2248 ROSMENUINFO SubMenuInfo;
2249 HDC Dc;
2250 HMENU Ret;
2251
2252 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst);
2253
2254 if (NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2255 {
2256 return MenuInfo->Self;
2257 }
2258
2259 MenuInitRosMenuItemInfo(&ItemInfo);
2260 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2261 {
2262 MenuCleanupRosMenuItemInfo(&ItemInfo);
2263 return MenuInfo->Self;
2264 }
2265 if (0 == (ItemInfo.fType & MF_POPUP) || 0 != (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)))
2266 {
2267 MenuCleanupRosMenuItemInfo(&ItemInfo);
2268 return MenuInfo->Self;
2269 }
2270
2271 /* message must be sent before using item,
2272 because nearly everything may be changed by the application ! */
2273
2274 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2275 if (0 == (Flags & TPM_NONOTIFY))
2276 {
2277 SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu,
2278 MAKELONG(MenuInfo->FocusedItem, IS_SYSTEM_MENU(MenuInfo)));
2279 }
2280
2281 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2282 {
2283 MenuCleanupRosMenuItemInfo(&ItemInfo);
2284 return MenuInfo->Self;
2285 }
2286 Rect = ItemInfo.Rect;
2287
2288 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2289 if (0 == (ItemInfo.fState & MF_HILITE))
2290 {
2291 if (0 != (MenuInfo->Flags & MF_POPUP))
2292 {
2293 Dc = GetDC(MenuInfo->Wnd);
2294 }
2295 else
2296 {
2297 Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
2298 }
2299
2300 SelectObject(Dc, hMenuFont);
2301 ItemInfo.fMask |= MIIM_STATE;
2302 ItemInfo.fState |= MF_HILITE;
2303 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2304 MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->Height,
2305 ! (MenuInfo->Flags & MF_POPUP), ODA_DRAWENTIRE);
2306 ReleaseDC(MenuInfo->Wnd, Dc);
2307 }
2308
2309 if (0 == ItemInfo.Rect.top && 0 == ItemInfo.Rect.left
2310 && 0 == ItemInfo.Rect.bottom && 0 == ItemInfo.Rect.right)
2311 {
2312 ItemInfo.Rect = Rect;
2313 }
2314
2315 ItemInfo.fMask |= MIIM_STATE;
2316 ItemInfo.fState |= MF_MOUSESELECT;
2317 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2318
2319 if (IS_SYSTEM_MENU(MenuInfo))
2320 {
2321 MenuInitSysMenuPopup(ItemInfo.hSubMenu, GetWindowLongPtrW(MenuInfo->Wnd, GWL_STYLE),
2322 GetClassLongPtrW(MenuInfo->Wnd, GCL_STYLE), HTSYSMENU);
2323
2324 NcGetSysPopupPos(MenuInfo->Wnd, &Rect);
2325 Rect.top = Rect.bottom;
2326 Rect.right = GetSystemMetrics(SM_CXSIZE);
2327 Rect.bottom = GetSystemMetrics(SM_CYSIZE);
2328 }
2329 else
2330 {
2331 GetWindowRect(MenuInfo->Wnd, &Rect);
2332 if (0 != (MenuInfo->Flags & MF_POPUP))
2333 {
2334 Rect.left += ItemInfo.Rect.right - GetSystemMetrics(SM_CXBORDER);
2335 Rect.top += ItemInfo.Rect.top - 3;
2336 Rect.right = ItemInfo.Rect.left - ItemInfo.Rect.right + GetSystemMetrics(SM_CXBORDER);
2337 Rect.bottom = ItemInfo.Rect.top - ItemInfo.Rect.bottom - 3 - 2
2338 - GetSystemMetrics(SM_CYBORDER);
2339 }
2340 else
2341 {
2342 Rect.left += ItemInfo.Rect.left;
2343 Rect.top += ItemInfo.Rect.bottom;
2344 Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left;
2345 Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top;
2346 }
2347 }
2348
2349 MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->FocusedItem, Flags,
2350 Rect.left, Rect.top, Rect.right, Rect.bottom );
2351 if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2352 {
2353 MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT);
2354 }
2355
2356 Ret = ItemInfo.hSubMenu;
2357 MenuCleanupRosMenuItemInfo(&ItemInfo);
2358
2359 return Ret;
2360 }
2361
2362 /**********************************************************************
2363 * MENU_EndMenu
2364 *
2365 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2366 *
2367 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2368 */
2369 void MENU_EndMenu( HWND hwnd )
2370 {
2371 ROSMENUINFO MenuInfo;
2372 BOOL Ret = FALSE;
2373 if (top_popup_hmenu)
2374 Ret = MenuGetRosMenuInfo(&MenuInfo, top_popup_hmenu);
2375 if (Ret && hwnd == MenuInfo.WndOwner) EndMenu();
2376 }
2377
2378 /***********************************************************************
2379 * MenuHideSubPopups
2380 *
2381 * Hide the sub-popup menus of this menu.
2382 */
2383 static void FASTCALL
2384 MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo,
2385 BOOL SendMenuSelect, UINT wFlags)
2386 {
2387 ROSMENUINFO SubMenuInfo;
2388 ROSMENUITEMINFO ItemInfo;
2389
2390 TRACE("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect);
2391
2392 if (NULL != MenuInfo && NULL != top_popup && NO_SELECTED_ITEM != MenuInfo->FocusedItem)
2393 {
2394 MenuInitRosMenuItemInfo(&ItemInfo);
2395 ItemInfo.fMask |= MIIM_FTYPE | MIIM_STATE;
2396 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo)
2397 || 0 == (ItemInfo.fType & MF_POPUP)
2398 || 0 == (ItemInfo.fState & MF_MOUSESELECT))
2399 {
2400 MenuCleanupRosMenuItemInfo(&ItemInfo);
2401 return;
2402 }
2403 ItemInfo.fState &= ~MF_MOUSESELECT;
2404 ItemInfo.fMask |= MIIM_STATE;
2405 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2406 if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2407 {
2408 MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE, wFlags);
2409 MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL);
2410 DestroyWindow(SubMenuInfo.Wnd);
2411 SubMenuInfo.Wnd = NULL;
2412 MenuSetRosMenuInfo(&SubMenuInfo);
2413
2414 if (!(wFlags & TPM_NONOTIFY))
2415 SendMessageW( WndOwner, WM_UNINITMENUPOPUP, (WPARAM)ItemInfo.hSubMenu,
2416 MAKELPARAM(0, IS_SYSTEM_MENU(&SubMenuInfo)) );
2417 }
2418 }
2419 }
2420
2421 /***********************************************************************
2422 * MenuSwitchTracking
2423 *
2424 * Helper function for menu navigation routines.
2425 */
2426 static void FASTCALL
2427 MenuSwitchTracking(MTRACKER* Mt, PROSMENUINFO PtMenuInfo, UINT Index, UINT wFlags)
2428 {
2429 ROSMENUINFO TopMenuInfo;
2430
2431 TRACE("%x menu=%x 0x%04x\n", Mt, PtMenuInfo->Self, Index);
2432
2433 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu) &&
2434 Mt->TopMenu != PtMenuInfo->Self &&
2435 0 == ((PtMenuInfo->Flags | TopMenuInfo.Flags) & MF_POPUP))
2436 {
2437 /* both are top level menus (system and menu-bar) */
2438 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
2439 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL);
2440 Mt->TopMenu = PtMenuInfo->Self;
2441 }
2442 else
2443 {
2444 MenuHideSubPopups(Mt->OwnerWnd, PtMenuInfo, FALSE, wFlags);
2445 }
2446
2447 MenuSelectItem(Mt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL);
2448 }
2449
2450 /***********************************************************************
2451 * MenuExecFocusedItem
2452 *
2453 * Execute a menu item (for instance when user pressed Enter).
2454 * Return the wID of the executed item. Otherwise, -1 indicating
2455 * that no menu item was executed, -2 if a popup is shown;
2456 * Have to receive the flags for the TrackPopupMenu options to avoid
2457 * sending unwanted message.
2458 *
2459 */
2460 static INT FASTCALL
2461 MenuExecFocusedItem(MTRACKER *Mt, PROSMENUINFO MenuInfo, UINT Flags)
2462 {
2463 ROSMENUITEMINFO ItemInfo;
2464 UINT wID;
2465
2466 TRACE("%p menu=%p\n", Mt, MenuInfo);
2467
2468 if (0 == MenuInfo->MenuItemCount || NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2469 {
2470 return -1;
2471 }
2472
2473 MenuInitRosMenuItemInfo(&ItemInfo);
2474 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2475 {
2476 MenuCleanupRosMenuItemInfo(&ItemInfo);
2477 return -1;
2478 }
2479
2480 TRACE("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu);
2481
2482 if (0 == (ItemInfo.fType & MF_POPUP))
2483 {
2484 if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))
2485 && 0 == (ItemInfo.fType & MF_SEPARATOR))
2486 {
2487 /* If TPM_RETURNCMD is set you return the id, but
2488 do not send a message to the owner */
2489 if (0 == (Flags & TPM_RETURNCMD))
2490 {
2491 if (0 != (MenuInfo->Flags & MF_SYSMENU))
2492 {
2493 PostMessageW(Mt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID,
2494 MAKELPARAM((SHORT) Mt->Pt.x, (SHORT) Mt->Pt.y));
2495 }
2496 else
2497 {
2498 if (MenuInfo->dwStyle & MNS_NOTIFYBYPOS)
2499 PostMessageW(Mt->OwnerWnd, WM_MENUCOMMAND,
2500 MenuInfo->FocusedItem,
2501 (LPARAM)MenuInfo->Self);
2502 else
2503 PostMessageW(Mt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0);
2504 }
2505 }
2506 wID = ItemInfo.wID;
2507 MenuCleanupRosMenuItemInfo(&ItemInfo);
2508 return wID;
2509 }
2510 }
2511 else
2512 {
2513 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, MenuInfo, TRUE, Flags);
2514 return -2;
2515 }
2516
2517 return -1;
2518 }
2519
2520 /***********************************************************************
2521 * MenuButtonDown
2522 *
2523 * Return TRUE if we can go on with menu tracking.
2524 */
2525 static BOOL FASTCALL
2526 MenuButtonDown(MTRACKER* Mt, HMENU PtMenu, UINT Flags)
2527 {
2528 int Index;
2529 ROSMENUINFO MenuInfo;
2530 ROSMENUITEMINFO Item;
2531
2532 TRACE("%x PtMenu=%p\n", Mt, PtMenu);
2533
2534 if (NULL != PtMenu)
2535 {
2536 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2537 {
2538 return FALSE;
2539 }
2540 if (IS_SYSTEM_MENU(&MenuInfo))
2541 {
2542 Index = 0;
2543 }
2544 else
2545 {
2546 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2547 }
2548 MenuInitRosMenuItemInfo(&Item);
2549 if (NO_SELECTED_ITEM == Index || ! MenuGetRosMenuItemInfo(PtMenu, Index, &Item))
2550 {
2551 MenuCleanupRosMenuItemInfo(&Item);
2552 return FALSE;
2553 }
2554
2555 if (!(Item.fType & MF_SEPARATOR) &&
2556 !(Item.fState & (MFS_DISABLED | MFS_GRAYED)) )
2557 {
2558 if (MenuInfo.FocusedItem != Index)
2559 {
2560 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
2561 }
2562
2563 /* If the popup menu is not already "popped" */
2564 if (0 == (Item.fState & MF_MOUSESELECT))
2565 {
2566 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2567 }
2568 }
2569
2570 MenuCleanupRosMenuItemInfo(&Item);
2571
2572 return TRUE;
2573 }
2574
2575 /* else the click was on the menu bar, finish the tracking */
2576
2577 return FALSE;
2578 }
2579
2580 /***********************************************************************
2581 * MenuButtonUp
2582 *
2583 * Return the value of MenuExecFocusedItem if
2584 * the selected item was not a popup. Else open the popup.
2585 * A -1 return value indicates that we go on with menu tracking.
2586 *
2587 */
2588 static INT FASTCALL
2589 MenuButtonUp(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2590 {
2591 UINT Id;
2592 ROSMENUINFO MenuInfo;
2593 ROSMENUITEMINFO ItemInfo;
2594
2595 TRACE("%p hmenu=%x\n", Mt, PtMenu);
2596
2597 if (NULL != PtMenu)
2598 {
2599 Id = 0;
2600 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2601 {
2602 return -1;
2603 }
2604
2605 if (! IS_SYSTEM_MENU(&MenuInfo))
2606 {
2607 Id = NtUserMenuItemFromPoint(Mt->OwnerWnd, MenuInfo.Self, Mt->Pt.x, Mt->Pt.y);
2608 }
2609 MenuInitRosMenuItemInfo(&ItemInfo);
2610 if (0 <= Id && MenuGetRosMenuItemInfo(MenuInfo.Self, Id, &ItemInfo) &&
2611 MenuInfo.FocusedItem == Id)
2612 {
2613 if (0 == (ItemInfo.fType & MF_POPUP))
2614 {
2615 INT ExecutedMenuId = MenuExecFocusedItem(Mt, &MenuInfo, Flags);
2616 MenuCleanupRosMenuItemInfo(&ItemInfo);
2617 return (ExecutedMenuId < 0) ? -1 : ExecutedMenuId;
2618 }
2619 MenuCleanupRosMenuItemInfo(&ItemInfo);
2620
2621 /* If we are dealing with the top-level menu */
2622 /* and this is a click on an already "popped" item: */
2623 /* Stop the menu tracking and close the opened submenus */
2624 if (Mt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide)
2625 {
2626 MenuCleanupRosMenuItemInfo(&ItemInfo);
2627 return 0;
2628 }
2629 }
2630 MenuCleanupRosMenuItemInfo(&ItemInfo);
2631 MenuInfo.TimeToHide = TRUE;
2632 MenuSetRosMenuInfo(&MenuInfo);
2633 }
2634
2635 return -1;
2636 }
2637
2638 /***********************************************************************
2639 * MenuPtMenu
2640 *
2641 * Walks menu chain trying to find a menu pt maps to.
2642 */
2643 static HMENU FASTCALL
2644 MenuPtMenu(HMENU Menu, POINT Pt)
2645 {
2646 extern LRESULT DefWndNCHitTest(HWND hWnd, POINT Point);
2647 ROSMENUINFO MenuInfo;
2648 ROSMENUITEMINFO ItemInfo;
2649 HMENU Ret = NULL;
2650 INT Ht;
2651
2652 if (! MenuGetRosMenuInfo(&MenuInfo, Menu))
2653 {
2654 return NULL;
2655 }
2656
2657 /* try subpopup first (if any) */
2658 if (NO_SELECTED_ITEM != MenuInfo.FocusedItem)
2659 {
2660 MenuInitRosMenuItemInfo(&ItemInfo);
2661 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo) &&
2662 0 != (ItemInfo.fType & MF_POPUP) &&
2663 0 != (ItemInfo.fState & MF_MOUSESELECT))
2664 {
2665 Ret = MenuPtMenu(ItemInfo.hSubMenu, Pt);
2666 if (NULL != Ret)
2667 {
2668 MenuCleanupRosMenuItemInfo(&ItemInfo);
2669 return Ret;
2670 }
2671 }
2672 MenuCleanupRosMenuItemInfo(&ItemInfo);
2673 }
2674
2675 /* check the current window (avoiding WM_HITTEST) */
2676 Ht = DefWndNCHitTest(MenuInfo.Wnd, Pt);
2677 if (0 != (MenuInfo.Flags & MF_POPUP))
2678 {
2679 if (HTNOWHERE != Ht && HTERROR != Ht)
2680 {
2681 Ret = Menu;
2682 }
2683 }
2684 else if (HTSYSMENU == Ht)
2685 {
2686 Ret = NtUserGetSystemMenu(MenuInfo.Wnd, FALSE);
2687 }
2688 else if (HTMENU == Ht)
2689 {
2690 Ret = GetMenu(MenuInfo.Wnd);
2691 }
2692
2693 return Ret;
2694 }
2695
2696 /***********************************************************************
2697 * MenuMouseMove
2698 *
2699 * Return TRUE if we can go on with menu tracking.
2700 */
2701 static BOOL FASTCALL
2702 MenuMouseMove(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2703 {
2704 UINT Index;
2705 ROSMENUINFO MenuInfo;
2706 ROSMENUITEMINFO ItemInfo;
2707
2708 if (NULL != PtMenu)
2709 {
2710 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2711 {
2712 return TRUE;
2713 }
2714 if (IS_SYSTEM_MENU(&MenuInfo))
2715 {
2716 Index = 0;
2717 }
2718 else
2719 {
2720 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2721 }
2722 }
2723 else
2724 {
2725 Index = NO_SELECTED_ITEM;
2726 }
2727
2728 if (NO_SELECTED_ITEM == Index)
2729 {
2730 if (Mt->CurrentMenu == MenuInfo.Self ||
2731 MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
2732 {
2733 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
2734 TRUE, Mt->TopMenu);
2735 }
2736 }
2737 else if (MenuInfo.FocusedItem != Index)
2738 {
2739 MenuInitRosMenuItemInfo(&ItemInfo);
2740 if (MenuGetRosMenuItemInfo(MenuInfo.Self, Index, &ItemInfo) &&
2741 !(ItemInfo.fType & MF_SEPARATOR))
2742 {
2743 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
2744 if (!(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED)))
2745 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2746 }
2747 MenuCleanupRosMenuItemInfo(&ItemInfo);
2748 }
2749
2750 return TRUE;
2751 }
2752
2753 /***********************************************************************
2754 * MenuGetSubPopup
2755 *
2756 * Return the handle of the selected sub-popup menu (if any).
2757 */
2758 static HMENU FASTCALL
2759 MenuGetSubPopup(HMENU Menu)
2760 {
2761 ROSMENUINFO MenuInfo;
2762 ROSMENUITEMINFO ItemInfo;
2763
2764 if (! MenuGetRosMenuInfo(&MenuInfo, Menu)
2765 || NO_SELECTED_ITEM == MenuInfo.FocusedItem)
2766 {
2767 return NULL;
2768 }
2769
2770 MenuInitRosMenuItemInfo(&ItemInfo);
2771 if (! MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
2772 {
2773 MenuCleanupRosMenuItemInfo(&ItemInfo);
2774 return NULL;
2775 }
2776 if (0 != (ItemInfo.fType & MF_POPUP) && 0 != (ItemInfo.fState & MF_MOUSESELECT))
2777 {
2778 MenuCleanupRosMenuItemInfo(&ItemInfo);
2779 return ItemInfo.hSubMenu;
2780 }
2781
2782 MenuCleanupRosMenuItemInfo(&ItemInfo);
2783 return NULL;
2784 }
2785
2786 /***********************************************************************
2787 * MenuDoNextMenu
2788 *
2789 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2790 */
2791 static LRESULT FASTCALL
2792 MenuDoNextMenu(MTRACKER* Mt, UINT Vk, UINT wFlags)
2793 {
2794 ROSMENUINFO TopMenuInfo;
2795 ROSMENUINFO MenuInfo;
2796
2797 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2798 {
2799 return (LRESULT) FALSE;
2800 }
2801
2802 if ((VK_LEFT == Vk && 0 == TopMenuInfo.FocusedItem)
2803 || (VK_RIGHT == Vk && TopMenuInfo.FocusedItem == TopMenuInfo.MenuItemCount - 1))
2804 {
2805 MDINEXTMENU NextMenu;
2806 HMENU NewMenu;
2807 HWND NewWnd;
2808 UINT Id = 0;
2809
2810 NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(Mt->TopMenu, 0) : Mt->TopMenu;
2811 NextMenu.hmenuNext = NULL;
2812 NextMenu.hwndNext = NULL;
2813 SendMessageW(Mt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
2814
2815 TRACE("%p [%p] -> %p [%p]\n",
2816 Mt->CurrentMenu, Mt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
2817
2818 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
2819 {
2820 DWORD Style = GetWindowLongPtrW(Mt->OwnerWnd, GWL_STYLE);
2821 NewWnd = Mt->OwnerWnd;
2822 if (IS_SYSTEM_MENU(&TopMenuInfo))
2823 {
2824 /* switch to the menu bar */
2825
2826 if (0 != (Style & WS_CHILD)
2827 || NULL == (NewMenu = GetMenu(NewWnd)))
2828 {
2829 return FALSE;
2830 }
2831
2832 if (VK_LEFT == Vk)
2833 {
2834 if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu))
2835 {
2836 return FALSE;
2837 }
2838 Id = MenuInfo.MenuItemCount - 1;
2839 }
2840 }
2841 else if (0 != (Style & WS_SYSMENU))
2842 {
2843 /* switch to the system menu */
2844 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2845 }
2846 else
2847 {
2848 return FALSE;
2849 }
2850 }
2851 else /* application returned a new menu to switch to */
2852 {
2853 NewMenu = NextMenu.hmenuNext;
2854 NewWnd = NextMenu.hwndNext;
2855
2856 if (IsMenu(NewMenu) && IsWindow(NewWnd))
2857 {
2858 DWORD Style = GetWindowLongPtrW(NewWnd, GWL_STYLE);
2859
2860 if (0 != (Style & WS_SYSMENU)
2861 && GetSystemMenu(NewWnd, FALSE) == NewMenu)
2862 {
2863 /* get the real system menu */
2864 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2865 }
2866 else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu)
2867 {
2868 /* FIXME: Not sure what to do here;
2869 * perhaps try to track NewMenu as a popup? */
2870
2871 WARN(" -- got confused.\n");
2872 return FALSE;
2873 }
2874 }
2875 else
2876 {
2877 return FALSE;
2878 }
2879 }
2880
2881 if (NewMenu != Mt->TopMenu)
2882 {
2883 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM,
2884 FALSE, 0 );
2885 if (Mt->CurrentMenu != Mt->TopMenu)
2886 {
2887 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
2888 }
2889 }
2890
2891 if (NewWnd != Mt->OwnerWnd)
2892 {
2893 Mt->OwnerWnd = NewWnd;
2894 SetCapture(Mt->OwnerWnd);
2895 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, Mt->OwnerWnd);
2896 }
2897
2898 Mt->TopMenu = Mt->CurrentMenu = NewMenu; /* all subpopups are hidden */
2899 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2900 {
2901 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0);
2902 }
2903
2904 return TRUE;
2905 }
2906
2907 return FALSE;
2908 }
2909
2910 /***********************************************************************
2911 * MenuSuspendPopup
2912 *
2913 * The idea is not to show the popup if the next input message is
2914 * going to hide it anyway.
2915 */
2916 static BOOL FASTCALL
2917 MenuSuspendPopup(MTRACKER* Mt, UINT Message)
2918 {
2919 MSG Msg;
2920
2921 Msg.hwnd = Mt->OwnerWnd;
2922
2923 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2924 Mt->TrackFlags |= TF_SKIPREMOVE;
2925
2926 switch (Message)
2927 {
2928 case WM_KEYDOWN:
2929 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2930 if (WM_KEYUP == Msg.message || WM_PAINT == Msg.message)
2931 {
2932 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2933 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2934 if (WM_KEYDOWN == Msg.message
2935 && (VK_LEFT == Msg.wParam || VK_RIGHT == Msg.wParam))
2936 {
2937 Mt->TrackFlags |= TF_SUSPENDPOPUP;
2938 return TRUE;
2939 }
2940 }
2941 break;
2942 }
2943
2944 /* failures go through this */
2945 Mt->TrackFlags &= ~TF_SUSPENDPOPUP;
2946
2947 return FALSE;
2948 }
2949
2950 /***********************************************************************
2951 * MenuKeyEscape
2952 *
2953 * Handle a VK_ESCAPE key event in a menu.
2954 */
2955 static BOOL FASTCALL
2956 MenuKeyEscape(MTRACKER *Mt, UINT Flags)
2957 {
2958 BOOL EndMenu = TRUE;
2959 ROSMENUINFO MenuInfo;
2960 HMENU MenuTmp, MenuPrev;
2961
2962 if (Mt->CurrentMenu != Mt->TopMenu)
2963 {
2964 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)
2965 && 0 != (MenuInfo.Flags & MF_POPUP))
2966 {
2967 MenuPrev = MenuTmp = Mt->TopMenu;
2968
2969 /* close topmost popup */
2970 while (MenuTmp != Mt->CurrentMenu)
2971 {
2972 MenuPrev = MenuTmp;
2973 MenuTmp = MenuGetSubPopup(MenuPrev);
2974 }
2975
2976 if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev))
2977 {
2978 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, TRUE, Flags);
2979 }
2980 Mt->CurrentMenu = MenuPrev;
2981 EndMenu = FALSE;
2982 }
2983 }
2984
2985 return EndMenu;
2986 }
2987
2988 /***********************************************************************
2989 * MenuKeyLeft
2990 *
2991 * Handle a VK_LEFT key event in a menu.
2992 */
2993 static void FASTCALL
2994 MenuKeyLeft(MTRACKER* Mt, UINT Flags)
2995 {
2996 ROSMENUINFO MenuInfo;
2997 ROSMENUINFO TopMenuInfo;
2998 ROSMENUINFO PrevMenuInfo;
2999 HMENU MenuTmp, MenuPrev;
3000 UINT PrevCol;
3001
3002 MenuPrev = MenuTmp = Mt->TopMenu;
3003
3004 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3005 {
3006 return;
3007 }
3008
3009 /* Try to move 1 column left (if possible) */
3010 if (NO_SELECTED_ITEM != (PrevCol = MenuGetStartOfPrevColumn(&MenuInfo)))
3011 {
3012 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3013 {
3014 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0);
3015 }
3016 return;
3017 }
3018
3019 /* close topmost popup */
3020 while (MenuTmp != Mt->CurrentMenu)
3021 {
3022 MenuPrev = MenuTmp;
3023 MenuTmp = MenuGetSubPopup(MenuPrev);
3024 }
3025
3026 if (! MenuGetRosMenuInfo(&PrevMenuInfo, MenuPrev))
3027 {
3028 return;
3029 }
3030 MenuHideSubPopups(Mt->OwnerWnd, &PrevMenuInfo, TRUE, Flags);
3031 Mt->CurrentMenu = MenuPrev;
3032
3033 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3034 {
3035 return;
3036 }
3037 if ((MenuPrev == Mt->TopMenu) && 0 == (TopMenuInfo.Flags & MF_POPUP))
3038 {
3039 /* move menu bar selection if no more popups are left */
3040
3041 if (! MenuDoNextMenu(Mt, VK_LEFT, Flags))
3042 {
3043 MenuMoveSelection(Mt->OwnerWnd, &TopMenuInfo, ITEM_PREV);
3044 }
3045
3046 if (MenuPrev != MenuTmp || 0 != (Mt->TrackFlags & TF_SUSPENDPOPUP))
3047 {
3048 /* A sublevel menu was displayed - display the next one
3049 * unless there is another displacement coming up */
3050
3051 if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
3052 && MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3053 {
3054 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &TopMenuInfo,
3055 TRUE, Flags);
3056 }
3057 }
3058 }
3059 }
3060
3061 /***********************************************************************
3062 * MenuKeyRight
3063 *
3064 * Handle a VK_RIGHT key event in a menu.
3065 */
3066 static void FASTCALL MenuKeyRight(MTRACKER *Mt, UINT Flags)
3067 {
3068 HMENU hmenutmp;
3069 ROSMENUINFO MenuInfo;
3070 ROSMENUINFO CurrentMenuInfo;
3071 UINT NextCol;
3072
3073 TRACE("MenuKeyRight called, cur %p, top %p.\n",
3074 Mt->CurrentMenu, Mt->TopMenu);
3075
3076 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu)) return;
3077 if ((MenuInfo.Flags & MF_POPUP) || (Mt->CurrentMenu != Mt->TopMenu))
3078 {
3079 /* If already displaying a popup, try to display sub-popup */
3080
3081 hmenutmp = Mt->CurrentMenu;
3082 if (MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
3083 {
3084 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &CurrentMenuInfo, TRUE, Flags);
3085 }
3086
3087 /* if subpopup was displayed then we are done */
3088 if (hmenutmp != Mt->CurrentMenu) return;
3089 }
3090
3091 if (! MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
3092 {
3093 return;
3094 }
3095
3096 /* Check to see if there's another column */
3097 if (NO_SELECTED_ITEM != (NextCol = MenuGetStartOfNextColumn(&CurrentMenuInfo)))
3098 {
3099 TRACE("Going to %d.\n", NextCol);
3100 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3101 {
3102 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NextCol, TRUE, 0);
3103 }
3104 return;
3105 }
3106
3107 if (0 == (MenuInfo.Flags & MF_POPUP)) /* menu bar tracking */
3108 {
3109 if (Mt->CurrentMenu != Mt->TopMenu)
3110 {
3111 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
3112 hmenutmp = Mt->CurrentMenu = Mt->TopMenu;
3113 }
3114 else
3115 {
3116 hmenutmp = NULL;
3117 }
3118
3119 /* try to move to the next item */
3120 if ( !MenuDoNextMenu(Mt, VK_RIGHT, Flags))
3121 MenuMoveSelection(Mt->OwnerWnd, &MenuInfo, ITEM_NEXT);
3122
3123 if ( hmenutmp || Mt->TrackFlags & TF_SUSPENDPOPUP )
3124 {
3125 if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
3126 && MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu))
3127 {
3128 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo,
3129 TRUE, Flags);
3130 }
3131 }
3132 }
3133 }
3134
3135 /***********************************************************************
3136 * MenuTrackMenu
3137 *
3138 * Menu tracking code.
3139 */
3140 static INT FASTCALL MenuTrackMenu(HMENU hmenu, UINT wFlags, INT x, INT y,
3141 HWND hwnd, const RECT *lprect )
3142 {
3143 MSG msg;
3144 ROSMENUINFO MenuInfo;
3145 ROSMENUITEMINFO ItemInfo;
3146 BOOL fRemove;
3147 INT executedMenuId = -1;
3148 MTRACKER mt;
3149 BOOL enterIdleSent = FALSE;
3150
3151 mt.TrackFlags = 0;
3152 mt.CurrentMenu = hmenu;
3153 mt.TopMenu = hmenu;
3154 mt.OwnerWnd = hwnd;
3155 mt.Pt.x = x;
3156 mt.Pt.y = y;
3157
3158 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n",
3159 hmenu, wFlags, x, y, hwnd, lprect ? lprect->left : 0, lprect ? lprect->top : 0,
3160 lprect ? lprect->right : 0, lprect ? lprect->bottom : 0);
3161
3162 if (!IsMenu(hmenu))
3163 {
3164 WARN("Invalid menu handle %p\n", hmenu);
3165 SetLastError( ERROR_INVALID_MENU_HANDLE );
3166 return FALSE;
3167 }
3168
3169 fEndMenu = FALSE;
3170 if (! MenuGetRosMenuInfo(&MenuInfo, hmenu))
3171 {
3172 return FALSE;
3173 }
3174
3175 if (wFlags & TPM_BUTTONDOWN)
3176 {
3177 /* Get the result in order to start the tracking or not */
3178 fRemove = MenuButtonDown( &mt, hmenu, wFlags );
3179 fEndMenu = !fRemove;
3180 }
3181
3182 SetCapture(mt.OwnerWnd);
3183 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, mt.OwnerWnd);
3184
3185 ERR("MenuTrackMenu 1\n");
3186 while (! fEndMenu)
3187 {
3188 PVOID menu = ValidateHandle(mt.CurrentMenu, VALIDATE_TYPE_MENU);
3189 if (!menu) /* sometimes happens if I do a window manager close */
3190 break;
3191
3192 /* we have to keep the message in the queue until it's
3193 * clear that menu loop is not over yet. */
3194
3195 for (;;)
3196 {
3197 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3198 {
3199 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3200 /* remove the message from the queue */
3201 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3202 }
3203 else
3204 {
3205 if (!enterIdleSent)
3206 {
3207 HWND win = MenuInfo.Flags & MF_POPUP ? MenuInfo.Wnd : NULL;
3208 enterIdleSent = TRUE;
3209 SendMessageW( mt.OwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM) win);
3210 }
3211 WaitMessage();
3212 }
3213 }
3214
3215 /* check if EndMenu() tried to cancel us, by posting this message */
3216 if (msg.message == WM_CANCELMODE)
3217 {
3218 /* we are now out of the loop */
3219 fEndMenu = TRUE;
3220
3221 /* remove the message from the queue */
3222 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3223
3224 /* break out of internal loop, ala ESCAPE */
3225 break;
3226 }
3227
3228 TranslateMessage( &msg );
3229 mt.Pt = msg.pt;
3230
3231 if ( (msg.hwnd == MenuInfo.Wnd) || (msg.message!=WM_TIMER) )
3232 enterIdleSent=FALSE;
3233
3234 fRemove = FALSE;
3235 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3236 {
3237 /*
3238 * Use the mouse coordinates in lParam instead of those in the MSG
3239 * struct to properly handle synthetic messages. They are already
3240 * in screen coordinates.
3241 */
3242 mt.Pt.x = (short)LOWORD(msg.lParam);
3243 mt.Pt.y = (short)HIWORD(msg.lParam);
3244
3245 /* Find a menu for this mouse event */
3246 hmenu = MenuPtMenu(mt.TopMenu, mt.Pt);
3247
3248 switch(msg.message)
3249 {
3250 /* no WM_NC... messages in captured state */
3251
3252 case WM_RBUTTONDBLCLK:
3253 case WM_RBUTTONDOWN:
3254 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3255 /* fall through */
3256 case WM_LBUTTONDBLCLK:
3257 case WM_LBUTTONDOWN:
3258 /* If the message belongs to the menu, removes it from the queue */
3259 /* Else, end menu tracking */
3260 fRemove = MenuButtonDown(&mt, hmenu, wFlags);
3261 fEndMenu = !fRemove;
3262 break;
3263
3264 case WM_RBUTTONUP:
3265 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3266 /* fall through */
3267 case WM_LBUTTONUP:
3268 /* Check if a menu was selected by the mouse */
3269 if (hmenu)
3270 {
3271 executedMenuId = MenuButtonUp( &mt, hmenu, wFlags);
3272 TRACE("executedMenuId %d\n", executedMenuId);
3273
3274 /* End the loop if executedMenuId is an item ID */
3275 /* or if the job was done (executedMenuId = 0). */
3276 fEndMenu = fRemove = (executedMenuId != -1);
3277 }
3278 /* No menu was selected by the mouse */
3279 /* if the function was called by TrackPopupMenu, continue
3280 with the menu tracking. If not, stop it */
3281 else
3282 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3283
3284 break;
3285
3286 case WM_MOUSEMOVE:
3287 /* the selected menu item must be changed every time */
3288 /* the mouse moves. */
3289
3290 if (hmenu)
3291 fEndMenu |= !MenuMouseMove( &mt, hmenu, wFlags );
3292
3293 } /* switch(msg.message) - mouse */
3294 }
3295 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3296 {
3297 fRemove = TRUE; /* Keyboard messages are always removed */
3298 switch(msg.message)
3299 {
3300 case WM_KEYDOWN:
3301 case WM_SYSKEYDOWN:
3302 switch(msg.wParam)
3303 {
3304 case VK_MENU:
3305 case VK_F10:
3306 fEndMenu = TRUE;
3307 break;
3308
3309 case VK_HOME:
3310 case VK_END:
3311 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3312 {
3313 MenuSelectItem(mt.OwnerWnd, &MenuInfo,
3314 NO_SELECTED_ITEM, FALSE, 0 );
3315 MenuMoveSelection(mt.OwnerWnd, &MenuInfo,
3316 VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV);
3317 }
3318 break;
3319
3320 case VK_UP:
3321 case VK_DOWN: /* If on menu bar, pull-down the menu */
3322 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3323 {
3324 if (!(MenuInfo.Flags & MF_POPUP))
3325 {
3326 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
3327 mt.CurrentMenu = MenuShowSubPopup(mt.OwnerWnd, &MenuInfo, TRUE, wFlags);
3328 }
3329 else /* otherwise try to move selection */
3330 MenuMoveSelection(mt.OwnerWnd, &MenuInfo,
3331 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3332 }
3333 break;
3334
3335 case VK_LEFT:
3336 MenuKeyLeft( &mt, wFlags );
3337 break;
3338
3339 case VK_RIGHT:
3340 MenuKeyRight( &mt, wFlags );
3341 break;
3342
3343 case VK_ESCAPE:
3344 fEndMenu = MenuKeyEscape(&mt, wFlags);
3345 break;
3346
3347 case VK_F1:
3348 {
3349 HELPINFO hi;
3350 hi.cbSize = sizeof(HELPINFO);
3351 hi.iContextType = HELPINFO_MENUITEM;
3352 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3353 {
3354 if (MenuInfo.FocusedItem == NO_SELECTED_ITEM)
3355 hi.iCtrlId = 0;
3356 else
3357 {
3358 MenuInitRosMenuItemInfo(&ItemInfo);
3359 if (MenuGetRosMenuItemInfo(MenuInfo.Self,
3360 MenuInfo.FocusedItem,
3361 &ItemInfo))
3362 {
3363 hi.iCtrlId = ItemInfo.wID;
3364 }
3365 else
3366 {
3367 hi.iCtrlId = 0;
3368 }
3369 MenuCleanupRosMenuItemInfo(&ItemInfo);
3370 }
3371 }
3372 hi.hItemHandle = hmenu;
3373 hi.dwContextId = MenuInfo.dwContextHelpID;
3374 hi.MousePos = msg.pt;
3375 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3376 break;
3377 }
3378
3379 default:
3380 break;
3381 }
3382 break; /* WM_KEYDOWN */
3383
3384 case WM_CHAR:
3385 case WM_SYSCHAR:
3386 {
3387 UINT pos;
3388
3389 if (! MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu)) break;
3390 if (msg.wParam == L'\r' || msg.wParam == L' ')
3391 {
3392 executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags);
3393 fEndMenu = (executedMenuId != -2);
3394 break;
3395 }
3396
3397 /* Hack to avoid control chars. */
3398 /* We will find a better way real soon... */
3399 if (msg.wParam < 32) break;
3400
3401 pos = MenuFindItemByKey(mt.OwnerWnd, &MenuInfo,
3402 LOWORD(msg.wParam), FALSE);
3403 if (pos == (UINT)-2) fEndMenu = TRUE;
3404 else if (pos == (UINT)-1) MessageBeep(0);
3405 else
3406 {
3407 MenuSelectItem(mt.OwnerWnd, &MenuInfo, pos,
3408 TRUE, 0);
3409 executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags);
3410 fEndMenu = (executedMenuId != -2);
3411 }
3412 }
3413 break;
3414 } /* switch(msg.message) - kbd */
3415 }
3416 else
3417 {
3418 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3419 DispatchMessageW( &msg );
3420 continue;
3421 }
3422
3423 if (!fEndMenu) fRemove = TRUE;
3424
3425 /* finally remove message from the queue */
3426
3427 if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) )
3428 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3429 else mt.TrackFlags &= ~TF_SKIPREMOVE;
3430 }
3431 ERR("MenuTrackMenu 2\n");
3432
3433 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, NULL);
3434 SetCapture(NULL); /* release the capture */
3435
3436 /* If dropdown is still painted and the close box is clicked on
3437 then the menu will be destroyed as part of the DispatchMessage above.
3438 This will then invalidate the menu handle in mt.hTopMenu. We should
3439 check for this first. */
3440 if( IsMenu( mt.TopMenu ) )
3441 {
3442 if (IsWindow(mt.OwnerWnd))
3443 {
3444 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
3445 {
3446 MenuHideSubPopups(mt.OwnerWnd, &MenuInfo, FALSE, wFlags);
3447
3448 if (MenuInfo.Flags & MF_POPUP)
3449 {
3450 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0);
3451 DestroyWindow(MenuInfo.Wnd);
3452 MenuInfo.Wnd = NULL;
3453
3454 if (!(MenuInfo.Flags & TPM_NONOTIFY))
3455 SendMessageW( mt.OwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.TopMenu,
3456 MAKELPARAM(0, IS_SYSTEM_MENU(&MenuInfo)) );
3457
3458 }
3459 MenuSelectItem( mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, 0 );
3460 }
3461
3462 SendMessageW( mt.OwnerWnd, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0 );
3463 }
3464
3465 /* Reset the variable for hiding menu */
3466 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
3467 {
3468 MenuInfo.TimeToHide = FALSE;
3469 MenuSetRosMenuInfo(&MenuInfo);
3470 }
3471 }
3472
3473 /* The return value is only used by TrackPopupMenu */
3474 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3475 if (executedMenuId == -1) executedMenuId = 0;
3476 return executedMenuId;
3477 }
3478
3479 /***********************************************************************
3480 * MenuInitTracking
3481 */
3482 static BOOL FASTCALL MenuInitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3483 {
3484 ROSMENUINFO MenuInfo;
3485
3486 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3487
3488 HideCaret(0);
3489
3490 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3491 if (!(wFlags & TPM_NONOTIFY))
3492 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3493
3494 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3495
3496 MenuGetRosMenuInfo(&MenuInfo, hMenu);
3497
3498 if (!(wFlags & TPM_NONOTIFY))
3499 {
3500 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3501 /* If an app changed/recreated menu bar entries in WM_INITMENU
3502 * menu sizes will be recalculated once the menu created/shown.
3503 */
3504
3505 if (!MenuInfo.Height)
3506 {
3507 /* app changed/recreated menu bar entries in WM_INITMENU
3508 Recalculate menu sizes else clicks will not work */
3509 SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3510 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3511
3512 }
3513 }
3514
3515 /* This makes the menus of applications built with Delphi work.
3516 * It also enables menus to be displayed in more than one window,
3517 * but there are some bugs left that need to be fixed in this case.
3518 */
3519 if(MenuInfo.Self == hMenu)
3520 {
3521 MenuInfo.Wnd = hWnd;
3522 MenuSetRosMenuInfo(&MenuInfo);
3523 }
3524
3525 IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART,
3526 hWnd,
3527 MenuInfo.Flags & MF_SYSMENU ? OBJID_SYSMENU : OBJID_MENU,
3528 CHILDID_SELF, 0);
3529 return TRUE;
3530 }
3531 /***********************************************************************
3532 * MenuExitTracking
3533 */
3534 static BOOL FASTCALL MenuExitTracking(HWND hWnd, BOOL bPopup)
3535 {
3536 TRACE("hwnd=%p\n", hWnd);
3537
3538 IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, hWnd, OBJID_WINDOW, CHILDID_SELF, 0);
3539 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3540 ShowCaret(0);
3541 top_popup = 0;
3542 top_popup_hmenu = NULL;
3543 return TRUE;
3544 }
3545
3546 /***********************************************************************
3547 * MenuTrackMouseMenuBar
3548 *
3549 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3550 */
3551 VOID MenuTrackMouseMenuBar( HWND hWnd, ULONG ht, POINT pt)
3552 {
3553 HMENU hMenu = (ht == HTSYSMENU) ? NtUserGetSystemMenu( hWnd, FALSE) : GetMenu(hWnd);
3554 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3555
3556 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
3557
3558 if (IsMenu(hMenu))
3559 {
3560 /* map point to parent client coordinates */
3561 HWND Parent = GetAncestor(hWnd, GA_PARENT );
3562 if (Parent != GetDesktopWindow())
3563 {
3564 ScreenToClient(Parent, &pt);
3565 }
3566
3567 MenuInitTracking(hWnd, hMenu, FALSE, wFlags);
3568 MenuTrackMenu(hMenu, wFlags, pt.x, pt.y, hWnd, NULL);
3569 MenuExitTracking(hWnd, FALSE);
3570 }
3571 }
3572
3573
3574 /***********************************************************************
3575 * MenuTrackKbdMenuBar
3576 *
3577 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3578 */
3579 VOID MenuTrackKbdMenuBar(HWND hwnd, UINT wParam, WCHAR wChar)
3580 {
3581 UINT uItem = NO_SELECTED_ITEM;
3582 HMENU hTrackMenu;
3583 ROSMENUINFO MenuInfo;
3584 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3585
3586 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3587
3588 /* find window that has a menu */
3589
3590 while (!((GetWindowLongPtrW( hwnd, GWL_STYLE ) &
3591 (WS_CHILD | WS_POPUP)) != WS_CHILD))
3592 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3593
3594 /* check if we have to track a system menu */
3595
3596 hTrackMenu = GetMenu( hwnd );
3597 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3598 {
3599 if (!(GetWindowLongPtrW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3600 hTrackMenu = NtUserGetSystemMenu(hwnd, FALSE);
3601 uItem = 0;
3602 wParam |= HTSYSMENU; /* prevent item lookup */
3603 }
3604
3605 if (!IsMenu( hTrackMenu )) return;
3606
3607 MenuInitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3608
3609 if (! MenuGetRosMenuInfo(&MenuInfo, hTrackMenu))
3610 {
3611 goto track_menu;
3612 }
3613
3614 if( wChar && wChar != ' ' )
3615 {
3616 uItem = MenuFindItemByKey( hwnd, &MenuInfo, wChar, (wParam & HTSYSMENU) );
3617 if ( uItem >= (UINT)(-2) )
3618 {
3619 if( uItem == (UINT)(-1) ) MessageBeep(0);
3620 /* schedule end of menu tracking */
3621 wFlags |= TF_ENDMENU;
3622 goto track_menu;
3623 }
3624 }
3625
3626 MenuSelectItem( hwnd, &MenuInfo, uItem, TRUE, 0 );
3627
3628 if (wParam & HTSYSMENU)
3629 {
3630 /* prevent sysmenu activation for managed windows on Alt down/up */
3631 // if (GetPropA( hwnd, "__wine_x11_managed" ))
3632 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3633 }
3634 else
3635 {
3636 if( uItem == NO_SELECTED_ITEM )
3637 MenuMoveSelection( hwnd, &MenuInfo, ITEM_NEXT );
3638 else
3639 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3640 }
3641
3642 track_menu:
3643 MenuTrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3644 MenuExitTracking( hwnd, FALSE );
3645
3646 }
3647
3648 /**********************************************************************
3649 * TrackPopupMenuEx (USER32.@)
3650 */
3651 BOOL WINAPI TrackPopupMenuEx( HMENU Menu, UINT Flags, int x, int y,
3652 HWND Wnd, LPTPMPARAMS Tpm)
3653 {
3654 BOOL ret = FALSE;
3655
3656 if (!IsMenu(Menu))
3657 {
3658 SetLastError( ERROR_INVALID_MENU_HANDLE );
3659 return FALSE;
3660 }
3661
3662 MenuInitTracking(Wnd, Menu, TRUE, Flags);
3663
3664 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3665 if (!(Flags & TPM_NONOTIFY))
3666 SendMessageW(Wnd, WM_INITMENUPOPUP, (WPARAM) Menu, 0);
3667
3668 if (MenuShowPopup(Wnd, Menu, 0, Flags, x, y, 0, 0 ))
3669 ret = MenuTrackMenu(Menu, Flags | TPM_POPUPMENU, 0, 0, Wnd,
3670 Tpm ? &Tpm->rcExclude : NULL);
3671 MenuExitTracking(Wnd, TRUE);
3672 return ret;
3673 }
3674
3675 /**********************************************************************
3676 * TrackPopupMenu (USER32.@)
3677 */
3678 BOOL WINAPI TrackPopupMenu( HMENU Menu, UINT Flags, int x, int y,
3679 int Reserved, HWND Wnd, CONST RECT *Rect)
3680 {
3681 return TrackPopupMenuEx( Menu, Flags, x, y, Wnd, NULL);
3682 }
3683
3684 /*
3685 * From MSDN:
3686 * The MFT_BITMAP, MFT_SEPARATOR, and MFT_STRING values cannot be combined
3687 * with one another. Also MFT_OWNERDRAW. Set fMask to MIIM_TYPE to use fType.
3688 *
3689 * Windows 2K/XP: fType is used only if fMask has a value of MIIM_FTYPE.
3690 *
3691 * MIIM_TYPE: Retrieves or sets the fType and dwTypeData members. Windows
3692 * 2K/XP: MIIM_TYPE is replaced by MIIM_BITMAP, MIIM_FTYPE, and MIIM_STRING.
3693 * MFT_STRING is replaced by MIIM_STRING.
3694 * (So, I guess we should use MIIM_STRING only for strings?)
3695 *
3696 * MIIM_FTYPE: Windows 2K/Windows XP: Retrieves or sets the fType member.
3697 *
3698 * Based on wine, SetMenuItemInfo_common:
3699 * 1) set MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP any one with MIIM_TYPE,
3700 * it will result in a error.
3701 * 2) set menu mask to MIIM_FTYPE and MFT_BITMAP ftype it will result in a error.
3702 * These conditions are addressed in Win32k IntSetMenuItemInfo.
3703 *
3704 */
3705 static
3706 BOOL
3707 FASTCALL
3708 MenuSetItemData(
3709 LPMENUITEMINFOW mii,
3710 UINT Flags,
3711 UINT_PTR IDNewItem,
3712 LPCWSTR NewItem,
3713 BOOL Unicode)
3714 {
3715 /*
3716 * Let us assume MIIM_FTYPE is set and building a new menu item structure.
3717 */
3718 if(Flags & MF_BITMAP)
3719 {
3720 mii->fMask |= MIIM_BITMAP; /* Use the new way of seting hbmpItem.*/
3721 mii->hbmpItem = (HBITMAP) NewItem;
3722
3723 if (Flags & MF_HELP)
3724 {
3725 /* increase ident */
3726 mii->fType |= MF_HELP;
3727 }
3728 }
3729 else if(Flags & MF_OWNERDRAW)
3730 {
3731 mii->fType |= MFT_OWNERDRAW;
3732 mii->fMask |= MIIM_DATA;
3733 mii->dwItemData = (DWORD_PTR) NewItem;
3734 }
3735 else if (Flags & MF_SEPARATOR)
3736 {
3737 mii->fType |= MFT_SEPARATOR;
3738 if (!(Flags & (MF_GRAYED|MF_DISABLED)))
3739 Flags |= MF_GRAYED|MF_DISABLED;
3740 }
3741 else /* Default action MF_STRING. */
3742 {
3743 /* Item beginning with a backspace is a help item */
3744 if (NewItem != NULL)
3745 {
3746 if (Unicode)
3747 {
3748 if (*NewItem == '\b')
3749 {
3750 mii->fType |= MF_HELP;
3751 NewItem++;
3752 }
3753 }
3754 else
3755 {
3756 LPCSTR NewItemA = (LPCSTR) NewItem;
3757 if (*NewItemA == '\b')
3758 {
3759 mii->fType |= MF_HELP;
3760 NewItemA++;
3761 NewItem = (LPCWSTR) NewItemA;
3762 }
3763 }
3764
3765 if (Flags & MF_HELP)
3766 mii->fType |= MF_HELP;
3767 mii->fMask |= MIIM_STRING;
3768 mii->fType |= MFT_STRING; /* Zero */
3769 mii->dwTypeData = (LPWSTR)NewItem;
3770 if (Unicode)
3771 mii->cch = (NULL == NewItem ? 0 : strlenW(NewItem));
3772 else
3773 mii->cch = (NULL == NewItem ? 0 : strlen((LPCSTR)NewItem));
3774 }
3775 else
3776 {
3777 mii->fType |= MFT_SEPARATOR;
3778 if (!(Flags & (MF_GRAYED|MF_DISABLED)))
3779 Flags |= MF_GRAYED|MF_DISABLED;
3780 }
3781 }
3782
3783 if(Flags & MF_RIGHTJUSTIFY) /* Same as MF_HELP */
3784 {
3785 mii->fType |= MFT_RIGHTJUSTIFY;
3786 }
3787
3788 if(Flags & MF_MENUBREAK)
3789 {
3790 mii->fType |= MFT_MENUBREAK;
3791 }
3792 else if(Flags & MF_MENUBARBREAK)
3793 {
3794 mii->fType |= MFT_MENUBARBREAK;
3795 }
3796
3797 if(Flags & MF_GRAYED || Flags & MF_DISABLED)
3798 {
3799 if (Flags & MF_GRAYED)
3800 mii->fState |= MF_GRAYED;
3801
3802 if (Flags & MF_DISABLED)
3803 mii->fState |= MF_DISABLED;
3804
3805 mii->fMask |= MIIM_STATE;
3806 }
3807 else if (Flags & MF_HILITE)
3808 {
3809 mii->fState |= MF_HILITE;
3810 mii->fMask |= MIIM_STATE;
3811 }
3812 else /* default state */
3813 {
3814 mii->fState |= MFS_ENABLED;
3815 mii->fMask |= MIIM_STATE;
3816 }
3817
3818 if(Flags & MF_POPUP)
3819 {
3820 mii->fType |= MF_POPUP;
3821 mii->fMask |= MIIM_SUBMENU;
3822 mii->hSubMenu = (HMENU)IDNewItem;
3823 }
3824 else
3825 {
3826 mii->fMask |= MIIM_ID;
3827 mii->wID = (UINT)IDNewItem;
3828 }
3829 return TRUE;
3830 }
3831
3832 NTSTATUS WINAPI
3833 User32CallLoadMenuFromKernel(PVOID Arguments, ULONG ArgumentLength)
3834 {
3835 PLOADMENU_CALLBACK_ARGUMENTS Common;
3836 LRESULT Result;
3837
3838 Common = (PLOADMENU_CALLBACK_ARGUMENTS) Arguments;
3839
3840 Result = (LRESULT)LoadMenuW( Common->hModule,
3841 IS_INTRESOURCE(Common->MenuName[0]) ?
3842 MAKEINTRESOURCE(Common->MenuName[0]) :
3843 (LPCWSTR)&Common->MenuName);
3844
3845 return ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS);
3846 }
3847
3848
3849 /* FUNCTIONS *****************************************************************/
3850
3851 /*static BOOL
3852 MenuIsStringItem(ULONG TypeData)
3853 {
3854 return(MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType));
3855 }*/
3856
3857
3858 /*
3859 * @implemented
3860 */
3861 BOOL WINAPI
3862 AppendMenuA(HMENU hMenu,
3863 UINT uFlags,
3864 UINT_PTR uIDNewItem,
3865 LPCSTR lpNewItem)
3866 {
3867 return(InsertMenuA(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
3868 lpNewItem));
3869 }
3870
3871
3872 /*
3873 * @implemented
3874 */
3875 BOOL WINAPI
3876 AppendMenuW(HMENU hMenu,
3877 UINT uFlags,
3878 UINT_PTR uIDNewItem,
3879 LPCWSTR lpNewItem)
3880 {
3881 return(InsertMenuW(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
3882 lpNewItem));
3883 }
3884
3885
3886 /*
3887 * @implemented
3888 */
3889 DWORD WINAPI
3890 CheckMenuItem(HMENU hmenu,
3891 UINT uIDCheckItem,
3892 UINT uCheck)
3893 {
3894 return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck);
3895 }
3896
3897 static
3898 BOOL
3899 MenuCheckMenuRadioItem(HMENU hMenu, UINT idFirst, UINT idLast, UINT idCheck, UINT uFlags, BOOL bCheck, PUINT pChecked, PUINT pUnchecked, PUINT pMenuChanged)
3900 {
3901 UINT ItemCount, i;
3902 PROSMENUITEMINFO Items = NULL;
3903 UINT cChecked, cUnchecked;
3904 BOOL bRet = TRUE;
3905 //ROSMENUINFO mi;
3906
3907 if(idFirst > idLast)
3908 return FALSE;
3909
3910 ItemCount = GetMenuItemCount(hMenu);
3911
3912 //mi.cbSize = sizeof(ROSMENUINFO);
3913 //if(!NtUserMenuInfo(hmenu, &mi, FALSE)) return ret;
3914
3915
3916 if(MenuGetAllRosMenuItemInfo(hMenu, &Items) <= 0)
3917 {
3918 ERR("MenuGetAllRosMenuItemInfo failed\n");
3919 return FALSE;
3920 }
3921
3922 cChecked = cUnchecked = 0;
3923
3924 for (i = 0 ; i < ItemCount; i++)
3925 {
3926 BOOL check = FALSE;
3927 if (0 != (Items[i].fType & MF_MENUBARBREAK)) continue;
3928 if (0 != (Items[i].fType & MF_SEPARATOR)) continue;
3929
3930 if ((Items[i].fType & MF_POPUP) && (uFlags == MF_BYCOMMAND))
3931 {
3932 MenuCheckMenuRadioItem(Items[i].hSubMenu, idFirst, idLast, idCheck, uFlags, bCheck, pChecked, pUnchecked, pMenuChanged);
3933 continue;
3934 }
3935 if (uFlags & MF_BYPOSITION)
3936 {
3937 if (i < idFirst || i > idLast)
3938 continue;
3939
3940 if (i == idCheck)
3941 {
3942 cChecked++;
3943 check = TRUE;
3944 }
3945 else
3946 {
3947 cUnchecked++;
3948 }
3949 }
3950 else
3951 {
3952 if (Items[i].wID < idFirst || Items[i].wID > idLast)
3953 continue;
3954
3955 if (Items[i].wID == idCheck)
3956 {
3957 cChecked++;
3958 check = TRUE;
3959 }
3960 else
3961 {
3962 cUnchecked++;
3963 }
3964 }
3965
3966 if (!bCheck)
3967 continue;
3968
3969 Items[i].fMask = MIIM_STATE | MIIM_FTYPE;
3970 if (check)
3971 {
3972 Items[i].fType |= MFT_RADIOCHECK;
3973 Items[i].fState |= MFS_CHECKED;
3974 }
3975 else
3976 {
3977 Items[i].fState &= ~MFS_CHECKED;
3978 }
3979
3980 if(!MenuSetRosMenuItemInfo(hMenu, i ,&Items[i]))
3981 {
3982 ERR("MenuSetRosMenuItemInfo failed\n");
3983 bRet = FALSE;
3984 break;
3985 }
3986 }
3987 HeapFree(GetProcessHeap(), 0, Items);
3988
3989 *pChecked += cChecked;
3990 *pUnchecked += cUnchecked;
3991
3992 if (cChecked || cUnchecked)
3993 (*pMenuChanged)++;
3994
3995 return bRet;
3996 }
3997
3998 /*
3999 * @implemented
4000 */
4001 BOOL WINAPI
4002 CheckMenuRadioItem(HMENU hmenu,
4003 UINT idFirst,
4004 UINT idLast,
4005 UINT idCheck,
4006 UINT uFlags)
4007 {
4008 UINT cChecked = 0;
4009 UINT cUnchecked = 0;
4010 UINT cMenuChanged = 0;
4011
4012 if (!MenuCheckMenuRadioItem(hmenu, idFirst, idLast, idCheck, uFlags, FALSE, &cChecked, &cUnchecked, &cMenuChanged))
4013 return FALSE;
4014
4015 if (cMenuChanged > 1)
4016 return FALSE;
4017
4018 cMenuChanged = 0;
4019 cChecked = 0;
4020 cUnchecked = 0;
4021
4022 if (!MenuCheckMenuRadioItem(hmenu, idFirst, idLast, idCheck, uFlags, TRUE, &cChecked, &cUnchecked, &cMenuChanged))
4023 return FALSE;
4024
4025 return (cChecked != 0);
4026 }
4027
4028
4029 /*
4030 * @implemented
4031 */
4032 HMENU WINAPI
4033 CreateMenu(VOID)
4034 {
4035 MenuLoadBitmaps();
4036 return (HMENU)NtUserCallNoParam(NOPARAM_ROUTINE_CREATEMENU);
4037 }
4038
4039
4040 /*
4041 * @implemented
4042 */
4043 HMENU WINAPI
4044 CreatePopupMenu(VOID)
4045 {
4046 MenuLoadBitmaps();
4047 return (HMENU)NtUserCallNoParam(NOPARAM_ROUTINE_CREATEMENUPOPUP);
4048 }
4049
4050
4051 /*
4052 * @implemented
4053 */
4054 BOOL WINAPI
4055 DrawMenuBar(HWND hWnd)
4056 {
4057 // return (BOOL)NtUserCallHwndLock(hWnd, HWNDLOCK_ROUTINE_DRAWMENUBAR);
4058 ROSMENUINFO MenuInfo;
4059 HMENU hMenu;
4060 hMenu = GetMenu(hWnd);
4061 if (!hMenu)
4062 return FALSE;
4063 MenuGetRosMenuInfo(&MenuInfo, hMenu);
4064 MenuInfo.Height = 0; // make sure to recalc size
4065 MenuSetRosMenuInfo(&MenuInfo);
4066 /* The wine method doesn't work and I suspect it's more effort
4067 then hackfix solution
4068 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4069 SWP_NOZORDER | SWP_FRAMECHANGED );
4070 return TRUE;*/
4071 // FIXME: hackfix
4072 DefWndNCPaint(hWnd,(HRGN)-1,-1);
4073 return TRUE;
4074 }
4075
4076 /*
4077 * @implemented
4078 */
4079 BOOL WINAPI
4080 EnableMenuItem(HMENU hMenu,
4081 UINT uIDEnableItem,
4082 UINT uEnable)
4083 {
4084 return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable);
4085 }
4086
4087 /*
4088 * @implemented
4089 */
4090 BOOL WINAPI
4091 EndMenu(VOID)
4092 {
4093 GUITHREADINFO guii;
4094 guii.cbSize = sizeof(GUITHREADINFO);
4095 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii) && guii.hwndMenuOwner)
4096 {
4097 if (!fEndMenu &&
4098 top_popup &&
4099 guii.hwndMenuOwner != top_popup )
4100 {
4101 ERR("Capture GUI pti hWnd does not match top_popup!\n");
4102 }
4103 }
4104
4105 /* if we are in the menu code, and it is active */
4106 if (!fEndMenu && top_popup)
4107 {
4108 /* terminate the menu handling code */
4109 fEndMenu = TRUE;
4110
4111 /* needs to be posted to wakeup the internal menu handler */
4112 /* which will now terminate the menu, in the event that */
4113 /* the main window was minimized, or lost focus, so we */
4114 /* don't end up with an orphaned menu */
4115 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4116 }
4117 return TRUE;
4118 }
4119
4120 // So this one maybe one day it will be a callback!
4121 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
4122 UINT wHilite )
4123 {
4124 ROSMENUINFO MenuInfo;
4125 ROSMENUITEMINFO mii;
4126 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
4127 if (!hWnd)
4128 {
4129 SetLastError(ERROR_INVALID_WINDOW_HANDLE);
4130 return FALSE;
4131 }
4132 if (!NtUserMenuItemInfo(hMenu, wItemID, wHilite, &mii, FALSE)) return FALSE;
4133 if (!NtUserMenuInfo(hMenu, &MenuInfo, FALSE)) return FALSE;
4134 if (MenuInfo.FocusedItem == wItemID) return TRUE;
4135 MenuHideSubPopups( hWnd, &MenuInfo, FALSE, 0 );
4136 MenuSelectItem( hWnd, &MenuInfo, wItemID, TRUE, 0 );
4137 return TRUE;
4138 }
4139
4140 /*
4141 * @implemented
4142 */
4143 HMENU WINAPI
4144 GetMenu(HWND hWnd)
4145 {
4146 PWND Wnd = ValidateHwnd(hWnd);
4147
4148 if (!Wnd)
4149 return NULL;
4150
4151 return UlongToHandle(Wnd->IDMenu);
4152 }
4153
4154
4155 /*
4156 * @implemented
4157 */
4158 LONG WINAPI
4159 GetMenuCheckMarkDimensions(VOID)
4160 {
4161 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK),
4162 GetSystemMetrics(SM_CYMENUCHECK)));
4163 }
4164
4165
4166 /*
4167 * @implemented
4168 */
4169 UINT WINAPI
4170 GetMenuDefaultItem(HMENU hMenu,
4171 UINT fByPos,
4172 UINT gmdiFlags)
4173 {
4174 return NtUserGetMenuDefaultItem(hMenu, fByPos, gmdiFlags);
4175 }
4176
4177
4178 /*
4179 * @implemented
4180 */
4181 BOOL WINAPI
4182 GetMenuInfo(HMENU hmenu,
4183 LPMENUINFO lpcmi)
4184 {
4185 ROSMENUINFO mi;
4186 BOOL res = FALSE;
4187
4188 if(!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
4189 return FALSE;
4190
4191 RtlZeroMemory(&mi, sizeof(MENUINFO));
4192 mi.cbSize = sizeof(MENUINFO);
4193 mi.fMask = lpcmi->fMask;
4194
4195 res = NtUserMenuInfo(hmenu, &mi, FALSE);
4196
4197 memcpy(lpcmi, &mi, sizeof(MENUINFO));
4198 return res;
4199 }
4200
4201
4202 /*
4203 * @implemented
4204 */
4205 int WINAPI
4206 GetMenuItemCount(HMENU Menu)
4207 {
4208 ROSMENUINFO MenuInfo;
4209
4210 return MenuGetRosMenuInfo(&MenuInfo, Menu) ? MenuInfo.MenuItemCount : 0;
4211 }
4212
4213
4214 /*
4215 * @implemented
4216 */
4217 UINT WINAPI
4218 GetMenuItemID(HMENU hMenu,
4219 int nPos)
4220 {
4221 ROSMENUITEMINFO mii;
4222
4223 mii.cbSize = sizeof(MENUITEMINFOW);
4224 mii.fMask = MIIM_ID | MIIM_SUBMENU;
4225
4226 if (! NtUserMenuItemInfo(hMenu, nPos, MF_BYPOSITION, &mii, FALSE))
4227 {
4228 return -1;
4229 }
4230
4231 if (NULL != mii.hSubMenu)
4232 {
4233 return -1;
4234 }
4235 if (0 == mii.wID)
4236 {
4237 return -1;
4238 }
4239
4240 return mii.wID;
4241 }
4242
4243
4244 /*
4245 * @implemented
4246 */
4247 BOOL WINAPI
4248 GetMenuItemInfoA(
4249 HMENU Menu,
4250 UINT Item,
4251 BOOL ByPosition,
4252 LPMENUITEMINFOA mii)
4253 {
4254 MENUITEMINFOW miiW;
4255 LPSTR AnsiBuffer;
4256 INT Count;
4257
4258 if (mii->cbSize != sizeof(MENUITEMINFOA) &&
4259 mii->cbSize != sizeof(MENUITEMINFOA) - sizeof(HBITMAP))
4260 {
4261 SetLastError(ERROR_INVALID_PARAMETER);
4262 return FALSE;
4263 }
4264
4265 if(!(mii->fMask & (MIIM_TYPE | MIIM_STRING)))
4266 {
4267 /* No text requested, just pass on */
4268 return NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) mii, FALSE);
4269 }
4270
4271 AnsiBuffer = mii->dwTypeData;
4272 Count = miiW.cch = mii->cch;
4273 RtlCopyMemory(&miiW, mii, mii->cbSize);
4274 miiW.dwTypeData = 0;
4275
4276 if (AnsiBuffer)
4277 {
4278 miiW.dwTypeData = RtlAllocateHeap(GetProcessHeap(), 0,
4279 miiW.cch * sizeof(WCHAR));
4280 if (miiW.dwTypeData == NULL) return FALSE;
4281 miiW.dwTypeData[0] = 0;
4282 }
4283
4284 if (!NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO)&miiW, FALSE))
4285 {
4286 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4287 return FALSE;
4288 }
4289
4290 RtlCopyMemory(mii, &miiW, miiW.cbSize);
4291
4292 if (!AnsiBuffer || !Count)
4293 {
4294 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4295 mii->dwTypeData = AnsiBuffer;
4296 mii->cch = miiW.cch;
4297 return TRUE;
4298 }
4299
4300 if ((miiW.fMask & MIIM_STRING) || (IS_STRING_ITEM(miiW.fType)))
4301 {
4302 if (miiW.cch)
4303 {
4304 if (!WideCharToMultiByte(CP_ACP, 0, miiW.dwTypeData, miiW.cch, AnsiBuffer, mii->cch, NULL, NULL))
4305 {
4306 AnsiBuffer[0] = 0;
4307 }
4308 if (Count > miiW.cch)
4309 {
4310 AnsiBuffer[miiW.cch] = 0;
4311 }
4312 mii->cch = mii->cch;
4313 }
4314 }
4315 else
4316 {
4317 AnsiBuffer[0] = 0;
4318 }
4319
4320 RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4321 mii->dwTypeData = AnsiBuffer;
4322
4323 return TRUE;
4324 }
4325
4326
4327 /*
4328 * @implemented
4329 */
4330 BOOL WINAPI
4331 GetMenuItemInfoW(
4332 HMENU Menu,
4333 UINT Item,
4334 BOOL ByPosition,
4335 LPMENUITEMINFOW mii)
4336 {
4337 MENUITEMINFOW miiW;
4338 LPWSTR String;
4339 INT Count;
4340
4341 if (mii->cbSize != sizeof(MENUITEMINFOW) &&
4342 mii->cbSize != sizeof(MENUITEMINFOW) - sizeof(HBITMAP))
4343 {
4344 SetLastError(ERROR_INVALID_PARAMETER);
4345 return FALSE;
4346 }
4347
4348 if(!(mii->fMask & (MIIM_TYPE | MIIM_STRING)))
4349 {
4350 /* No text requested, just pass on */
4351 return NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) mii, FALSE);
4352 }
4353
4354 String = mii->dwTypeData;
4355 Count = mii->cch;
4356 RtlCopyMemory(&miiW, mii, mii->cbSize);
4357 miiW.dwTypeData = 0;
4358
4359 if (String)
4360 {
4361 miiW.dwTypeData = RtlAllocateHeap(GetProcessHeap(), 0,
4362 miiW.cch * sizeof(WCHAR));
4363 if (miiW.dwTypeData == NULL) return FALSE;
4364 miiW.dwTypeData[0] = 0;
4365 }
4366
4367 if (!NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) &miiW, FALSE))
4368 {
4369 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4370 return FALSE;
4371 }
4372
4373 RtlCopyMemory(mii, &miiW, miiW.cbSize); // Okay to over write user data.
4374
4375 if (!String || !Count)
4376 {
4377 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4378 mii->dwTypeData = String; // may not be zero.
4379 mii->cch = miiW.cch;
4380 return TRUE;
4381 }
4382
4383 if ((miiW.fMask & MIIM_STRING) || (IS_STRING_ITEM(miiW.fType)))
4384 {
4385 lstrcpynW( String, miiW.dwTypeData, Count );
4386 }
4387
4388 RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4389 mii->dwTypeData = String;
4390 mii->cch = strlenW(String);
4391 return TRUE;
4392 }
4393
4394
4395 /*
4396 * @implemented
4397 */
4398 UINT
4399 WINAPI
4400 GetMenuState(
4401 HMENU hMenu,
4402 UINT uId,
4403 UINT uFlags)
4404 {
4405 ROSMENUINFO MenuInfo;
4406 ROSMENUITEMINFO mii;
4407 memset( &mii, 0, sizeof(mii) );
4408 mii.cbSize = sizeof(MENUITEMINFOW);
4409 mii.fMask = MIIM_STATE | MIIM_FTYPE | MIIM_SUBMENU;
4410
4411 SetLastError(0);
4412 if(NtUserMenuItemInfo(hMenu, uId, uFlags, &mii, FALSE))
4413 {
4414 UINT nSubItems = 0;
4415 if(mii.hSubMenu)
4416 {
4417 if (! MenuGetRosMenuInfo(&MenuInfo, mii.hSubMenu))
4418 {
4419 return (UINT) -1;
4420 }
4421 nSubItems = MenuInfo.MenuItemCount;
4422
4423 /* FIXME - ported from wine, does that work (0xff)? */
4424 if(GetLastError() != ERROR_INVALID_MENU_HANDLE)
4425 return (nSubItems << 8) | ((mii.fState | mii.fType) & 0xff);
4426
4427 return (UINT)-1; /* Invalid submenu */
4428 }
4429
4430 /* FIXME - ported from wine, does that work? */
4431 return (mii.fType | mii.fState);
4432 }
4433
4434 return (UINT)-1;
4435 }
4436
4437
4438 /*
4439 * @implemented
4440 */
4441 int
4442 WINAPI
4443 GetMenuStringA(
4444 HMENU hMenu,
4445 UINT uIDItem,
4446 LPSTR lpString,
4447 int nMaxCount,
4448 UINT uFlag)
4449 {
4450 MENUITEMINFOA mii;
4451 memset( &mii, 0, sizeof(mii) );
4452 mii.dwTypeData = lpString;
4453 mii.fMask = MIIM_STRING | MIIM_FTYPE;
4454 mii.fType = MFT_STRING;
4455 mii.cbSize = sizeof(MENUITEMINFOA);
4456 mii.cch = nMaxCount;
4457
4458 if(!(GetMenuItemInfoA( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&mii)))
4459 return 0;
4460 else
4461 return mii.cch;
4462 }
4463
4464
4465 /*
4466 * @implemented
4467 */
4468 int
4469 WINAPI
4470 GetMenuStringW(
4471 HMENU hMenu,
4472 UINT uIDItem,
4473 LPWSTR lpString,
4474 int nMaxCount,
4475 UINT uFlag)
4476 {
4477 MENUITEMINFOW miiW;
4478 memset( &miiW, 0, sizeof(miiW) );
4479 miiW.dwTypeData = lpString;
4480 miiW.fMask = MIIM_STRING | MIIM_FTYPE;
4481 miiW.fType = MFT_STRING;
4482 miiW.cbSize = sizeof(MENUITEMINFOW);
4483 miiW.cch = nMaxCount;
4484
4485 if(!(GetMenuItemInfoW( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&miiW)))
4486 return 0;
4487 else
4488 return miiW.cch;
4489 }
4490
4491
4492 /*
4493 * @implemented
4494 */
4495 HMENU
4496 WINAPI
4497 GetSubMenu(
4498 HMENU hMenu,
4499 int nPos)
4500 {
4501 ROSMENUITEMINFO mi;
4502
4503 mi.cbSize = sizeof(MENUITEMINFOW);
4504 mi.fMask = MIIM_SUBMENU;
4505
4506 if (NtUserMenuItemInfo(hMenu, (UINT)nPos, MF_BYPOSITION, &mi, FALSE))
4507 {
4508 return IsMenu(mi.hSubMenu) ? mi.hSubMenu : NULL;
4509 }
4510
4511 return NULL;
4512 }
4513
4514 /*
4515 * @implemented
4516 */
4517 HMENU
4518 WINAPI
4519 GetSystemMenu(
4520 HWND hWnd,
4521 BOOL bRevert)
4522 {
4523 HMENU TopMenu;
4524
4525 TopMenu = NtUserGetSystemMenu(hWnd, bRevert);
4526
4527 return NULL == TopMenu ? NULL : GetSubMenu(TopMenu, 0);
4528 }
4529
4530
4531 /*
4532 * @implemented
4533 */
4534 BOOL
4535 WINAPI
4536 InsertMenuA(
4537 HMENU hMenu,
4538 UINT uPosition,
4539 UINT uFlags,
4540 UINT_PTR uIDNewItem,
4541 LPCSTR lpNewItem)
4542 {
4543 MENUITEMINFOA mii;
4544 memset( &mii, 0, sizeof(mii) );
4545 mii.cbSize = sizeof(MENUITEMINFOA);
4546 mii.fMask = MIIM_FTYPE;
4547
4548 MenuSetItemData((LPMENUITEMINFOW) &mii,
4549 uFlags,
4550 uIDNewItem,
4551 (LPCWSTR) lpNewItem,
4552 FALSE);
4553
4554 return InsertMenuItemA(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii);
4555 }
4556
4557
4558
4559 /*
4560 * @implemented
4561 */
4562 BOOL
4563 WINAPI
4564 InsertMenuItemA(
4565 HMENU hMenu,
4566 UINT uItem,
4567 BOOL fByPosition,
4568 LPCMENUITEMINFOA lpmii)
4569 {
4570 MENUITEMINFOW mi;
4571 UNICODE_STRING MenuText;
4572 BOOL res = FALSE;
4573 BOOL CleanHeap = FALSE;
4574 NTSTATUS Status;
4575
4576 if((lpmii->cbSize == sizeof(MENUITEMINFOA)) ||
4577 (lpmii->cbSize == sizeof(MENUITEMINFOA) - sizeof(HBITMAP)))
4578 {
4579 RtlCopyMemory ( &mi, lpmii, lpmii->cbSize );
4580
4581 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
4582 {
4583 mi.cbSize = sizeof( MENUITEMINFOW);
4584 mi.hbmpItem = NULL;
4585 }
4586 /* copy the text string */
4587 if (((mi.fMask & MIIM_STRING) ||
4588 ((mi.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mi.fType) == MF_STRING)))
4589 && mi.dwTypeData != NULL)
4590 {
4591 Status = RtlCreateUnicodeStringFromAsciiz(&MenuText, (LPSTR)mi.dwTypeData);
4592 if (!NT_SUCCESS (Status))
4593 {
4594 SetLastError (RtlNtStatusToDosError(Status));
4595 return FALSE;
4596 }
4597 mi.dwTypeData = MenuText.Buffer;
4598 mi.cch = MenuText.Length / sizeof(WCHAR);
4599 CleanHeap = TRUE;
4600 }
4601 res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mi, NULL);
4602
4603 if ( CleanHeap ) RtlFreeUnicodeString ( &MenuText );
4604 }
4605 return res;
4606 }
4607
4608
4609 /*
4610 * @implemented
4611 */
4612 BOOL
4613 WINAPI
4614 InsertMenuItemW(
4615 HMENU hMenu,
4616 UINT uItem,
4617 BOOL fByPosition,
4618 LPCMENUITEMINFOW lpmii)
4619 {
4620 MENUITEMINFOW mi;
4621 UNICODE_STRING MenuText;
4622 BOOL res = FALSE;
4623
4624 /* while we could just pass 'lpmii' to win32k, we make a copy so that
4625 if a bad user passes bad data, we crash his process instead of the
4626 entire kernel */
4627
4628 if((lpmii->cbSize == sizeof(MENUITEMINFOW)) ||
4629 (lpmii->cbSize == sizeof(MENUITEMINFOW) - sizeof(HBITMAP)))
4630 {
4631 RtlCopyMemory(&mi, lpmii, lpmii->cbSize);
4632
4633 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
4634 {
4635 mi.cbSize = sizeof( MENUITEMINFOW);
4636 mi.hbmpItem = NULL;
4637 }
4638 /* copy the text string */
4639 if (((mi.fMask & MIIM_STRING) ||
4640 ((mi.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mi.fType) == MF_STRING)))
4641 && mi.dwTypeData != NULL)
4642 {
4643 RtlInitUnicodeString(&MenuText, (PWSTR)lpmii->dwTypeData);
4644 mi.dwTypeData = MenuText.Buffer;
4645 mi.cch = MenuText.Length / sizeof(WCHAR);
4646 }
4647 res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mi, NULL);
4648 }
4649 return res;
4650 }
4651
4652
4653 /*
4654 * @implemented
4655 */
4656 BOOL
4657 WINAPI
4658 InsertMenuW(
4659 HMENU hMenu,
4660 UINT uPosition,
4661 UINT uFlags,
4662 UINT_PTR uIDNewItem,
4663 LPCWSTR lpNewItem)
4664 {
4665 MENUITEMINFOW mii;
4666 memset( &mii, 0, sizeof(mii) );
4667 mii.cbSize = sizeof(MENUITEMINFOW);
4668 mii.fMask = MIIM_FTYPE;
4669
4670 MenuSetItemData( &mii,
4671 uFlags,
4672 uIDNewItem,
4673 lpNewItem,
4674 TRUE);
4675
4676 return InsertMenuItemW(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii);
4677 }
4678
4679
4680 /*
4681 * @implemented
4682 */
4683 BOOL
4684 WINAPI
4685 IsMenu(
4686 HMENU Menu)
4687 {
4688 if (ValidateHandle(Menu, VALIDATE_TYPE_MENU)) return TRUE;
4689 return FALSE;
4690 }
4691
4692
4693 /*
4694 * @implemented
4695 */
4696 HMENU WINAPI
4697 LoadMenuA(HINSTANCE hInstance,
4698 LPCSTR lpMenuName)
4699 {
4700 HANDLE Resource = FindResourceA(hInstance, lpMenuName, MAKEINTRESOURCEA(4));
4701 if (Resource == NULL)
4702 {
4703 return(NULL);
4704 }
4705 return(LoadMenuIndirectA((PVOID)LoadResource(hInstance, Resource)));
4706 }
4707
4708
4709 /*
4710 * @implemented
4711 */
4712 HMENU WINAPI
4713 LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate)
4714 {
4715 return(LoadMenuIndirectW(lpMenuTemplate));
4716 }
4717
4718
4719 /*
4720 * @implemented
4721 */
4722 HMENU WINAPI
4723 LoadMenuIndirectW(CONST MENUTEMPLATE *lpMenuTemplate)
4724 {
4725 HMENU hMenu;
4726 WORD version, offset;
4727 LPCSTR p = (LPCSTR)lpMenuTemplate;
4728
4729 version = GET_WORD(p);
4730 p += sizeof(WORD);
4731
4732 switch (version)
4733 {
4734 case 0: /* standard format is version of 0 */
4735 offset = GET_WORD(p);
4736 p += sizeof(WORD) + offset;
4737 if (!(hMenu = CreateMenu())) return 0;
4738 if (!MENU_ParseResource(p, hMenu, TRUE))
4739 {
4740 DestroyMenu(hMenu);
4741 return 0;
4742 }
4743 return hMenu;
4744 case 1: /* extended format is version of 1 */
4745 offset = GET_WORD(p);
4746 p += sizeof(WORD) + offset;
4747 if (!(hMenu = CreateMenu())) return 0;
4748 if (!MENUEX_ParseResource(p, hMenu))
4749 {
4750 DestroyMenu( hMenu );
4751 return 0;
4752 }
4753 return hMenu;
4754 default:
4755 DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version);
4756 return 0;
4757 }
4758 }
4759
4760
4761 /*
4762 * @implemented
4763 */
4764 HMENU WINAPI
4765 LoadMenuW(HINSTANCE hInstance,
4766 LPCWSTR lpMenuName)
4767 {
4768 HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU);
4769 if (Resource == NULL)
4770 {
4771 return(NULL);
4772 }
4773 return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource)));
4774 }
4775
4776
4777 /*
4778 * @implemented
4779 */
4780 int
4781 WINAPI
4782 MenuItemFromPoint(
4783 HWND hWnd,
4784 HMENU hMenu,
4785 POINT ptScreen)
4786 {
4787 return NtUserMenuItemFromPoint(hWnd, hMenu, ptScreen.x, ptScreen.y);
4788 }
4789
4790
4791 /*
4792 * @implemented
4793 */
4794 BOOL
4795 WINAPI
4796 ModifyMenuA(
4797 HMENU hMnu,
4798 UINT uPosition,
4799 UINT uFlags,
4800 UINT_PTR uIDNewItem,
4801 LPCSTR lpNewItem)
4802 {
4803 ROSMENUINFO mi;
4804 ROSMENUITEMINFO rmii;
4805 MENUITEMINFOA mii;
4806 memset( &mii, 0, sizeof(mii) );
4807 mii.cbSize = sizeof(MENUITEMINFOA);
4808 mii.fMask = MIIM_FTYPE;
4809
4810 if (!MenuGetRosMenuInfo( &mi, hMnu )) return FALSE;
4811
4812 mi.Height = 0;
4813
4814 if (!MenuSetRosMenuInfo( &mi )) return FALSE;
4815
4816 MenuInitRosMenuItemInfo( &rmii );
4817
4818 if(!MenuGetRosMenuItemInfo( hMnu, uPosition, &rmii)) return FALSE;
4819
4820 if ((rmii.fType & MF_POPUP) && (uFlags & MF_POPUP) && (rmii.hSubMenu != (HMENU)uIDNewItem))
4821 NtUserDestroyMenu( rmii.hSubMenu ); /* ModifyMenu() spec */
4822
4823 MenuCleanupRosMenuItemInfo( &rmii );
4824
4825 MenuSetItemData((LPMENUITEMINFOW) &mii,
4826 uFlags,
4827 uIDNewItem,
4828 (LPCWSTR) lpNewItem,
4829 FALSE);
4830
4831 return SetMenuItemInfoA( hMnu,
4832 uPosition,
4833 (BOOL)(MF_BYPOSITION & uFlags),
4834 &mii);
4835 }
4836
4837
4838 /*
4839 * @implemented
4840 */
4841 BOOL
4842 WINAPI
4843 ModifyMenuW(
4844 HMENU hMnu,
4845 UINT uPosition,
4846 UINT uFlags,
4847 UINT_PTR uIDNewItem,
4848 LPCWSTR lpNewItem)
4849 {
4850 ROSMENUINFO mi;
4851 ROSMENUITEMINFO rmii;
4852 MENUITEMINFOW mii;
4853 memset ( &mii, 0, sizeof(mii) );
4854 mii.cbSize = sizeof(MENUITEMINFOW);
4855 mii.fMask = MIIM_FTYPE;
4856
4857 if (!MenuGetRosMenuInfo( &mi, hMnu )) return FALSE;
4858
4859 mi.Height = 0; // Force size recalculation.
4860
4861 if (!MenuSetRosMenuInfo( &mi )) return FALSE;
4862
4863 MenuInitRosMenuItemInfo( &rmii );
4864
4865 if(!MenuGetRosMenuItemInfo( hMnu, uPosition, &rmii)) return FALSE;
4866
4867 if ((rmii.fType & MF_POPUP) && (uFlags & MF_POPUP) && (rmii.hSubMenu != (HMENU)uIDNewItem))
4868 NtUserDestroyMenu( rmii.hSubMenu ); /* ModifyMenu() spec */
4869
4870 MenuCleanupRosMenuItemInfo( &rmii );
4871
4872 /* Init new data for this menu item */
4873 MenuSetItemData( &mii,
4874 uFlags,
4875 uIDNewItem,
4876 lpNewItem,
4877 TRUE);
4878
4879 /* Now, make Win32k IntSetMenuItemInfo handle the changes to this menu item. */
4880 return SetMenuItemInfoW( hMnu,
4881 uPosition,
4882 (BOOL)(MF_BYPOSITION & uFlags),
4883 &mii);
4884 }
4885
4886
4887 /*
4888 * @implemented
4889 */
4890 BOOL WINAPI
4891 SetMenu(HWND hWnd,
4892 HMENU hMenu)
4893 {
4894 return NtUserSetMenu(hWnd, hMenu, TRUE);
4895 }
4896
4897
4898 /*
4899 * @implemented
4900 */
4901 BOOL
4902 WINAPI
4903 SetMenuInfo(
4904 HMENU hmenu,
4905 LPCMENUINFO lpcmi)
4906 {
4907 ROSMENUINFO mi;
4908 BOOL res = FALSE;
4909
4910 if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
4911 {
4912 SetLastError(ERROR_INVALID_PARAMETER);
4913 return res;
4914 }
4915
4916 memcpy(&mi, lpcmi, sizeof(MENUINFO));
4917 return NtUserMenuInfo(hmenu, &mi, TRUE);
4918 }
4919
4920
4921 /*
4922 * @implemented
4923 */
4924 BOOL
4925 WINAPI
4926 SetMenuItemBitmaps(
4927 HMENU hMenu,
4928 UINT uPosition,
4929 UINT uFlags,
4930 HBITMAP hBitmapUnchecked,
4931 HBITMAP hBitmapChecked)
4932 {
4933 ROSMENUITEMINFO uItem;
4934 memset ( &uItem, 0, sizeof(uItem) );
4935 uItem.fMask = MIIM_STATE | MIIM_BITMAP;
4936
4937 if(!(NtUserMenuItemInfo(hMenu, uPosition,
4938 (BOOL)(MF_BYPOSITION & uFlags), &uItem, FALSE))) return FALSE;
4939
4940 if (!hBitmapChecked && !hBitmapUnchecked)
4941 {
4942 uItem.fState &= ~MF_USECHECKBITMAPS;
4943 }
4944 else /* Install new bitmaps */
4945 {
4946 uItem.hbmpChecked = hBitmapChecked;
4947 uItem.hbmpUnchecked = hBitmapUnchecked;
4948 uItem.fState |= MF_USECHECKBITMAPS;
4949 }
4950 return NtUserMenuItemInfo(hMenu, uPosition,
4951 (BOOL)(MF_BYPOSITION & uFlags), &uItem, TRUE);
4952 }
4953
4954
4955 /*
4956 * @implemented
4957 */
4958 BOOL
4959 WINAPI
4960 SetMenuItemInfoA(
4961 HMENU hMenu,
4962 UINT uItem,
4963 BOOL fByPosition,
4964 LPCMENUITEMINFOA lpmii)
4965 {
4966 MENUITEMINFOW MenuItemInfoW;
4967 UNICODE_STRING UnicodeString;
4968 NTSTATUS Status;
4969 ULONG Result = FALSE;
4970
4971 RtlCopyMemory(&MenuItemInfoW, lpmii, min(lpmii->cbSize, sizeof(MENUITEMINFOW)));
4972
4973 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
4974 {
4975 MenuItemInfoW.cbSize = sizeof( MENUITEMINFOW);
4976 MenuItemInfoW.hbmpItem = NULL;
4977 }
4978 /*
4979 * MIIM_STRING == good
4980 * MIIM_TYPE & MFT_STRING == good
4981 * MIIM_STRING & MFT_STRING == good
4982 * MIIM_STRING & MFT_OWNERSRAW == good
4983 */
4984 if (((MenuItemInfoW.fMask & MIIM_STRING) ||
4985 ((MenuItemInfoW.fMask & MIIM_TYPE) &&
4986 (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
4987 && MenuItemInfoW.dwTypeData != NULL)
4988 {
4989 /* cch is ignored when the content of a menu item is set by calling SetMenuItemInfo. */
4990 Status = RtlCreateUnicodeStringFromAsciiz(&UnicodeString,
4991 (LPSTR)MenuItemInfoW.dwTypeData);
4992 if (!NT_SUCCESS (Status))
4993 {
4994 SetLastError (RtlNtStatusToDosError(Status));
4995 return FALSE;
4996 }
4997 MenuItemInfoW.dwTypeData = UnicodeString.Buffer;
4998 MenuItemInfoW.cch = UnicodeString.Length / sizeof(WCHAR);
4999 }
5000 else
5001 {
5002 UnicodeString.Buffer = NULL;
5003 }
5004
5005 Result = NtUserMenuItemInfo(hMenu, uItem, fByPosition,
5006 (PROSMENUITEMINFO)&MenuItemInfoW, TRUE);
5007
5008 if (UnicodeString.Buffer != NULL)
5009 {
5010 RtlFreeUnicodeString(&UnicodeString);
5011 }
5012
5013 return Result;
5014 }
5015
5016
5017 /*
5018 * @implemented
5019 */
5020 BOOL
5021 WINAPI
5022 SetMenuItemInfoW(
5023 HMENU hMenu,
5024 UINT uItem,
5025 BOOL fByPosition,
5026 LPCMENUITEMINFOW lpmii)
5027 {
5028 MENUITEMINFOW MenuItemInfoW;
5029 ULONG Result;
5030
5031 RtlCopyMemory(&MenuItemInfoW, lpmii, min(lpmii->cbSize, sizeof(MENUITEMINFOW)));
5032
5033 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
5034 {
5035 MenuItemInfoW.cbSize = sizeof( MENUITEMINFOW);
5036 MenuItemInfoW.hbmpItem = NULL;
5037 }
5038
5039 if (((MenuItemInfoW.fMask & MIIM_STRING) ||
5040 ((MenuItemInfoW.fMask & MIIM_TYPE) &&
5041 (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
5042 && MenuItemInfoW.dwTypeData != NULL)
5043 {
5044 MenuItemInfoW.cch = strlenW(MenuItemInfoW.dwTypeData);
5045 }
5046 Result = NtUserMenuItemInfo(hMenu, uItem, fByPosition,
5047 (PROSMENUITEMINFO)&MenuItemInfoW, TRUE);
5048
5049 return Result;
5050 }
5051
5052 /*
5053 * @implemented
5054 */
5055 BOOL
5056 WINAPI
5057 SetSystemMenu (
5058 HWND hwnd,
5059 HMENU hMenu)
5060 {
5061 if(!hwnd)
5062 {
5063 SetLastError(ERROR_INVALID_WINDOW_HANDLE);
5064 return FALSE;
5065 }
5066 if(!hMenu)
5067 {
5068 SetLastError(ERROR_INVALID_MENU_HANDLE);
5069 return FALSE;
5070 }
5071 return NtUserSetSystemMenu(hwnd, hMenu);
5072 }
5073
5074 //
5075 // Example for the Win32/User32 rewrite.
5076 // Def = TrackPopupMenuEx@24=NtUserTrackPopupMenuEx@24
5077 //
5078 //
5079 BOOL
5080 WINAPI
5081 NEWTrackPopupMenu(
5082 HMENU Menu,
5083 UINT Flags,
5084 int x,
5085 int y,
5086 int Reserved,
5087 HWND Wnd,
5088 CONST RECT *Rect)
5089 {
5090 return NtUserTrackPopupMenuEx( Menu,
5091 Flags,
5092 x,
5093 y,
5094 Wnd,
5095 NULL); // LPTPMPARAMS is null
5096 }
5097
5098
5099 /*
5100 * @implemented
5101 */
5102 DWORD
5103 WINAPI
5104 GetMenuContextHelpId(HMENU hmenu)
5105 {
5106 ROSMENUINFO mi;
5107 mi.cbSize = sizeof(ROSMENUINFO);
5108 mi.fMask = MIM_HELPID;
5109
5110 if(NtUserMenuInfo(hmenu, &mi, FALSE))
5111 {
5112 return mi.dwContextHelpID;
5113 }
5114 return 0;
5115 }
5116
5117 /*
5118 * @unimplemented
5119 */
5120 BOOL
5121 WINAPI
5122 MenuWindowProcA(
5123 HWND hWnd,
5124 ULONG_PTR Result,
5125 UINT Msg,
5126 WPARAM wParam,
5127 LPARAM lParam
5128 )
5129 {
5130 if ( Msg < WM_USER)
5131 {
5132 LRESULT lResult;
5133 lResult = PopupMenuWndProcA(hWnd, Msg, wParam, lParam );
5134 if (Result)
5135 {
5136 Result = (ULONG_PTR)lResult;
5137 return TRUE;
5138 }
5139 return FALSE;
5140 }
5141 return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, TRUE);
5142
5143 }
5144
5145 /*
5146 * @unimplemented
5147 */
5148 BOOL
5149 WINAPI
5150 MenuWindowProcW(
5151 HWND hWnd,
5152 ULONG_PTR Result,
5153 UINT Msg,
5154 WPARAM wParam,
5155 LPARAM lParam
5156 )
5157 {
5158 if ( Msg < WM_USER)
5159 {
5160 LRESULT lResult;
5161 lResult = PopupMenuWndProcW(hWnd, Msg, wParam, lParam );
5162 if (Result)
5163 {
5164 Result = (ULONG_PTR)lResult;
5165 return TRUE;
5166 }
5167 return FALSE;
5168 }
5169 return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, FALSE);
5170 }
5171
5172 /*
5173 * @implemented
5174 */
5175 BOOL
5176 WINAPI
5177 ChangeMenuW(
5178 HMENU hMenu,
5179 UINT cmd,
5180 LPCWSTR lpszNewItem,
5181 UINT cmdInsert,
5182 UINT flags)
5183 {
5184 /*
5185 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5186 for MF_DELETE. We should check the parameters for all others
5187 MF_* actions also (anybody got a doc on ChangeMenu?).
5188 */
5189
5190 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
5191 {
5192 case MF_APPEND :
5193 return AppendMenuW(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
5194
5195 case MF_DELETE :
5196 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
5197
5198 case MF_CHANGE :
5199 return ModifyMenuW(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
5200
5201 case MF_REMOVE :
5202 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
5203 flags &~ MF_REMOVE);
5204
5205 default : /* MF_INSERT */
5206 return InsertMenuW(hMenu, cmd, flags, cmdInsert, lpszNewItem);
5207 };
5208 }
5209
5210 /*
5211 * @implemented
5212 */
5213 BOOL
5214 WINAPI
5215 ChangeMenuA(
5216 HMENU hMenu,
5217 UINT cmd,
5218 LPCSTR lpszNewItem,
5219 UINT cmdInsert,
5220 UINT flags)
5221 {
5222 /*
5223 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5224 for MF_DELETE. We should check the parameters for all others
5225 MF_* actions also (anybody got a doc on ChangeMenu?).
5226 */
5227
5228 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
5229 {
5230 case MF_APPEND :
5231 return AppendMenuA(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
5232
5233 case MF_DELETE :
5234 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
5235
5236 case MF_CHANGE :
5237 return ModifyMenuA(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
5238
5239 case MF_REMOVE :
5240 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
5241 flags &~ MF_REMOVE);
5242
5243 default : /* MF_INSERT */
5244 return InsertMenuA(hMenu, cmd, flags, cmdInsert, lpszNewItem);
5245 };
5246 }
5247
5248
5249
5250
5251