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