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