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