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