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