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