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