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