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