Merge trunk HEAD (r46369)
[reactos.git] / reactos / dll / win32 / user32 / windows / menu.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS user32.dll
5 * FILE: user32/windows/menu.c
6 * PURPOSE: Menus
7 *
8 * PROGRAMMERS: Casper S. Hornstrup
9 * James Tabor
10 */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <user32.h>
15
16 #include <wine/debug.h>
17 WINE_DEFAULT_DEBUG_CHANNEL(user32);
18
19 LRESULT DefWndNCPaint(HWND hWnd, HRGN hRgn, BOOL Active);
20
21 /* internal popup menu window messages */
22 #define MM_SETMENUHANDLE (WM_USER + 0)
23 #define MM_GETMENUHANDLE (WM_USER + 1)
24
25 /* Internal MenuTrackMenu() flags */
26 #define TPM_INTERNAL 0xF0000000
27 #define TPM_ENTERIDLEEX 0x80000000 /* set owner window for WM_ENTERIDLE */
28 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
29 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
30
31 /* TYPES *********************************************************************/
32
33 #define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)
34
35 #define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK)
36 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
37 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
38
39 #define IS_SYSTEM_MENU(MenuInfo) \
40 (0 == ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
41
42 #define IS_SYSTEM_POPUP(MenuInfo) \
43 (0 != ((MenuInfo)->Flags & MF_POPUP) && 0 != ((MenuInfo)->Flags & MF_SYSMENU))
44
45 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
46
47 #define MENU_ITEM_HBMP_SPACE (5)
48 #define MENU_BAR_ITEMS_SPACE (12)
49 #define SEPARATOR_HEIGHT (5)
50 #define MENU_TAB_SPACE (8)
51
52 #define ITEM_PREV -1
53 #define ITEM_NEXT 1
54
55 #define MAKEINTATOMA(atom) ((LPCSTR)((ULONG_PTR)((WORD)(atom))))
56 #define MAKEINTATOMW(atom) ((LPCWSTR)((ULONG_PTR)((WORD)(atom))))
57 #define POPUPMENU_CLASS_ATOMA MAKEINTATOMA(32768) /* PopupMenu */
58 #define POPUPMENU_CLASS_ATOMW MAKEINTATOMW(32768) /* PopupMenu */
59
60 /* internal flags for menu tracking */
61
62 #define TF_ENDMENU 0x0001
63 #define TF_SUSPENDPOPUP 0x0002
64 #define TF_SKIPREMOVE 0x0004
65
66 typedef struct
67 {
68 UINT TrackFlags;
69 HMENU CurrentMenu; /* current submenu (can be equal to hTopMenu)*/
70 HMENU TopMenu; /* initial menu */
71 HWND OwnerWnd; /* where notifications are sent */
72 POINT Pt;
73 } MTRACKER;
74
75 //static LRESULT WINAPI PopupMenuWndProcA(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);
76 //static LRESULT WINAPI PopupMenuWndProcW(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
77
78 /*********************************************************************
79 * PopupMenu class descriptor
80 */
81 const struct builtin_class_descr POPUPMENU_builtin_class =
82 {
83 POPUPMENU_CLASS_ATOMW, /* name */
84 CS_SAVEBITS | CS_DBLCLKS, /* style */
85 (WNDPROC) NULL, /* FIXME - procA */
86 (WNDPROC) PopupMenuWndProcW, /* FIXME - procW */
87 sizeof(MENUINFO *), /* extra */
88 (LPCWSTR) IDC_ARROW, /* cursor */
89 (HBRUSH)(COLOR_MENU + 1) /* brush */
90 };
91
92
93 /* INTERNAL FUNCTIONS ********************************************************/
94
95 /* Rip the fun and easy to use and fun WINE unicode string manipulation routines.
96 * Of course I didnt copy the ASM code because we want this to be portable
97 * and it needs to go away.
98 */
99
100 #ifndef GET_WORD
101 #define GET_WORD(ptr) (*(WORD *)(ptr))
102 #endif
103 #ifndef GET_DWORD
104 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
105 #endif
106
107 HFONT hMenuFont = NULL;
108 HFONT hMenuFontBold = NULL;
109
110 /* Flag set by EndMenu() to force an exit from menu tracking */
111 static BOOL fEndMenu = FALSE;
112
113 /* Use global popup window because there's no way 2 menus can
114 * be tracked at the same time. */
115 static HWND TopPopup;
116
117 /* Dimension of the menu bitmaps */
118 static HBITMAP BmpSysMenu = NULL;
119
120 static SIZE MenuCharSize;
121
122 /***********************************************************************
123 * MenuGetRosMenuInfo
124 *
125 * Get full information about menu
126 */
127 static BOOL FASTCALL
128 MenuGetRosMenuInfo(PROSMENUINFO MenuInfo, HMENU Menu)
129 {
130 MenuInfo->cbSize = sizeof(ROSMENUINFO);
131 MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE;
132
133 return NtUserMenuInfo(Menu, MenuInfo, FALSE);
134 }
135
136 /***********************************************************************
137 * MenuSetRosMenuInfo
138 *
139 * Set full information about menu
140 */
141 static BOOL FASTCALL
142 MenuSetRosMenuInfo(PROSMENUINFO MenuInfo)
143 {
144 MenuInfo->cbSize = sizeof(ROSMENUINFO);
145 MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE;
146
147 return NtUserMenuInfo(MenuInfo->Self, MenuInfo, TRUE);
148 }
149
150 /***********************************************************************
151 * MenuInitRosMenuItemInfo
152 *
153 * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo
154 */
155 static VOID FASTCALL
156 MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
157 {
158 ZeroMemory(ItemInfo, sizeof(ROSMENUITEMINFO));
159 ItemInfo->cbSize = sizeof(ROSMENUITEMINFO);
160 }
161
162 /***********************************************************************
163 * MenuGetRosMenuItemInfo
164 *
165 * Get full information about a menu item
166 */
167 static BOOL FASTCALL
168 MenuGetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo)
169 {
170 UINT Save_Mask = ItemInfo->fMask; /* Save the org mask bits. */
171
172 if (ItemInfo->dwTypeData != NULL)
173 {
174 HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData);
175 }
176
177
178 ItemInfo->fMask = MIIM_BITMAP | MIIM_CHECKMARKS | MIIM_DATA | MIIM_FTYPE
179 | MIIM_ID | MIIM_STATE | MIIM_STRING | MIIM_SUBMENU | MIIM_TYPE;
180 ItemInfo->dwTypeData = NULL;
181
182 if (! NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, FALSE))
183 {
184 ItemInfo->fType = 0;
185 return FALSE;
186 }
187
188 if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING)
189 {
190 ItemInfo->cch++;
191 ItemInfo->dwTypeData = HeapAlloc(GetProcessHeap(), 0,
192 ItemInfo->cch * sizeof(WCHAR));
193 if (NULL == ItemInfo->dwTypeData)
194 {
195 return FALSE;
196 }
197
198 if (! NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, FALSE))
199 {
200 ItemInfo->fType = 0;
201 return FALSE;
202 }
203 ItemInfo->dwTypeData[ItemInfo->cch - 1] = UNICODE_NULL;
204 }
205 ItemInfo->fMask = Save_Mask;
206 return TRUE;
207 }
208
209 /***********************************************************************
210 * MenuSetRosMenuItemInfo
211 *
212 * Set selected information about a menu item, need to set the mask bits.
213 */
214 static BOOL FASTCALL
215 MenuSetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo)
216 {
217 BOOL Ret;
218
219 if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING &&
220 ItemInfo->dwTypeData != NULL)
221 {
222 ItemInfo->cch = strlenW(ItemInfo->dwTypeData);
223 }
224 Ret = NtUserMenuItemInfo(Menu, Index, TRUE, ItemInfo, TRUE);
225
226 return Ret;
227 }
228
229 /***********************************************************************
230 * MenuCleanupRosMenuItemInfo
231 *
232 * Cleanup after use of MenuGet/SetRosMenuItemInfo
233 */
234 static VOID FASTCALL
235 MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
236 {
237 if (ItemInfo->dwTypeData != NULL)
238 {
239 HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData);
240 ItemInfo->dwTypeData = NULL;
241 }
242 }
243
244 /***********************************************************************
245 * MenuGetAllRosMenuItemInfo
246 *
247 * Get full information about all menu items
248 */
249 static INT FASTCALL
250 MenuGetAllRosMenuItemInfo(HMENU Menu, PROSMENUITEMINFO *ItemInfo)
251 {
252 DWORD BufSize;
253
254 BufSize = NtUserBuildMenuItemList(Menu, (VOID *) 1, 0, 0);
255 if (BufSize == (DWORD) -1 || BufSize == 0)
256 {
257 return -1;
258 }
259 *ItemInfo = HeapAlloc(GetProcessHeap(), 0, BufSize);
260 if (NULL == *ItemInfo)
261 {
262 return -1;
263 }
264
265 return NtUserBuildMenuItemList(Menu, *ItemInfo, BufSize, 0);
266 }
267
268 /***********************************************************************
269 * MenuCleanupAllRosMenuItemInfo
270 *
271 * Cleanup after use of MenuGetAllRosMenuItemInfo
272 */
273 static VOID FASTCALL
274 MenuCleanupAllRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
275 {
276 HeapFree(GetProcessHeap(), 0, ItemInfo);
277 }
278
279
280 /***********************************************************************
281 * MenuLoadBitmaps
282 *
283 * Load the arrow bitmap. We can't do this from MenuInit since user32
284 * can also be used (and thus initialized) from text-mode.
285 */
286 static void FASTCALL
287 MenuLoadBitmaps(VOID)
288 {
289 /* Load system buttons bitmaps */
290 if (NULL == BmpSysMenu)
291 {
292 BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
293 }
294 }
295
296 /***********************************************************************
297 * MenuGetBitmapItemSize
298 *
299 * Get the size of a bitmap item.
300 */
301 static void FASTCALL
302 MenuGetBitmapItemSize(PROSMENUITEMINFO lpitem, SIZE *Size, HWND WndOwner)
303 {
304 BITMAP Bm;
305 HBITMAP Bmp = lpitem->hbmpItem;
306
307 Size->cx = Size->cy = 0;
308
309 /* check if there is a magic menu item associated with this item */
310 if (IS_MAGIC_BITMAP(Bmp))
311 {
312 switch((INT_PTR) Bmp)
313 {
314 case (INT_PTR)HBMMENU_CALLBACK:
315 {
316 MEASUREITEMSTRUCT measItem;
317 measItem.CtlType = ODT_MENU;
318 measItem.CtlID = 0;
319 measItem.itemID = lpitem->wID;
320 measItem.itemWidth = lpitem->Rect.right - lpitem->Rect.left;
321 measItem.itemHeight = lpitem->Rect.bottom - lpitem->Rect.top;
322 measItem.itemData = lpitem->dwItemData;
323 SendMessageW( WndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
324 Size->cx = measItem.itemWidth;
325 Size->cy = measItem.itemHeight;
326 return;
327 }
328 break;
329
330 case (INT_PTR) HBMMENU_SYSTEM:
331 if (0 != lpitem->dwItemData)
332 {
333 Bmp = (HBITMAP)(ULONG_PTR) lpitem->dwItemData;
334 break;
335 }
336 /* fall through */
337 case (INT_PTR) HBMMENU_MBAR_RESTORE:
338 case (INT_PTR) HBMMENU_MBAR_MINIMIZE:
339 case (INT_PTR) HBMMENU_MBAR_CLOSE:
340 case (INT_PTR) HBMMENU_MBAR_MINIMIZE_D:
341 case (INT_PTR) HBMMENU_MBAR_CLOSE_D:
342 case (INT_PTR) HBMMENU_POPUP_CLOSE:
343 case (INT_PTR) HBMMENU_POPUP_RESTORE:
344 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
345 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
346 /* FIXME: Why we need to subtract these magic values? */
347 /* to make them smaller than the menu bar? */
348 Size->cx = GetSystemMetrics(SM_CXSIZE) - 2;
349 Size->cy = GetSystemMetrics(SM_CYSIZE) - 4;
350 return;
351 }
352 }
353
354 if (GetObjectW(Bmp, sizeof(BITMAP), &Bm))
355 {
356 Size->cx = Bm.bmWidth;
357 Size->cy = Bm.bmHeight;
358 }
359 }
360
361 /***********************************************************************
362 * MenuDrawPopupGlyph
363 *
364 * Draws popup magic glyphs (can be found in system menu).
365 */
366 static void FASTCALL
367 MenuDrawPopupGlyph(HDC dc, LPRECT r, INT_PTR popupMagic, BOOL inactive, BOOL hilite)
368 {
369 LOGFONTW lf;
370 HFONT hFont, hOldFont;
371 COLORREF clrsave;
372 INT bkmode;
373 TCHAR symbol;
374 switch (popupMagic)
375 {
376 case (INT_PTR) HBMMENU_POPUP_RESTORE:
377 symbol = '2';
378 break;
379 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
380 symbol = '0';
381 break;
382 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
383 symbol = '1';
384 break;
385 case (INT_PTR) HBMMENU_POPUP_CLOSE:
386 symbol = 'r';
387 break;
388 default:
389 ERR("Invalid popup magic bitmap %d\n", (int)popupMagic);
390 return;
391 }
392 ZeroMemory(&lf, sizeof(LOGFONTW));
393 InflateRect(r, -2, -2);
394 lf.lfHeight = r->bottom - r->top;
395 lf.lfWidth = 0;
396 lf.lfWeight = FW_NORMAL;
397 lf.lfCharSet = DEFAULT_CHARSET;
398 lstrcpy(lf.lfFaceName, TEXT("Marlett"));
399 hFont = CreateFontIndirect(&lf);
400 /* save font and text color */
401 hOldFont = SelectObject(dc, hFont);
402 clrsave = GetTextColor(dc);
403 bkmode = GetBkMode(dc);
404 /* set color and drawing mode */
405 SetBkMode(dc, TRANSPARENT);
406 if (inactive)
407 {
408 /* draw shadow */
409 if (!hilite)
410 {
411 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
412 TextOut(dc, r->left + 1, r->top + 1, &symbol, 1);
413 }
414 }
415 SetTextColor(dc, GetSysColor(inactive ? COLOR_GRAYTEXT : (hilite ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT)));
416 /* draw selected symbol */
417 TextOut(dc, r->left, r->top, &symbol, 1);
418 /* restore previous settings */
419 SetTextColor(dc, clrsave);
420 SelectObject(dc, hOldFont);
421 SetBkMode(dc, bkmode);
422 DeleteObject(hFont);
423 }
424
425 /***********************************************************************
426 * MenuDrawBitmapItem
427 *
428 * Draw a bitmap item.
429 */
430 static void FASTCALL
431 MenuDrawBitmapItem(HDC Dc, PROSMENUITEMINFO Item, const RECT *Rect,
432 HMENU hmenu, HWND WndOwner, UINT odaction, BOOL MenuBar)
433 {
434 BITMAP Bm;
435 DWORD Rop;
436 HDC DcMem;
437 HBITMAP Bmp;
438 int w = Rect->right - Rect->left;
439 int h = Rect->bottom - Rect->top;
440 int BmpXoffset = 0;
441 int Left, Top;
442 HBITMAP hbmpToDraw = (HBITMAP) Item->hbmpItem;
443 Bmp = hbmpToDraw;
444
445 /* Check if there is a magic menu item associated with this item */
446 if (IS_MAGIC_BITMAP(hbmpToDraw))
447 {
448 UINT Flags = 0;
449 RECT r;
450
451 r = *Rect;
452 switch ((INT_PTR)hbmpToDraw)
453 {
454 case (INT_PTR) HBMMENU_SYSTEM:
455 if (NULL != Item->dwTypeData)
456 {
457 Bmp = (HBITMAP)Item->dwTypeData;
458 if (! GetObjectW(Bmp, sizeof(BITMAP), &Bm))
459 {
460 return;
461 }
462 }
463 else
464 {
465 if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
466 Bmp = BmpSysMenu;
467 if (! GetObjectW(Bmp, sizeof(BITMAP), &Bm))
468 {
469 return;
470 }
471 /* only use right half of the bitmap */
472 BmpXoffset = Bm.bmWidth / 2;
473 Bm.bmWidth -= BmpXoffset;
474 }
475 goto got_bitmap;
476 case (INT_PTR) HBMMENU_MBAR_RESTORE:
477 Flags = DFCS_CAPTIONRESTORE;
478 break;
479 case (INT_PTR) HBMMENU_MBAR_MINIMIZE:
480 r.right += 1;
481 Flags = DFCS_CAPTIONMIN;
482 break;
483 case (INT_PTR) HBMMENU_MBAR_MINIMIZE_D:
484 r.right += 1;
485 Flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
486 break;
487 case (INT_PTR) HBMMENU_MBAR_CLOSE:
488 Flags = DFCS_CAPTIONCLOSE;
489 break;
490 case (INT_PTR) HBMMENU_MBAR_CLOSE_D:
491 Flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
492 break;
493 case (INT_PTR) HBMMENU_CALLBACK:
494 {
495 DRAWITEMSTRUCT drawItem;
496 POINT origorg;
497 drawItem.CtlType = ODT_MENU;
498 drawItem.CtlID = 0;
499 drawItem.itemID = Item->wID;
500 drawItem.itemAction = odaction;
501 drawItem.itemState = (Item->fState & MF_CHECKED)?ODS_CHECKED:0;
502 drawItem.itemState |= (Item->fState & MF_DEFAULT)?ODS_DEFAULT:0;
503 drawItem.itemState |= (Item->fState & MF_DISABLED)?ODS_DISABLED:0;
504 drawItem.itemState |= (Item->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
505 drawItem.itemState |= (Item->fState & MF_HILITE)?ODS_SELECTED:0;
506 drawItem.hwndItem = (HWND)hmenu;
507 drawItem.hDC = Dc;
508 drawItem.rcItem = *Rect;
509 drawItem.itemData = Item->dwItemData;
510 /* some applications make this assumption on the DC's origin */
511 SetViewportOrgEx( Dc, Item->Rect.left, Item->Rect.top, &origorg);
512 OffsetRect( &drawItem.rcItem, - Item->Rect.left, - Item->Rect.top);
513 SendMessageW( WndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
514 SetViewportOrgEx( Dc, origorg.x, origorg.y, NULL);
515 return;
516 }
517 break;
518
519 case (INT_PTR) HBMMENU_POPUP_CLOSE:
520 case (INT_PTR) HBMMENU_POPUP_RESTORE:
521 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
522 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
523 MenuDrawPopupGlyph(Dc, &r, (INT_PTR)hbmpToDraw, Item->fState & MF_GRAYED, Item->fState & MF_HILITE);
524 return;
525 }
526 InflateRect(&r, -1, -1);
527 if (0 != (Item->fState & MF_HILITE))
528 {
529 Flags |= DFCS_PUSHED;
530 }
531 DrawFrameControl(Dc, &r, DFC_CAPTION, Flags);
532 return;
533 }
534
535 if (NULL == Bmp || ! GetObjectW(Bmp, sizeof(BITMAP), &Bm))
536 {
537 return;
538 }
539
540 got_bitmap:
541 DcMem = CreateCompatibleDC(Dc);
542 SelectObject(DcMem, Bmp);
543
544 /* handle fontsize > bitmap_height */
545 Top = (Bm.bmHeight < h) ? Rect->top + (h - Bm.bmHeight) / 2 : Rect->top;
546 Left = Rect->left;
547 Rop= ((Item->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmpToDraw)) ? NOTSRCCOPY : SRCCOPY;
548 if ((Item->fState & MF_HILITE) && Item->hbmpItem)
549 {
550 SetBkColor(Dc, GetSysColor(COLOR_HIGHLIGHT));
551 }
552 BitBlt(Dc, Left, Top, w, h, DcMem, BmpXoffset, 0, Rop);
553 DeleteDC(DcMem);
554 }
555
556 /***********************************************************************
557 * MenuDrawMenuItem
558 *
559 * Draw a single menu item.
560 */
561 static void FASTCALL
562 MenuDrawMenuItem(HWND hWnd, PROSMENUINFO MenuInfo, HWND WndOwner, HDC Dc,
563 PROSMENUITEMINFO Item, UINT Height, BOOL MenuBar, UINT Action)
564 {
565 RECT Rect;
566 PWCHAR Text;
567 BOOL flat_menu = FALSE;
568 int bkgnd;
569 PWND Wnd = ValidateHwnd(hWnd);
570
571 if (!Wnd)
572 return;
573
574 if (0 != (Item->fType & MF_SYSMENU))
575 {
576 if ( (Wnd->style & WS_MINIMIZE))
577 {
578 UserGetInsideRectNC(Wnd, &Rect);
579 UserDrawSysMenuButton(hWnd, Dc, &Rect,
580 Item->fState & (MF_HILITE | MF_MOUSESELECT));
581 }
582 return;
583 }
584
585 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
586 bkgnd = (MenuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
587
588 /* Setup colors */
589
590 if (0 != (Item->fState & MF_HILITE))
591 {
592 if (MenuBar && !flat_menu)
593 {
594 SetTextColor(Dc, GetSysColor(COLOR_MENUTEXT));
595 SetBkColor(Dc, GetSysColor(COLOR_MENU));
596 }
597 else
598 {
599 if (0 != (Item->fState & MF_GRAYED))
600 {
601 SetTextColor(Dc, GetSysColor(COLOR_GRAYTEXT));
602 }
603 else
604 {
605 SetTextColor(Dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
606 }
607 SetBkColor(Dc, GetSysColor(COLOR_HIGHLIGHT));
608 }
609 }
610 else
611 {
612 if (0 != (Item->fState & MF_GRAYED))
613 {
614 SetTextColor(Dc, GetSysColor(COLOR_GRAYTEXT));
615 }
616 else
617 {
618 SetTextColor(Dc, GetSysColor(COLOR_MENUTEXT));
619 }
620 SetBkColor(Dc, GetSysColor(bkgnd));
621 }
622
623 Rect = Item->Rect;
624
625 if (Item->fType & MF_OWNERDRAW)
626 {
627 /*
628 ** Experimentation under Windows reveals that an owner-drawn
629 ** menu is given the rectangle which includes the space it requested
630 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
631 ** and a popup-menu arrow. This is the value of lpitem->rect.
632 ** Windows will leave all drawing to the application except for
633 ** the popup-menu arrow. Windows always draws that itself, after
634 ** the menu owner has finished drawing.
635 */
636 DRAWITEMSTRUCT dis;
637
638 dis.CtlType = ODT_MENU;
639 dis.CtlID = 0;
640 dis.itemID = Item->wID;
641 dis.itemData = (DWORD)Item->dwItemData;
642 dis.itemState = 0;
643 if (0 != (Item->fState & MF_CHECKED))
644 {
645 dis.itemState |= ODS_CHECKED;
646 }
647 if (0 != (Item->fState & MF_GRAYED))
648 {
649 dis.itemState |= ODS_GRAYED | ODS_DISABLED;
650 }
651 if (0 != (Item->fState & MF_HILITE))
652 {
653 dis.itemState |= ODS_SELECTED;
654 }
655 dis.itemAction = Action; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
656 dis.hwndItem = (HWND) MenuInfo->Self;
657 dis.hDC = Dc;
658 dis.rcItem = Rect;
659 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
660 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hWnd,
661 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
662 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
663 dis.rcItem.bottom);
664 SendMessageW(WndOwner, WM_DRAWITEM, 0, (LPARAM) &dis);
665 /* Draw the popup-menu arrow */
666 if (0 != (Item->fType & MF_POPUP))
667 {
668 RECT rectTemp;
669 CopyRect(&rectTemp, &Rect);
670 rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK);
671 DrawFrameControl(Dc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
672 }
673 return;
674 }
675
676 TRACE("rect={%ld,%ld,%ld,%ld}\n", Item->Rect.left, Item->Rect.top,
677 Item->Rect.right, Item->Rect.bottom);
678
679 if (MenuBar && 0 != (Item->fType & MF_SEPARATOR))
680 {
681 return;
682 }
683
684 if (Item->fState & MF_HILITE)
685 {
686 if (flat_menu)
687 {
688 InflateRect (&Rect, -1, -1);
689 FillRect(Dc, &Rect, GetSysColorBrush(COLOR_MENUHILIGHT));
690 InflateRect (&Rect, 1, 1);
691 FrameRect(Dc, &Rect, GetSysColorBrush(COLOR_HIGHLIGHT));
692 }
693 else
694 {
695 if (MenuBar)
696 {
697 DrawEdge(Dc, &Rect, BDR_SUNKENOUTER, BF_RECT);
698 }
699 else
700 {
701 FillRect(Dc, &Rect, GetSysColorBrush(COLOR_HIGHLIGHT));
702 }
703 }
704 }
705 else
706 {
707 FillRect(Dc, &Rect, GetSysColorBrush(bkgnd));
708 }
709
710 SetBkMode(Dc, TRANSPARENT);
711
712 /* vertical separator */
713 if (! MenuBar && 0 != (Item->fType & MF_MENUBARBREAK))
714 {
715 HPEN oldPen;
716 RECT rc = Rect;
717 rc.left -= 3;
718 rc.top = 3;
719 rc.bottom = Height - 3;
720 if (flat_menu)
721 {
722 oldPen = SelectObject( Dc, GetStockObject(DC_PEN) );
723 SetDCPenColor(Dc, GetSysColor(COLOR_BTNSHADOW));
724 MoveToEx( Dc, rc.left, rc.top, NULL );
725 LineTo( Dc, rc.left, rc.bottom );
726 SelectObject( Dc, oldPen );
727 }
728 else
729 DrawEdge(Dc, &rc, EDGE_ETCHED, BF_LEFT);
730 }
731
732 /* horizontal separator */
733 if (0 != (Item->fType & MF_SEPARATOR))
734 {
735 HPEN oldPen;
736 RECT rc = Rect;
737 rc.left++;
738 rc.right--;
739 rc.top += SEPARATOR_HEIGHT / 2;
740 if (flat_menu)
741 {
742 oldPen = SelectObject( Dc, GetStockObject(DC_PEN) );
743 SetDCPenColor(Dc, GetSysColor(COLOR_BTNSHADOW));
744 MoveToEx( Dc, rc.left, rc.top, NULL );
745 LineTo( Dc, rc.right, rc.top );
746 SelectObject( Dc, oldPen );
747 }
748 else
749 DrawEdge(Dc, &rc, EDGE_ETCHED, BF_TOP);
750 return;
751 }
752
753 #if 0
754 /* helper lines for debugging */
755 /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
756 FrameRect(Dc, &Rect, GetStockObject(BLACK_BRUSH));
757 SelectObject(Dc, GetStockObject(DC_PEN));
758 SetDCPenColor(Dc, GetSysColor(COLOR_WINDOWFRAME));
759 MoveToEx(Dc, Rect.left, (Rect.top + Rect.bottom) / 2, NULL);
760 LineTo(Dc, Rect.right, (Rect.top + Rect.bottom) / 2);
761 #endif
762
763 if (! MenuBar)
764 {
765 INT y = Rect.top + Rect.bottom;
766 RECT Rc = Rect;
767 UINT CheckBitmapWidth = GetSystemMetrics(SM_CXMENUCHECK);
768 UINT CheckBitmapHeight = GetSystemMetrics(SM_CYMENUCHECK);
769 int checked = FALSE;
770 /* Draw the check mark
771 *
772 * FIXME:
773 * Custom checkmark bitmaps are monochrome but not always 1bpp.
774 */
775 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
776 {
777 HBITMAP bm = 0 != (Item->fState & MF_CHECKED) ? Item->hbmpChecked : Item->hbmpUnchecked;
778 if (NULL != bm) /* we have a custom bitmap */
779 {
780 HDC DcMem = CreateCompatibleDC(Dc);
781 SelectObject(DcMem, bm);
782 BitBlt(Dc, Rc.left, (y - CheckBitmapHeight) / 2,
783 CheckBitmapWidth, CheckBitmapHeight,
784 DcMem, 0, 0, SRCCOPY);
785 DeleteDC(DcMem);
786 checked = TRUE;
787 }
788 else if (0 != (Item->fState & MF_CHECKED)) /* standard bitmaps */
789 {
790 RECT rectTemp;
791 CopyRect(&rectTemp, &Rect);
792 rectTemp.right = rectTemp.left + GetSystemMetrics(SM_CXMENUCHECK);
793 DrawFrameControl(Dc, &rectTemp, DFC_MENU,
794 0 != (Item->fType & MFT_RADIOCHECK) ?
795 DFCS_MENUBULLET : DFCS_MENUCHECK);
796 checked = TRUE;
797 }
798 }
799 if (Item->hbmpItem)
800 {
801 RECT bmpRect;
802 CopyRect(&bmpRect, &Rect);
803 if (!(MenuInfo->dwStyle & MNS_CHECKORBMP) && !(MenuInfo->dwStyle & MNS_NOCHECK))
804 bmpRect.left += CheckBitmapWidth + 2;
805 if (!(checked && (MenuInfo->dwStyle & MNS_CHECKORBMP)))
806 {
807 bmpRect.right = bmpRect.left + MenuInfo->maxBmpSize.cx;
808 MenuDrawBitmapItem(Dc, Item, &bmpRect, MenuInfo->Self, WndOwner, Action, MenuBar);
809 }
810 }
811 /* Draw the popup-menu arrow */
812 if (0 != (Item->fType & MF_POPUP))
813 {
814 RECT rectTemp;
815 CopyRect(&rectTemp, &Rect);
816 rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK);
817 DrawFrameControl(Dc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
818 }
819 Rect.left += 4;
820 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
821 Rect.left += CheckBitmapWidth;
822 Rect.right -= CheckBitmapWidth;
823 }
824 else if (Item->hbmpItem) /* Draw the bitmap */
825 {
826 MenuDrawBitmapItem(Dc, Item, &Rect, MenuInfo->Self, WndOwner, Action, MenuBar);
827 }
828
829 /* No bitmap - process text if present */
830 if (Item->Text)
831 {
832 register int i = 0;
833 HFONT FontOld = NULL;
834
835 UINT uFormat = MenuBar ? DT_CENTER | DT_VCENTER | DT_SINGLELINE
836 : DT_LEFT | DT_VCENTER | DT_SINGLELINE;
837
838 if(MenuInfo->dwStyle & MNS_CHECKORBMP)
839 Rect.left += max(0, MenuInfo->maxBmpSize.cx - GetSystemMetrics(SM_CXMENUCHECK));
840 else
841 Rect.left += MenuInfo->maxBmpSize.cx;
842
843 if (0 != (Item->fState & MFS_DEFAULT))
844 {
845 FontOld = SelectObject(Dc, hMenuFontBold);
846 }
847
848 if (MenuBar)
849 {
850 Rect.left += MENU_BAR_ITEMS_SPACE / 2;
851 Rect.right -= MENU_BAR_ITEMS_SPACE / 2;
852 }
853
854 Text = (PWCHAR) Item->dwTypeData;
855 if(Text)
856 {
857 for (i = 0; L'\0' != Text[i]; i++)
858 {
859 if (L'\t' == Text[i] || L'\b' == Text[i])
860 {
861 break;
862 }
863 }
864 }
865
866 if (0 != (Item->fState & MF_GRAYED))
867 {
868 if (0 == (Item->fState & MF_HILITE))
869 {
870 ++Rect.left; ++Rect.top; ++Rect.right; ++Rect.bottom;
871 SetTextColor(Dc, RGB(0xff, 0xff, 0xff));
872 DrawTextW(Dc, Text, i, &Rect, uFormat);
873 --Rect.left; --Rect.top; --Rect.right; --Rect.bottom;
874 }
875 SetTextColor(Dc, RGB(0x80, 0x80, 0x80));
876 }
877
878 DrawTextW(Dc, Text, i, &Rect, uFormat);
879
880 /* paint the shortcut text */
881 if (! MenuBar && L'\0' != Text[i]) /* There's a tab or flush-right char */
882 {
883 if (L'\t' == Text[i])
884 {
885 Rect.left = Item->XTab;
886 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
887 }
888 else
889 {
890 Rect.right = Item->XTab;
891 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
892 }
893
894 if (0 != (Item->fState & MF_GRAYED))
895 {
896 if (0 == (Item->fState & MF_HILITE))
897 {
898 ++Rect.left; ++Rect.top; ++Rect.right; ++Rect.bottom;
899 SetTextColor(Dc, RGB(0xff, 0xff, 0xff));
900 DrawTextW(Dc, Text + i + 1, -1, &Rect, uFormat);
901 --Rect.left; --Rect.top; --Rect.right; --Rect.bottom;
902 }
903 SetTextColor(Dc, RGB(0x80, 0x80, 0x80));
904 }
905 DrawTextW(Dc, Text + i + 1, -1, &Rect, uFormat);
906 }
907
908 if (NULL != FontOld)
909 {
910 SelectObject(Dc, FontOld);
911 }
912 }
913 }
914
915 /***********************************************************************
916 * MenuDrawPopupMenu
917 *
918 * Paint a popup menu.
919 */
920 static void FASTCALL
921 MenuDrawPopupMenu(HWND Wnd, HDC Dc, HMENU Menu)
922 {
923 HBRUSH PrevBrush = NULL;
924 HPEN PrevPen;
925 RECT Rect;
926 ROSMENUINFO MenuInfo;
927 ROSMENUITEMINFO ItemInfo;
928 UINT u;
929
930 TRACE("wnd=%x dc=%x menu=%x\n", Wnd, Dc, Menu);
931
932 GetClientRect(Wnd, &Rect);
933
934 if (NULL != (PrevBrush = SelectObject(Dc, GetSysColorBrush(COLOR_MENU)))
935 && NULL != SelectObject(Dc, hMenuFont))
936 {
937 Rectangle(Dc, Rect.left, Rect.top, Rect.right, Rect.bottom);
938
939 PrevPen = SelectObject(Dc, GetStockObject(NULL_PEN));
940 if (NULL != PrevPen)
941 {
942 BOOL flat_menu = FALSE;
943
944 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
945 if (flat_menu)
946 FrameRect(Dc, &Rect, GetSysColorBrush(COLOR_BTNSHADOW));
947 else
948 DrawEdge(Dc, &Rect, EDGE_RAISED, BF_RECT);
949
950 /* draw menu items */
951
952 if (MenuGetRosMenuInfo(&MenuInfo, Menu) && 0 != MenuInfo.MenuItemCount)
953 {
954 MenuInitRosMenuItemInfo(&ItemInfo);
955
956 for (u = 0; u < MenuInfo.MenuItemCount; u++)
957 {
958 if (MenuGetRosMenuItemInfo(MenuInfo.Self, u, &ItemInfo))
959 {
960 MenuDrawMenuItem(Wnd, &MenuInfo, MenuInfo.WndOwner, Dc, &ItemInfo,
961 MenuInfo.Height, FALSE, ODA_DRAWENTIRE);
962 }
963 }
964
965 MenuCleanupRosMenuItemInfo(&ItemInfo);
966 }
967 }
968 else
969 {
970 SelectObject(Dc, PrevBrush);
971 }
972 }
973 }
974
975 LRESULT WINAPI
976 PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
977 {
978 TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
979
980 switch(Message)
981 {
982 case WM_CREATE:
983 {
984 CREATESTRUCTA *cs = (CREATESTRUCTA *) lParam;
985 SetWindowLongPtrA(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
986 return 0;
987 }
988
989 case WM_MOUSEACTIVATE: /* We don't want to be activated */
990 return MA_NOACTIVATE;
991
992 case WM_PAINT:
993 {
994 PAINTSTRUCT ps;
995 BeginPaint(Wnd, &ps);
996 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrA(Wnd, 0));
997 EndPaint(Wnd, &ps);
998 return 0;
999 }
1000
1001 case WM_PRINTCLIENT:
1002 {
1003 MenuDrawPopupMenu( Wnd, (HDC)wParam,
1004 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
1005 return 0;
1006 }
1007
1008 case WM_ERASEBKGND:
1009 return 1;
1010
1011 case WM_DESTROY:
1012 /* zero out global pointer in case resident popup window was destroyed. */
1013 if (Wnd == TopPopup)
1014 {
1015 TopPopup = NULL;
1016 }
1017 break;
1018
1019 case WM_SHOWWINDOW:
1020 if (0 != wParam)
1021 {
1022 if (0 == GetWindowLongPtrA(Wnd, 0))
1023 {
1024 OutputDebugStringA("no menu to display\n");
1025 }
1026 }
1027 else
1028 {
1029 SetWindowLongPtrA(Wnd, 0, 0);
1030 }
1031 break;
1032
1033 case MM_SETMENUHANDLE:
1034 SetWindowLongPtrA(Wnd, 0, wParam);
1035 break;
1036
1037 case MM_GETMENUHANDLE:
1038 case MN_GETHMENU:
1039 return GetWindowLongPtrA(Wnd, 0);
1040
1041 default:
1042 return DefWindowProcA(Wnd, Message, wParam, lParam);
1043 }
1044 return 0;
1045 }
1046
1047 LRESULT WINAPI
1048 PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
1049 {
1050 TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
1051
1052 switch(Message)
1053 {
1054 case WM_CREATE:
1055 {
1056 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
1057 SetWindowLongPtrW(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
1058 return 0;
1059 }
1060
1061 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1062 return MA_NOACTIVATE;
1063
1064 case WM_PAINT:
1065 {
1066 PAINTSTRUCT ps;
1067 BeginPaint(Wnd, &ps);
1068 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrW(Wnd, 0));
1069 EndPaint(Wnd, &ps);
1070 return 0;
1071 }
1072
1073 case WM_PRINTCLIENT:
1074 {
1075 MenuDrawPopupMenu( Wnd, (HDC)wParam,
1076 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
1077 return 0;
1078 }
1079
1080 case WM_ERASEBKGND:
1081 return 1;
1082
1083 case WM_DESTROY:
1084 /* zero out global pointer in case resident popup window was destroyed. */
1085 if (Wnd == TopPopup)
1086 {
1087 TopPopup = NULL;
1088 }
1089 break;
1090
1091 case WM_SHOWWINDOW:
1092 if (0 != wParam)
1093 {
1094 if (0 == GetWindowLongPtrW(Wnd, 0))
1095 {
1096 OutputDebugStringA("no menu to display\n");
1097 }
1098 }
1099 else
1100 {
1101 SetWindowLongPtrW(Wnd, 0, 0);
1102 }
1103 break;
1104
1105 case MM_SETMENUHANDLE:
1106 SetWindowLongPtrW(Wnd, 0, wParam);
1107 break;
1108
1109 case MM_GETMENUHANDLE:
1110 case MN_GETHMENU:
1111 return GetWindowLongPtrW(Wnd, 0);
1112
1113 default:
1114 return DefWindowProcW(Wnd, Message, wParam, lParam);
1115 }
1116
1117 return 0;
1118 }
1119
1120 /**********************************************************************
1121 * MENUEX_ParseResource
1122 *
1123 * Parse an extended menu resource and add items to the menu.
1124 * Return a pointer to the end of the resource.
1125 *
1126 * FIXME - should we be passing an LPCSTR to a predominantly UNICODE function?
1127 */
1128 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
1129 {
1130 WORD resinfo;
1131
1132 do
1133 {
1134 MENUITEMINFOW mii;
1135
1136 mii.cbSize = sizeof(mii);
1137 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
1138 mii.fType = GET_DWORD(res);
1139 res += sizeof(DWORD);
1140 mii.fState = GET_DWORD(res);
1141 res += sizeof(DWORD);
1142 mii.wID = GET_DWORD(res);
1143 res += sizeof(DWORD);
1144 resinfo = GET_WORD(res);
1145 res += sizeof(WORD);
1146 /* Align the text on a word boundary. */
1147 res += (~((UINT_PTR)res - 1)) & 1;
1148 mii.dwTypeData = (LPWSTR) res;
1149 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
1150 /* Align the following fields on a dword boundary. */
1151 res += (~((UINT_PTR)res - 1)) & 3;
1152
1153 if (resinfo & 1) /* Pop-up? */
1154 {
1155 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
1156 res += sizeof(DWORD);
1157 mii.hSubMenu = CreatePopupMenu();
1158 if (!mii.hSubMenu)
1159 return NULL;
1160 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu)))
1161 {
1162 DestroyMenu(mii.hSubMenu);
1163 return NULL;
1164 }
1165 mii.fMask |= MIIM_SUBMENU;
1166 mii.fType |= MF_POPUP;
1167 mii.wID = (UINT_PTR) mii.hSubMenu;
1168 }
1169 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
1170 {
1171 mii.fType |= MF_SEPARATOR;
1172 }
1173 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
1174 }
1175 while (!(resinfo & MF_END));
1176 return res;
1177 }
1178
1179
1180 /**********************************************************************
1181 * MENU_ParseResource
1182 *
1183 * Parse a standard menu resource and add items to the menu.
1184 * Return a pointer to the end of the resource.
1185 *
1186 * NOTE: flags is equivalent to the mtOption field
1187 */
1188 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu, BOOL unicode )
1189 {
1190 WORD flags, id = 0;
1191 HMENU hSubMenu;
1192 LPCSTR str;
1193 BOOL end = FALSE;
1194
1195 do
1196 {
1197 flags = GET_WORD(res);
1198
1199 /* remove MF_END flag before passing it to AppendMenu()! */
1200 end = (flags & MF_END);
1201 if(end) flags ^= MF_END;
1202
1203 res += sizeof(WORD);
1204 if(!(flags & MF_POPUP))
1205 {
1206 id = GET_WORD(res);
1207 res += sizeof(WORD);
1208 }
1209 str = res;
1210 if(!unicode)
1211 res += strlen(str) + 1;
1212 else
1213 res += (strlenW((LPCWSTR)str) + 1) * sizeof(WCHAR);
1214 if (flags & MF_POPUP)
1215 {
1216 hSubMenu = CreatePopupMenu();
1217 if(!hSubMenu) return NULL;
1218 if(!(res = MENU_ParseResource(res, hSubMenu, unicode)))
1219 return NULL;
1220 if(!unicode)
1221 AppendMenuA(hMenu, flags, (UINT_PTR)hSubMenu, str);
1222 else
1223 AppendMenuW(hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str);
1224 }
1225 else /* Not a popup */
1226 {
1227 if(!unicode)
1228 {
1229 if (*str == 0)
1230 flags = MF_SEPARATOR;
1231 }
1232 else
1233 {
1234 if (*(LPCWSTR)str == 0)
1235 flags = MF_SEPARATOR;
1236 }
1237
1238 if (flags & MF_SEPARATOR)
1239 {
1240 if (!(flags & (MF_GRAYED | MF_DISABLED)))
1241 flags |= MF_GRAYED | MF_DISABLED;
1242 }
1243
1244 if(!unicode)
1245 AppendMenuA(hMenu, flags, id, *str ? str : NULL);
1246 else
1247 AppendMenuW(hMenu, flags, id,
1248 *(LPCWSTR)str ? (LPCWSTR)str : NULL);
1249 }
1250 } while(!end);
1251
1252 return res;
1253 }
1254
1255
1256 NTSTATUS WINAPI
1257 User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength)
1258 {
1259 HMENU hmenu = LoadMenuW(User32Instance, L"SYSMENU");
1260 LRESULT Result = (LRESULT)hmenu;
1261 MENUINFO menuinfo = {0};
1262 MENUITEMINFOW info = {0};
1263
1264 // removing space for checkboxes from menu
1265 menuinfo.cbSize = sizeof(menuinfo);
1266 menuinfo.fMask = MIM_STYLE;
1267 GetMenuInfo(hmenu, &menuinfo);
1268 menuinfo.dwStyle |= MNS_NOCHECK;
1269 SetMenuInfo(hmenu, &menuinfo);
1270
1271 // adding bitmaps to menu items
1272 info.cbSize = sizeof(info);
1273 info.fMask |= MIIM_BITMAP;
1274 info.hbmpItem = HBMMENU_POPUP_MINIMIZE;
1275 SetMenuItemInfoW(hmenu, SC_MINIMIZE, FALSE, &info);
1276 info.hbmpItem = HBMMENU_POPUP_RESTORE;
1277 SetMenuItemInfoW(hmenu, SC_RESTORE, FALSE, &info);
1278 info.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
1279 SetMenuItemInfoW(hmenu, SC_MAXIMIZE, FALSE, &info);
1280 info.hbmpItem = HBMMENU_POPUP_CLOSE;
1281 SetMenuItemInfoW(hmenu, SC_CLOSE, FALSE, &info);
1282
1283 return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS));
1284 }
1285
1286
1287 BOOL
1288 MenuInit(VOID)
1289 {
1290 NONCLIENTMETRICSW ncm;
1291
1292 /* get the menu font */
1293 if(!hMenuFont || !hMenuFontBold)
1294 {
1295 ncm.cbSize = sizeof(ncm);
1296 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
1297 {
1298 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
1299 return FALSE;
1300 }
1301
1302 hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont);
1303 if(hMenuFont == NULL)
1304 {
1305 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
1306 return FALSE;
1307 }
1308
1309 ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
1310 hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont);
1311 if(hMenuFontBold == NULL)
1312 {
1313 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
1314 DeleteObject(hMenuFont);
1315 hMenuFont = NULL;
1316 return FALSE;
1317 }
1318 }
1319
1320 return TRUE;
1321 }
1322
1323
1324 VOID
1325 MenuCleanup(VOID)
1326 {
1327 if (hMenuFont)
1328 {
1329 DeleteObject(hMenuFont);
1330 hMenuFont = NULL;
1331 }
1332
1333 if (hMenuFontBold)
1334 {
1335 DeleteObject(hMenuFontBold);
1336 hMenuFontBold = NULL;
1337 }
1338 }
1339
1340
1341
1342 /***********************************************************************
1343 * MenuCalcItemSize
1344 *
1345 * Calculate the size of the menu item and store it in ItemInfo->rect.
1346 */
1347 static void FASTCALL
1348 MenuCalcItemSize(HDC Dc, PROSMENUITEMINFO ItemInfo, PROSMENUINFO MenuInfo, HWND WndOwner,
1349 INT OrgX, INT OrgY, BOOL MenuBar)
1350 {
1351 PWCHAR p;
1352 INT itemheight = 0;
1353 UINT CheckBitmapWidth = GetSystemMetrics(SM_CXMENUCHECK);
1354
1355 TRACE("dc=%x owner=%x (%d,%d)\n", Dc, WndOwner, OrgX, OrgY);
1356
1357 MenuCharSize.cx = GdiGetCharDimensions( Dc, NULL, &MenuCharSize.cy );
1358
1359 SetRect(&ItemInfo->Rect, OrgX, OrgY, OrgX, OrgY);
1360
1361 if (0 != (ItemInfo->fType & MF_OWNERDRAW))
1362 {
1363 /*
1364 ** Experimentation under Windows reveals that an owner-drawn
1365 ** menu is expected to return the size of the content part of
1366 ** the menu item, not including the checkmark nor the submenu
1367 ** arrow. Windows adds those values itself and returns the
1368 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
1369 */
1370 MEASUREITEMSTRUCT mis;
1371 mis.CtlType = ODT_MENU;
1372 mis.CtlID = 0;
1373 mis.itemID = ItemInfo->wID;
1374 mis.itemData = (DWORD)ItemInfo->dwItemData;
1375 mis.itemHeight = HIWORD( GetDialogBaseUnits());
1376 mis.itemWidth = 0;
1377 SendMessageW(WndOwner, WM_MEASUREITEM, 0, (LPARAM) &mis);
1378 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1379 * width of a menufont character to the width of an owner-drawn menu.
1380 */
1381 ItemInfo->Rect.right += mis.itemWidth + 2 * MenuCharSize.cx;
1382
1383 if (MenuBar)
1384 {
1385 /* under at least win95 you seem to be given a standard
1386 height for the menu and the height value is ignored */
1387 ItemInfo->Rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1388 }
1389 else
1390 {
1391 ItemInfo->Rect.bottom += mis.itemHeight;
1392 }
1393
1394 TRACE("id=%04x size=%dx%d\n", ItemInfo->wID, mis.itemWidth, mis.itemHeight);
1395 return;
1396 }
1397
1398 if (0 != (ItemInfo->fType & MF_SEPARATOR))
1399 {
1400 ItemInfo->Rect.bottom += SEPARATOR_HEIGHT;
1401 if( !MenuBar)
1402 ItemInfo->Rect.right += CheckBitmapWidth + MenuCharSize.cx;
1403 return;
1404 }
1405
1406 ItemInfo->XTab = 0;
1407
1408 if (ItemInfo->hbmpItem)
1409 {
1410 SIZE Size;
1411
1412 if (!MenuBar) /* hbmpItem */
1413 {
1414 MenuGetBitmapItemSize(ItemInfo, &Size, WndOwner );
1415 /* Keep the size of the bitmap in callback mode to be able
1416 * to draw it correctly */
1417 ItemInfo->Rect.right = ItemInfo->Rect.left + Size.cx;
1418 if (MenuInfo->maxBmpSize.cx < abs(Size.cx) + MENU_ITEM_HBMP_SPACE ||
1419 MenuInfo->maxBmpSize.cy < abs(Size.cy))
1420 {
1421 MenuInfo->maxBmpSize.cx = abs(Size.cx) + MENU_ITEM_HBMP_SPACE;
1422 MenuInfo->maxBmpSize.cy = abs(Size.cy);
1423 }
1424 MenuSetRosMenuInfo(MenuInfo);
1425 itemheight = Size.cy + 2;
1426
1427 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
1428 ItemInfo->Rect.right += 2 * CheckBitmapWidth;
1429 ItemInfo->Rect.right += 4 + MenuCharSize.cx;
1430 ItemInfo->XTab = ItemInfo->Rect.right;
1431 ItemInfo->Rect.right += CheckBitmapWidth;
1432 }
1433 else /* hbmpItem & MenuBar */
1434 {
1435 MenuGetBitmapItemSize(ItemInfo, &Size, WndOwner );
1436 ItemInfo->Rect.right += Size.cx;
1437 if( ItemInfo->Text) ItemInfo->Rect.right += 2;
1438 itemheight = Size.cy;
1439
1440 /* Special case: Minimize button doesn't have a space behind it. */
1441 if (ItemInfo->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE ||
1442 ItemInfo->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE_D)
1443 ItemInfo->Rect.right -= 1;
1444 }
1445 }
1446 else if (!MenuBar)
1447 {
1448 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
1449 ItemInfo->Rect.right += CheckBitmapWidth;
1450 ItemInfo->Rect.right += 4 + MenuCharSize.cx;
1451 ItemInfo->XTab = ItemInfo->Rect.right;
1452 ItemInfo->Rect.right += CheckBitmapWidth;
1453 }
1454
1455 /* it must be a text item - unless it's the system menu */
1456 if (0 == (ItemInfo->fType & MF_SYSMENU) && ItemInfo->Text)
1457 {
1458 HFONT hfontOld = NULL;
1459 RECT rc = ItemInfo->Rect;
1460 LONG txtheight, txtwidth;
1461
1462 if ( ItemInfo->fState & MFS_DEFAULT )
1463 {
1464 hfontOld = SelectObject( Dc, hMenuFontBold );
1465 }
1466 if (MenuBar)
1467 {
1468 txtheight = DrawTextW( Dc, ItemInfo->dwTypeData, -1, &rc,
1469 DT_SINGLELINE|DT_CALCRECT);
1470 ItemInfo->Rect.right += rc.right - rc.left;
1471 itemheight = max( max( itemheight, txtheight),
1472 GetSystemMetrics( SM_CYMENU) - 1);
1473 ItemInfo->Rect.right += 2 * MenuCharSize.cx;
1474 }
1475 else
1476 {
1477 if ((p = strchrW( ItemInfo->dwTypeData, '\t' )) != NULL)
1478 {
1479 RECT tmprc = rc;
1480 LONG tmpheight;
1481 int n = (int)( p - ItemInfo->dwTypeData);
1482 /* Item contains a tab (only meaningful in popup menus) */
1483 /* get text size before the tab */
1484 txtheight = DrawTextW( Dc, ItemInfo->dwTypeData, n, &rc,
1485 DT_SINGLELINE|DT_CALCRECT);
1486 txtwidth = rc.right - rc.left;
1487 p += 1; /* advance past the Tab */
1488 /* get text size after the tab */
1489 tmpheight = DrawTextW( Dc, p, -1, &tmprc, DT_SINGLELINE|DT_CALCRECT);
1490 ItemInfo->XTab += txtwidth;
1491 txtheight = max( txtheight, tmpheight);
1492 txtwidth += MenuCharSize.cx + /* space for the tab */
1493 tmprc.right - tmprc.left; /* space for the short cut */
1494 }
1495 else
1496 {
1497 txtheight = DrawTextW( Dc, ItemInfo->dwTypeData, -1, &rc,
1498 DT_SINGLELINE|DT_CALCRECT);
1499 txtwidth = rc.right - rc.left;
1500 ItemInfo->XTab += txtwidth;
1501 }
1502 ItemInfo->Rect.right += 2 + txtwidth;
1503 itemheight = max( itemheight, max( txtheight + 2, MenuCharSize.cy + 4));
1504 }
1505 if (hfontOld) SelectObject (Dc, hfontOld);
1506 }
1507 else if( MenuBar)
1508 {
1509 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1510 }
1511 ItemInfo->Rect.bottom += itemheight;
1512 TRACE("(%ld,%ld)-(%ld,%ld)\n", ItemInfo->Rect.left, ItemInfo->Rect.top, ItemInfo->Rect.right, ItemInfo->Rect.bottom);
1513 }
1514
1515 /***********************************************************************
1516 * MenuPopupMenuCalcSize
1517 *
1518 * Calculate the size of a popup menu.
1519 */
1520 static void FASTCALL
1521 MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo, HWND WndOwner)
1522 {
1523 ROSMENUITEMINFO ItemInfo;
1524 HDC Dc;
1525 int Start, i;
1526 int OrgX, OrgY, MaxX, MaxTab, MaxTabWidth;
1527
1528 MenuInfo->Width = MenuInfo->Height = 0;
1529 if (0 == MenuInfo->MenuItemCount)
1530 {
1531 MenuSetRosMenuInfo(MenuInfo);
1532 return;
1533 }
1534
1535 Dc = GetDC(NULL);
1536 SelectObject(Dc, hMenuFont);
1537
1538 Start = 0;
1539 MaxX = 2 + 1;
1540
1541 MenuInfo->maxBmpSize.cx = 0;
1542 MenuInfo->maxBmpSize.cy = 0;
1543
1544 MenuInitRosMenuItemInfo(&ItemInfo);
1545 while (Start < MenuInfo->MenuItemCount)
1546 {
1547 OrgX = MaxX;
1548 OrgY = 2;
1549
1550 MaxTab = MaxTabWidth = 0;
1551
1552 /* Parse items until column break or end of menu */
1553 for (i = Start; i < MenuInfo->MenuItemCount; i++)
1554 {
1555 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo))
1556 {
1557 MenuCleanupRosMenuItemInfo(&ItemInfo);
1558 MenuSetRosMenuInfo(MenuInfo);
1559 return;
1560 }
1561 if (i != Start &&
1562 0 != (ItemInfo.fType & (MF_MENUBREAK | MF_MENUBARBREAK)))
1563 {
1564 break;
1565 }
1566 MenuCalcItemSize(Dc, &ItemInfo, MenuInfo, WndOwner, OrgX, OrgY, FALSE);
1567 if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo))
1568 {
1569 MenuCleanupRosMenuItemInfo(&ItemInfo);
1570 MenuSetRosMenuInfo(MenuInfo);
1571 return;
1572 }
1573 // Not sure here,, The patch from wine removes this.
1574 // if (0 != (ItemInfo.fType & MF_MENUBARBREAK))
1575 // {
1576 // OrgX++;
1577 // }
1578 MaxX = max(MaxX, ItemInfo.Rect.right);
1579 OrgY = ItemInfo.Rect.bottom;
1580 if ((ItemInfo.Text) && 0 != ItemInfo.XTab)
1581 {
1582 MaxTab = max(MaxTab, ItemInfo.XTab);
1583 MaxTabWidth = max(MaxTabWidth, ItemInfo.Rect.right - ItemInfo.XTab);
1584 }
1585 }
1586
1587 /* Finish the column (set all items to the largest width found) */
1588 MaxX = max(MaxX, MaxTab + MaxTabWidth);
1589 while (Start < i)
1590 {
1591 if (MenuGetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo))
1592 {
1593 ItemInfo.Rect.right = MaxX;
1594 if ((ItemInfo.Text) && 0 != ItemInfo.XTab)
1595 {
1596 ItemInfo.XTab = MaxTab;
1597 }
1598 MenuSetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo);
1599 }
1600 Start++;
1601 }
1602 MenuInfo->Height = max(MenuInfo->Height, OrgY);
1603 }
1604
1605 MenuInfo->Width = MaxX;
1606
1607 /* space for 3d border */
1608 MenuInfo->Height += 2;
1609 MenuInfo->Width += 2;
1610
1611 ReleaseDC(NULL, Dc);
1612 MenuCleanupRosMenuItemInfo(&ItemInfo);
1613 MenuSetRosMenuInfo(MenuInfo);
1614 }
1615
1616 /***********************************************************************
1617 * MenuMenuBarCalcSize
1618 *
1619 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1620 * height is off by 1 pixel which causes lengthy window relocations when
1621 * active document window is maximized/restored.
1622 *
1623 * Calculate the size of the menu bar.
1624 */
1625 static void FASTCALL
1626 MenuMenuBarCalcSize(HDC Dc, LPRECT Rect, PROSMENUINFO MenuInfo, HWND WndOwner)
1627 {
1628 ROSMENUITEMINFO ItemInfo;
1629 int Start, i, OrgX, OrgY, MaxY, HelpPos;
1630
1631 if (NULL == Rect || NULL == MenuInfo)
1632 {
1633 return;
1634 }
1635 if (0 == MenuInfo->MenuItemCount)
1636 {
1637 return;
1638 }
1639
1640 TRACE("left=%ld top=%ld right=%ld bottom=%ld\n",
1641 Rect->left, Rect->top, Rect->right, Rect->bottom);
1642 MenuInfo->Width = Rect->right - Rect->left;
1643 MenuInfo->Height = 0;
1644 MaxY = Rect->top + 1;
1645 Start = 0;
1646 HelpPos = -1;
1647
1648 MenuInfo->maxBmpSize.cx = 0;
1649 MenuInfo->maxBmpSize.cy = 0;
1650
1651 MenuInitRosMenuItemInfo(&ItemInfo);
1652 while (Start < MenuInfo->MenuItemCount)
1653 {
1654 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo))
1655 {
1656 MenuCleanupRosMenuItemInfo(&ItemInfo);
1657 return;
1658 }
1659 OrgX = Rect->left;
1660 OrgY = MaxY;
1661
1662 /* Parse items until line break or end of menu */
1663 for (i = Start; i < MenuInfo->MenuItemCount; i++)
1664 {
1665 if (-1 == HelpPos && 0 != (ItemInfo.fType & MF_RIGHTJUSTIFY))
1666 {
1667 HelpPos = i;
1668 }
1669 if (i != Start &&
1670 0 != (ItemInfo.fType & (MF_MENUBREAK | MF_MENUBARBREAK)))
1671 {
1672 break;
1673 }
1674
1675 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", OrgX, OrgY);
1676 MenuCalcItemSize(Dc, &ItemInfo, MenuInfo, WndOwner, OrgX, OrgY, TRUE);
1677 if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo))
1678 {
1679 MenuCleanupRosMenuItemInfo(&ItemInfo);
1680 return;
1681 }
1682
1683 if (ItemInfo.Rect.right > Rect->right)
1684 {
1685 if (i != Start)
1686 {
1687 break;
1688 }
1689 else
1690 {
1691 ItemInfo.Rect.right = Rect->right;
1692 }
1693 }
1694 MaxY = max(MaxY, ItemInfo.Rect.bottom );
1695 OrgX = ItemInfo.Rect.right;
1696 if (i + 1 < MenuInfo->MenuItemCount)
1697 {
1698 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i + 1, &ItemInfo))
1699 {
1700 MenuCleanupRosMenuItemInfo(&ItemInfo);
1701 return;
1702 }
1703 }
1704 }
1705
1706 /* FIXME: Is this really needed? */ /*NO! it is not needed, why make the
1707 HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
1708 #if 0
1709 /* Finish the line (set all items to the largest height found) */
1710 while (Start < i)
1711 {
1712 if (MenuGetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo))
1713 {
1714 ItemInfo.Rect.bottom = MaxY;
1715 MenuSetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo);
1716 }
1717 Start++;
1718 }
1719 #else
1720 Start = i; /* This works! */
1721 #endif
1722 }
1723
1724 Rect->bottom = MaxY;
1725 MenuInfo->Height = Rect->bottom - Rect->top;
1726 MenuSetRosMenuInfo(MenuInfo);
1727
1728 if (-1 != HelpPos)
1729 {
1730 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1731 /* the last item (if several lines, only move the last line) */
1732 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->MenuItemCount - 1, &ItemInfo))
1733 {
1734 MenuCleanupRosMenuItemInfo(&ItemInfo);
1735 return;
1736 }
1737 OrgY = ItemInfo.Rect.top;
1738 OrgX = Rect->right;
1739 for (i = MenuInfo->MenuItemCount - 1; HelpPos <= i; i--)
1740 {
1741 if (i < HelpPos)
1742 {
1743 break; /* done */
1744 }
1745 if (ItemInfo.Rect.top != OrgY)
1746 {
1747 break; /* Other line */
1748 }
1749 if (OrgX <= ItemInfo.Rect.right)
1750 {
1751 break; /* Too far right already */
1752 }
1753 ItemInfo.Rect.left += OrgX - ItemInfo.Rect.right;
1754 ItemInfo.Rect.right = OrgX;
1755 OrgX = ItemInfo.Rect.left;
1756 MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo);
1757 if (HelpPos + 1 <= i &&
1758 ! MenuGetRosMenuItemInfo(MenuInfo->Self, i - 1, &ItemInfo))
1759 {
1760 MenuCleanupRosMenuItemInfo(&ItemInfo);
1761 return;
1762 }
1763 }
1764 }
1765
1766 MenuCleanupRosMenuItemInfo(&ItemInfo);
1767 }
1768
1769 /***********************************************************************
1770 * DrawMenuBarTemp (USER32.@)
1771 *
1772 * UNDOCUMENTED !!
1773 *
1774 * called by W98SE desk.cpl Control Panel Applet
1775 *
1776 * Not 100% sure about the param names, but close.
1777 *
1778 * @implemented
1779 */
1780 DWORD WINAPI
1781 DrawMenuBarTemp(HWND Wnd, HDC DC, LPRECT Rect, HMENU Menu, HFONT Font)
1782 {
1783 ROSMENUINFO MenuInfo;
1784 ROSMENUITEMINFO ItemInfo;
1785 UINT i;
1786 HFONT FontOld = NULL;
1787 BOOL flat_menu = FALSE;
1788
1789 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1790
1791 if (NULL == Menu)
1792 {
1793 Menu = GetMenu(Wnd);
1794 }
1795
1796 if (NULL == Font)
1797 {
1798 Font = hMenuFont;
1799 }
1800
1801 if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
1802 {
1803 return GetSystemMetrics(SM_CYMENU);
1804 }
1805
1806 TRACE("(%x, %x, %p, %x, %x)\n", Wnd, DC, Rect, Menu, Font);
1807
1808 FontOld = SelectObject(DC, Font);
1809
1810 if (0 == MenuInfo.Height)
1811 {
1812 MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
1813 }
1814
1815 Rect->bottom = Rect->top + MenuInfo.Height;
1816
1817 FillRect(DC, Rect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
1818
1819 SelectObject(DC, GetStockObject(DC_PEN));
1820 SetDCPenColor(DC, GetSysColor(COLOR_3DFACE));
1821 MoveToEx(DC, Rect->left, Rect->bottom, NULL);
1822 LineTo(DC, Rect->right, Rect->bottom);
1823
1824 if (0 == MenuInfo.MenuItemCount)
1825 {
1826 SelectObject(DC, FontOld);
1827 return GetSystemMetrics(SM_CYMENU);
1828 }
1829
1830 MenuInitRosMenuItemInfo(&ItemInfo);
1831 for (i = 0; i < MenuInfo.MenuItemCount; i++)
1832 {
1833 if (MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
1834 {
1835 MenuDrawMenuItem(Wnd, &MenuInfo, Wnd, DC, &ItemInfo,
1836 MenuInfo.Height, TRUE, ODA_DRAWENTIRE);
1837 }
1838 }
1839 MenuCleanupRosMenuItemInfo(&ItemInfo);
1840
1841 SelectObject(DC, FontOld);
1842
1843 return MenuInfo.Height;
1844 }
1845
1846
1847 /***********************************************************************
1848 * MenuDrawMenuBar
1849 *
1850 * Paint a menu bar. Returns the height of the menu bar.
1851 * called from [windows/nonclient.c]
1852 */
1853 UINT MenuDrawMenuBar(HDC DC, LPRECT Rect, HWND Wnd, BOOL SuppressDraw)
1854 {
1855 ROSMENUINFO MenuInfo;
1856 HFONT FontOld = NULL;
1857 HMENU Menu = GetMenu(Wnd);
1858
1859 if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
1860 {
1861 return GetSystemMetrics(SM_CYMENU);
1862 }
1863
1864 if (SuppressDraw)
1865 {
1866 FontOld = SelectObject(DC, hMenuFont);
1867
1868 MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
1869
1870 Rect->bottom = Rect->top + MenuInfo.Height;
1871
1872 if (NULL != FontOld)
1873 {
1874 SelectObject(DC, FontOld);
1875 }
1876 return MenuInfo.Height;
1877 }
1878 else
1879 {
1880 return DrawMenuBarTemp(Wnd, DC, Rect, Menu, NULL);
1881 }
1882 }
1883
1884 /***********************************************************************
1885 * MenuInitTracking
1886 */
1887 static BOOL FASTCALL
1888 MenuInitTracking(HWND Wnd, HMENU Menu, BOOL Popup, UINT Flags)
1889 {
1890 TRACE("Wnd=%p Menu=%p\n", Wnd, Menu);
1891
1892 HideCaret(0);
1893
1894 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
1895 if (0 == (Flags & TPM_NONOTIFY))
1896 {
1897 SendMessageW(Wnd, WM_ENTERMENULOOP, Popup, 0);
1898 }
1899
1900 SendMessageW(Wnd, WM_SETCURSOR, (WPARAM) Wnd, HTCAPTION);
1901
1902 if (0 == (Flags & TPM_NONOTIFY))
1903 {
1904 ROSMENUINFO MenuInfo;
1905
1906 SendMessageW(Wnd, WM_INITMENU, (WPARAM)Menu, 0);
1907
1908 MenuGetRosMenuInfo(&MenuInfo, Menu);
1909
1910 if (0 == MenuInfo.Height)
1911 {
1912 /* app changed/recreated menu bar entries in WM_INITMENU
1913 Recalculate menu sizes else clicks will not work */
1914 SetWindowPos(Wnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
1915 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
1916
1917 }
1918 /* This makes the menus of applications built with Delphi work.
1919 * It also enables menus to be displayed in more than one window,
1920 * but there are some bugs left that need to be fixed in this case.
1921 */
1922 if(MenuInfo.Self == Menu)
1923 {
1924 MenuInfo.Wnd = Wnd;
1925 MenuSetRosMenuInfo(&MenuInfo);
1926 }
1927 }
1928
1929 return TRUE;
1930 }
1931
1932
1933 /***********************************************************************
1934 * MenuShowPopup
1935 *
1936 * Display a popup menu.
1937 */
1938 static BOOL FASTCALL
1939 MenuShowPopup(HWND WndOwner, HMENU Menu, UINT Id, UINT flags,
1940 INT X, INT Y, INT XAnchor, INT YAnchor )
1941 {
1942 ROSMENUINFO MenuInfo;
1943 ROSMENUITEMINFO ItemInfo;
1944 UINT Width, Height;
1945 POINT pt;
1946 HMONITOR monitor;
1947 MONITORINFO info;
1948
1949 TRACE("owner=%x hmenu=%x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1950 WndOwner, Menu, Id, X, Y, XAnchor, YAnchor);
1951
1952 if (! MenuGetRosMenuInfo(&MenuInfo, Menu))
1953 {
1954 return FALSE;
1955 }
1956
1957 if (NO_SELECTED_ITEM != MenuInfo.FocusedItem)
1958 {
1959 MenuInitRosMenuItemInfo(&ItemInfo);
1960 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
1961 {
1962 ItemInfo.fMask |= MIIM_STATE;
1963 ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
1964 MenuSetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo);
1965 }
1966 MenuCleanupRosMenuItemInfo(&ItemInfo);
1967 MenuInfo.FocusedItem = NO_SELECTED_ITEM;
1968 }
1969
1970 /* store the owner for DrawItem */
1971 MenuInfo.WndOwner = WndOwner;
1972 MenuSetRosMenuInfo(&MenuInfo);
1973
1974 MenuPopupMenuCalcSize(&MenuInfo, WndOwner);
1975
1976 /* adjust popup menu pos so that it fits within the desktop */
1977
1978 Width = MenuInfo.Width + GetSystemMetrics(SM_CXBORDER);
1979 Height = MenuInfo.Height + GetSystemMetrics(SM_CYBORDER);
1980
1981 /* FIXME: should use item rect */
1982 pt.x = X;
1983 pt.y = Y;
1984 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1985 info.cbSize = sizeof(info);
1986 GetMonitorInfoW( monitor, &info );
1987
1988 if( flags & TPM_RIGHTALIGN ) X -= Width;
1989 if( flags & TPM_CENTERALIGN ) X -= Width / 2;
1990
1991 if( flags & TPM_BOTTOMALIGN ) Y -= Height;
1992 if( flags & TPM_VCENTERALIGN ) Y -= Height / 2;
1993
1994 if (X + Width > info.rcWork.right)
1995 {
1996 if ( XAnchor && X >= Width - XAnchor)
1997 X -= Width - XAnchor;
1998
1999 if ( X + Width > info.rcWork.right)
2000 X = info.rcWork.right - Width;
2001 }
2002
2003 if ( X < info.rcWork.left ) X = info.rcWork.left;
2004
2005 if (Y + Height > info.rcWork.bottom)
2006 {
2007 if ( YAnchor && Y >= Height + YAnchor)
2008 Y -= Height + YAnchor;
2009
2010 if ( Y + Height > info.rcWork.bottom)
2011 Y = info.rcWork.bottom - Height;
2012 }
2013
2014 if ( Y < info.rcWork.top ) Y = info.rcWork.top;
2015
2016 /* NOTE: In Windows, top menu popup is not owned. */
2017 MenuInfo.Wnd = CreateWindowExW(0, POPUPMENU_CLASS_ATOMW, NULL,
2018 WS_POPUP, X, Y, Width, Height,
2019 WndOwner, 0, (HINSTANCE) GetWindowLongPtrW(WndOwner, GWLP_HINSTANCE),
2020 (LPVOID) MenuInfo.Self);
2021 if (NULL == MenuInfo.Wnd || ! MenuSetRosMenuInfo(&MenuInfo))
2022 {
2023 return FALSE;
2024 }
2025 if (NULL == TopPopup)
2026 {
2027 TopPopup = MenuInfo.Wnd;
2028 }
2029
2030 /* Display the window */
2031 SetWindowPos(MenuInfo.Wnd, HWND_TOPMOST, 0, 0, 0, 0,
2032 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
2033 UpdateWindow(MenuInfo.Wnd);
2034
2035 return TRUE;
2036 }
2037
2038 /***********************************************************************
2039 * MenuFindSubMenu
2040 *
2041 * Find a Sub menu. Return the position of the submenu, and modifies
2042 * *hmenu in case it is found in another sub-menu.
2043 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
2044 */
2045 static UINT FASTCALL
2046 MenuFindSubMenu(HMENU *Menu, HMENU SubTarget)
2047 {
2048 ROSMENUINFO MenuInfo;
2049 ROSMENUITEMINFO ItemInfo;
2050 UINT i;
2051 HMENU SubMenu;
2052 UINT Pos;
2053
2054 if ((HMENU) 0xffff == *Menu
2055 || ! MenuGetRosMenuInfo(&MenuInfo, *Menu))
2056 {
2057 return NO_SELECTED_ITEM;
2058 }
2059
2060 MenuInitRosMenuItemInfo(&ItemInfo);
2061 for (i = 0; i < MenuInfo.MenuItemCount; i++)
2062 {
2063 if (! MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
2064 {
2065 MenuCleanupRosMenuItemInfo(&ItemInfo);
2066 return NO_SELECTED_ITEM;
2067 }
2068 if (0 == (ItemInfo.fType & MF_POPUP))
2069 {
2070 continue;
2071 }
2072 if (ItemInfo.hSubMenu == SubTarget)
2073 {
2074 MenuCleanupRosMenuItemInfo(&ItemInfo);
2075 return i;
2076 }
2077 SubMenu = ItemInfo.hSubMenu;
2078 Pos = MenuFindSubMenu(&SubMenu, SubTarget);
2079 if (NO_SELECTED_ITEM != Pos)
2080 {
2081 *Menu = SubMenu;
2082 return Pos;
2083 }
2084 }
2085 MenuCleanupRosMenuItemInfo(&ItemInfo);
2086
2087 return NO_SELECTED_ITEM;
2088 }
2089
2090 /***********************************************************************
2091 * MenuSelectItem
2092 */
2093 static void FASTCALL
2094 MenuSelectItem(HWND WndOwner, PROSMENUINFO MenuInfo, UINT Index,
2095 BOOL SendMenuSelect, HMENU TopMenu)
2096 {
2097 HDC Dc;
2098 ROSMENUITEMINFO ItemInfo;
2099 ROSMENUINFO TopMenuInfo;
2100 int Pos;
2101
2102 TRACE("owner=%x menu=%p index=0x%04x select=0x%04x\n", WndOwner, MenuInfo, Index, SendMenuSelect);
2103
2104 if (NULL == MenuInfo || 0 == MenuInfo->MenuItemCount || NULL == MenuInfo->Wnd)
2105 {
2106 return;
2107 }
2108
2109 if (MenuInfo->FocusedItem == Index)
2110 {
2111 return;
2112 }
2113
2114 if (0 != (MenuInfo->Flags & MF_POPUP))
2115 {
2116 Dc = GetDC(MenuInfo->Wnd);
2117 }
2118 else
2119 {
2120 Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
2121 }
2122
2123 if (NULL == TopPopup)
2124 {
2125 TopPopup = MenuInfo->Wnd;
2126 }
2127
2128 SelectObject(Dc, hMenuFont);
2129 MenuInitRosMenuItemInfo(&ItemInfo);
2130 /* Clear previous highlighted item */
2131 if (NO_SELECTED_ITEM != MenuInfo->FocusedItem)
2132 {
2133 if (MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2134 {
2135 ItemInfo.fMask |= MIIM_STATE;
2136 ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
2137 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2138 }
2139 MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo,
2140 MenuInfo->Height, ! (MenuInfo->Flags & MF_POPUP),
2141 ODA_SELECT);
2142 }
2143
2144 /* Highlight new item (if any) */
2145 MenuInfo->FocusedItem = Index;
2146 MenuSetRosMenuInfo(MenuInfo);
2147 if (NO_SELECTED_ITEM != MenuInfo->FocusedItem)
2148 {
2149 if (MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2150 {
2151 if (0 == (ItemInfo.fType & MF_SEPARATOR))
2152 {
2153 ItemInfo.fMask |= MIIM_STATE;
2154 ItemInfo.fState |= MF_HILITE;
2155 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2156 MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc,
2157 &ItemInfo, MenuInfo->Height, ! (MenuInfo->Flags & MF_POPUP),
2158 ODA_SELECT);
2159 }
2160 if (SendMenuSelect)
2161 {
2162 SendMessageW(WndOwner, WM_MENUSELECT,
2163 MAKELONG(ItemInfo.fType & MF_POPUP ? Index : ItemInfo.wID,
2164 ItemInfo.fType | ItemInfo.fState | MF_MOUSESELECT |
2165 (MenuInfo->Flags & MF_SYSMENU)), (LPARAM) MenuInfo->Self);
2166 }
2167 }
2168 }
2169 else if (SendMenuSelect)
2170 {
2171 if (NULL != TopMenu)
2172 {
2173 Pos = MenuFindSubMenu(&TopMenu, MenuInfo->Self);
2174 if (NO_SELECTED_ITEM != Pos)
2175 {
2176 if (MenuGetRosMenuInfo(&TopMenuInfo, TopMenu)
2177 && MenuGetRosMenuItemInfo(TopMenu, Pos, &ItemInfo))
2178 {
2179 SendMessageW(WndOwner, WM_MENUSELECT,
2180 MAKELONG(Pos, ItemInfo.fType | ItemInfo.fState
2181 | MF_MOUSESELECT
2182 | (TopMenuInfo.Flags & MF_SYSMENU)),
2183 (LPARAM) TopMenu);
2184 }
2185 }
2186 }
2187 }
2188 MenuCleanupRosMenuItemInfo(&ItemInfo);
2189 ReleaseDC(MenuInfo->Wnd, Dc);
2190 }
2191
2192 /***********************************************************************
2193 * MenuMoveSelection
2194 *
2195 * Moves currently selected item according to the Offset parameter.
2196 * If there is no selection then it should select the last item if
2197 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
2198 */
2199 static void FASTCALL
2200 MenuMoveSelection(HWND WndOwner, PROSMENUINFO MenuInfo, INT Offset)
2201 {
2202 INT i;
2203 ROSMENUITEMINFO ItemInfo;
2204 INT OrigPos;
2205
2206 TRACE("hwnd=%x menu=%x off=0x%04x\n", WndOwner, MenuInfo, Offset);
2207
2208 /* Prevent looping */
2209 if (0 == MenuInfo->MenuItemCount || 0 == Offset)
2210 return;
2211 else if (Offset < -1)
2212 Offset = -1;
2213 else if (Offset > 1)
2214 Offset = 1;
2215
2216 MenuInitRosMenuItemInfo(&ItemInfo);
2217
2218 OrigPos = MenuInfo->FocusedItem;
2219 if (OrigPos == NO_SELECTED_ITEM) /* NO_SELECTED_ITEM is not -1 ! */
2220 {
2221 OrigPos = 0;
2222 i = -1;
2223 }
2224 else
2225 {
2226 i = MenuInfo->FocusedItem;
2227 }
2228
2229 do
2230 {
2231 /* Step */
2232 i += Offset;
2233 /* Clip and wrap around */
2234 if (i < 0)
2235 {
2236 i = MenuInfo->MenuItemCount - 1;
2237 }
2238 else if (i >= MenuInfo->MenuItemCount)
2239 {
2240 i = 0;
2241 }
2242 /* If this is a good candidate; */
2243 if (MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo) &&
2244 0 == (ItemInfo.fType & MF_SEPARATOR))
2245 {
2246 MenuSelectItem(WndOwner, MenuInfo, i, TRUE, NULL);
2247 MenuCleanupRosMenuItemInfo(&ItemInfo);
2248 return;
2249 }
2250 } while (i != OrigPos);
2251
2252 /* Not found */
2253 MenuCleanupRosMenuItemInfo(&ItemInfo);
2254 }
2255
2256 /***********************************************************************
2257 * MenuInitSysMenuPopup
2258 *
2259 * Grey the appropriate items in System menu.
2260 */
2261 void FASTCALL
2262 MenuInitSysMenuPopup(HMENU Menu, DWORD Style, DWORD ClsStyle, LONG HitTest )
2263 {
2264 BOOL Gray;
2265 UINT DefItem;
2266 #if 0
2267 MENUITEMINFOW mii;
2268 #endif
2269
2270 Gray = 0 == (Style & WS_THICKFRAME) || 0 != (Style & (WS_MAXIMIZE | WS_MINIMIZE));
2271 EnableMenuItem(Menu, SC_SIZE, (Gray ? MF_GRAYED : MF_ENABLED));
2272 Gray = 0 != (Style & WS_MAXIMIZE);
2273 EnableMenuItem(Menu, SC_MOVE, (Gray ? MF_GRAYED : MF_ENABLED));
2274 Gray = 0 == (Style & WS_MINIMIZEBOX) || 0 != (Style & WS_MINIMIZE);
2275 EnableMenuItem(Menu, SC_MINIMIZE, (Gray ? MF_GRAYED : MF_ENABLED));
2276 Gray = 0 == (Style & WS_MAXIMIZEBOX) || 0 != (Style & WS_MAXIMIZE);
2277 EnableMenuItem(Menu, SC_MAXIMIZE, (Gray ? MF_GRAYED : MF_ENABLED));
2278 Gray = 0 == (Style & (WS_MAXIMIZE | WS_MINIMIZE));
2279 EnableMenuItem(Menu, SC_RESTORE, (Gray ? MF_GRAYED : MF_ENABLED));
2280 Gray = 0 != (ClsStyle & CS_NOCLOSE);
2281
2282 /* The menu item must keep its state if it's disabled */
2283 if (Gray)
2284 {
2285 EnableMenuItem(Menu, SC_CLOSE, MF_GRAYED);
2286 }
2287
2288 /* Set default menu item */
2289 if(Style & WS_MINIMIZE)
2290 {
2291 DefItem = SC_RESTORE;
2292 }
2293 else
2294 {
2295 if(HitTest == HTCAPTION)
2296 {
2297 DefItem = ((Style & (WS_MAXIMIZE | WS_MINIMIZE)) ? SC_RESTORE : SC_MAXIMIZE);
2298 }
2299 else
2300 {
2301 DefItem = SC_CLOSE;
2302 }
2303 }
2304 #if 0
2305 mii.cbSize = sizeof(MENUITEMINFOW);
2306 mii.fMask |= MIIM_STATE;
2307 if((DefItem != SC_CLOSE) && GetMenuItemInfoW(Menu, DefItem, FALSE, &mii) &&
2308 (mii.fState & (MFS_GRAYED | MFS_DISABLED)))
2309 {
2310 DefItem = SC_CLOSE;
2311 }
2312 #endif
2313 SetMenuDefaultItem(Menu, DefItem, MF_BYCOMMAND);
2314 }
2315
2316 /***********************************************************************
2317 * MenuShowSubPopup
2318 *
2319 * Display the sub-menu of the selected item of this menu.
2320 * Return the handle of the submenu, or menu if no submenu to display.
2321 */
2322 static HMENU FASTCALL
2323 MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags)
2324 {
2325 extern void FASTCALL NcGetSysPopupPos(HWND Wnd, RECT *Rect);
2326 RECT Rect;
2327 ROSMENUITEMINFO ItemInfo;
2328 ROSMENUINFO SubMenuInfo;
2329 HDC Dc;
2330 HMENU Ret;
2331
2332 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst);
2333
2334 if (NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2335 {
2336 return MenuInfo->Self;
2337 }
2338
2339 MenuInitRosMenuItemInfo(&ItemInfo);
2340 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2341 {
2342 MenuCleanupRosMenuItemInfo(&ItemInfo);
2343 return MenuInfo->Self;
2344 }
2345 if (0 == (ItemInfo.fType & MF_POPUP) || 0 != (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)))
2346 {
2347 MenuCleanupRosMenuItemInfo(&ItemInfo);
2348 return MenuInfo->Self;
2349 }
2350
2351 /* message must be sent before using item,
2352 because nearly everything may be changed by the application ! */
2353
2354 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2355 if (0 == (Flags & TPM_NONOTIFY))
2356 {
2357 SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu,
2358 MAKELONG(MenuInfo->FocusedItem, IS_SYSTEM_MENU(MenuInfo)));
2359 }
2360
2361 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2362 {
2363 MenuCleanupRosMenuItemInfo(&ItemInfo);
2364 return MenuInfo->Self;
2365 }
2366 Rect = ItemInfo.Rect;
2367
2368 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2369 if (0 == (ItemInfo.fState & MF_HILITE))
2370 {
2371 if (0 != (MenuInfo->Flags & MF_POPUP))
2372 {
2373 Dc = GetDC(MenuInfo->Wnd);
2374 }
2375 else
2376 {
2377 Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
2378 }
2379
2380 SelectObject(Dc, hMenuFont);
2381 ItemInfo.fMask |= MIIM_STATE;
2382 ItemInfo.fState |= MF_HILITE;
2383 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2384 MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->Height,
2385 ! (MenuInfo->Flags & MF_POPUP), ODA_DRAWENTIRE);
2386 ReleaseDC(MenuInfo->Wnd, Dc);
2387 }
2388
2389 if (0 == ItemInfo.Rect.top && 0 == ItemInfo.Rect.left
2390 && 0 == ItemInfo.Rect.bottom && 0 == ItemInfo.Rect.right)
2391 {
2392 ItemInfo.Rect = Rect;
2393 }
2394
2395 ItemInfo.fMask |= MIIM_STATE;
2396 ItemInfo.fState |= MF_MOUSESELECT;
2397 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2398
2399 if (IS_SYSTEM_MENU(MenuInfo))
2400 {
2401 MenuInitSysMenuPopup(ItemInfo.hSubMenu, GetWindowLongPtrW(MenuInfo->Wnd, GWL_STYLE),
2402 GetClassLongPtrW(MenuInfo->Wnd, GCL_STYLE), HTSYSMENU);
2403
2404 NcGetSysPopupPos(MenuInfo->Wnd, &Rect);
2405 Rect.top = Rect.bottom;
2406 Rect.right = GetSystemMetrics(SM_CXSIZE);
2407 Rect.bottom = GetSystemMetrics(SM_CYSIZE);
2408 }
2409 else
2410 {
2411 GetWindowRect(MenuInfo->Wnd, &Rect);
2412 if (0 != (MenuInfo->Flags & MF_POPUP))
2413 {
2414 Rect.left += ItemInfo.Rect.right - GetSystemMetrics(SM_CXBORDER);
2415 Rect.top += ItemInfo.Rect.top - 3;
2416 Rect.right = ItemInfo.Rect.left - ItemInfo.Rect.right + GetSystemMetrics(SM_CXBORDER);
2417 Rect.bottom = ItemInfo.Rect.top - ItemInfo.Rect.bottom - 3 - 2
2418 - GetSystemMetrics(SM_CYBORDER);
2419 }
2420 else
2421 {
2422 Rect.left += ItemInfo.Rect.left;
2423 Rect.top += ItemInfo.Rect.bottom;
2424 Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left;
2425 Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top;
2426 }
2427 }
2428
2429 MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->FocusedItem, Flags,
2430 Rect.left, Rect.top, Rect.right, Rect.bottom );
2431 if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2432 {
2433 MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT);
2434 }
2435
2436 Ret = ItemInfo.hSubMenu;
2437 MenuCleanupRosMenuItemInfo(&ItemInfo);
2438
2439 return Ret;
2440 }
2441
2442 /***********************************************************************
2443 * MenuHideSubPopups
2444 *
2445 * Hide the sub-popup menus of this menu.
2446 */
2447 static void FASTCALL
2448 MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SendMenuSelect)
2449 {
2450 ROSMENUINFO SubMenuInfo;
2451 ROSMENUITEMINFO ItemInfo;
2452
2453 TRACE("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect);
2454
2455 if (NULL != MenuInfo && NULL != TopPopup && NO_SELECTED_ITEM != MenuInfo->FocusedItem)
2456 {
2457 MenuInitRosMenuItemInfo(&ItemInfo);
2458 ItemInfo.fMask |= MIIM_FTYPE | MIIM_STATE;
2459 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo)
2460 || 0 == (ItemInfo.fType & MF_POPUP)
2461 || 0 == (ItemInfo.fState & MF_MOUSESELECT))
2462 {
2463 MenuCleanupRosMenuItemInfo(&ItemInfo);
2464 return;
2465 }
2466 ItemInfo.fState &= ~MF_MOUSESELECT;
2467 ItemInfo.fMask |= MIIM_STATE;
2468 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2469 if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2470 {
2471 MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE);
2472 MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL);
2473 DestroyWindow(SubMenuInfo.Wnd);
2474 SubMenuInfo.Wnd = NULL;
2475 MenuSetRosMenuInfo(&SubMenuInfo);
2476 }
2477 }
2478 }
2479
2480 /***********************************************************************
2481 * MenuSwitchTracking
2482 *
2483 * Helper function for menu navigation routines.
2484 */
2485 static void FASTCALL
2486 MenuSwitchTracking(MTRACKER* Mt, PROSMENUINFO PtMenuInfo, UINT Index)
2487 {
2488 ROSMENUINFO TopMenuInfo;
2489
2490 TRACE("%x menu=%x 0x%04x\n", Mt, PtMenuInfo->Self, Index);
2491
2492 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu) &&
2493 Mt->TopMenu != PtMenuInfo->Self &&
2494 0 == ((PtMenuInfo->Flags | TopMenuInfo.Flags) & MF_POPUP))
2495 {
2496 /* both are top level menus (system and menu-bar) */
2497 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE);
2498 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL);
2499 Mt->TopMenu = PtMenuInfo->Self;
2500 }
2501 else
2502 {
2503 MenuHideSubPopups(Mt->OwnerWnd, PtMenuInfo, FALSE);
2504 }
2505
2506 MenuSelectItem(Mt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL);
2507 }
2508
2509 /***********************************************************************
2510 * MenuExecFocusedItem
2511 *
2512 * Execute a menu item (for instance when user pressed Enter).
2513 * Return the wID of the executed item. Otherwise, -1 indicating
2514 * that no menu item was executed, -2 if a popup is shown;
2515 * Have to receive the flags for the TrackPopupMenu options to avoid
2516 * sending unwanted message.
2517 *
2518 */
2519 static INT FASTCALL
2520 MenuExecFocusedItem(MTRACKER *Mt, PROSMENUINFO MenuInfo, UINT Flags)
2521 {
2522 ROSMENUITEMINFO ItemInfo;
2523 UINT wID;
2524
2525 TRACE("%p menu=%p\n", Mt, MenuInfo);
2526
2527 if (0 == MenuInfo->MenuItemCount || NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2528 {
2529 return -1;
2530 }
2531
2532 MenuInitRosMenuItemInfo(&ItemInfo);
2533 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2534 {
2535 MenuCleanupRosMenuItemInfo(&ItemInfo);
2536 return -1;
2537 }
2538
2539 TRACE("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu);
2540
2541 if (0 == (ItemInfo.fType & MF_POPUP))
2542 {
2543 if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))
2544 && 0 == (ItemInfo.fType & MF_SEPARATOR))
2545 {
2546 /* If TPM_RETURNCMD is set you return the id, but
2547 do not send a message to the owner */
2548 if (0 == (Flags & TPM_RETURNCMD))
2549 {
2550 if (0 != (MenuInfo->Flags & MF_SYSMENU))
2551 {
2552 PostMessageW(Mt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID,
2553 MAKELPARAM((SHORT) Mt->Pt.x, (SHORT) Mt->Pt.y));
2554 }
2555 else
2556 {
2557 if (MenuInfo->dwStyle & MNS_NOTIFYBYPOS)
2558 PostMessageW(Mt->OwnerWnd, WM_MENUCOMMAND,
2559 MenuInfo->FocusedItem,
2560 (LPARAM)MenuInfo->Self);
2561 else
2562 PostMessageW(Mt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0);
2563 }
2564 }
2565 wID = ItemInfo.wID;
2566 MenuCleanupRosMenuItemInfo(&ItemInfo);
2567 return wID;
2568 }
2569 }
2570 else
2571 {
2572 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, MenuInfo, TRUE, Flags);
2573 return -2;
2574 }
2575
2576 return -1;
2577 }
2578
2579 /***********************************************************************
2580 * MenuButtonDown
2581 *
2582 * Return TRUE if we can go on with menu tracking.
2583 */
2584 static BOOL FASTCALL
2585 MenuButtonDown(MTRACKER* Mt, HMENU PtMenu, UINT Flags)
2586 {
2587 int Index;
2588 ROSMENUINFO MenuInfo;
2589 ROSMENUITEMINFO Item;
2590
2591 TRACE("%x PtMenu=%p\n", Mt, PtMenu);
2592
2593 if (NULL != PtMenu)
2594 {
2595 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2596 {
2597 return FALSE;
2598 }
2599 if (IS_SYSTEM_MENU(&MenuInfo))
2600 {
2601 Index = 0;
2602 }
2603 else
2604 {
2605 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2606 }
2607 MenuInitRosMenuItemInfo(&Item);
2608 if (NO_SELECTED_ITEM == Index || ! MenuGetRosMenuItemInfo(PtMenu, Index, &Item))
2609 {
2610 MenuCleanupRosMenuItemInfo(&Item);
2611 return FALSE;
2612 }
2613
2614 if (!(Item.fType & MF_SEPARATOR) &&
2615 !(Item.fState & (MFS_DISABLED | MFS_GRAYED)) )
2616 {
2617 if (MenuInfo.FocusedItem != Index)
2618 {
2619 MenuSwitchTracking(Mt, &MenuInfo, Index);
2620 }
2621
2622 /* If the popup menu is not already "popped" */
2623 if (0 == (Item.fState & MF_MOUSESELECT))
2624 {
2625 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2626 }
2627 }
2628
2629 MenuCleanupRosMenuItemInfo(&Item);
2630
2631 return TRUE;
2632 }
2633
2634 /* else the click was on the menu bar, finish the tracking */
2635
2636 return FALSE;
2637 }
2638
2639 /***********************************************************************
2640 * MenuButtonUp
2641 *
2642 * Return the value of MenuExecFocusedItem if
2643 * the selected item was not a popup. Else open the popup.
2644 * A -1 return value indicates that we go on with menu tracking.
2645 *
2646 */
2647 static INT FASTCALL
2648 MenuButtonUp(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2649 {
2650 UINT Id;
2651 ROSMENUINFO MenuInfo;
2652 ROSMENUITEMINFO ItemInfo;
2653
2654 TRACE("%p hmenu=%x\n", Mt, PtMenu);
2655
2656 if (NULL != PtMenu)
2657 {
2658 Id = 0;
2659 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2660 {
2661 return -1;
2662 }
2663
2664 if (! IS_SYSTEM_MENU(&MenuInfo))
2665 {
2666 Id = NtUserMenuItemFromPoint(Mt->OwnerWnd, MenuInfo.Self, Mt->Pt.x, Mt->Pt.y);
2667 }
2668 MenuInitRosMenuItemInfo(&ItemInfo);
2669 if (0 <= Id && MenuGetRosMenuItemInfo(MenuInfo.Self, Id, &ItemInfo) &&
2670 MenuInfo.FocusedItem == Id)
2671 {
2672 if (0 == (ItemInfo.fType & MF_POPUP))
2673 {
2674 INT ExecutedMenuId = MenuExecFocusedItem(Mt, &MenuInfo, Flags);
2675 MenuCleanupRosMenuItemInfo(&ItemInfo);
2676 return (ExecutedMenuId < 0) ? -1 : ExecutedMenuId;
2677 }
2678 MenuCleanupRosMenuItemInfo(&ItemInfo);
2679
2680 /* If we are dealing with the top-level menu */
2681 /* and this is a click on an already "popped" item: */
2682 /* Stop the menu tracking and close the opened submenus */
2683 if (Mt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide)
2684 {
2685 MenuCleanupRosMenuItemInfo(&ItemInfo);
2686 return 0;
2687 }
2688 }
2689 MenuCleanupRosMenuItemInfo(&ItemInfo);
2690 MenuInfo.TimeToHide = TRUE;
2691 MenuSetRosMenuInfo(&MenuInfo);
2692 }
2693
2694 return -1;
2695 }
2696
2697 /***********************************************************************
2698 * MenuPtMenu
2699 *
2700 * Walks menu chain trying to find a menu pt maps to.
2701 */
2702 static HMENU FASTCALL
2703 MenuPtMenu(HMENU Menu, POINT Pt)
2704 {
2705 extern LRESULT DefWndNCHitTest(HWND hWnd, POINT Point);
2706 ROSMENUINFO MenuInfo;
2707 ROSMENUITEMINFO ItemInfo;
2708 HMENU Ret = NULL;
2709 INT Ht;
2710
2711 if (! MenuGetRosMenuInfo(&MenuInfo, Menu))
2712 {
2713 return NULL;
2714 }
2715
2716 /* try subpopup first (if any) */
2717 if (NO_SELECTED_ITEM != MenuInfo.FocusedItem)
2718 {
2719 MenuInitRosMenuItemInfo(&ItemInfo);
2720 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo) &&
2721 0 != (ItemInfo.fType & MF_POPUP) &&
2722 0 != (ItemInfo.fState & MF_MOUSESELECT))
2723 {
2724 Ret = MenuPtMenu(ItemInfo.hSubMenu, Pt);
2725 if (NULL != Ret)
2726 {
2727 MenuCleanupRosMenuItemInfo(&ItemInfo);
2728 return Ret;
2729 }
2730 }
2731 MenuCleanupRosMenuItemInfo(&ItemInfo);
2732 }
2733
2734 /* check the current window (avoiding WM_HITTEST) */
2735 Ht = DefWndNCHitTest(MenuInfo.Wnd, Pt);
2736 if (0 != (MenuInfo.Flags & MF_POPUP))
2737 {
2738 if (HTNOWHERE != Ht && HTERROR != Ht)
2739 {
2740 Ret = Menu;
2741 }
2742 }
2743 else if (HTSYSMENU == Ht)
2744 {
2745 Ret = NtUserGetSystemMenu(MenuInfo.Wnd, FALSE);
2746 }
2747 else if (HTMENU == Ht)
2748 {
2749 Ret = GetMenu(MenuInfo.Wnd);
2750 }
2751
2752 return Ret;
2753 }
2754
2755 /***********************************************************************
2756 * MenuMouseMove
2757 *
2758 * Return TRUE if we can go on with menu tracking.
2759 */
2760 static BOOL FASTCALL
2761 MenuMouseMove(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2762 {
2763 UINT Index;
2764 ROSMENUINFO MenuInfo;
2765 ROSMENUITEMINFO ItemInfo;
2766
2767 if (NULL != PtMenu)
2768 {
2769 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2770 {
2771 return TRUE;
2772 }
2773 if (IS_SYSTEM_MENU(&MenuInfo))
2774 {
2775 Index = 0;
2776 }
2777 else
2778 {
2779 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2780 }
2781 }
2782 else
2783 {
2784 Index = NO_SELECTED_ITEM;
2785 }
2786
2787 if (NO_SELECTED_ITEM == Index)
2788 {
2789 if (Mt->CurrentMenu == MenuInfo.Self ||
2790 MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
2791 {
2792 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
2793 TRUE, Mt->TopMenu);
2794 }
2795 }
2796 else if (MenuInfo.FocusedItem != Index)
2797 {
2798 MenuInitRosMenuItemInfo(&ItemInfo);
2799 if (MenuGetRosMenuItemInfo(MenuInfo.Self, Index, &ItemInfo) &&
2800 !(ItemInfo.fType & MF_SEPARATOR))
2801 {
2802 MenuSwitchTracking(Mt, &MenuInfo, Index);
2803 if (!(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED)))
2804 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2805 }
2806 MenuCleanupRosMenuItemInfo(&ItemInfo);
2807 }
2808
2809 return TRUE;
2810 }
2811
2812 /******************************************************************************
2813 *
2814 * UINT MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo)
2815 */
2816 static UINT MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo)
2817 {
2818 UINT i;
2819 PROSMENUITEMINFO MenuItems;
2820
2821 i = MenuInfo->FocusedItem;
2822 if (NO_SELECTED_ITEM == i)
2823 {
2824 return i;
2825 }
2826
2827 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0)
2828 {
2829 return NO_SELECTED_ITEM;
2830 }
2831
2832 for (i++ ; i < MenuInfo->MenuItemCount; i++)
2833 {
2834 if (0 != (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)))
2835 {
2836 return i;
2837 }
2838 }
2839
2840 return NO_SELECTED_ITEM;
2841 }
2842
2843 /******************************************************************************
2844 *
2845 * UINT MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo)
2846 */
2847 static UINT FASTCALL
2848 MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo)
2849 {
2850 UINT i;
2851 PROSMENUITEMINFO MenuItems;
2852
2853 if (0 == MenuInfo->FocusedItem || NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2854 {
2855 return NO_SELECTED_ITEM;
2856 }
2857
2858 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0)
2859 {
2860 return NO_SELECTED_ITEM;
2861 }
2862
2863 /* Find the start of the column */
2864
2865 for (i = MenuInfo->FocusedItem;
2866 0 != i && 0 == (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
2867 --i)
2868 {
2869 ; /* empty */
2870 }
2871
2872 if (0 == i)
2873 {
2874 MenuCleanupAllRosMenuItemInfo(MenuItems);
2875 return NO_SELECTED_ITEM;
2876 }
2877
2878 for (--i; 0 != i; --i)
2879 {
2880 if (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
2881 {
2882 break;
2883 }
2884 }
2885
2886 MenuCleanupAllRosMenuItemInfo(MenuItems);
2887 TRACE("ret %d.\n", i );
2888
2889 return i;
2890 }
2891
2892 /***********************************************************************
2893 * MenuGetSubPopup
2894 *
2895 * Return the handle of the selected sub-popup menu (if any).
2896 */
2897 static HMENU FASTCALL
2898 MenuGetSubPopup(HMENU Menu)
2899 {
2900 ROSMENUINFO MenuInfo;
2901 ROSMENUITEMINFO ItemInfo;
2902
2903 if (! MenuGetRosMenuInfo(&MenuInfo, Menu)
2904 || NO_SELECTED_ITEM == MenuInfo.FocusedItem)
2905 {
2906 return NULL;
2907 }
2908
2909 MenuInitRosMenuItemInfo(&ItemInfo);
2910 if (! MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
2911 {
2912 MenuCleanupRosMenuItemInfo(&ItemInfo);
2913 return NULL;
2914 }
2915 if (0 != (ItemInfo.fType & MF_POPUP) && 0 != (ItemInfo.fState & MF_MOUSESELECT))
2916 {
2917 MenuCleanupRosMenuItemInfo(&ItemInfo);
2918 return ItemInfo.hSubMenu;
2919 }
2920
2921 MenuCleanupRosMenuItemInfo(&ItemInfo);
2922 return NULL;
2923 }
2924
2925 /***********************************************************************
2926 * MenuDoNextMenu
2927 *
2928 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2929 */
2930 static LRESULT FASTCALL
2931 MenuDoNextMenu(MTRACKER* Mt, UINT Vk)
2932 {
2933 ROSMENUINFO TopMenuInfo;
2934 ROSMENUINFO MenuInfo;
2935
2936 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2937 {
2938 return (LRESULT) FALSE;
2939 }
2940
2941 if ((VK_LEFT == Vk && 0 == TopMenuInfo.FocusedItem)
2942 || (VK_RIGHT == Vk && TopMenuInfo.FocusedItem == TopMenuInfo.MenuItemCount - 1))
2943 {
2944 MDINEXTMENU NextMenu;
2945 HMENU NewMenu;
2946 HWND NewWnd;
2947 UINT Id = 0;
2948
2949 NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(Mt->TopMenu, 0) : Mt->TopMenu;
2950 NextMenu.hmenuNext = NULL;
2951 NextMenu.hwndNext = NULL;
2952 SendMessageW(Mt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
2953
2954 TRACE("%p [%p] -> %p [%p]\n",
2955 Mt->CurrentMenu, Mt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
2956
2957 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
2958 {
2959 DWORD Style = GetWindowLongPtrW(Mt->OwnerWnd, GWL_STYLE);
2960 NewWnd = Mt->OwnerWnd;
2961 if (IS_SYSTEM_MENU(&TopMenuInfo))
2962 {
2963 /* switch to the menu bar */
2964
2965 if (0 != (Style & WS_CHILD)
2966 || NULL == (NewMenu = GetMenu(NewWnd)))
2967 {
2968 return FALSE;
2969 }
2970
2971 if (VK_LEFT == Vk)
2972 {
2973 if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu))
2974 {
2975 return FALSE;
2976 }
2977 Id = MenuInfo.MenuItemCount - 1;
2978 }
2979 }
2980 else if (0 != (Style & WS_SYSMENU))
2981 {
2982 /* switch to the system menu */
2983 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2984 }
2985 else
2986 {
2987 return FALSE;
2988 }
2989 }
2990 else /* application returned a new menu to switch to */
2991 {
2992 NewMenu = NextMenu.hmenuNext;
2993 NewWnd = NextMenu.hwndNext;
2994
2995 if (IsMenu(NewMenu) && IsWindow(NewWnd))
2996 {
2997 DWORD Style = GetWindowLongPtrW(NewWnd, GWL_STYLE);
2998
2999 if (0 != (Style & WS_SYSMENU)
3000 && GetSystemMenu(NewWnd, FALSE) == NewMenu)
3001 {
3002 /* get the real system menu */
3003 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
3004 }
3005 else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu)
3006 {
3007 /* FIXME: Not sure what to do here;
3008 * perhaps try to track NewMenu as a popup? */
3009
3010 WARN(" -- got confused.\n");
3011 return FALSE;
3012 }
3013 }
3014 else
3015 {
3016 return FALSE;
3017 }
3018 }
3019
3020 if (NewMenu != Mt->TopMenu)
3021 {
3022 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM,
3023 FALSE, 0 );
3024 if (Mt->CurrentMenu != Mt->TopMenu)
3025 {
3026 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE);
3027 }
3028 }
3029
3030 if (NewWnd != Mt->OwnerWnd)
3031 {
3032 Mt->OwnerWnd = NewWnd;
3033 SetCapture(Mt->OwnerWnd);
3034 (void)NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, Mt->OwnerWnd);
3035 }
3036
3037 Mt->TopMenu = Mt->CurrentMenu = NewMenu; /* all subpopups are hidden */
3038 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3039 {
3040 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0);
3041 }
3042
3043 return TRUE;
3044 }
3045
3046 return FALSE;
3047 }
3048
3049 /***********************************************************************
3050 * MenuSuspendPopup
3051 *
3052 * The idea is not to show the popup if the next input message is
3053 * going to hide it anyway.
3054 */
3055 static BOOL FASTCALL
3056 MenuSuspendPopup(MTRACKER* Mt, UINT Message)
3057 {
3058 MSG Msg;
3059
3060 Msg.hwnd = Mt->OwnerWnd;
3061
3062 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
3063 Mt->TrackFlags |= TF_SKIPREMOVE;
3064
3065 switch (Message)
3066 {
3067 case WM_KEYDOWN:
3068 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
3069 if (WM_KEYUP == Msg.message || WM_PAINT == Msg.message)
3070 {
3071 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
3072 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
3073 if (WM_KEYDOWN == Msg.message
3074 && (VK_LEFT == Msg.wParam || VK_RIGHT == Msg.wParam))
3075 {
3076 Mt->TrackFlags |= TF_SUSPENDPOPUP;
3077 return TRUE;
3078 }
3079 }
3080 break;
3081 }
3082
3083 /* failures go through this */
3084 Mt->TrackFlags &= ~TF_SUSPENDPOPUP;
3085
3086 return FALSE;
3087 }
3088
3089 /***********************************************************************
3090 * MenuKeyEscape
3091 *
3092 * Handle a VK_ESCAPE key event in a menu.
3093 */
3094 static BOOL FASTCALL
3095 MenuKeyEscape(MTRACKER *Mt, UINT Flags)
3096 {
3097 BOOL EndMenu = TRUE;
3098 ROSMENUINFO MenuInfo;
3099 HMENU MenuTmp, MenuPrev;
3100
3101 if (Mt->CurrentMenu != Mt->TopMenu)
3102 {
3103 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)
3104 && 0 != (MenuInfo.Flags & MF_POPUP))
3105 {
3106 MenuPrev = MenuTmp = Mt->TopMenu;
3107
3108 /* close topmost popup */
3109 while (MenuTmp != Mt->CurrentMenu)
3110 {
3111 MenuPrev = MenuTmp;
3112 MenuTmp = MenuGetSubPopup(MenuPrev);
3113 }
3114
3115 if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev))
3116 {
3117 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, TRUE);
3118 }
3119 Mt->CurrentMenu = MenuPrev;
3120 EndMenu = FALSE;
3121 }
3122 }
3123
3124 return EndMenu;
3125 }
3126
3127 /***********************************************************************
3128 * MenuKeyLeft
3129 *
3130 * Handle a VK_LEFT key event in a menu.
3131 */
3132 static void FASTCALL
3133 MenuKeyLeft(MTRACKER* Mt, UINT Flags)
3134 {
3135 ROSMENUINFO MenuInfo;
3136 ROSMENUINFO TopMenuInfo;
3137 ROSMENUINFO PrevMenuInfo;
3138 HMENU MenuTmp, MenuPrev;
3139 UINT PrevCol;
3140
3141 MenuPrev = MenuTmp = Mt->TopMenu;
3142
3143 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3144 {
3145 return;
3146 }
3147
3148 /* Try to move 1 column left (if possible) */
3149 if (NO_SELECTED_ITEM != (PrevCol = MenuGetStartOfPrevColumn(&MenuInfo)))
3150 {
3151 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3152 {
3153 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0);