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