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