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