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