Fix the USER32 DLL initialization and cleanup routines to prevent memory/resource...
[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 HMODULE hUser32;
1047 hUser32 = GetModuleHandleW(L"USER32");
1048 Result = (LRESULT)LoadMenuW(hUser32, L"SYSMENU");
1049 return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS));
1050 }
1051
1052
1053 BOOL
1054 MenuInit(VOID)
1055 {
1056 NONCLIENTMETRICSW ncm;
1057
1058 /* get the menu font */
1059 if(!hMenuFont || !hMenuFontBold)
1060 {
1061 ncm.cbSize = sizeof(ncm);
1062 if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
1063 {
1064 DbgPrint("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
1065 return FALSE;
1066 }
1067
1068 hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont);
1069 if(hMenuFont == NULL)
1070 {
1071 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
1072 return FALSE;
1073 }
1074
1075 ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
1076 hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont);
1077 if(hMenuFontBold == NULL)
1078 {
1079 DbgPrint("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
1080 DeleteObject(hMenuFont);
1081 hMenuFont = NULL;
1082 return FALSE;
1083 }
1084 }
1085
1086 return TRUE;
1087 }
1088
1089
1090 VOID
1091 MenuCleanup(VOID)
1092 {
1093 if (hMenuFont)
1094 {
1095 DeleteObject(hMenuFont);
1096 hMenuFont = NULL;
1097 }
1098
1099 if (hMenuFontBold)
1100 {
1101 DeleteObject(hMenuFontBold);
1102 hMenuFontBold = NULL;
1103 }
1104 }
1105
1106
1107
1108 /***********************************************************************
1109 * MenuCalcItemSize
1110 *
1111 * Calculate the size of the menu item and store it in ItemInfo->rect.
1112 */
1113 static void FASTCALL
1114 MenuCalcItemSize(HDC Dc, PROSMENUITEMINFO ItemInfo, HWND WndOwner,
1115 INT OrgX, INT OrgY, BOOL MenuBar)
1116 {
1117 PWCHAR p;
1118 UINT CheckBitmapWidth = GetSystemMetrics(SM_CXMENUCHECK);
1119
1120 DPRINT("dc=%x owner=%x (%d,%d)\n", Dc, WndOwner, OrgX, OrgY);
1121
1122 SetRect(&ItemInfo->Rect, OrgX, OrgY, OrgX, OrgY);
1123
1124 if (0 != (ItemInfo->fType & MF_OWNERDRAW))
1125 {
1126 /*
1127 ** Experimentation under Windows reveals that an owner-drawn
1128 ** menu is expected to return the size of the content part of
1129 ** the menu item, not including the checkmark nor the submenu
1130 ** arrow. Windows adds those values itself and returns the
1131 ** enlarged rectangle on subsequent WM_DRAWITEM messages.
1132 */
1133 MEASUREITEMSTRUCT mis;
1134 mis.CtlType = ODT_MENU;
1135 mis.CtlID = 0;
1136 mis.itemID = ItemInfo->wID;
1137 mis.itemData = (DWORD)ItemInfo->dwItemData;
1138 mis.itemHeight = 0;
1139 mis.itemWidth = 0;
1140 SendMessageW(WndOwner, WM_MEASUREITEM, 0, (LPARAM) &mis);
1141 ItemInfo->Rect.right += mis.itemWidth;
1142
1143 if (MenuBar)
1144 {
1145 ItemInfo->Rect.right += MENU_BAR_ITEMS_SPACE;
1146
1147 /* under at least win95 you seem to be given a standard
1148 height for the menu and the height value is ignored */
1149
1150 ItemInfo->Rect.bottom += GetSystemMetrics(SM_CYMENU) - 1;
1151 }
1152 else
1153 {
1154 ItemInfo->Rect.bottom += mis.itemHeight;
1155 }
1156
1157 DPRINT("id=%04x size=%dx%d\n", ItemInfo->wID, mis.itemWidth, mis.itemHeight);
1158 /* Fall through to get check/arrow width calculation. */
1159 }
1160
1161 if (0 != (ItemInfo->fType & MF_SEPARATOR))
1162 {
1163 ItemInfo->Rect.bottom += SEPARATOR_HEIGHT;
1164 return;
1165 }
1166
1167 if (! MenuBar)
1168 {
1169 ItemInfo->Rect.right += 2 * CheckBitmapWidth;
1170 if (0 != (ItemInfo->fType & MF_POPUP))
1171 {
1172 ItemInfo->Rect.right += ArrowBitmapWidth;
1173 }
1174 }
1175
1176 if (0 != (ItemInfo->fType & MF_OWNERDRAW))
1177 {
1178 return;
1179 }
1180
1181 if (IS_BITMAP_ITEM(ItemInfo->fType))
1182 {
1183 SIZE Size;
1184
1185 MenuGetBitmapItemSize((int) ItemInfo->hbmpItem, (DWORD) ItemInfo->hbmpItem, &Size);
1186 ItemInfo->Rect.right += Size.cx;
1187 ItemInfo->Rect.bottom += Size.cy;
1188
1189 /* Leave space for the sunken border */
1190 ItemInfo->Rect.right += 2;
1191 ItemInfo->Rect.bottom += 2;
1192
1193 /* Special case: Minimize button doesn't have a space behind it. */
1194 if (ItemInfo->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE ||
1195 ItemInfo->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE_D)
1196 ItemInfo->Rect.right -= 1;
1197 }
1198
1199 /* it must be a text item - unless it's the system menu */
1200 if (0 == (ItemInfo->fType & MF_SYSMENU) && IS_STRING_ITEM(ItemInfo->fType))
1201 {
1202 SIZE Size;
1203
1204 GetTextExtentPoint32W(Dc, (LPWSTR) ItemInfo->dwTypeData,
1205 wcslen((LPWSTR) ItemInfo->dwTypeData), &Size);
1206
1207 ItemInfo->Rect.right += Size.cx;
1208 ItemInfo->Rect.bottom += max(Size.cy, GetSystemMetrics(SM_CYMENU) - 1);
1209 ItemInfo->XTab = 0;
1210
1211 if (MenuBar)
1212 {
1213 ItemInfo->Rect.right += MENU_BAR_ITEMS_SPACE;
1214 }
1215 else if ((p = wcschr((LPWSTR) ItemInfo->dwTypeData, L'\t' )) != NULL)
1216 {
1217 /* Item contains a tab (only meaningful in popup menus) */
1218 GetTextExtentPoint32W(Dc, (LPWSTR) ItemInfo->dwTypeData,
1219 (int)(p - (LPWSTR) ItemInfo->dwTypeData), &Size);
1220 ItemInfo->XTab = CheckBitmapWidth + MENU_TAB_SPACE + Size.cx;
1221 ItemInfo->Rect.right += MENU_TAB_SPACE;
1222 }
1223 else
1224 {
1225 if (NULL != wcschr((LPWSTR) ItemInfo->dwTypeData, L'\b'))
1226 {
1227 ItemInfo->Rect.right += MENU_TAB_SPACE;
1228 }
1229 ItemInfo->XTab = ItemInfo->Rect.right - CheckBitmapWidth
1230 - ArrowBitmapWidth;
1231 }
1232 }
1233
1234 DPRINT("(%ld,%ld)-(%ld,%ld)\n", ItemInfo->Rect.left, ItemInfo->Rect.top, ItemInfo->Rect.right, ItemInfo->Rect.bottom);
1235 }
1236
1237 /***********************************************************************
1238 * MenuPopupMenuCalcSize
1239 *
1240 * Calculate the size of a popup menu.
1241 */
1242 static void FASTCALL
1243 MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo, HWND WndOwner)
1244 {
1245 ROSMENUITEMINFO ItemInfo;
1246 HDC Dc;
1247 int Start, i;
1248 int OrgX, OrgY, MaxX, MaxTab, MaxTabWidth;
1249
1250 MenuInfo->Width = MenuInfo->Height = 0;
1251 if (0 == MenuInfo->MenuItemCount)
1252 {
1253 MenuSetRosMenuInfo(MenuInfo);
1254 return;
1255 }
1256
1257 Dc = GetDC(NULL);
1258 SelectObject(Dc, hMenuFont);
1259
1260 Start = 0;
1261 MaxX = 2 + 1;
1262
1263 MenuInitRosMenuItemInfo(&ItemInfo);
1264 while (Start < MenuInfo->MenuItemCount)
1265 {
1266 OrgX = MaxX;
1267 OrgY = 2;
1268
1269 MaxTab = MaxTabWidth = 0;
1270
1271 /* Parse items until column break or end of menu */
1272 for (i = Start; i < MenuInfo->MenuItemCount; i++)
1273 {
1274 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo))
1275 {
1276 MenuCleanupRosMenuItemInfo(&ItemInfo);
1277 MenuSetRosMenuInfo(MenuInfo);
1278 return;
1279 }
1280 if (i != Start &&
1281 0 != (ItemInfo.fType & (MF_MENUBREAK | MF_MENUBARBREAK)))
1282 {
1283 break;
1284 }
1285
1286 MenuCalcItemSize(Dc, &ItemInfo, WndOwner, OrgX, OrgY, FALSE);
1287 if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo))
1288 {
1289 MenuCleanupRosMenuItemInfo(&ItemInfo);
1290 MenuSetRosMenuInfo(MenuInfo);
1291 return;
1292 }
1293
1294 if (0 != (ItemInfo.fType & MF_MENUBARBREAK))
1295 {
1296 OrgX++;
1297 }
1298 MaxX = max(MaxX, ItemInfo.Rect.right);
1299 OrgY = ItemInfo.Rect.bottom;
1300 if (IS_STRING_ITEM(ItemInfo.fType) && 0 != ItemInfo.XTab)
1301 {
1302 MaxTab = max(MaxTab, ItemInfo.XTab);
1303 MaxTabWidth = max(MaxTabWidth, ItemInfo.Rect.right - ItemInfo.XTab);
1304 }
1305 }
1306
1307 /* Finish the column (set all items to the largest width found) */
1308 MaxX = max(MaxX, MaxTab + MaxTabWidth);
1309 while (Start < i)
1310 {
1311 if (MenuGetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo))
1312 {
1313 ItemInfo.Rect.right = MaxX;
1314 if (IS_STRING_ITEM(ItemInfo.fType) && 0 != ItemInfo.XTab)
1315 {
1316 ItemInfo.XTab = MaxTab;
1317 }
1318 MenuSetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo);
1319 }
1320 Start++;
1321 }
1322 MenuInfo->Height = max(MenuInfo->Height, OrgY);
1323 }
1324
1325 MenuInfo->Width = MaxX;
1326
1327 /* space for 3d border */
1328 MenuInfo->Height += 2;
1329 MenuInfo->Width += 2;
1330
1331 ReleaseDC(NULL, Dc);
1332 MenuCleanupRosMenuItemInfo(&ItemInfo);
1333 MenuSetRosMenuInfo(MenuInfo);
1334 }
1335
1336 /***********************************************************************
1337 * MenuMenuBarCalcSize
1338 *
1339 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1340 * height is off by 1 pixel which causes lengthy window relocations when
1341 * active document window is maximized/restored.
1342 *
1343 * Calculate the size of the menu bar.
1344 */
1345 static void FASTCALL
1346 MenuMenuBarCalcSize(HDC Dc, LPRECT Rect, PROSMENUINFO MenuInfo, HWND WndOwner)
1347 {
1348 ROSMENUITEMINFO ItemInfo;
1349 int Start, i, OrgX, OrgY, MaxY, HelpPos;
1350
1351 if (NULL == Rect || NULL == MenuInfo)
1352 {
1353 return;
1354 }
1355 if (0 == MenuInfo->MenuItemCount)
1356 {
1357 return;
1358 }
1359
1360 DPRINT("left=%ld top=%ld right=%ld bottom=%ld\n",
1361 Rect->left, Rect->top, Rect->right, Rect->bottom);
1362 MenuInfo->Width = Rect->right - Rect->left;
1363 MenuInfo->Height = 0;
1364 MaxY = Rect->top + 1;
1365 Start = 0;
1366 HelpPos = -1;
1367 MenuInitRosMenuItemInfo(&ItemInfo);
1368 while (Start < MenuInfo->MenuItemCount)
1369 {
1370 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo))
1371 {
1372 MenuCleanupRosMenuItemInfo(&ItemInfo);
1373 return;
1374 }
1375 OrgX = Rect->left;
1376 OrgY = MaxY;
1377
1378 /* Parse items until line break or end of menu */
1379 for (i = Start; i < MenuInfo->MenuItemCount; i++)
1380 {
1381 if (-1 == HelpPos && 0 != (ItemInfo.fType & MF_RIGHTJUSTIFY))
1382 {
1383 HelpPos = i;
1384 }
1385 if (i != Start &&
1386 0 != (ItemInfo.fType & (MF_MENUBREAK | MF_MENUBARBREAK)))
1387 {
1388 break;
1389 }
1390
1391 DPRINT("calling MENU_CalcItemSize org=(%d, %d)\n", OrgX, OrgY);
1392 MenuCalcItemSize(Dc, &ItemInfo, WndOwner, OrgX, OrgY, TRUE);
1393 if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo))
1394 {
1395 MenuCleanupRosMenuItemInfo(&ItemInfo);
1396 return;
1397 }
1398
1399 if (ItemInfo.Rect.right > Rect->right)
1400 {
1401 if (i != Start)
1402 {
1403 break;
1404 }
1405 else
1406 {
1407 ItemInfo.Rect.right = Rect->right;
1408 }
1409 }
1410 MaxY = max(MaxY, ItemInfo.Rect.bottom );
1411 OrgX = ItemInfo.Rect.right;
1412 if (i + 1 < MenuInfo->MenuItemCount)
1413 {
1414 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i + 1, &ItemInfo))
1415 {
1416 MenuCleanupRosMenuItemInfo(&ItemInfo);
1417 return;
1418 }
1419 }
1420 }
1421
1422 /* FIXME: Is this really needed? */
1423 #if 0
1424 /* Finish the line (set all items to the largest height found) */
1425 while (Start < i)
1426 {
1427 if (MenuGetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo))
1428 {
1429 ItemInfo.Rect.bottom = MaxY;
1430 MenuSetRosMenuItemInfo(MenuInfo->Self, Start, &ItemInfo);
1431 }
1432 Start++;
1433 }
1434 #else
1435 Start = i;
1436 #endif
1437 }
1438
1439 Rect->bottom = MaxY;
1440 MenuInfo->Height = Rect->bottom - Rect->top;
1441 MenuSetRosMenuInfo(MenuInfo);
1442
1443 if (-1 != HelpPos)
1444 {
1445 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1446 /* the last item (if several lines, only move the last line) */
1447 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->MenuItemCount - 1, &ItemInfo))
1448 {
1449 MenuCleanupRosMenuItemInfo(&ItemInfo);
1450 return;
1451 }
1452 OrgY = ItemInfo.Rect.top;
1453 OrgX = Rect->right;
1454 for (i = MenuInfo->MenuItemCount - 1; HelpPos <= i; i--)
1455 {
1456 if (i < HelpPos)
1457 {
1458 break; /* done */
1459 }
1460 if (ItemInfo.Rect.top != OrgY)
1461 {
1462 break; /* Other line */
1463 }
1464 if (OrgX <= ItemInfo.Rect.right)
1465 {
1466 break; /* Too far right already */
1467 }
1468 ItemInfo.Rect.left += OrgX - ItemInfo.Rect.right;
1469 ItemInfo.Rect.right = OrgX;
1470 OrgX = ItemInfo.Rect.left;
1471 MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo);
1472 if (HelpPos + 1 <= i &&
1473 ! MenuGetRosMenuItemInfo(MenuInfo->Self, i - 1, &ItemInfo))
1474 {
1475 MenuCleanupRosMenuItemInfo(&ItemInfo);
1476 return;
1477 }
1478 }
1479 }
1480
1481 MenuCleanupRosMenuItemInfo(&ItemInfo);
1482 }
1483
1484 /***********************************************************************
1485 * DrawMenuBarTemp (USER32.@)
1486 *
1487 * UNDOCUMENTED !!
1488 *
1489 * called by W98SE desk.cpl Control Panel Applet
1490 *
1491 * Not 100% sure about the param names, but close.
1492 *
1493 * @implemented
1494 */
1495 DWORD WINAPI
1496 DrawMenuBarTemp(HWND Wnd, HDC DC, LPRECT Rect, HMENU Menu, HFONT Font)
1497 {
1498 ROSMENUINFO MenuInfo;
1499 ROSMENUITEMINFO ItemInfo;
1500 UINT i;
1501 HFONT FontOld = NULL;
1502
1503 if (NULL == Menu)
1504 {
1505 Menu = GetMenu(Wnd);
1506 }
1507
1508 if (NULL == Font)
1509 {
1510 Font = hMenuFont;
1511 }
1512
1513 if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
1514 {
1515 return GetSystemMetrics(SM_CYMENU);
1516 }
1517
1518 DPRINT("(%x, %x, %p, %x, %x)\n", Wnd, DC, Rect, Menu, Font);
1519
1520 FontOld = SelectObject(DC, Font);
1521
1522 if (0 == MenuInfo.Height)
1523 {
1524 MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
1525 }
1526
1527 Rect->bottom = Rect->top + MenuInfo.Height;
1528
1529 FillRect(DC, Rect, GetSysColorBrush(COLOR_MENU));
1530
1531 SelectObject(DC, GetSysColorPen(COLOR_3DFACE));
1532 MoveToEx(DC, Rect->left, Rect->bottom, NULL);
1533 LineTo(DC, Rect->right, Rect->bottom);
1534
1535 if (0 == MenuInfo.MenuItemCount)
1536 {
1537 SelectObject(DC, FontOld);
1538 return GetSystemMetrics(SM_CYMENU);
1539 }
1540
1541 MenuInitRosMenuItemInfo(&ItemInfo);
1542 for (i = 0; i < MenuInfo.MenuItemCount; i++)
1543 {
1544 if (MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
1545 {
1546 MenuDrawMenuItem(Wnd, &MenuInfo, Wnd, DC, &ItemInfo,
1547 MenuInfo.Height, TRUE, ODA_DRAWENTIRE);
1548 }
1549 }
1550 MenuCleanupRosMenuItemInfo(&ItemInfo);
1551
1552 SelectObject(DC, FontOld);
1553
1554 return MenuInfo.Height;
1555 }
1556
1557
1558 /***********************************************************************
1559 * MenuDrawMenuBar
1560 *
1561 * Paint a menu bar. Returns the height of the menu bar.
1562 * called from [windows/nonclient.c]
1563 */
1564 UINT MenuDrawMenuBar(HDC DC, LPRECT Rect, HWND Wnd, BOOL SuppressDraw)
1565 {
1566 ROSMENUINFO MenuInfo;
1567 HFONT FontOld = NULL;
1568 HMENU Menu = GetMenu(Wnd);
1569
1570 if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
1571 {
1572 return GetSystemMetrics(SM_CYMENU);
1573 }
1574
1575 if (SuppressDraw)
1576 {
1577 FontOld = SelectObject(DC, hMenuFont);
1578
1579 MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
1580
1581 Rect->bottom = Rect->top + MenuInfo.Height;
1582
1583 if (NULL != FontOld)
1584 {
1585 SelectObject(DC, FontOld);
1586 }
1587 return MenuInfo.Height;
1588 }
1589 else
1590 {
1591 return DrawMenuBarTemp(Wnd, DC, Rect, Menu, NULL);
1592 }
1593 }
1594
1595 /***********************************************************************
1596 * MenuInitTracking
1597 */
1598 static BOOL FASTCALL
1599 MenuInitTracking(HWND Wnd, HMENU Menu, BOOL Popup, UINT Flags)
1600 {
1601 DPRINT("Wnd=%p Menu=%p\n", Wnd, Menu);
1602
1603 HideCaret(0);
1604
1605 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
1606 if (0 == (Flags & TPM_NONOTIFY))
1607 {
1608 SendMessageW(Wnd, WM_ENTERMENULOOP, Popup, 0);
1609 }
1610
1611 SendMessageW(Wnd, WM_SETCURSOR, (WPARAM) Wnd, HTCAPTION);
1612
1613 if (0 == (Flags & TPM_NONOTIFY))
1614 {
1615 ROSMENUINFO MenuInfo;
1616
1617 SendMessageW(Wnd, WM_INITMENU, (WPARAM)Menu, 0);
1618
1619 if (MenuGetRosMenuInfo(&MenuInfo, Menu) && 0 == MenuInfo.Height)
1620 {
1621 /* app changed/recreated menu bar entries in WM_INITMENU
1622 Recalculate menu sizes else clicks will not work */
1623 SetWindowPos(Wnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
1624 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
1625
1626 }
1627 }
1628
1629 return TRUE;
1630 }
1631
1632
1633 /***********************************************************************
1634 * MenuShowPopup
1635 *
1636 * Display a popup menu.
1637 */
1638 static BOOL FASTCALL
1639 MenuShowPopup(HWND WndOwner, HMENU Menu, UINT Id,
1640 INT X, INT Y, INT XAnchor, INT YAnchor )
1641 {
1642 ROSMENUINFO MenuInfo;
1643 ROSMENUITEMINFO ItemInfo;
1644 UINT Width, Height;
1645
1646 DPRINT("owner=%x hmenu=%x id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1647 WndOwner, Menu, Id, X, Y, XAnchor, YAnchor);
1648
1649 if (! MenuGetRosMenuInfo(&MenuInfo, Menu))
1650 {
1651 return FALSE;
1652 }
1653
1654 if (NO_SELECTED_ITEM != MenuInfo.FocusedItem)
1655 {
1656 MenuInitRosMenuItemInfo(&ItemInfo);
1657 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
1658 {
1659 ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
1660 MenuSetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo);
1661 }
1662 MenuCleanupRosMenuItemInfo(&ItemInfo);
1663 MenuInfo.FocusedItem = NO_SELECTED_ITEM;
1664 }
1665
1666 /* store the owner for DrawItem */
1667 MenuInfo.WndOwner = WndOwner;
1668 MenuSetRosMenuInfo(&MenuInfo);
1669
1670 MenuPopupMenuCalcSize(&MenuInfo, WndOwner);
1671
1672 /* adjust popup menu pos so that it fits within the desktop */
1673
1674 Width = MenuInfo.Width + GetSystemMetrics(SM_CXBORDER);
1675 Height = MenuInfo.Height + GetSystemMetrics(SM_CYBORDER);
1676
1677 if (GetSystemMetrics(SM_CXSCREEN ) < X + Width)
1678 {
1679 if (0 != XAnchor)
1680 {
1681 X -= Width - XAnchor;
1682 }
1683 if (GetSystemMetrics(SM_CXSCREEN) < X + Width)
1684 {
1685 X = GetSystemMetrics(SM_CXSCREEN) - Width;
1686 }
1687 }
1688 if (X < 0 )
1689 {
1690 X = 0;
1691 }
1692
1693 if (GetSystemMetrics(SM_CYSCREEN) < Y + Height)
1694 {
1695 if (0 != YAnchor)
1696 {
1697 Y -= Height + YAnchor;
1698 }
1699 if (GetSystemMetrics(SM_CYSCREEN) < Y + Height)
1700 {
1701 Y = GetSystemMetrics(SM_CYSCREEN) - Height;
1702 }
1703 }
1704 if (Y < 0 )
1705 {
1706 Y = 0;
1707 }
1708
1709
1710 /* NOTE: In Windows, top menu popup is not owned. */
1711 MenuInfo.Wnd = CreateWindowExW(0, POPUPMENU_CLASS_ATOMW, NULL,
1712 WS_POPUP, X, Y, Width, Height,
1713 WndOwner, 0, (HINSTANCE) GetWindowLongW(WndOwner, GWL_HINSTANCE),
1714 (LPVOID) MenuInfo.Self);
1715 if (NULL == MenuInfo.Wnd || ! MenuSetRosMenuInfo(&MenuInfo))
1716 {
1717 return FALSE;
1718 }
1719 if (NULL == TopPopup)
1720 {
1721 TopPopup = MenuInfo.Wnd;
1722 }
1723
1724 /* Display the window */
1725 SetWindowPos(MenuInfo.Wnd, HWND_TOPMOST, 0, 0, 0, 0,
1726 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
1727 UpdateWindow(MenuInfo.Wnd);
1728
1729 return TRUE;
1730 }
1731
1732 /***********************************************************************
1733 * MenuFindSubMenu
1734 *
1735 * Find a Sub menu. Return the position of the submenu, and modifies
1736 * *hmenu in case it is found in another sub-menu.
1737 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
1738 */
1739 static UINT FASTCALL
1740 MenuFindSubMenu(HMENU *Menu, HMENU SubTarget)
1741 {
1742 ROSMENUINFO MenuInfo;
1743 ROSMENUITEMINFO ItemInfo;
1744 UINT i;
1745 HMENU SubMenu;
1746 UINT Pos;
1747
1748 if ((HMENU) 0xffff == *Menu
1749 || ! MenuGetRosMenuInfo(&MenuInfo, *Menu))
1750 {
1751 return NO_SELECTED_ITEM;
1752 }
1753
1754 MenuInitRosMenuItemInfo(&ItemInfo);
1755 for (i = 0; i < MenuInfo.MenuItemCount; i++)
1756 {
1757 if (! MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
1758 {
1759 MenuCleanupRosMenuItemInfo(&ItemInfo);
1760 return NO_SELECTED_ITEM;
1761 }
1762 if (0 == (ItemInfo.fType & MF_POPUP))
1763 {
1764 continue;
1765 }
1766 if (ItemInfo.hSubMenu == SubTarget)
1767 {
1768 MenuCleanupRosMenuItemInfo(&ItemInfo);
1769 return i;
1770 }
1771 SubMenu = ItemInfo.hSubMenu;
1772 Pos = MenuFindSubMenu(&SubMenu, SubTarget);
1773 if (NO_SELECTED_ITEM != Pos)
1774 {
1775 *Menu = SubMenu;
1776 return Pos;
1777 }
1778 }
1779 MenuCleanupRosMenuItemInfo(&ItemInfo);
1780
1781 return NO_SELECTED_ITEM;
1782 }
1783
1784 /***********************************************************************
1785 * MenuSelectItem
1786 */
1787 static void FASTCALL
1788 MenuSelectItem(HWND WndOwner, PROSMENUINFO MenuInfo, UINT Index,
1789 BOOL SendMenuSelect, HMENU TopMenu)
1790 {
1791 HDC Dc;
1792 ROSMENUITEMINFO ItemInfo;
1793 ROSMENUINFO TopMenuInfo;
1794 int Pos;
1795
1796 DPRINT("owner=%x menu=%p index=0x%04x select=0x%04x\n", WndOwner, MenuInfo, Index, SendMenuSelect);
1797
1798 if (NULL == MenuInfo || 0 == MenuInfo->MenuItemCount || NULL == MenuInfo->Wnd)
1799 {
1800 return;
1801 }
1802
1803 if (MenuInfo->FocusedItem == Index)
1804 {
1805 return;
1806 }
1807
1808 if (0 != (MenuInfo->Flags & MF_POPUP))
1809 {
1810 Dc = GetDC(MenuInfo->Wnd);
1811 }
1812 else
1813 {
1814 Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
1815 }
1816
1817 if (NULL == TopPopup)
1818 {
1819 TopPopup = MenuInfo->Wnd;
1820 }
1821
1822 SelectObject(Dc, hMenuFont);
1823 MenuInitRosMenuItemInfo(&ItemInfo);
1824
1825 /* Clear previous highlighted item */
1826 if (NO_SELECTED_ITEM != MenuInfo->FocusedItem)
1827 {
1828 if (MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
1829 {
1830 ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
1831 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
1832 }
1833 MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo,
1834 MenuInfo->Height, ! (MenuInfo->Flags & MF_POPUP),
1835 ODA_SELECT);
1836 }
1837
1838 /* Highlight new item (if any) */
1839 MenuInfo->FocusedItem = Index;
1840 MenuSetRosMenuInfo(MenuInfo);
1841 if (NO_SELECTED_ITEM != MenuInfo->FocusedItem)
1842 {
1843 if (MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
1844 {
1845 if (0 == (ItemInfo.fType & MF_SEPARATOR))
1846 {
1847 ItemInfo.fState |= MF_HILITE;
1848 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
1849 MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc,
1850 &ItemInfo, MenuInfo->Height, ! (MenuInfo->Flags & MF_POPUP),
1851 ODA_SELECT);
1852 }
1853 if (SendMenuSelect)
1854 {
1855 SendMessageW(WndOwner, WM_MENUSELECT,
1856 MAKELONG(ItemInfo.fType & MF_POPUP ? Index : ItemInfo.wID,
1857 ItemInfo.fType | ItemInfo.fState | MF_MOUSESELECT |
1858 (MenuInfo->Flags & MF_SYSMENU)), (LPARAM) MenuInfo->Self);
1859 }
1860 }
1861 }
1862 else if (SendMenuSelect)
1863 {
1864 if (NULL != TopMenu)
1865 {
1866 Pos = MenuFindSubMenu(&TopMenu, MenuInfo->Self);
1867 if (NO_SELECTED_ITEM != Pos)
1868 {
1869 if (MenuGetRosMenuInfo(&TopMenuInfo, TopMenu)
1870 && MenuGetRosMenuItemInfo(TopMenu, Pos, &ItemInfo))
1871 {
1872 SendMessageW(WndOwner, WM_MENUSELECT,
1873 MAKELONG(Pos, ItemInfo.fType | ItemInfo.fState
1874 | MF_MOUSESELECT
1875 | (TopMenuInfo.Flags & MF_SYSMENU)),
1876 (LPARAM) TopMenu);
1877 }
1878 }
1879 }
1880 }
1881
1882 MenuCleanupRosMenuItemInfo(&ItemInfo);
1883 ReleaseDC(MenuInfo->Wnd, Dc);
1884 }
1885
1886 /***********************************************************************
1887 * MenuMoveSelection
1888 *
1889 * Moves currently selected item according to the Offset parameter.
1890 * If there is no selection then it should select the last item if
1891 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
1892 */
1893 static void FASTCALL
1894 MenuMoveSelection(HWND WndOwner, PROSMENUINFO MenuInfo, INT Offset)
1895 {
1896 INT i;
1897 ROSMENUITEMINFO ItemInfo;
1898 INT OrigPos;
1899
1900 DPRINT("hwnd=%x menu=%x off=0x%04x\n", WndOwner, MenuInfo, Offset);
1901
1902 /* Prevent looping */
1903 if (0 == MenuInfo->MenuItemCount || 0 == Offset)
1904 return;
1905 else if (Offset < -1)
1906 Offset = -1;
1907 else if (Offset > 1)
1908 Offset = 1;
1909
1910 MenuInitRosMenuItemInfo(&ItemInfo);
1911
1912 OrigPos = MenuInfo->FocusedItem;
1913 if (OrigPos == NO_SELECTED_ITEM) /* NO_SELECTED_ITEM is not -1 ! */
1914 {
1915 OrigPos = 0;
1916 i = -1;
1917 }
1918 else
1919 {
1920 i = MenuInfo->FocusedItem;
1921 }
1922
1923 do
1924 {
1925 /* Step */
1926 i += Offset;
1927 /* Clip and wrap around */
1928 if (i < 0)
1929 {
1930 i = MenuInfo->MenuItemCount - 1;
1931 }
1932 else if (i >= MenuInfo->MenuItemCount)
1933 {
1934 i = 0;
1935 }
1936 /* If this is a good candidate; */
1937 if (MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo) &&
1938 0 == (ItemInfo.fType & MF_SEPARATOR) &&
1939 0 == (ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED)) )
1940 {
1941 MenuSelectItem(WndOwner, MenuInfo, i, TRUE, NULL);
1942 MenuCleanupRosMenuItemInfo(&ItemInfo);
1943 return;
1944 }
1945 } while (i != OrigPos);
1946
1947 /* Not found */
1948 MenuCleanupRosMenuItemInfo(&ItemInfo);
1949 }
1950
1951 /***********************************************************************
1952 * MenuInitSysMenuPopup
1953 *
1954 * Grey the appropriate items in System menu.
1955 */
1956 void FASTCALL
1957 MenuInitSysMenuPopup(HMENU Menu, DWORD Style, DWORD ClsStyle, LONG HitTest )
1958 {
1959 BOOL Gray;
1960 UINT DefItem;
1961 #if 0
1962 MENUITEMINFOW mii;
1963 #endif
1964
1965 Gray = 0 == (Style & WS_THICKFRAME) || 0 != (Style & (WS_MAXIMIZE | WS_MINIMIZE));
1966 EnableMenuItem(Menu, SC_SIZE, (Gray ? MF_GRAYED : MF_ENABLED));
1967 Gray = 0 != (Style & WS_MAXIMIZE);
1968 EnableMenuItem(Menu, SC_MOVE, (Gray ? MF_GRAYED : MF_ENABLED));
1969 Gray = 0 == (Style & WS_MINIMIZEBOX) || 0 != (Style & WS_MINIMIZE);
1970 EnableMenuItem(Menu, SC_MINIMIZE, (Gray ? MF_GRAYED : MF_ENABLED));
1971 Gray = 0 == (Style & WS_MAXIMIZEBOX) || 0 != (Style & WS_MAXIMIZE);
1972 EnableMenuItem(Menu, SC_MAXIMIZE, (Gray ? MF_GRAYED : MF_ENABLED));
1973 Gray = 0 == (Style & (WS_MAXIMIZE | WS_MINIMIZE));
1974 EnableMenuItem(Menu, SC_RESTORE, (Gray ? MF_GRAYED : MF_ENABLED));
1975 Gray = 0 != (ClsStyle & CS_NOCLOSE);
1976
1977 /* The menu item must keep its state if it's disabled */
1978 if (Gray)
1979 {
1980 EnableMenuItem(Menu, SC_CLOSE, MF_GRAYED);
1981 }
1982
1983 /* Set default menu item */
1984 if(Style & WS_MINIMIZE)
1985 {
1986 DefItem = SC_RESTORE;
1987 }
1988 else
1989 {
1990 if(HitTest == HTCAPTION)
1991 {
1992 DefItem = ((Style & (WS_MAXIMIZE | WS_MINIMIZE)) ? SC_RESTORE : SC_MAXIMIZE);
1993 }
1994 else
1995 {
1996 DefItem = SC_CLOSE;
1997 }
1998 }
1999 #if 0
2000 mii.cbSize = sizeof(MENUITEMINFOW);
2001 mii.fMask = MIIM_STATE;
2002 if((DefItem != SC_CLOSE) && GetMenuItemInfoW(Menu, DefItem, FALSE, &mii) &&
2003 (mii.fState & (MFS_GRAYED | MFS_DISABLED)))
2004 {
2005 DefItem = SC_CLOSE;
2006 }
2007 #endif
2008 SetMenuDefaultItem(Menu, DefItem, MF_BYCOMMAND);
2009 }
2010
2011 /***********************************************************************
2012 * MenuShowSubPopup
2013 *
2014 * Display the sub-menu of the selected item of this menu.
2015 * Return the handle of the submenu, or menu if no submenu to display.
2016 */
2017 static HMENU FASTCALL
2018 MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags)
2019 {
2020 extern void FASTCALL NcGetSysPopupPos(HWND Wnd, RECT *Rect);
2021 RECT Rect;
2022 ROSMENUITEMINFO ItemInfo;
2023 ROSMENUINFO SubMenuInfo;
2024 HDC Dc;
2025 HMENU Ret;
2026
2027 DPRINT("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst);
2028
2029 if (NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2030 {
2031 return MenuInfo->Self;
2032 }
2033
2034 MenuInitRosMenuItemInfo(&ItemInfo);
2035 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2036 {
2037 MenuCleanupRosMenuItemInfo(&ItemInfo);
2038 return MenuInfo->Self;
2039 }
2040 if (0 == (ItemInfo.fType & MF_POPUP) || 0 != (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)))
2041 {
2042 MenuCleanupRosMenuItemInfo(&ItemInfo);
2043 return MenuInfo->Self;
2044 }
2045
2046 /* message must be sent before using item,
2047 because nearly everything may be changed by the application ! */
2048
2049 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2050 if (0 == (Flags & TPM_NONOTIFY))
2051 {
2052 SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu,
2053 MAKELONG(MenuInfo->FocusedItem, IS_SYSTEM_MENU(MenuInfo)));
2054 }
2055
2056 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2057 {
2058 MenuCleanupRosMenuItemInfo(&ItemInfo);
2059 return MenuInfo->Self;
2060 }
2061 Rect = ItemInfo.Rect;
2062
2063 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2064 if (0 == (ItemInfo.fState & MF_HILITE))
2065 {
2066 if (0 != (MenuInfo->Flags & MF_POPUP))
2067 {
2068 Dc = GetDC(MenuInfo->Wnd);
2069 }
2070 else
2071 {
2072 Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
2073 }
2074
2075 SelectObject(Dc, hMenuFont);
2076
2077 ItemInfo.fState |= MF_HILITE;
2078 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2079 MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->Height,
2080 ! (MenuInfo->Flags & MF_POPUP), ODA_DRAWENTIRE);
2081 ReleaseDC(MenuInfo->Wnd, Dc);
2082 }
2083
2084 if (0 == ItemInfo.Rect.top && 0 == ItemInfo.Rect.left
2085 && 0 == ItemInfo.Rect.bottom && 0 == ItemInfo.Rect.right)
2086 {
2087 ItemInfo.Rect = Rect;
2088 }
2089
2090 ItemInfo.fState |= MF_MOUSESELECT;
2091
2092 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2093
2094 if (IS_SYSTEM_MENU(MenuInfo))
2095 {
2096 MenuInitSysMenuPopup(ItemInfo.hSubMenu, GetWindowLongW(MenuInfo->Wnd, GWL_STYLE),
2097 GetClassLongW(MenuInfo->Wnd, GCL_STYLE), HTSYSMENU);
2098
2099 NcGetSysPopupPos(MenuInfo->Wnd, &Rect);
2100 Rect.top = Rect.bottom;
2101 Rect.right = GetSystemMetrics(SM_CXSIZE);
2102 Rect.bottom = GetSystemMetrics(SM_CYSIZE);
2103 }
2104 else
2105 {
2106 GetWindowRect(MenuInfo->Wnd, &Rect);
2107 if (0 != (MenuInfo->Flags & MF_POPUP))
2108 {
2109 Rect.left += ItemInfo.Rect.right - GetSystemMetrics(SM_CXBORDER);
2110 Rect.top += ItemInfo.Rect.top;
2111 Rect.right = ItemInfo.Rect.left - ItemInfo.Rect.right + GetSystemMetrics(SM_CXBORDER);
2112 Rect.bottom = ItemInfo.Rect.top - ItemInfo.Rect.bottom;
2113 }
2114 else
2115 {
2116 Rect.left += ItemInfo.Rect.left;
2117 Rect.top += ItemInfo.Rect.bottom;
2118 Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left;
2119 Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top;
2120 }
2121 }
2122
2123 MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->FocusedItem,
2124 Rect.left, Rect.top, Rect.right, Rect.bottom );
2125 if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2126 {
2127 MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT);
2128 }
2129
2130 Ret = ItemInfo.hSubMenu;
2131 MenuCleanupRosMenuItemInfo(&ItemInfo);
2132
2133 return Ret;
2134 }
2135
2136 /***********************************************************************
2137 * MenuHideSubPopups
2138 *
2139 * Hide the sub-popup menus of this menu.
2140 */
2141 static void FASTCALL
2142 MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SendMenuSelect)
2143 {
2144 ROSMENUINFO SubMenuInfo;
2145 ROSMENUITEMINFO ItemInfo;
2146
2147 DPRINT("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect);
2148
2149 if (NULL != MenuInfo && NULL != TopPopup && NO_SELECTED_ITEM != MenuInfo->FocusedItem)
2150 {
2151 MenuInitRosMenuItemInfo(&ItemInfo);
2152 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo)
2153 || 0 == (ItemInfo.fType & MF_POPUP)
2154 || 0 == (ItemInfo.fState & MF_MOUSESELECT))
2155 {
2156 MenuCleanupRosMenuItemInfo(&ItemInfo);
2157 return;
2158 }
2159 ItemInfo.fState &= ~MF_MOUSESELECT;
2160 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo);
2161 if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2162 {
2163 MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE);
2164 MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL);
2165 DestroyWindow(SubMenuInfo.Wnd);
2166 SubMenuInfo.Wnd = NULL;
2167 MenuSetRosMenuInfo(&SubMenuInfo);
2168 }
2169 }
2170 }
2171
2172 /***********************************************************************
2173 * MenuSwitchTracking
2174 *
2175 * Helper function for menu navigation routines.
2176 */
2177 static void FASTCALL
2178 MenuSwitchTracking(MTRACKER* Mt, PROSMENUINFO PtMenuInfo, UINT Index)
2179 {
2180 ROSMENUINFO TopMenuInfo;
2181
2182 DPRINT("%x menu=%x 0x%04x\n", Mt, PtMenuInfo->Self, Index);
2183
2184 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu) &&
2185 Mt->TopMenu != PtMenuInfo->Self &&
2186 0 == ((PtMenuInfo->Flags | TopMenuInfo.Flags) & MF_POPUP))
2187 {
2188 /* both are top level menus (system and menu-bar) */
2189 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE);
2190 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL);
2191 Mt->TopMenu = PtMenuInfo->Self;
2192 }
2193 else
2194 {
2195 MenuHideSubPopups(Mt->OwnerWnd, PtMenuInfo, FALSE);
2196 }
2197
2198 MenuSelectItem(Mt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL);
2199 }
2200
2201 /***********************************************************************
2202 * MenuExecFocusedItem
2203 *
2204 * Execute a menu item (for instance when user pressed Enter).
2205 * Return the wID of the executed item. Otherwise, -1 indicating
2206 * that no menu item was executed;
2207 * Have to receive the flags for the TrackPopupMenu options to avoid
2208 * sending unwanted message.
2209 *
2210 */
2211 static INT FASTCALL
2212 MenuExecFocusedItem(MTRACKER *Mt, PROSMENUINFO MenuInfo, UINT Flags)
2213 {
2214 ROSMENUITEMINFO ItemInfo;
2215 UINT wID;
2216
2217 DPRINT("%p menu=%p\n", Mt, MenuInfo);
2218
2219 if (0 == MenuInfo->MenuItemCount || NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2220 {
2221 return -1;
2222 }
2223
2224 MenuInitRosMenuItemInfo(&ItemInfo);
2225 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->FocusedItem, &ItemInfo))
2226 {
2227 MenuCleanupRosMenuItemInfo(&ItemInfo);
2228 return -1;
2229 }
2230
2231 DPRINT("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu);
2232
2233 if (0 == (ItemInfo.fType & MF_POPUP))
2234 {
2235 if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))
2236 && 0 == (ItemInfo.fType & MF_SEPARATOR))
2237 {
2238 /* If TPM_RETURNCMD is set you return the id, but
2239 do not send a message to the owner */
2240 if (0 == (Flags & TPM_RETURNCMD))
2241 {
2242 if (0 != (MenuInfo->Flags & MF_SYSMENU))
2243 {
2244 PostMessageW(Mt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID,
2245 MAKELPARAM((SHORT) Mt->Pt.x, (SHORT) Mt->Pt.y));
2246 }
2247 else
2248 {
2249 PostMessageW(Mt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0);
2250 }
2251 }
2252 wID = ItemInfo.wID;
2253 MenuCleanupRosMenuItemInfo(&ItemInfo);
2254 return wID;
2255 }
2256 }
2257 else
2258 {
2259 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, MenuInfo, TRUE, Flags);
2260 }
2261
2262 return -1;
2263 }
2264
2265 /***********************************************************************
2266 * MenuButtonDown
2267 *
2268 * Return TRUE if we can go on with menu tracking.
2269 */
2270 static BOOL FASTCALL
2271 MenuButtonDown(MTRACKER* Mt, HMENU PtMenu, UINT Flags)
2272 {
2273 int Index;
2274 ROSMENUINFO MenuInfo;
2275 ROSMENUITEMINFO Item;
2276
2277 DPRINT("%x PtMenu=%p\n", Mt, PtMenu);
2278
2279 if (NULL != PtMenu)
2280 {
2281 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2282 {
2283 return FALSE;
2284 }
2285 if (IS_SYSTEM_MENU(&MenuInfo))
2286 {
2287 Index = 0;
2288 }
2289 else
2290 {
2291 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2292 }
2293 MenuInitRosMenuItemInfo(&Item);
2294 if (NO_SELECTED_ITEM == Index || ! MenuGetRosMenuItemInfo(PtMenu, Index, &Item))
2295 {
2296 MenuCleanupRosMenuItemInfo(&Item);
2297 return FALSE;
2298 }
2299
2300 if (!(Item.fType & MF_SEPARATOR) &&
2301 !(Item.fState & (MFS_DISABLED | MFS_GRAYED)) )
2302 {
2303 if (MenuInfo.FocusedItem != Index)
2304 {
2305 MenuSwitchTracking(Mt, &MenuInfo, Index);
2306 }
2307
2308 /* If the popup menu is not already "popped" */
2309 if (0 == (Item.fState & MF_MOUSESELECT))
2310 {
2311 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2312 }
2313 }
2314
2315 MenuCleanupRosMenuItemInfo(&Item);
2316
2317 return TRUE;
2318 }
2319
2320 /* else the click was on the menu bar, finish the tracking */
2321
2322 return FALSE;
2323 }
2324
2325 /***********************************************************************
2326 * MenuButtonUp
2327 *
2328 * Return the value of MenuExecFocusedItem if
2329 * the selected item was not a popup. Else open the popup.
2330 * A -1 return value indicates that we go on with menu tracking.
2331 *
2332 */
2333 static INT FASTCALL
2334 MenuButtonUp(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2335 {
2336 UINT Id;
2337 ROSMENUINFO MenuInfo;
2338 ROSMENUITEMINFO ItemInfo;
2339
2340 DPRINT("%p hmenu=%x\n", Mt, PtMenu);
2341
2342 if (NULL != PtMenu)
2343 {
2344 Id = 0;
2345 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2346 {
2347 return -1;
2348 }
2349
2350 if (! IS_SYSTEM_MENU(&MenuInfo))
2351 {
2352 Id = NtUserMenuItemFromPoint(Mt->OwnerWnd, MenuInfo.Self, Mt->Pt.x, Mt->Pt.y);
2353 }
2354 MenuInitRosMenuItemInfo(&ItemInfo);
2355 if (0 <= Id && MenuGetRosMenuItemInfo(MenuInfo.Self, Id, &ItemInfo) &&
2356 MenuInfo.FocusedItem == Id)
2357 {
2358 if (0 == (ItemInfo.fType & MF_POPUP))
2359 {
2360 MenuCleanupRosMenuItemInfo(&ItemInfo);
2361 return MenuExecFocusedItem(Mt, &MenuInfo, Flags);
2362 }
2363 MenuCleanupRosMenuItemInfo(&ItemInfo);
2364
2365 /* If we are dealing with the top-level menu */
2366 /* and this is a click on an already "popped" item: */
2367 /* Stop the menu tracking and close the opened submenus */
2368 if (Mt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide)
2369 {
2370 MenuCleanupRosMenuItemInfo(&ItemInfo);
2371 return 0;
2372 }
2373 }
2374 MenuCleanupRosMenuItemInfo(&ItemInfo);
2375 MenuInfo.TimeToHide = TRUE;
2376 MenuSetRosMenuInfo(&MenuInfo);
2377 }
2378
2379 return -1;
2380 }
2381
2382 /***********************************************************************
2383 * MenuPtMenu
2384 *
2385 * Walks menu chain trying to find a menu pt maps to.
2386 */
2387 static HMENU FASTCALL
2388 MenuPtMenu(HMENU Menu, POINT Pt)
2389 {
2390 extern LRESULT DefWndNCHitTest(HWND hWnd, POINT Point);
2391 ROSMENUINFO MenuInfo;
2392 ROSMENUITEMINFO ItemInfo;
2393 HMENU Ret = NULL;
2394 INT Ht;
2395
2396 if (! MenuGetRosMenuInfo(&MenuInfo, Menu))
2397 {
2398 return NULL;
2399 }
2400
2401 /* try subpopup first (if any) */
2402 if (NO_SELECTED_ITEM != MenuInfo.FocusedItem)
2403 {
2404 MenuInitRosMenuItemInfo(&ItemInfo);
2405 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo) &&
2406 0 != (ItemInfo.fType & MF_POPUP) &&
2407 0 != (ItemInfo.fState & MF_MOUSESELECT))
2408 {
2409 Ret = MenuPtMenu(ItemInfo.hSubMenu, Pt);
2410 if (NULL != Ret)
2411 {
2412 MenuCleanupRosMenuItemInfo(&ItemInfo);
2413 return Ret;
2414 }
2415 }
2416 MenuCleanupRosMenuItemInfo(&ItemInfo);
2417 }
2418
2419 /* check the current window (avoiding WM_HITTEST) */
2420 Ht = DefWndNCHitTest(MenuInfo.Wnd, Pt);
2421 if (0 != (MenuInfo.Flags & MF_POPUP))
2422 {
2423 if (HTNOWHERE != Ht && HTERROR != Ht)
2424 {
2425 Ret = Menu;
2426 }
2427 }
2428 else if (HTSYSMENU == Ht)
2429 {
2430 Ret = NtUserGetSystemMenu(MenuInfo.Wnd, FALSE);
2431 }
2432 else if (HTMENU == Ht)
2433 {
2434 Ret = GetMenu(MenuInfo.Wnd);
2435 }
2436
2437 return Ret;
2438 }
2439
2440 /***********************************************************************
2441 * MenuMouseMove
2442 *
2443 * Return TRUE if we can go on with menu tracking.
2444 */
2445 static BOOL FASTCALL
2446 MenuMouseMove(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
2447 {
2448 UINT Index;
2449 ROSMENUINFO MenuInfo;
2450 ROSMENUITEMINFO ItemInfo;
2451
2452 if (NULL != PtMenu)
2453 {
2454 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
2455 {
2456 return TRUE;
2457 }
2458 if (IS_SYSTEM_MENU(&MenuInfo))
2459 {
2460 Index = 0;
2461 }
2462 else
2463 {
2464 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
2465 }
2466 }
2467 else
2468 {
2469 Index = NO_SELECTED_ITEM;
2470 }
2471
2472 if (NO_SELECTED_ITEM == Index)
2473 {
2474 if (Mt->CurrentMenu == MenuInfo.Self ||
2475 MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
2476 {
2477 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
2478 TRUE, Mt->TopMenu);
2479 }
2480 }
2481 else if (MenuInfo.FocusedItem != Index)
2482 {
2483 MenuInitRosMenuItemInfo(&ItemInfo);
2484 if (MenuGetRosMenuItemInfo(MenuInfo.Self, Index, &ItemInfo) &&
2485 !(ItemInfo.fType & MF_SEPARATOR) &&
2486 !(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED)) )
2487 {
2488 MenuSwitchTracking(Mt, &MenuInfo, Index);
2489 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
2490 }
2491 MenuCleanupRosMenuItemInfo(&ItemInfo);
2492 }
2493
2494 return TRUE;
2495 }
2496
2497 /******************************************************************************
2498 *
2499 * UINT MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo)
2500 */
2501 static UINT MenuGetStartOfNextColumn(PROSMENUINFO MenuInfo)
2502 {
2503 UINT i;
2504 PROSMENUITEMINFO MenuItems;
2505
2506 i = MenuInfo->FocusedItem;
2507 if (NO_SELECTED_ITEM == i)
2508 {
2509 return i;
2510 }
2511
2512 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0)
2513 {
2514 return NO_SELECTED_ITEM;
2515 }
2516
2517 for (i++ ; i < MenuInfo->MenuItemCount; i++)
2518 {
2519 if (0 != (MenuItems[i].fType & MF_MENUBARBREAK))
2520 {
2521 return i;
2522 }
2523 }
2524
2525 return NO_SELECTED_ITEM;
2526 }
2527
2528 /******************************************************************************
2529 *
2530 * UINT MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo)
2531 */
2532 static UINT FASTCALL
2533 MenuGetStartOfPrevColumn(PROSMENUINFO MenuInfo)
2534 {
2535 UINT i;
2536 PROSMENUITEMINFO MenuItems;
2537
2538 if (0 == MenuInfo->FocusedItem || NO_SELECTED_ITEM == MenuInfo->FocusedItem)
2539 {
2540 return NO_SELECTED_ITEM;
2541 }
2542
2543 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0)
2544 {
2545 return NO_SELECTED_ITEM;
2546 }
2547
2548 /* Find the start of the column */
2549
2550 for (i = MenuInfo->FocusedItem;
2551 0 != i && 0 == (MenuItems[i].fType & MF_MENUBARBREAK);
2552 --i)
2553 {
2554 ; /* empty */
2555 }
2556
2557 if (0 == i)
2558 {
2559 MenuCleanupAllRosMenuItemInfo(MenuItems);
2560 return NO_SELECTED_ITEM;
2561 }
2562
2563 for (--i; 0 != i; --i)
2564 {
2565 if (MenuItems[i].fType & MF_MENUBARBREAK)
2566 {
2567 break;
2568 }
2569 }
2570
2571 MenuCleanupAllRosMenuItemInfo(MenuItems);
2572 DPRINT("ret %d.\n", i );
2573
2574 return i;
2575 }
2576
2577 /***********************************************************************
2578 * MenuGetSubPopup
2579 *
2580 * Return the handle of the selected sub-popup menu (if any).
2581 */
2582 static HMENU FASTCALL
2583 MenuGetSubPopup(HMENU Menu)
2584 {
2585 ROSMENUINFO MenuInfo;
2586 ROSMENUITEMINFO ItemInfo;
2587
2588 if (! MenuGetRosMenuInfo(&MenuInfo, Menu)
2589 || NO_SELECTED_ITEM == MenuInfo.FocusedItem)
2590 {
2591 return NULL;
2592 }
2593
2594 MenuInitRosMenuItemInfo(&ItemInfo);
2595 if (! MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.FocusedItem, &ItemInfo))
2596 {
2597 MenuCleanupRosMenuItemInfo(&ItemInfo);
2598 return NULL;
2599 }
2600 if (0 != (ItemInfo.fType & MF_POPUP) && 0 != (ItemInfo.fState & MF_MOUSESELECT))
2601 {
2602 MenuCleanupRosMenuItemInfo(&ItemInfo);
2603 return ItemInfo.hSubMenu;
2604 }
2605
2606 MenuCleanupRosMenuItemInfo(&ItemInfo);
2607 return NULL;
2608 }
2609
2610 /***********************************************************************
2611 * MenuDoNextMenu
2612 *
2613 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2614 */
2615 static LRESULT FASTCALL
2616 MenuDoNextMenu(MTRACKER* Mt, UINT Vk)
2617 {
2618 ROSMENUINFO TopMenuInfo;
2619 ROSMENUINFO MenuInfo;
2620
2621 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2622 {
2623 return (LRESULT) FALSE;
2624 }
2625
2626 if ((VK_LEFT == Vk && 0 == TopMenuInfo.FocusedItem)
2627 || (VK_RIGHT == Vk && TopMenuInfo.FocusedItem == TopMenuInfo.MenuItemCount - 1))
2628 {
2629 MDINEXTMENU NextMenu;
2630 HMENU NewMenu;
2631 HWND NewWnd;
2632 UINT Id = 0;
2633
2634 NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(Mt->TopMenu, 0) : Mt->TopMenu;
2635 NextMenu.hmenuNext = NULL;
2636 NextMenu.hwndNext = NULL;
2637 SendMessageW(Mt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
2638
2639 DPRINT("%p [%p] -> %p [%p]\n",
2640 Mt->CurrentMenu, Mt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
2641
2642 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
2643 {
2644 DWORD Style = GetWindowLongW(Mt->OwnerWnd, GWL_STYLE);
2645 NewWnd = Mt->OwnerWnd;
2646 if (IS_SYSTEM_MENU(&TopMenuInfo))
2647 {
2648 /* switch to the menu bar */
2649
2650 if (0 != (Style & WS_CHILD)
2651 || NULL == (NewMenu = GetMenu(NewWnd)))
2652 {
2653 return FALSE;
2654 }
2655
2656 if (VK_LEFT == Vk)
2657 {
2658 if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu))
2659 {
2660 return FALSE;
2661 }
2662 Id = MenuInfo.MenuItemCount - 1;
2663 }
2664 }
2665 else if (0 != (Style & WS_SYSMENU))
2666 {
2667 /* switch to the system menu */
2668 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2669 }
2670 else
2671 {
2672 return FALSE;
2673 }
2674 }
2675 else /* application returned a new menu to switch to */
2676 {
2677 NewMenu = NextMenu.hmenuNext;
2678 NewWnd = NextMenu.hwndNext;
2679
2680 if (IsMenu(NewMenu) && IsWindow(NewWnd))
2681 {
2682 DWORD Style = GetWindowLongW(NewWnd, GWL_STYLE);
2683
2684 if (0 != (Style & WS_SYSMENU)
2685 && GetSystemMenu(NewWnd, FALSE) == NewMenu)
2686 {
2687 /* get the real system menu */
2688 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
2689 }
2690 else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu)
2691 {
2692 /* FIXME: Not sure what to do here;
2693 * perhaps try to track NewMenu as a popup? */
2694
2695 DPRINT(" -- got confused.\n");
2696 return FALSE;
2697 }
2698 }
2699 else
2700 {
2701 return FALSE;
2702 }
2703 }
2704
2705 if (NewMenu != Mt->TopMenu)
2706 {
2707 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM,
2708 FALSE, 0 );
2709 if (Mt->CurrentMenu != Mt->TopMenu)
2710 {
2711 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE);
2712 }
2713 }
2714
2715 if (NewWnd != Mt->OwnerWnd)
2716 {
2717 Mt->OwnerWnd = NewWnd;
2718 SetCapture(Mt->OwnerWnd);
2719 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, Mt->OwnerWnd);
2720 }
2721
2722 Mt->TopMenu = Mt->CurrentMenu = NewMenu; /* all subpopups are hidden */
2723 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2724 {
2725 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0);
2726 }
2727
2728 return TRUE;
2729 }
2730
2731 return FALSE;
2732 }
2733
2734 /***********************************************************************
2735 * MenuSuspendPopup
2736 *
2737 * The idea is not to show the popup if the next input message is
2738 * going to hide it anyway.
2739 */
2740 static BOOL FASTCALL
2741 MenuSuspendPopup(MTRACKER* Mt, UINT Message)
2742 {
2743 MSG Msg;
2744
2745 Msg.hwnd = Mt->OwnerWnd;
2746
2747 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2748 Mt->TrackFlags |= TF_SKIPREMOVE;
2749
2750 switch (Message)
2751 {
2752 case WM_KEYDOWN:
2753 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2754 if (WM_KEYUP == Msg.message || WM_PAINT == Msg.message)
2755 {
2756 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2757 PeekMessageW(&Msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2758 if (WM_KEYDOWN == Msg.message
2759 && (VK_LEFT == Msg.wParam || VK_RIGHT == Msg.wParam))
2760 {
2761 Mt->TrackFlags |= TF_SUSPENDPOPUP;
2762 return TRUE;
2763 }
2764 }
2765 break;
2766 }
2767
2768 /* failures go through this */
2769 Mt->TrackFlags &= ~TF_SUSPENDPOPUP;
2770
2771 return FALSE;
2772 }
2773
2774 /***********************************************************************
2775 * MenuKeyEscape
2776 *
2777 * Handle a VK_ESCAPE key event in a menu.
2778 */
2779 static BOOL FASTCALL
2780 MenuKeyEscape(MTRACKER *Mt, UINT Flags)
2781 {
2782 BOOL EndMenu = TRUE;
2783 ROSMENUINFO MenuInfo;
2784 HMENU MenuTmp, MenuPrev;
2785
2786 if (Mt->CurrentMenu != Mt->TopMenu)
2787 {
2788 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)
2789 && 0 != (MenuInfo.Flags & MF_POPUP))
2790 {
2791 MenuPrev = MenuTmp = Mt->TopMenu;
2792
2793 /* close topmost popup */
2794 while (MenuTmp != Mt->CurrentMenu)
2795 {
2796 MenuPrev = MenuTmp;
2797 MenuTmp = MenuGetSubPopup(MenuPrev);
2798 }
2799
2800 if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev))
2801 {
2802 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, TRUE);
2803 }
2804 Mt->CurrentMenu = MenuPrev;
2805 EndMenu = FALSE;
2806 }
2807 }
2808
2809 return EndMenu;
2810 }
2811
2812 /***********************************************************************
2813 * MenuKeyLeft
2814 *
2815 * Handle a VK_LEFT key event in a menu.
2816 */
2817 static void FASTCALL
2818 MenuKeyLeft(MTRACKER* Mt, UINT Flags)
2819 {
2820 ROSMENUINFO MenuInfo;
2821 ROSMENUINFO TopMenuInfo;
2822 ROSMENUINFO PrevMenuInfo;
2823 HMENU MenuTmp, MenuPrev;
2824 UINT PrevCol;
2825
2826 MenuPrev = MenuTmp = Mt->TopMenu;
2827
2828 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
2829 {
2830 return;
2831 }
2832
2833 /* Try to move 1 column left (if possible) */
2834 if (NO_SELECTED_ITEM != (PrevCol = MenuGetStartOfPrevColumn(&MenuInfo)))
2835 {
2836 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
2837 {
2838 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0);
2839 }
2840 return;
2841 }
2842
2843 /* close topmost popup */
2844 while (MenuTmp != Mt->CurrentMenu)
2845 {
2846 MenuPrev = MenuTmp;
2847 MenuTmp = MenuGetSubPopup(MenuPrev);
2848 }
2849
2850 if (! MenuGetRosMenuInfo(&PrevMenuInfo, MenuPrev))
2851 {
2852 return;
2853 }
2854 MenuHideSubPopups(Mt->OwnerWnd, &PrevMenuInfo, TRUE);
2855 Mt->CurrentMenu = MenuPrev;
2856
2857 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2858 {
2859 return;
2860 }
2861 if ((MenuPrev == Mt->TopMenu) && 0 == (TopMenuInfo.Flags & MF_POPUP))
2862 {
2863 /* move menu bar selection if no more popups are left */
2864
2865 if (! MenuDoNextMenu(Mt, VK_LEFT))
2866 {
2867 MenuMoveSelection(Mt->OwnerWnd, &TopMenuInfo, ITEM_PREV);
2868 }
2869
2870 if (MenuPrev != MenuTmp || 0 != (Mt->TrackFlags & TF_SUSPENDPOPUP))
2871 {
2872 /* A sublevel menu was displayed - display the next one
2873 * unless there is another displacement coming up */
2874
2875 if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
2876 && MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
2877 {
2878 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &TopMenuInfo,
2879 TRUE, Flags);
2880 }
2881 }
2882 }
2883 }
2884
2885 /***********************************************************************
2886 * MenuKeyRight
2887 *
2888 * Handle a VK_RIGHT key event in a menu.
2889 */
2890 static void FASTCALL
2891 MenuKeyRight(MTRACKER *Mt, UINT Flags)
2892 {
2893 HMENU MenuTmp;
2894 ROSMENUINFO MenuInfo;
2895 ROSMENUINFO CurrentMenuInfo;
2896 UINT NextCol;
2897
2898 DPRINT("MenuKeyRight called, cur %p, top %p.\n",
2899 Mt->CurrentMenu, Mt->TopMenu);
2900
2901 if (! MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu))
2902 {
2903 return;
2904 }
2905 if (0 != (MenuInfo.Flags & MF_POPUP) || (Mt->CurrentMenu != Mt->TopMenu))
2906 {
2907 /* If already displaying a popup, try to display sub-popup */
2908
2909 MenuTmp = Mt->CurrentMenu;
2910 if (MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
2911 {
2912 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &CurrentMenuInfo, TRUE, Flags);
2913 }
2914
2915 /* if subpopup was displayed then we are done */
2916 if (MenuTmp != Mt->CurrentMenu)
2917 {
2918 return;
2919 }
2920 }
2921
2922 if (! MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
2923 {
2924 return;
2925 }
2926
2927 /* Check to see if there's another column */
2928 if (NO_SELECTED_ITEM != (NextCol = MenuGetStartOfNextColumn(&CurrentMenuInfo)))
2929 {
2930 DPRINT("Going to %d.\n", NextCol);
2931 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
2932 {
2933 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NextCol, TRUE, 0);
2934 }
2935 return;
2936 }
2937
2938 if (0 == (MenuInfo.Flags & MF_POPUP)) /* menu bar tracking */
2939 {
2940 if (Mt->CurrentMenu != Mt->TopMenu)
2941 {
2942 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, FALSE );
2943 MenuTmp = Mt->CurrentMenu = Mt->TopMenu;
2944 }
2945 else
2946 {
2947 MenuTmp = NULL;
2948 }
2949
2950 /* try to move to the next item */
2951 if (! MenuDoNextMenu(Mt, VK_RIGHT))
2952 {
2953 MenuMoveSelection(Mt->OwnerWnd, &MenuInfo, ITEM_NEXT);
2954 }
2955
2956 if (NULL != MenuTmp || 0 != (Mt->TrackFlags & TF_SUSPENDPOPUP))
2957 {
2958 if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
2959 && MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu))
2960 {
2961 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo,
2962 TRUE, Flags);
2963 }
2964 }
2965 }
2966 }
2967
2968 /***********************************************************************
2969 * MenuFindItemByKey
2970 *
2971 * Find the menu item selected by a key press.
2972 * Return item id, -1 if none, -2 if we should close the menu.
2973 */
2974 static UINT FASTCALL
2975 MenuFindItemByKey(HWND WndOwner, PROSMENUINFO MenuInfo,
2976 WCHAR Key, BOOL ForceMenuChar)
2977 {
2978 ROSMENUINFO SysMenuInfo;
2979 PROSMENUITEMINFO Items, ItemInfo;
2980 LRESULT MenuChar;
2981 UINT i;
2982
2983 DPRINT("\tlooking for '%c' (0x%02x) in [%p]\n", (char) Key, Key, MenuInfo);
2984
2985 if (NULL == MenuInfo || ! IsMenu(MenuInfo->Self))
2986 {
2987 if (MenuGetRosMenuInfo(&SysMenuInfo, GetSystemMenu(WndOwner, FALSE)))
2988 {
2989 MenuInfo = &SysMenuInfo;
2990 }
2991 else
2992 {
2993 MenuInfo = NULL;
2994 }
2995 }
2996
2997 if (NULL != MenuInfo)
2998 {
2999 if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &Items) <= 0)
3000 {
3001 return -1;
3002 }
3003 if (! ForceMenuChar)
3004 {
3005 Key = towupper(Key);
3006 ItemInfo = Items;
3007 for (i = 0; i < MenuInfo->MenuItemCount; i++, ItemInfo++)
3008 {
3009 if (IS_STRING_ITEM(ItemInfo->fType) && NULL != ItemInfo->dwTypeData)
3010 {
3011 WCHAR *p = (WCHAR *) ItemInfo->dwTypeData - 2;
3012 do
3013 {
3014 p = wcschr(p + 2, '&');
3015 }
3016 while (NULL != p && L'&' == p[1]);
3017 if (NULL != p && (towupper(p[1]) == Key))
3018 {
3019 return i;
3020 }
3021 }
3022 }
3023 }
3024
3025 MenuChar = SendMessageW(WndOwner, WM_MENUCHAR,
3026 MAKEWPARAM(Key, MenuInfo->Flags), (LPARAM) MenuInfo->Self);
3027 if (2 == HIWORD(MenuChar))
3028 {
3029 return LOWORD(MenuChar);
3030 }
3031 if (1 == HIWORD(MenuChar))
3032 {
3033 return (UINT) (-2);
3034 }
3035 }
3036
3037 return (UINT)(-1);
3038 }
3039
3040 /***********************************************************************
3041 * MenuTrackMenu
3042 *
3043 * Menu tracking code.
3044 */
3045 static INT FASTCALL
3046 MenuTrackMenu(HMENU Menu, UINT Flags, INT x, INT y,
3047 HWND Wnd, const RECT *Rect )
3048 {
3049 MSG Msg;
3050 ROSMENUINFO MenuInfo;
3051 ROSMENUITEMINFO ItemInfo;
3052 BOOL fRemove;
3053 INT ExecutedMenuId = -1;
3054 MTRACKER Mt;
3055 BOOL EnterIdleSent = FALSE;
3056
3057 Mt.TrackFlags = 0;
3058 Mt.CurrentMenu = Menu;
3059 Mt.TopMenu = Menu;
3060 Mt.OwnerWnd = Wnd;
3061 Mt.Pt.x = x;
3062 Mt.Pt.y = y;
3063
3064 DPRINT("Menu=%x Flags=0x%08x (%d,%d) Wnd=%x (%ld,%ld)-(%ld,%ld)\n",
3065 Menu, Flags, x, y, Wnd, Rect ? Rect->left : 0, Rect ? Rect->top : 0,
3066 Rect ? Rect->right : 0, Rect ? Rect->bottom : 0);
3067
3068 fEndMenu = FALSE;
3069 if (! MenuGetRosMenuInfo(&MenuInfo, Menu))
3070 {
3071 return FALSE;
3072 }
3073
3074 if (0 != (Flags & TPM_BUTTONDOWN))
3075 {
3076 /* Get the result in order to start the tracking or not */
3077 fRemove = MenuButtonDown(&Mt, Menu, Flags);
3078 fEndMenu = ! fRemove;
3079 }
3080
3081 SetCapture(Mt.OwnerWnd);
3082 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, Mt.OwnerWnd);
3083
3084 while (! fEndMenu)
3085 {
3086 /* we have to keep the message in the queue until it's
3087 * clear that menu loop is not over yet. */
3088
3089 for (;;)
3090 {
3091 if (PeekMessageW(&Msg, 0, 0, 0, PM_NOREMOVE))
3092 {
3093 if (! CallMsgFilterW(&Msg, MSGF_MENU))
3094 {
3095 break;
3096 }
3097 /* remove the message from the queue */
3098 PeekMessageW(&Msg, 0, Msg.message, Msg.message, PM_REMOVE );
3099 }
3100 else
3101 {
3102 if (! EnterIdleSent)
3103 {
3104 HWND Win = (0 != (Flags & TPM_ENTERIDLEEX)
3105 && 0 != (MenuInfo.Flags & MF_POPUP)) ? MenuInfo.Wnd : NULL;
3106 EnterIdleSent = TRUE;
3107 SendMessageW(Mt.OwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM) Win);
3108 }
3109 WaitMessage();
3110 }
3111 }
3112
3113 /* check if EndMenu() tried to cancel us, by posting this message */
3114 if (WM_CANCELMODE == Msg.message)
3115 {
3116 /* we are now out of the loop */
3117 fEndMenu = TRUE;
3118
3119 /* remove the message from the queue */
3120 PeekMessageW(&Msg, 0, Msg.message, Msg.message, PM_REMOVE);
3121
3122 /* break out of internal loop, ala ESCAPE */
3123 break;
3124 }
3125
3126 TranslateMessage(&Msg);
3127 Mt.Pt = Msg.pt;
3128
3129 if (Msg.hwnd == MenuInfo.Wnd || WM_TIMER != Msg.message)
3130 {
3131 EnterIdleSent = FALSE;
3132 }
3133
3134 fRemove = FALSE;
3135 if (WM_MOUSEFIRST <= Msg.message && Msg.message <= WM_MOUSELAST)
3136 {
3137 /*
3138 * Use the mouse coordinates in lParam instead of those in the MSG
3139 * struct to properly handle synthetic messages. They are already
3140 * in screen coordinates.
3141 */
3142 Mt.Pt.x = (short) LOWORD(Msg.lParam);
3143 Mt.Pt.y = (short) HIWORD(Msg.lParam);
3144
3145 /* Find a menu for this mouse event */
3146 Menu = MenuPtMenu(Mt.TopMenu, Mt.Pt);
3147
3148 switch(Msg.message)
3149 {
3150 /* no WM_NC... messages in captured state */
3151
3152 case WM_RBUTTONDBLCLK:
3153 case WM_RBUTTONDOWN:
3154 if (0 == (Flags & TPM_RIGHTBUTTON))
3155 {
3156 break;
3157 }
3158 /* fall through */
3159 case WM_LBUTTONDBLCLK:
3160 case WM_LBUTTONDOWN:
3161 /* If the message belongs to the menu, removes it from the queue */
3162 /* Else, end menu tracking */
3163 fRemove = MenuButtonDown(&Mt, Menu, Flags);
3164 fEndMenu = ! fRemove;
3165 break;
3166
3167 case WM_RBUTTONUP:
3168 if (0 == (Flags & TPM_RIGHTBUTTON))
3169 {
3170 break;
3171 }
3172 /* fall through */
3173 case WM_LBUTTONUP:
3174 /* Check if a menu was selected by the mouse */
3175 if (NULL != Menu)
3176 {
3177 ExecutedMenuId = MenuButtonUp(&Mt, Menu, Flags);
3178
3179 /* End the loop if ExecutedMenuId is an item ID */
3180 /* or if the job was done (ExecutedMenuId = 0). */
3181 fEndMenu = fRemove = (-1 != ExecutedMenuId);
3182 }
3183 else
3184 {
3185 /* No menu was selected by the mouse */
3186 /* if the function was called by TrackPopupMenu, continue
3187 with the menu tracking. If not, stop it */
3188 fEndMenu = (0 != (Flags & TPM_POPUPMENU) ? FALSE : TRUE);
3189 }
3190 break;
3191
3192 case WM_MOUSEMOVE:
3193 if (Menu)
3194 {
3195 fEndMenu |= ! MenuMouseMove(&Mt, Menu, Flags);
3196 }
3197 break;
3198
3199 } /* switch(Msg.message) - mouse */
3200 }
3201 else if (WM_KEYFIRST <= Msg.message && Msg.message <= WM_KEYLAST)
3202 {
3203 fRemove = TRUE; /* Keyboard messages are always removed */
3204 switch(Msg.message)
3205 {
3206 case WM_KEYDOWN:
3207 switch(Msg.wParam)
3208 {
3209 case VK_HOME:
3210 case VK_END:
3211 if (MenuGetRosMenuInfo(&MenuInfo, Mt.CurrentMenu))
3212 {
3213 MenuSelectItem(Mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
3214 FALSE, 0 );
3215 }
3216 /* fall through */
3217
3218 case VK_UP:
3219 if (MenuGetRosMenuInfo(&MenuInfo, Mt.CurrentMenu))
3220 {
3221 MenuMoveSelection(Mt.OwnerWnd, &MenuInfo,
3222 VK_HOME == Msg.wParam ? ITEM_NEXT : ITEM_PREV);
3223 }
3224 break;
3225
3226 case VK_DOWN: /* If on menu bar, pull-down the menu */
3227 if (MenuGetRosMenuInfo(&MenuInfo, Mt.CurrentMenu))
3228 {
3229 if (0 == (MenuInfo.Flags & MF_POPUP))
3230 {
3231 if (MenuGetRosMenuInfo(&MenuInfo, Mt.TopMenu))
3232 {
3233 Mt.CurrentMenu = MenuShowSubPopup(Mt.OwnerWnd, &MenuInfo,
3234 TRUE, Flags);
3235 }
3236 }
3237 else /* otherwise try to move selection */
3238 {
3239 MenuMoveSelection(Mt.OwnerWnd, &MenuInfo, ITEM_NEXT);
3240 }
3241 }
3242 break;
3243
3244 case VK_LEFT:
3245 MenuKeyLeft(&Mt, Flags);
3246 break;
3247
3248 case VK_RIGHT:
3249 MenuKeyRight(&Mt, Flags);