Sync with trunk (48237)
[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 /* Display the window */
1640
1641 SetWindowPos( MenuInfo.Wnd, HWND_TOPMOST, 0, 0, 0, 0,
1642 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
1643 UpdateWindow( MenuInfo.Wnd );
1644 return TRUE;
1645 }
1646
1647
1648 /***********************************************************************
1649 * MenuSelectItem
1650 */
1651 static void FASTCALL MenuSelectItem(HWND hwndOwner, PROSMENUINFO hmenu, UINT wIndex,
1652 BOOL sendMenuSelect, HMENU topmenu)
1653 {
1654 ROSMENUITEMINFO ItemInfo;
1655 ROSMENUINFO TopMenuInfo;
1656 HDC hdc;
1657
1658 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1659
1660 if (!hmenu || !hmenu->MenuItemCount || !hmenu->Wnd) return;
1661 if (hmenu->FocusedItem == wIndex) return;
1662 if (hmenu->Flags & MF_POPUP) hdc = GetDC(hmenu->Wnd);
1663 else hdc = GetDCEx(hmenu->Wnd, 0, DCX_CACHE | DCX_WINDOW);
1664 if (!top_popup) {
1665 top_popup = hmenu->Wnd;
1666 top_popup_hmenu = hmenu->Self;
1667 }
1668
1669 SelectObject( hdc, hMenuFont );
1670
1671 MenuInitRosMenuItemInfo(&ItemInfo);
1672
1673 /* Clear previous highlighted item */
1674 if (hmenu->FocusedItem != NO_SELECTED_ITEM)
1675 {
1676 if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->FocusedItem, &ItemInfo))
1677 {
1678 ItemInfo.fMask |= MIIM_STATE;
1679 ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
1680 MenuSetRosMenuItemInfo(hmenu->Self, hmenu->FocusedItem, &ItemInfo);
1681 }
1682 MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc, &ItemInfo,
1683 hmenu->Height, ! (hmenu->Flags & MF_POPUP),
1684 ODA_SELECT);
1685 }
1686
1687 /* Highlight new item (if any) */
1688 hmenu->FocusedItem = wIndex;
1689 MenuSetRosMenuInfo(hmenu);
1690 if (hmenu->FocusedItem != NO_SELECTED_ITEM)
1691 {
1692 if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->FocusedItem, &ItemInfo))
1693 {
1694 if (!(ItemInfo.fType & MF_SEPARATOR))
1695 {
1696 ItemInfo.fMask |= MIIM_STATE;
1697 ItemInfo.fState |= MF_HILITE;
1698 MenuSetRosMenuItemInfo(hmenu->Self, hmenu->FocusedItem, &ItemInfo);
1699 MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc,
1700 &ItemInfo, hmenu->Height, ! (hmenu->Flags & MF_POPUP),
1701 ODA_SELECT);
1702 }
1703 if (sendMenuSelect)
1704 {
1705 SendMessageW(hwndOwner, WM_MENUSELECT,
1706 MAKELONG(ItemInfo.fType & MF_POPUP ? wIndex : ItemInfo.wID,
1707 ItemInfo.fType | ItemInfo.fState | MF_MOUSESELECT |
1708 (hmenu->Flags & MF_SYSMENU)), (LPARAM) hmenu->Self);
1709 }
1710 }
1711 }
1712 else if (sendMenuSelect) {
1713 if(topmenu) {
1714 int pos;
1715 pos = MenuFindSubMenu(&topmenu, hmenu->Self);
1716 if (pos != NO_SELECTED_ITEM)
1717 {
1718 if (MenuGetRosMenuInfo(&TopMenuInfo, topmenu)
1719 && MenuGetRosMenuItemInfo(topmenu, pos, &ItemInfo))
1720 {
1721 SendMessageW(hwndOwner, WM_MENUSELECT,
1722 MAKELONG(Pos, ItemInfo.fType | ItemInfo.fState
1723 | MF_MOUSESELECT
1724 | (TopMenuInfo.Flags & MF_SYSMENU)),
1725 (LPARAM) topmenu);
1726 }
1727 }
1728 }
1729 }
1730 MenuCleanupRosMenuItemInfo(&ItemInfo);
1731 ReleaseDC(hmenu->Wnd, hdc);
1732 }
1733
1734 /***********************************************************************
1735 * MenuMoveSelection
1736 *
1737 * Moves currently selected item according to the Offset parameter.
1738 * If there is no selection then it should select the last item if
1739 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
1740 */
1741 static void FASTCALL
1742 MenuMoveSelection(HWND WndOwner, PROSMENUINFO MenuInfo, INT Offset)
1743 {
1744 INT i;
1745 ROSMENUITEMINFO ItemInfo;
1746 INT OrigPos;
1747
1748 TRACE("hwnd=%x menu=%x off=0x%04x\n", WndOwner, MenuInfo, Offset);
1749
1750 /* Prevent looping */
1751 if (0 == MenuInfo->MenuItemCount || 0 == Offset)
1752 return;
1753 else if (Offset < -1)
1754 Offset = -1;
1755 else if (Offset > 1)
1756 Offset = 1;
1757
1758 MenuInitRosMenuItemInfo(&ItemInfo);
1759
1760 OrigPos = MenuInfo->FocusedItem;
1761 if (OrigPos == NO_SELECTED_ITEM) /* NO_SELECTED_ITEM is not -1 ! */
1762 {
1763 OrigPos = 0;
1764 i = -1;
1765 }
1766 else
1767 {
1768 i = MenuInfo->FocusedItem;
1769 }
1770
1771 do
1772 {
1773 /* Step */
1774 i += Offset;
1775 /* Clip and wrap around */
1776 if (i < 0)
1777 {
1778 i = MenuInfo->MenuItemCount - 1;
1779 }
1780 else if (i >= MenuInfo->MenuItemCount)
1781 {
1782 i = 0;
1783 }
1784 /* If this is a good candidate; */
1785 if (MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo) &&
1786 0 == (ItemInfo.fType & MF_SEPARATOR))
1787 {
1788 MenuSelectItem(WndOwner, MenuInfo, i, TRUE, NULL);
1789 MenuCleanupRosMenuItemInfo(&ItemInfo);
1790 return;
1791 }
1792 } while (i != OrigPos);
1793
1794 /* Not found */
1795 MenuCleanupRosMenuItemInfo(&ItemInfo);
1796 }
1797
1798 LRESULT WINAPI PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
1799 {
1800 TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
1801
1802 switch(Message)
1803 {
1804 case WM_CREATE:
1805 {
1806 CREATESTRUCTA *cs = (CREATESTRUCTA *) lParam;
1807 SetWindowLongPtrA(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
1808 return 0;
1809 }
1810
1811 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1812 return MA_NOACTIVATE;
1813
1814 case WM_PAINT:
1815 {
1816 PAINTSTRUCT ps;
1817 BeginPaint(Wnd, &ps);
1818 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrA(Wnd, 0));
1819 EndPaint(Wnd, &ps);
1820 return 0;
1821 }
1822
1823 case WM_PRINTCLIENT:
1824 {
1825 MenuDrawPopupMenu( Wnd, (HDC)wParam,
1826 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
1827 return 0;
1828 }
1829
1830 case WM_ERASEBKGND:
1831 return 1;
1832
1833 case WM_DESTROY:
1834 /* zero out global pointer in case resident popup window was destroyed. */
1835 if (Wnd == top_popup)
1836 {
1837 top_popup = NULL;
1838 top_popup_hmenu = NULL;
1839 }
1840 break;
1841
1842 case WM_SHOWWINDOW:
1843 if (0 != wParam)
1844 {
1845 if (0 == GetWindowLongPtrA(Wnd, 0))
1846 {
1847 OutputDebugStringA("no menu to display\n");
1848 }
1849 }
1850 else
1851 {
1852 SetWindowLongPtrA(Wnd, 0, 0);
1853 }
1854 break;
1855
1856 case MM_SETMENUHANDLE:
1857 SetWindowLongPtrA(Wnd, 0, wParam);
1858 break;
1859
1860 case MM_GETMENUHANDLE:
1861 case MN_GETHMENU:
1862 return GetWindowLongPtrA(Wnd, 0);
1863
1864 default:
1865 return DefWindowProcA(Wnd, Message, wParam, lParam);
1866 }
1867 return 0;
1868 }
1869
1870 LRESULT WINAPI
1871 PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
1872 {
1873 TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
1874
1875 switch(Message)
1876 {
1877 case WM_CREATE:
1878 {
1879 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
1880 SetWindowLongPtrW(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
1881 return 0;
1882 }
1883
1884 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1885 return MA_NOACTIVATE;
1886
1887 case WM_PAINT:
1888 {
1889 PAINTSTRUCT ps;
1890 BeginPaint(Wnd, &ps);
1891 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrW(Wnd, 0));
1892 EndPaint(Wnd, &ps);
1893 return 0;
1894 }
1895
1896 case WM_PRINTCLIENT:
1897 {
1898 MenuDrawPopupMenu( Wnd, (HDC)wParam,
1899 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
1900 return 0;
1901 }
1902
1903 case WM_ERASEBKGND:
1904 return 1;
1905
1906 case WM_DESTROY:
1907 /* zero out global pointer in case resident popup window was destroyed. */
1908 if (Wnd == top_popup)
1909 {
1910 top_popup = NULL;
1911 top_popup_hmenu = NULL;
1912 }
1913 break;
1914
1915 case WM_SHOWWINDOW:
1916 if (0 != wParam)
1917 {
1918 if (0 == GetWindowLongPtrW(Wnd, 0))
1919 {
1920 OutputDebugStringA("no menu to display\n");
1921 }
1922 }
1923 else
1924 {
1925 SetWindowLongPtrW(Wnd, 0, 0);
1926 }
1927 break;
1928
1929 case MM_SETMENUHANDLE:
1930 SetWindowLongPtrW(Wnd, 0, wParam);
1931 break;
1932
1933 case MM_GETMENUHANDLE:
1934 case MN_GETHMENU:
1935 return GetWindowLongPtrW(Wnd, 0);
1936
1937 default:
1938 return DefWindowProcW(Wnd, Message, wParam, lParam);
1939 }
1940
1941 return 0;
1942 }
1943
1944 /**********************************************************************
1945 * MENU_ParseResource
1946 *
1947 * Parse a standard menu resource and add items to the menu.
1948 * Return a pointer to the end of the resource.
1949 *
1950 * NOTE: flags is equivalent to the mtOption field
1951 */
1952 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1953 {
1954 WORD flags, id = 0;
1955 HMENU hSubMenu;
1956 LPCSTR str;
1957 BOOL end = FALSE;
1958
1959 do
1960 {
1961 flags = GET_WORD(res);
1962
1963 /* remove MF_END flag before passing it to AppendMenu()! */
1964 end = (flags & MF_END);
1965 if(end) flags ^= MF_END;
1966
1967 res += sizeof(WORD);
1968 if(!(flags & MF_POPUP))
1969 {
1970 id = GET_WORD(res);
1971 res += sizeof(WORD);
1972 }
1973 str = res;
1974 if(!unicode)
1975 res += strlen(str) + 1;
1976 else
1977 res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1978 if (flags & MF_POPUP)
1979 {
1980 hSubMenu = CreatePopupMenu();
1981 if(!hSubMenu) return NULL;
1982 if(!(res = MENU_ParseResource(res, hSubMenu, unicode)))
1983 return NULL;
1984 if(!unicode)
1985 AppendMenuA(hMenu, flags, (UINT)hSubMenu, str);
1986 else
1987 AppendMenuW(hMenu, flags, (UINT)hSubMenu, (LPCWSTR)str);
1988 }
1989 else /* Not a popup */
1990 {
1991 if(!unicode)
1992 {
1993 if (*str == 0)
1994 flags = MF_SEPARATOR;
1995 }
1996 else
1997 {
1998 if (*(LPCWSTR)str == 0)
1999 flags = MF_SEPARATOR;
2000 }
2001
2002 if (flags & MF_SEPARATOR)
2003 {
2004 if (!(flags & (MF_GRAYED | MF_DISABLED)))
2005 flags |= MF_GRAYED | MF_DISABLED;
2006 }
2007
2008 if(!unicode)
2009 AppendMenuA(hMenu, flags, id, *str ? str : NULL);
2010 else
2011 AppendMenuW(hMenu, flags, id,
2012 *(LPCWSTR)str ? (LPCWSTR)str : NULL);
2013 }
2014 } while(!end);
2015 return res;
2016 }
2017
2018
2019 /**********************************************************************
2020 * MENUEX_ParseResource
2021 *
2022 * Parse an extended menu resource and add items to the menu.
2023 * Return a pointer to the end of the resource.
2024 */
2025 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2026 {
2027 WORD resinfo;
2028 do {
2029 MENUITEMINFOW mii;
2030
2031 mii.cbSize = sizeof(mii);
2032 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
2033 mii.fType = GET_DWORD(res);
2034 res += sizeof(DWORD);
2035 mii.fState = GET_DWORD(res);
2036 res += sizeof(DWORD);
2037 mii.wID = GET_DWORD(res);
2038 res += sizeof(DWORD);
2039 resinfo = GET_WORD(res);
2040 res += sizeof(WORD);
2041 /* Align the text on a word boundary. */
2042 res += (~((UINT_PTR)res - 1)) & 1;
2043 mii.dwTypeData = (LPWSTR) res;
2044 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2045 /* Align the following fields on a dword boundary. */
2046 res += (~((UINT_PTR)res - 1)) & 3;
2047
2048 TRACE("Menu item: [%08x,%08x,%04x,%04x,%S]\n",
2049 mii.fType, mii.fState, mii.wID, resinfo, mii.dwTypeData);
2050
2051 if (resinfo & 1) { /* Pop-up? */
2052 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2053 res += sizeof(DWORD);
2054 mii.hSubMenu = CreatePopupMenu();
2055 if (!mii.hSubMenu)
2056 return NULL;
2057 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2058 DestroyMenu(mii.hSubMenu);
2059 return NULL;
2060 }
2061 mii.fMask |= MIIM_SUBMENU;
2062 mii.fType |= MF_POPUP;
2063 mii.wID = (UINT) mii.hSubMenu;
2064 }
2065 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2066 {
2067 mii.fType |= MF_SEPARATOR;
2068 }
2069 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2070 } while (!(resinfo & MF_END));
2071 return res;
2072 }
2073
2074 NTSTATUS WINAPI
2075 User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength)
2076 {
2077 HMENU hmenu = LoadMenuW(User32Instance, L"SYSMENU");
2078 LRESULT Result = (LRESULT)hmenu;
2079 MENUINFO menuinfo = {0};
2080 MENUITEMINFOW info = {0};
2081
2082 // removing space for checkboxes from menu
2083 menuinfo.cbSize = sizeof(menuinfo);
2084 menuinfo.fMask = MIM_STYLE;
2085 GetMenuInfo(hmenu, &menuinfo);
2086 menuinfo.dwStyle |= MNS_NOCHECK;
2087 SetMenuInfo(hmenu, &menuinfo);
2088
2089 // adding bitmaps to menu items
2090 info.cbSize = sizeof(info);
2091 info.fMask |= MIIM_BITMAP;
2092 info.hbmpItem = HBMMENU_POPUP_MINIMIZE;
2093 SetMenuItemInfoW(hmenu, SC_MINIMIZE, FALSE, &info);
2094 info.hbmpItem = HBMMENU_POPUP_RESTORE;
2095 SetMenuItemInfoW(hmenu, SC_RESTORE, FALSE, &info);
2096 info.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
2097 SetMenuItemInfoW(hmenu, SC_MAXIMIZE, FALSE, &info);
2098 info.hbmpItem = HBMMENU_POPUP_CLOSE;
2099 SetMenuItemInfoW(hmenu, SC_CLOSE, FALSE, &info);
2100
2101 return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS));
2102 }
2103
2104
2105 BOOL
2106 MenuInit(VOID)
2107 {
2108 NONCLIENTMETRICSW ncm;
2109
2110 /* get the menu font */
2111 if(!hMenuFont || !hMenuFontBold)
2112 {
2113 ncm.cbSize = sizeof(ncm);
2114 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
2115 {
2116 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
2117 return FALSE;
2118 }
2119
2120 hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont);
2121 if(hMenuFont == NULL)
2122 {
2123 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
2124 return FALSE;
2125 }
2126
2127 ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
2128 hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont);
2129 if(hMenuFontBold == NULL)
2130 {
2131 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
2132 DeleteObject(hMenuFont);
2133 hMenuFont = NULL;
2134 return FALSE;
2135 }
2136 }
2137
2138 return TRUE;
2139 }
2140
2141 VOID
2142 MenuCleanup(VOID)
2143 {
2144 if (hMenuFont)
2145 {
2146 DeleteObject(hMenuFont);
2147 hMenuFont = NULL;
2148 }
2149
2150 if (hMenuFontBold)
2151 {
2152 DeleteObject(hMenuFontBold);
2153 hMenuFontBold = NULL;
2154 }
2155 }
2156
2157 /***********************************************************************
2158 * DrawMenuBarTemp (USER32.@)
2159 *
2160 * UNDOCUMENTED !!
2161 *
2162 * called by W98SE desk.cpl Control Panel Applet
2163 *
2164 * Not 100% sure about the param names, but close.
2165 *
2166 * @implemented
2167 */
2168 DWORD WINAPI
2169 DrawMenuBarTemp(HWND Wnd, HDC DC, LPRECT Rect, HMENU Menu, HFONT Font)
2170 {
2171 ROSMENUINFO MenuInfo;
2172 ROSMENUITEMINFO ItemInfo;
2173 UINT i;
2174 HFONT FontOld = NULL;
2175 BOOL flat_menu = FALSE;
2176
2177 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
2178
2179 if (NULL == Menu)
2180 {
2181 Menu = GetMenu(Wnd);
2182 }
2183
2184 if (NULL == Font)
2185 {
2186 Font = hMenuFont;
2187 }
2188
2189 if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
2190 {
2191 return GetSystemMetrics(SM_CYMENU);
2192 }
2193
2194 TRACE("(%x, %x, %p, %x, %x)\n", Wnd, DC, Rect, Menu, Font);
2195
2196 FontOld = SelectObject(DC, Font);
2197
2198 if (0 == MenuInfo.Height)
2199 {
2200 MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
2201 }
2202
2203 Rect->bottom = Rect->top + MenuInfo.Height;
2204
2205 FillRect(DC, Rect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
2206
2207 SelectObject(DC, GetStockObject(DC_PEN));
2208 SetDCPenColor(DC, GetSysColor(COLOR_3DFACE));
2209 MoveToEx(DC, Rect->left, Rect->bottom - 1, NULL);
2210 LineTo(DC, Rect->right, Rect->bottom - 1);
2211
2212 if (0 == MenuInfo.MenuItemCount)
2213 {
2214 SelectObject(DC, FontOld);
2215 return GetSystemMetrics(SM_CYMENU);
2216 }
2217
2218 MenuInitRosMenuItemInfo(&ItemInfo);
2219 for (i = 0; i < MenuInfo.MenuItemCount; i++)
2220 {
2221 if (MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
2222 {
2223 MenuDrawMenuItem(Wnd, &MenuInfo, Wnd, DC, &ItemInfo,
2224 MenuInfo.Height, TRUE, ODA_DRAWENTIRE);
2225 }
2226 }
2227 MenuCleanupRosMenuItemInfo(&ItemInfo);
2228
2229 SelectObject(DC, FontOld);
2230
2231 return MenuInfo.Height;
2232 }
2233
2234 /***********************************************************************
2235 * MenuShowSubPopup
2236 *
2237 * Display the sub-menu of the selected item of this menu.
2238 * Return the handle of the submenu, or menu if no submenu to display.
2239 */
2240 static HMENU FASTCALL
2241 MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags)
2242 {
2243 extern void FASTCALL NcGetSysPopupPos(HWND Wnd, RECT *Rect);
2244 RECT Rect;
2245 ROSMENUITEMINFO ItemInfo;
2246 ROSMENUINFO SubMenuInfo;
2247 HDC Dc;
2248 HMENU Ret;
2249
2250 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst);
2251
2252 if (NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2253 {
2254 return MenuInfo->Self;
2255 }
2256
2257 MenuInitRosMenuItemInfo(&ItemInfo);
2258 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2259 {
2260 MenuCleanupRosMenuItemInfo(&ItemInfo);
2261 return MenuInfo->Self;
2262 }
2263 if (0 == (ItemInfo.fType & MF_POPUP) || 0 != (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)))
2264 {
2265 MenuCleanupRosMenuItemInfo(&ItemInfo);
2266 return MenuInfo->Self;
2267 }
2268
2269 /* message must be sent before using item,
2270 because nearly everything may be changed by the application ! */
2271
2272 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2273 if (0 == (Flags & TPM_NONOTIFY))
2274 {
2275 SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu,
2276 MAKELONG(MenuInfo->FocusedItem, IS_SYSTEM_MENU(MenuInfo)));
2277 }
2278
2279 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2280 {
2281 MenuCleanupRosMenuItemInfo(&ItemInfo);
2282 return MenuInfo->Self;
2283 }
2284 Rect = ItemInfo.Rect;
2285
2286 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2287 if (0 == (ItemInfo.fState & MF_HILITE))
2288 {
2289 if (0 != (MenuInfo->Flags & MF_POPUP))
2290 {
2291 Dc = GetDC(MenuInfo->Wnd);
2292 }
2293 else
2294 {
2295 Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
2296 }
2297
2298 SelectObject(Dc, hMenuFont);
2299 ItemInfo.fMask |= MIIM_STATE;
2300 ItemInfo.fState |= MF_HILITE;
2301 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2302 MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->Height,
2303 ! (MenuInfo->Flags & MF_POPUP), ODA_DRAWENTIRE);
2304 ReleaseDC(MenuInfo->Wnd, Dc);
2305 }
2306
2307 if (0 == ItemInfo.Rect.top && 0 == ItemInfo.Rect.left
2308 && 0 == ItemInfo.Rect.bottom && 0 == ItemInfo.Rect.right)
2309 {
2310 ItemInfo.Rect = Rect;
2311 }
2312
2313 ItemInfo.fMask |= MIIM_STATE;
2314 ItemInfo.fState |= MF_MOUSESELECT;
2315 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2316
2317 if (IS_SYSTEM_MENU(MenuInfo))
2318 {
2319 MenuInitSysMenuPopup(ItemInfo.hSubMenu, GetWindowLongPtrW(MenuInfo->Wnd, GWL_STYLE),
2320 GetClassLongPtrW(MenuInfo->Wnd, GCL_STYLE), HTSYSMENU);
2321
2322 NcGetSysPopupPos(MenuInfo->Wnd, &Rect);
2323 Rect.top = Rect.bottom;
2324 Rect.right = GetSystemMetrics(SM_CXSIZE);
2325 Rect.bottom = GetSystemMetrics(SM_CYSIZE);
2326 }
2327 else
2328 {
2329 GetWindowRect(MenuInfo->Wnd, &Rect);
2330 if (0 != (MenuInfo->Flags & MF_POPUP))
2331 {
2332 Rect.left += ItemInfo.Rect.right - GetSystemMetrics(SM_CXBORDER);
2333 Rect.top += ItemInfo.Rect.top - 3;
2334 Rect.right = ItemInfo.Rect.left - ItemInfo.Rect.right + GetSystemMetrics(SM_CXBORDER);
2335 Rect.bottom = ItemInfo.Rect.top - ItemInfo.Rect.bottom - 3 - 2
2336 - GetSystemMetrics(SM_CYBORDER);
2337 }
2338 else
2339 {
2340 Rect.left += ItemInfo.Rect.left;
2341 Rect.top += ItemInfo.Rect.bottom;
2342 Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left;
2343 Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top;
2344 }
2345 }
2346
2347 MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->FocusedItem, Flags,
2348 Rect.left, Rect.top, Rect.right, Rect.bottom );
2349 if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2350 {
2351 MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT);
2352 }
2353
2354 Ret = ItemInfo.hSubMenu;
2355 MenuCleanupRosMenuItemInfo(&ItemInfo);
2356
2357 return Ret;
2358 }
2359
2360 /**********************************************************************
2361 * MENU_EndMenu
2362 *
2363 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2364 *
2365 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2366 */
2367 void MENU_EndMenu( HWND hwnd )
2368 {
2369 ROSMENUINFO MenuInfo;
2370 BOOL Ret = FALSE;
2371 if (top_popup_hmenu)
2372 Ret = MenuGetRosMenuInfo(&MenuInfo, top_popup_hmenu);
2373 if (Ret && hwnd == MenuInfo.WndOwner) EndMenu();
2374 }
2375
2376 /***********************************************************************
2377 * MenuHideSubPopups
2378 *
2379 * Hide the sub-popup menus of this menu.
2380 */
2381 static void FASTCALL
2382 MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo,
2383 BOOL SendMenuSelect, UINT wFlags)
2384 {
2385 ROSMENUINFO SubMenuInfo;
2386 ROSMENUITEMINFO ItemInfo;
2387
2388 TRACE("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect);
2389
2390 if (NULL != MenuInfo && NULL != top_popup && NO_SELECTED_ITEM != MenuInfo->FocusedItem)
2391 {
2392 MenuInitRosMenuItemInfo(&ItemInfo);
2393 ItemInfo.fMask |= MIIM_FTYPE | MIIM_STATE;
2394 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo)
2395 || 0 == (ItemInfo.fType & MF_POPUP)
2396 || 0 == (ItemInfo.fState & MF_MOUSESELECT))
2397 {
2398 MenuCleanupRosMenuItemInfo(&ItemInfo);
2399 return;
2400 }
2401 ItemInfo.fState &= ~MF_MOUSESELECT;
2402 ItemInfo.fMask |= MIIM_STATE;
2403 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2404 if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2405 {
2406 MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE, wFlags);
2407 MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL);
2408 DestroyWindow(SubMenuInfo.Wnd);
2409 SubMenuInfo.Wnd = NULL;
2410 MenuSetRosMenuInfo(&SubMenuInfo);
2411
2412 if (!(wFlags & TPM_NONOTIFY))
2413 SendMessageW( WndOwner, WM_UNINITMENUPOPUP, (WPARAM)ItemInfo.hSubMenu,
2414 MAKELPARAM(0, IS_SYSTEM_MENU(&SubMenuInfo)) );
2415 }
2416 }
2417 }
2418
2419 /***********************************************************************
2420 * MenuSwitchTracking
2421 *
2422 * Helper function for menu navigation routines.
2423 */
2424 static void FASTCALL
2425 MenuSwitchTracking(MTRACKER* Mt, PROSMENUINFO PtMenuInfo, UINT Index, UINT wFlags)
2426 {
2427 ROSMENUINFO TopMenuInfo;
2428
2429 TRACE("%x menu=%x 0x%04x\n", Mt, PtMenuInfo->Self, Index);
2430
2431 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu) &&
2432 Mt->TopMenu != PtMenuInfo->Self &&
2433 0 == ((PtMenuInfo->Flags | TopMenuInfo.Flags) & MF_POPUP))
2434 {
2435 /* both are top level menus (system and menu-bar) */
2436 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
2437 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL);
2438 Mt->TopMenu = PtMenuInfo->Self;
2439 }
2440 else
2441 {
2442 MenuHideSubPopups(Mt->OwnerWnd, PtMenuInfo, FALSE, wFlags);
2443 }
2444
2445 MenuSelectItem(Mt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL);
2446 }
2447
2448 /***********************************************************************
2449 * MenuExecFocusedItem
2450 *
2451 * Execute a menu item (for instance when user pressed Enter).
2452 * Return the wID of the executed item. Otherwise, -1 indicating
2453 * that no menu item was executed, -2 if a popup is shown;
2454 * Have to receive the flags for the TrackPopupMenu options to avoid
2455 * sending unwanted message.
2456 *
2457 */
2458 static INT FASTCALL
2459 MenuExecFocusedItem(MTRACKER *Mt, PROSMENUINFO MenuInfo, UINT Flags)
2460 {
2461 ROSMENUITEMINFO ItemInfo;
2462 UINT wID;
2463
2464 TRACE("%p menu=%p\n", Mt, MenuInfo);
2465
2466 if (0 == MenuInfo->MenuItemCount || NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2467 {
2468 return -1;
2469 }
2470
2471 MenuInitRosMenuItemInfo(&ItemInfo);
2472 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2473 {
2474 MenuCleanupRosMenuItemInfo(&ItemInfo);
2475 return -1;
2476 }
2477
2478 TRACE("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu);
2479
2480 if (0 == (ItemInfo.fType & MF_POPUP))
2481 {
2482 if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))
2483 && 0 == (ItemInfo.fType & MF_SEPARATOR))
2484 {
2485 /* If TPM_RETURNCMD is set you return the id, but
2486 do not send a message to the owner */
2487 if (0 == (Flags & TPM_RETURNCMD))
2488 {
2489 if (0 != (MenuInfo->Flags & MF_SYSMENU))
2490 {
2491 PostMessageW(Mt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID,
2492 MAKELPARAM((SHORT) Mt->Pt.x, (SHORT) Mt->Pt.y));
2493 }
2494 else
2495 {
2496 if (MenuInfo->dwStyle & MNS_NOTIFYBYPOS)
2497 PostMessageW(Mt->OwnerWnd, WM_MENUCOMMAND,
2498 MenuInfo->FocusedItem,
2499 (LPARAM)MenuInfo->Self);
2500 else
2501 PostMessageW(Mt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0);
2502 }
2503 }
2504 wID = ItemInfo.wID;
2505 MenuCleanupRosMenuItemInfo(&ItemInfo);
2506 return wID;
2507 }
2508 }
2509 else
2510 {
2511 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, MenuInfo, TRUE, Flags);
2512 return -2;
2513 }
2514
2515 return -1;
2516 }
2517
2518 /***********************************************************************
2519 * MenuButtonDown
2520 *
2521 * Return TRUE if we can go on with menu tracking.
2522 */
2523 static BOOL FASTCALL
2524 MenuButtonDown(MTRACKER* Mt, HMENU PtMenu, UINT Flags)
2525 {
2526 int Index;
2527 ROSMENUINFO MenuInfo;
2528 ROSMENUITEMINFO Item;
2529
2530 TRACE("%x PtMenu=%p\n", Mt, PtMenu);
2531
2532 if (NULL != PtMenu)
2533 {
2534 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2535 {
2536 return FALSE;
2537 }
2538 if (IS_SYSTEM_MENU(&MenuInfo))
2539 {
2540 Index = 0;
2541 }
2542 else
2543 {
2544 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2545 }
2546 MenuInitRosMenuItemInfo(&Item);
2547 if (NO_SELECTED_ITEM == Index || ! MenuGetRosMenuItemInfo(PtMenu, Index, &Item))
2548 {
2549 MenuCleanupRosMenuItemInfo(&Item);
2550 return FALSE;
2551 }
2552
2553 if (!(Item.fType & MF_SEPARATOR) &&
2554 !(Item.fState & (MFS_DISABLED | MFS_GRAYED)) )
2555 {
2556 if (MenuInfo.FocusedItem != Index)
2557 {
2558 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
2559 }
2560
2561 /* If the popup menu is not already "popped" */
2562 if (0 == (Item.fState & MF_MOUSESELECT))
2563 {
2564 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2565 }
2566 }
2567
2568 MenuCleanupRosMenuItemInfo(&Item);
2569
2570 return TRUE;
2571 }
2572
2573 /* else the click was on the menu bar, finish the tracking */
2574
2575 return FALSE;
2576 }
2577
2578 /***********************************************************************
2579 * MenuButtonUp
2580 *
2581 * Return the value of MenuExecFocusedItem if
2582 * the selected item was not a popup. Else open the popup.
2583 * A -1 return value indicates that we go on with menu tracking.
2584 *
2585 */
2586 static INT FASTCALL
2587 MenuButtonUp(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2588 {
2589 UINT Id;
2590 ROSMENUINFO MenuInfo;
2591 ROSMENUITEMINFO ItemInfo;
2592
2593 TRACE("%p hmenu=%x\n", Mt, PtMenu);
2594
2595 if (NULL != PtMenu)
2596 {
2597 Id = 0;
2598 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2599 {
2600 return -1;
2601 }
2602
2603 if (! IS_SYSTEM_MENU(&MenuInfo))
2604 {
2605 Id = NtUserMenuItemFromPoint(Mt->OwnerWnd, MenuInfo.Self, Mt->Pt.x, Mt->Pt.y);
2606 }
2607 MenuInitRosMenuItemInfo(&ItemInfo);
2608 if (0 <= Id && MenuGetRosMenuItemInfo(MenuInfo.Self, Id, &ItemInfo) &&
2609 MenuInfo.FocusedItem == Id)
2610 {
2611 if (0 == (ItemInfo.fType & MF_POPUP))
2612 {
2613 INT ExecutedMenuId = MenuExecFocusedItem(Mt, &MenuInfo, Flags);
2614 MenuCleanupRosMenuItemInfo(&ItemInfo);
2615 return (ExecutedMenuId < 0) ? -1 : ExecutedMenuId;
2616 }
2617 MenuCleanupRosMenuItemInfo(&ItemInfo);
2618
2619 /* If we are dealing with the top-level menu */
2620 /* and this is a click on an already "popped" item: */
2621 /* Stop the menu tracking and close the opened submenus */
2622 if (Mt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide)
2623 {
2624 MenuCleanupRosMenuItemInfo(&ItemInfo);
2625 return 0;
2626 }
2627 }
2628 MenuCleanupRosMenuItemInfo(&ItemInfo);
2629 MenuInfo.TimeToHide = TRUE;
2630 MenuSetRosMenuInfo(&MenuInfo);
2631 }
2632
2633 return -1;
2634 }
2635
2636 /***********************************************************************
2637 * MenuPtMenu
2638 *
2639 * Walks menu chain trying to find a menu pt maps to.
2640 */
2641 static HMENU FASTCALL
2642 MenuPtMenu(HMENU Menu, POINT Pt)
2643 {
2644 extern LRESULT DefWndNCHitTest(HWND hWnd, POINT Point);
2645 ROSMENUINFO MenuInfo;
2646 ROSMENUITEMINFO ItemInfo;
2647 HMENU Ret = NULL;
2648 INT Ht;
2649
2650 if (! MenuGetRosMenuInfo(&MenuInfo, Menu))
2651 {
2652 return NULL;
2653 }
2654
2655 /* try subpopup first (if any) */
2656 if (NO_SELECTED_ITEM != MenuInfo.FocusedItem)
2657 {
2658 MenuInitRosMenuItemInfo(&ItemInfo);
2659 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo) &&
2660 0 != (ItemInfo.fType & MF_POPUP) &&
2661 0 != (ItemInfo.fState & MF_MOUSESELECT))
2662 {
2663 Ret = MenuPtMenu(ItemInfo.hSubMenu, Pt);
2664 if (NULL != Ret)
2665 {
2666 MenuCleanupRosMenuItemInfo(&ItemInfo);
2667 return Ret;
2668 }
2669 }
2670 MenuCleanupRosMenuItemInfo(&ItemInfo);
2671 }
2672
2673 /* check the current window (avoiding WM_HITTEST) */
2674 Ht = DefWndNCHitTest(MenuInfo.Wnd, Pt);
2675 if (0 != (MenuInfo.Flags & MF_POPUP))
2676 {
2677 if (HTNOWHERE != Ht && HTERROR != Ht)
2678 {
2679 Ret = Menu;
2680 }
2681 }
2682 else if (HTSYSMENU == Ht)
2683 {
2684 Ret = NtUserGetSystemMenu(MenuInfo.Wnd, FALSE);
2685 }
2686 else if (HTMENU == Ht)
2687 {
2688 Ret = GetMenu(MenuInfo.Wnd);
2689 }
2690
2691 return Ret;
2692 }
2693
2694 /***********************************************************************
2695 * MenuMouseMove
2696 *
2697 * Return TRUE if we can go on with menu tracking.
2698 */
2699 static BOOL FASTCALL
2700 MenuMouseMove(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2701 {
2702 UINT Index;
2703 ROSMENUINFO MenuInfo;
2704 ROSMENUITEMINFO ItemInfo;
2705
2706 if (NULL != PtMenu)
2707 {
2708 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2709 {
2710 return TRUE;
2711 }
2712 if (IS_SYSTEM_MENU(&MenuInfo))
2713 {
2714 Index = 0;
2715 }
2716 else
2717 {
2718 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2719 }
2720 }
2721 else
2722 {
2723 Index = NO_SELECTED_ITEM;
2724 }
2725
2726 if (NO_SELECTED_ITEM == Index)
2727 {
2728 if (Mt->CurrentMenu == MenuInfo.Self ||
2729 MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
2730 {
2731 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
2732 TRUE, Mt->TopMenu);
2733 }
2734 }
2735 else if (MenuInfo.FocusedItem != Index)
2736 {
2737 MenuInitRosMenuItemInfo(&ItemInfo);
2738 if (MenuGetRosMenuItemInfo(MenuInfo.Self, Index, &ItemInfo) &&
2739 !(ItemInfo.fType & MF_SEPARATOR))
2740 {
2741 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
2742 if (!(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED)))
2743 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2744 }
2745 MenuCleanupRosMenuItemInfo(&ItemInfo);
2746 }
2747
2748 return TRUE;
2749 }
2750
2751 /***********************************************************************
2752 * MenuGetSubPopup
2753 *
2754 * Return the handle of the selected sub-popup menu (if any).
2755 */
2756 static HMENU FASTCALL
2757 MenuGetSubPopup(HMENU Menu)
2758 {
2759 ROSMENUINFO MenuInfo;
2760 ROSMENUITEMINFO ItemInfo;
2761
2762 if (! MenuGetRosMenuInfo(&MenuInfo, Menu)
2763 || NO_SELECTED_ITEM == MenuInfo.FocusedItem)
2764 {
2765 return NULL;
2766 }
2767
2768 MenuInitRosMenuItemInfo(&ItemInfo);
2769 if (! MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
2770 {
2771 MenuCleanupRosMenuItemInfo(&ItemInfo);
2772 return NULL;
2773 }
2774 if (0 != (ItemInfo.fType & MF_POPUP) && 0 != (ItemInfo.fState & MF_MOUSESELECT))
2775 {
2776 MenuCleanupRosMenuItemInfo(&ItemInfo);
2777 return ItemInfo.hSubMenu;
2778 }
2779
2780 MenuCleanupRosMenuItemInfo(&ItemInfo);
2781 return NULL;
2782 }
2783
2784 /***********************************************************************
2785 * MenuDoNextMenu
2786 *
2787 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2788 */
2789 static LRESULT FASTCALL
2790 MenuDoNextMenu(MTRACKER* Mt, UINT Vk, UINT wFlags)
2791 {
2792 ROSMENUINFO TopMenuInfo;
2793 ROSMENUINFO MenuInfo;
2794
2795 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2796 {
2797 return (LRESULT) FALSE;
2798 }
2799
2800 if ((VK_LEFT == Vk && 0 == TopMenuInfo.FocusedItem)
2801 || (VK_RIGHT == Vk && TopMenuInfo.FocusedItem == TopMenuInfo.MenuItemCount - 1))
2802 {
2803 MDINEXTMENU NextMenu;
2804 HMENU NewMenu;
2805 HWND NewWnd;
2806 UINT Id = 0;
2807
2808 NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(Mt->TopMenu, 0) : Mt->TopMenu;
2809 NextMenu.hmenuNext = NULL;
2810 NextMenu.hwndNext = NULL;
2811 SendMessageW(Mt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
2812
2813 TRACE("%p [%p] -> %p [%p]\n",
2814 Mt->CurrentMenu, Mt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
2815
2816 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
2817 {
2818 DWORD Style = GetWindowLongPtrW(Mt->OwnerWnd, GWL_STYLE);
2819 NewWnd = Mt->OwnerWnd;
2820 if (IS_SYSTEM_MENU(&TopMenuInfo))
2821 {
2822 /* switch to the menu bar */
2823
2824 if (0 != (Style & WS_CHILD)
2825 || NULL == (NewMenu = GetMenu(NewWnd)))
2826 {
2827 return FALSE;
2828 }
2829
2830 if (VK_LEFT == Vk)
2831 {
2832 if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu))
2833 {
2834 return FALSE;
2835 }
2836 Id = MenuInfo.MenuItemCount - 1;
2837 }
2838 }
2839 else if (0 != (Style & WS_SYSMENU))
2840 {
2841 /* switch to the system menu */
2842 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2843 }
2844 else
2845 {
2846 return FALSE;
2847 }
2848 }
2849 else /* application returned a new menu to switch to */
2850 {
2851 NewMenu = NextMenu.hmenuNext;
2852 NewWnd = NextMenu.hwndNext;
2853
2854 if (IsMenu(NewMenu) && IsWindow(NewWnd))
2855 {
2856 DWORD Style = GetWindowLongPtrW(NewWnd, GWL_STYLE);
2857
2858 if (0 != (Style & WS_SYSMENU)
2859 && GetSystemMenu(NewWnd, FALSE) == NewMenu)
2860 {
2861 /* get the real system menu */
2862 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2863 }
2864 else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu)
2865 {
2866 /* FIXME: Not sure what to do here;
2867 * perhaps try to track NewMenu as a popup? */
2868
2869 WARN(" -- got confused.\n");
2870 return FALSE;
2871 }
2872 }
2873 else
2874 {
2875 return FALSE;
2876 }
2877 }
2878
2879 if (NewMenu != Mt->TopMenu)
2880 {
2881 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM,
2882 FALSE, 0 );
2883 if (Mt->CurrentMenu != Mt->TopMenu)
2884 {
2885 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
2886 }
2887 }
2888
2889 if (NewWnd != Mt->OwnerWnd)
2890 {
2891 Mt->OwnerWnd = NewWnd;
2892 SetCapture(Mt->OwnerWnd);
2893 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, Mt->OwnerWnd);
2894 }
2895
2896 Mt->TopMenu = Mt->CurrentMenu = NewMenu; /* all subpopups are hidden */
2897 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2898 {
2899 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0);
2900 }
2901
2902 return TRUE;
2903 }
2904
2905 return FALSE;
2906 }
2907
2908 /***********************************************************************
2909 * MenuSuspendPopup
2910 *
2911 * The idea is not to show the popup if the next input message is
2912 * going to hide it anyway.
2913 */
2914 static BOOL FASTCALL
2915 MenuSuspendPopup(MTRACKER* Mt, UINT Message)
2916 {
2917 MSG Msg;
2918
2919 Msg.hwnd = Mt->OwnerWnd;
2920
2921 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2922 Mt->TrackFlags |= TF_SKIPREMOVE;
2923
2924 switch (Message)
2925 {
2926 case WM_KEYDOWN:
2927 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2928 if (WM_KEYUP == Msg.message || WM_PAINT == Msg.message)
2929 {
2930 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2931 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2932 if (WM_KEYDOWN == Msg.message
2933 && (VK_LEFT == Msg.wParam || VK_RIGHT == Msg.wParam))
2934 {
2935 Mt->TrackFlags |= TF_SUSPENDPOPUP;
2936 return TRUE;
2937 }
2938 }
2939 break;
2940 }
2941
2942 /* failures go through this */
2943 Mt->TrackFlags &= ~TF_SUSPENDPOPUP;
2944
2945 return FALSE;
2946 }
2947
2948 /***********************************************************************
2949 * MenuKeyEscape
2950 *
2951 * Handle a VK_ESCAPE key event in a menu.
2952 */
2953 static BOOL FASTCALL
2954 MenuKeyEscape(MTRACKER *Mt, UINT Flags)
2955 {
2956 BOOL EndMenu = TRUE;
2957 ROSMENUINFO MenuInfo;
2958 HMENU MenuTmp, MenuPrev;
2959
2960 if (Mt->CurrentMenu != Mt->TopMenu)
2961 {
2962 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)
2963 && 0 != (MenuInfo.Flags & MF_POPUP))
2964 {
2965 MenuPrev = MenuTmp = Mt->TopMenu;
2966
2967 /* close topmost popup */
2968 while (MenuTmp != Mt->CurrentMenu)
2969 {
2970 MenuPrev = MenuTmp;
2971 MenuTmp = MenuGetSubPopup(MenuPrev);
2972 }
2973
2974 if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev))
2975 {
2976 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, TRUE, Flags);
2977 }
2978 Mt->CurrentMenu = MenuPrev;
2979 EndMenu = FALSE;
2980 }
2981 }
2982
2983 return EndMenu;
2984 }
2985
2986 /***********************************************************************
2987 * MenuKeyLeft
2988 *
2989 * Handle a VK_LEFT key event in a menu.
2990 */
2991 static void FASTCALL
2992 MenuKeyLeft(MTRACKER* Mt, UINT Flags)
2993 {
2994 ROSMENUINFO MenuInfo;
2995 ROSMENUINFO TopMenuInfo;
2996 ROSMENUINFO PrevMenuInfo;
2997 HMENU MenuTmp, MenuPrev;
2998 UINT PrevCol;
2999
3000 MenuPrev = MenuTmp = Mt->TopMenu;
3001
3002 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3003 {
3004 return;
3005 }
3006
3007 /* Try to move 1 column left (if possible) */
3008 if (NO_SELECTED_ITEM != (PrevCol = MenuGetStartOfPrevColumn(&MenuInfo)))
3009 {
3010 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3011 {
3012 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0);
3013 }
3014 return;
3015 }
3016
3017 /* close topmost popup */
3018 while (MenuTmp != Mt->CurrentMenu)
3019 {
3020 MenuPrev = MenuTmp;
3021 MenuTmp = MenuGetSubPopup(MenuPrev);
3022 }
3023
3024 if (! MenuGetRosMenuInfo(&PrevMenuInfo, MenuPrev))
3025 {
3026 return;
3027 }
3028 MenuHideSubPopups(Mt->OwnerWnd, &PrevMenuInfo, TRUE, Flags);
3029 Mt->CurrentMenu = MenuPrev;
3030
3031 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3032 {
3033 return;
3034 }
3035 if ((MenuPrev == Mt->TopMenu) && 0 == (TopMenuInfo.Flags & MF_POPUP))
3036 {
3037 /* move menu bar selection if no more popups are left */
3038
3039 if (! MenuDoNextMenu(Mt, VK_LEFT, Flags))
3040 {
3041 MenuMoveSelection(Mt->OwnerWnd, &TopMenuInfo, ITEM_PREV);
3042 }
3043
3044 if (MenuPrev != MenuTmp || 0 != (Mt->TrackFlags & TF_SUSPENDPOPUP))
3045 {
3046 /* A sublevel menu was displayed - display the next one
3047 * unless there is another displacement coming up */
3048
3049 if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
3050 && MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3051 {
3052 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &TopMenuInfo,
3053 TRUE, Flags);
3054 }
3055 }
3056 }
3057 }
3058
3059 /***********************************************************************
3060 * MenuKeyRight
3061 *
3062 * Handle a VK_RIGHT key event in a menu.
3063 */
3064 static void FASTCALL MenuKeyRight(MTRACKER *Mt, UINT Flags)
3065 {
3066 HMENU hmenutmp;
3067 ROSMENUINFO MenuInfo;
3068 ROSMENUINFO CurrentMenuInfo;
3069 UINT NextCol;
3070
3071 TRACE("MenuKeyRight called, cur %p, top %p.\n",
3072 Mt->CurrentMenu, Mt->TopMenu);
3073
3074 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu)) return;
3075 if ((MenuInfo.Flags & MF_POPUP) || (Mt->CurrentMenu != Mt->TopMenu))
3076 {
3077 /* If already displaying a popup, try to display sub-popup */
3078
3079 hmenutmp = Mt->CurrentMenu;
3080 if (MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
3081 {
3082 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &CurrentMenuInfo, TRUE, Flags);
3083 }
3084
3085 /* if subpopup was displayed then we are done */
3086 if (hmenutmp != Mt->CurrentMenu) return;
3087 }
3088
3089 if (! MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
3090 {
3091 return;
3092 }
3093
3094 /* Check to see if there's another column */
3095 if (NO_SELECTED_ITEM != (NextCol = MenuGetStartOfNextColumn(&CurrentMenuInfo)))
3096 {
3097 TRACE("Going to %d.\n", NextCol);
3098 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3099 {
3100 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NextCol, TRUE, 0);
3101 }
3102 return;
3103 }
3104
3105 if (0 == (MenuInfo.Flags & MF_POPUP)) /* menu bar tracking */
3106 {
3107 if (Mt->CurrentMenu != Mt->TopMenu)
3108 {
3109 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
3110 hmenutmp = Mt->CurrentMenu = Mt->TopMenu;
3111 }
3112 else
3113 {
3114 hmenutmp = NULL;
3115 }
3116
3117 /* try to move to the next item */
3118 if ( !MenuDoNextMenu(Mt, VK_RIGHT, Flags))
3119 MenuMoveSelection(Mt->OwnerWnd, &MenuInfo, ITEM_NEXT);
3120
3121 if ( hmenutmp || Mt->TrackFlags & TF_SUSPENDPOPUP )
3122 {
3123 if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
3124 && MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu))
3125 {
3126 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo,
3127 TRUE, Flags);
3128 }
3129 }
3130 }
3131 }
3132
3133 /***********************************************************************
3134 * MenuTrackMenu
3135 *
3136 * Menu tracking code.
3137 */
3138 static INT FASTCALL MenuTrackMenu(HMENU hmenu, UINT wFlags, INT x, INT y,
3139 HWND hwnd, const RECT *lprect )
3140 {
3141 MSG msg;
3142 ROSMENUINFO MenuInfo;
3143 ROSMENUITEMINFO ItemInfo;
3144 BOOL fRemove;
3145 INT executedMenuId = -1;
3146 MTRACKER mt;
3147 BOOL enterIdleSent = FALSE;
3148
3149 mt.TrackFlags = 0;
3150 mt.CurrentMenu = hmenu;
3151 mt.TopMenu = hmenu;
3152 mt.OwnerWnd = hwnd;
3153 mt.Pt.x = x;
3154 mt.Pt.y = y;
3155
3156 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n",
3157 hmenu, wFlags, x, y, hwnd, lprect ? lprect->left : 0, lprect ? lprect->top : 0,
3158 lprect ? lprect->right : 0, lprect ? lprect->bottom : 0);
3159
3160 if (!IsMenu(hmenu))
3161 {
3162 WARN("Invalid menu handle %p\n", hmenu);
3163 SetLastError( ERROR_INVALID_MENU_HANDLE );
3164 return FALSE;
3165 }
3166
3167 fEndMenu = FALSE;
3168 if (! MenuGetRosMenuInfo(&MenuInfo, hmenu))
3169 {
3170 return FALSE;
3171 }
3172
3173 if (wFlags & TPM_BUTTONDOWN)
3174 {
3175 /* Get the result in order to start the tracking or not */
3176 fRemove = MenuButtonDown( &mt, hmenu, wFlags );
3177 fEndMenu = !fRemove;
3178 }
3179
3180 SetCapture(mt.OwnerWnd);
3181 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, mt.OwnerWnd);
3182
3183 ERR("MenuTrackMenu 1\n");
3184 while (! fEndMenu)
3185 {
3186 PVOID menu = ValidateHandle(mt.CurrentMenu, VALIDATE_TYPE_MENU);
3187 if (!menu) /* sometimes happens if I do a window manager close */
3188 break;
3189
3190 /* we have to keep the message in the queue until it's
3191 * clear that menu loop is not over yet. */
3192
3193 for (;;)
3194 {
3195 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3196 {
3197 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3198 /* remove the message from the queue */
3199 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3200 }
3201 else
3202 {
3203 if (!enterIdleSent)
3204 {
3205 HWND win = MenuInfo.Flags & MF_POPUP ? MenuInfo.Wnd : NULL;
3206 enterIdleSent = TRUE;
3207 SendMessageW( mt.OwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM) win);
3208 }
3209 WaitMessage();
3210 }
3211 }
3212
3213 /* check if EndMenu() tried to cancel us, by posting this message */
3214 if (msg.message == WM_CANCELMODE)
3215 {
3216 /* we are now out of the loop */
3217 fEndMenu = TRUE;
3218
3219 /* remove the message from the queue */
3220 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3221
3222 /* break out of internal loop, ala ESCAPE */
3223 break;
3224 }
3225
3226 TranslateMessage( &msg );
3227 mt.Pt = msg.pt;
3228
3229 if ( (msg.hwnd == MenuInfo.Wnd) || (msg.message!=WM_TIMER) )
3230 enterIdleSent=FALSE;
3231
3232 fRemove = FALSE;
3233 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3234 {
3235 /*
3236 * Use the mouse coordinates in lParam instead of those in the MSG
3237 * struct to properly handle synthetic messages. They are already
3238 * in screen coordinates.
3239 */
3240 mt.Pt.x = (short)LOWORD(msg.lParam);
3241 mt.Pt.y = (short)HIWORD(msg.lParam);
3242
3243 /* Find a menu for this mouse event */
3244 hmenu = MenuPtMenu(mt.TopMenu, mt.Pt);
3245
3246 switch(msg.message)
3247 {
3248 /* no WM_NC... messages in captured state */
3249
3250 case WM_RBUTTONDBLCLK:
3251 case WM_RBUTTONDOWN:
3252 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3253 /* fall through */
3254 case WM_LBUTTONDBLCLK:
3255 case WM_LBUTTONDOWN:
3256 /* If the message belongs to the menu, removes it from the queue */
3257 /* Else, end menu tracking */
3258 fRemove = MenuButtonDown(&mt, hmenu, wFlags);
3259 fEndMenu = !fRemove;
3260 break;
3261
3262 case WM_RBUTTONUP:
3263 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3264 /* fall through */
3265 case WM_LBUTTONUP:
3266 /* Check if a menu was selected by the mouse */
3267 if (hmenu)
3268 {
3269 executedMenuId = MenuButtonUp( &mt, hmenu, wFlags);
3270 TRACE("executedMenuId %d\n", executedMenuId);
3271
3272 /* End the loop if executedMenuId is an item ID */
3273 /* or if the job was done (executedMenuId = 0). */
3274 fEndMenu = fRemove = (executedMenuId != -1);
3275 }
3276 /* No menu was selected by the mouse */
3277 /* if the function was called by TrackPopupMenu, continue
3278 with the menu tracking. If not, stop it */
3279 else
3280 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3281
3282 break;
3283
3284 case WM_MOUSEMOVE:
3285 /* the selected menu item must be changed every time */
3286 /* the mouse moves. */
3287
3288 if (hmenu)
3289 fEndMenu |= !MenuMouseMove( &mt, hmenu, wFlags );
3290
3291 } /* switch(msg.message) - mouse */
3292 }
3293 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3294 {
3295 fRemove = TRUE; /* Keyboard messages are always removed */
3296 switch(msg.message)
3297 {
3298 case WM_KEYDOWN:
3299 case WM_SYSKEYDOWN:
3300 switch(msg.wParam)
3301 {
3302 case VK_MENU:
3303 case VK_F10:
3304 fEndMenu = TRUE;
3305 break;
3306
3307 case VK_HOME:
3308 case VK_END:
3309 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3310 {
3311 MenuSelectItem(mt.OwnerWnd, &MenuInfo,
3312 NO_SELECTED_ITEM, FALSE, 0 );
3313 MenuMoveSelection(mt.OwnerWnd, &MenuInfo,
3314 VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV);
3315 }
3316 break;
3317
3318 case VK_UP:
3319 case VK_DOWN: /* If on menu bar, pull-down the menu */
3320 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3321 {
3322 if (!(MenuInfo.Flags & MF_POPUP))
3323 {
3324 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
3325 mt.CurrentMenu = MenuShowSubPopup(mt.OwnerWnd, &MenuInfo, TRUE, wFlags);
3326 }
3327 else /* otherwise try to move selection */
3328 MenuMoveSelection(mt.OwnerWnd, &MenuInfo,
3329 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3330 }
3331 break;
3332
3333 case VK_LEFT:
3334 MenuKeyLeft( &mt, wFlags );
3335 break;
3336
3337 case VK_RIGHT:
3338 MenuKeyRight( &mt, wFlags );
3339 break;
3340
3341 case VK_ESCAPE:
3342 fEndMenu = MenuKeyEscape(&mt, wFlags);
3343 break;
3344
3345 case VK_F1:
3346 {
3347 HELPINFO hi;
3348 hi.cbSize = sizeof(HELPINFO);
3349 hi.iContextType = HELPINFO_MENUITEM;
3350 if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
3351 {
3352 if (MenuInfo.FocusedItem == NO_SELECTED_ITEM)
3353 hi.iCtrlId = 0;
3354 else
3355 {
3356 MenuInitRosMenuItemInfo(&ItemInfo);
3357 if (MenuGetRosMenuItemInfo(MenuInfo.Self,
3358 MenuInfo.FocusedItem,
3359 &ItemInfo))
3360 {
3361 hi.iCtrlId = ItemInfo.wID;
3362 }
3363 else
3364 {
3365 hi.iCtrlId = 0;
3366 }
3367 MenuCleanupRosMenuItemInfo(&ItemInfo);
3368 }
3369 }
3370 hi.hItemHandle = hmenu;
3371 hi.dwContextId = MenuInfo.dwContextHelpID;
3372 hi.MousePos = msg.pt;
3373 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3374 break;
3375 }
3376
3377 default:
3378 break;
3379 }
3380 break; /* WM_KEYDOWN */
3381
3382 case WM_CHAR:
3383 case WM_SYSCHAR:
3384 {
3385 UINT pos;
3386
3387 if (! MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu)) break;
3388 if (msg.wParam == L'\r' || msg.wParam == L' ')
3389 {
3390 executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags);
3391 fEndMenu = (executedMenuId != -2);
3392 break;
3393 }
3394
3395 /* Hack to avoid control chars. */
3396 /* We will find a better way real soon... */
3397 if (msg.wParam < 32) break;
3398
3399 pos = MenuFindItemByKey(mt.OwnerWnd, &MenuInfo,
3400 LOWORD(msg.wParam), FALSE);
3401 if (pos == (UINT)-2) fEndMenu = TRUE;
3402 else if (pos == (UINT)-1) MessageBeep(0);
3403 else
3404 {
3405 MenuSelectItem(mt.OwnerWnd, &MenuInfo, pos,
3406 TRUE, 0);
3407 executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags);
3408 fEndMenu = (executedMenuId != -2);
3409 }
3410 }
3411 break;
3412 } /* switch(msg.message) - kbd */
3413 }
3414 else
3415 {
3416 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3417 DispatchMessageW( &msg );
3418 continue;
3419 }
3420
3421 if (!fEndMenu) fRemove = TRUE;
3422
3423 /* finally remove message from the queue */
3424
3425 if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) )
3426 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3427 else mt.TrackFlags &= ~TF_SKIPREMOVE;
3428 }
3429 ERR("MenuTrackMenu 2\n");
3430
3431 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, NULL);
3432 SetCapture(NULL); /* release the capture */
3433
3434 /* If dropdown is still painted and the close box is clicked on
3435 then the menu will be destroyed as part of the DispatchMessage above.
3436 This will then invalidate the menu handle in mt.hTopMenu. We should
3437 check for this first. */
3438 if( IsMenu( mt.TopMenu ) )
3439 {
3440 if (IsWindow(mt.OwnerWnd))
3441 {
3442 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
3443 {
3444 MenuHideSubPopups(mt.OwnerWnd, &MenuInfo, FALSE, wFlags);
3445
3446 if (MenuInfo.Flags & MF_POPUP)
3447 {
3448 DestroyWindow(MenuInfo.Wnd);
3449 MenuInfo.Wnd = NULL;
3450
3451 if (!(MenuInfo.Flags & TPM_NONOTIFY))
3452 SendMessageW( mt.OwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.TopMenu,
3453 MAKELPARAM(0, IS_SYSTEM_MENU(&MenuInfo)) );
3454
3455 }
3456 MenuSelectItem( mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, 0 );
3457 }
3458
3459 SendMessageW( mt.OwnerWnd, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0 );
3460 }
3461
3462 /* Reset the variable for hiding menu */
3463 if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
3464 {
3465 MenuInfo.TimeToHide = FALSE;
3466 MenuSetRosMenuInfo(&MenuInfo);
3467 }
3468 }
3469
3470 /* The return value is only used by TrackPopupMenu */
3471 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3472 if (executedMenuId == -1) executedMenuId = 0;
3473 return executedMenuId;
3474 }
3475
3476 /***********************************************************************
3477 * MenuInitTracking
3478 */
3479 static BOOL FASTCALL MenuInitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3480 {
3481 ROSMENUINFO MenuInfo;
3482
3483 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3484
3485 HideCaret(0);
3486
3487 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3488 if (!(wFlags & TPM_NONOTIFY))
3489 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3490
3491 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3492
3493 MenuGetRosMenuInfo(&MenuInfo, hMenu);
3494
3495 if (!(wFlags & TPM_NONOTIFY))
3496 {
3497 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3498 /* If an app changed/recreated menu bar entries in WM_INITMENU
3499 * menu sizes will be recalculated once the menu created/shown.
3500 */
3501
3502 if (!MenuInfo.Height)
3503 {
3504 /* app changed/recreated menu bar entries in WM_INITMENU
3505 Recalculate menu sizes else clicks will not work */
3506 SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
3507 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
3508
3509 }
3510 }
3511
3512 /* This makes the menus of applications built with Delphi work.
3513 * It also enables menus to be displayed in more than one window,
3514 * but there are some bugs left that need to be fixed in this case.
3515 */
3516 if(MenuInfo.Self == hMenu)
3517 {
3518 MenuInfo.Wnd = hWnd;
3519 MenuSetRosMenuInfo(&MenuInfo);
3520 }
3521
3522 return TRUE;
3523 }
3524 /***********************************************************************
3525 * MenuExitTracking
3526 */
3527 static BOOL FASTCALL MenuExitTracking(HWND hWnd, BOOL bPopup)
3528 {
3529 TRACE("hwnd=%p\n", hWnd);
3530
3531 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3532 ShowCaret(0);
3533 top_popup = 0;
3534 top_popup_hmenu = NULL;
3535 return TRUE;
3536 }
3537
3538 /***********************************************************************
3539 * MenuTrackMouseMenuBar
3540 *
3541 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3542 */
3543 VOID MenuTrackMouseMenuBar( HWND hWnd, ULONG ht, POINT pt)
3544 {
3545 HMENU hMenu = (ht == HTSYSMENU) ? NtUserGetSystemMenu( hWnd, FALSE) : GetMenu(hWnd);
3546 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3547
3548 TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
3549
3550 if (IsMenu(hMenu))
3551 {
3552 /* map point to parent client coordinates */
3553 HWND Parent = GetAncestor(hWnd, GA_PARENT );
3554 if (Parent != GetDesktopWindow())
3555 {
3556 ScreenToClient(Parent, &pt);
3557 }
3558
3559 MenuInitTracking(hWnd, hMenu, FALSE, wFlags);
3560 MenuTrackMenu(hMenu, wFlags, pt.x, pt.y, hWnd, NULL);
3561 MenuExitTracking(hWnd, FALSE);
3562 }
3563 }
3564
3565
3566 /***********************************************************************
3567 * MenuTrackKbdMenuBar
3568 *
3569 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3570 */
3571 VOID MenuTrackKbdMenuBar(HWND hwnd, UINT wParam, WCHAR wChar)
3572 {
3573 UINT uItem = NO_SELECTED_ITEM;
3574 HMENU hTrackMenu;
3575 ROSMENUINFO MenuInfo;
3576 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3577
3578 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3579
3580 /* find window that has a menu */
3581
3582 while (!((GetWindowLongPtrW( hwnd, GWL_STYLE ) &
3583 (WS_CHILD | WS_POPUP)) != WS_CHILD))
3584 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3585
3586 /* check if we have to track a system menu */
3587
3588 hTrackMenu = GetMenu( hwnd );
3589 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3590 {
3591 if (!(GetWindowLongPtrW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3592 hTrackMenu = NtUserGetSystemMenu(hwnd, FALSE);
3593 uItem = 0;
3594 wParam |= HTSYSMENU; /* prevent item lookup */
3595 }
3596
3597 if (!IsMenu( hTrackMenu )) return;
3598
3599 MenuInitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3600
3601 if (! MenuGetRosMenuInfo(&MenuInfo, hTrackMenu))
3602 {
3603 goto track_menu;
3604 }
3605
3606 if( wChar && wChar != ' ' )
3607 {
3608 uItem = MenuFindItemByKey( hwnd, &MenuInfo, wChar, (wParam & HTSYSMENU) );
3609 if ( uItem >= (UINT)(-2) )
3610 {
3611 if( uItem == (UINT)(-1) ) MessageBeep(0);
3612 /* schedule end of menu tracking */
3613 wFlags |= TF_ENDMENU;
3614 goto track_menu;
3615 }
3616 }
3617
3618 MenuSelectItem( hwnd, &MenuInfo, uItem, TRUE, 0 );
3619
3620 if (wParam & HTSYSMENU)
3621 {
3622 /* prevent sysmenu activation for managed windows on Alt down/up */
3623 // if (GetPropA( hwnd, "__wine_x11_managed" ))
3624 wFlags |= TF_ENDMENU; /* schedule end of menu tracking */
3625 }
3626 else
3627 {
3628 if( uItem == NO_SELECTED_ITEM )
3629 MenuMoveSelection( hwnd, &MenuInfo, ITEM_NEXT );
3630 else
3631 PostMessageW( hwnd, WM_KEYDOWN, VK_DOWN, 0L );
3632 }
3633
3634 track_menu:
3635 MenuTrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3636 MenuExitTracking( hwnd, FALSE );
3637
3638 }
3639
3640 /**********************************************************************
3641 * TrackPopupMenuEx (USER32.@)
3642 */
3643 BOOL WINAPI TrackPopupMenuEx( HMENU Menu, UINT Flags, int x, int y,
3644 HWND Wnd, LPTPMPARAMS Tpm)
3645 {
3646 BOOL ret = FALSE;
3647
3648 if (!IsMenu(Menu))
3649 {
3650 SetLastError( ERROR_INVALID_MENU_HANDLE );
3651 return FALSE;
3652 }
3653
3654 MenuInitTracking(Wnd, Menu, TRUE, Flags);
3655
3656 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3657 if (!(Flags & TPM_NONOTIFY))
3658 SendMessageW(Wnd, WM_INITMENUPOPUP, (WPARAM) Menu, 0);
3659
3660 if (MenuShowPopup(Wnd, Menu, 0, Flags, x, y, 0, 0 ))
3661 ret = MenuTrackMenu(Menu, Flags | TPM_POPUPMENU, 0, 0, Wnd,
3662 Tpm ? &Tpm->rcExclude : NULL);
3663 MenuExitTracking(Wnd, TRUE);
3664 return ret;
3665 }
3666
3667 /**********************************************************************
3668 * TrackPopupMenu (USER32.@)
3669 */
3670 BOOL WINAPI TrackPopupMenu( HMENU Menu, UINT Flags, int x, int y,
3671 int Reserved, HWND Wnd, CONST RECT *Rect)
3672 {
3673 return TrackPopupMenuEx( Menu, Flags, x, y, Wnd, NULL);
3674 }
3675
3676 /*
3677 * From MSDN:
3678 * The MFT_BITMAP, MFT_SEPARATOR, and MFT_STRING values cannot be combined
3679 * with one another. Also MFT_OWNERDRAW. Set fMask to MIIM_TYPE to use fType.
3680 *
3681 * Windows 2K/XP: fType is used only if fMask has a value of MIIM_FTYPE.
3682 *
3683 * MIIM_TYPE: Retrieves or sets the fType and dwTypeData members. Windows
3684 * 2K/XP: MIIM_TYPE is replaced by MIIM_BITMAP, MIIM_FTYPE, and MIIM_STRING.
3685 * MFT_STRING is replaced by MIIM_STRING.
3686 * (So, I guess we should use MIIM_STRING only for strings?)
3687 *
3688 * MIIM_FTYPE: Windows 2K/Windows XP: Retrieves or sets the fType member.
3689 *
3690 * Based on wine, SetMenuItemInfo_common:
3691 * 1) set MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP any one with MIIM_TYPE,
3692 * it will result in a error.
3693 * 2) set menu mask to MIIM_FTYPE and MFT_BITMAP ftype it will result in a error.
3694 * These conditions are addressed in Win32k IntSetMenuItemInfo.
3695 *
3696 */
3697 static
3698 BOOL
3699 FASTCALL
3700 MenuSetItemData(
3701 LPMENUITEMINFOW mii,
3702 UINT Flags,
3703 UINT_PTR IDNewItem,
3704 LPCWSTR NewItem,
3705 BOOL Unicode)
3706 {
3707 /*
3708 * Let us assume MIIM_FTYPE is set and building a new menu item structure.
3709 */
3710 if(Flags & MF_BITMAP)
3711 {
3712 mii->fMask |= MIIM_BITMAP; /* Use the new way of seting hbmpItem.*/
3713 mii->hbmpItem = (HBITMAP) NewItem;
3714
3715 if (Flags & MF_HELP)
3716 {
3717 /* increase ident */
3718 mii->fType |= MF_HELP;
3719 }
3720 }
3721 else if(Flags & MF_OWNERDRAW)
3722 {
3723 mii->fType |= MFT_OWNERDRAW;
3724 mii->fMask |= MIIM_DATA;
3725 mii->dwItemData = (DWORD_PTR) NewItem;
3726 }
3727 else if (Flags & MF_SEPARATOR)
3728 {
3729 mii->fType |= MFT_SEPARATOR;
3730 if (!(Flags & (MF_GRAYED|MF_DISABLED)))
3731 Flags |= MF_GRAYED|MF_DISABLED;
3732 }
3733 else /* Default action MF_STRING. */
3734 {
3735 /* Item beginning with a backspace is a help item */
3736 if (NewItem != NULL)
3737 {
3738 if (Unicode)
3739 {
3740 if (*NewItem == '\b')
3741 {
3742 mii->fType |= MF_HELP;
3743 NewItem++;
3744 }
3745 }
3746 else
3747 {
3748 LPCSTR NewItemA = (LPCSTR) NewItem;
3749 if (*NewItemA == '\b')
3750 {
3751 mii->fType |= MF_HELP;
3752 NewItemA++;
3753 NewItem = (LPCWSTR) NewItemA;
3754 }
3755 }
3756
3757 if (Flags & MF_HELP)
3758 mii->fType |= MF_HELP;
3759 mii->fMask |= MIIM_STRING;
3760 mii->fType |= MFT_STRING; /* Zero */
3761 mii->dwTypeData = (LPWSTR)NewItem;
3762 if (Unicode)
3763 mii->cch = (NULL == NewItem ? 0 : strlenW(NewItem));
3764 else
3765 mii->cch = (NULL == NewItem ? 0 : strlen((LPCSTR)NewItem));
3766 }
3767 else
3768 {
3769 mii->fType |= MFT_SEPARATOR;
3770 if (!(Flags & (MF_GRAYED|MF_DISABLED)))
3771 Flags |= MF_GRAYED|MF_DISABLED;
3772 }
3773 }
3774
3775 if(Flags & MF_RIGHTJUSTIFY) /* Same as MF_HELP */
3776 {
3777 mii->fType |= MFT_RIGHTJUSTIFY;
3778 }
3779
3780 if(Flags & MF_MENUBREAK)
3781 {
3782 mii->fType |= MFT_MENUBREAK;
3783 }
3784 else if(Flags & MF_MENUBARBREAK)
3785 {
3786 mii->fType |= MFT_MENUBARBREAK;
3787 }
3788
3789 if(Flags & MF_GRAYED || Flags & MF_DISABLED)
3790 {
3791 if (Flags & MF_GRAYED)
3792 mii->fState |= MF_GRAYED;
3793
3794 if (Flags & MF_DISABLED)
3795 mii->fState |= MF_DISABLED;
3796
3797 mii->fMask |= MIIM_STATE;
3798 }
3799 else if (Flags & MF_HILITE)
3800 {
3801 mii->fState |= MF_HILITE;
3802 mii->fMask |= MIIM_STATE;
3803 }
3804 else /* default state */
3805 {
3806 mii->fState |= MFS_ENABLED;
3807 mii->fMask |= MIIM_STATE;
3808 }
3809
3810 if(Flags & MF_POPUP)
3811 {
3812 mii->fType |= MF_POPUP;
3813 mii->fMask |= MIIM_SUBMENU;
3814 mii->hSubMenu = (HMENU)IDNewItem;
3815 }
3816 else
3817 {
3818 mii->fMask |= MIIM_ID;
3819 mii->wID = (UINT)IDNewItem;
3820 }
3821 return TRUE;
3822 }
3823
3824 NTSTATUS WINAPI
3825 User32CallLoadMenuFromKernel(PVOID Arguments, ULONG ArgumentLength)
3826 {
3827 PLOADMENU_CALLBACK_ARGUMENTS Common;
3828 LRESULT Result;
3829
3830 Common = (PLOADMENU_CALLBACK_ARGUMENTS) Arguments;
3831
3832 Result = (LRESULT)LoadMenuW( Common->hModule,
3833 IS_INTRESOURCE(Common->MenuName[0]) ?
3834 MAKEINTRESOURCE(Common->MenuName[0]) :
3835 (LPCWSTR)&Common->MenuName);
3836
3837 return ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS);
3838 }
3839
3840
3841 /* FUNCTIONS *****************************************************************/
3842
3843 /*static BOOL
3844 MenuIsStringItem(ULONG TypeData)
3845 {
3846 return(MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType));
3847 }*/
3848
3849
3850 /*
3851 * @implemented
3852 */
3853 BOOL WINAPI
3854 AppendMenuA(HMENU hMenu,
3855 UINT uFlags,
3856 UINT_PTR uIDNewItem,
3857 LPCSTR lpNewItem)
3858 {
3859 return(InsertMenuA(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
3860 lpNewItem));
3861 }
3862
3863
3864 /*
3865 * @implemented
3866 */
3867 BOOL WINAPI
3868 AppendMenuW(HMENU hMenu,
3869 UINT uFlags,
3870 UINT_PTR uIDNewItem,
3871 LPCWSTR lpNewItem)
3872 {
3873 return(InsertMenuW(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
3874 lpNewItem));
3875 }
3876
3877
3878 /*
3879 * @implemented
3880 */
3881 DWORD WINAPI
3882 CheckMenuItem(HMENU hmenu,
3883 UINT uIDCheckItem,
3884 UINT uCheck)
3885 {
3886 return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck);
3887 }
3888
3889 static
3890 BOOL
3891 MenuCheckMenuRadioItem(HMENU hMenu, UINT idFirst, UINT idLast, UINT idCheck, UINT uFlags, BOOL bCheck, PUINT pChecked, PUINT pUnchecked, PUINT pMenuChanged)
3892 {
3893 UINT ItemCount, i;
3894 PROSMENUITEMINFO Items = NULL;
3895 UINT cChecked, cUnchecked;
3896 BOOL bRet = TRUE;
3897 //ROSMENUINFO mi;
3898
3899 if(idFirst > idLast)
3900 return FALSE;
3901
3902 ItemCount = GetMenuItemCount(hMenu);
3903
3904 //mi.cbSize = sizeof(ROSMENUINFO);
3905 //if(!NtUserMenuInfo(hmenu, &mi, FALSE)) return ret;
3906
3907
3908 if(MenuGetAllRosMenuItemInfo(hMenu, &Items) <= 0)
3909 {
3910 ERR("MenuGetAllRosMenuItemInfo failed\n");
3911 return FALSE;
3912 }
3913
3914 cChecked = cUnchecked = 0;
3915
3916 for (i = 0 ; i < ItemCount; i++)
3917 {
3918 BOOL check = FALSE;
3919 if (0 != (Items[i].fType & MF_MENUBARBREAK)) continue;
3920 if (0 != (Items[i].fType & MF_SEPARATOR)) continue;
3921
3922 if ((Items[i].fType & MF_POPUP) && (uFlags == MF_BYCOMMAND))
3923 {
3924 MenuCheckMenuRadioItem(Items[i].hSubMenu, idFirst, idLast, idCheck, uFlags, bCheck, pChecked, pUnchecked, pMenuChanged);
3925 continue;
3926 }
3927 if (uFlags & MF_BYPOSITION)
3928 {
3929 if (i < idFirst || i > idLast)
3930 continue;
3931
3932 if (i == idCheck)
3933 {
3934 cChecked++;
3935 check = TRUE;
3936 }
3937 else
3938 {
3939 cUnchecked++;
3940 }
3941 }
3942 else
3943 {
3944 if (Items[i].wID < idFirst || Items[i].wID > idLast)
3945 continue;
3946
3947 if (Items[i].wID == idCheck)
3948 {
3949 cChecked++;
3950 check = TRUE;
3951 }
3952 else
3953 {
3954 cUnchecked++;
3955 }
3956 }
3957
3958 if (!bCheck)
3959 continue;
3960
3961 Items[i].fMask = MIIM_STATE | MIIM_FTYPE;
3962 if (check)
3963 {
3964 Items[i].fType |= MFT_RADIOCHECK;
3965 Items[i].fState |= MFS_CHECKED;
3966 }
3967 else
3968 {
3969 Items[i].fState &= ~MFS_CHECKED;
3970 }
3971
3972 if(!MenuSetRosMenuItemInfo(hMenu, i ,&Items[i]))
3973 {
3974 ERR("MenuSetRosMenuItemInfo failed\n");
3975 bRet = FALSE;
3976 break;
3977 }
3978 }
3979 HeapFree(GetProcessHeap(), 0, Items);
3980
3981 *pChecked += cChecked;
3982 *pUnchecked += cUnchecked;
3983
3984 if (cChecked || cUnchecked)
3985 (*pMenuChanged)++;
3986
3987 return bRet;
3988 }
3989
3990 /*
3991 * @implemented
3992 */
3993 BOOL WINAPI
3994 CheckMenuRadioItem(HMENU hmenu,
3995 UINT idFirst,
3996 UINT idLast,
3997 UINT idCheck,
3998 UINT uFlags)
3999 {
4000 UINT cChecked = 0;
4001 UINT cUnchecked = 0;
4002 UINT cMenuChanged = 0;
4003
4004 if (!MenuCheckMenuRadioItem(hmenu, idFirst, idLast, idCheck, uFlags, FALSE, &cChecked, &cUnchecked, &cMenuChanged))
4005 return FALSE;
4006
4007 if (cMenuChanged > 1)
4008 return FALSE;
4009
4010 cMenuChanged = 0;
4011 cChecked = 0;
4012 cUnchecked = 0;
4013
4014 if (!MenuCheckMenuRadioItem(hmenu, idFirst, idLast, idCheck, uFlags, TRUE, &cChecked, &cUnchecked, &cMenuChanged))
4015 return FALSE;
4016
4017 return (cChecked != 0);
4018 }
4019
4020
4021 /*
4022 * @implemented
4023 */
4024 HMENU WINAPI
4025 CreateMenu(VOID)
4026 {
4027 MenuLoadBitmaps();
4028 return (HMENU)NtUserCallNoParam(NOPARAM_ROUTINE_CREATEMENU);
4029 }
4030
4031
4032 /*
4033 * @implemented
4034 */
4035 HMENU WINAPI
4036 CreatePopupMenu(VOID)
4037 {
4038 MenuLoadBitmaps();
4039 return (HMENU)NtUserCallNoParam(NOPARAM_ROUTINE_CREATEMENUPOPUP);
4040 }
4041
4042
4043 /*
4044 * @implemented
4045 */
4046 BOOL WINAPI
4047 DrawMenuBar(HWND hWnd)
4048 {
4049 // return (BOOL)NtUserCallHwndLock(hWnd, HWNDLOCK_ROUTINE_DRAWMENUBAR);
4050 ROSMENUINFO MenuInfo;
4051 HMENU hMenu;
4052 hMenu = GetMenu(hWnd);
4053 if (!hMenu)
4054 return FALSE;
4055 MenuGetRosMenuInfo(&MenuInfo, hMenu);
4056 MenuInfo.Height = 0; // make sure to recalc size
4057 MenuSetRosMenuInfo(&MenuInfo);
4058 /* The wine method doesn't work and I suspect it's more effort
4059 then hackfix solution
4060 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4061 SWP_NOZORDER | SWP_FRAMECHANGED );
4062 return TRUE;*/
4063 // FIXME: hackfix
4064 DefWndNCPaint(hWnd,(HRGN)-1,-1);
4065 return TRUE;
4066 }
4067
4068 /*
4069 * @implemented
4070 */
4071 BOOL WINAPI
4072 EnableMenuItem(HMENU hMenu,
4073 UINT uIDEnableItem,
4074 UINT uEnable)
4075 {
4076 return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable);
4077 }
4078
4079 /*
4080 * @implemented
4081 */
4082 BOOL WINAPI
4083 EndMenu(VOID)
4084 {
4085 GUITHREADINFO guii;
4086 guii.cbSize = sizeof(GUITHREADINFO);
4087 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii) && guii.hwndMenuOwner)
4088 {
4089 if (!fEndMenu &&
4090 top_popup &&
4091 guii.hwndMenuOwner != top_popup )
4092 {
4093 ERR("Capture GUI pti hWnd does not match top_popup!\n");
4094 }
4095 }
4096
4097 /* if we are in the menu code, and it is active */
4098 if (!fEndMenu && top_popup)
4099 {
4100 /* terminate the menu handling code */
4101 fEndMenu = TRUE;
4102
4103 /* needs to be posted to wakeup the internal menu handler */
4104 /* which will now terminate the menu, in the event that */
4105 /* the main window was minimized, or lost focus, so we */
4106 /* don't end up with an orphaned menu */
4107 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4108 }
4109 return TRUE;
4110 }
4111
4112 // So this one maybe one day it will be a callback!
4113 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
4114 UINT wHilite )
4115 {
4116 ROSMENUINFO MenuInfo;
4117 ROSMENUITEMINFO mii;
4118 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
4119 if (!hWnd)
4120 {
4121 SetLastError(ERROR_INVALID_WINDOW_HANDLE);
4122 return FALSE;
4123 }
4124 if (!NtUserMenuItemInfo(hMenu, wItemID, wHilite, &mii, FALSE)) return FALSE;
4125 if (!NtUserMenuInfo(hMenu, &MenuInfo, FALSE)) return FALSE;
4126 if (MenuInfo.FocusedItem == wItemID) return TRUE;
4127 MenuHideSubPopups( hWnd, &MenuInfo, FALSE, 0 );
4128 MenuSelectItem( hWnd, &MenuInfo, wItemID, TRUE, 0 );
4129 return TRUE;
4130 }
4131
4132 /*
4133 * @implemented
4134 */
4135 HMENU WINAPI
4136 GetMenu(HWND hWnd)
4137 {
4138 PWND Wnd = ValidateHwnd(hWnd);
4139
4140 if (!Wnd)
4141 return NULL;
4142
4143 return UlongToHandle(Wnd->IDMenu);
4144 }
4145
4146
4147 /*
4148 * @implemented
4149 */
4150 LONG WINAPI
4151 GetMenuCheckMarkDimensions(VOID)
4152 {
4153 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK),
4154 GetSystemMetrics(SM_CYMENUCHECK)));
4155 }
4156
4157
4158 /*
4159 * @implemented
4160 */
4161 UINT WINAPI
4162 GetMenuDefaultItem(HMENU hMenu,
4163 UINT fByPos,
4164 UINT gmdiFlags)
4165 {
4166 return NtUserGetMenuDefaultItem(hMenu, fByPos, gmdiFlags);
4167 }
4168
4169
4170 /*
4171 * @implemented
4172 */
4173 BOOL WINAPI
4174 GetMenuInfo(HMENU hmenu,
4175 LPMENUINFO lpcmi)
4176 {
4177 ROSMENUINFO mi;
4178 BOOL res = FALSE;
4179
4180 if(!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
4181 return FALSE;
4182
4183 RtlZeroMemory(&mi, sizeof(MENUINFO));
4184 mi.cbSize = sizeof(MENUINFO);
4185 mi.fMask = lpcmi->fMask;
4186
4187 res = NtUserMenuInfo(hmenu, &mi, FALSE);
4188
4189 memcpy(lpcmi, &mi, sizeof(MENUINFO));
4190 return res;
4191 }
4192
4193
4194 /*
4195 * @implemented
4196 */
4197 int WINAPI
4198 GetMenuItemCount(HMENU Menu)
4199 {
4200 ROSMENUINFO MenuInfo;
4201
4202 return MenuGetRosMenuInfo(&MenuInfo, Menu) ? MenuInfo.MenuItemCount : 0;
4203 }
4204
4205
4206 /*
4207 * @implemented
4208 */
4209 UINT WINAPI
4210 GetMenuItemID(HMENU hMenu,
4211 int nPos)
4212 {
4213 ROSMENUITEMINFO mii;
4214
4215 mii.cbSize = sizeof(MENUITEMINFOW);
4216 mii.fMask = MIIM_ID | MIIM_SUBMENU;
4217
4218 if (! NtUserMenuItemInfo(hMenu, nPos, MF_BYPOSITION, &mii, FALSE))
4219 {
4220 return -1;
4221 }
4222
4223 if (NULL != mii.hSubMenu)
4224 {
4225 return -1;
4226 }
4227 if (0 == mii.wID)
4228 {
4229 return -1;
4230 }
4231
4232 return mii.wID;
4233 }
4234
4235
4236 /*
4237 * @implemented
4238 */
4239 BOOL WINAPI
4240 GetMenuItemInfoA(
4241 HMENU Menu,
4242 UINT Item,
4243 BOOL ByPosition,
4244 LPMENUITEMINFOA mii)
4245 {
4246 MENUITEMINFOW miiW;
4247 LPSTR AnsiBuffer;
4248 INT Count;
4249
4250 if (mii->cbSize != sizeof(MENUITEMINFOA) &&
4251 mii->cbSize != sizeof(MENUITEMINFOA) - sizeof(HBITMAP))
4252 {
4253 SetLastError(ERROR_INVALID_PARAMETER);
4254 return FALSE;
4255 }
4256
4257 if(!(mii->fMask & (MIIM_TYPE | MIIM_STRING)))
4258 {
4259 /* No text requested, just pass on */
4260 return NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) mii, FALSE);
4261 }
4262
4263 AnsiBuffer = mii->dwTypeData;
4264 Count = miiW.cch = mii->cch;
4265 RtlCopyMemory(&miiW, mii, mii->cbSize);
4266 miiW.dwTypeData = 0;
4267
4268 if (AnsiBuffer)
4269 {
4270 miiW.dwTypeData = RtlAllocateHeap(GetProcessHeap(), 0,
4271 miiW.cch * sizeof(WCHAR));
4272 if (miiW.dwTypeData == NULL) return FALSE;
4273 miiW.dwTypeData[0] = 0;
4274 }
4275
4276 if (!NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO)&miiW, FALSE))
4277 {
4278 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4279 return FALSE;
4280 }
4281
4282 RtlCopyMemory(mii, &miiW, miiW.cbSize);
4283
4284 if (!AnsiBuffer || !Count)
4285 {
4286 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4287 mii->dwTypeData = AnsiBuffer;
4288 mii->cch = miiW.cch;
4289 return TRUE;
4290 }
4291
4292 if ((miiW.fMask & MIIM_STRING) || (IS_STRING_ITEM(miiW.fType)))
4293 {
4294 if (miiW.cch)
4295 {
4296 if (!WideCharToMultiByte(CP_ACP, 0, miiW.dwTypeData, miiW.cch, AnsiBuffer, mii->cch, NULL, NULL))
4297 {
4298 AnsiBuffer[0] = 0;
4299 }
4300 if (Count > miiW.cch)
4301 {
4302 AnsiBuffer[miiW.cch] = 0;
4303 }
4304 mii->cch = mii->cch;
4305 }
4306 }
4307 else
4308 {
4309 AnsiBuffer[0] = 0;
4310 }
4311
4312 RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4313 mii->dwTypeData = AnsiBuffer;
4314
4315 return TRUE;
4316 }
4317
4318
4319 /*
4320 * @implemented
4321 */
4322 BOOL WINAPI
4323 GetMenuItemInfoW(
4324 HMENU Menu,
4325 UINT Item,
4326 BOOL ByPosition,
4327 LPMENUITEMINFOW mii)
4328 {
4329 MENUITEMINFOW miiW;
4330 LPWSTR String;
4331 INT Count;
4332
4333 if (mii->cbSize != sizeof(MENUITEMINFOW) &&
4334 mii->cbSize != sizeof(MENUITEMINFOW) - sizeof(HBITMAP))
4335 {
4336 SetLastError(ERROR_INVALID_PARAMETER);
4337 return FALSE;
4338 }
4339
4340 if(!(mii->fMask & (MIIM_TYPE | MIIM_STRING)))
4341 {
4342 /* No text requested, just pass on */
4343 return NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) mii, FALSE);
4344 }
4345
4346 String = mii->dwTypeData;
4347 Count = mii->cch;
4348 RtlCopyMemory(&miiW, mii, mii->cbSize);
4349 miiW.dwTypeData = 0;
4350
4351 if (String)
4352 {
4353 miiW.dwTypeData = RtlAllocateHeap(GetProcessHeap(), 0,
4354 miiW.cch * sizeof(WCHAR));
4355 if (miiW.dwTypeData == NULL) return FALSE;
4356 miiW.dwTypeData[0] = 0;
4357 }
4358
4359 if (!NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) &miiW, FALSE))
4360 {
4361 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4362 return FALSE;
4363 }
4364
4365 RtlCopyMemory(mii, &miiW, miiW.cbSize); // Okay to over write user data.
4366
4367 if (!String || !Count)
4368 {
4369 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4370 mii->dwTypeData = String; // may not be zero.
4371 mii->cch = miiW.cch;
4372 return TRUE;
4373 }
4374
4375 if ((miiW.fMask & MIIM_STRING) || (IS_STRING_ITEM(miiW.fType)))
4376 {
4377 lstrcpynW( String, miiW.dwTypeData, Count );
4378 }
4379
4380 RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4381 mii->dwTypeData = String;
4382 mii->cch = strlenW(String);
4383 return TRUE;
4384 }
4385
4386
4387 /*
4388 * @implemented
4389 */
4390 UINT
4391 WINAPI
4392 GetMenuState(
4393 HMENU hMenu,
4394 UINT uId,
4395 UINT uFlags)
4396 {
4397 ROSMENUINFO MenuInfo;
4398 ROSMENUITEMINFO mii;
4399 memset( &mii, 0, sizeof(mii) );
4400 mii.cbSize = sizeof(MENUITEMINFOW);
4401 mii.fMask = MIIM_STATE | MIIM_FTYPE | MIIM_SUBMENU;
4402
4403 SetLastError(0);
4404 if(NtUserMenuItemInfo(hMenu, uId, uFlags, &mii, FALSE))
4405 {
4406 UINT nSubItems = 0;
4407 if(mii.hSubMenu)
4408 {
4409 if (! MenuGetRosMenuInfo(&MenuInfo, mii.hSubMenu))
4410 {
4411 return (UINT) -1;
4412 }
4413 nSubItems = MenuInfo.MenuItemCount;
4414
4415 /* FIXME - ported from wine, does that work (0xff)? */
4416 if(GetLastError() != ERROR_INVALID_MENU_HANDLE)
4417 return (nSubItems << 8) | ((mii.fState | mii.fType) & 0xff);
4418
4419 return (UINT)-1; /* Invalid submenu */
4420 }
4421
4422 /* FIXME - ported from wine, does that work? */
4423 return (mii.fType | mii.fState);
4424 }
4425
4426 return (UINT)-1;
4427 }
4428
4429
4430 /*
4431 * @implemented
4432 */
4433 int
4434 WINAPI
4435 GetMenuStringA(
4436 HMENU hMenu,
4437 UINT uIDItem,
4438 LPSTR lpString,
4439 int nMaxCount,
4440 UINT uFlag)
4441 {
4442 MENUITEMINFOA mii;
4443 memset( &mii, 0, sizeof(mii) );
4444 mii.dwTypeData = lpString;
4445 mii.fMask = MIIM_STRING | MIIM_FTYPE;
4446 mii.fType = MFT_STRING;
4447 mii.cbSize = sizeof(MENUITEMINFOA);
4448 mii.cch = nMaxCount;
4449
4450 if(!(GetMenuItemInfoA( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&mii)))
4451 return 0;
4452 else
4453 return mii.cch;
4454 }
4455
4456
4457 /*
4458 * @implemented
4459 */
4460 int
4461 WINAPI
4462 GetMenuStringW(
4463 HMENU hMenu,
4464 UINT uIDItem,
4465 LPWSTR lpString,
4466 int nMaxCount,
4467 UINT uFlag)
4468 {
4469 MENUITEMINFOW miiW;
4470 memset( &miiW, 0, sizeof(miiW) );
4471 miiW.dwTypeData = lpString;
4472 miiW.fMask = MIIM_STRING | MIIM_FTYPE;
4473 miiW.fType = MFT_STRING;
4474 miiW.cbSize = sizeof(MENUITEMINFOW);
4475 miiW.cch = nMaxCount;
4476
4477 if(!(GetMenuItemInfoW( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&miiW)))
4478 return 0;
4479 else
4480 return miiW.cch;
4481 }
4482
4483
4484 /*
4485 * @implemented
4486 */
4487 HMENU
4488 WINAPI
4489 GetSubMenu(
4490 HMENU hMenu,
4491 int nPos)
4492 {
4493 ROSMENUITEMINFO mi;
4494
4495 mi.cbSize = sizeof(MENUITEMINFOW);
4496 mi.fMask = MIIM_SUBMENU;
4497
4498 if (NtUserMenuItemInfo(hMenu, (UINT)nPos, MF_BYPOSITION, &mi, FALSE))
4499 {
4500 return IsMenu(mi.hSubMenu) ? mi.hSubMenu : NULL;
4501 }
4502
4503 return NULL;
4504 }
4505
4506 /*
4507 * @implemented
4508 */
4509 HMENU
4510 WINAPI
4511 GetSystemMenu(
4512 HWND hWnd,
4513 BOOL bRevert)
4514 {
4515 HMENU TopMenu;
4516
4517 TopMenu = NtUserGetSystemMenu(hWnd, bRevert);
4518
4519 return NULL == TopMenu ? NULL : GetSubMenu(TopMenu, 0);
4520 }
4521
4522
4523 /*
4524 * @implemented
4525 */
4526 BOOL
4527 WINAPI
4528 InsertMenuA(
4529 HMENU hMenu,
4530 UINT uPosition,
4531 UINT uFlags,
4532 UINT_PTR uIDNewItem,
4533 LPCSTR lpNewItem)
4534 {
4535 MENUITEMINFOA mii;
4536 memset( &mii, 0, sizeof(mii) );
4537 mii.cbSize = sizeof(MENUITEMINFOA);
4538 mii.fMask = MIIM_FTYPE;
4539
4540 MenuSetItemData((LPMENUITEMINFOW) &mii,
4541 uFlags,
4542 uIDNewItem,
4543 (LPCWSTR) lpNewItem,
4544 FALSE);
4545
4546 return InsertMenuItemA(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii);
4547 }
4548
4549
4550
4551 /*
4552 * @implemented
4553 */
4554 BOOL
4555 WINAPI
4556 InsertMenuItemA(
4557 HMENU hMenu,
4558 UINT uItem,
4559 BOOL fByPosition,
4560 LPCMENUITEMINFOA lpmii)
4561 {
4562 MENUITEMINFOW mi;
4563 UNICODE_STRING MenuText;
4564 BOOL res = FALSE;
4565 BOOL CleanHeap = FALSE;
4566 NTSTATUS Status;
4567
4568 if((lpmii->cbSize == sizeof(MENUITEMINFOA)) ||
4569 (lpmii->cbSize == sizeof(MENUITEMINFOA) - sizeof(HBITMAP)))
4570 {
4571 RtlCopyMemory ( &mi, lpmii, lpmii->cbSize );
4572
4573 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
4574 {
4575 mi.cbSize = sizeof( MENUITEMINFOW);
4576 mi.hbmpItem = NULL;
4577 }
4578 /* copy the text string */
4579 if (((mi.fMask & MIIM_STRING) ||
4580 ((mi.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mi.fType) == MF_STRING)))
4581 && mi.dwTypeData != NULL)
4582 {
4583 Status = RtlCreateUnicodeStringFromAsciiz(&MenuText, (LPSTR)mi.dwTypeData);
4584 if (!NT_SUCCESS (Status))
4585 {
4586 SetLastError (RtlNtStatusToDosError(Status));
4587 return FALSE;
4588 }
4589 mi.dwTypeData = MenuText.Buffer;
4590 mi.cch = MenuText.Length / sizeof(WCHAR);
4591 CleanHeap = TRUE;
4592 }
4593 res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mi, NULL);
4594
4595 if ( CleanHeap ) RtlFreeUnicodeString ( &MenuText );
4596 }
4597 return res;
4598 }
4599
4600
4601 /*
4602 * @implemented
4603 */
4604 BOOL
4605 WINAPI
4606 InsertMenuItemW(
4607 HMENU hMenu,
4608 UINT uItem,
4609 BOOL fByPosition,
4610 LPCMENUITEMINFOW lpmii)
4611 {
4612 MENUITEMINFOW mi;
4613 UNICODE_STRING MenuText;
4614 BOOL res = FALSE;
4615
4616 /* while we could just pass 'lpmii' to win32k, we make a copy so that
4617 if a bad user passes bad data, we crash his process instead of the
4618 entire kernel */
4619
4620 if((lpmii->cbSize == sizeof(MENUITEMINFOW)) ||
4621 (lpmii->cbSize == sizeof(MENUITEMINFOW) - sizeof(HBITMAP)))
4622 {
4623 RtlCopyMemory(&mi, lpmii, lpmii->cbSize);
4624
4625 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
4626 {
4627 mi.cbSize = sizeof( MENUITEMINFOW);
4628 mi.hbmpItem = NULL;
4629 }
4630 /* copy the text string */
4631 if (((mi.fMask & MIIM_STRING) ||
4632 ((mi.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mi.fType) == MF_STRING)))
4633 && mi.dwTypeData != NULL)
4634 {
4635 RtlInitUnicodeString(&MenuText, (PWSTR)lpmii->dwTypeData);
4636 mi.dwTypeData = MenuText.Buffer;
4637 mi.cch = MenuText.Length / sizeof(WCHAR);
4638 }
4639 res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mi, NULL);
4640 }
4641 return res;
4642 }
4643
4644
4645 /*
4646 * @implemented
4647 */
4648 BOOL
4649 WINAPI
4650 InsertMenuW(
4651 HMENU hMenu,
4652 UINT uPosition,
4653 UINT uFlags,
4654 UINT_PTR uIDNewItem,
4655 LPCWSTR lpNewItem)
4656 {
4657 MENUITEMINFOW mii;
4658 memset( &mii, 0, sizeof(mii) );
4659 mii.cbSize = sizeof(MENUITEMINFOW);
4660 mii.fMask = MIIM_FTYPE;
4661
4662 MenuSetItemData( &mii,
4663 uFlags,
4664 uIDNewItem,
4665 lpNewItem,
4666 TRUE);
4667
4668 return InsertMenuItemW(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii);
4669 }
4670
4671
4672 /*
4673 * @implemented
4674 */
4675 BOOL
4676 WINAPI
4677 IsMenu(
4678 HMENU Menu)
4679 {
4680 if (ValidateHandle(Menu, VALIDATE_TYPE_MENU)) return TRUE;
4681 return FALSE;
4682 }
4683
4684
4685 /*
4686 * @implemented
4687 */
4688 HMENU WINAPI
4689 LoadMenuA(HINSTANCE hInstance,
4690 LPCSTR lpMenuName)
4691 {
4692 HANDLE Resource = FindResourceA(hInstance, lpMenuName, MAKEINTRESOURCEA(4));
4693 if (Resource == NULL)
4694 {
4695 return(NULL);
4696 }
4697 return(LoadMenuIndirectA((PVOID)LoadResource(hInstance, Resource)));
4698 }
4699
4700
4701 /*
4702 * @implemented
4703 */
4704 HMENU WINAPI
4705 LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate)
4706 {
4707 return(LoadMenuIndirectW(lpMenuTemplate));
4708 }
4709
4710
4711 /*
4712 * @implemented
4713 */
4714 HMENU WINAPI
4715 LoadMenuIndirectW(CONST MENUTEMPLATE *lpMenuTemplate)
4716 {
4717 HMENU hMenu;
4718 WORD version, offset;
4719 LPCSTR p = (LPCSTR)lpMenuTemplate;
4720
4721 version = GET_WORD(p);
4722 p += sizeof(WORD);
4723
4724 switch (version)
4725 {
4726 case 0: /* standard format is version of 0 */
4727 offset = GET_WORD(p);
4728 p += sizeof(WORD) + offset;
4729 if (!(hMenu = CreateMenu())) return 0;
4730 if (!MENU_ParseResource(p, hMenu, TRUE))
4731 {
4732 DestroyMenu(hMenu);
4733 return 0;
4734 }
4735 return hMenu;
4736 case 1: /* extended format is version of 1 */
4737 offset = GET_WORD(p);
4738 p += sizeof(WORD) + offset;
4739 if (!(hMenu = CreateMenu())) return 0;
4740 if (!MENUEX_ParseResource(p, hMenu))
4741 {
4742 DestroyMenu( hMenu );
4743 return 0;
4744 }
4745 return hMenu;
4746 default:
4747 DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version);
4748 return 0;
4749 }
4750 }
4751
4752
4753 /*
4754 * @implemented
4755 */
4756 HMENU WINAPI
4757 LoadMenuW(HINSTANCE hInstance,
4758 LPCWSTR lpMenuName)
4759 {
4760 HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU);
4761 if (Resource == NULL)
4762 {
4763 return(NULL);
4764 }
4765 return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource)));
4766 }
4767
4768
4769 /*
4770 * @implemented
4771 */
4772 int
4773 WINAPI
4774 MenuItemFromPoint(
4775 HWND hWnd,
4776 HMENU hMenu,
4777 POINT ptScreen)
4778 {
4779 return NtUserMenuItemFromPoint(hWnd, hMenu, ptScreen.x, ptScreen.y);
4780 }
4781
4782
4783 /*
4784 * @implemented
4785 */
4786 BOOL
4787 WINAPI
4788 ModifyMenuA(
4789 HMENU hMnu,
4790 UINT uPosition,
4791 UINT uFlags,
4792 UINT_PTR uIDNewItem,
4793 LPCSTR lpNewItem)
4794 {
4795 ROSMENUINFO mi;
4796 ROSMENUITEMINFO rmii;
4797 MENUITEMINFOA mii;
4798 memset( &mii, 0, sizeof(mii) );
4799 mii.cbSize = sizeof(MENUITEMINFOA);
4800 mii.fMask = MIIM_FTYPE;
4801
4802 if (!MenuGetRosMenuInfo( &mi, hMnu )) return FALSE;
4803
4804 mi.Height = 0;
4805
4806 if (!MenuSetRosMenuInfo( &mi )) return FALSE;
4807
4808 MenuInitRosMenuItemInfo( &rmii );
4809
4810 if(!MenuGetRosMenuItemInfo( hMnu, uPosition, &rmii)) return FALSE;
4811
4812 if ((rmii.fType & MF_POPUP) && (uFlags & MF_POPUP) && (rmii.hSubMenu != (HMENU)uIDNewItem))
4813 NtUserDestroyMenu( rmii.hSubMenu ); /* ModifyMenu() spec */
4814
4815 MenuCleanupRosMenuItemInfo( &rmii );
4816
4817 MenuSetItemData((LPMENUITEMINFOW) &mii,
4818 uFlags,
4819 uIDNewItem,
4820 (LPCWSTR) lpNewItem,
4821 FALSE);
4822
4823 return SetMenuItemInfoA( hMnu,
4824 uPosition,
4825 (BOOL)(MF_BYPOSITION & uFlags),
4826 &mii);
4827 }
4828
4829
4830 /*
4831 * @implemented
4832 */
4833 BOOL
4834 WINAPI
4835 ModifyMenuW(
4836 HMENU hMnu,
4837 UINT uPosition,
4838 UINT uFlags,
4839 UINT_PTR uIDNewItem,
4840 LPCWSTR lpNewItem)
4841 {
4842 ROSMENUINFO mi;
4843 ROSMENUITEMINFO rmii;
4844 MENUITEMINFOW mii;
4845 memset ( &mii, 0, sizeof(mii) );
4846 mii.cbSize = sizeof(MENUITEMINFOW);
4847 mii.fMask = MIIM_FTYPE;
4848
4849 if (!MenuGetRosMenuInfo( &mi, hMnu )) return FALSE;
4850
4851 mi.Height = 0; // Force size recalculation.
4852
4853 if (!MenuSetRosMenuInfo( &mi )) return FALSE;
4854
4855 MenuInitRosMenuItemInfo( &rmii );
4856
4857 if(!MenuGetRosMenuItemInfo( hMnu, uPosition, &rmii)) return FALSE;
4858
4859 if ((rmii.fType & MF_POPUP) && (uFlags & MF_POPUP) && (rmii.hSubMenu != (HMENU)uIDNewItem))
4860 NtUserDestroyMenu( rmii.hSubMenu ); /* ModifyMenu() spec */
4861
4862 MenuCleanupRosMenuItemInfo( &rmii );
4863
4864 /* Init new data for this menu item */
4865 MenuSetItemData( &mii,
4866 uFlags,
4867 uIDNewItem,
4868 lpNewItem,
4869 TRUE);
4870
4871 /* Now, make Win32k IntSetMenuItemInfo handle the changes to this menu item. */
4872 return SetMenuItemInfoW( hMnu,
4873 uPosition,
4874 (BOOL)(MF_BYPOSITION & uFlags),
4875 &mii);
4876 }
4877
4878
4879 /*
4880 * @implemented
4881 */
4882 BOOL WINAPI
4883 SetMenu(HWND hWnd,
4884 HMENU hMenu)
4885 {
4886 return NtUserSetMenu(hWnd, hMenu, TRUE);
4887 }
4888
4889
4890 /*
4891 * @implemented
4892 */
4893 BOOL
4894 WINAPI
4895 SetMenuInfo(
4896 HMENU hmenu,
4897 LPCMENUINFO lpcmi)
4898 {
4899 ROSMENUINFO mi;
4900 BOOL res = FALSE;
4901
4902 if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
4903 {
4904 SetLastError(ERROR_INVALID_PARAMETER);
4905 return res;
4906 }
4907
4908 memcpy(&mi, lpcmi, sizeof(MENUINFO));
4909 return NtUserMenuInfo(hmenu, &mi, TRUE);
4910 }
4911
4912
4913 /*
4914 * @implemented
4915 */
4916 BOOL
4917 WINAPI
4918 SetMenuItemBitmaps(
4919 HMENU hMenu,
4920 UINT uPosition,
4921 UINT uFlags,
4922 HBITMAP hBitmapUnchecked,
4923 HBITMAP hBitmapChecked)
4924 {
4925 ROSMENUITEMINFO uItem;
4926 memset ( &uItem, 0, sizeof(uItem) );
4927 uItem.fMask = MIIM_STATE | MIIM_BITMAP;
4928
4929 if(!(NtUserMenuItemInfo(hMenu, uPosition,
4930 (BOOL)(MF_BYPOSITION & uFlags), &uItem, FALSE))) return FALSE;
4931
4932 if (!hBitmapChecked && !hBitmapUnchecked)
4933 {
4934 uItem.fState &= ~MF_USECHECKBITMAPS;
4935 }
4936 else /* Install new bitmaps */
4937 {
4938 uItem.hbmpChecked = hBitmapChecked;
4939 uItem.hbmpUnchecked = hBitmapUnchecked;
4940 uItem.fState |= MF_USECHECKBITMAPS;
4941 }
4942 return NtUserMenuItemInfo(hMenu, uPosition,
4943 (BOOL)(MF_BYPOSITION & uFlags), &uItem, TRUE);
4944 }
4945
4946
4947 /*
4948 * @implemented
4949 */
4950 BOOL
4951 WINAPI
4952 SetMenuItemInfoA(
4953 HMENU hMenu,
4954 UINT uItem,
4955 BOOL fByPosition,
4956 LPCMENUITEMINFOA lpmii)
4957 {
4958 MENUITEMINFOW MenuItemInfoW;
4959 UNICODE_STRING UnicodeString;
4960 NTSTATUS Status;
4961 ULONG Result = FALSE;
4962
4963 RtlCopyMemory(&MenuItemInfoW, lpmii, min(lpmii->cbSize, sizeof(MENUITEMINFOW)));
4964
4965 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
4966 {
4967 MenuItemInfoW.cbSize = sizeof( MENUITEMINFOW);
4968 MenuItemInfoW.hbmpItem = NULL;
4969 }
4970 /*
4971 * MIIM_STRING == good
4972 * MIIM_TYPE & MFT_STRING == good
4973 * MIIM_STRING & MFT_STRING == good
4974 * MIIM_STRING & MFT_OWNERSRAW == good
4975 */
4976 if (((MenuItemInfoW.fMask & MIIM_STRING) ||
4977 ((MenuItemInfoW.fMask & MIIM_TYPE) &&
4978 (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
4979 && MenuItemInfoW.dwTypeData != NULL)
4980 {
4981 /* cch is ignored when the content of a menu item is set by calling SetMenuItemInfo. */
4982 Status = RtlCreateUnicodeStringFromAsciiz(&UnicodeString,
4983 (LPSTR)MenuItemInfoW.dwTypeData);
4984 if (!NT_SUCCESS (Status))
4985 {
4986 SetLastError (RtlNtStatusToDosError(Status));
4987 return FALSE;
4988 }
4989 MenuItemInfoW.dwTypeData = UnicodeString.Buffer;
4990 MenuItemInfoW.cch = UnicodeString.Length / sizeof(WCHAR);
4991 }
4992 else
4993 {
4994 UnicodeString.Buffer = NULL;
4995 }
4996
4997 Result = NtUserMenuItemInfo(hMenu, uItem, fByPosition,
4998 (PROSMENUITEMINFO)&MenuItemInfoW, TRUE);
4999
5000 if (UnicodeString.Buffer != NULL)
5001 {
5002 RtlFreeUnicodeString(&UnicodeString);
5003 }
5004
5005 return Result;
5006 }
5007
5008
5009 /*
5010 * @implemented
5011 */
5012 BOOL
5013 WINAPI
5014 SetMenuItemInfoW(
5015 HMENU hMenu,
5016 UINT uItem,
5017 BOOL fByPosition,
5018 LPCMENUITEMINFOW lpmii)
5019 {
5020 MENUITEMINFOW MenuItemInfoW;
5021 ULONG Result;
5022
5023 RtlCopyMemory(&MenuItemInfoW, lpmii, min(lpmii->cbSize, sizeof(MENUITEMINFOW)));
5024
5025 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
5026 {
5027 MenuItemInfoW.cbSize = sizeof( MENUITEMINFOW);
5028 MenuItemInfoW.hbmpItem = NULL;
5029 }
5030
5031 if (((MenuItemInfoW.fMask & MIIM_STRING) ||
5032 ((MenuItemInfoW.fMask & MIIM_TYPE) &&
5033 (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
5034 && MenuItemInfoW.dwTypeData != NULL)
5035 {
5036 MenuItemInfoW.cch = strlenW(MenuItemInfoW.dwTypeData);
5037 }
5038 Result = NtUserMenuItemInfo(hMenu, uItem, fByPosition,
5039 (PROSMENUITEMINFO)&MenuItemInfoW, TRUE);
5040
5041 return Result;
5042 }
5043
5044 /*
5045 * @implemented
5046 */
5047 BOOL
5048 WINAPI
5049 SetSystemMenu (
5050 HWND hwnd,
5051 HMENU hMenu)
5052 {
5053 if(!hwnd)
5054 {
5055 SetLastError(ERROR_INVALID_WINDOW_HANDLE);
5056 return FALSE;
5057 }
5058 if(!hMenu)
5059 {
5060 SetLastError(ERROR_INVALID_MENU_HANDLE);
5061 return FALSE;
5062 }
5063 return NtUserSetSystemMenu(hwnd, hMenu);
5064 }
5065
5066 //
5067 // Example for the Win32/User32 rewrite.
5068 // Def = TrackPopupMenuEx@24=NtUserTrackPopupMenuEx@24
5069 //
5070 //
5071 BOOL
5072 WINAPI
5073 NEWTrackPopupMenu(
5074 HMENU Menu,
5075 UINT Flags,
5076 int x,
5077 int y,
5078 int Reserved,
5079 HWND Wnd,
5080 CONST RECT *Rect)
5081 {
5082 return NtUserTrackPopupMenuEx( Menu,
5083 Flags,
5084 x,
5085 y,
5086 Wnd,
5087 NULL); // LPTPMPARAMS is null
5088 }
5089
5090
5091 /*
5092 * @implemented
5093 */
5094 DWORD
5095 WINAPI
5096 GetMenuContextHelpId(HMENU hmenu)
5097 {
5098 ROSMENUINFO mi;
5099 mi.cbSize = sizeof(ROSMENUINFO);
5100 mi.fMask = MIM_HELPID;
5101
5102 if(NtUserMenuInfo(hmenu, &mi, FALSE))
5103 {
5104 return mi.dwContextHelpID;
5105 }
5106 return 0;
5107 }
5108
5109 /*
5110 * @unimplemented
5111 */
5112 BOOL
5113 WINAPI
5114 MenuWindowProcA(
5115 HWND hWnd,
5116 ULONG_PTR Result,
5117 UINT Msg,
5118 WPARAM wParam,
5119 LPARAM lParam
5120 )
5121 {
5122 if ( Msg < WM_USER)
5123 {
5124 LRESULT lResult;
5125 lResult = PopupMenuWndProcA(hWnd, Msg, wParam, lParam );
5126 if (Result)
5127 {
5128 Result = (ULONG_PTR)lResult;
5129 return TRUE;
5130 }
5131 return FALSE;
5132 }
5133 return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, TRUE);
5134
5135 }
5136
5137 /*
5138 * @unimplemented
5139 */
5140 BOOL
5141 WINAPI
5142 MenuWindowProcW(
5143 HWND hWnd,
5144 ULONG_PTR Result,
5145 UINT Msg,
5146 WPARAM wParam,
5147 LPARAM lParam
5148 )
5149 {
5150 if ( Msg < WM_USER)
5151 {
5152 LRESULT lResult;
5153 lResult = PopupMenuWndProcW(hWnd, Msg, wParam, lParam );
5154 if (Result)
5155 {
5156 Result = (ULONG_PTR)lResult;
5157 return TRUE;
5158 }
5159 return FALSE;
5160 }
5161 return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, FALSE);
5162 }
5163
5164 /*
5165 * @implemented
5166 */
5167 BOOL
5168 WINAPI
5169 ChangeMenuW(
5170 HMENU hMenu,
5171 UINT cmd,
5172 LPCWSTR lpszNewItem,
5173 UINT cmdInsert,
5174 UINT flags)
5175 {
5176 /*
5177 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5178 for MF_DELETE. We should check the parameters for all others
5179 MF_* actions also (anybody got a doc on ChangeMenu?).
5180 */
5181
5182 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
5183 {
5184 case MF_APPEND :
5185 return AppendMenuW(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
5186
5187 case MF_DELETE :
5188 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
5189
5190 case MF_CHANGE :
5191 return ModifyMenuW(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
5192
5193 case MF_REMOVE :
5194 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
5195 flags &~ MF_REMOVE);
5196
5197 default : /* MF_INSERT */
5198 return InsertMenuW(hMenu, cmd, flags, cmdInsert, lpszNewItem);
5199 };
5200 }
5201
5202 /*
5203 * @implemented
5204 */
5205 BOOL
5206 WINAPI
5207 ChangeMenuA(
5208 HMENU hMenu,
5209 UINT cmd,
5210 LPCSTR lpszNewItem,
5211 UINT cmdInsert,
5212 UINT flags)
5213 {
5214 /*
5215 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5216 for MF_DELETE. We should check the parameters for all others
5217 MF_* actions also (anybody got a doc on ChangeMenu?).
5218 */
5219
5220 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
5221 {
5222 case MF_APPEND :
5223 return AppendMenuA(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
5224
5225 case MF_DELETE :
5226 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
5227
5228 case MF_CHANGE :
5229 return ModifyMenuA(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
5230
5231 case MF_REMOVE :
5232 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
5233 flags &~ MF_REMOVE);
5234
5235 default : /* MF_INSERT */
5236 return InsertMenuA(hMenu, cmd, flags, cmdInsert, lpszNewItem);
5237 };
5238 }
5239
5240
5241
5242
5243