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