65d174989c61fdef7a154b68ba8b21aa7c4ddc60
[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);
3250 break;
3251
3252 case VK_ESCAPE:
3253 fEndMenu = MenuKeyEscape(&Mt, Flags);
3254 break;
3255
3256 case VK_F1:
3257 {
3258 HELPINFO hi;
3259 hi.cbSize = sizeof(HELPINFO);
3260 hi.iContextType = HELPINFO_MENUITEM;
3261 if (MenuGetRosMenuInfo(&MenuInfo, Mt.CurrentMenu))
3262 {
3263 if (NO_SELECTED_ITEM == MenuInfo.FocusedItem)
3264 {
3265 hi.iCtrlId = 0;
3266 }
3267 else
3268 {
3269 MenuInitRosMenuItemInfo(&ItemInfo);
3270 if (MenuGetRosMenuItemInfo(MenuInfo.Self,
3271 MenuInfo.FocusedItem,
3272 &ItemInfo))
3273 {
3274 hi.iCtrlId = ItemInfo.wID;
3275 }
3276 else
3277 {
3278 hi.iCtrlId = 0;
3279 }
3280 MenuCleanupRosMenuItemInfo(&ItemInfo);
3281 }
3282 }
3283 hi.hItemHandle = Menu;
3284 hi.dwContextId = MenuInfo.dwContextHelpID;
3285 hi.MousePos = Msg.pt;
3286 SendMessageW(Wnd, WM_HELP, 0, (LPARAM) &hi);
3287 break;
3288 }
3289
3290 default:
3291 break;
3292 }
3293 break; /* WM_KEYDOWN */
3294
3295 case WM_SYSKEYDOWN:
3296 switch (Msg.wParam)
3297 {
3298 DbgPrint("Menu.c WM_SYSKEYDOWN wPram %d\n",Msg.wParam);
3299 case VK_MENU:
3300 fEndMenu = TRUE;
3301 break;
3302 case VK_LMENU:
3303 fEndMenu = TRUE;
3304 break;
3305 }
3306 break; /* WM_SYSKEYDOWN */
3307
3308 case WM_CHAR:
3309 {
3310 UINT Pos;
3311
3312 if (! MenuGetRosMenuInfo(&MenuInfo, Mt.CurrentMenu))
3313 {
3314 break;
3315 }
3316 if (L'\r' == Msg.wParam || L' ' == Msg.wParam)
3317 {
3318 ExecutedMenuId = MenuExecFocusedItem(&Mt, &MenuInfo, Flags);
3319 fEndMenu = (ExecutedMenuId != -1);
3320 break;
3321 }
3322
3323 /* Hack to avoid control chars. */
3324 /* We will find a better way real soon... */
3325 if (Msg.wParam < 32)
3326 {
3327 break;
3328 }
3329
3330 Pos = MenuFindItemByKey(Mt.OwnerWnd, &MenuInfo,
3331 LOWORD(Msg.wParam), FALSE);
3332 if ((UINT) -2 == Pos)
3333 {
3334 fEndMenu = TRUE;
3335 }
3336 else if ((UINT) -1 == Pos)
3337 {
3338 MessageBeep(0);
3339 }
3340 else
3341 {
3342 MenuSelectItem(Mt.OwnerWnd, &MenuInfo, Pos, TRUE, 0);
3343 ExecutedMenuId = MenuExecFocusedItem(&Mt, &MenuInfo, Flags);
3344 fEndMenu = (-1 != ExecutedMenuId);
3345 }
3346 }
3347 break;
3348 } /* switch(msg.message) - kbd */
3349 }
3350 else
3351 {
3352 DispatchMessageW(&Msg);
3353 }
3354
3355 if (! fEndMenu)
3356 {
3357 fRemove = TRUE;
3358 }
3359
3360 /* finally remove message from the queue */
3361
3362 if (fRemove && 0 == (Mt.TrackFlags & TF_SKIPREMOVE))
3363 {
3364 PeekMessageW(&Msg, 0, Msg.message, Msg.message, PM_REMOVE);
3365 }
3366 else
3367 {
3368 Mt.TrackFlags &= ~TF_SKIPREMOVE;
3369 }
3370 }
3371
3372 NtUserSetGUIThreadHandle(MSQ_STATE_MENUOWNER, NULL);
3373 SetCapture(NULL); /* release the capture */
3374
3375 /* If dropdown is still painted and the close box is clicked on
3376 then the menu will be destroyed as part of the DispatchMessage above.
3377 This will then invalidate the menu handle in Mt.hTopMenu. We should
3378 check for this first. */
3379 if (IsMenu(Mt.TopMenu))
3380 {
3381 if (IsWindow(Mt.OwnerWnd))
3382 {
3383 if (MenuGetRosMenuInfo(&MenuInfo, Mt.TopMenu))
3384 {
3385 MenuHideSubPopups(Mt.OwnerWnd, &MenuInfo, FALSE);
3386
3387 if (0 != (MenuInfo.Flags & MF_POPUP))
3388 {
3389 DestroyWindow(MenuInfo.Wnd);
3390 MenuInfo.Wnd = NULL;
3391 }
3392 MenuSelectItem(Mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, NULL);
3393 }
3394
3395 SendMessageW(Mt.OwnerWnd, WM_MENUSELECT, MAKELONG(0, 0xffff), 0);
3396 }
3397
3398 if (MenuGetRosMenuInfo(&MenuInfo, Mt.TopMenu))
3399 {
3400 /* Reset the variable for hiding menu */
3401 MenuInfo.TimeToHide = FALSE;
3402 MenuSetRosMenuInfo(&MenuInfo);
3403 }
3404 }
3405
3406 /* The return value is only used by TrackPopupMenu */
3407 return (-1 != ExecutedMenuId) ? ExecutedMenuId : 0;
3408 }
3409
3410 /***********************************************************************
3411 * MenuExitTracking
3412 */
3413 static BOOL FASTCALL
3414 MenuExitTracking(HWND Wnd)
3415 {
3416 DPRINT("hwnd=%p\n", Wnd);
3417
3418 SendMessageW(Wnd, WM_EXITMENULOOP, 0, 0);
3419 ShowCaret(0);
3420 return TRUE;
3421 }
3422
3423
3424 VOID
3425 MenuTrackMouseMenuBar(HWND Wnd, ULONG Ht, POINT Pt)
3426 {
3427 HMENU Menu = (HTSYSMENU == Ht) ? NtUserGetSystemMenu(Wnd, FALSE) : GetMenu(Wnd);
3428 UINT Flags = TPM_ENTERIDLEEX | TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3429
3430 DPRINT("wnd=%p ht=0x%04x (%ld,%ld)\n", Wnd, Ht, Pt.x, Pt.y);
3431
3432 if (IsMenu(Menu))
3433 {
3434 /* map point to parent client coordinates */
3435 HWND Parent = GetAncestor(Wnd, GA_PARENT );
3436 if (Parent != GetDesktopWindow())
3437 {
3438 ScreenToClient(Parent, &Pt);
3439 }
3440
3441 MenuInitTracking(Wnd, Menu, FALSE, Flags);
3442 MenuTrackMenu(Menu, Flags, Pt.x, Pt.y, Wnd, NULL);
3443 MenuExitTracking(Wnd);
3444 }
3445 }
3446
3447
3448 VOID
3449 MenuTrackKbdMenuBar(HWND hWnd, ULONG wParam, ULONG Key)
3450 {
3451 }
3452
3453 /* FUNCTIONS *****************************************************************/
3454
3455 /*static BOOL
3456 MenuIsStringItem(ULONG TypeData)
3457 {
3458 return(MF_STRING == MENU_ITEM_TYPE(ItemInfo->fType));
3459 }*/
3460
3461
3462 /*
3463 * @implemented
3464 */
3465 BOOL STDCALL
3466 AppendMenuA(HMENU hMenu,
3467 UINT uFlags,
3468 UINT_PTR uIDNewItem,
3469 LPCSTR lpNewItem)
3470 {
3471 return(InsertMenuA(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
3472 lpNewItem));
3473 }
3474
3475
3476 /*
3477 * @implemented
3478 */
3479 BOOL STDCALL
3480 AppendMenuW(HMENU hMenu,
3481 UINT uFlags,
3482 UINT_PTR uIDNewItem,
3483 LPCWSTR lpNewItem)
3484 {
3485 return(InsertMenuW(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem,
3486 lpNewItem));
3487 }
3488
3489
3490 /*
3491 * @implemented
3492 */
3493 DWORD STDCALL
3494 CheckMenuItem(HMENU hmenu,
3495 UINT uIDCheckItem,
3496 UINT uCheck)
3497 {
3498 return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck);
3499 }
3500
3501
3502 /*
3503 * @implemented
3504 */
3505 BOOL STDCALL
3506 CheckMenuRadioItem(HMENU hmenu,
3507 UINT idFirst,
3508 UINT idLast,
3509 UINT idCheck,
3510 UINT uFlags)
3511 {
3512 ROSMENUINFO mi;
3513 PROSMENUITEMINFO Items;
3514 int i;
3515 BOOL ret = FALSE;
3516
3517 mi.cbSize = sizeof(MENUINFO);
3518
3519 UNIMPLEMENTED;
3520
3521 if(idFirst > idLast) return ret;
3522
3523 if(!NtUserMenuInfo(hmenu, &mi, FALSE)) return ret;
3524
3525 if(MenuGetAllRosMenuItemInfo(mi.Self, &Items) <= 0) return ret;
3526
3527 for (i = 0 ; i < mi.MenuItemCount; i++)
3528 {
3529 if (0 != (Items[i].fType & MF_MENUBARBREAK)) break;
3530 if ( i >= idFirst && i <= idLast )
3531 {
3532 if ( i == idCheck)
3533 {
3534 Items[i].fType |= MFT_RADIOCHECK;
3535 Items[i].fState |= MFS_CHECKED;
3536 }
3537 else
3538 {
3539 Items[i].fType &= ~MFT_RADIOCHECK;
3540 Items[i].fState &= ~MFS_CHECKED;
3541 }
3542 if(!MenuSetRosMenuItemInfo(mi.Self, i ,&Items[i]))
3543 break;
3544 }
3545 if ( i == mi.MenuItemCount) ret = TRUE;
3546 }
3547 MenuCleanupRosMenuItemInfo(Items);
3548 return ret;
3549 }
3550
3551
3552 /*
3553 * @implemented
3554 */
3555 HMENU STDCALL
3556 CreateMenu(VOID)
3557 {
3558 MenuLoadBitmaps();
3559 return NtUserCreateMenu(FALSE);
3560 }
3561
3562
3563 /*
3564 * @implemented
3565 */
3566 HMENU STDCALL
3567 CreatePopupMenu(VOID)
3568 {
3569 MenuLoadBitmaps();
3570 return NtUserCreateMenu(TRUE);
3571 }
3572
3573
3574 /*
3575 * @implemented
3576 */
3577 BOOL STDCALL
3578 DeleteMenu(HMENU hMenu,
3579 UINT uPosition,
3580 UINT uFlags)
3581 {
3582 return NtUserDeleteMenu(hMenu, uPosition, uFlags);
3583 }
3584
3585
3586 /*
3587 * @implemented
3588 */
3589 BOOL STDCALL
3590 DestroyMenu(HMENU hMenu)
3591 {
3592 return NtUserDestroyMenu(hMenu);
3593 }
3594
3595
3596 /*
3597 * @implemented
3598 */
3599 BOOL STDCALL
3600 DrawMenuBar(HWND hWnd)
3601 {
3602 return (BOOL)NtUserCallHwndLock(hWnd, HWNDLOCK_ROUTINE_DRAWMENUBAR);
3603 }
3604
3605
3606 /*
3607 * @implemented
3608 */
3609 BOOL STDCALL
3610 EnableMenuItem(HMENU hMenu,
3611 UINT uIDEnableItem,
3612 UINT uEnable)
3613 {
3614 return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable);
3615 }
3616
3617 /*
3618 * @implemented
3619 */
3620 BOOL STDCALL
3621 EndMenu(VOID)
3622 {
3623 GUITHREADINFO guii;
3624 guii.cbSize = sizeof(GUITHREADINFO);
3625 if(GetGUIThreadInfo(GetCurrentThreadId(), &guii) && guii.hwndMenuOwner)
3626 {
3627 PostMessageW(guii.hwndMenuOwner, WM_CANCELMODE, 0, 0);
3628 }
3629 return TRUE;
3630 }
3631
3632
3633 /*
3634 * @implemented
3635 */
3636 HMENU STDCALL
3637 GetMenu(HWND hWnd)
3638 {
3639 return NtUserGetMenu(hWnd);
3640 }
3641
3642
3643 /*
3644 * @implemented
3645 */
3646 BOOL STDCALL
3647 GetMenuBarInfo(HWND hwnd,
3648 LONG idObject,
3649 LONG idItem,
3650 PMENUBARINFO pmbi)
3651 {
3652 return (BOOL)NtUserGetMenuBarInfo(hwnd, idObject, idItem, pmbi);
3653 }
3654
3655
3656 /*
3657 * @implemented
3658 */
3659 LONG STDCALL
3660 GetMenuCheckMarkDimensions(VOID)
3661 {
3662 return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK),
3663 GetSystemMetrics(SM_CYMENUCHECK)));
3664 }
3665
3666
3667 /*
3668 * @implemented
3669 */
3670 UINT STDCALL
3671 GetMenuDefaultItem(HMENU hMenu,
3672 UINT fByPos,
3673 UINT gmdiFlags)
3674 {
3675 return NtUserGetMenuDefaultItem(hMenu, fByPos, gmdiFlags);
3676 }
3677
3678
3679 /*
3680 * @implemented
3681 */
3682 BOOL STDCALL
3683 GetMenuInfo(HMENU hmenu,
3684 LPMENUINFO lpcmi)
3685 {
3686 ROSMENUINFO mi;
3687 BOOL res = FALSE;
3688
3689 if(!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
3690 return FALSE;
3691
3692 RtlZeroMemory(&mi, sizeof(MENUINFO));
3693 mi.cbSize = sizeof(MENUINFO);
3694 mi.fMask = lpcmi->fMask;
3695
3696 res = NtUserMenuInfo(hmenu, &mi, FALSE);
3697
3698 memcpy(lpcmi, &mi, sizeof(MENUINFO));
3699 return res;
3700 }
3701
3702
3703 /*
3704 * @implemented
3705 */
3706 int STDCALL
3707 GetMenuItemCount(HMENU Menu)
3708 {
3709 ROSMENUINFO MenuInfo;
3710
3711 return MenuGetRosMenuInfo(&MenuInfo, Menu) ? MenuInfo.MenuItemCount : 0;
3712 }
3713
3714
3715 /*
3716 * @implemented
3717 */
3718 UINT STDCALL
3719 GetMenuItemID(HMENU hMenu,
3720 int nPos)
3721 {
3722 ROSMENUITEMINFO mii;
3723
3724 mii.cbSize = sizeof(MENUITEMINFOW);
3725 mii.fMask = MIIM_ID | MIIM_SUBMENU;
3726
3727 if (! NtUserMenuItemInfo(hMenu, nPos, MF_BYPOSITION, &mii, FALSE))
3728 {
3729 return -1;
3730 }
3731
3732 if (NULL != mii.hSubMenu)
3733 {
3734 return -1;
3735 }
3736 if (0 == mii.wID)
3737 {
3738 return -1;
3739 }
3740
3741 return mii.wID;
3742 }
3743
3744
3745 /*
3746 * @implemented
3747 */
3748 BOOL STDCALL
3749 GetMenuItemInfoA(
3750 HMENU Menu,
3751 UINT Item,
3752 BOOL ByPosition,
3753 LPMENUITEMINFOA mii)
3754 {
3755 LPSTR AnsiBuffer;
3756 MENUITEMINFOW miiW;
3757
3758 if (mii->cbSize != sizeof(MENUITEMINFOA) &&
3759 mii->cbSize != sizeof(MENUITEMINFOA) - sizeof(HBITMAP))
3760 {
3761 SetLastError(ERROR_INVALID_PARAMETER);
3762 return FALSE;
3763 }
3764
3765 if ((mii->fMask & (MIIM_STRING | MIIM_TYPE)) == 0)
3766 {
3767 /* No text requested, just pass on */
3768 return NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO) mii, FALSE);
3769 }
3770
3771 RtlCopyMemory(&miiW, mii, mii->cbSize);
3772 AnsiBuffer = mii->dwTypeData;
3773
3774 if (AnsiBuffer != NULL)
3775 {
3776 miiW.dwTypeData = RtlAllocateHeap(GetProcessHeap(), 0,
3777 miiW.cch * sizeof(WCHAR));
3778 if (miiW.dwTypeData == NULL)
3779 return FALSE;
3780 }
3781
3782 if (!NtUserMenuItemInfo(Menu, Item, ByPosition, (PROSMENUITEMINFO)&miiW, FALSE))
3783 {
3784 HeapFree(GetProcessHeap(), 0, miiW.dwTypeData);
3785 return FALSE;
3786 }
3787
3788 if (AnsiBuffer != NULL)
3789 {
3790 if (IS_STRING_ITEM(miiW.fType))
3791 {
3792 WideCharToMultiByte(CP_ACP, 0, miiW.dwTypeData, miiW.cch, AnsiBuffer,
3793 mii->cch, NULL, NULL);
3794 }
3795 RtlFreeHeap(GetProcessHeap(), 0, miiW.dwTypeData);
3796 }
3797
3798 RtlCopyMemory(mii, &miiW, miiW.cbSize);
3799 if (AnsiBuffer)
3800 {
3801 mii->dwTypeData = AnsiBuffer;
3802 mii->cch = strlen(AnsiBuffer);
3803 }
3804 return TRUE;
3805 }