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