08e33325c3141863b27f12afd2f6665b31b73581
[reactos.git] / reactos / win32ss / user / user32 / windows / menu.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS user32.dll
4 * FILE: user32/windows/menu.c
5 * PURPOSE: Menus
6 *
7 * PROGRAMMERS: Casper S. Hornstrup
8 * James Tabor
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <user32.h>
14 #include <wine/debug.h>
15
16 LRESULT DefWndNCPaint(HWND hWnd, HRGN hRgn, BOOL Active);
17 BOOL WINAPI GdiValidateHandle(HGDIOBJ hobj);
18 LRESULT DefWndNCHitTest(HWND hWnd, POINT Point);
19 void FASTCALL NcGetSysPopupPos(HWND Wnd, RECT *Rect);
20
21 WINE_DEFAULT_DEBUG_CHANNEL(menu);
22
23 /* internal popup menu window messages */
24
25 #define MM_SETMENUHANDLE (WM_USER + 0)
26 #define MM_GETMENUHANDLE (WM_USER + 1)
27
28 /* internal flags for menu tracking */
29
30 #define TF_ENDMENU 0x10000
31 #define TF_SUSPENDPOPUP 0x20000
32 #define TF_SKIPREMOVE 0x40000
33
34 #define ITEM_PREV -1
35 #define ITEM_NEXT 1
36
37 /* Internal MenuTrackMenu() flags */
38 #define TPM_INTERNAL 0xF0000000
39 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
40 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
41
42 /* top and bottom margins for popup menus */
43 #define MENU_TOP_MARGIN 3
44 #define MENU_BOTTOM_MARGIN 2
45
46 #define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)
47
48 #define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK)
49
50 #define MNS_STYLE_MASK (MNS_NOCHECK|MNS_MODELESS|MNS_DRAGDROP|MNS_AUTODISMISS|MNS_NOTIFYBYPOS|MNS_CHECKORBMP)
51
52 #define MENUITEMINFO_TYPE_MASK \
53 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
54 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
55 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
56
57 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
58
59 #define STATE_MASK (~TYPE_MASK)
60
61 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
62
63 #define MII_STATE_MASK (MFS_GRAYED|MFS_CHECKED|MFS_HILITE|MFS_DEFAULT)
64
65 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
66 #define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
67 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
68
69 #define IS_SYSTEM_MENU(MenuInfo) \
70 (0 == ((MenuInfo)->fFlags & MNF_POPUP) && 0 != ((MenuInfo)->fFlags & MNF_SYSDESKMN))
71
72 #define IS_SYSTEM_POPUP(MenuInfo) \
73 (0 != ((MenuInfo)->fFlags & MNF_POPUP) && 0 != ((MenuInfo)->fFlags & MNF_SYSDESKMN))
74
75 #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
76
77 /* Use global popup window because there's no way 2 menus can
78 * be tracked at the same time. */
79 static HWND top_popup;
80 static HMENU top_popup_hmenu;
81
82 /* Flag set by EndMenu() to force an exit from menu tracking */
83 static BOOL fEndMenu = FALSE;
84
85 #define MENU_ITEM_HBMP_SPACE (5)
86 #define MENU_BAR_ITEMS_SPACE (12)
87 #define SEPARATOR_HEIGHT (5)
88 #define MENU_TAB_SPACE (8)
89
90 typedef struct
91 {
92 UINT TrackFlags;
93 HMENU CurrentMenu; /* current submenu (can be equal to hTopMenu)*/
94 HMENU TopMenu; /* initial menu */
95 HWND OwnerWnd; /* where notifications are sent */
96 POINT Pt;
97 } MTRACKER;
98
99
100 /*********************************************************************
101 * PopupMenu class descriptor
102 */
103 const struct builtin_class_descr POPUPMENU_builtin_class =
104 {
105 WC_MENU, /* name */
106 CS_SAVEBITS | CS_DBLCLKS, /* style */
107 (WNDPROC) NULL, /* FIXME - procA */
108 (WNDPROC) PopupMenuWndProcW, /* FIXME - procW */
109 sizeof(MENUINFO *), /* extra */
110 (LPCWSTR) IDC_ARROW, /* cursor */
111 (HBRUSH)(COLOR_MENU + 1) /* brush */
112 };
113
114 #ifndef GET_WORD
115 #define GET_WORD(ptr) (*(WORD *)(ptr))
116 #endif
117 #ifndef GET_DWORD
118 #define GET_DWORD(ptr) (*(DWORD *)(ptr))
119 #endif
120
121 HFONT hMenuFont = NULL;
122 HFONT hMenuFontBold = NULL;
123
124 /* Dimension of the menu bitmaps */
125 static HBITMAP BmpSysMenu = NULL;
126
127 static SIZE MenuCharSize;
128
129
130 /***********************************************************************
131 * MENU_GetMenu
132 *
133 * Validate the given menu handle and returns the menu structure pointer.
134 */
135 FORCEINLINE PMENU MENU_GetMenu(HMENU hMenu)
136 {
137 return ValidateHandleNoErr(hMenu, TYPE_MENU);
138 }
139
140 /***********************************************************************
141 * get_win_sys_menu
142 *
143 * Get the system menu of a window
144 */
145 static HMENU get_win_sys_menu( HWND hwnd )
146 {
147 HMENU ret = 0;
148 WND *win = ValidateHwnd( hwnd );
149 if (win)
150 {
151 ret = win->SystemMenu;
152 }
153 return ret;
154 }
155
156 /***********************************************************************
157 * MENU_FindItem
158 *
159 * Find a menu item. Return a pointer on the item, and modifies *hmenu
160 * in case the item was in a sub-menu.
161 */
162 ITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
163 {
164 MENU *menu;
165 ITEM *fallback = NULL;
166 UINT fallback_pos = 0;
167 UINT i;
168 PITEM pItem;
169
170 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
171 if (wFlags & MF_BYPOSITION)
172 {
173 if (*nPos >= menu->cItems) return NULL;
174 pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
175 if (pItem) pItem = &pItem[*nPos];
176 return pItem;
177 }
178 else
179 {
180 PITEM item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
181 for (i = 0; item, i < menu->cItems; i++, item++)
182 {
183 if (item->spSubMenu)
184 {
185 PMENU pSubMenu = DesktopPtrToUser(item->spSubMenu);
186 HMENU hsubmenu = UserHMGetHandle(pSubMenu);
187 ITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
188 if (subitem)
189 {
190 *hmenu = hsubmenu;
191 return subitem;
192 }
193 else if (item->wID == *nPos)
194 {
195 /* fallback to this item if nothing else found */
196 fallback_pos = i;
197 fallback = item;
198 }
199 }
200 else if (item->wID == *nPos)
201 {
202 *nPos = i;
203 return item;
204 }
205 }
206 }
207
208 if (fallback)
209 *nPos = fallback_pos;
210
211 return fallback;
212 }
213
214 #define MAX_GOINTOSUBMENU (0x10)
215 UINT FASTCALL
216 IntGetMenuDefaultItem(PMENU Menu, BOOL fByPos, UINT gmdiFlags, DWORD *gismc)
217 {
218 UINT i = 0;
219 PITEM Item = Menu->rgItems ? DesktopPtrToUser(Menu->rgItems) : NULL;
220
221 /* empty menu */
222 if (!Item) return -1;
223
224 while ( !( Item->fState & MFS_DEFAULT ) )
225 {
226 i++; Item++;
227 if (i >= Menu->cItems ) return -1;
228 }
229
230 /* default: don't return disabled items */
231 if ( (!(GMDI_USEDISABLED & gmdiFlags)) && (Item->fState & MFS_DISABLED )) return -1;
232
233 /* search rekursiv when needed */
234 if ( (Item->fType & MF_POPUP) && (gmdiFlags & GMDI_GOINTOPOPUPS) && Item->spSubMenu)
235 {
236 UINT ret;
237 (*gismc)++;
238 ret = IntGetMenuDefaultItem( DesktopPtrToUser(Item->spSubMenu), fByPos, gmdiFlags, gismc );
239 (*gismc)--;
240 if ( -1 != ret ) return ret;
241
242 /* when item not found in submenu, return the popup item */
243 }
244 return ( fByPos ) ? i : Item->wID;
245 }
246
247 static BOOL GetMenuItemInfo_common ( HMENU hmenu,
248 UINT item,
249 BOOL bypos,
250 LPMENUITEMINFOW lpmii,
251 BOOL unicode)
252 {
253 ITEM *pItem = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
254
255 //debug_print_menuitem("GetMenuItemInfo_common: ", pItem, "");
256
257 if (!pItem)
258 {
259 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
260 return FALSE;
261 }
262
263 if( lpmii->fMask & MIIM_TYPE)
264 {
265 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP))
266 {
267 ERR("invalid combination of fMask bits used\n");
268 /* this does not happen on Win9x/ME */
269 SetLastError( ERROR_INVALID_PARAMETER);
270 return FALSE;
271 }
272 lpmii->fType = pItem->fType & MENUITEMINFO_TYPE_MASK;
273 if( pItem->hbmp) lpmii->fType |= MFT_BITMAP;
274 lpmii->hbmpItem = pItem->hbmp; /* not on Win9x/ME */
275 if( lpmii->fType & MFT_BITMAP)
276 {
277 lpmii->dwTypeData = (LPWSTR) pItem->hbmp;
278 lpmii->cch = 0;
279 }
280 else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR))
281 {
282 /* this does not happen on Win9x/ME */
283 lpmii->dwTypeData = 0;
284 lpmii->cch = 0;
285 }
286 }
287
288 /* copy the text string */
289 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)))
290 {
291 if( !pItem->Xlpstr )
292 { // Very strange this fixes a wine test with a crash.
293 if(lpmii->dwTypeData && lpmii->cch && !(GdiValidateHandle((HGDIOBJ)lpmii->dwTypeData)) )
294 {
295 lpmii->cch = 0;
296 if( unicode)
297 *((WCHAR *)lpmii->dwTypeData) = 0;
298 else
299 *((CHAR *)lpmii->dwTypeData) = 0;
300 }
301 }
302 else
303 {
304 int len;
305 LPWSTR text = DesktopPtrToUser(pItem->Xlpstr);
306 if (unicode)
307 {
308 len = strlenW(text);
309 if(lpmii->dwTypeData && lpmii->cch)
310 lstrcpynW(lpmii->dwTypeData, text, lpmii->cch);
311 }
312 else
313 {
314 len = WideCharToMultiByte( CP_ACP, 0, text, -1, NULL, 0, NULL, NULL ) - 1;
315 if(lpmii->dwTypeData && lpmii->cch)
316 if (!WideCharToMultiByte( CP_ACP, 0, text, -1,
317 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
318 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
319 }
320 /* if we've copied a substring we return its length */
321 if(lpmii->dwTypeData && lpmii->cch)
322 if (lpmii->cch <= len + 1)
323 lpmii->cch--;
324 else
325 lpmii->cch = len;
326 else
327 {
328 /* return length of string */
329 /* not on Win9x/ME if fType & MFT_BITMAP */
330 lpmii->cch = len;
331 }
332 }
333 }
334
335 if (lpmii->fMask & MIIM_FTYPE)
336 lpmii->fType = pItem->fType & MENUITEMINFO_TYPE_MASK;
337
338 if (lpmii->fMask & MIIM_BITMAP)
339 lpmii->hbmpItem = pItem->hbmp;
340
341 if (lpmii->fMask & MIIM_STATE)
342 lpmii->fState = pItem->fState & MENUITEMINFO_STATE_MASK;
343
344 if (lpmii->fMask & MIIM_ID)
345 lpmii->wID = pItem->wID;
346
347 if (lpmii->fMask & MIIM_SUBMENU && pItem->spSubMenu )
348 {
349 PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
350 HMENU hSubMenu = UserHMGetHandle(pSubMenu);
351 lpmii->hSubMenu = hSubMenu;
352 }
353 else
354 {
355 /* hSubMenu is always cleared
356 * (not on Win9x/ME ) */
357 lpmii->hSubMenu = 0;
358 }
359
360 if (lpmii->fMask & MIIM_CHECKMARKS)
361 {
362 lpmii->hbmpChecked = pItem->hbmpChecked;
363 lpmii->hbmpUnchecked = pItem->hbmpUnchecked;
364 }
365 if (lpmii->fMask & MIIM_DATA)
366 lpmii->dwItemData = pItem->dwItemData;
367
368 return TRUE;
369 }
370
371 /***********************************************************************
372 * MenuGetRosMenuInfo
373 *
374 * Get full information about menu
375 */
376 static BOOL FASTCALL
377 MenuGetRosMenuInfo(PROSMENUINFO MenuInfo, HMENU Menu)
378 {
379 PMENU pMenu;
380 if (!(pMenu = ValidateHandleNoErr(Menu, TYPE_MENU))) return FALSE;
381
382 MenuInfo->hbrBack = pMenu->hbrBack;
383 MenuInfo->dwContextHelpID = pMenu->dwContextHelpId;
384 MenuInfo->cyMax = pMenu->cyMax;
385 MenuInfo->dwMenuData = pMenu->dwMenuData;
386 MenuInfo->dwStyle = pMenu->fFlags & MNS_STYLE_MASK;
387
388 MenuInfo->cItems = pMenu->cItems;
389
390 MenuInfo->iItem = pMenu->iItem;
391 MenuInfo->cxMenu = pMenu->cxMenu;
392 MenuInfo->cyMenu = pMenu->cyMenu;
393 MenuInfo->spwndNotify = pMenu->spwndNotify;
394 MenuInfo->cxTextAlign = pMenu->cxTextAlign;
395 MenuInfo->iTop = pMenu->iMaxTop;
396 MenuInfo->iMaxTop = pMenu->iMaxTop;
397 MenuInfo->dwArrowsOn = pMenu->dwArrowsOn;
398
399 MenuInfo->fFlags = pMenu->fFlags;
400 MenuInfo->Self = pMenu->head.h;
401 MenuInfo->TimeToHide = pMenu->TimeToHide;
402 MenuInfo->Wnd = pMenu->hWnd;
403 return TRUE;
404 }
405
406 /***********************************************************************
407 * MenuSetRosMenuInfo
408 *
409 * Set full information about menu
410 */
411 static BOOL FASTCALL
412 MenuSetRosMenuInfo(PROSMENUINFO MenuInfo)
413 {
414 MenuInfo->cbSize = sizeof(ROSMENUINFO);
415 MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE;
416
417 return NtUserThunkedMenuInfo(MenuInfo->Self, (LPCMENUINFO)MenuInfo);
418 }
419
420 /***********************************************************************
421 * MenuInitRosMenuItemInfo
422 *
423 * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo
424 */
425 static VOID FASTCALL
426 MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
427 {
428 ZeroMemory(ItemInfo, sizeof(ROSMENUITEMINFO));
429 ItemInfo->cbSize = sizeof(ROSMENUITEMINFO);
430 }
431
432 /***********************************************************************
433 * MenuGetRosMenuItemInfo
434 *
435 * Get full information about a menu item
436 */
437 static BOOL FASTCALL
438 MenuGetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo)
439 {
440 PITEM pItem;
441 UINT Save_Mask = ItemInfo->fMask; /* Save the org mask bits. */
442
443 if (ItemInfo->dwTypeData != NULL)
444 {
445 HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData);
446 }
447
448 ItemInfo->dwTypeData = NULL;
449
450 if (!(pItem = MENU_FindItem(&Menu, &Index, MF_BYPOSITION)))
451 {
452 ItemInfo->fType = 0;
453 return FALSE;
454 }
455
456 ItemInfo->fType = pItem->fType;
457 ItemInfo->hbmpItem = pItem->hbmp;
458 ItemInfo->hbmpChecked = pItem->hbmpChecked;
459 ItemInfo->hbmpUnchecked = pItem->hbmpUnchecked;
460 ItemInfo->dwItemData = pItem->dwItemData;
461 ItemInfo->wID = pItem->wID;
462 ItemInfo->fState = pItem->fState;
463
464 if (pItem->spSubMenu)
465 {
466 PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
467 HMENU hSubMenu = UserHMGetHandle(pSubMenu);
468 ItemInfo->hSubMenu = hSubMenu;
469 }
470 else
471 ItemInfo->hSubMenu = NULL;
472
473 if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING)
474 {
475 LPWSTR lpstr = pItem->lpstr.Buffer ? DesktopPtrToUser(pItem->lpstr.Buffer) : NULL;
476 if (lpstr)
477 {
478 ItemInfo->cch = pItem->lpstr.Length / sizeof(WCHAR);
479 ItemInfo->cch++;
480 ItemInfo->dwTypeData = HeapAlloc(GetProcessHeap(), 0, ItemInfo->cch * sizeof(WCHAR));
481 if (ItemInfo->dwTypeData == NULL)
482 {
483 return FALSE;
484 }
485 RtlCopyMemory(ItemInfo->dwTypeData, lpstr, min(ItemInfo->cch * sizeof(WCHAR), pItem->lpstr.MaximumLength));
486 }
487 else
488 {
489 ItemInfo->cch = 0;
490 }
491 }
492
493 ItemInfo->Rect.left = pItem->xItem;
494 ItemInfo->Rect.top = pItem->yItem;
495 ItemInfo->Rect.right = pItem->cxItem; // Do this for now......
496 ItemInfo->Rect.bottom = pItem->cyItem;
497 ItemInfo->dxTab = pItem->dxTab;
498 ItemInfo->lpstr = pItem->lpstr.Buffer;
499 ItemInfo->maxBmpSize.cx = pItem->cxBmp;
500 ItemInfo->maxBmpSize.cy = pItem->cyBmp;
501
502 ItemInfo->fMask = Save_Mask;
503 return TRUE;
504 }
505
506 /***********************************************************************
507 * MenuSetRosMenuItemInfo
508 *
509 * Set selected information about a menu item, need to set the mask bits.
510 */
511 static BOOL FASTCALL
512 MenuSetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo)
513 {
514 BOOL Ret;
515
516 if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING &&
517 ItemInfo->dwTypeData != NULL)
518 {
519 ItemInfo->cch = strlenW(ItemInfo->dwTypeData);
520 }
521 if (ItemInfo->hSubMenu)
522 {
523 if (!IsMenu(ItemInfo->hSubMenu)) ItemInfo->hSubMenu = NULL;
524 }
525 Ret = NtUserThunkedMenuItemInfo(Menu, Index, TRUE, FALSE, (LPMENUITEMINFOW)ItemInfo, NULL);
526 return Ret;
527 }
528
529 /***********************************************************************
530 * MenuCleanupRosMenuItemInfo
531 *
532 * Cleanup after use of MenuGet/SetRosMenuItemInfo
533 */
534 static VOID FASTCALL
535 MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
536 {
537 if (ItemInfo->dwTypeData != NULL)
538 {
539 HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData);
540 ItemInfo->dwTypeData = NULL;
541 }
542 }
543
544 /***********************************************************************
545 * MenuInitSysMenuPopup
546 *
547 * Grey the appropriate items in System menu.
548 */
549 void FASTCALL MenuInitSysMenuPopup(HMENU hmenu, DWORD style, DWORD clsStyle, LONG HitTest )
550 {
551 BOOL gray;
552 UINT DefItem;
553 #if 0
554 MENUITEMINFOW mii;
555 #endif
556
557 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
558 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
559 gray = ((style & WS_MAXIMIZE) != 0);
560 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
561 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
562 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
563 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
564 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
565 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
566 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
567 gray = (clsStyle & CS_NOCLOSE) != 0;
568
569 /* The menu item must keep its state if it's disabled */
570 if(gray)
571 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
572
573 /* Set default menu item */
574 if(style & WS_MINIMIZE) DefItem = SC_RESTORE;
575 else if(HitTest == HTCAPTION) DefItem = ((style & (WS_MAXIMIZE | WS_MINIMIZE)) ? SC_RESTORE : SC_MAXIMIZE);
576 else DefItem = SC_CLOSE;
577 #if 0
578 mii.cbSize = sizeof(MENUITEMINFOW);
579 mii.fMask |= MIIM_STATE;
580 if((DefItem != SC_CLOSE) && GetMenuItemInfoW(hmenu, DefItem, FALSE, &mii) &&
581 (mii.fState & (MFS_GRAYED | MFS_DISABLED))) DefItem = SC_CLOSE;
582 #endif
583 SetMenuDefaultItem(hmenu, DefItem, MF_BYCOMMAND);
584 }
585
586 /******************************************************************************
587 *
588 * UINT MenuGetStartOfNextColumn(
589 * PROSMENUINFO MenuInfo)
590 *
591 *****************************************************************************/
592
593 static UINT MENU_GetStartOfNextColumn(
594 HMENU hMenu )
595 {
596 MENU *menu = MENU_GetMenu(hMenu);
597 PITEM pItem;
598 UINT i;
599
600 if(!menu)
601 return NO_SELECTED_ITEM;
602
603 i = menu->iItem + 1;
604 if( i == NO_SELECTED_ITEM )
605 return i;
606
607 pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
608 if (!pItem) return NO_SELECTED_ITEM;
609 for( ; i < menu->cItems; ++i ) {
610 if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
611 return i;
612 }
613
614 return NO_SELECTED_ITEM;
615 }
616
617 /******************************************************************************
618 *
619 * UINT MenuGetStartOfPrevColumn(
620 * PROSMENUINFO MenuInfo)
621 *
622 *****************************************************************************/
623 static UINT MENU_GetStartOfPrevColumn(
624 HMENU hMenu )
625 {
626 MENU *menu = MENU_GetMenu(hMenu);
627 UINT i;
628 PITEM pItem;
629
630 if( !menu )
631 return NO_SELECTED_ITEM;
632
633 if( menu->iItem == 0 || menu->iItem == NO_SELECTED_ITEM )
634 return NO_SELECTED_ITEM;
635
636 pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
637 if (!pItem) return NO_SELECTED_ITEM;
638
639 /* Find the start of the column */
640
641 for(i = menu->iItem; i != 0 &&
642 !(pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
643 --i); /* empty */
644
645 if(i == 0)
646 return NO_SELECTED_ITEM;
647
648 for(--i; i != 0; --i) {
649 if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
650 break;
651 }
652
653 TRACE("ret %d.\n", i );
654
655 return i;
656 }
657
658 /***********************************************************************
659 * MenuLoadBitmaps
660 *
661 * Load the arrow bitmap. We can't do this from MenuInit since user32
662 * can also be used (and thus initialized) from text-mode.
663 */
664 static void FASTCALL
665 MenuLoadBitmaps(VOID)
666 {
667 /* Load system buttons bitmaps */
668 if (NULL == BmpSysMenu)
669 {
670 BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
671 }
672 }
673 /////////// Make gpsi OBMI via callback //////////////
674 /***********************************************************************
675 * get_arrow_bitmap
676 */
677 HBITMAP get_arrow_bitmap(void)
678 {
679 static HBITMAP arrow_bitmap;
680
681 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
682 return arrow_bitmap;
683 }
684
685 /***********************************************************************
686 * get_down_arrow_bitmap DFCS_MENUARROWDOWN
687 */
688 HBITMAP get_down_arrow_bitmap(void)
689 {
690 static HBITMAP arrow_bitmap;
691
692 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
693 return arrow_bitmap;
694 }
695
696 /***********************************************************************
697 * get_down_arrow_inactive_bitmap DFCS_MENUARROWDOWN | DFCS_INACTIVE
698 */
699 HBITMAP get_down_arrow_inactive_bitmap(void)
700 {
701 static HBITMAP arrow_bitmap;
702
703 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
704 return arrow_bitmap;
705 }
706
707 /***********************************************************************
708 * get_up_arrow_bitmap DFCS_MENUARROWUP
709 */
710 HBITMAP get_up_arrow_bitmap(void)
711 {
712 static HBITMAP arrow_bitmap;
713
714 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
715 return arrow_bitmap;
716 }
717
718 /***********************************************************************
719 * get_up_arrow_inactive_bitmap DFCS_MENUARROWUP | DFCS_INACTIVE
720 */
721 static HBITMAP get_up_arrow_inactive_bitmap(void)
722 {
723 static HBITMAP arrow_bitmap;
724
725 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
726 return arrow_bitmap;
727 }
728 ////////////////
729 /***********************************************************************
730 * MenuFindSubMenu
731 *
732 * Find a Sub menu. Return the position of the submenu, and modifies
733 * *hmenu in case it is found in another sub-menu.
734 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
735 */
736 static UINT FASTCALL MenuFindSubMenu(HMENU *hmenu, HMENU hSubTarget )
737 {
738 PMENU menu, pSubMenu;
739 HMENU hSubMenu;
740 UINT i;
741 PITEM item;
742
743 if (((*hmenu)==(HMENU)0xffff) ||(!(menu = MENU_GetMenu(*hmenu))))
744 return NO_SELECTED_ITEM;
745
746 item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
747 for (i = 0; i < menu->cItems; i++, item++)
748 {
749 if (!item->spSubMenu)
750 continue;
751 else
752 {
753 pSubMenu = DesktopPtrToUser(item->spSubMenu);
754 hSubMenu = UserHMGetHandle(pSubMenu);
755 if (hSubMenu == hSubTarget)
756 {
757 return i;
758 }
759 else
760 {
761 HMENU hsubmenu = hSubMenu;
762 UINT pos = MenuFindSubMenu( &hsubmenu, hSubTarget );
763 if (pos != NO_SELECTED_ITEM)
764 {
765 *hmenu = hsubmenu;
766 return pos;
767 }
768 }
769 }
770 }
771 return NO_SELECTED_ITEM;
772 }
773
774 /***********************************************************************
775 * MENU_AdjustMenuItemRect
776 *
777 * Adjust menu item rectangle according to scrolling state.
778 */
779 static void
780 MENU_AdjustMenuItemRect(PROSMENUINFO menu, LPRECT rect)
781 {
782 if (menu->dwArrowsOn)
783 {
784 UINT arrow_bitmap_height;
785 BITMAP bmp;
786
787 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
788 arrow_bitmap_height = bmp.bmHeight;
789 rect->top += arrow_bitmap_height - menu->iTop;
790 rect->bottom += arrow_bitmap_height - menu->iTop;
791 }
792 }
793
794 /***********************************************************************
795 * MenuDrawPopupGlyph
796 *
797 * Draws popup magic glyphs (can be found in system menu).
798 */
799 static void FASTCALL
800 MenuDrawPopupGlyph(HDC dc, LPRECT r, INT_PTR popupMagic, BOOL inactive, BOOL hilite)
801 {
802 LOGFONTW lf;
803 HFONT hFont, hOldFont;
804 COLORREF clrsave;
805 INT bkmode;
806 TCHAR symbol;
807 switch (popupMagic)
808 {
809 case (INT_PTR) HBMMENU_POPUP_RESTORE:
810 symbol = '2';
811 break;
812 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
813 symbol = '0';
814 break;
815 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
816 symbol = '1';
817 break;
818 case (INT_PTR) HBMMENU_POPUP_CLOSE:
819 symbol = 'r';
820 break;
821 default:
822 ERR("Invalid popup magic bitmap %d\n", (int)popupMagic);
823 return;
824 }
825 ZeroMemory(&lf, sizeof(LOGFONTW));
826 InflateRect(r, -2, -2);
827 lf.lfHeight = r->bottom - r->top;
828 lf.lfWidth = 0;
829 lf.lfWeight = FW_NORMAL;
830 lf.lfCharSet = DEFAULT_CHARSET;
831 lstrcpy(lf.lfFaceName, TEXT("Marlett"));
832 hFont = CreateFontIndirect(&lf);
833 /* save font and text color */
834 hOldFont = SelectObject(dc, hFont);
835 clrsave = GetTextColor(dc);
836 bkmode = GetBkMode(dc);
837 /* set color and drawing mode */
838 SetBkMode(dc, TRANSPARENT);
839 if (inactive)
840 {
841 /* draw shadow */
842 if (!hilite)
843 {
844 SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
845 TextOut(dc, r->left + 1, r->top + 1, &symbol, 1);
846 }
847 }
848 SetTextColor(dc, GetSysColor(inactive ? COLOR_GRAYTEXT : (hilite ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT)));
849 /* draw selected symbol */
850 TextOut(dc, r->left, r->top, &symbol, 1);
851 /* restore previous settings */
852 SetTextColor(dc, clrsave);
853 SelectObject(dc, hOldFont);
854 SetBkMode(dc, bkmode);
855 DeleteObject(hFont);
856 }
857
858 /***********************************************************************
859 * MenuFindItemByKey
860 *
861 * Find the menu item selected by a key press.
862 * Return item id, -1 if none, -2 if we should close the menu.
863 */
864 static UINT FASTCALL MENU_FindItemByKey(HWND WndOwner, HMENU hmenu,
865 WCHAR Key, BOOL ForceMenuChar)
866 {
867 LRESULT MenuChar;
868 WORD Flags = 0;
869
870 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)Key, Key, hmenu );
871
872 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(WndOwner), 0);
873 if (hmenu)
874 {
875 MENU *menu = MENU_GetMenu( hmenu );
876 ITEM *item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
877
878 if( !ForceMenuChar )
879 {
880 UINT i;
881 BOOL cjk = GetSystemMetrics( SM_DBCSENABLED );
882
883 for (i = 0; i < menu->cItems; i++, item++)
884 {
885 LPWSTR text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL;
886 if( text)
887 {
888 const WCHAR *p = text - 2;
889 do
890 {
891 const WCHAR *q = p + 2;
892 p = strchrW (q, '&');
893 if (!p && cjk) p = strchrW (q, '\036'); /* Japanese Win16 */
894 }
895 while (p != NULL && p [1] == '&');
896 if (p && (toupperW(p[1]) == toupperW(Key))) return i;
897 }
898 }
899 }
900
901 Flags |= menu->fFlags & MNF_POPUP ? MF_POPUP : 0;
902 Flags |= menu->fFlags & MNF_SYSDESKMN ? MF_SYSMENU : 0;
903
904 MenuChar = SendMessageW(WndOwner, WM_MENUCHAR,
905 MAKEWPARAM(Key, Flags), (LPARAM) hmenu);
906 if (HIWORD(MenuChar) == MNC_EXECUTE) return LOWORD(MenuChar);
907 if (HIWORD(MenuChar) == MNC_CLOSE) return (UINT)(-2);
908 }
909 return (UINT)(-1);
910 }
911
912 /***********************************************************************
913 * MenuGetBitmapItemSize
914 *
915 * Get the size of a bitmap item.
916 */
917 static void FASTCALL MenuGetBitmapItemSize(PROSMENUITEMINFO lpitem, SIZE *size, HWND WndOwner)
918 {
919 BITMAP bm;
920 HBITMAP bmp = lpitem->hbmpItem;
921
922 size->cx = size->cy = 0;
923
924 /* check if there is a magic menu item associated with this item */
925 if (IS_MAGIC_BITMAP(bmp))
926 {
927 switch((INT_PTR) bmp)
928 {
929 case (INT_PTR)HBMMENU_CALLBACK:
930 {
931 MEASUREITEMSTRUCT measItem;
932 measItem.CtlType = ODT_MENU;
933 measItem.CtlID = 0;
934 measItem.itemID = lpitem->wID;
935 measItem.itemWidth = lpitem->Rect.right - lpitem->Rect.left;
936 measItem.itemHeight = lpitem->Rect.bottom - lpitem->Rect.top;
937 measItem.itemData = lpitem->dwItemData;
938 SendMessageW( WndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
939 size->cx = measItem.itemWidth;
940 size->cy = measItem.itemHeight;
941 return;
942 }
943 break;
944
945 case (INT_PTR) HBMMENU_SYSTEM:
946 if (0 != lpitem->dwItemData)
947 {
948 bmp = (HBITMAP) lpitem->dwItemData;
949 break;
950 }
951 /* fall through */
952 case (INT_PTR) HBMMENU_MBAR_RESTORE:
953 case (INT_PTR) HBMMENU_MBAR_MINIMIZE:
954 case (INT_PTR) HBMMENU_MBAR_CLOSE:
955 case (INT_PTR) HBMMENU_MBAR_MINIMIZE_D:
956 case (INT_PTR) HBMMENU_MBAR_CLOSE_D:
957 case (INT_PTR) HBMMENU_POPUP_CLOSE:
958 case (INT_PTR) HBMMENU_POPUP_RESTORE:
959 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
960 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
961 /* FIXME: Why we need to subtract these magic values? */
962 /* to make them smaller than the menu bar? */
963 size->cx = GetSystemMetrics(SM_CXSIZE) - 2;
964 size->cy = GetSystemMetrics(SM_CYSIZE) - 4;
965 return;
966 }
967 }
968
969 if (GetObjectW(bmp, sizeof(BITMAP), &bm))
970 {
971 size->cx = bm.bmWidth;
972 size->cy = bm.bmHeight;
973 }
974 }
975
976 /***********************************************************************
977 * MenuDrawBitmapItem
978 *
979 * Draw a bitmap item.
980 */
981 static void FASTCALL MenuDrawBitmapItem(HDC hdc, PROSMENUITEMINFO lpitem, const RECT *rect,
982 PROSMENUINFO MenuInfo, HWND WndOwner, UINT odaction, BOOL MenuBar)
983 {
984 BITMAP bm;
985 DWORD rop;
986 HDC hdcMem;
987 HBITMAP bmp;
988 int w = rect->right - rect->left;
989 int h = rect->bottom - rect->top;
990 int bmp_xoffset = 0;
991 int left, top;
992 HBITMAP hbmToDraw = lpitem->hbmpItem;
993 bmp = hbmToDraw;
994
995 /* Check if there is a magic menu item associated with this item */
996 if (IS_MAGIC_BITMAP(hbmToDraw))
997 {
998 UINT flags = 0;
999 RECT r;
1000
1001 r = *rect;
1002 switch ((INT_PTR)hbmToDraw)
1003 {
1004 case (INT_PTR)HBMMENU_SYSTEM:
1005 if (lpitem->dwTypeData)
1006 {
1007 bmp = (HBITMAP)lpitem->dwTypeData;
1008 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
1009 }
1010 else
1011 {
1012 if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
1013 bmp = BmpSysMenu;
1014 if (! GetObjectW(bmp, sizeof(bm), &bm)) return;
1015 /* only use right half of the bitmap */
1016 bmp_xoffset = bm.bmWidth / 2;
1017 bm.bmWidth -= bmp_xoffset;
1018 }
1019 goto got_bitmap;
1020 case (INT_PTR)HBMMENU_MBAR_RESTORE:
1021 flags = DFCS_CAPTIONRESTORE;
1022 break;
1023 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
1024 r.right += 1;
1025 flags = DFCS_CAPTIONMIN;
1026 break;
1027 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
1028 r.right += 1;
1029 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
1030 break;
1031 case (INT_PTR)HBMMENU_MBAR_CLOSE:
1032 flags = DFCS_CAPTIONCLOSE;
1033 break;
1034 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
1035 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
1036 break;
1037 case (INT_PTR)HBMMENU_CALLBACK:
1038 {
1039 DRAWITEMSTRUCT drawItem;
1040 POINT origorg;
1041 drawItem.CtlType = ODT_MENU;
1042 drawItem.CtlID = 0;
1043 drawItem.itemID = lpitem->wID;
1044 drawItem.itemAction = odaction;
1045 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
1046 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
1047 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
1048 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
1049 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
1050 //drawItem.itemState |= (!(MenuInfo->fFlags & MNF_UNDERLINE))?ODS_NOACCEL:0;
1051 //drawItem.itemState |= (MenuInfo->fFlags & MNF_INACTIVE)?ODS_INACTIVE:0;
1052 drawItem.hwndItem = (HWND)MenuInfo->Self;
1053 drawItem.hDC = hdc;
1054 drawItem.rcItem = *rect;
1055 drawItem.itemData = lpitem->dwItemData;
1056 /* some applications make this assumption on the DC's origin */
1057 SetViewportOrgEx( hdc, lpitem->Rect.left, lpitem->Rect.top, &origorg);
1058 OffsetRect( &drawItem.rcItem, - lpitem->Rect.left, - lpitem->Rect.top);
1059 SendMessageW( WndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
1060 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1061 return;
1062 }
1063 break;
1064
1065 case (INT_PTR) HBMMENU_POPUP_CLOSE:
1066 case (INT_PTR) HBMMENU_POPUP_RESTORE:
1067 case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
1068 case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
1069 MenuDrawPopupGlyph(hdc, &r, (INT_PTR)hbmToDraw, lpitem->fState & MF_GRAYED, lpitem->fState & MF_HILITE);
1070 return;
1071 }
1072 InflateRect(&r, -1, -1);
1073 if (0 != (lpitem->fState & MF_HILITE))
1074 {
1075 flags |= DFCS_PUSHED;
1076 }
1077 DrawFrameControl(hdc, &r, DFC_CAPTION, flags);
1078 return;
1079 }
1080
1081 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
1082
1083 got_bitmap:
1084 hdcMem = CreateCompatibleDC( hdc );
1085 SelectObject( hdcMem, bmp );
1086
1087 /* handle fontsize > bitmap_height */
1088 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
1089 left=rect->left;
1090 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
1091 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
1092 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1093 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
1094 DeleteDC( hdcMem );
1095 }
1096
1097 /***********************************************************************
1098 * MenuCalcItemSize
1099 *
1100 * Calculate the size of the menu item and store it in lpitem->rect.
1101 */
1102 static void FASTCALL MenuCalcItemSize( HDC hdc, PROSMENUITEMINFO lpitem, PROSMENUINFO MenuInfo, HWND hwndOwner,
1103 INT orgX, INT orgY, BOOL menuBar, BOOL textandbmp)
1104 {
1105 WCHAR *p;
1106 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1107 UINT arrow_bitmap_width;
1108 BITMAP bm;
1109 INT itemheight = 0;
1110
1111 TRACE("dc=%x owner=%x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1112
1113 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1114 arrow_bitmap_width = bm.bmWidth;
1115
1116 MenuCharSize.cx = GdiGetCharDimensions( hdc, NULL, &MenuCharSize.cy );
1117
1118 SetRect( &lpitem->Rect, orgX, orgY, orgX, orgY );
1119
1120 if (lpitem->fType & MF_OWNERDRAW)
1121 {
1122 MEASUREITEMSTRUCT mis;
1123 mis.CtlType = ODT_MENU;
1124 mis.CtlID = 0;
1125 mis.itemID = lpitem->wID;
1126 mis.itemData = lpitem->dwItemData;
1127 mis.itemHeight = HIWORD( GetDialogBaseUnits());
1128 mis.itemWidth = 0;
1129 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1130 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1131 * width of a menufont character to the width of an owner-drawn menu.
1132 */
1133 lpitem->Rect.right += mis.itemWidth + 2 * MenuCharSize.cx;
1134 if (menuBar) {
1135 /* under at least win95 you seem to be given a standard
1136 height for the menu and the height value is ignored */
1137 lpitem->Rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1138 } else
1139 lpitem->Rect.bottom += mis.itemHeight;
1140 // Or this,
1141 //Item->cxBmp = mis.itemWidth;
1142 //Item->cyBmp = mis.itemHeight;
1143 TRACE("id=%04lx size=%dx%d\n",
1144 lpitem->wID, lpitem->Rect.right-lpitem->Rect.left,
1145 lpitem->Rect.bottom-lpitem->Rect.top);
1146 return;
1147 }
1148
1149 if (lpitem->fType & MF_SEPARATOR)
1150 {
1151 lpitem->Rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;//SEPARATOR_HEIGHT;
1152 if( !menuBar)
1153 lpitem->Rect.right += arrow_bitmap_width/*check_bitmap_width*/ + MenuCharSize.cx;
1154 return;
1155 }
1156
1157 lpitem->dxTab = 0;
1158
1159 if (lpitem->hbmpItem)
1160 {
1161 SIZE size;
1162
1163 if (!menuBar) {
1164 MenuGetBitmapItemSize(lpitem, &size, hwndOwner );
1165 /* Keep the size of the bitmap in callback mode to be able
1166 * to draw it correctly */
1167 lpitem->maxBmpSize = size;
1168 MenuInfo->cxTextAlign = max(MenuInfo->cxTextAlign, size.cx);
1169 MenuSetRosMenuInfo(MenuInfo);
1170 lpitem->Rect.right += size.cx + 2;
1171 itemheight = size.cy + 2;
1172
1173 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
1174 lpitem->Rect.right += 2 * check_bitmap_width;
1175 lpitem->Rect.right += 4 + MenuCharSize.cx;
1176 lpitem->dxTab = lpitem->Rect.right;
1177 lpitem->Rect.right += arrow_bitmap_width;//check_bitmap_width;
1178 } else /* hbmpItem & MenuBar */ {
1179 MenuGetBitmapItemSize(lpitem, &size, hwndOwner );
1180 lpitem->Rect.right += size.cx;
1181 if( lpitem->lpstr) lpitem->Rect.right += 2;
1182 itemheight = size.cy;
1183
1184 /* Special case: Minimize button doesn't have a space behind it. */
1185 if (lpitem->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE ||
1186 lpitem->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE_D)
1187 lpitem->Rect.right -= 1;
1188 }
1189 }
1190 else if (!menuBar) {
1191 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
1192 lpitem->Rect.right += check_bitmap_width;
1193 lpitem->Rect.right += 4 + MenuCharSize.cx;
1194 lpitem->dxTab = lpitem->Rect.right;
1195 lpitem->Rect.right += check_bitmap_width;
1196 }
1197
1198 /* it must be a text item - unless it's the system menu */
1199 if (!(lpitem->fType & MF_SYSMENU) && lpitem->lpstr) {
1200 HFONT hfontOld = NULL;
1201 RECT rc = lpitem->Rect;
1202 LONG txtheight, txtwidth;
1203
1204 if ( lpitem->fState & MFS_DEFAULT ) {
1205 hfontOld = SelectObject( hdc, hMenuFontBold );
1206 }
1207 if (menuBar) {
1208 txtheight = DrawTextW( hdc, lpitem->dwTypeData, -1, &rc,
1209 DT_SINGLELINE|DT_CALCRECT);
1210 lpitem->Rect.right += rc.right - rc.left;
1211 itemheight = max( max( itemheight, txtheight),
1212 GetSystemMetrics( SM_CYMENU) - 1);
1213 lpitem->Rect.right += 2 * MenuCharSize.cx;
1214 } else {
1215 if ((p = strchrW( lpitem->dwTypeData, '\t' )) != NULL) {
1216 RECT tmprc = rc;
1217 LONG tmpheight;
1218 int n = (int)( p - lpitem->dwTypeData);
1219 /* Item contains a tab (only meaningful in popup menus) */
1220 /* get text size before the tab */
1221 txtheight = DrawTextW( hdc, lpitem->dwTypeData, n, &rc,
1222 DT_SINGLELINE|DT_CALCRECT);
1223 txtwidth = rc.right - rc.left;
1224 p += 1; /* advance past the Tab */
1225 /* get text size after the tab */
1226 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1227 DT_SINGLELINE|DT_CALCRECT);
1228 lpitem->dxTab += txtwidth;
1229 txtheight = max( txtheight, tmpheight);
1230 txtwidth += MenuCharSize.cx + /* space for the tab */
1231 tmprc.right - tmprc.left; /* space for the short cut */
1232 } else {
1233 txtheight = DrawTextW( hdc, lpitem->dwTypeData, -1, &rc,
1234 DT_SINGLELINE|DT_CALCRECT);
1235 txtwidth = rc.right - rc.left;
1236 lpitem->dxTab += txtwidth;
1237 }
1238 lpitem->Rect.right += 2 + txtwidth;
1239 itemheight = max( itemheight,
1240 max( txtheight + 2, MenuCharSize.cy + 4));
1241 }
1242 if (hfontOld) SelectObject (hdc, hfontOld);
1243 } else if( menuBar) {
1244 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1245 }
1246 lpitem->Rect.bottom += itemheight;
1247 TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->Rect.left, lpitem->Rect.top, lpitem->Rect.right, lpitem->Rect.bottom);
1248 }
1249
1250 /***********************************************************************
1251 * MENU_GetMaxPopupHeight
1252 */
1253 static UINT
1254 MENU_GetMaxPopupHeight(PROSMENUINFO lppop)
1255 {
1256 if (lppop->cyMax)
1257 return lppop->cyMax;
1258 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1259 }
1260
1261 /***********************************************************************
1262 * MenuPopupMenuCalcSize
1263 *
1264 * Calculate the size of a popup menu.
1265 */
1266 static void FASTCALL MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo, HWND WndOwner)
1267 {
1268 ROSMENUITEMINFO lpitem;
1269 HDC hdc;
1270 int start, i;
1271 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1272 BOOL textandbmp = FALSE;
1273
1274 MenuInfo->cxMenu = MenuInfo->cyMenu = 0;
1275 if (MenuInfo->cItems == 0)
1276 {
1277 MenuSetRosMenuInfo(MenuInfo);
1278 return;
1279 }
1280
1281 hdc = GetDC(NULL);
1282 SelectObject( hdc, hMenuFont );
1283
1284 start = 0;
1285 maxX = 2 + 1;
1286
1287 MenuInfo->cxTextAlign = 0;
1288
1289 MenuInitRosMenuItemInfo(&lpitem);
1290 while (start < MenuInfo->cItems)
1291 {
1292 orgX = maxX;
1293 orgY = 2;
1294
1295 maxTab = maxTabWidth = 0;
1296
1297 /* Parse items until column break or end of menu */
1298 for (i = start; i < MenuInfo->cItems; i++)
1299 {
1300 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i, &lpitem))
1301 {
1302 MenuCleanupRosMenuItemInfo(&lpitem);
1303 MenuSetRosMenuInfo(MenuInfo);
1304 return;
1305 }
1306 if (i != start &&
1307 (lpitem.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1308
1309 if( lpitem.lpstr && lpitem.hbmpItem) textandbmp = TRUE;
1310
1311 MenuCalcItemSize(hdc, &lpitem, MenuInfo, WndOwner, orgX, orgY, FALSE, textandbmp);
1312 if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &lpitem))
1313 {
1314 MenuCleanupRosMenuItemInfo(&lpitem);
1315 MenuSetRosMenuInfo(MenuInfo);
1316 return;
1317 }
1318 // Not sure here,, The patch from wine removes this.
1319 // if ((lpitem.fType & MF_MENUBARBREAK) != 0)
1320 // {
1321 // OrgX++;
1322 // }
1323 maxX = max(maxX, lpitem.Rect.right);
1324 orgY = lpitem.Rect.bottom;
1325 if ((lpitem.lpstr) && lpitem.dxTab )
1326 {
1327 maxTab = max( maxTab, lpitem.dxTab );
1328 maxTabWidth = max(maxTabWidth, lpitem.Rect.right - lpitem.dxTab);
1329 }
1330 }
1331
1332 /* Finish the column (set all items to the largest width found) */
1333 maxX = max( maxX, maxTab + maxTabWidth );
1334 while (start < i)
1335 {
1336 if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &lpitem))
1337 {
1338 lpitem.Rect.right = maxX;
1339 if ((lpitem.lpstr) && 0 != lpitem.dxTab)
1340 {
1341 lpitem.dxTab = maxTab;
1342 }
1343 MenuSetRosMenuItemInfo(MenuInfo->Self, start, &lpitem);
1344 }
1345 start++;
1346 }
1347 MenuInfo->cyMenu = max(MenuInfo->cyMenu, orgY);
1348 }
1349
1350 MenuInfo->cxMenu = maxX;
1351 /* if none of the items have both text and bitmap then
1352 * the text and bitmaps are all aligned on the left. If there is at
1353 * least one item with both text and bitmap then bitmaps are
1354 * on the left and texts left aligned with the right hand side
1355 * of the bitmaps */
1356 if( !textandbmp) MenuInfo->cxTextAlign = 0;
1357
1358 /* space for 3d border */
1359 MenuInfo->cyMenu += MENU_BOTTOM_MARGIN;
1360 MenuInfo->cxMenu += 2;
1361
1362 /* Adjust popup height if it exceeds maximum */
1363 maxHeight = MENU_GetMaxPopupHeight(MenuInfo);
1364 MenuInfo->iMaxTop = MenuInfo->cyMenu - MENU_TOP_MARGIN;
1365 if (MenuInfo->cyMenu >= maxHeight)
1366 {
1367 MenuInfo->cyMenu = maxHeight;
1368 MenuInfo->dwArrowsOn = 1;
1369 }
1370 else
1371 {
1372 MenuInfo->dwArrowsOn = 0;
1373 }
1374
1375 MenuCleanupRosMenuItemInfo(&lpitem);
1376 MenuSetRosMenuInfo(MenuInfo);
1377 ReleaseDC( 0, hdc );
1378 }
1379
1380 /***********************************************************************
1381 * MenuMenuBarCalcSize
1382 *
1383 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1384 * height is off by 1 pixel which causes lengthy window relocations when
1385 * active document window is maximized/restored.
1386 *
1387 * Calculate the size of the menu bar.
1388 */
1389 static void FASTCALL MenuMenuBarCalcSize( HDC hdc, LPRECT lprect,
1390 PROSMENUINFO MenuInfo, HWND hwndOwner )
1391 {
1392 ROSMENUITEMINFO ItemInfo;
1393 int start, i, orgX, orgY, maxY, helpPos;
1394
1395 if ((lprect == NULL) || (MenuInfo == NULL)) return;
1396 if (MenuInfo->cItems == 0) return;
1397 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1398 MenuInfo->cxMenu = lprect->right - lprect->left;
1399 MenuInfo->cyMenu = 0;
1400 maxY = lprect->top + 1;
1401 start = 0;
1402 helpPos = -1;
1403
1404 MenuInfo->cxTextAlign = 0;
1405
1406 MenuInitRosMenuItemInfo(&ItemInfo);
1407 while (start < MenuInfo->cItems)
1408 {
1409 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo))
1410 {
1411 MenuCleanupRosMenuItemInfo(&ItemInfo);
1412 return;
1413 }
1414 orgX = lprect->left;
1415 orgY = maxY;
1416
1417 /* Parse items until line break or end of menu */
1418 for (i = start; i < MenuInfo->cItems; i++)
1419 {
1420 if ((helpPos == -1) && (ItemInfo.fType & MF_RIGHTJUSTIFY)) helpPos = i;
1421 if ((i != start) &&
1422 (ItemInfo.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1423
1424 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY);
1425 MenuCalcItemSize(hdc, &ItemInfo, MenuInfo, hwndOwner, orgX, orgY, TRUE, FALSE);
1426 if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo))
1427 {
1428 MenuCleanupRosMenuItemInfo(&ItemInfo);
1429 return;
1430 }
1431
1432 if (ItemInfo.Rect.right > lprect->right)
1433 {
1434 if (i != start) break;
1435 else ItemInfo.Rect.right = lprect->right;
1436 }
1437 maxY = max( maxY, ItemInfo.Rect.bottom );
1438 orgX = ItemInfo.Rect.right;
1439 if (i + 1 < MenuInfo->cItems)
1440 {
1441 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i + 1, &ItemInfo))
1442 {
1443 MenuCleanupRosMenuItemInfo(&ItemInfo);
1444 return;
1445 }
1446 }
1447 }
1448
1449 /* FIXME: Is this really needed? */ /*NO! it is not needed, why make the
1450 HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
1451 #if 0
1452 /* Finish the line (set all items to the largest height found) */
1453 while (start < i)
1454 {
1455 if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo))
1456 {
1457 ItemInfo.Rect.bottom = maxY;
1458 MenuSetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo);
1459 }
1460 start++;
1461 }
1462 #else
1463 start = i; /* This works! */
1464 #endif
1465 }
1466
1467 lprect->bottom = maxY;
1468 MenuInfo->cyMenu = lprect->bottom - lprect->top;
1469 MenuSetRosMenuInfo(MenuInfo);
1470
1471 if (helpPos != -1)
1472 {
1473 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1474 /* the last item (if several lines, only move the last line) */
1475 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->cItems - 1, &ItemInfo))
1476 {
1477 MenuCleanupRosMenuItemInfo(&ItemInfo);
1478 return;
1479 }
1480 orgY = ItemInfo.Rect.top;
1481 orgX = lprect->right;
1482 for (i = MenuInfo->cItems - 1; helpPos <= i; i--)
1483 {
1484 if (i < helpPos)
1485 {
1486 break; /* done */
1487 }
1488 if (ItemInfo.Rect.top != orgY)
1489 {
1490 break; /* Other line */
1491 }
1492 if (orgX <= ItemInfo.Rect.right)
1493 {
1494 break; /* Too far right already */
1495 }
1496 ItemInfo.Rect.left += orgX - ItemInfo.Rect.right;
1497 ItemInfo.Rect.right = orgX;
1498 orgX = ItemInfo.Rect.left;
1499 MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo);
1500 if (helpPos + 1 <= i &&
1501 ! MenuGetRosMenuItemInfo(MenuInfo->Self, i - 1, &ItemInfo))
1502 {
1503 MenuCleanupRosMenuItemInfo(&ItemInfo);
1504 return;
1505 }
1506 }
1507 }
1508
1509 MenuCleanupRosMenuItemInfo(&ItemInfo);
1510 }
1511
1512 /***********************************************************************
1513 * MENU_DrawScrollArrows
1514 *
1515 * Draw scroll arrows.
1516 */
1517 static void
1518 MENU_DrawScrollArrows(PROSMENUINFO lppop, HDC hdc)
1519 {
1520 HDC hdcMem = CreateCompatibleDC(hdc);
1521 HBITMAP hOrigBitmap;
1522 UINT arrow_bitmap_width, arrow_bitmap_height;
1523 BITMAP bmp;
1524 RECT rect;
1525
1526 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1527 arrow_bitmap_width = bmp.bmWidth;
1528 arrow_bitmap_height = bmp.bmHeight;
1529
1530
1531 if (lppop->iTop)
1532 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1533 else
1534 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1535 rect.left = 0;
1536 rect.top = 0;
1537 rect.right = lppop->cxMenu;
1538 rect.bottom = arrow_bitmap_height;
1539 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1540 BitBlt(hdc, (lppop->cxMenu - arrow_bitmap_width) / 2, 0,
1541 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1542 rect.top = lppop->cyMenu - arrow_bitmap_height;
1543 rect.bottom = lppop->cyMenu;
1544 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1545 if (lppop->iTop < lppop->iMaxTop - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1546 SelectObject(hdcMem, get_down_arrow_bitmap());
1547 else
1548 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1549 BitBlt(hdc, (lppop->cxMenu - arrow_bitmap_width) / 2,
1550 lppop->cyMenu - arrow_bitmap_height,
1551 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1552 SelectObject(hdcMem, hOrigBitmap);
1553 DeleteDC(hdcMem);
1554 }
1555
1556 /***********************************************************************
1557 * MenuDrawMenuItem
1558 *
1559 * Draw a single menu item.
1560 */
1561 static void FASTCALL MenuDrawMenuItem(HWND hWnd, PROSMENUINFO MenuInfo, HWND WndOwner, HDC hdc,
1562 PROSMENUITEMINFO lpitem, UINT Height, BOOL menuBar, UINT odaction)
1563 {
1564 RECT rect;
1565 PWCHAR Text;
1566 BOOL flat_menu = FALSE;
1567 int bkgnd;
1568 PWND Wnd = ValidateHwndNoErr(hWnd);
1569
1570 if (!Wnd)
1571 return;
1572
1573 if (lpitem->fType & MF_SYSMENU)
1574 {
1575 if ( (Wnd->style & WS_MINIMIZE))
1576 {
1577 UserGetInsideRectNC(Wnd, &rect);
1578 UserDrawSysMenuButton(hWnd, hdc, &rect, lpitem->fState & (MF_HILITE | MF_MOUSESELECT));
1579 }
1580 return;
1581 }
1582
1583 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1584 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1585
1586 /* Setup colors */
1587
1588 if (lpitem->fState & MF_HILITE)
1589 {
1590 if(menuBar && !flat_menu) {
1591 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1592 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1593 } else {
1594 if (lpitem->fState & MF_GRAYED)
1595 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1596 else
1597 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1598 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1599 }
1600 }
1601 else
1602 {
1603 if (lpitem->fState & MF_GRAYED)
1604 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1605 else
1606 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1607 SetBkColor( hdc, GetSysColor( bkgnd ) );
1608 }
1609
1610 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->Rect));
1611 rect = lpitem->Rect;
1612 MENU_AdjustMenuItemRect(MenuInfo, &rect);
1613
1614 if (lpitem->fType & MF_OWNERDRAW)
1615 {
1616 /*
1617 ** Experimentation under Windows reveals that an owner-drawn
1618 ** menu is given the rectangle which includes the space it requested
1619 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1620 ** and a popup-menu arrow. This is the value of lpitem->rect.
1621 ** Windows will leave all drawing to the application except for
1622 ** the popup-menu arrow. Windows always draws that itself, after
1623 ** the menu owner has finished drawing.
1624 */
1625 DRAWITEMSTRUCT dis;
1626
1627 dis.CtlType = ODT_MENU;
1628 dis.CtlID = 0;
1629 dis.itemID = lpitem->wID;
1630 dis.itemData = (DWORD)lpitem->dwItemData;
1631 dis.itemState = 0;
1632 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1633 if (lpitem->fState & MF_DEFAULT) dis.itemState |= ODS_DEFAULT;
1634 if (lpitem->fState & MF_DISABLED) dis.itemState |= ODS_DISABLED;
1635 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED | ODS_DISABLED;
1636 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1637 //if (!(MenuInfo->fFlags & MNF_UNDERLINE)) dis.itemState |= ODS_NOACCEL;
1638 //if (MenuInfo->fFlags & MNF_INACTIVE) dis.itemState |= ODS_INACTIVE;
1639 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1640 dis.hwndItem = (HWND) MenuInfo->Self;
1641 dis.hDC = hdc;
1642 dis.rcItem = rect;
1643 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1644 "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hWnd,
1645 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1646 dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
1647 dis.rcItem.bottom);
1648 SendMessageW(WndOwner, WM_DRAWITEM, 0, (LPARAM) &dis);
1649 /* Draw the popup-menu arrow */
1650 if (lpitem->hSubMenu)
1651 {
1652 RECT rectTemp;
1653 CopyRect(&rectTemp, &rect);
1654 rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK);
1655 DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
1656 }
1657 return;
1658 }
1659
1660 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1661
1662 if (lpitem->fState & MF_HILITE)
1663 {
1664 if (flat_menu)
1665 {
1666 InflateRect (&rect, -1, -1);
1667 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1668 InflateRect (&rect, 1, 1);
1669 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1670 }
1671 else
1672 {
1673 if(menuBar)
1674 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1675 else
1676 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1677 }
1678 }
1679 else
1680 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1681
1682 SetBkMode( hdc, TRANSPARENT );
1683
1684 /* vertical separator */
1685 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1686 {
1687 HPEN oldPen;
1688 RECT rc = rect;
1689
1690 rc.left -= 3;
1691 rc.top = 3;
1692 rc.bottom = Height - 3;
1693 if (flat_menu)
1694 {
1695 oldPen = SelectObject( hdc, GetStockObject(DC_PEN) );
1696 SetDCPenColor(hdc, GetSysColor(COLOR_BTNSHADOW));
1697 MoveToEx( hdc, rc.left, rc.top, NULL );
1698 LineTo( hdc, rc.left, rc.bottom );
1699 SelectObject( hdc, oldPen );
1700 }
1701 else
1702 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1703 }
1704
1705 /* horizontal separator */
1706 if (lpitem->fType & MF_SEPARATOR)
1707 {
1708 HPEN oldPen;
1709 RECT rc = rect;
1710
1711 rc.left++;
1712 rc.right--;
1713 rc.top += SEPARATOR_HEIGHT / 2;
1714 if (flat_menu)
1715 {
1716 oldPen = SelectObject( hdc, GetStockObject(DC_PEN) );
1717 SetDCPenColor( hdc, GetSysColor(COLOR_BTNSHADOW));
1718 MoveToEx( hdc, rc.left, rc.top, NULL );
1719 LineTo( hdc, rc.right, rc.top );
1720 SelectObject( hdc, oldPen );
1721 }
1722 else
1723 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1724 return;
1725 }
1726
1727 #if 0
1728 /* helper lines for debugging */
1729 /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
1730 FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1731 SelectObject(hdc, GetStockObject(DC_PEN));
1732 SetDCPenColor(hdc, GetSysColor(COLOR_WINDOWFRAME));
1733 MoveToEx(hdc, rect.left, (rect.top + rect.bottom) / 2, NULL);
1734 LineTo(hdc, rect.right, (rect.top + rect.bottom) / 2);
1735 #endif
1736
1737 if (!menuBar)
1738 {
1739 HBITMAP bm;
1740 INT y = rect.top + rect.bottom;
1741 RECT rc = rect;
1742 BOOL checked = FALSE;
1743 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1744 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1745 /* Draw the check mark
1746 *
1747 * FIXME:
1748 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1749 */
1750 if( !(MenuInfo->dwStyle & MNS_NOCHECK)) {
1751 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hbmpChecked :
1752 lpitem->hbmpUnchecked;
1753 if (bm) /* we have a custom bitmap */
1754 {
1755 HDC hdcMem = CreateCompatibleDC( hdc );
1756
1757 SelectObject( hdcMem, bm );
1758 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1759 check_bitmap_width, check_bitmap_height,
1760 hdcMem, 0, 0, SRCCOPY );
1761 DeleteDC( hdcMem );
1762 checked = TRUE;
1763 }
1764 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1765 {
1766 RECT r;
1767 CopyRect(&r, &rect);
1768 r.right = r.left + GetSystemMetrics(SM_CXMENUCHECK);
1769 DrawFrameControl( hdc, &r, DFC_MENU,
1770 (lpitem->fType & MFT_RADIOCHECK) ?
1771 DFCS_MENUBULLET : DFCS_MENUCHECK);
1772 checked = TRUE;
1773 }
1774 }
1775 if ( lpitem->hbmpItem )
1776 {
1777 RECT bmpRect;
1778 CopyRect(&bmpRect, &rect);
1779 if (!(MenuInfo->dwStyle & MNS_CHECKORBMP) && !(MenuInfo->dwStyle & MNS_NOCHECK))
1780 bmpRect.left += check_bitmap_width + 2;
1781 if (!(checked && (MenuInfo->dwStyle & MNS_CHECKORBMP)))
1782 {
1783 bmpRect.right = bmpRect.left + lpitem->maxBmpSize.cx;
1784 MenuDrawBitmapItem(hdc, lpitem, &bmpRect, MenuInfo, WndOwner, odaction, menuBar);
1785 }
1786 }
1787 /* Draw the popup-menu arrow */
1788 if (lpitem->hSubMenu)
1789 {
1790 RECT rectTemp;
1791 CopyRect(&rectTemp, &rect);
1792 rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK);
1793 DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
1794 }
1795 rect.left += 4;
1796 if( !(MenuInfo->dwStyle & MNS_NOCHECK))
1797 rect.left += check_bitmap_width;
1798 rect.right -= check_bitmap_width;
1799 }
1800 else if( lpitem->hbmpItem)
1801 { /* Draw the bitmap */
1802 MenuDrawBitmapItem(hdc, lpitem, &rect, MenuInfo, WndOwner, odaction, menuBar);
1803 }
1804
1805 /* process text if present */
1806 if (lpitem->lpstr)
1807 {
1808 register int i = 0;
1809 HFONT hfontOld = 0;
1810
1811 UINT uFormat = menuBar ?
1812 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1813 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1814
1815 if((MenuInfo->dwStyle & MNS_CHECKORBMP))
1816 rect.left += max(0, (int)(MenuInfo->cxTextAlign - GetSystemMetrics(SM_CXMENUCHECK)));
1817 else
1818 rect.left += MenuInfo->cxTextAlign;
1819
1820 if ( lpitem->fState & MFS_DEFAULT )
1821 {
1822 hfontOld = SelectObject(hdc, hMenuFontBold);
1823 }
1824
1825 if (menuBar) {
1826 rect.left += MENU_BAR_ITEMS_SPACE / 2;
1827 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
1828 }
1829
1830 Text = (PWCHAR) lpitem->dwTypeData;
1831 if(Text)
1832 {
1833 for (i = 0; L'\0' != Text[i]; i++)
1834 if (Text[i] == L'\t' || Text[i] == L'\b')
1835 break;
1836 }
1837
1838 if(lpitem->fState & MF_GRAYED)
1839 {
1840 if (!(lpitem->fState & MF_HILITE) )
1841 {
1842 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1843 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1844 DrawTextW( hdc, Text, i, &rect, uFormat );
1845 --rect.left; --rect.top; --rect.right; --rect.bottom;
1846 }
1847 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1848 }
1849
1850 DrawTextW( hdc, Text, i, &rect, uFormat);
1851
1852 /* paint the shortcut text */
1853 if (!menuBar && L'\0' != Text[i]) /* There's a tab or flush-right char */
1854 {
1855 if (L'\t' == Text[i])
1856 {
1857 rect.left = lpitem->dxTab;
1858 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1859 }
1860 else
1861 {
1862 rect.right = lpitem->dxTab;
1863 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1864 }
1865
1866 if (lpitem->fState & MF_GRAYED)
1867 {
1868 if (!(lpitem->fState & MF_HILITE) )
1869 {
1870 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1871 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1872 DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat);
1873 --rect.left; --rect.top; --rect.right; --rect.bottom;
1874 }
1875 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1876 }
1877 DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat );
1878 }
1879
1880 if (hfontOld)
1881 SelectObject (hdc, hfontOld);
1882 }
1883 }
1884
1885 /***********************************************************************
1886 * MenuDrawPopupMenu
1887 *
1888 * Paint a popup menu.
1889 */
1890 static void FASTCALL MenuDrawPopupMenu(HWND hwnd, HDC hdc, HMENU hmenu )
1891 {
1892 HBRUSH hPrevBrush = 0;
1893 RECT rect;
1894
1895 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1896
1897 GetClientRect( hwnd, &rect );
1898
1899 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1900 && (SelectObject( hdc, hMenuFont)))
1901 {
1902 HPEN hPrevPen;
1903
1904 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1905
1906 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1907 if ( hPrevPen )
1908 {
1909 BOOL flat_menu = FALSE;
1910 ROSMENUINFO MenuInfo;
1911 ROSMENUITEMINFO ItemInfo;
1912
1913 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1914 if (flat_menu)
1915 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1916 else
1917 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1918
1919 /* draw menu items */
1920 //TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1921 if (MenuGetRosMenuInfo(&MenuInfo, hmenu) && MenuInfo.cItems)
1922 {
1923 UINT u;
1924 MenuInitRosMenuItemInfo(&ItemInfo);
1925
1926 for (u = 0; u < MenuInfo.cItems; u++)
1927 {
1928 if (MenuGetRosMenuItemInfo(MenuInfo.Self, u, &ItemInfo))
1929 {
1930 HWND WndOwner = MenuInfo.spwndNotify ? MenuInfo.spwndNotify->head.h : NULL;
1931 MenuDrawMenuItem(hwnd, &MenuInfo, WndOwner, hdc, &ItemInfo,
1932 MenuInfo.cyMenu, FALSE, ODA_DRAWENTIRE);
1933 }
1934 }
1935
1936 /* draw scroll arrows */
1937 if (MenuInfo.dwArrowsOn)
1938 MENU_DrawScrollArrows(&MenuInfo, hdc);
1939
1940 MenuSetRosMenuInfo(&MenuInfo);
1941 MenuCleanupRosMenuItemInfo(&ItemInfo);
1942 }
1943 } else
1944 {
1945 SelectObject( hdc, hPrevBrush );
1946 }
1947 }
1948 }
1949
1950 /***********************************************************************
1951 * MenuDrawMenuBar
1952 *
1953 * Paint a menu bar. Returns the height of the menu bar.
1954 * called from [windows/nonclient.c]
1955 */
1956 UINT MenuDrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1957 BOOL suppress_draw)
1958 {
1959 ROSMENUINFO lppop;
1960 HFONT hfontOld = 0;
1961 HMENU hMenu = GetMenu(hwnd);
1962
1963 if (! MenuGetRosMenuInfo(&lppop, hMenu) || lprect == NULL)
1964 {
1965 return GetSystemMetrics(SM_CYMENU);
1966 }
1967
1968 if (suppress_draw)
1969 {
1970 hfontOld = SelectObject(hDC, hMenuFont);
1971
1972 MenuMenuBarCalcSize(hDC, lprect, &lppop, hwnd);
1973
1974 lprect->bottom = lprect->top + lppop.cyMenu;
1975
1976 if (hfontOld) SelectObject( hDC, hfontOld);
1977 return lppop.cyMenu;
1978 }
1979 else
1980 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1981 }
1982
1983 /***********************************************************************
1984 * MENU_InitPopup
1985 *
1986 * Popup menu initialization before WM_ENTERMENULOOP.
1987 */
1988 static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags )
1989 {
1990 MENU *menu;
1991 DWORD ex_style = 0;
1992 ROSMENUINFO MenuInfo;
1993
1994 TRACE("owner=%p hmenu=%p\n", hwndOwner, hmenu);
1995
1996 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1997
1998 /* store the owner for DrawItem */
1999 if (!IsWindow( hwndOwner ))
2000 {
2001 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
2002 return FALSE;
2003 }
2004 MenuGetRosMenuInfo(&MenuInfo, menu->head.h);
2005 //menu->hwndOwner = hwndOwner;
2006 MenuInfo.spwndNotify = ValidateHwndNoErr( hwndOwner );
2007
2008 if (flags & TPM_LAYOUTRTL)
2009 ex_style = WS_EX_LAYOUTRTL;
2010
2011 /* NOTE: In Windows, top menu popup is not owned. */
2012 //menu->hWnd = CreateWindowExW( ex_style, WC_MENU, NULL,
2013 MenuInfo.Wnd = CreateWindowExW( ex_style, WC_MENU, NULL,
2014 WS_POPUP, 0, 0, 0, 0,
2015 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
2016 (LPVOID)hmenu );
2017 MenuSetRosMenuInfo(&MenuInfo);
2018 if( !menu->hWnd ) return FALSE;
2019 return TRUE;
2020 }
2021
2022 /***********************************************************************
2023 * MenuShowPopup
2024 *
2025 * Display a popup menu.
2026 */
2027 static BOOL FASTCALL MenuShowPopup(HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
2028 INT x, INT y, INT xanchor, INT yanchor )
2029 {
2030 ROSMENUINFO MenuInfo;
2031 ROSMENUITEMINFO ItemInfo;
2032 UINT width, height;
2033 POINT pt;
2034 HMONITOR monitor;
2035 MONITORINFO info;
2036
2037 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
2038 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
2039
2040 if (! MenuGetRosMenuInfo(&MenuInfo, hmenu)) return FALSE;
2041 if (MenuInfo.iItem != NO_SELECTED_ITEM)
2042 {
2043 MenuInitRosMenuItemInfo(&ItemInfo);
2044 if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.iItem, &ItemInfo))
2045 {
2046 ItemInfo.fMask |= MIIM_STATE;
2047 ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
2048 MenuSetRosMenuItemInfo(MenuInfo.Self, MenuInfo.iItem, &ItemInfo);
2049 }
2050 MenuCleanupRosMenuItemInfo(&ItemInfo);
2051 MenuInfo.iItem = NO_SELECTED_ITEM;
2052 }
2053
2054 //menu->dwArrowsOn = 0;
2055 MenuInfo.dwArrowsOn = 0;
2056 MenuSetRosMenuInfo(&MenuInfo);
2057 MenuPopupMenuCalcSize(&MenuInfo, hwndOwner);
2058
2059 /* adjust popup menu pos so that it fits within the desktop */
2060
2061 width = MenuInfo.cxMenu + GetSystemMetrics(SM_CXBORDER);
2062 height = MenuInfo.cyMenu + GetSystemMetrics(SM_CYBORDER);
2063
2064 /* FIXME: should use item rect */
2065 pt.x = x;
2066 pt.y = y;
2067 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
2068 info.cbSize = sizeof(info);
2069 GetMonitorInfoW( monitor, &info );
2070
2071 if (flags & TPM_LAYOUTRTL)
2072 flags ^= TPM_RIGHTALIGN;
2073
2074 if( flags & TPM_RIGHTALIGN ) x -= width;
2075 if( flags & TPM_CENTERALIGN ) x -= width / 2;
2076
2077 if( flags & TPM_BOTTOMALIGN ) y -= height;
2078 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
2079
2080 if( x + width > info.rcMonitor.right)
2081 {
2082 if( xanchor && x >= width - xanchor )
2083 x -= width - xanchor;
2084
2085 if( x + width > info.rcMonitor.right)
2086 x = info.rcMonitor.right - width;
2087 }
2088 if( x < info.rcMonitor.left ) x = info.rcMonitor.left;
2089
2090 if( y + height > info.rcMonitor.bottom)
2091 {
2092 if( yanchor && y >= height + yanchor )
2093 y -= height + yanchor;
2094
2095 if( y + height > info.rcMonitor.bottom)
2096 y = info.rcMonitor.bottom - height;
2097 }
2098 if( y < info.rcMonitor.top ) y = info.rcMonitor.top;
2099
2100 if (!top_popup) {
2101 top_popup = MenuInfo.Wnd;
2102 top_popup_hmenu = hmenu;
2103 }
2104 /* Display the window */
2105
2106 SetWindowPos( MenuInfo.Wnd, HWND_TOPMOST, x, y, width, height,
2107 SWP_SHOWWINDOW | SWP_NOACTIVATE);
2108 UpdateWindow( MenuInfo.Wnd );
2109
2110 IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0);
2111
2112 return TRUE;
2113 }
2114
2115 /***********************************************************************
2116 * MENU_EnsureMenuItemVisible
2117 */
2118 void
2119 MENU_EnsureMenuItemVisible(PROSMENUINFO lppop, PROSMENUITEMINFO item, HDC hdc)
2120 {
2121 if (lppop->dwArrowsOn)
2122 {
2123 //ITEM *item = &lppop->items[wIndex];
2124 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
2125 UINT nOldPos = lppop->iTop;
2126 RECT rc;
2127 UINT arrow_bitmap_height;
2128 BITMAP bmp;
2129
2130 GetClientRect(lppop->Wnd, &rc);
2131
2132 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
2133 arrow_bitmap_height = bmp.bmHeight;
2134
2135 rc.top += arrow_bitmap_height;
2136 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
2137
2138 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
2139 if (item->Rect.bottom > lppop->iTop + nMaxHeight)
2140 {
2141 lppop->iTop = item->Rect.bottom - nMaxHeight;
2142 ScrollWindow(lppop->Wnd, 0, nOldPos - lppop->iTop, &rc, &rc);
2143 MENU_DrawScrollArrows(lppop, hdc);
2144 }
2145 else if (item->Rect.top - MENU_TOP_MARGIN < lppop->iTop)
2146 {
2147 lppop->iTop = item->Rect.top - MENU_TOP_MARGIN;
2148 ScrollWindow(lppop->Wnd, 0, nOldPos - lppop->iTop, &rc, &rc);
2149 MENU_DrawScrollArrows(lppop, hdc);
2150 }
2151 }
2152 }
2153
2154 /***********************************************************************
2155 * MenuSelectItem
2156 */
2157 static void FASTCALL MenuSelectItem(HWND hwndOwner, PROSMENUINFO hmenu, UINT wIndex,
2158 BOOL sendMenuSelect, HMENU topmenu)
2159 {
2160 ROSMENUITEMINFO ItemInfo;
2161 ROSMENUINFO TopMenuInfo;
2162 HDC hdc;
2163
2164 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
2165
2166 if (!hmenu || !hmenu->cItems || !hmenu->Wnd) return;
2167 if (hmenu->iItem == wIndex) return;
2168 if (hmenu->fFlags & MNF_POPUP) hdc = GetDC(hmenu->Wnd);
2169 else hdc = GetDCEx(hmenu->Wnd, 0, DCX_CACHE | DCX_WINDOW);
2170 if (!top_popup) {
2171 top_popup = hmenu->Wnd;
2172 top_popup_hmenu = hmenu->Self;
2173 }
2174
2175 SelectObject( hdc, hMenuFont );
2176
2177 MenuInitRosMenuItemInfo(&ItemInfo);
2178
2179 /* Clear previous highlighted item */
2180 if (hmenu->iItem != NO_SELECTED_ITEM)
2181 {
2182 if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo))
2183 {
2184 ItemInfo.fMask |= MIIM_STATE;
2185 ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
2186 MenuSetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo);
2187 }
2188 //MENU_EnsureMenuItemVisible(hmenu, &ItemInfo, hdc);
2189 MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc, &ItemInfo,
2190 hmenu->cyMenu, !(hmenu->fFlags & MNF_POPUP),
2191 ODA_SELECT);
2192 }
2193
2194 /* Highlight new item (if any) */
2195 hmenu->iItem = wIndex;
2196 MenuSetRosMenuInfo(hmenu);
2197 if (hmenu->iItem != NO_SELECTED_ITEM)
2198 {
2199 if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo))
2200 {
2201 if (!(ItemInfo.fType & MF_SEPARATOR))
2202 {
2203 ItemInfo.fMask |= MIIM_STATE;
2204 ItemInfo.fState |= MF_HILITE;
2205 MenuSetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo);
2206 MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc,
2207 &ItemInfo, hmenu->cyMenu, !(hmenu->fFlags & MNF_POPUP),
2208 ODA_SELECT);
2209 }
2210 if (sendMenuSelect)
2211 {
2212 WPARAM wParam = MAKEWPARAM( ItemInfo.hSubMenu ? wIndex : ItemInfo.wID,
2213 ItemInfo.fType | ItemInfo.fState |
2214 (ItemInfo.hSubMenu ? MF_POPUP : 0) |
2215 (hmenu->fFlags & MNF_SYSDESKMN ? MF_SYSMENU : 0 ) );
2216
2217 SendMessageW(hwndOwner, WM_MENUSELECT, wParam, (LPARAM) hmenu->Self);
2218 }
2219 }
2220 }
2221 else if (sendMenuSelect)
2222 {
2223 if(topmenu)
2224 {
2225 int pos;
2226 pos = MenuFindSubMenu(&topmenu, hmenu->Self);
2227 if (pos != NO_SELECTED_ITEM)
2228 {
2229 if (MenuGetRosMenuInfo(&TopMenuInfo, topmenu)
2230 && MenuGetRosMenuItemInfo(topmenu, pos, &ItemInfo))
2231 {
2232 WPARAM wParam = MAKEWPARAM( Pos, ItemInfo.fType | ItemInfo.fState |
2233 (ItemInfo.hSubMenu ? MF_POPUP : 0) |
2234 (TopMenuInfo.fFlags & MNF_SYSDESKMN ? MF_SYSMENU : 0 ) );
2235
2236 SendMessageW(hwndOwner, WM_MENUSELECT, wParam, (LPARAM) topmenu);
2237 }
2238 }
2239 }
2240 }
2241 MenuCleanupRosMenuItemInfo(&ItemInfo);
2242 ReleaseDC(hmenu->Wnd, hdc);
2243 }
2244
2245 /***********************************************************************
2246 * MenuMoveSelection
2247 *
2248 * Moves currently selected item according to the Offset parameter.
2249 * If there is no selection then it should select the last item if
2250 * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
2251 */
2252 static void FASTCALL
2253 MenuMoveSelection(HWND WndOwner, PROSMENUINFO MenuInfo, INT Offset)
2254 {
2255 INT i;
2256 ROSMENUITEMINFO ItemInfo;
2257 INT OrigPos;
2258
2259 TRACE("hwnd=%x menu=%x off=0x%04x\n", WndOwner, MenuInfo, Offset);
2260
2261 /* Prevent looping */
2262 if (0 == MenuInfo->cItems || 0 == Offset)
2263 return;
2264 else if (Offset < -1)
2265 Offset = -1;
2266 else if (Offset > 1)
2267 Offset = 1;
2268
2269 MenuInitRosMenuItemInfo(&ItemInfo);
2270
2271 OrigPos = MenuInfo->iItem;
2272 if (OrigPos == NO_SELECTED_ITEM) /* NO_SELECTED_ITEM is not -1 ! */
2273 {
2274 OrigPos = 0;
2275 i = -1;
2276 }
2277 else
2278 {
2279 i = MenuInfo->iItem;
2280 }
2281
2282 do
2283 {
2284 /* Step */
2285 i += Offset;
2286 /* Clip and wrap around */
2287 if (i < 0)
2288 {
2289 i = MenuInfo->cItems - 1;
2290 }
2291 else if (i >= MenuInfo->cItems)
2292 {
2293 i = 0;
2294 }
2295 /* If this is a good candidate; */
2296 if (MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo) &&
2297 0 == (ItemInfo.fType & MF_SEPARATOR))
2298 {
2299 MenuSelectItem(WndOwner, MenuInfo, i, TRUE, NULL);
2300 MenuCleanupRosMenuItemInfo(&ItemInfo);
2301 return;
2302 }
2303 } while (i != OrigPos);
2304
2305 /* Not found */
2306 MenuCleanupRosMenuItemInfo(&ItemInfo);
2307 }
2308
2309 #if 0
2310 LRESULT WINAPI
2311 PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
2312 {
2313 #ifdef __REACTOS__ // Do this now, remove after Server side is fixed.
2314 PWND pWnd;
2315 PPOPUPMENU pPopupMenu;
2316
2317 pWnd = ValidateHwndNoErr(Wnd);
2318 if (pWnd)
2319 {
2320 if (!pWnd->fnid)
2321 {
2322 if (Message != WM_NCCREATE)
2323 {
2324 return DefWindowProcW(Wnd, Message, wParam, lParam);
2325 }
2326 NtUserSetWindowFNID(Wnd, FNID_MENU);
2327 pPopupMenu = HeapAlloc( GetProcessHeap(), 0, sizeof(POPUPMENU) );
2328 pPopupMenu->spwndPopupMenu = pWnd;
2329 SetWindowLongPtrW(Wnd, 0, (LONG_PTR)pPopupMenu);
2330 }
2331 else
2332 {
2333 if (pWnd->fnid != FNID_MENU)
2334 {
2335 ERR("Wrong window class for Menu!\n");
2336 return 0;
2337 }
2338 pPopupMenu = ((PMENUWND)pWnd)->ppopupmenu;
2339 }
2340 }
2341 #endif
2342
2343 TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
2344
2345 switch(Message)
2346 {
2347 case WM_CREATE:
2348 {
2349 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
2350 pPopupMenu->spmenu = ValidateHandle(cs->lpCreateParams, TYPE_MENU);
2351 return 0;
2352 }
2353
2354 case WM_MOUSEACTIVATE: /* We don't want to be activated */
2355 return MA_NOACTIVATE;
2356
2357 case WM_PAINT:
2358 {
2359 PAINTSTRUCT ps;
2360 BeginPaint(Wnd, &ps);
2361 MenuDrawPopupMenu(Wnd, ps.hdc, pPopupMenu->spmenu->head.h);
2362 EndPaint(Wnd, &ps);
2363 return 0;
2364 }
2365
2366 case WM_PRINTCLIENT:
2367 {
2368 MenuDrawPopupMenu( Wnd, (HDC)wParam, pPopupMenu->spmenu->head.h);
2369 return 0;
2370 }
2371
2372 case WM_ERASEBKGND:
2373 return 1;
2374
2375 case WM_DESTROY:
2376 /* zero out global pointer in case resident popup window was destroyed. */
2377 if (Wnd == top_popup)
2378 {
2379 top_popup = NULL;
2380 top_popup_hmenu = NULL;
2381 }
2382 break;
2383
2384 case WM_NCDESTROY:
2385 {
2386 HeapFree( GetProcessHeap(), 0, pPopupMenu );
2387 SetWindowLongPtrW(Wnd, 0, 0);
2388 NtUserSetWindowFNID(Wnd, FNID_DESTROY);
2389 break;
2390 }
2391
2392 case WM_SHOWWINDOW:
2393 if (0 != wParam)
2394 {
2395 if (!pPopupMenu || !pPopupMenu->spmenu)
2396 {
2397 OutputDebugStringA("no menu to display\n");
2398 }
2399 }
2400 /*else
2401 {
2402 pPopupMenu->spmenu = NULL; ///// WTF?
2403 }*/
2404 break;
2405
2406 case MM_SETMENUHANDLE:
2407 {
2408 PMENU pmenu = ValidateHandle((HMENU)wParam, TYPE_MENU);
2409 if (!pmenu)
2410 {
2411 ERR("Bad Menu Handle\n");
2412 break;
2413 }
2414 pPopupMenu->spmenu = pmenu;
2415 break;
2416 }
2417
2418 case MM_GETMENUHANDLE:
2419 case MN_GETHMENU:
2420 return (LRESULT)(pPopupMenu ? (pPopupMenu->spmenu ? pPopupMenu->spmenu->head.h : NULL) : NULL);
2421
2422 default:
2423 return DefWindowProcW(Wnd, Message, wParam, lParam);
2424 }
2425
2426 return 0;
2427 }
2428 #endif
2429
2430 LRESULT WINAPI
2431 PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
2432 {
2433 #ifdef __REACTOS__ // Do this now, remove after Server side is fixed.
2434 PWND pWnd;
2435
2436 pWnd = ValidateHwnd(Wnd);
2437 if (pWnd)
2438 {
2439 if (!pWnd->fnid)
2440 {
2441 if (Message != WM_NCCREATE)
2442 {
2443 return DefWindowProcW(Wnd, Message, wParam, lParam);
2444 }
2445 NtUserSetWindowFNID(Wnd, FNID_MENU);
2446 }
2447 else
2448 {
2449 if (pWnd->fnid != FNID_MENU)
2450 {
2451 ERR("Wrong window class for Menu!\n");
2452 return 0;
2453 }
2454 }
2455 }
2456 #endif
2457
2458 TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
2459
2460 switch(Message)
2461 {
2462 case WM_CREATE:
2463 {
2464 CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
2465 SetWindowLongPtrW(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
2466 return 0;
2467 }
2468
2469 case WM_MOUSEACTIVATE: /* We don't want to be activated */
2470 return MA_NOACTIVATE;
2471
2472 case WM_PAINT:
2473 {
2474 PAINTSTRUCT ps;
2475 BeginPaint(Wnd, &ps);
2476 MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrW(Wnd, 0));
2477 EndPaint(Wnd, &ps);
2478 return 0;
2479 }
2480
2481 case WM_PRINTCLIENT:
2482 {
2483 MenuDrawPopupMenu( Wnd, (HDC)wParam,
2484 (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
2485 return 0;
2486 }
2487
2488 case WM_ERASEBKGND:
2489 return 1;
2490
2491 case WM_DESTROY:
2492 /* zero out global pointer in case resident popup window was destroyed. */
2493 if (Wnd == top_popup)
2494 {
2495 top_popup = NULL;
2496 top_popup_hmenu = NULL;
2497 }
2498 break;
2499
2500 #ifdef __REACTOS__
2501 case WM_NCDESTROY:
2502 NtUserSetWindowFNID(Wnd, FNID_DESTROY);
2503 break;
2504 #endif
2505
2506 case WM_SHOWWINDOW:
2507 if (0 != wParam)
2508 {
2509 if (0 == GetWindowLongPtrW(Wnd, 0))
2510 {
2511 OutputDebugStringA("no menu to display\n");
2512 }
2513 }
2514 else
2515 {
2516 SetWindowLongPtrW(Wnd, 0, 0);
2517 }
2518 break;
2519
2520 case MM_SETMENUHANDLE:
2521 SetWindowLongPtrW(Wnd, 0, wParam);
2522 break;
2523
2524 case MM_GETMENUHANDLE:
2525 case MN_GETHMENU:
2526 return GetWindowLongPtrW(Wnd, 0);
2527
2528 default:
2529 return DefWindowProcW(Wnd, Message, wParam, lParam);
2530 }
2531
2532 return 0;
2533 }
2534
2535 //
2536 // This breaks some test results. Should handle A2U if called!
2537 //
2538 LRESULT WINAPI PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
2539 {
2540 PWND pWnd;
2541
2542 pWnd = ValidateHwnd(Wnd);
2543 if (pWnd && !pWnd->fnid && Message != WM_NCCREATE)
2544 {
2545 return DefWindowProcA(Wnd, Message, wParam, lParam);
2546 }
2547 TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
2548
2549 switch(Message)
2550 {
2551 case WM_NCCREATE:
2552 case WM_CREATE:
2553 case WM_MOUSEACTIVATE:
2554 case WM_PAINT:
2555 case WM_PRINTCLIENT:
2556 case WM_ERASEBKGND:
2557 case WM_DESTROY:
2558 case WM_NCDESTROY:
2559 case WM_SHOWWINDOW:
2560 case MM_SETMENUHANDLE:
2561 case MM_GETMENUHANDLE:
2562 case MN_GETHMENU:
2563 return PopupMenuWndProcW(Wnd, Message, wParam, lParam);
2564
2565 default:
2566 return DefWindowProcA(Wnd, Message, wParam, lParam);
2567 }
2568 return 0;
2569 }
2570
2571 /**********************************************************************
2572 * MENU_ParseResource
2573 *
2574 * Parse a standard menu resource and add items to the menu.
2575 * Return a pointer to the end of the resource.
2576 *
2577 * NOTE: flags is equivalent to the mtOption field
2578 */
2579 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu)
2580 {
2581 WORD flags, id = 0;
2582 HMENU hSubMenu;
2583 LPCWSTR str;
2584 BOOL end = FALSE;
2585
2586 do
2587 {
2588 flags = GET_WORD(res);
2589
2590 /* remove MF_END flag before passing it to AppendMenu()! */
2591 end = (flags & MF_END);
2592 if(end) flags ^= MF_END;
2593
2594 res += sizeof(WORD);
2595 if(!(flags & MF_POPUP))
2596 {
2597 id = GET_WORD(res);
2598 res += sizeof(WORD);
2599 }
2600 str = (LPCWSTR)res;
2601 res += (strlenW(str) + 1) * sizeof(WCHAR);
2602
2603 if (flags & MF_POPUP)
2604 {
2605 hSubMenu = CreatePopupMenu();
2606 if(!hSubMenu) return NULL;
2607 if(!(res = MENU_ParseResource(res, hSubMenu))) return NULL;
2608 AppendMenuW(hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str);
2609 }
2610 else /* Not a popup */
2611 {
2612 AppendMenuW(hMenu, flags, id, *(LPCWSTR)str ? (LPCWSTR)str : NULL);
2613 }
2614 } while(!end);
2615 return res;
2616 }
2617
2618 /**********************************************************************
2619 * MENUEX_ParseResource
2620 *
2621 * Parse an extended menu resource and add items to the menu.
2622 * Return a pointer to the end of the resource.
2623 */
2624 static LPCSTR MENUEX_ParseResource(LPCSTR res, HMENU hMenu)
2625 {
2626 WORD resinfo;
2627 do
2628 {
2629 MENUITEMINFOW mii;
2630
2631 mii.cbSize = sizeof(mii);
2632 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2633 mii.fType = GET_DWORD(res);
2634 res += sizeof(DWORD);
2635 mii.fState = GET_DWORD(res);
2636 res += sizeof(DWORD);
2637 mii.wID = GET_DWORD(res);
2638 res += sizeof(DWORD);
2639 resinfo = GET_WORD(res);
2640 res += sizeof(WORD);
2641 /* Align the text on a word boundary. */
2642 res += (~((UINT_PTR)res - 1)) & 1;
2643 mii.dwTypeData = (LPWSTR)res;
2644 mii.cch = strlenW(mii.dwTypeData);
2645 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2646 /* Align the following fields on a dword boundary. */
2647 res += (~((UINT_PTR)res - 1)) & 3;
2648
2649 TRACE("Menu item: [%08x,%08x,%04x,%04x,%S]\n",
2650 mii.fType, mii.fState, mii.wID, resinfo, mii.dwTypeData);
2651
2652 if (resinfo & 1) /* Pop-up? */
2653 {
2654 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2655 res += sizeof(DWORD);
2656 mii.hSubMenu = CreatePopupMenu();
2657 if (!mii.hSubMenu)
2658 {
2659 ERR("CreatePopupMenu failed\n");
2660 return NULL;
2661 }
2662
2663 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu)))
2664 {
2665 ERR("MENUEX_ParseResource failed\n");
2666 DestroyMenu(mii.hSubMenu);
2667 return NULL;
2668 }
2669 mii.fMask |= MIIM_SUBMENU;
2670 }
2671 else if (!mii.dwTypeData[0] && !(mii.fType & MF_SEPARATOR))
2672 {
2673 mii.fType |= MF_SEPARATOR;
2674 }
2675 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2676 } while (!(resinfo & MF_END));
2677 return res;
2678 }
2679
2680
2681 /***********************************************************************
2682 * DrawMenuBarTemp (USER32.@)
2683 *
2684 * UNDOCUMENTED !!
2685 *
2686 * called by W98SE desk.cpl Control Panel Applet
2687 *
2688 * Not 100% sure about the param names, but close.
2689 *
2690 * @implemented
2691 */
2692 DWORD WINAPI
2693 DrawMenuBarTemp(HWND Wnd, HDC DC, LPRECT Rect, HMENU Menu, HFONT Font)
2694 {
2695 ROSMENUINFO MenuInfo;
2696 ROSMENUITEMINFO ItemInfo;
2697 UINT i;
2698 HFONT FontOld = NULL;
2699 BOOL flat_menu = FALSE;
2700
2701 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
2702
2703 if (NULL == Menu)
2704 {
2705 Menu = GetMenu(Wnd);
2706 }
2707
2708 if (NULL == Font)
2709 {
2710 Font = hMenuFont;
2711 }
2712
2713 if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
2714 {
2715 return GetSystemMetrics(SM_CYMENU);
2716 }
2717
2718 TRACE("(%x, %x, %p, %x, %x)\n", Wnd, DC, Rect, Menu, Font);
2719
2720 FontOld = SelectObject(DC, Font);
2721
2722 if (0 == MenuInfo.cyMenu)
2723 {
2724 MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
2725 }
2726
2727 Rect->bottom = Rect->top + MenuInfo.cyMenu;
2728
2729 FillRect(DC, Rect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
2730
2731 SelectObject(DC, GetStockObject(DC_PEN));
2732 SetDCPenColor(DC, GetSysColor(COLOR_3DFACE));
2733 MoveToEx(DC, Rect->left, Rect->bottom - 1, NULL);
2734 LineTo(DC, Rect->right, Rect->bottom - 1);
2735
2736 if (0 == MenuInfo.cItems)
2737 {
2738 SelectObject(DC, FontOld);
2739 return GetSystemMetrics(SM_CYMENU);
2740 }
2741
2742 MenuInitRosMenuItemInfo(&ItemInfo);
2743 for (i = 0; i < MenuInfo.cItems; i++)
2744 {
2745 if (MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
2746 {
2747 MenuDrawMenuItem(Wnd, &MenuInfo, Wnd, DC, &ItemInfo,
2748 MenuInfo.cyMenu, TRUE, ODA_DRAWENTIRE);
2749 }
2750 }
2751 MenuCleanupRosMenuItemInfo(&ItemInfo);
2752
2753 SelectObject(DC, FontOld);
2754
2755 return MenuInfo.cyMenu;
2756 }
2757
2758
2759 /***********************************************************************
2760 * MenuShowSubPopup
2761 *
2762 * Display the sub-menu of the selected item of this menu.
2763 * Return the handle of the submenu, or menu if no submenu to display.
2764 */
2765 static HMENU FASTCALL
2766 MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags)
2767 {
2768 RECT Rect;
2769 ROSMENUITEMINFO ItemInfo;
2770 ROSMENUINFO SubMenuInfo;
2771 HDC Dc;
2772 HMENU Ret;
2773
2774 TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst);
2775
2776 if (NO_SELECTED_ITEM == MenuInfo->iItem)
2777 {
2778 return MenuInfo->Self;
2779 }
2780
2781 MenuInitRosMenuItemInfo(&ItemInfo);
2782 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo))
2783 {
2784 MenuCleanupRosMenuItemInfo(&ItemInfo);
2785 return MenuInfo->Self;
2786 }
2787 if (0 == (ItemInfo.hSubMenu) || 0 != (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)))
2788 {
2789 MenuCleanupRosMenuItemInfo(&ItemInfo);
2790 return MenuInfo->Self;
2791 }
2792
2793 /* message must be sent before using item,
2794 because nearly everything may be changed by the application ! */
2795
2796 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2797 if (0 == (Flags & TPM_NONOTIFY))
2798 {
2799 SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu,
2800 MAKELPARAM(MenuInfo->iItem, IS_SYSTEM_MENU(MenuInfo)));
2801 }
2802
2803 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo))
2804 {
2805 MenuCleanupRosMenuItemInfo(&ItemInfo);
2806 return MenuInfo->Self;
2807 }
2808 Rect = ItemInfo.Rect;
2809
2810 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2811 if (0 == (ItemInfo.fState & MF_HILITE))
2812 {
2813 if (0 != (MenuInfo->fFlags & MNF_POPUP))
2814 {
2815 Dc = GetDC(MenuInfo->Wnd);
2816 }
2817 else
2818 {
2819 Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
2820 }
2821
2822 SelectObject(Dc, hMenuFont);
2823 ItemInfo.fMask |= MIIM_STATE;
2824 ItemInfo.fState |= MF_HILITE;
2825 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo);
2826 MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->cyMenu,
2827 !(MenuInfo->fFlags & MNF_POPUP), ODA_DRAWENTIRE);
2828 ReleaseDC(MenuInfo->Wnd, Dc);
2829 }
2830
2831 if (0 == ItemInfo.Rect.top && 0 == ItemInfo.Rect.left
2832 && 0 == ItemInfo.Rect.bottom && 0 == ItemInfo.Rect.right)
2833 {
2834 ItemInfo.Rect = Rect;
2835 }
2836
2837 ItemInfo.fMask |= MIIM_STATE;
2838 ItemInfo.fState |= MF_MOUSESELECT;
2839 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo);
2840
2841 if (IS_SYSTEM_MENU(MenuInfo))
2842 {
2843 ERR("Right click on window bar and Draw system menu!\n");
2844 MenuInitSysMenuPopup(ItemInfo.hSubMenu, GetWindowLongPtrW(MenuInfo->Wnd, GWL_STYLE),
2845 GetClassLongPtrW(MenuInfo->Wnd, GCL_STYLE), HTSYSMENU);
2846 if (Flags & TPM_LAYOUTRTL) Rect.left;
2847 NcGetSysPopupPos(MenuInfo->Wnd, &Rect);
2848 Rect.top = Rect.bottom;
2849 Rect.right = GetSystemMetrics(SM_CXSIZE);
2850 Rect.bottom = GetSystemMetrics(SM_CYSIZE);
2851 }
2852 else
2853 {
2854 GetWindowRect(MenuInfo->Wnd, &Rect);
2855 if (0 != (MenuInfo->fFlags & MNF_POPUP))
2856 {
2857 RECT rc = ItemInfo.Rect;
2858
2859 MENU_AdjustMenuItemRect(MenuInfo, &rc);
2860
2861 if(Flags & TPM_LAYOUTRTL)
2862 Rect.left += GetSystemMetrics(SM_CXBORDER);
2863 else
2864 Rect.left += ItemInfo.Rect.right- GetSystemMetrics(SM_CXBORDER);
2865 Rect.top += rc.top - MENU_TOP_MARGIN;//3;
2866 Rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2867 Rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN - MENU_BOTTOM_MARGIN/*2*/
2868 - GetSystemMetrics(SM_CYBORDER);
2869 }
2870 else
2871 {
2872 if(Flags & TPM_LAYOUTRTL)
2873 Rect.left += Rect.right - ItemInfo.Rect.left;
2874 else
2875 Rect.left += ItemInfo.Rect.left;
2876 Rect.top += ItemInfo.Rect.bottom;
2877 Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left;
2878 Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top;
2879 }
2880 }
2881
2882 /* use default alignment for submenus */
2883 Flags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2884
2885 MENU_InitPopup( WndOwner, ItemInfo.hSubMenu, Flags );
2886
2887 MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->iItem, Flags,
2888 Rect.left, Rect.top, Rect.right, Rect.bottom );
2889 if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2890 {
2891 MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT);
2892 }
2893
2894 Ret = ItemInfo.hSubMenu;
2895 MenuCleanupRosMenuItemInfo(&ItemInfo);
2896
2897 return Ret;
2898 }
2899
2900 /**********************************************************************
2901 * MENU_EndMenu
2902 *
2903 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2904 *
2905 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2906 */
2907 void MENU_EndMenu( HWND hwnd )
2908 {
2909 MENU *menu;
2910 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2911 if (menu && ( hwnd == menu->hWnd || hwnd == (menu->spwndNotify ? menu->spwndNotify->head.h : NULL)) )
2912 EndMenu();
2913 }
2914
2915 /***********************************************************************
2916 * MenuHideSubPopups
2917 *
2918 * Hide the sub-popup menus of this menu.
2919 */
2920 static void FASTCALL
2921 MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo,
2922 BOOL SendMenuSelect, UINT wFlags)
2923 {
2924 ROSMENUINFO SubMenuInfo;
2925 ROSMENUITEMINFO ItemInfo;
2926
2927 TRACE("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect);
2928
2929 if (NULL != MenuInfo && NULL != top_popup && NO_SELECTED_ITEM != MenuInfo->iItem)
2930 {
2931 MenuInitRosMenuItemInfo(&ItemInfo);
2932 ItemInfo.fMask |= MIIM_FTYPE | MIIM_STATE;
2933 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo)
2934 || 0 == (ItemInfo.hSubMenu)
2935 || 0 == (ItemInfo.fState & MF_MOUSESELECT))
2936 {
2937 MenuCleanupRosMenuItemInfo(&ItemInfo);
2938 return;
2939 }
2940 ItemInfo.fState &= ~MF_MOUSESELECT;
2941 ItemInfo.fMask |= MIIM_STATE;
2942 MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo);
2943 if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
2944 {
2945 MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE, wFlags);
2946 MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL);
2947
2948 DestroyWindow(SubMenuInfo.Wnd);
2949 /* Native returns handle to destroyed window */
2950 if (!(wFlags & TPM_NONOTIFY))
2951 SendMessageW( WndOwner, WM_UNINITMENUPOPUP, (WPARAM)ItemInfo.hSubMenu,
2952 MAKELPARAM(0, IS_SYSTEM_MENU(&SubMenuInfo)) );
2953 ////
2954 // Call WM_UNINITMENUPOPUP FIRST before destroy!!
2955 // Fixes todo_wine User32 test menu.c line 2233 GetMenuBarInfo callback....
2956 //
2957 SubMenuInfo.Wnd = NULL;
2958 MenuSetRosMenuInfo(&SubMenuInfo);
2959 ////
2960 }
2961 }
2962 }
2963
2964 /***********************************************************************
2965 * MenuSwitchTracking
2966 *
2967 * Helper function for menu navigation routines.
2968 */
2969 static void FASTCALL
2970 MenuSwitchTracking(MTRACKER* Mt, PROSMENUINFO PtMenuInfo, UINT Index, UINT wFlags)
2971 {
2972 ROSMENUINFO TopMenuInfo;
2973
2974 TRACE("%x menu=%x 0x%04x\n", Mt, PtMenuInfo->Self, Index);
2975
2976 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu) &&
2977 Mt->TopMenu != PtMenuInfo->Self &&
2978 0 == ((PtMenuInfo->fFlags | TopMenuInfo.fFlags) & MNF_POPUP))
2979 {
2980 /* both are top level menus (system and menu-bar) */
2981 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
2982 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL);
2983 Mt->TopMenu = PtMenuInfo->Self;
2984 }
2985 else
2986 {
2987 MenuHideSubPopups(Mt->OwnerWnd, PtMenuInfo, FALSE, wFlags);
2988 }
2989
2990 MenuSelectItem(Mt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL);
2991 }
2992
2993 /***********************************************************************
2994 * MenuExecFocusedItem
2995 *
2996 * Execute a menu item (for instance when user pressed Enter).
2997 * Return the wID of the executed item. Otherwise, -1 indicating
2998 * that no menu item was executed, -2 if a popup is shown;
2999 * Have to receive the flags for the TrackPopupMenu options to avoid
3000 * sending unwanted message.
3001 *
3002 */
3003 static INT FASTCALL
3004 MenuExecFocusedItem(MTRACKER *Mt, PROSMENUINFO MenuInfo, UINT Flags)
3005 {
3006 ROSMENUITEMINFO ItemInfo;
3007 UINT wID;
3008
3009 TRACE("%p menu=%p\n", Mt, MenuInfo);
3010
3011 if (0 == MenuInfo->cItems || NO_SELECTED_ITEM == MenuInfo->iItem)
3012 {
3013 return -1;
3014 }
3015
3016 MenuInitRosMenuItemInfo(&ItemInfo);
3017 if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo))
3018 {
3019 MenuCleanupRosMenuItemInfo(&ItemInfo);
3020 return -1;
3021 }
3022
3023 TRACE("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu);
3024
3025 if (0 == (ItemInfo.hSubMenu))
3026 {
3027 if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))
3028 && 0 == (ItemInfo.fType & MF_SEPARATOR))
3029 {
3030 /* If TPM_RETURNCMD is set you return the id, but
3031 do not send a message to the owner */
3032 if (0 == (Flags & TPM_RETURNCMD))
3033 {
3034 if (0 != (MenuInfo->fFlags & MNF_SYSDESKMN))
3035 {
3036 PostMessageW(Mt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID,
3037 MAKELPARAM((SHORT) Mt->Pt.x, (SHORT) Mt->Pt.y));
3038 }
3039 else
3040 {
3041 ROSMENUINFO topmenuI;
3042 BOOL ret = MenuGetRosMenuInfo(&topmenuI, Mt->TopMenu);
3043 DWORD dwStyle = MenuInfo->dwStyle | (ret ? topmenuI.dwStyle : 0);
3044
3045 if (dwStyle & MNS_NOTIFYBYPOS)
3046 PostMessageW(Mt->OwnerWnd, WM_MENUCOMMAND, MenuInfo->iItem, (LPARAM)MenuInfo->Self);
3047 else
3048 PostMessageW(Mt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0);
3049 }
3050 }
3051 wID = ItemInfo.wID;
3052 MenuCleanupRosMenuItemInfo(&ItemInfo);
3053 return wID;
3054 }
3055 }
3056 else
3057 {
3058 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, MenuInfo, TRUE, Flags);
3059 return -2;
3060 }
3061
3062 return -1;
3063 }
3064
3065 /***********************************************************************
3066 * MenuButtonDown
3067 *
3068 * Return TRUE if we can go on with menu tracking.
3069 */
3070 static BOOL FASTCALL
3071 MENU_ButtonDown(MTRACKER* Mt, HMENU PtMenu, UINT Flags)
3072 {
3073 int Index;
3074 ROSMENUINFO MenuInfo;
3075 ROSMENUITEMINFO Item;
3076
3077 TRACE("%x PtMenu=%p\n", Mt, PtMenu);
3078
3079 if (NULL != PtMenu)
3080 {
3081 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
3082 {
3083 return FALSE;
3084 }
3085 if (IS_SYSTEM_MENU(&MenuInfo))
3086 {
3087 Index = 0;
3088 }
3089 else
3090 {
3091 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
3092 }
3093 MenuInitRosMenuItemInfo(&Item);
3094 if (NO_SELECTED_ITEM == Index || ! MenuGetRosMenuItemInfo(PtMenu, Index, &Item))
3095 {
3096 MenuCleanupRosMenuItemInfo(&Item);
3097 return FALSE;
3098 }
3099
3100 if (!(Item.fType & MF_SEPARATOR) &&
3101 !(Item.fState & (MFS_DISABLED | MFS_GRAYED)) )
3102 {
3103 if (MenuInfo.iItem != Index)
3104 {
3105 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
3106 }
3107
3108 /* If the popup menu is not already "popped" */
3109 if (0 == (Item.fState & MF_MOUSESELECT))
3110 {
3111 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
3112 }
3113 }
3114
3115 MenuCleanupRosMenuItemInfo(&Item);
3116
3117 return TRUE;
3118 }
3119
3120 /* else the click was on the menu bar, finish the tracking */
3121
3122 return FALSE;
3123 }
3124
3125 /***********************************************************************
3126 * MenuButtonUp
3127 *
3128 * Return the value of MenuExecFocusedItem if
3129 * the selected item was not a popup. Else open the popup.
3130 * A -1 return value indicates that we go on with menu tracking.
3131 *
3132 */
3133 static INT FASTCALL
3134 MENU_ButtonUp(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
3135 {
3136 INT Id;
3137 ROSMENUINFO MenuInfo;
3138 ROSMENUITEMINFO ItemInfo;
3139
3140 TRACE("%p hmenu=%x\n", Mt, PtMenu);
3141
3142 if (NULL != PtMenu)
3143 {
3144 Id = 0;
3145 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
3146 {
3147 return -1;
3148 }
3149
3150 if (! IS_SYSTEM_MENU(&MenuInfo))
3151 {
3152 Id = NtUserMenuItemFromPoint(Mt->OwnerWnd, MenuInfo.Self, Mt->Pt.x, Mt->Pt.y);
3153 }
3154 MenuInitRosMenuItemInfo(&ItemInfo);
3155 if (0 <= Id && MenuGetRosMenuItemInfo(MenuInfo.Self, Id, &ItemInfo) &&
3156 MenuInfo.iItem == Id)
3157 {
3158 if (0 == (ItemInfo.hSubMenu))
3159 {
3160 INT ExecutedMenuId = MenuExecFocusedItem(Mt, &MenuInfo, Flags);
3161 MenuCleanupRosMenuItemInfo(&ItemInfo);
3162 return (ExecutedMenuId < 0) ? -1 : ExecutedMenuId;
3163 }
3164 MenuCleanupRosMenuItemInfo(&ItemInfo);
3165
3166 /* If we are dealing with the top-level menu */
3167 /* and this is a click on an already "popped" item: */
3168 /* Stop the menu tracking and close the opened submenus */
3169 if (Mt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide)
3170 {
3171 MenuCleanupRosMenuItemInfo(&ItemInfo);
3172 return 0;
3173 }
3174 }
3175 MenuCleanupRosMenuItemInfo(&ItemInfo);
3176 MenuInfo.TimeToHide = TRUE;
3177 MenuSetRosMenuInfo(&MenuInfo);
3178 }
3179
3180 return -1;
3181 }
3182
3183 /***********************************************************************
3184 * MenuPtMenu
3185 *
3186 * Walks menu chain trying to find a menu pt maps to.
3187 */
3188 static HMENU FASTCALL
3189 MENU_PtMenu(HMENU hMenu, POINT pt)
3190 {
3191 MENU *menu;
3192 PITEM pItem;
3193 HMENU ret = NULL;
3194
3195 menu = MENU_GetMenu( hMenu );
3196 if (!menu) return NULL;
3197
3198 /* try subpopup first (if any) */
3199 if (menu->iItem != NO_SELECTED_ITEM)
3200 {
3201 pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
3202 if ( pItem ) pItem = &pItem[menu->iItem];
3203 if ( pItem && pItem->spSubMenu && pItem->fState & MF_MOUSESELECT)
3204 {
3205 PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
3206 ret = MENU_PtMenu( UserHMGetHandle(pSubMenu), pt);
3207 }
3208 }
3209
3210 /* check the current window (avoiding WM_HITTEST) */
3211 if (!ret)
3212 {
3213 INT ht = DefWndNCHitTest(menu->hWnd, pt);
3214 if ( menu->fFlags & MNF_POPUP )
3215 {
3216 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
3217 }
3218 else if (ht == HTSYSMENU)
3219 ret = NtUserGetSystemMenu(menu->hWnd, FALSE);
3220 else if (ht == HTMENU)
3221 ret = GetMenu( menu->hWnd );
3222 }
3223 return ret;
3224 }
3225
3226 /***********************************************************************
3227 * MenuMouseMove
3228 *
3229 * Return TRUE if we can go on with menu tracking.
3230 */
3231 static BOOL FASTCALL
3232 MenuMouseMove(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
3233 {
3234 INT Index;
3235 ROSMENUINFO MenuInfo;
3236 ROSMENUITEMINFO ItemInfo;
3237
3238 if (NULL != PtMenu)
3239 {
3240 if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
3241 {
3242 return TRUE;
3243 }
3244 if (IS_SYSTEM_MENU(&MenuInfo))
3245 {
3246 Index = 0;
3247 }
3248 else
3249 {
3250 Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
3251 }
3252 }
3253 else
3254 {
3255 Index = NO_SELECTED_ITEM;
3256 }
3257
3258 if (NO_SELECTED_ITEM == Index)
3259 {
3260 if (Mt->CurrentMenu == MenuInfo.Self ||
3261 MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3262 {
3263 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
3264 TRUE, Mt->TopMenu);
3265 }
3266 }
3267 else if (MenuInfo.iItem != Index)
3268 {
3269 MenuInitRosMenuItemInfo(&ItemInfo);
3270 if (MenuGetRosMenuItemInfo(MenuInfo.Self, Index, &ItemInfo) &&
3271 !(ItemInfo.fType & MF_SEPARATOR))
3272 {
3273 MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
3274 if (!(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED)))
3275 Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
3276 }
3277 MenuCleanupRosMenuItemInfo(&ItemInfo);
3278 }
3279
3280 return TRUE;
3281 }
3282
3283 /***********************************************************************
3284 * MenuGetSubPopup
3285 *
3286 * Return the handle of the selected sub-popup menu (if any).
3287 */
3288 static
3289 HMENU MENU_GetSubPopup( HMENU hmenu )
3290 {
3291 MENU *menu;
3292 ITEM *item;
3293
3294 menu = MENU_GetMenu( hmenu );
3295
3296 if ((!menu) || (menu->iItem == NO_SELECTED_ITEM)) return 0;
3297
3298 //item = &menu->rgItems[menu->iItem];
3299 item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
3300 if (item) item = &item[menu->iItem];
3301 if (item && (item->spSubMenu) && (item->fState & MF_MOUSESELECT))
3302 {
3303 PMENU pSubMenu = DesktopPtrToUser(item->spSubMenu);
3304 return UserHMGetHandle(pSubMenu);
3305 }
3306 return 0;
3307 }
3308
3309 /***********************************************************************
3310 * MenuDoNextMenu
3311 *
3312 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
3313 */
3314 static LRESULT FASTCALL
3315 MenuDoNextMenu(MTRACKER* Mt, UINT Vk, UINT wFlags)
3316 {
3317 ROSMENUINFO TopMenuInfo;
3318 ROSMENUINFO MenuInfo;
3319
3320 if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3321 {
3322 return (LRESULT) FALSE;
3323 }
3324
3325 if ((VK_LEFT == Vk && 0 == TopMenuInfo.iItem)
3326 || (VK_RIGHT == Vk && TopMenuInfo.iItem == TopMenuInfo.cItems - 1))
3327 {
3328 MDINEXTMENU NextMenu;
3329 HMENU NewMenu;
3330 HWND NewWnd;
3331 UINT Id = 0;
3332
3333 NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(Mt->TopMenu, 0) : Mt->TopMenu;
3334 NextMenu.hmenuNext = NULL;
3335 NextMenu.hwndNext = NULL;
3336 SendMessageW(Mt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
3337
3338 TRACE("%p [%p] -> %p [%p]\n",
3339 Mt->CurrentMenu, Mt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
3340
3341 if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
3342 {
3343 DWORD Style = GetWindowLongPtrW(Mt->OwnerWnd, GWL_STYLE);
3344 NewWnd = Mt->OwnerWnd;
3345 if (IS_SYSTEM_MENU(&TopMenuInfo))
3346 {
3347 /* switch to the menu bar */
3348
3349 if (0 != (Style & WS_CHILD)
3350 || NULL == (NewMenu = GetMenu(NewWnd)))
3351 {
3352 return FALSE;
3353 }
3354
3355 if (VK_LEFT == Vk)
3356 {
3357 if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu))
3358 {
3359 return FALSE;
3360 }
3361 Id = MenuInfo.cItems - 1;
3362 }
3363 }
3364 else if (0 != (Style & WS_SYSMENU))
3365 {
3366 /* switch to the system menu */
3367 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
3368 }
3369 else
3370 {
3371 return FALSE;
3372 }
3373 }
3374 else /* application returned a new menu to switch to */
3375 {
3376 NewMenu = NextMenu.hmenuNext;
3377 NewWnd = NextMenu.hwndNext;
3378
3379 if (IsMenu(NewMenu) && IsWindow(NewWnd))
3380 {
3381 DWORD Style = GetWindowLongPtrW(NewWnd, GWL_STYLE);
3382
3383 if (0 != (Style & WS_SYSMENU)
3384 && GetSystemMenu(NewWnd, FALSE) == NewMenu)
3385 {
3386 /* get the real system menu */
3387 NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
3388 }
3389 else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu)
3390 {
3391 /* FIXME: Not sure what to do here;
3392 * perhaps try to track NewMenu as a popup? */
3393
3394 WARN(" -- got confused.\n");
3395 return FALSE;
3396 }
3397 }
3398 else
3399 {
3400 return FALSE;
3401 }
3402 }
3403
3404 if (NewMenu != Mt->TopMenu)
3405 {
3406 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM,
3407 FALSE, 0 );
3408 if (Mt->CurrentMenu != Mt->TopMenu)
3409 {
3410 MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
3411 }
3412 }
3413
3414 if (NewWnd != Mt->OwnerWnd)
3415 {
3416 Mt->OwnerWnd = NewWnd;
3417 NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, Mt->OwnerWnd); // 1
3418 SetCapture(Mt->OwnerWnd); // 2
3419 }
3420
3421 Mt->TopMenu = Mt->CurrentMenu = NewMenu; /* all subpopups are hidden */
3422 if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
3423 {
3424 MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0);
3425 }
3426
3427 return TRUE;
3428 }
3429
3430 return FALSE;
3431 }
3432
3433 /***********************************************************************
3434 * MenuSuspendPopup
3435 *
3436 * The idea is not to show the popup if the next input message is
3437 * going to hide it anyway.
3438 */
3439 static BOOL FASTCALL
3440 MenuSuspendPopup(MTRACKER* Mt, UINT uMsg)
3441 {
3442 MSG msg;
3443
3444 msg.hwnd = Mt->OwnerWnd;
3445
3446 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE); // ported incorrectly since 8317 GvG
3447 // Mt->TrackFlags |= TF_SKIPREMOVE; // This sends TrackMenu into a loop with arrow keys!!!!
3448
3449 switch( uMsg )
3450 {
3451 case WM_KEYDOWN:
3452 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
3453 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
3454 {
3455 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
3456 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
3457 if( msg.message == WM_KEYDOWN &&
3458 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
3459 {
3460 Mt->TrackFlags |= TF_SUSPENDPOPUP;
3461 return TRUE;
3462 }
3463 }
3464 break;
3465 }
3466 /* failures go through this */
3467 Mt->TrackFlags &= ~TF_SUSPENDPOPUP;
3468 return FALSE;
3469 }
3470
3471 /***********************************************************************
3472 * MenuKeyEscape
3473 *
3474 * Handle a VK_ESCAPE key event in a menu.
3475 */
3476 static BOOL FASTCALL
3477 MenuKeyEscape(MTRACKER *Mt, UINT Flags)
3478 {
3479 BOOL EndMenu = TRUE;
3480 ROSMENUINFO MenuInfo;
3481 HMENU MenuTmp, MenuPrev;
3482
3483 if (Mt->CurrentMenu != Mt->TopMenu)
3484 {
3485 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)
3486 && 0 != (MenuInfo.fFlags & MNF_POPUP))
3487 {
3488 MenuPrev = MenuTmp = Mt->TopMenu;
3489
3490 /* close topmost popup */
3491 while (MenuTmp != Mt->CurrentMenu)
3492 {
3493 MenuPrev = MenuTmp;
3494 MenuTmp = MENU_GetSubPopup(MenuPrev);
3495 }
3496
3497 if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev))
3498 {
3499 MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, TRUE, Flags);
3500 }
3501 Mt->CurrentMenu = MenuPrev;
3502 EndMenu = FALSE;
3503 }
3504 }
3505
3506 return EndMenu;
3507 }
3508
3509 /***********************************************************************
3510 * MenuKeyLeft
3511 *
3512 * Handle a VK_LEFT key event in a menu.
3513 */
3514 static void FASTCALL
3515 MenuKeyLeft(MTRACKER* Mt, UINT Flags)
3516 {
3517 ROSMENUINFO MenuInfo;
3518 ROSMENUINFO TopMenuInfo;
3519 ROSMENUINFO PrevMenuInfo;
3520 HMENU MenuTmp, MenuPrev;
3521 UINT PrevCol;
3522
3523 MenuPrev = MenuTmp = Mt->TopMenu;
3524
3525 /* Try to move 1 column left (if possible) */
3526 if ( (PrevCol = MENU_GetStartOfPrevColumn(Mt->CurrentMenu)) != NO_SELECTED_ITEM)
3527 {
3528 if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
3529 {
3530 MenuSelectItem(Mt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0);
3531 }
3532 return;
3533 }
3534
3535 /* close topmost popup */
3536 while (MenuTmp