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