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