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