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