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