Implemented NtUser-GetMenuItemRect.
[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 mii->cch = strlen(AnsiBuffer);
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 * @implemented
3796 */
3797 BOOL STDCALL
3798 GetMenuItemRect(HWND hWnd,
3799 HMENU hMenu,
3800 UINT uItem,
3801 LPRECT lprcItem)
3802 {
3803 return NtUserGetMenuItemRect( hWnd, hMenu, uItem, lprcItem);
3804 }
3805
3806
3807 /*
3808 * @implemented
3809 */
3810 UINT
3811 STDCALL
3812 GetMenuState(
3813 HMENU hMenu,
3814 UINT uId,
3815 UINT uFlags)
3816 {
3817 ROSMENUINFO MenuInfo;
3818 ROSMENUITEMINFO mii;
3819
3820 mii.cbSize = sizeof(MENUITEMINFOW);
3821 mii.fMask = MIIM_STATE | MIIM_TYPE | MIIM_SUBMENU;
3822 mii.dwTypeData = NULL;
3823
3824 SetLastError(0);
3825 if(NtUserMenuItemInfo(hMenu, uId, uFlags, &mii, FALSE))
3826 {
3827 UINT nSubItems = 0;
3828 if(mii.hSubMenu)
3829 {
3830 if (! MenuGetRosMenuInfo(&MenuInfo, mii.hSubMenu))
3831 {
3832 return (UINT) -1;
3833 }
3834 nSubItems = MenuInfo.MenuItemCount;
3835
3836 /* FIXME - ported from wine, does that work (0xff)? */
3837 if(GetLastError() != ERROR_INVALID_MENU_HANDLE)
3838 return (nSubItems << 8) | ((mii.fState | mii.fType) & 0xff);
3839
3840 return (UINT)-1; /* Invalid submenu */
3841 }
3842
3843 /* FIXME - ported from wine, does that work? */
3844 return (mii.fType | mii.fState);
3845 }
3846
3847 return (UINT)-1;
3848 }
3849
3850
3851 /*
3852 * @implemented
3853 */
3854 int
3855 STDCALL
3856 GetMenuStringA(
3857 HMENU hMenu,
3858 UINT uIDItem,
3859 LPSTR lpString,
3860 int nMaxCount,
3861 UINT uFlag)
3862 {
3863 MENUITEMINFOA mii;
3864 mii.dwTypeData = lpString;
3865 mii.fMask = MIIM_STRING;
3866 mii.fType = MF_STRING;
3867 mii.cbSize = sizeof(MENUITEMINFOA);
3868 mii.cch = nMaxCount;
3869
3870 if(!(GetMenuItemInfoA( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&mii)))
3871 return 0;
3872 else
3873 return mii.cch;
3874 }
3875
3876
3877 /*
3878 * @implemented
3879 */
3880 int
3881 STDCALL
3882 GetMenuStringW(
3883 HMENU hMenu,
3884 UINT uIDItem,
3885 LPWSTR lpString,
3886 int nMaxCount,
3887 UINT uFlag)
3888 {
3889 MENUITEMINFOW miiW;
3890 miiW.dwTypeData = lpString;
3891 miiW.fMask = MIIM_STRING;
3892 miiW.cbSize = sizeof(MENUITEMINFOW);
3893 miiW.cch = nMaxCount;
3894
3895 if(!(GetMenuItemInfoW( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&miiW)))
3896 return 0;
3897 else
3898 return miiW.cch;
3899 }
3900
3901
3902 /*
3903 * @implemented
3904 */
3905 HMENU
3906 STDCALL
3907 GetSubMenu(
3908 HMENU hMenu,
3909 int nPos)
3910 {
3911 ROSMENUITEMINFO mi;
3912
3913 mi.cbSize = sizeof(MENUITEMINFOW);
3914 mi.fMask = MIIM_SUBMENU;
3915
3916 if (NtUserMenuItemInfo(hMenu, (UINT)nPos, MF_BYPOSITION, &mi, FALSE))
3917 {
3918 return IsMenu(mi.hSubMenu) ? mi.hSubMenu : NULL;
3919 }
3920
3921 return NULL;
3922 }
3923
3924 /*
3925 * @implemented
3926 */
3927 HMENU
3928 STDCALL
3929 GetSystemMenu(
3930 HWND hWnd,
3931 BOOL bRevert)
3932 {
3933 HMENU TopMenu;
3934
3935 TopMenu = NtUserGetSystemMenu(hWnd, bRevert);
3936
3937 return NULL == TopMenu ? NULL : GetSubMenu(TopMenu, 0);
3938 }
3939
3940
3941 /*
3942 * @implemented
3943 */
3944 BOOL
3945 STDCALL
3946 HiliteMenuItem(
3947 HWND hwnd,
3948 HMENU hmenu,
3949 UINT uItemHilite,
3950 UINT uHilite)
3951 {
3952 return NtUserHiliteMenuItem(hwnd, hmenu, uItemHilite, uHilite);
3953 }
3954
3955
3956 /*
3957 * @implemented
3958 */
3959 BOOL
3960 STDCALL
3961 InsertMenuA(
3962 HMENU hMenu,
3963 UINT uPosition,
3964 UINT uFlags,
3965 UINT_PTR uIDNewItem,
3966 LPCSTR lpNewItem)
3967 {
3968 MENUITEMINFOA mii;
3969 mii.cbSize = sizeof(MENUITEMINFOA);
3970 mii.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE;
3971 mii.fType = 0;
3972 mii.fState = MFS_ENABLED;
3973
3974 if(uFlags & MF_BITMAP)
3975 {
3976 mii.fType |= MFT_BITMAP;
3977 mii.fMask |= MIIM_BITMAP;
3978 mii.hbmpItem = (HBITMAP) lpNewItem;
3979 }
3980 else if(uFlags & MF_OWNERDRAW)
3981 {
3982 mii.fType |= MFT_OWNERDRAW;
3983 mii.fMask |= MIIM_DATA;
3984 mii.dwItemData = (DWORD) lpNewItem;
3985 }
3986 else
3987 {
3988 mii.fMask |= MIIM_TYPE;
3989 mii.dwTypeData = (LPSTR)lpNewItem;
3990 mii.cch = (NULL == lpNewItem ? 0 : strlen(lpNewItem));
3991 }
3992
3993 if(uFlags & MF_RIGHTJUSTIFY)
3994 {
3995 mii.fType |= MFT_RIGHTJUSTIFY;
3996 }
3997 if(uFlags & MF_MENUBREAK)
3998 {
3999 mii.fType |= MFT_MENUBREAK;
4000 }
4001 if(uFlags & MF_MENUBARBREAK)
4002 {
4003 mii.fType |= MFT_MENUBARBREAK;
4004 }
4005 if(uFlags & MF_DISABLED)
4006 {
4007 mii.fState |= MFS_DISABLED;
4008 }
4009 if(uFlags & MF_GRAYED)
4010 {
4011 mii.fState |= MFS_GRAYED;
4012 }
4013
4014 if(uFlags & MF_POPUP)
4015 {
4016 mii.fType |= MF_POPUP;
4017 mii.fMask |= MIIM_SUBMENU;
4018 mii.hSubMenu = (HMENU)uIDNewItem;
4019 }
4020 else
4021 {
4022 mii.fMask |= MIIM_ID;
4023 mii.wID = (UINT)uIDNewItem;
4024 }
4025 return InsertMenuItemA(hMenu, uPosition, (BOOL)!(MF_BYPOSITION & uFlags), &mii);
4026 }
4027
4028
4029 /*
4030 * @implemented
4031 */
4032 BOOL
4033 STDCALL
4034 InsertMenuItemA(
4035 HMENU hMenu,
4036 UINT uItem,
4037 BOOL fByPosition,
4038 LPCMENUITEMINFOA lpmii)
4039 {
4040 MENUITEMINFOW mi;
4041 UNICODE_STRING MenuText;
4042 BOOL res = FALSE;
4043 BOOL CleanHeap = FALSE;
4044 NTSTATUS Status;
4045
4046 if((lpmii->cbSize == sizeof(MENUITEMINFOA)) ||
4047 (lpmii->cbSize == sizeof(MENUITEMINFOA) - sizeof(HBITMAP)))
4048 {
4049 RtlMoveMemory ( &mi, lpmii, lpmii->cbSize );
4050
4051 /* copy the text string */
4052 if((mi.fMask & (MIIM_TYPE | MIIM_STRING)) &&
4053 (MENU_ITEM_TYPE(mi.fType) == MF_STRING) && mi.dwTypeData)
4054 {
4055 Status = RtlCreateUnicodeStringFromAsciiz(&MenuText, (LPSTR)mi.dwTypeData);
4056 if (!NT_SUCCESS (Status))
4057 {
4058 SetLastError (RtlNtStatusToDosError(Status));
4059 return FALSE;
4060 }
4061 mi.dwTypeData = MenuText.Buffer;
4062 mi.cch = MenuText.Length / sizeof(WCHAR);
4063 CleanHeap = TRUE;
4064 }
4065
4066 res = NtUserInsertMenuItem(hMenu, uItem, fByPosition, &mi);
4067
4068 if ( CleanHeap ) RtlFreeUnicodeString ( &MenuText );
4069 }
4070 return res;
4071 }
4072
4073
4074 /*
4075 * @implemented
4076 */
4077 BOOL
4078 STDCALL
4079 InsertMenuItemW(
4080 HMENU hMenu,
4081 UINT uItem,
4082 BOOL fByPosition,
4083 LPCMENUITEMINFOW lpmii)
4084 {
4085 MENUITEMINFOW mi;
4086 UNICODE_STRING MenuText;
4087 BOOL res = FALSE;
4088 mi.hbmpItem = (HBITMAP)0;
4089
4090 /* while we could just pass 'lpmii' to win32k, we make a copy so that
4091 if a bad user passes bad data, we crash his process instead of the
4092 entire kernel */
4093
4094 if((lpmii->cbSize == sizeof(MENUITEMINFOW)) ||
4095 (lpmii->cbSize == sizeof(MENUITEMINFOW) - sizeof(HBITMAP)))
4096 {
4097 memcpy(&mi, lpmii, lpmii->cbSize);
4098
4099 /* copy the text string */
4100 if((mi.fMask & (MIIM_TYPE | MIIM_STRING)) &&
4101 (MENU_ITEM_TYPE(mi.fType) == MF_STRING) &&
4102 mi.dwTypeData != NULL)
4103 {
4104 RtlInitUnicodeString(&MenuText, (PWSTR)lpmii->dwTypeData);
4105 mi.dwTypeData = MenuText.Buffer;
4106 mi.cch = MenuText.Length / sizeof(WCHAR);
4107 };
4108
4109 res = NtUserInsertMenuItem(hMenu, uItem, fByPosition, &mi);
4110 }
4111 return res;
4112 }
4113
4114
4115 /*
4116 * @implemented
4117 */
4118 BOOL
4119 STDCALL
4120 InsertMenuW(
4121 HMENU hMenu,
4122 UINT uPosition,
4123 UINT uFlags,
4124 UINT_PTR uIDNewItem,
4125 LPCWSTR lpNewItem)
4126 {
4127 MENUITEMINFOW mii;
4128 mii.cbSize = sizeof(MENUITEMINFOW);
4129 mii.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE;
4130 mii.fType = 0;
4131 mii.fState = MFS_ENABLED;
4132
4133 if(uFlags & MF_BITMAP)
4134 {
4135 mii.fType |= MFT_BITMAP;
4136 mii.fMask |= MIIM_BITMAP;
4137 mii.hbmpItem = (HBITMAP) lpNewItem;
4138 }
4139 else if(uFlags & MF_OWNERDRAW)
4140 {
4141 mii.fType |= MFT_OWNERDRAW;
4142 mii.fMask |= MIIM_DATA;
4143 mii.dwItemData = (DWORD) lpNewItem;
4144 }
4145 else
4146 {
4147 mii.fMask |= MIIM_TYPE;
4148 mii.dwTypeData = (LPWSTR)lpNewItem;
4149 mii.cch = (NULL == lpNewItem ? 0 : wcslen(lpNewItem));
4150 }
4151
4152 if(uFlags & MF_RIGHTJUSTIFY)
4153 {
4154 mii.fType |= MFT_RIGHTJUSTIFY;
4155 }
4156 if(uFlags & MF_MENUBREAK)
4157 {
4158 mii.fType |= MFT_MENUBREAK;
4159 }
4160 if(uFlags & MF_MENUBARBREAK)
4161 {
4162 mii.fType |= MFT_MENUBARBREAK;
4163 }
4164 if(uFlags & MF_DISABLED)
4165 {
4166 mii.fState |= MFS_DISABLED;
4167 }
4168 if(uFlags & MF_GRAYED)
4169 {
4170 mii.fState |= MFS_GRAYED;
4171 }
4172
4173 if(uFlags & MF_POPUP)
4174 {
4175 mii.fType |= MF_POPUP;
4176 mii.fMask |= MIIM_SUBMENU;
4177 mii.hSubMenu = (HMENU)uIDNewItem;
4178 }
4179 else
4180 {
4181 mii.fMask |= MIIM_ID;
4182 mii.wID = (UINT)uIDNewItem;
4183 }
4184 return InsertMenuItemW(hMenu, uPosition, (BOOL)!(MF_BYPOSITION & uFlags), &mii);
4185 }
4186
4187
4188 /*
4189 * @implemented
4190 */
4191 BOOL
4192 STDCALL
4193 IsMenu(
4194 HMENU Menu)
4195 {
4196 ROSMENUINFO MenuInfo;
4197
4198 return MenuGetRosMenuInfo(&MenuInfo, Menu);
4199 }
4200
4201
4202 /*
4203 * @implemented
4204 */
4205 HMENU STDCALL
4206 LoadMenuA(HINSTANCE hInstance,
4207 LPCSTR lpMenuName)
4208 {
4209 HANDLE Resource = FindResourceA(hInstance, lpMenuName, MAKEINTRESOURCEA(4));
4210 if (Resource == NULL)
4211 {
4212 return(NULL);
4213 }
4214 return(LoadMenuIndirectA((PVOID)LoadResource(hInstance, Resource)));
4215 }
4216
4217
4218 /*
4219 * @implemented
4220 */
4221 HMENU STDCALL
4222 LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate)
4223 {
4224 return(LoadMenuIndirectW(lpMenuTemplate));
4225 }
4226
4227
4228 /*
4229 * @implemented
4230 */
4231 HMENU STDCALL
4232 LoadMenuIndirectW(CONST MENUTEMPLATE *lpMenuTemplate)
4233 {
4234 HMENU hMenu;
4235 WORD version, offset;
4236 LPCSTR p = (LPCSTR)lpMenuTemplate;
4237
4238 version = GET_WORD(p);
4239 p += sizeof(WORD);
4240
4241 switch (version)
4242 {
4243 case 0: /* standard format is version of 0 */
4244 offset = GET_WORD(p);
4245 p += sizeof(WORD) + offset;
4246 if (!(hMenu = CreateMenu())) return 0;
4247 if (!MENU_ParseResource(p, hMenu, TRUE))
4248 {
4249 DestroyMenu(hMenu);
4250 return 0;
4251 }
4252 return hMenu;
4253 case 1: /* extended format is version of 1 */
4254 offset = GET_WORD(p);
4255 p += sizeof(WORD) + offset;
4256 if (!(hMenu = CreateMenu())) return 0;
4257 if (!MENUEX_ParseResource(p, hMenu))
4258 {
4259 DestroyMenu( hMenu );
4260 return 0;
4261 }
4262 return hMenu;
4263 default:
4264 DbgPrint("LoadMenuIndirectW(): version %d not supported.\n", version);
4265 return 0;
4266 }
4267 }
4268
4269
4270 /*
4271 * @implemented
4272 */
4273 HMENU STDCALL
4274 LoadMenuW(HINSTANCE hInstance,
4275 LPCWSTR lpMenuName)
4276 {
4277 HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU);
4278 if (Resource == NULL)
4279 {
4280 return(NULL);
4281 }
4282 return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource)));
4283 }
4284
4285
4286 /*
4287 * @implemented
4288 */
4289 int
4290 STDCALL
4291 MenuItemFromPoint(
4292 HWND hWnd,
4293 HMENU hMenu,
4294 POINT ptScreen)
4295 {
4296 return NtUserMenuItemFromPoint(hWnd, hMenu, ptScreen.x, ptScreen.y);
4297 }
4298
4299
4300 /*
4301 * @implemented
4302 */
4303 BOOL
4304 STDCALL
4305 ModifyMenuA(
4306 HMENU hMnu,
4307 UINT uPosition,
4308 UINT uFlags,
4309 UINT_PTR uIDNewItem,
4310 LPCSTR lpNewItem)
4311 {
4312 MENUITEMINFOA mii;
4313 mii.cbSize = sizeof(MENUITEMINFOA);
4314 mii.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE;
4315 mii.fType = 0;
4316 mii.fState = MFS_ENABLED;
4317
4318 UNIMPLEMENTED;
4319
4320 if(!GetMenuItemInfoA( hMnu,
4321 uPosition,
4322 (BOOL)!(MF_BYPOSITION & uFlags),
4323 &mii)) return FALSE;
4324
4325 if(uFlags & MF_BITMAP)
4326 {
4327 mii.fType |= MFT_BITMAP;
4328 mii.fMask |= MIIM_BITMAP;
4329 mii.hbmpItem = (HBITMAP) lpNewItem;
4330 }
4331 else if(uFlags & MF_OWNERDRAW)
4332 {
4333 mii.fType |= MFT_OWNERDRAW;
4334 mii.fMask |= MIIM_DATA;
4335 mii.dwItemData = (DWORD) lpNewItem;
4336 }
4337 else /* Default action MF_STRING. */
4338 {
4339 if(mii.dwTypeData != NULL)
4340 {
4341 HeapFree(GetProcessHeap(),0, mii.dwTypeData);
4342 }
4343 /* Item beginning with a backspace is a help item */
4344 if (*lpNewItem == '\b')
4345 {
4346 mii.fType |= MF_HELP;
4347 lpNewItem++;
4348 }
4349 mii.fMask |= MIIM_TYPE;
4350 mii.dwTypeData = (LPSTR)lpNewItem;
4351 mii.cch = (NULL == lpNewItem ? 0 : strlen(lpNewItem));
4352 }
4353
4354 if(uFlags & MF_RIGHTJUSTIFY)
4355 {
4356 mii.fType |= MFT_RIGHTJUSTIFY;
4357 }
4358 if(uFlags & MF_MENUBREAK)
4359 {
4360 mii.fType |= MFT_MENUBREAK;
4361 }
4362 if(uFlags & MF_MENUBARBREAK)
4363 {
4364 mii.fType |= MFT_MENUBARBREAK;
4365 }
4366 if(uFlags & MF_DISABLED)
4367 {
4368 mii.fState |= MFS_DISABLED;
4369 }
4370 if(uFlags & MF_GRAYED)
4371 {
4372 mii.fState |= MFS_GRAYED;
4373 }
4374
4375 if ((mii.fType & MF_POPUP) && (uFlags & MF_POPUP) && (mii.hSubMenu != (HMENU)uIDNewItem))
4376 NtUserDestroyMenu( mii.hSubMenu ); /* ModifyMenu() spec */
4377
4378 if(uFlags & MF_POPUP)
4379 {
4380 mii.fType |= MF_POPUP;
4381 mii.fMask |= MIIM_SUBMENU;
4382 mii.hSubMenu = (HMENU)uIDNewItem;
4383 }
4384 else
4385 {
4386 mii.fMask |= MIIM_ID;
4387 mii.wID = (UINT)uIDNewItem;
4388 }
4389
4390 return SetMenuItemInfoA( hMnu,
4391 uPosition,
4392 (BOOL)!(MF_BYPOSITION & uFlags),
4393 &mii);
4394 }
4395
4396
4397 /*
4398 * @implemented
4399 */
4400 BOOL
4401 STDCALL
4402 ModifyMenuW(
4403 HMENU hMnu,
4404 UINT uPosition,
4405 UINT uFlags,
4406 UINT_PTR uIDNewItem,
4407 LPCWSTR lpNewItem)
4408 {
4409 MENUITEMINFOW mii;
4410 mii.cbSize = sizeof(MENUITEMINFOW);
4411 mii.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE;
4412 mii.fType = 0;
4413 mii.fState = MFS_ENABLED;
4414
4415 UNIMPLEMENTED;
4416
4417 if(!NtUserMenuItemInfo( hMnu,
4418 uPosition,
4419 (BOOL)!(MF_BYPOSITION & uFlags),
4420 (PROSMENUITEMINFO) &mii,
4421 FALSE)) return FALSE;
4422
4423 if(uFlags & MF_BITMAP)
4424 {
4425 mii.fType |= MFT_BITMAP;
4426 mii.fMask |= MIIM_BITMAP;
4427 mii.hbmpItem = (HBITMAP) lpNewItem;
4428 }
4429 else if(uFlags & MF_OWNERDRAW)
4430 {
4431 mii.fType |= MFT_OWNERDRAW;
4432 mii.fMask |= MIIM_DATA;
4433 mii.dwItemData = (DWORD) lpNewItem;
4434 }
4435 else
4436 {
4437 if(mii.dwTypeData != NULL)
4438 {
4439 HeapFree(GetProcessHeap(),0, mii.dwTypeData);
4440 }
4441 if (*lpNewItem == '\b')
4442 {
4443 mii.fType |= MF_HELP;
4444 lpNewItem++;
4445 }
4446 mii.fMask |= MIIM_TYPE;
4447 mii.dwTypeData = (LPWSTR)lpNewItem;
4448 mii.cch = (NULL == lpNewItem ? 0 : wcslen(lpNewItem));
4449 }
4450
4451 if(uFlags & MF_RIGHTJUSTIFY)
4452 {
4453 mii.fType |= MFT_RIGHTJUSTIFY;
4454 }
4455 if(uFlags & MF_MENUBREAK)
4456 {
4457 mii.fType |= MFT_MENUBREAK;
4458 }
4459 if(uFlags & MF_MENUBARBREAK)
4460 {
4461 mii.fType |= MFT_MENUBARBREAK;
4462 }
4463 if(uFlags & MF_DISABLED)
4464 {
4465 mii.fState |= MFS_DISABLED;
4466 }
4467 if(uFlags & MF_GRAYED)
4468 {
4469 mii.fState |= MFS_GRAYED;
4470 }
4471
4472 if ((mii.fType & MF_POPUP) && (uFlags & MF_POPUP) && (mii.hSubMenu != (HMENU)uIDNewItem))
4473 NtUserDestroyMenu( mii.hSubMenu );
4474
4475 if(uFlags & MF_POPUP)
4476 {
4477 mii.fType |= MF_POPUP;
4478 mii.fMask |= MIIM_SUBMENU;
4479 mii.hSubMenu = (HMENU)uIDNewItem;
4480 }
4481 else
4482 {
4483 mii.fMask |= MIIM_ID;
4484 mii.wID = (UINT)uIDNewItem;
4485 }
4486
4487 return SetMenuItemInfoW( hMnu,
4488 uPosition,
4489 (BOOL)!(MF_BYPOSITION & uFlags),
4490 &mii);
4491 }
4492
4493
4494 /*
4495 * @implemented
4496 */
4497 BOOL
4498 STDCALL
4499 RemoveMenu(
4500 HMENU hMenu,
4501 UINT uPosition,
4502 UINT uFlags)
4503 {
4504 return NtUserRemoveMenu(hMenu, uPosition, uFlags);
4505 }
4506
4507
4508 /*
4509 * @implemented
4510 */
4511 BOOL STDCALL
4512 SetMenu(HWND hWnd,
4513 HMENU hMenu)
4514 {
4515 return NtUserSetMenu(hWnd, hMenu, TRUE);
4516 }
4517
4518
4519 /*
4520 * @implemented
4521 */
4522 BOOL
4523 STDCALL
4524 SetMenuDefaultItem(
4525 HMENU hMenu,
4526 UINT uItem,
4527 UINT fByPos)
4528 {
4529 return NtUserSetMenuDefaultItem(hMenu, uItem, fByPos);
4530 }
4531
4532
4533 /*
4534 * @implemented
4535 */
4536 BOOL
4537 STDCALL
4538 SetMenuInfo(
4539 HMENU hmenu,
4540 LPCMENUINFO lpcmi)
4541 {
4542 ROSMENUINFO mi;
4543 BOOL res = FALSE;
4544 if(lpcmi->cbSize != sizeof(MENUINFO))
4545 return res;
4546
4547 memcpy(&mi, lpcmi, sizeof(MENUINFO));
4548 return NtUserMenuInfo(hmenu, &mi, TRUE);
4549 }
4550
4551
4552 /*
4553 * @unimplemented
4554 */
4555 BOOL
4556 STDCALL
4557 SetMenuItemBitmaps(
4558 HMENU hMenu,
4559 UINT uPosition,
4560 UINT uFlags,
4561 HBITMAP hBitmapUnchecked,
4562 HBITMAP hBitmapChecked)
4563 {
4564 UNIMPLEMENTED;
4565 return FALSE;
4566 }
4567
4568
4569 /*
4570 * @unimplemented
4571 */
4572 BOOL
4573 STDCALL
4574 SetMenuItemInfoA(
4575 HMENU hMenu,
4576 UINT uItem,
4577 BOOL fByPosition,
4578 LPCMENUITEMINFOA lpmii)
4579 {
4580 MENUITEMINFOW MenuItemInfoW;
4581 UNICODE_STRING UnicodeString;
4582 ULONG Result;
4583
4584 RtlCopyMemory(&MenuItemInfoW, lpmii, min(lpmii->cbSize, sizeof(MENUITEMINFOW)));
4585
4586 if ((MenuItemInfoW.fMask & (MIIM_TYPE | MIIM_STRING)) &&
4587 (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING) &&
4588 MenuItemInfoW.dwTypeData != NULL)
4589 {
4590 RtlCreateUnicodeStringFromAsciiz(&UnicodeString,
4591 (LPSTR)MenuItemInfoW.dwTypeData);
4592 MenuItemInfoW.dwTypeData = UnicodeString.Buffer;
4593 MenuItemInfoW.cch = UnicodeString.Length / sizeof(WCHAR);
4594 }
4595 else
4596 {
4597 UnicodeString.Buffer = NULL;
4598 }
4599
4600 Result = NtUserMenuItemInfo(hMenu, uItem, fByPosition,
4601 (PROSMENUITEMINFO)&MenuItemInfoW, TRUE);
4602
4603 if (UnicodeString.Buffer != NULL)
4604 {
4605 RtlFreeUnicodeString(&UnicodeString);
4606 }
4607
4608 return Result;
4609 }
4610
4611
4612 /*
4613 * @unimplemented
4614 */
4615 BOOL
4616 STDCALL
4617 SetMenuItemInfoW(
4618 HMENU hMenu,
4619 UINT uItem,
4620 BOOL fByPosition,
4621 LPCMENUITEMINFOW lpmii)
4622 {
4623 MENUITEMINFOW MenuItemInfoW;
4624
4625 RtlCopyMemory(&MenuItemInfoW, lpmii, min(lpmii->cbSize, sizeof(MENUITEMINFOW)));
4626 MenuItemInfoW.cch = wcslen(MenuItemInfoW.dwTypeData);
4627
4628 return NtUserMenuItemInfo(hMenu, uItem, fByPosition,
4629 (PROSMENUITEMINFO)&MenuItemInfoW, TRUE);
4630 }
4631
4632 /*
4633 * @implemented
4634 */
4635 BOOL
4636 STDCALL
4637 SetSystemMenu (
4638 HWND hwnd,
4639 HMENU hMenu)
4640 {
4641 if(!hwnd)
4642 {
4643 SetLastError(ERROR_INVALID_WINDOW_HANDLE);
4644 return FALSE;
4645 }
4646 if(!hMenu)
4647 {
4648 SetLastError(ERROR_INVALID_MENU_HANDLE);
4649 return FALSE;
4650 }
4651 return NtUserSetSystemMenu(hwnd, hMenu);
4652 }
4653
4654
4655 /*
4656 * @implemented
4657 */
4658 BOOL
4659 STDCALL
4660 TrackPopupMenu(
4661 HMENU Menu,
4662 UINT Flags,
4663 int x,
4664 int y,
4665 int Reserved,
4666 HWND Wnd,
4667 CONST RECT *Rect)
4668 {
4669 BOOL ret = FALSE;
4670
4671 MenuInitTracking(Wnd, Menu, TRUE, Flags);
4672
4673 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
4674 if (0 == (Flags & TPM_NONOTIFY))
4675 {
4676 SendMessageW(Wnd, WM_INITMENUPOPUP, (WPARAM) Menu, 0);
4677 }
4678
4679 if (MenuShowPopup(Wnd, Menu, 0, x, y, 0, 0 ))
4680 {
4681 ret = MenuTrackMenu(Menu, Flags | TPM_POPUPMENU, 0, 0, Wnd, Rect);
4682 }
4683 MenuExitTracking(Wnd);
4684
4685 return ret;
4686 }
4687
4688
4689 /*
4690 * @unimplemented
4691 */
4692 BOOL
4693 STDCALL
4694 TrackPopupMenuEx(
4695 HMENU Menu,
4696 UINT Flags,
4697 int x,
4698 int y,
4699 HWND Wnd,
4700 LPTPMPARAMS Tpm)
4701 {
4702 /* Not fully implemented */
4703 return TrackPopupMenu(Menu, Flags, x, y, 0, Wnd,
4704 NULL != Tpm ? &Tpm->rcExclude : NULL);
4705 }
4706
4707
4708 /*
4709 * @implemented
4710 */
4711 BOOL
4712 STDCALL
4713 SetMenuContextHelpId(HMENU hmenu,
4714 DWORD dwContextHelpId)
4715 {
4716 return NtUserSetMenuContextHelpId(hmenu, dwContextHelpId);
4717 }
4718
4719
4720 /*
4721 * @implemented
4722 */
4723 DWORD
4724 STDCALL
4725 GetMenuContextHelpId(HMENU hmenu)
4726 {
4727 ROSMENUINFO mi;
4728 mi.cbSize = sizeof(ROSMENUINFO);
4729 mi.fMask = MIM_HELPID;
4730
4731 if(NtUserMenuInfo(hmenu, &mi, FALSE))
4732 {
4733 return mi.dwContextHelpID;
4734 }
4735 return 0;
4736 }
4737
4738 /*
4739 * @unimplemented
4740 */
4741 LRESULT
4742 STDCALL
4743 MenuWindowProcA(
4744 HWND hWnd,
4745 UINT Msg,
4746 WPARAM wParam,
4747 LPARAM lParam
4748 )
4749 {
4750 UNIMPLEMENTED;
4751 return FALSE;
4752 }
4753
4754 /*
4755 * @unimplemented
4756 */
4757 LRESULT
4758 STDCALL
4759 MenuWindowProcW(
4760 HWND hWnd,
4761 UINT Msg,
4762 WPARAM wParam,
4763 LPARAM lParam
4764 )
4765 {
4766 UNIMPLEMENTED;
4767 return FALSE;
4768 }
4769
4770 /*
4771 * @implemented
4772 */
4773 BOOL
4774 STDCALL
4775 ChangeMenuW(
4776 HMENU hMenu,
4777 UINT cmd,
4778 LPCWSTR lpszNewItem,
4779 UINT cmdInsert,
4780 UINT flags)
4781 {
4782 /*
4783 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
4784 for MF_DELETE. We should check the parameters for all others
4785 MF_* actions also (anybody got a doc on ChangeMenu?).
4786 */
4787
4788 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
4789 {
4790 case MF_APPEND :
4791 return AppendMenuW(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
4792
4793 case MF_DELETE :
4794 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
4795
4796 case MF_CHANGE :
4797 return ModifyMenuW(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
4798
4799 case MF_REMOVE :
4800 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
4801 flags &~ MF_REMOVE);
4802
4803 default : /* MF_INSERT */
4804 return InsertMenuW(hMenu, cmd, flags, cmdInsert, lpszNewItem);
4805 };
4806 }
4807
4808 /*
4809 * @implemented
4810 */
4811 BOOL
4812 STDCALL
4813 ChangeMenuA(
4814 HMENU hMenu,
4815 UINT cmd,
4816 LPCSTR lpszNewItem,
4817 UINT cmdInsert,
4818 UINT flags)
4819 {
4820 /*
4821 FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
4822 for MF_DELETE. We should check the parameters for all others
4823 MF_* actions also (anybody got a doc on ChangeMenu?).
4824 */
4825
4826 switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
4827 {
4828 case MF_APPEND :
4829 return AppendMenuA(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
4830
4831 case MF_DELETE :
4832 return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
4833
4834 case MF_CHANGE :
4835 return ModifyMenuA(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
4836
4837 case MF_REMOVE :
4838 return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
4839 flags &~ MF_REMOVE);
4840
4841 default : /* MF_INSERT */
4842 return InsertMenuA(hMenu, cmd, flags, cmdInsert, lpszNewItem);
4843 };
4844 }