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