- Fix a crash in SetMenuInfo.
[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, (LPCWSTR)&Common->MenuName);
3971
3972 return ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS);
3973 }
3974
3975
3976 /* FUNCTIONS *****************************************************************/
3977
3978 /*static BOOL
3979 MenuIsStringItem(ULONG TypeData)
3980 {
3981 return(MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType));
3982 }*/
3983
3984
3985 /*
3986 * @implemented
3987 */
3988 BOOL WINAPI
3989 AppendMenuA(HMENU hMenu,
3990 UINT uFlags,
3991 UINT_PTR uIDNewItem,
3992 LPCSTR lpNewItem)
3993 {
3994 return(InsertMenuA(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
3995 lpNewItem));
3996 }
3997
3998
3999 /*
4000 * @implemented
4001 */
4002 BOOL WINAPI
4003 AppendMenuW(HMENU hMenu,
4004 UINT uFlags,
4005 UINT_PTR uIDNewItem,
4006 LPCWSTR lpNewItem)
4007 {
4008 return(InsertMenuW(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
4009 lpNewItem));
4010 }
4011
4012
4013 /*
4014 * @implemented
4015 */
4016 DWORD WINAPI
4017 CheckMenuItem(HMENU hmenu,
4018 UINT uIDCheckItem,
4019 UINT uCheck)
4020 {
4021 return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck);
4022 }
4023
4024 static
4025 BOOL
4026 MenuCheckMenuRadioItem(HMENU hMenu, UINT idFirst, UINT idLast, UINT idCheck, UINT uFlags, BOOL bCheck, PUINT pChecked, PUINT pUnchecked, PUINT pMenuChanged)
4027 {
4028 UINT ItemCount, i;
4029 PROSMENUITEMINFO Items = NULL;
4030 UINT cChecked, cUnchecked;
4031 BOOL bRet = TRUE;
4032 //ROSMENUINFO mi;
4033
4034 if(idFirst > idLast)
4035 return FALSE;
4036
4037 ItemCount = GetMenuItemCount(hMenu);
4038
4039 //mi.cbSize = sizeof(ROSMENUINFO);
4040 //if(!NtUserMenuInfo(hmenu, &mi, FALSE)) return ret;
4041
4042
4043 if(MenuGetAllRosMenuItemInfo(hMenu, &Items) <= 0)
4044 {
4045 ERR("MenuGetAllRosMenuItemInfo failed\n");
4046 return FALSE;
4047 }
4048
4049 cChecked = cUnchecked = 0;
4050
4051 for (i = 0 ; i < ItemCount; i++)
4052 {
4053 BOOL check = FALSE;
4054 if (0 != (Items[i].fType & MF_MENUBARBREAK)) continue;
4055 if (0 != (Items[i].fType & MF_SEPARATOR)) continue;
4056
4057 if ((Items[i].fType & MF_POPUP) && (uFlags == MF_BYCOMMAND))
4058 {
4059 MenuCheckMenuRadioItem(Items[i].hSubMenu, idFirst, idLast, idCheck, uFlags, bCheck, pChecked, pUnchecked, pMenuChanged);
4060 continue;
4061 }
4062 if (uFlags & MF_BYPOSITION)
4063 {
4064 if (i < idFirst || i > idLast)
4065 continue;
4066
4067 if (i == idCheck)
4068 {
4069 cChecked++;
4070 check = TRUE;
4071 }
4072 else
4073 {
4074 cUnchecked++;
4075 }
4076 }
4077 else
4078 {
4079 if (Items[i].wID < idFirst || Items[i].wID > idLast)
4080 continue;
4081
4082 if (Items[i].wID == idCheck)
4083 {
4084 cChecked++;
4085 check = TRUE;
4086 }
4087 else
4088 {
4089 cUnchecked++;
4090 }
4091 }
4092
4093 if (!bCheck)
4094 continue;
4095
4096 Items[i].fMask = MIIM_STATE | MIIM_FTYPE;
4097 if (check)
4098 {
4099 Items[i].fType |= MFT_RADIOCHECK;
4100 Items[i].fState |= MFS_CHECKED;
4101 }
4102 else
4103 {
4104 Items[i].fState &= ~MFS_CHECKED;
4105 }
4106
4107 if(!MenuSetRosMenuItemInfo(hMenu, i ,&Items[i]))
4108 {
4109 ERR("MenuSetRosMenuItemInfo failed\n");
4110 bRet = FALSE;
4111 break;
4112 }
4113 }
4114 HeapFree(GetProcessHeap(), 0, Items);
4115
4116 *pChecked += cChecked;
4117 *pUnchecked += cUnchecked;
4118
4119 if (cChecked || cUnchecked)
4120 (*pMenuChanged)++;
4121
4122 return bRet;
4123 }
4124
4125 /*
4126 * @implemented
4127 */
4128 BOOL WINAPI
4129 CheckMenuRadioItem(HMENU hmenu,
4130 UINT idFirst,
4131 UINT idLast,
4132 UINT idCheck,
4133 UINT uFlags)
4134 {
4135 UINT cChecked = 0;
4136 UINT cUnchecked = 0;
4137 UINT cMenuChanged = 0;
4138
4139 if (!MenuCheckMenuRadioItem(hmenu, idFirst, idLast, idCheck, uFlags, FALSE, &cChecked, &cUnchecked, &cMenuChanged))
4140 return FALSE;
4141
4142 if (cMenuChanged > 1)
4143 return FALSE;
4144
4145 cMenuChanged = 0;
4146 cChecked = 0;
4147 cUnchecked = 0;
4148
4149 if (!MenuCheckMenuRadioItem(hmenu, idFirst, idLast, idCheck, uFlags, TRUE, &cChecked, &cUnchecked, &cMenuChanged))
4150 return FALSE;
4151
4152 return (cChecked != 0);
4153 }
4154
4155
4156 /*
4157 * @implemented
4158 */
4159 HMENU WINAPI
4160 CreateMenu(VOID)
4161 {
4162 MenuLoadBitmaps();
4163 return (HMENU)NtUserCallNoParam(NOPARAM_ROUTINE_CREATEMENU);
4164 }
4165
4166
4167 /*
4168 * @implemented
4169 */
4170 HMENU WINAPI
4171 CreatePopupMenu(VOID)
4172 {
4173 MenuLoadBitmaps();
4174 return (HMENU)NtUserCallNoParam(NOPARAM_ROUTINE_CREATEMENUPOPUP);
4175 }
4176
4177
4178 /*
4179 * @implemented
4180 */
4181 BOOL WINAPI
4182 DrawMenuBar(HWND hWnd)
4183 {
4184 // return (BOOL)NtUserCallHwndLock(hWnd, HWNDLOCK_ROUTINE_DRAWMENUBAR);
4185 ROSMENUINFO MenuInfo;
4186 HMENU hMenu;
4187 hMenu = GetMenu(hWnd);
4188 if (!hMenu)
4189 return FALSE;
4190 MenuGetRosMenuInfo(&MenuInfo, hMenu);
4191 MenuInfo.Height = 0; // make sure to recalc size
4192 MenuSetRosMenuInfo(&MenuInfo);
4193 /* The wine method doesn't work and I suspect it's more effort
4194 then hackfix solution
4195 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4196 SWP_NOZORDER | SWP_FRAMECHANGED );
4197 return TRUE;*/
4198 // FIXME: hackfix
4199 DefWndNCPaint(hWnd,(HRGN)-1,-1);
4200 return TRUE;
4201 }
4202
4203
4204 /*
4205 * @implemented
4206 */
4207 BOOL WINAPI
4208 EnableMenuItem(HMENU hMenu,
4209 UINT uIDEnableItem,
4210 UINT uEnable)
4211 {
4212 return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable);
4213 }
4214
4215 /*
4216 * @implemented
4217 */
4218 BOOL WINAPI
4219 EndMenu(VOID)
4220 {
4221 GUITHREADINFO guii;
4222 guii.cbSize = sizeof(GUITHREADINFO);
4223 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii) && guii.hwndMenuOwner)
4224 {
4225 PostMessageW(guii.hwndMenuOwner, WM_CANCELMODE, 0, 0);
4226 }
4227 return TRUE;
4228 }
4229
4230
4231 /*
4232 * @implemented
4233 */
4234 HMENU WINAPI
4235 GetMenu(HWND hWnd)
4236 {
4237 return NtUserGetMenu(hWnd);
4238 }
4239
4240
4241 /*
4242 * @implemented
4243 */
4244 LONG WINAPI
4245 GetMenuCheckMarkDimensions(VOID)
4246 {
4247 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK),
4248 GetSystemMetrics(SM_CYMENUCHECK)));
4249 }
4250
4251
4252 /*
4253 * @implemented
4254 */
4255 UINT WINAPI
4256 GetMenuDefaultItem(HMENU hMenu,
4257 UINT fByPos,
4258 UINT gmdiFlags)
4259 {
4260 return NtUserGetMenuDefaultItem(hMenu, fByPos, gmdiFlags);
4261 }
4262
4263
4264 /*
4265 * @implemented
4266 */
4267 BOOL WINAPI
4268 GetMenuInfo(HMENU hmenu,
4269 LPMENUINFO lpcmi)
4270 {
4271 ROSMENUINFO mi;
4272 BOOL res = FALSE;
4273
4274 if(!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
4275 return FALSE;
4276
4277 RtlZeroMemory(&mi, sizeof(MENUINFO));
4278 mi.cbSize = sizeof(MENUINFO);
4279 mi.fMask = lpcmi->fMask;
4280
4281 res = NtUserMenuInfo(hmenu, &mi, FALSE);
4282
4283 memcpy(lpcmi, &mi, sizeof(MENUINFO));
4284 return res;
4285 }
4286
4287
4288 /*
4289 * @implemented
4290 */
4291 int WINAPI
4292 GetMenuItemCount(HMENU Menu)
4293 {
4294 ROSMENUINFO MenuInfo;
4295
4296 return MenuGetRosMenuInfo(&MenuInfo, Menu) ? MenuInfo.MenuItemCount : 0;
4297 }
4298
4299
4300 /*
4301 * @implemented
4302 */
4303 UINT WINAPI
4304 GetMenuItemID(HMENU hMenu,
4305 int nPos)
4306 {
4307 ROSMENUITEMINFO mii;
4308
4309 mii.cbSize = sizeof(MENUITEMINFOW);
4310 mii.fMask = MIIM_ID | MIIM_SUBMENU;
4311
4312 if (! NtUserMenuItemInfo(hMenu, nPos, MF_BYPOSITION, &mii, FALSE))
4313 {
4314 return -1;
4315 }
4316
4317 if (NULL != mii.hSubMenu)
4318 {
4319 return -1;
4320 }
4321 if (0 == mii.wID)
4322 {
4323 return -1;
4324 }
4325
4326 return mii.wID;
4327 }
4328
4329
4330 /*
4331 * @implemented
4332 */
4333 BOOL WINAPI
4334 GetMenuItemInfoA(
4335 HMENU Menu,
4336 UINT Item,
4337 BOOL ByPosition,
4338 LPMENUITEMINFOA mii)
4339 {
4340 MENUITEMINFOW miiW;
4341 LPSTR AnsiBuffer;
4342 INT Count;
4343
4344 if (mii->cbSize != sizeof(MENUITEMINFOA) &&
4345 mii->cbSize != sizeof(MENUITEMINFOA) - sizeof(HBITMAP))
4346 {
4347 SetLastError(ERROR_INVALID_PARAMETER);
4348 return FALSE;
4349 }
4350
4351 if(!(mii->fMask & (MIIM_TYPE | MIIM_STRING)))
4352 {
4353 /* No text requested, just pass on */
4354 return NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) mii, FALSE);
4355 }
4356
4357 AnsiBuffer = mii->dwTypeData;
4358 Count = miiW.cch = mii->cch;
4359 RtlCopyMemory(&miiW, mii, mii->cbSize);
4360 miiW.dwTypeData = 0;
4361
4362 if (AnsiBuffer)
4363 {
4364 miiW.dwTypeData = RtlAllocateHeap(GetProcessHeap(), 0,
4365 miiW.cch * sizeof(WCHAR));
4366 if (miiW.dwTypeData == NULL) return FALSE;
4367 miiW.dwTypeData[0] = 0;
4368 }
4369
4370 if (!NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO)&miiW, FALSE))
4371 {
4372 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4373 return FALSE;
4374 }
4375
4376 RtlCopyMemory(mii, &miiW, miiW.cbSize);
4377
4378 if (!AnsiBuffer || !Count)
4379 {
4380 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4381 mii->dwTypeData = AnsiBuffer;
4382 mii->cch = miiW.cch;
4383 return TRUE;
4384 }
4385
4386 if ((miiW.fMask & MIIM_STRING) || (IS_STRING_ITEM(miiW.fType)))
4387 {
4388 if (miiW.cch)
4389 {
4390 if (!WideCharToMultiByte(CP_ACP, 0, miiW.dwTypeData, miiW.cch, AnsiBuffer, mii->cch, NULL, NULL))
4391 {
4392 AnsiBuffer[0] = 0;
4393 }
4394 if (Count > miiW.cch)
4395 {
4396 AnsiBuffer[miiW.cch] = 0;
4397 }
4398 mii->cch = mii->cch;
4399 }
4400 }
4401 else
4402 {
4403 AnsiBuffer[0] = 0;
4404 }
4405
4406 RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4407 mii->dwTypeData = AnsiBuffer;
4408
4409 return TRUE;
4410 }
4411
4412
4413 /*
4414 * @implemented
4415 */
4416 BOOL WINAPI
4417 GetMenuItemInfoW(
4418 HMENU Menu,
4419 UINT Item,
4420 BOOL ByPosition,
4421 LPMENUITEMINFOW mii)
4422 {
4423 MENUITEMINFOW miiW;
4424 LPWSTR String;
4425 INT Count;
4426
4427 if (mii->cbSize != sizeof(MENUITEMINFOW) &&
4428 mii->cbSize != sizeof(MENUITEMINFOW) - sizeof(HBITMAP))
4429 {
4430 SetLastError(ERROR_INVALID_PARAMETER);
4431 return FALSE;
4432 }
4433
4434 if(!(mii->fMask & (MIIM_TYPE | MIIM_STRING)))
4435 {
4436 /* No text requested, just pass on */
4437 return NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) mii, FALSE);
4438 }
4439
4440 String = mii->dwTypeData;
4441 Count = mii->cch;
4442 RtlCopyMemory(&miiW, mii, mii->cbSize);
4443 miiW.dwTypeData = 0;
4444
4445 if (String)
4446 {
4447 miiW.dwTypeData = RtlAllocateHeap(GetProcessHeap(), 0,
4448 miiW.cch * sizeof(WCHAR));
4449 if (miiW.dwTypeData == NULL) return FALSE;
4450 miiW.dwTypeData[0] = 0;
4451 }
4452
4453 if (!NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) &miiW, FALSE))
4454 {
4455 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4456 return FALSE;
4457 }
4458
4459 RtlCopyMemory(mii, &miiW, miiW.cbSize); // Okay to over write user data.
4460
4461 if (!String || !Count)
4462 {
4463 if (miiW.dwTypeData) RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4464 mii->dwTypeData = String; // may not be zero.
4465 mii->cch = miiW.cch;
4466 return TRUE;
4467 }
4468
4469 if ((miiW.fMask & MIIM_STRING) || (IS_STRING_ITEM(miiW.fType)))
4470 {
4471 lstrcpynW( String, miiW.dwTypeData, Count );
4472 }
4473
4474 RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
4475 mii->dwTypeData = String;
4476 mii->cch = strlenW(String);
4477 return TRUE;
4478 }
4479
4480
4481 /*
4482 * @implemented
4483 */
4484 UINT
4485 WINAPI
4486 GetMenuState(
4487 HMENU hMenu,
4488 UINT uId,
4489 UINT uFlags)
4490 {
4491 ROSMENUINFO MenuInfo;
4492 ROSMENUITEMINFO mii;
4493 memset( &mii, 0, sizeof(mii) );
4494 mii.cbSize = sizeof(MENUITEMINFOW);
4495 mii.fMask = MIIM_STATE | MIIM_FTYPE | MIIM_SUBMENU;
4496
4497 SetLastError(0);
4498 if(NtUserMenuItemInfo(hMenu, uId, uFlags, &mii, FALSE))
4499 {
4500 UINT nSubItems = 0;
4501 if(mii.hSubMenu)
4502 {
4503 if (! MenuGetRosMenuInfo(&MenuInfo, mii.hSubMenu))
4504 {
4505 return (UINT) -1;
4506 }
4507 nSubItems = MenuInfo.MenuItemCount;
4508
4509 /* FIXME - ported from wine, does that work (0xff)? */
4510 if(GetLastError() != ERROR_INVALID_MENU_HANDLE)
4511 return (nSubItems << 8) | ((mii.fState | mii.fType) & 0xff);
4512
4513 return (UINT)-1; /* Invalid submenu */
4514 }
4515
4516 /* FIXME - ported from wine, does that work? */
4517 return (mii.fType | mii.fState);
4518 }
4519
4520 return (UINT)-1;
4521 }
4522
4523
4524 /*
4525 * @implemented
4526 */
4527 int
4528 WINAPI
4529 GetMenuStringA(
4530 HMENU hMenu,
4531 UINT uIDItem,
4532 LPSTR lpString,
4533 int nMaxCount,
4534 UINT uFlag)
4535 {
4536 MENUITEMINFOA mii;
4537 memset( &mii, 0, sizeof(mii) );
4538 mii.dwTypeData = lpString;
4539 mii.fMask = MIIM_STRING | MIIM_FTYPE;
4540 mii.fType = MFT_STRING;
4541 mii.cbSize = sizeof(MENUITEMINFOA);
4542 mii.cch = nMaxCount;
4543
4544 if(!(GetMenuItemInfoA( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&mii)))
4545 return 0;
4546 else
4547 return mii.cch;
4548 }
4549
4550
4551 /*
4552 * @implemented
4553 */
4554 int
4555 WINAPI
4556 GetMenuStringW(
4557 HMENU hMenu,
4558 UINT uIDItem,
4559 LPWSTR lpString,
4560 int nMaxCount,
4561 UINT uFlag)
4562 {
4563 MENUITEMINFOW miiW;
4564 memset( &miiW, 0, sizeof(miiW) );
4565 miiW.dwTypeData = lpString;
4566 miiW.fMask = MIIM_STRING | MIIM_FTYPE;
4567 miiW.fType = MFT_STRING;
4568 miiW.cbSize = sizeof(MENUITEMINFOW);
4569 miiW.cch = nMaxCount;
4570
4571 if(!(GetMenuItemInfoW( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&miiW)))
4572 return 0;
4573 else
4574 return miiW.cch;
4575 }
4576
4577
4578 /*
4579 * @implemented
4580 */
4581 HMENU
4582 WINAPI
4583 GetSubMenu(
4584 HMENU hMenu,
4585 int nPos)
4586 {
4587 ROSMENUITEMINFO mi;
4588
4589 mi.cbSize = sizeof(MENUITEMINFOW);
4590 mi.fMask = MIIM_SUBMENU;
4591
4592 if (NtUserMenuItemInfo(hMenu, (UINT)nPos, MF_BYPOSITION, &mi, FALSE))
4593 {
4594 return IsMenu(mi.hSubMenu) ? mi.hSubMenu : NULL;
4595 }
4596
4597 return NULL;
4598 }
4599
4600 /*
4601 * @implemented
4602 */
4603 HMENU
4604 WINAPI
4605 GetSystemMenu(
4606 HWND hWnd,
4607 BOOL bRevert)
4608 {
4609 HMENU TopMenu;
4610
4611 TopMenu = NtUserGetSystemMenu(hWnd, bRevert);
4612
4613 return NULL == TopMenu ? NULL : GetSubMenu(TopMenu, 0);
4614 }
4615
4616
4617 /*
4618 * @implemented
4619 */
4620 BOOL
4621 WINAPI
4622 InsertMenuA(
4623 HMENU hMenu,
4624 UINT uPosition,
4625 UINT uFlags,
4626 UINT_PTR uIDNewItem,
4627 LPCSTR lpNewItem)
4628 {
4629 MENUITEMINFOA mii;
4630 memset( &mii, 0, sizeof(mii) );
4631 mii.cbSize = sizeof(MENUITEMINFOA);
4632 mii.fMask = MIIM_FTYPE;
4633
4634 MenuSetItemData((LPMENUITEMINFOW) &mii,
4635 uFlags,
4636 uIDNewItem,
4637 (LPCWSTR) lpNewItem,
4638 FALSE);
4639
4640 return InsertMenuItemA(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii);
4641 }
4642
4643
4644
4645 /*
4646 * @implemented
4647 */
4648 BOOL
4649 WINAPI
4650 InsertMenuItemA(
4651 HMENU hMenu,
4652 UINT uItem,
4653 BOOL fByPosition,
4654 LPCMENUITEMINFOA lpmii)
4655 {
4656 MENUITEMINFOW mi;
4657 UNICODE_STRING MenuText;
4658 BOOL res = FALSE;
4659 BOOL CleanHeap = FALSE;
4660 NTSTATUS Status;
4661
4662 if((lpmii->cbSize == sizeof(MENUITEMINFOA)) ||
4663 (lpmii->cbSize == sizeof(MENUITEMINFOA) - sizeof(HBITMAP)))
4664 {
4665 RtlCopyMemory ( &mi, lpmii, lpmii->cbSize );
4666
4667 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
4668 {
4669 mi.cbSize = sizeof( MENUITEMINFOW);
4670 mi.hbmpItem = NULL;
4671 }
4672 /* copy the text string */
4673 if (((mi.fMask & MIIM_STRING) ||
4674 ((mi.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mi.fType) == MF_STRING)))
4675 && mi.dwTypeData != NULL)
4676 {
4677 Status = RtlCreateUnicodeStringFromAsciiz(&MenuText, (LPSTR)mi.dwTypeData);
4678 if (!NT_SUCCESS (Status))
4679 {
4680 SetLastError (RtlNtStatusToDosError(Status));
4681 return FALSE;
4682 }
4683 mi.dwTypeData = MenuText.Buffer;
4684 mi.cch = MenuText.Length / sizeof(WCHAR);
4685 CleanHeap = TRUE;
4686 }
4687 res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mi, NULL);
4688
4689 if ( CleanHeap ) RtlFreeUnicodeString ( &MenuText );
4690 }
4691 return res;
4692 }
4693
4694
4695 /*
4696 * @implemented
4697 */
4698 BOOL
4699 WINAPI
4700 InsertMenuItemW(
4701 HMENU hMenu,
4702 UINT uItem,
4703 BOOL fByPosition,
4704 LPCMENUITEMINFOW lpmii)
4705 {
4706 MENUITEMINFOW mi;
4707 UNICODE_STRING MenuText;
4708 BOOL res = FALSE;
4709
4710 /* while we could just pass 'lpmii' to win32k, we make a copy so that
4711 if a bad user passes bad data, we crash his process instead of the
4712 entire kernel */
4713
4714 if((lpmii->cbSize == sizeof(MENUITEMINFOW)) ||
4715 (lpmii->cbSize == sizeof(MENUITEMINFOW) - sizeof(HBITMAP)))
4716 {
4717 RtlCopyMemory(&mi, lpmii, lpmii->cbSize);
4718
4719 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
4720 {
4721 mi.cbSize = sizeof( MENUITEMINFOW);
4722 mi.hbmpItem = NULL;
4723 }
4724 /* copy the text string */
4725 if (((mi.fMask & MIIM_STRING) ||
4726 ((mi.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mi.fType) == MF_STRING)))
4727 && mi.dwTypeData != NULL)
4728 {
4729 RtlInitUnicodeString(&MenuText, (PWSTR)lpmii->dwTypeData);
4730 mi.dwTypeData = MenuText.Buffer;
4731 mi.cch = MenuText.Length / sizeof(WCHAR);
4732 }
4733 res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mi, NULL);
4734 }
4735 return res;
4736 }
4737
4738
4739 /*
4740 * @implemented
4741 */
4742 BOOL
4743 WINAPI
4744 InsertMenuW(
4745 HMENU hMenu,
4746 UINT uPosition,
4747 UINT uFlags,
4748 UINT_PTR uIDNewItem,
4749 LPCWSTR lpNewItem)
4750 {
4751 MENUITEMINFOW mii;
4752 memset( &mii, 0, sizeof(mii) );
4753 mii.cbSize = sizeof(MENUITEMINFOW);
4754 mii.fMask = MIIM_FTYPE;
4755
4756 MenuSetItemData( &mii,
4757 uFlags,
4758 uIDNewItem,
4759 lpNewItem,
4760 TRUE);
4761
4762 return InsertMenuItemW(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii);
4763 }
4764
4765
4766 /*
4767 * @implemented
4768 */
4769 BOOL
4770 WINAPI
4771 IsMenu(
4772 HMENU Menu)
4773 {
4774 ROSMENUINFO MenuInfo;
4775
4776 return MenuGetRosMenuInfo(&MenuInfo, Menu);
4777 }
4778
4779
4780 /*
4781 * @implemented
4782 */
4783 HMENU WINAPI
4784 LoadMenuA(HINSTANCE hInstance,
4785 LPCSTR lpMenuName)
4786 {
4787 HANDLE Resource = FindResourceA(hInstance, lpMenuName, MAKEINTRESOURCEA(4));
4788 if (Resource == NULL)
4789 {
4790 return(NULL);
4791 }
4792 return(LoadMenuIndirectA((PVOID)LoadResource(hInstance, Resource)));
4793 }
4794
4795
4796 /*
4797 * @implemented
4798 */
4799 HMENU WINAPI
4800 LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate)
4801 {
4802 return(LoadMenuIndirectW(lpMenuTemplate));
4803 }
4804
4805
4806 /*
4807 * @implemented
4808 */
4809 HMENU WINAPI
4810 LoadMenuIndirectW(CONST MENUTEMPLATE *lpMenuTemplate)
4811 {
4812 HMENU hMenu;
4813 WORD version, offset;
4814 LPCSTR p = (LPCSTR)lpMenuTemplate;
4815
4816 version = GET_WORD(p);
4817 p += sizeof(WORD);
4818
4819 switch (version)
4820 {
4821 case 0: /* standard format is version of 0 */
4822 offset = GET_WORD(p);
4823 p += sizeof(WORD) + offset;
4824 if (!(hMenu = CreateMenu())) return 0;
4825 if (!MENU_ParseResource(p, hMenu, TRUE))
4826 {
4827 DestroyMenu(hMenu);
4828 return 0;
4829 }
4830 return hMenu;
4831 case 1: /* extended format is version of 1 */
4832 offset = GET_WORD(p);
4833 p += sizeof(WORD) + offset;
4834 if (!(hMenu = CreateMenu())) return 0;
4835 if (!MENUEX_ParseResource(p, hMenu))
4836 {
4837 DestroyMenu( hMenu );
4838 return 0;
4839 }
4840 return hMenu;
4841 default:
4842 DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version);
4843 return 0;
4844 }
4845 }
4846
4847
4848 /*
4849 * @implemented
4850 */
4851 HMENU WINAPI
4852 LoadMenuW(HINSTANCE hInstance,
4853 LPCWSTR lpMenuName)
4854 {
4855 HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU);
4856 if (Resource == NULL)
4857 {
4858 return(NULL);
4859 }
4860 return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource)));
4861 }
4862
4863
4864 /*
4865 * @implemented
4866 */
4867 int
4868 WINAPI
4869 MenuItemFromPoint(
4870 HWND hWnd,
4871 HMENU hMenu,
4872 POINT ptScreen)
4873 {
4874 return NtUserMenuItemFromPoint(hWnd, hMenu, ptScreen.x, ptScreen.y);
4875 }
4876
4877
4878 /*
4879 * @implemented
4880 */
4881 BOOL
4882 WINAPI
4883 ModifyMenuA(
4884 HMENU hMnu,
4885 UINT uPosition,
4886 UINT uFlags,
4887 UINT_PTR uIDNewItem,
4888 LPCSTR lpNewItem)
4889 {
4890 ROSMENUINFO mi;
4891 ROSMENUITEMINFO rmii;
4892 MENUITEMINFOA mii;
4893 memset( &mii, 0, sizeof(mii) );
4894 mii.cbSize = sizeof(MENUITEMINFOA);
4895 mii.fMask = MIIM_FTYPE;
4896
4897 if (!MenuGetRosMenuInfo( &mi, hMnu )) return FALSE;
4898
4899 mi.Height = 0;
4900
4901 if (!MenuSetRosMenuInfo( &mi )) return FALSE;
4902
4903 MenuInitRosMenuItemInfo( &rmii );
4904
4905 if(!MenuGetRosMenuItemInfo( hMnu, uPosition, &rmii)) return FALSE;
4906
4907 if ((rmii.fType & MF_POPUP) && (uFlags & MF_POPUP) && (rmii.hSubMenu != (HMENU)uIDNewItem))
4908 NtUserDestroyMenu( rmii.hSubMenu ); /* ModifyMenu() spec */
4909
4910 MenuCleanupRosMenuItemInfo( &rmii );
4911
4912 MenuSetItemData((LPMENUITEMINFOW) &mii,
4913 uFlags,
4914 uIDNewItem,
4915 (LPCWSTR) lpNewItem,
4916 FALSE);
4917
4918 return SetMenuItemInfoA( hMnu,
4919 uPosition,
4920 (BOOL)(MF_BYPOSITION & uFlags),
4921 &mii);
4922 }
4923
4924
4925 /*
4926 * @implemented
4927 */
4928 BOOL
4929 WINAPI
4930 ModifyMenuW(
4931 HMENU hMnu,
4932 UINT uPosition,
4933 UINT uFlags,
4934 UINT_PTR uIDNewItem,
4935 LPCWSTR lpNewItem)
4936 {
4937 ROSMENUINFO mi;
4938 ROSMENUITEMINFO rmii;
4939 MENUITEMINFOW mii;
4940 memset ( &mii, 0, sizeof(mii) );
4941 mii.cbSize = sizeof(MENUITEMINFOW);
4942 mii.fMask = MIIM_FTYPE;
4943
4944 if (!MenuGetRosMenuInfo( &mi, hMnu )) return FALSE;
4945
4946 mi.Height = 0; // Force size recalculation.
4947
4948 if (!MenuSetRosMenuInfo( &mi )) return FALSE;
4949
4950 MenuInitRosMenuItemInfo( &rmii );
4951
4952 if(!MenuGetRosMenuItemInfo( hMnu, uPosition, &rmii)) return FALSE;
4953
4954 if ((rmii.fType & MF_POPUP) && (uFlags & MF_POPUP) && (rmii.hSubMenu != (HMENU)uIDNewItem))
4955 NtUserDestroyMenu( rmii.hSubMenu ); /* ModifyMenu() spec */
4956
4957 MenuCleanupRosMenuItemInfo( &rmii );
4958
4959 /* Init new data for this menu item */
4960 MenuSetItemData( &mii,
4961 uFlags,
4962 uIDNewItem,
4963 lpNewItem,
4964 TRUE);
4965
4966 /* Now, make Win32k IntSetMenuItemInfo handle the changes to this menu item. */
4967 return SetMenuItemInfoW( hMnu,
4968 uPosition,
4969 (BOOL)(MF_BYPOSITION & uFlags),
4970 &mii);
4971 }
4972
4973
4974 /*
4975 * @implemented
4976 */
4977 BOOL WINAPI
4978 SetMenu(HWND hWnd,
4979 HMENU hMenu)
4980 {
4981 return NtUserSetMenu(hWnd, hMenu, TRUE);
4982 }
4983
4984
4985 /*
4986 * @implemented
4987 */
4988 BOOL
4989 WINAPI
4990 SetMenuInfo(
4991 HMENU hmenu,
4992 LPCMENUINFO lpcmi)
4993 {
4994 ROSMENUINFO mi;
4995 BOOL res = FALSE;
4996
4997 if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
4998 {
4999 SetLastError(ERROR_INVALID_PARAMETER);
5000 return res;
5001 }
5002
5003 memcpy(&mi, lpcmi, sizeof(MENUINFO));
5004 return NtUserMenuInfo(hmenu, &mi, TRUE);
5005 }
5006
5007
5008 /*
5009 * @implemented
5010 */
5011 BOOL
5012 WINAPI
5013 SetMenuItemBitmaps(
5014 HMENU hMenu,
5015 UINT uPosition,
5016 UINT uFlags,
5017 HBITMAP hBitmapUnchecked,
5018 HBITMAP hBitmapChecked)
5019 {
5020 ROSMENUITEMINFO uItem;
5021 memset ( &uItem, 0, sizeof(uItem) );
5022 uItem.fMask = MIIM_STATE | MIIM_BITMAP;
5023
5024 if(!(NtUserMenuItemInfo(hMenu, uPosition,
5025 (BOOL)(MF_BYPOSITION & uFlags), &uItem, FALSE))) return FALSE;
5026
5027 if (!hBitmapChecked && !hBitmapUnchecked)
5028 {
5029 uItem.fState &= ~MF_USECHECKBITMAPS;
5030 }
5031 else /* Install new bitmaps */
5032 {
5033 uItem.hbmpChecked = hBitmapChecked;
5034 uItem.hbmpUnchecked = hBitmapUnchecked;
5035 uItem.fState |= MF_USECHECKBITMAPS;
5036 }
5037 return NtUserMenuItemInfo(hMenu, uPosition,
5038 (BOOL)(MF_BYPOSITION & uFlags), &uItem, TRUE);
5039 }
5040
5041
5042 /*
5043 * @implemented
5044 */
5045 BOOL
5046 WINAPI
5047 SetMenuItemInfoA(
5048 HMENU hMenu,
5049 UINT uItem,
5050 BOOL fByPosition,
5051 LPCMENUITEMINFOA lpmii)
5052 {
5053 MENUITEMINFOW MenuItemInfoW;
5054 UNICODE_STRING UnicodeString;
5055 NTSTATUS Status;
5056 ULONG Result = FALSE;
5057
5058 RtlCopyMemory(&MenuItemInfoW, lpmii, min(lpmii->cbSize, sizeof(MENUITEMINFOW)));
5059
5060 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
5061 {
5062 MenuItemInfoW.cbSize = sizeof( MENUITEMINFOW);
5063 MenuItemInfoW.hbmpItem = NULL;
5064 }
5065 /*
5066 * MIIM_STRING == good
5067 * MIIM_TYPE & MFT_STRING == good
5068 * MIIM_STRING & MFT_STRING == good
5069 * MIIM_STRING & MFT_OWNERSRAW == good
5070 */
5071 if (((MenuItemInfoW.fMask & MIIM_STRING) ||
5072 ((MenuItemInfoW.fMask & MIIM_TYPE) &&
5073 (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
5074 && MenuItemInfoW.dwTypeData != NULL)
5075 {
5076 /* cch is ignored when the content of a menu item is set by calling SetMenuItemInfo. */
5077 Status = RtlCreateUnicodeStringFromAsciiz(&UnicodeString,
5078 (LPSTR)MenuItemInfoW.dwTypeData);
5079 if (!NT_SUCCESS (Status))
5080 {
5081 SetLastError (RtlNtStatusToDosError(Status));
5082 return FALSE;
5083 }
5084 MenuItemInfoW.dwTypeData = UnicodeString.Buffer;
5085 MenuItemInfoW.cch = UnicodeString.Length / sizeof(WCHAR);
5086 }
5087 else
5088 {
5089 UnicodeString.Buffer = NULL;
5090 }
5091
5092 Result = NtUserMenuItemInfo(hMenu, uItem, fByPosition,
5093 (PROSMENUITEMINFO)&MenuItemInfoW, TRUE);
5094
5095 if (UnicodeString.Buffer != NULL)
5096 {
5097 RtlFreeUnicodeString(&UnicodeString);
5098 }
5099
5100 return Result;
5101 }
5102
5103
5104 /*
5105 * @implemented
5106 */
5107 BOOL
5108 WINAPI
5109 SetMenuItemInfoW(
5110 HMENU hMenu,
5111 UINT uItem,
5112 BOOL fByPosition,
5113 LPCMENUITEMINFOW lpmii)
5114 {
5115 MENUITEMINFOW MenuItemInfoW;
5116 ULONG Result;
5117
5118 RtlCopyMemory(&MenuItemInfoW, lpmii, min(lpmii->cbSize, sizeof(MENUITEMINFOW)));
5119
5120 if( lpmii->cbSize != sizeof( MENUITEMINFOW))
5121 {
5122 MenuItemInfoW.cbSize = sizeof( MENUITEMINFOW);
5123 MenuItemInfoW.hbmpItem = NULL;
5124 }
5125
5126 if (((MenuItemInfoW.fMask & MIIM_STRING) ||
5127 ((MenuItemInfoW.fMask & MIIM_TYPE) &&
5128 (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
5129 && MenuItemInfoW.dwTypeData != NULL)
5130 {
5131 MenuItemInfoW.cch = strlenW(MenuItemInfoW.dwTypeData);
5132 }
5133 Result = NtUserMenuItemInfo(hMenu, uItem, fByPosition,
5134 (PROSMENUITEMINFO)&MenuItemInfoW, TRUE);
5135
5136 return Result;
5137 }
5138
5139 /*
5140 * @implemented
5141 */
5142 BOOL
5143 WINAPI
5144 SetSystemMenu (
5145 HWND hwnd,
5146 HMENU hMenu)
5147 {
5148 if(!hwnd)
5149 {
5150 SetLastError(ERROR_INVALID_WINDOW_HANDLE);
5151 return FALSE;
5152 }
5153 if(!hMenu)
5154 {
5155 SetLastError(ERROR_INVALID_MENU_HANDLE);
5156 return FALSE;
5157 }
5158 return NtUserSetSystemMenu(hwnd, hMenu);
5159 }
5160
5161
5162 /*
5163 * @implemented
5164 */
5165 BOOL
5166 WINAPI
5167 TrackPopupMenu(
5168 HMENU Menu,
5169 UINT Flags,
5170 int x,
5171 int y,
5172 int Reserved,
5173 HWND Wnd,
5174 CONST RECT *Rect)
5175 {
5176 BOOL ret = FALSE;
5177
5178 MenuInitTracking(Wnd, Menu, TRUE, Flags);
5179
5180 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
5181 if (0 == (Flags & TPM_NONOTIFY))
5182 {
5183 SendMessageW(Wnd, WM_INITMENUPOPUP, (WPARAM) Menu, 0);
5184 }
5185
5186 if (MenuShowPopup(Wnd, Menu, 0, x, y, 0, 0 ))
5187 {
5188 ret = MenuTrackMenu(Menu, Flags | TPM_POPUPMENU, 0, 0, Wnd, Rect);
5189 }
5190 MenuExitTracking(Wnd);
5191
5192 return ret;
5193 }
5194
5195
5196 /*
5197 * @unimplemented
5198 */
5199 BOOL
5200 WINAPI
5201 TrackPopupMenuEx(
5202 HMENU Menu,
5203 UINT Flags,
5204 int x,
5205 int y,
5206 HWND Wnd,
5207 LPTPMPARAMS Tpm)
5208 {
5209 /* Not fully implemented */
5210 return TrackPopupMenu(Menu, Flags, x, y, 0, Wnd,
5211 NULL != Tpm ? &Tpm->rcExclude : NULL);
5212 }
5213
5214 //
5215 // Example for the Win32/User32 rewrite.
5216 // Def = TrackPopupMenuEx@24=NtUserTrackPopupMenuEx@24
5217 //
5218 //
5219 BOOL
5220 WINAPI
5221 NEWTrackPopupMenu(
5222 HMENU Menu,
5223 UINT Flags,
5224 int x,
5225 int y,
5226 int Reserved,
5227 HWND Wnd,
5228 CONST RECT *Rect)
5229 {
5230 return NtUserTrackPopupMenuEx( Menu,
5231 Flags,
5232 x,
5233 y,
5234 Wnd,
5235 NULL); // LPTPMPARAMS is null
5236 }
5237
5238
5239 /*
5240 * @implemented
5241 */
5242 DWORD
5243 WINAPI
5244 GetMenuContextHelpId(HMENU hmenu)
5245 {
5246 ROSMENUINFO mi;
5247 mi.cbSize = sizeof(ROSMENUINFO);
5248 mi.fMask = MIM_HELPID;
5249
5250 if(NtUserMenuInfo(hmenu, &mi, FALSE))
5251 {
5252 return mi.dwContextHelpID;
5253 }
5254 return 0;
5255 }
5256
5257 /*
5258 * @unimplemented
5259 */
5260 BOOL
5261 WINAPI
5262 MenuWindowProcA(
5263 HWND hWnd,
5264 ULONG_PTR Result,
5265 UINT Msg,
5266 WPARAM wParam,
5267 LPARAM lParam
5268 )
5269 {
5270 if ( Msg < WM_USER)
5271 {
5272 LRESULT lResult;
5273 lResult = PopupMenuWndProcA(hWnd, Msg, wParam, lParam );
5274 if (Result)
5275 {
5276 Result = (ULONG_PTR)lResult;
5277 return TRUE;
5278 }
5279 return FALSE;
5280 }
5281 return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, TRUE);
5282
5283 }
5284
5285 /*
5286 * @unimplemented
5287 */
5288 BOOL
5289 WINAPI
5290 MenuWindowProcW(
5291 HWND hWnd,
5292 ULONG_PTR Result,
5293 UINT Msg,
5294 WPARAM wParam,
5295 LPARAM lParam
5296 )
5297 {
5298 if ( Msg < WM_USER)
5299 {
5300 LRESULT lResult;
5301 lResult = PopupMenuWndProcW(hWnd, Msg, wParam, lParam );
5302 if (Result)
5303 {
5304 Result = (ULONG_PTR)lResult;
5305 return TRUE;
5306 }
5307 return FALSE;
5308 }
5309 return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, FALSE);
5310 }
5311
5312 /*
5313 * @implemented
5314 */
5315 BOOL
5316 WINAPI
5317 ChangeMenuW(
5318 HMENU hMenu,
5319 UINT cmd,
5320 LPCWSTR lpszNewItem,
5321 UINT cmdInsert,
5322 UINT flags)
5323 {
5324 /*
5325 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5326 for MF_DELETE. We should check the parameters for all others
5327 MF_* actions also (anybody got a doc on ChangeMenu?).
5328 */
5329
5330 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
5331 {
5332 case MF_APPEND :
5333 return AppendMenuW(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
5334
5335 case MF_DELETE :
5336 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
5337
5338 case MF_CHANGE :
5339 return ModifyMenuW(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
5340
5341 case MF_REMOVE :
5342 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
5343 flags &~ MF_REMOVE);
5344
5345 default : /* MF_INSERT */
5346 return InsertMenuW(hMenu, cmd, flags, cmdInsert, lpszNewItem);
5347 };
5348 }
5349
5350 /*
5351 * @implemented
5352 */
5353 BOOL
5354 WINAPI
5355 ChangeMenuA(
5356 HMENU hMenu,
5357 UINT cmd,
5358 LPCSTR lpszNewItem,
5359 UINT cmdInsert,
5360 UINT flags)
5361 {
5362 /*
5363 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
5364 for MF_DELETE. We should check the parameters for all others
5365 MF_* actions also (anybody got a doc on ChangeMenu?).
5366 */
5367
5368 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
5369 {
5370 case MF_APPEND :
5371 return AppendMenuA(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
5372
5373 case MF_DELETE :
5374 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
5375
5376 case MF_CHANGE :
5377 return ModifyMenuA(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
5378
5379 case MF_REMOVE :
5380 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
5381 flags &~ MF_REMOVE);
5382
5383 default : /* MF_INSERT */
5384 return InsertMenuA(hMenu, cmd, flags, cmdInsert, lpszNewItem);
5385 };
5386 }
5387
5388
5389
5390
5391