1 /*
2 * Menu functions
3 *
4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994 Alexandre Julliard
6 * Copyright 1997 Morten Welinder
7 * Copyright 2005 Maxime Bellengé
8 * Copyright 2006 Phil Krylov
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25 /*
26 * Note: the style MF_MOUSESELECT is used to mark popup items that
27 * have been selected, i.e. their popup menu is currently displayed.
28 * This is probably not the meaning this style has in MS-Windows.
29 *
30 * Note 2: where there is a difference, these menu API's are according
31 * the behavior of Windows 2k and Windows XP. Known differences with
32 * Windows 9x/ME are documented in the comments, in case an application
33 * is found to depend on the old behavior.
34 *
35 * TODO:
36 * implements styles :
37 * - MNS_AUTODISMISS
38 * - MNS_DRAGDROP
39 * - MNS_MODELESS
40 */
41
42 #include "config.h"
43 #include "wine/port.h"
44
45 #include <stdarg.h>
46 #include <string.h>
47
48 #define OEMRESOURCE
49
50 #include "windef.h"
51 #include "winbase.h"
52 #include "wingdi.h"
53 #include "winnls.h"
54 #include "wine/server.h"
55 #include "wine/unicode.h"
56 #include "wine/exception.h"
57 #include "win.h"
58 #include "controls.h"
59 #include "user_private.h"
60 #include "wine/debug.h"
61
62 WINE_DEFAULT_DEBUG_CHANNEL(menu);
63 WINE_DECLARE_DEBUG_CHANNEL(accel);
64
65 /* internal popup menu window messages */
66
67 #define MM_SETMENUHANDLE (WM_USER + 0)
68 #define MM_GETMENUHANDLE (WM_USER + 1)
69
70 /* Menu item structure */
71 typedef struct {
72 /* ----------- MENUITEMINFO Stuff ----------- */
73 UINT fType; /* Item type. */
74 UINT fState; /* Item state. */
75 UINT_PTR wID; /* Item id. */
76 HMENU hSubMenu; /* Pop-up menu. */
77 HBITMAP hCheckBit; /* Bitmap when checked. */
78 HBITMAP hUnCheckBit; /* Bitmap when unchecked. */
79 LPWSTR text; /* Item text. */
80 ULONG_PTR dwItemData; /* Application defined. */
81 LPWSTR dwTypeData; /* depends on fMask */
82 HBITMAP hbmpItem; /* bitmap */
83 /* ----------- Wine stuff ----------- */
84 RECT rect; /* Item area (relative to menu window) */
85 UINT xTab; /* X position of text after Tab */
86 SIZE bmpsize; /* size needed for the HBMMENU_CALLBACK
87 * bitmap */
88 } MENUITEM;
89
90 /* Popup menu structure */
91 typedef struct {
92 struct user_object obj;
93 WORD wFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */
94 WORD Width; /* Width of the whole menu */
95 WORD Height; /* Height of the whole menu */
96 UINT nItems; /* Number of items in the menu */
97 HWND hWnd; /* Window containing the menu */
98 MENUITEM *items; /* Array of menu items */
99 UINT FocusedItem; /* Currently focused item */
100 HWND hwndOwner; /* window receiving the messages for ownerdraw */
101 BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
102 BOOL bScrolling; /* Scroll arrows are active */
103 UINT nScrollPos; /* Current scroll position */
104 UINT nTotalHeight; /* Total height of menu items inside menu */
105 /* ------------ MENUINFO members ------ */
106 DWORD dwStyle; /* Extended menu style */
107 UINT cyMax; /* max height of the whole menu, 0 is screen height */
108 HBRUSH hbrBack; /* brush for menu background */
109 DWORD dwContextHelpID;
110 DWORD dwMenuData; /* application defined value */
111 HMENU hSysMenuOwner; /* Handle to the dummy sys menu holder */
112 WORD textOffset; /* Offset of text when items have both bitmaps and text */
113 } POPUPMENU, *LPPOPUPMENU;
114
115 /* internal flags for menu tracking */
116
117 #define TF_ENDMENU 0x10000
118 #define TF_SUSPENDPOPUP 0x20000
119 #define TF_SKIPREMOVE 0x40000
120
121 typedef struct
122 {
123 UINT trackFlags;
124 HMENU hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
125 HMENU hTopMenu; /* initial menu */
126 HWND hOwnerWnd; /* where notifications are sent */
127 POINT pt;
128 } MTRACKER;
129
130 #define MENU_MAGIC 0x554d /* 'MU' */
131
132 #define ITEM_PREV -1
133 #define ITEM_NEXT 1
134
135 /* Internal MENU_TrackMenu() flags */
136 #define TPM_INTERNAL 0xF0000000
137 #define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
138 #define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
139
140 /* Space between 2 columns */
141 #define MENU_COL_SPACE 4
142
143 /* top and bottom margins for popup menus */
144 #define MENU_TOP_MARGIN 3
145 #define MENU_BOTTOM_MARGIN 2
146
147 /* maximum allowed depth of any branch in the menu tree.
148 * This value is slightly larger than in windows (25) to
149 * stay on the safe side. */
150 #define MAXMENUDEPTH 30
151
152 /* (other menu->FocusedItem values give the position of the focused item) */
153 #define NO_SELECTED_ITEM 0xffff
154
155 #define MENU_ITEM_TYPE(flags) \
156 ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))
157
158 /* macro to test that flags do not indicate bitmap, ownerdraw or separator */
159 #define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
160 #define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
161
162 #define IS_SYSTEM_MENU(menu) \
163 (!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
164
165 #define MENUITEMINFO_TYPE_MASK \
166 (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
167 MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
168 MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
169 #define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
170 #define STATE_MASK (~TYPE_MASK)
171 #define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
172
173 #define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
174
175 static SIZE menucharsize;
176 static UINT ODitemheight; /* default owner drawn item height */
177
178 /* Use global popup window because there's no way 2 menus can
179 * be tracked at the same time. */
180 static HWND top_popup;
181 static HMENU top_popup_hmenu;
182
183 /* Flag set by EndMenu() to force an exit from menu tracking */
184 static BOOL fEndMenu = FALSE;
185
186 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
187
188 static BOOL SetMenuItemInfo_common( MENUITEM *, const MENUITEMINFOW *, BOOL);
189
190 /*********************************************************************
191 * menu class descriptor
192 */
193 const struct builtin_class_descr MENU_builtin_class =
194 {
195 (LPCWSTR)POPUPMENU_CLASS_ATOM, /* name */
196 CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, /* style */
197 WINPROC_MENU, /* proc */
198 sizeof(HMENU), /* extra */
199 IDC_ARROW, /* cursor */
200 (HBRUSH)(COLOR_MENU+1) /* brush */
201 };
202
203
204 /***********************************************************************
205 * debug_print_menuitem
206 *
207 * Print a menuitem in readable form.
208 */
209
210 #define debug_print_menuitem(pre, mp, post) \
211 do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
212
213 #define MENUOUT(text) \
214 TRACE("%s%s", (count++ ? "," : ""), (text))
215
216 #define MENUFLAG(bit,text) \
217 do { \
218 if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
219 } while (0)
220
221 static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
222 const char *postfix)
223 {
224 static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
225 "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
226 "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
227 "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
228 TRACE("%s ", prefix);
229 if (mp) {
230 UINT flags = mp->fType;
231 TRACE( "{ ID=0x%lx", mp->wID);
232 if ( mp->hSubMenu)
233 TRACE( ", Sub=%p", mp->hSubMenu);
234 if (flags) {
235 int count = 0;
236 TRACE( ", fType=");
237 MENUFLAG( MFT_SEPARATOR, "sep");
238 MENUFLAG( MFT_OWNERDRAW, "own");
239 MENUFLAG( MFT_BITMAP, "bit");
240 MENUFLAG(MF_POPUP, "pop");
241 MENUFLAG(MFT_MENUBARBREAK, "barbrk");
242 MENUFLAG(MFT_MENUBREAK, "brk");
243 MENUFLAG(MFT_RADIOCHECK, "radio");
244 MENUFLAG(MFT_RIGHTORDER, "rorder");
245 MENUFLAG(MF_SYSMENU, "sys");
246 MENUFLAG(MFT_RIGHTJUSTIFY, "right"); /* same as MF_HELP */
247 if (flags)
248 TRACE( "+0x%x", flags);
249 }
250 flags = mp->fState;
251 if (flags) {
252 int count = 0;
253 TRACE( ", State=");
254 MENUFLAG(MFS_GRAYED, "grey");
255 MENUFLAG(MFS_DEFAULT, "default");
256 MENUFLAG(MFS_DISABLED, "dis");
257 MENUFLAG(MFS_CHECKED, "check");
258 MENUFLAG(MFS_HILITE, "hi");
259 MENUFLAG(MF_USECHECKBITMAPS, "usebit");
260 MENUFLAG(MF_MOUSESELECT, "mouse");
261 if (flags)
262 TRACE( "+0x%x", flags);
263 }
264 if (mp->hCheckBit)
265 TRACE( ", Chk=%p", mp->hCheckBit);
266 if (mp->hUnCheckBit)
267 TRACE( ", Unc=%p", mp->hUnCheckBit);
268 if (mp->text)
269 TRACE( ", Text=%s", debugstr_w(mp->text));
270 if (mp->dwItemData)
271 TRACE( ", ItemData=0x%08lx", mp->dwItemData);
272 if (mp->hbmpItem)
273 {
274 if( IS_MAGIC_BITMAP(mp->hbmpItem))
275 TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
276 else
277 TRACE( ", hbitmap=%p", mp->hbmpItem);
278 }
279 TRACE( " }");
280 } else
281 TRACE( "NULL");
282 TRACE(" %s\n", postfix);
283 }
284
285 #undef MENUOUT
286 #undef MENUFLAG
287
288
289 /***********************************************************************
290 * MENU_GetMenu
291 *
292 * Validate the given menu handle and returns the menu structure pointer.
293 */
294 static POPUPMENU *MENU_GetMenu(HMENU hMenu)
295 {
296 POPUPMENU *menu = get_user_handle_ptr( hMenu, USER_MENU );
297
298 if (menu == OBJ_OTHER_PROCESS)
299 {
300 WARN( "other process menu %p?\n", hMenu);
301 return NULL;
302 }
303 if (menu) release_user_handle_ptr( menu ); /* FIXME! */
304 else WARN("invalid menu handle=%p\n", hMenu);
305 return menu;
306 }
307
308 /***********************************************************************
309 * get_win_sys_menu
310 *
311 * Get the system menu of a window
312 */
313 static HMENU get_win_sys_menu( HWND hwnd )
314 {
315 HMENU ret = 0;
316 WND *win = WIN_GetPtr( hwnd );
317 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
318 {
319 ret = win->hSysMenu;
320 WIN_ReleasePtr( win );
321 }
322 return ret;
323 }
324
325 /***********************************************************************
326 * get_menu_font
327 */
328 static HFONT get_menu_font( BOOL bold )
329 {
330 static HFONT hMenuFont, hMenuFontBold;
331
332 HFONT ret = bold ? hMenuFontBold : hMenuFont;
333
334 if (!ret)
335 {
336 NONCLIENTMETRICSW ncm;
337 HFONT prev;
338
339 ncm.cbSize = sizeof(NONCLIENTMETRICSW);
340 SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);
341
342 if (bold)
343 {
344 ncm.lfMenuFont.lfWeight += 300;
345 if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
346 }
347 if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
348 prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
349 ret, NULL );
350 if (prev)
351 {
352 /* another thread beat us to it */
353 DeleteObject( ret );
354 ret = prev;
355 }
356 }
357 return ret;
358 }
359
360 /***********************************************************************
361 * get_arrow_bitmap
362 */
363 static HBITMAP get_arrow_bitmap(void)
364 {
365 static HBITMAP arrow_bitmap;
366
367 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
368 return arrow_bitmap;
369 }
370
371 /***********************************************************************
372 * get_down_arrow_bitmap
373 */
374 static HBITMAP get_down_arrow_bitmap(void)
375 {
376 static HBITMAP arrow_bitmap;
377
378 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
379 return arrow_bitmap;
380 }
381
382 /***********************************************************************
383 * get_down_arrow_inactive_bitmap
384 */
385 static HBITMAP get_down_arrow_inactive_bitmap(void)
386 {
387 static HBITMAP arrow_bitmap;
388
389 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
390 return arrow_bitmap;
391 }
392
393 /***********************************************************************
394 * get_up_arrow_bitmap
395 */
396 static HBITMAP get_up_arrow_bitmap(void)
397 {
398 static HBITMAP arrow_bitmap;
399
400 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
401 return arrow_bitmap;
402 }
403
404 /***********************************************************************
405 * get_up_arrow_inactive_bitmap
406 */
407 static HBITMAP get_up_arrow_inactive_bitmap(void)
408 {
409 static HBITMAP arrow_bitmap;
410
411 if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
412 return arrow_bitmap;
413 }
414
415 /***********************************************************************
416 * MENU_CopySysPopup
417 *
418 * Return the default system menu.
419 */
420 static HMENU MENU_CopySysPopup(BOOL mdi)
421 {
422 static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
423 static const WCHAR sysmenumdiW[] = {'S','Y','S','M','E','N','U','M','D','I',0};
424 HMENU hMenu = LoadMenuW(user32_module, (mdi ? sysmenumdiW : sysmenuW));
425
426 if( hMenu ) {
427 MENUINFO minfo;
428 MENUITEMINFOW miteminfo;
429 POPUPMENU* menu = MENU_GetMenu(hMenu);
430 menu->wFlags |= MF_SYSMENU | MF_POPUP;
431 /* decorate the menu with bitmaps */
432 minfo.cbSize = sizeof( MENUINFO);
433 minfo.dwStyle = MNS_CHECKORBMP;
434 minfo.fMask = MIM_STYLE;
435 SetMenuInfo( hMenu, &minfo);
436 miteminfo.cbSize = sizeof( MENUITEMINFOW);
437 miteminfo.fMask = MIIM_BITMAP;
438 miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
439 SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
440 miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
441 SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
442 miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
443 SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
444 miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
445 SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
446 SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
447 }
448 else
449 ERR("Unable to load default system menu\n" );
450
451 TRACE("returning %p (mdi=%d).\n", hMenu, mdi );
452
453 return hMenu;
454 }
455
456
457 /**********************************************************************
458 * MENU_GetSysMenu
459 *
460 * Create a copy of the system menu. System menu in Windows is
461 * a special menu bar with the single entry - system menu popup.
462 * This popup is presented to the outside world as a "system menu".
463 * However, the real system menu handle is sometimes seen in the
464 * WM_MENUSELECT parameters (and Word 6 likes it this way).
465 */
466 static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
467 {
468 HMENU hMenu;
469
470 TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
471 if ((hMenu = CreateMenu()))
472 {
473 POPUPMENU *menu = MENU_GetMenu(hMenu);
474 menu->wFlags = MF_SYSMENU;
475 menu->hWnd = WIN_GetFullHandle( hWnd );
476 TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
477
478 if (!hPopupMenu)
479 {
480 if (GetWindowLongW(hWnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
481 hPopupMenu = MENU_CopySysPopup(TRUE);
482 else
483 hPopupMenu = MENU_CopySysPopup(FALSE);
484 }
485
486 if (hPopupMenu)
487 {
488 if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
489 DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);
490
491 InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
492 (UINT_PTR)hPopupMenu, NULL );
493
494 menu->items[0].fType = MF_SYSMENU | MF_POPUP;
495 menu->items[0].fState = 0;
496 if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
497
498 TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
499 return hMenu;
500 }
501 DestroyMenu( hMenu );
502 }
503 ERR("failed to load system menu!\n");
504 return 0;
505 }
506
507
508 /***********************************************************************
509 * MENU_InitSysMenuPopup
510 *
511 * Grey the appropriate items in System menu.
512 */
513 static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
514 {
515 BOOL gray;
516
517 gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
518 EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
519 gray = ((style & WS_MAXIMIZE) != 0);
520 EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
521 gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
522 EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
523 gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
524 EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
525 gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
526 EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
527 gray = (clsStyle & CS_NOCLOSE) != 0;
528
529 /* The menu item must keep its state if it's disabled */
530 if(gray)
531 EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
532 }
533
534
535 /******************************************************************************
536 *
537 * UINT MENU_GetStartOfNextColumn(
538 * HMENU hMenu )
539 *
540 *****************************************************************************/
541
542 static UINT MENU_GetStartOfNextColumn(
543 HMENU hMenu )
544 {
545 POPUPMENU *menu = MENU_GetMenu(hMenu);
546 UINT i;
547
548 if(!menu)
549 return NO_SELECTED_ITEM;
550
551 i = menu->FocusedItem + 1;
552 if( i == NO_SELECTED_ITEM )
553 return i;
554
555 for( ; i < menu->nItems; ++i ) {
556 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
557 return i;
558 }
559
560 return NO_SELECTED_ITEM;
561 }
562
563
564 /******************************************************************************
565 *
566 * UINT MENU_GetStartOfPrevColumn(
567 * HMENU hMenu )
568 *
569 *****************************************************************************/
570
571 static UINT MENU_GetStartOfPrevColumn(
572 HMENU hMenu )
573 {
574 POPUPMENU *menu = MENU_GetMenu(hMenu);
575 UINT i;
576
577 if( !menu )
578 return NO_SELECTED_ITEM;
579
580 if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
581 return NO_SELECTED_ITEM;
582
583 /* Find the start of the column */
584
585 for(i = menu->FocusedItem; i != 0 &&
586 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
587 --i); /* empty */
588
589 if(i == 0)
590 return NO_SELECTED_ITEM;
591
592 for(--i; i != 0; --i) {
593 if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
594 break;
595 }
596
597 TRACE("ret %d.\n", i );
598
599 return i;
600 }
601
602
603
604 /***********************************************************************
605 * MENU_FindItem
606 *
607 * Find a menu item. Return a pointer on the item, and modifies *hmenu
608 * in case the item was in a sub-menu.
609 */
610 static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
611 {
612 POPUPMENU *menu;
613 MENUITEM *fallback = NULL;
614 UINT fallback_pos = 0;
615 UINT i;
616
617 if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
618 if (wFlags & MF_BYPOSITION)
619 {
620 if (*nPos >= menu->nItems) return NULL;
621 return &menu->items[*nPos];
622 }
623 else
624 {
625 MENUITEM *item = menu->items;
626 for (i = 0; i < menu->nItems; i++, item++)
627 {
628 if (item->fType & MF_POPUP)
629 {
630 HMENU hsubmenu = item->hSubMenu;
631 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
632 if (subitem)
633 {
634 *hmenu = hsubmenu;
635 return subitem;
636 }
637 else if (item->wID == *nPos)
638 {
639 /* fallback to this item if nothing else found */
640 fallback_pos = i;
641 fallback = item;
642 }
643 }
644 else if (item->wID == *nPos)
645 {
646 *nPos = i;
647 return item;
648 }
649 }
650 }
651
652 if (fallback)
653 *nPos = fallback_pos;
654
655 return fallback;
656 }
657
658 /***********************************************************************
659 * MENU_FindSubMenu
660 *
661 * Find a Sub menu. Return the position of the submenu, and modifies
662 * *hmenu in case it is found in another sub-menu.
663 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
664 */
665 static UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
666 {
667 POPUPMENU *menu;
668 UINT i;
669 MENUITEM *item;
670 if (((*hmenu)==(HMENU)0xffff) ||
671 (!(menu = MENU_GetMenu(*hmenu))))
672 return NO_SELECTED_ITEM;
673 item = menu->items;
674 for (i = 0; i < menu->nItems; i++, item++) {
675 if(!(item->fType & MF_POPUP)) continue;
676 if (item->hSubMenu == hSubTarget) {
677 return i;
678 }
679 else {
680 HMENU hsubmenu = item->hSubMenu;
681 UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
682 if (pos != NO_SELECTED_ITEM) {
683 *hmenu = hsubmenu;
684 return pos;
685 }
686 }
687 }
688 return NO_SELECTED_ITEM;
689 }
690
691 /***********************************************************************
692 * MENU_FreeItemData
693 */
694 static void MENU_FreeItemData( MENUITEM* item )
695 {
696 /* delete text */
697 HeapFree( GetProcessHeap(), 0, item->text );
698 }
699
700 /***********************************************************************
701 * MENU_AdjustMenuItemRect
702 *
703 * Adjust menu item rectangle according to scrolling state.
704 */
705 static void
706 MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
707 {
708 if (menu->bScrolling)
709 {
710 UINT arrow_bitmap_height;
711 BITMAP bmp;
712
713 GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
714 arrow_bitmap_height = bmp.bmHeight;
715 rect->top += arrow_bitmap_height - menu->nScrollPos;
716 rect->bottom += arrow_bitmap_height - menu->nScrollPos;
717 }
718 }
719
720
721 /***********************************************************************
722 * MENU_FindItemByCoords
723 *
724 * Find the item at the specified coordinates (screen coords). Does
725 * not work for child windows and therefore should not be called for
726 * an arbitrary system menu.
727 */
728 static MENUITEM *MENU_FindItemByCoords( const POPUPMENU *menu,
729 POINT pt, UINT *pos )
730 {
731 MENUITEM *item;
732 UINT i;
733 RECT rect;
734
735 if (!GetWindowRect(menu->hWnd, &rect)) return NULL;
736 if (GetWindowLongW( menu->hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) pt.x = rect.right - 1 - pt.x;
737 else pt.x -= rect.left;
738 pt.y -= rect.top;
739 item = menu->items;
740 for (i = 0; i < menu->nItems; i++, item++)
741 {
742 rect = item->rect;
743 MENU_AdjustMenuItemRect(menu, &rect);
744 if (PtInRect(&rect, pt))
745 {
746 if (pos) *pos = i;
747 return item;
748 }
749 }
750 return NULL;
751 }
752
753
754 /***********************************************************************
755 * MENU_FindItemByKey
756 *
757 * Find the menu item selected by a key press.
758 * Return item id, -1 if none, -2 if we should close the menu.
759 */
760 static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
761 WCHAR key, BOOL forceMenuChar )
762 {
763 TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
764
765 if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
766
767 if (hmenu)
768 {
769 POPUPMENU *menu = MENU_GetMenu( hmenu );
770 MENUITEM *item = menu->items;
771 LRESULT menuchar;
772
773 if( !forceMenuChar )
774 {
775 UINT i;
776
777 for (i = 0; i < menu->nItems; i++, item++)
778 {
779 if( item->text)
780 {
781 WCHAR *p = item->text - 2;
782 do
783 {
784 p = strchrW (p + 2, '&');
785 }
786 while (p != NULL && p [1] == '&');
787 if (p && (toupperW(p[1]) == toupperW(key))) return i;
788 }
789 }
790 }
791 menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
792 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
793 if (HIWORD(menuchar) == MNC_EXECUTE) return LOWORD(menuchar);
794 if (HIWORD(menuchar) == MNC_CLOSE) return (UINT)(-2);
795 }
796 return (UINT)(-1);
797 }
798
799
800 /***********************************************************************
801 * MENU_GetBitmapItemSize
802 *
803 * Get the size of a bitmap item.
804 */
805 static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
806 HWND hwndOwner)
807 {
808 BITMAP bm;
809 HBITMAP bmp = lpitem->hbmpItem;
810
811 size->cx = size->cy = 0;
812
813 /* check if there is a magic menu item associated with this item */
814 switch( (INT_PTR) bmp )
815 {
816 case (INT_PTR)HBMMENU_CALLBACK:
817 {
818 MEASUREITEMSTRUCT measItem;
819 measItem.CtlType = ODT_MENU;
820 measItem.CtlID = 0;
821 measItem.itemID = lpitem->wID;
822 measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
823 measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
824 measItem.itemData = lpitem->dwItemData;
825 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&measItem);
826 size->cx = measItem.itemWidth;
827 size->cy = measItem.itemHeight;
828 return;
829 }
830 break;
831 case (INT_PTR)HBMMENU_SYSTEM:
832 if (lpitem->dwItemData)
833 {
834 bmp = (HBITMAP)lpitem->dwItemData;
835 break;
836 }
837 /* fall through */
838 case (INT_PTR)HBMMENU_MBAR_RESTORE:
839 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
840 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
841 case (INT_PTR)HBMMENU_MBAR_CLOSE:
842 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
843 size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
844 size->cy = size->cx;
845 return;
846 case (INT_PTR)HBMMENU_POPUP_CLOSE:
847 case (INT_PTR)HBMMENU_POPUP_RESTORE:
848 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
849 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
850 size->cx = GetSystemMetrics( SM_CXMENUSIZE);
851 size->cy = GetSystemMetrics( SM_CYMENUSIZE);
852 return;
853 }
854 if (GetObjectW(bmp, sizeof(bm), &bm ))
855 {
856 size->cx = bm.bmWidth;
857 size->cy = bm.bmHeight;
858 }
859 }
860
861 /***********************************************************************
862 * MENU_DrawBitmapItem
863 *
864 * Draw a bitmap item.
865 */
866 static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
867 HMENU hmenu, HWND hwndOwner, UINT odaction, BOOL menuBar)
868 {
869 BITMAP bm;
870 DWORD rop;
871 HDC hdcMem;
872 HBITMAP bmp;
873 int w = rect->right - rect->left;
874 int h = rect->bottom - rect->top;
875 int bmp_xoffset = 0;
876 int left, top;
877 HBITMAP hbmToDraw = lpitem->hbmpItem;
878 bmp = hbmToDraw;
879
880 /* Check if there is a magic menu item associated with this item */
881 if (IS_MAGIC_BITMAP(hbmToDraw))
882 {
883 UINT flags = 0;
884 WCHAR bmchr = 0;
885 RECT r;
886
887 switch((INT_PTR)hbmToDraw)
888 {
889 case (INT_PTR)HBMMENU_SYSTEM:
890 if (lpitem->dwItemData)
891 {
892 bmp = (HBITMAP)lpitem->dwItemData;
893 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
894 }
895 else
896 {
897 static HBITMAP hBmpSysMenu;
898
899 if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
900 bmp = hBmpSysMenu;
901 if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
902 /* only use right half of the bitmap */
903 bmp_xoffset = bm.bmWidth / 2;
904 bm.bmWidth -= bmp_xoffset;
905 }
906 goto got_bitmap;
907 case (INT_PTR)HBMMENU_MBAR_RESTORE:
908 flags = DFCS_CAPTIONRESTORE;
909 break;
910 case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
911 flags = DFCS_CAPTIONMIN;
912 break;
913 case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
914 flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
915 break;
916 case (INT_PTR)HBMMENU_MBAR_CLOSE:
917 flags = DFCS_CAPTIONCLOSE;
918 break;
919 case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
920 flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
921 break;
922 case (INT_PTR)HBMMENU_CALLBACK:
923 {
924 DRAWITEMSTRUCT drawItem;
925 drawItem.CtlType = ODT_MENU;
926 drawItem.CtlID = 0;
927 drawItem.itemID = lpitem->wID;
928 drawItem.itemAction = odaction;
929 drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
930 drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
931 drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
932 drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
933 drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
934 drawItem.hwndItem = (HWND)hmenu;
935 drawItem.hDC = hdc;
936 drawItem.itemData = lpitem->dwItemData;
937 drawItem.rcItem = *rect;
938 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
939 return;
940 }
941 break;
942 case (INT_PTR)HBMMENU_POPUP_CLOSE:
943 bmchr = 0x72;
944 break;
945 case (INT_PTR)HBMMENU_POPUP_RESTORE:
946 bmchr = 0x32;
947 break;
948 case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
949 bmchr = 0x31;
950 break;
951 case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
952 bmchr = 0x30;
953 break;
954 default:
955 FIXME("Magic %p not implemented\n", hbmToDraw);
956 return;
957 }
958 if (bmchr)
959 {
960 /* draw the magic bitmaps using marlett font characters */
961 /* FIXME: fontsize and the position (x,y) could probably be better */
962 HFONT hfont, hfontsav;
963 LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
964 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
965 { 'M','a','r','l','e','t','t',0 } };
966 logfont.lfHeight = min( h, w) - 5 ;
967 TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
968 hfont = CreateFontIndirectW( &logfont);
969 hfontsav = SelectObject(hdc, hfont);
970 TextOutW( hdc, rect->left, rect->top + 2, &bmchr, 1);
971 SelectObject(hdc, hfontsav);
972 DeleteObject( hfont);
973 }
974 else
975 {
976 r = *rect;
977 InflateRect( &r, -1, -1 );
978 if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
979 DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
980 }
981 return;
982 }
983
984 if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
985
986 got_bitmap:
987 hdcMem = CreateCompatibleDC( hdc );
988 SelectObject( hdcMem, bmp );
989
990 /* handle fontsize > bitmap_height */
991 top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
992 left=rect->left;
993 rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
994 if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
995 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
996 BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
997 DeleteDC( hdcMem );
998 }
999
1000
1001 /***********************************************************************
1002 * MENU_CalcItemSize
1003 *
1004 * Calculate the size of the menu item and store it in lpitem->rect.
1005 */
1006 static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
1007 INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
1008 {
1009 WCHAR *p;
1010 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1011 UINT arrow_bitmap_width;
1012 BITMAP bm;
1013 INT itemheight;
1014
1015 TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1016 debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
1017 (menuBar ? " (MenuBar)" : ""));
1018
1019 GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
1020 arrow_bitmap_width = bm.bmWidth;
1021
1022 /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
1023 if( !menucharsize.cx ) {
1024 menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
1025 /* Win95/98/ME will use menucharsize.cy here. Testing is possible
1026 * but it is unlikely an application will depend on that */
1027 ODitemheight = HIWORD( GetDialogBaseUnits());
1028 }
1029
1030 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
1031
1032 if (lpitem->fType & MF_OWNERDRAW)
1033 {
1034 MEASUREITEMSTRUCT mis;
1035 mis.CtlType = ODT_MENU;
1036 mis.CtlID = 0;
1037 mis.itemID = lpitem->wID;
1038 mis.itemData = lpitem->dwItemData;
1039 mis.itemHeight = ODitemheight;
1040 mis.itemWidth = 0;
1041 SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1042 /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
1043 * width of a menufont character to the width of an owner-drawn menu.
1044 */
1045 lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
1046 if (menuBar) {
1047 /* under at least win95 you seem to be given a standard
1048 height for the menu and the height value is ignored */
1049 lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
1050 } else
1051 lpitem->rect.bottom += mis.itemHeight;
1052
1053 TRACE("id=%04lx size=%dx%d\n",
1054 lpitem->wID, lpitem->rect.right-lpitem->rect.left,
1055 lpitem->rect.bottom-lpitem->rect.top);
1056 return;
1057 }
1058
1059 if (lpitem->fType & MF_SEPARATOR)
1060 {
1061 lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
1062 if( !menuBar)
1063 lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
1064 return;
1065 }
1066
1067 itemheight = 0;
1068 lpitem->xTab = 0;
1069
1070 if (!menuBar) {
1071 if (lpitem->hbmpItem) {
1072 SIZE size;
1073
1074 MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
1075 /* Keep the size of the bitmap in callback mode to be able
1076 * to draw it correctly */
1077 lpitem->bmpsize = size;
1078 lppop->textOffset = max( lppop->textOffset, size.cx);
1079 lpitem->rect.right += size.cx + 2;
1080 itemheight = size.cy + 2;
1081 }
1082 if( !(lppop->dwStyle & MNS_NOCHECK))
1083 lpitem->rect.right += check_bitmap_width;
1084 lpitem->rect.right += 4 + menucharsize.cx;
1085 lpitem->xTab = lpitem->rect.right;
1086 lpitem->rect.right += arrow_bitmap_width;
1087 } else if (lpitem->hbmpItem) { /* menuBar */
1088 SIZE size;
1089
1090 MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
1091 lpitem->bmpsize = size;
1092 lpitem->rect.right += size.cx;
1093 if( lpitem->text) lpitem->rect.right += 2;
1094 itemheight = size.cy;
1095 }
1096
1097 /* it must be a text item - unless it's the system menu */
1098 if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1099 HFONT hfontOld = NULL;
1100 RECT rc = lpitem->rect;
1101 LONG txtheight, txtwidth;
1102
1103 if ( lpitem->fState & MFS_DEFAULT ) {
1104 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1105 }
1106 if (menuBar) {
1107 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1108 DT_SINGLELINE|DT_CALCRECT);
1109 lpitem->rect.right += rc.right - rc.left;
1110 itemheight = max( max( itemheight, txtheight),
1111 GetSystemMetrics( SM_CYMENU) - 1);
1112 lpitem->rect.right += 2 * menucharsize.cx;
1113 } else {
1114 if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
1115 RECT tmprc = rc;
1116 LONG tmpheight;
1117 int n = (int)( p - lpitem->text);
1118 /* Item contains a tab (only meaningful in popup menus) */
1119 /* get text size before the tab */
1120 txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
1121 DT_SINGLELINE|DT_CALCRECT);
1122 txtwidth = rc.right - rc.left;
1123 p += 1; /* advance past the Tab */
1124 /* get text size after the tab */
1125 tmpheight = DrawTextW( hdc, p, -1, &tmprc,
1126 DT_SINGLELINE|DT_CALCRECT);
1127 lpitem->xTab += txtwidth;
1128 txtheight = max( txtheight, tmpheight);
1129 txtwidth += menucharsize.cx + /* space for the tab */
1130 tmprc.right - tmprc.left; /* space for the short cut */
1131 } else {
1132 txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
1133 DT_SINGLELINE|DT_CALCRECT);
1134 txtwidth = rc.right - rc.left;
1135 lpitem->xTab += txtwidth;
1136 }
1137 lpitem->rect.right += 2 + txtwidth;
1138 itemheight = max( itemheight,
1139 max( txtheight + 2, menucharsize.cy + 4));
1140 }
1141 if (hfontOld) SelectObject (hdc, hfontOld);
1142 } else if( menuBar) {
1143 itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
1144 }
1145 lpitem->rect.bottom += itemheight;
1146 TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
1147 }
1148
1149
1150 /***********************************************************************
1151 * MENU_GetMaxPopupHeight
1152 */
1153 static UINT
1154 MENU_GetMaxPopupHeight(const POPUPMENU *lppop)
1155 {
1156 if (lppop->cyMax)
1157 return lppop->cyMax;
1158 return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
1159 }
1160
1161
1162 /***********************************************************************
1163 * MENU_PopupMenuCalcSize
1164 *
1165 * Calculate the size of a popup menu.
1166 */
1167 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop )
1168 {
1169 MENUITEM *lpitem;
1170 HDC hdc;
1171 UINT start, i;
1172 int textandbmp = FALSE;
1173 int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
1174
1175 lppop->Width = lppop->Height = 0;
1176 if (lppop->nItems == 0) return;
1177 hdc = GetDC( 0 );
1178
1179 SelectObject( hdc, get_menu_font(FALSE));
1180
1181 start = 0;
1182 maxX = 2 + 1;
1183
1184 lppop->textOffset = 0;
1185
1186 while (start < lppop->nItems)
1187 {
1188 lpitem = &lppop->items[start];
1189 orgX = maxX;
1190 if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1191 orgX += MENU_COL_SPACE;
1192 orgY = MENU_TOP_MARGIN;
1193
1194 maxTab = maxTabWidth = 0;
1195 /* Parse items until column break or end of menu */
1196 for (i = start; i < lppop->nItems; i++, lpitem++)
1197 {
1198 if ((i != start) &&
1199 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1200
1201 MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1202 maxX = max( maxX, lpitem->rect.right );
1203 orgY = lpitem->rect.bottom;
1204 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1205 {
1206 maxTab = max( maxTab, lpitem->xTab );
1207 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
1208 }
1209 if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
1210 }
1211
1212 /* Finish the column (set all items to the largest width found) */
1213 maxX = max( maxX, maxTab + maxTabWidth );
1214 for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
1215 {
1216 lpitem->rect.right = maxX;
1217 if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1218 lpitem->xTab = maxTab;
1219
1220 }
1221 lppop->Height = max( lppop->Height, orgY );
1222 }
1223
1224 lppop->Width = maxX;
1225 /* if none of the items have both text and bitmap then
1226 * the text and bitmaps are all aligned on the left. If there is at
1227 * least one item with both text and bitmap then bitmaps are
1228 * on the left and texts left aligned with the right hand side
1229 * of the bitmaps */
1230 if( !textandbmp) lppop->textOffset = 0;
1231
1232 /* space for 3d border */
1233 lppop->Height += MENU_BOTTOM_MARGIN;
1234 lppop->Width += 2;
1235
1236 /* Adjust popup height if it exceeds maximum */
1237 maxHeight = MENU_GetMaxPopupHeight(lppop);
1238 lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
1239 if (lppop->Height >= maxHeight)
1240 {
1241 lppop->Height = maxHeight;
1242 lppop->bScrolling = TRUE;
1243 }
1244 else
1245 {
1246 lppop->bScrolling = FALSE;
1247 }
1248
1249 ReleaseDC( 0, hdc );
1250 }
1251
1252
1253 /***********************************************************************
1254 * MENU_MenuBarCalcSize
1255 *
1256 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
1257 * height is off by 1 pixel which causes lengthy window relocations when
1258 * active document window is maximized/restored.
1259 *
1260 * Calculate the size of the menu bar.
1261 */
1262 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
1263 LPPOPUPMENU lppop, HWND hwndOwner )
1264 {
1265 MENUITEM *lpitem;
1266 UINT start, i, helpPos;
1267 int orgX, orgY, maxY;
1268
1269 if ((lprect == NULL) || (lppop == NULL)) return;
1270 if (lppop->nItems == 0) return;
1271 TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1272 lppop->Width = lprect->right - lprect->left;
1273 lppop->Height = 0;
1274 maxY = lprect->top+1;
1275 start = 0;
1276 helpPos = ~0U;
1277 lppop->textOffset = 0;
1278 while (start < lppop->nItems)
1279 {
1280 lpitem = &lppop->items[start];
1281 orgX = lprect->left;
1282 orgY = maxY;
1283
1284 /* Parse items until line break or end of menu */
1285 for (i = start; i < lppop->nItems; i++, lpitem++)
1286 {
1287 if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
1288 if ((i != start) &&
1289 (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
1290
1291 TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
1292 debug_print_menuitem (" item: ", lpitem, "");
1293 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1294
1295 if (lpitem->rect.right > lprect->right)
1296 {
1297 if (i != start) break;
1298 else lpitem->rect.right = lprect->right;
1299 }
1300 maxY = max( maxY, lpitem->rect.bottom );
1301 orgX = lpitem->rect.right;
1302 }
1303
1304 /* Finish the line (set all items to the largest height found) */
1305 while (start < i) lppop->items[start++].rect.bottom = maxY;
1306 }
1307
1308 lprect->bottom = maxY;
1309 lppop->Height = lprect->bottom - lprect->top;
1310
1311 /* Flush right all items between the MF_RIGHTJUSTIFY and */
1312 /* the last item (if several lines, only move the last line) */
1313 if (helpPos == ~0U) return;
1314 lpitem = &lppop->items[lppop->nItems-1];
1315 orgY = lpitem->rect.top;
1316 orgX = lprect->right;
1317 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
1318 if (lpitem->rect.top != orgY) break; /* Other line */
1319 if (lpitem->rect.right >= orgX) break; /* Too far right already */
1320 lpitem->rect.left += orgX - lpitem->rect.right;
1321 lpitem->rect.right = orgX;
1322 orgX = lpitem->rect.left;
1323 }
1324 }
1325
1326
1327 /***********************************************************************
1328 * MENU_DrawScrollArrows
1329 *
1330 * Draw scroll arrows.
1331 */
1332 static void
1333 MENU_DrawScrollArrows(const POPUPMENU *lppop, HDC hdc)
1334 {
1335 HDC hdcMem = CreateCompatibleDC(hdc);
1336 HBITMAP hOrigBitmap;
1337 UINT arrow_bitmap_width, arrow_bitmap_height;
1338 BITMAP bmp;
1339 RECT rect;
1340
1341 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1342 arrow_bitmap_width = bmp.bmWidth;
1343 arrow_bitmap_height = bmp.bmHeight;
1344
1345
1346 if (lppop->nScrollPos)
1347 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
1348 else
1349 hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
1350 rect.left = 0;
1351 rect.top = 0;
1352 rect.right = lppop->Width;
1353 rect.bottom = arrow_bitmap_height;
1354 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1355 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
1356 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1357 rect.top = lppop->Height - arrow_bitmap_height;
1358 rect.bottom = lppop->Height;
1359 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
1360 if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
1361 SelectObject(hdcMem, get_down_arrow_bitmap());
1362 else
1363 SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
1364 BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
1365 lppop->Height - arrow_bitmap_height,
1366 arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
1367 SelectObject(hdcMem, hOrigBitmap);
1368 DeleteDC(hdcMem);
1369 }
1370
1371
1372 /***********************************************************************
1373 * draw_popup_arrow
1374 *
1375 * Draws the popup-menu arrow.
1376 */
1377 static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
1378 UINT arrow_bitmap_height)
1379 {
1380 HDC hdcMem = CreateCompatibleDC( hdc );
1381 HBITMAP hOrigBitmap;
1382
1383 hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
1384 BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
1385 (rect.top + rect.bottom - arrow_bitmap_height) / 2,
1386 arrow_bitmap_width, arrow_bitmap_height,
1387 hdcMem, 0, 0, SRCCOPY );
1388 SelectObject( hdcMem, hOrigBitmap );
1389 DeleteDC( hdcMem );
1390 }
1391 /***********************************************************************
1392 * MENU_DrawMenuItem
1393 *
1394 * Draw a single menu item.
1395 */
1396 static void MENU_DrawMenuItem( HWND hwnd, HMENU hmenu, HWND hwndOwner, HDC hdc, MENUITEM *lpitem,
1397 UINT height, BOOL menuBar, UINT odaction )
1398 {
1399 RECT rect;
1400 BOOL flat_menu = FALSE;
1401 int bkgnd;
1402 UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1403 POPUPMENU *menu = MENU_GetMenu(hmenu);
1404 RECT bmprc;
1405
1406 debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
1407
1408 if (!menuBar) {
1409 BITMAP bmp;
1410 GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
1411 arrow_bitmap_width = bmp.bmWidth;
1412 arrow_bitmap_height = bmp.bmHeight;
1413 }
1414
1415 if (lpitem->fType & MF_SYSMENU)
1416 {
1417 if( !IsIconic(hwnd) )
1418 NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
1419 return;
1420 }
1421
1422 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1423 bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
1424
1425 /* Setup colors */
1426
1427 if (lpitem->fState & MF_HILITE)
1428 {
1429 if(menuBar && !flat_menu) {
1430 SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
1431 SetBkColor(hdc, GetSysColor(COLOR_MENU));
1432 } else {
1433 if(lpitem->fState & MF_GRAYED)
1434 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
1435 else
1436 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1437 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1438 }
1439 }
1440 else
1441 {
1442 if (lpitem->fState & MF_GRAYED)
1443 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
1444 else
1445 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1446 SetBkColor( hdc, GetSysColor( bkgnd ) );
1447 }
1448
1449 TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->rect));
1450 rect = lpitem->rect;
1451 MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
1452
1453 if (lpitem->fType & MF_OWNERDRAW)
1454 {
1455 /*
1456 ** Experimentation under Windows reveals that an owner-drawn
1457 ** menu is given the rectangle which includes the space it requested
1458 ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
1459 ** and a popup-menu arrow. This is the value of lpitem->rect.
1460 ** Windows will leave all drawing to the application except for
1461 ** the popup-menu arrow. Windows always draws that itself, after
1462 ** the menu owner has finished drawing.
1463 */
1464 DRAWITEMSTRUCT dis;
1465
1466 dis.CtlType = ODT_MENU;
1467 dis.CtlID = 0;
1468 dis.itemID = lpitem->wID;
1469 dis.itemData = lpitem->dwItemData;
1470 dis.itemState = 0;
1471 if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1472 if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED|ODS_DISABLED;
1473 if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
1474 dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1475 dis.hwndItem = (HWND)hmenu;
1476 dis.hDC = hdc;
1477 dis.rcItem = rect;
1478 TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1479 "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1480 dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1481 dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1482 SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1483 /* Draw the popup-menu arrow */
1484 if (lpitem->fType & MF_POPUP)
1485 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1486 arrow_bitmap_height);
1487 return;
1488 }
1489
1490 if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
1491
1492 if (lpitem->fState & MF_HILITE)
1493 {
1494 if (flat_menu)
1495 {
1496 InflateRect (&rect, -1, -1);
1497 FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
1498 InflateRect (&rect, 1, 1);
1499 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1500 }
1501 else
1502 {
1503 if(menuBar)
1504 DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
1505 else
1506 FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
1507 }
1508 }
1509 else
1510 FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
1511
1512 SetBkMode( hdc, TRANSPARENT );
1513
1514 /* vertical separator */
1515 if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
1516 {
1517 HPEN oldPen;
1518 RECT rc = rect;
1519
1520 rc.left -= MENU_COL_SPACE / 2 + 1;
1521 rc.top = 3;
1522 rc.bottom = height - 3;
1523 if (flat_menu)
1524 {
1525 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1526 MoveToEx( hdc, rc.left, rc.top, NULL );
1527 LineTo( hdc, rc.left, rc.bottom );
1528 SelectObject( hdc, oldPen );
1529 }
1530 else
1531 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
1532 }
1533
1534 /* horizontal separator */
1535 if (lpitem->fType & MF_SEPARATOR)
1536 {
1537 HPEN oldPen;
1538 RECT rc = rect;
1539
1540 rc.left++;
1541 rc.right--;
1542 rc.top = ( rc.top + rc.bottom) / 2;
1543 if (flat_menu)
1544 {
1545 oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
1546 MoveToEx( hdc, rc.left, rc.top, NULL );
1547 LineTo( hdc, rc.right, rc.top );
1548 SelectObject( hdc, oldPen );
1549 }
1550 else
1551 DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1552 return;
1553 }
1554
1555 /* helper lines for debugging */
1556 /* FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1557 SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1558 MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1559 LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
1560 */
1561
1562 if (lpitem->hbmpItem) {
1563 /* calculate the bitmap rectangle in coordinates relative
1564 * to the item rectangle */
1565 if( menuBar) {
1566 if( lpitem->hbmpItem == HBMMENU_CALLBACK)
1567 bmprc.left = 3;
1568 else
1569 bmprc.left = lpitem->text ? menucharsize.cx : 0;
1570 }
1571 else if (menu->dwStyle & MNS_NOCHECK)
1572 bmprc.left = 4;
1573 else if (menu->dwStyle & MNS_CHECKORBMP)
1574 bmprc.left = 2;
1575 else
1576 bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
1577 bmprc.right = bmprc.left + lpitem->bmpsize.cx;
1578 if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1579 bmprc.top = 0;
1580 else
1581 bmprc.top = (rect.bottom - rect.top -
1582 lpitem->bmpsize.cy) / 2;
1583 bmprc.bottom = bmprc.top + lpitem->bmpsize.cy;
1584 }
1585
1586 if (!menuBar)
1587 {
1588 HBITMAP bm;
1589 INT y = rect.top + rect.bottom;
1590 RECT rc = rect;
1591 int checked = FALSE;
1592 UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1593 UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1594 /* Draw the check mark
1595 *
1596 * FIXME:
1597 * Custom checkmark bitmaps are monochrome but not always 1bpp.
1598 */
1599 if( !(menu->dwStyle & MNS_NOCHECK)) {
1600 bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
1601 lpitem->hUnCheckBit;
1602 if (bm) /* we have a custom bitmap */
1603 {
1604 HDC hdcMem = CreateCompatibleDC( hdc );
1605
1606 SelectObject( hdcMem, bm );
1607 BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
1608 check_bitmap_width, check_bitmap_height,
1609 hdcMem, 0, 0, SRCCOPY );
1610 DeleteDC( hdcMem );
1611 checked = TRUE;
1612 }
1613 else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
1614 {
1615 RECT r;
1616 HBITMAP bm = CreateBitmap( check_bitmap_width,
1617 check_bitmap_height, 1, 1, NULL );
1618 HDC hdcMem = CreateCompatibleDC( hdc );
1619
1620 SelectObject( hdcMem, bm );
1621 SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
1622 DrawFrameControl( hdcMem, &r, DFC_MENU,
1623 (lpitem->fType & MFT_RADIOCHECK) ?
1624 DFCS_MENUBULLET : DFCS_MENUCHECK );
1625 BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom,
1626 hdcMem, 0, 0, SRCCOPY );
1627 DeleteDC( hdcMem );
1628 DeleteObject( bm );
1629 checked = TRUE;
1630 }
1631 }
1632 if( lpitem->hbmpItem &&
1633 !( checked && (menu->dwStyle & MNS_CHECKORBMP))) {
1634 POINT origorg;
1635 /* some applications make this assumption on the DC's origin */
1636 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1637 MENU_DrawBitmapItem(hdc, lpitem, &bmprc, hmenu, hwndOwner,
1638 odaction, FALSE);
1639 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1640 }
1641 /* Draw the popup-menu arrow */
1642 if (lpitem->fType & MF_POPUP)
1643 draw_popup_arrow( hdc, rect, arrow_bitmap_width,
1644 arrow_bitmap_height);
1645 rect.left += 4;
1646 if( !(menu->dwStyle & MNS_NOCHECK))
1647 rect.left += check_bitmap_width;
1648 rect.right -= arrow_bitmap_width;
1649 }
1650 else if( lpitem->hbmpItem)
1651 { /* Draw the bitmap */
1652 POINT origorg;
1653
1654 SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1655 MENU_DrawBitmapItem( hdc, lpitem, &bmprc, hmenu, hwndOwner,
1656 odaction, menuBar);
1657 SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1658 }
1659 /* process text if present */
1660 if (lpitem->text)
1661 {
1662 register int i;
1663 HFONT hfontOld = 0;
1664
1665 UINT uFormat = (menuBar) ?
1666 DT_CENTER | DT_VCENTER | DT_SINGLELINE :
1667 DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1668
1669 if( !(menu->dwStyle & MNS_CHECKORBMP))
1670 rect.left += menu->textOffset;
1671
1672 if ( lpitem->fState & MFS_DEFAULT )
1673 {
1674 hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1675 }
1676
1677 if (menuBar) {
1678 if( lpitem->hbmpItem)
1679 rect.left += lpitem->bmpsize.cx;
1680 if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
1681 rect.left += menucharsize.cx;
1682 rect.right -= menucharsize.cx;
1683 }
1684
1685 for (i = 0; lpitem->text[i]; i++)
1686 if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
1687 break;
1688
1689 if(lpitem->fState & MF_GRAYED)
1690 {
1691 if (!(lpitem->fState & MF_HILITE) )
1692 {
1693 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1694 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1695 DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1696 --rect.left; --rect.top; --rect.right; --rect.bottom;
1697 }
1698 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1699 }
1700
1701 DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
1702
1703 /* paint the shortcut text */
1704 if (!menuBar && lpitem->text[i]) /* There's a tab or flush-right char */
1705 {
1706 if (lpitem->text[i] == '\t')
1707 {
1708 rect.left = lpitem->xTab;
1709 uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
1710 }
1711 else
1712 {
1713 rect.right = lpitem->xTab;
1714 uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
1715 }
1716
1717 if(lpitem->fState & MF_GRAYED)
1718 {
1719 if (!(lpitem->fState & MF_HILITE) )
1720 {
1721 ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1722 SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1723 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1724 --rect.left; --rect.top; --rect.right; --rect.bottom;
1725 }
1726 SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
1727 }
1728 DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1729 }
1730
1731 if (hfontOld)
1732 SelectObject (hdc, hfontOld);
1733 }
1734 }
1735
1736
1737 /***********************************************************************
1738 * MENU_DrawPopupMenu
1739 *
1740 * Paint a popup menu.
1741 */
1742 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
1743 {
1744 HBRUSH hPrevBrush = 0;
1745 RECT rect;
1746
1747 TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1748
1749 GetClientRect( hwnd, &rect );
1750
1751 if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
1752 && (SelectObject( hdc, get_menu_font(FALSE))))
1753 {
1754 HPEN hPrevPen;
1755
1756 Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
1757
1758 hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
1759 if( hPrevPen )
1760 {
1761 POPUPMENU *menu;
1762 BOOL flat_menu = FALSE;
1763
1764 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
1765 if (flat_menu)
1766 FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
1767 else
1768 DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
1769
1770 if( (menu = MENU_GetMenu( hmenu )))
1771 {
1772 TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1773 /* draw menu items */
1774 if( menu->nItems)
1775 {
1776 MENUITEM *item;
1777 UINT u;
1778
1779 item = menu->items;
1780 for( u = menu->nItems; u > 0; u--, item++)
1781 MENU_DrawMenuItem( hwnd, hmenu, menu->hwndOwner, hdc,
1782 item, menu->Height, FALSE, ODA_DRAWENTIRE );
1783 }
1784 /* draw scroll arrows */
1785 if (menu->bScrolling)
1786 MENU_DrawScrollArrows(menu, hdc);
1787 }
1788 } else
1789 {
1790 SelectObject( hdc, hPrevBrush );
1791 }
1792 }
1793 }
1794
1795 /***********************************************************************
1796 * MENU_DrawMenuBar
1797 *
1798 * Paint a menu bar. Returns the height of the menu bar.
1799 * called from [windows/nonclient.c]
1800 */
1801 UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
1802 BOOL suppress_draw)
1803 {
1804 LPPOPUPMENU lppop;
1805 HFONT hfontOld = 0;
1806 HMENU hMenu = GetMenu(hwnd);
1807
1808 lppop = MENU_GetMenu( hMenu );
1809 if (lppop == NULL || lprect == NULL)
1810 {
1811 return GetSystemMetrics(SM_CYMENU);
1812 }
1813
1814 if (suppress_draw)
1815 {
1816 hfontOld = SelectObject( hDC, get_menu_font(FALSE));
1817
1818 if (lppop->Height == 0)
1819 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
1820
1821 lprect->bottom = lprect->top + lppop->Height;
1822
1823 if (hfontOld) SelectObject( hDC, hfontOld);
1824 return lppop->Height;
1825 }
1826 else
1827 return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
1828 }
1829
1830
1831 /***********************************************************************
1832 * MENU_ShowPopup
1833 *
1834 * Display a popup menu.
1835 */
1836 static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1837 INT x, INT y, INT xanchor, INT yanchor )
1838 {
1839 POPUPMENU *menu;
1840 INT width, height;
1841 POINT pt;
1842 HMONITOR monitor;
1843 MONITORINFO info;
1844 DWORD ex_style = 0;
1845
1846 TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
1847 hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1848
1849 if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
1850 if (menu->FocusedItem != NO_SELECTED_ITEM)
1851 {
1852 menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1853 menu->FocusedItem = NO_SELECTED_ITEM;
1854 }
1855
1856 /* store the owner for DrawItem */
1857 if (!IsWindow( hwndOwner ))
1858 {
1859 SetLastError( ERROR_INVALID_WINDOW_HANDLE );
1860 return FALSE;
1861 }
1862 menu->hwndOwner = hwndOwner;
1863
1864 menu->nScrollPos = 0;
1865 MENU_PopupMenuCalcSize( menu );
1866
1867 /* adjust popup menu pos so that it fits within the desktop */
1868
1869 width = menu->Width + GetSystemMetrics(SM_CXBORDER);
1870 height = menu->Height + GetSystemMetrics(SM_CYBORDER);
1871
1872 /* FIXME: should use item rect */
1873 pt.x = x;
1874 pt.y = y;
1875 monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
1876 info.cbSize = sizeof(info);
1877 GetMonitorInfoW( monitor, &info );
1878
1879 if (flags & TPM_LAYOUTRTL)
1880 {
1881 ex_style = WS_EX_LAYOUTRTL;
1882 flags ^= TPM_RIGHTALIGN;
1883 }
1884
1885 if( flags & TPM_RIGHTALIGN ) x -= width;
1886 if( flags & TPM_CENTERALIGN ) x -= width / 2;
1887
1888 if( flags & TPM_BOTTOMALIGN ) y -= height;
1889 if( flags & TPM_VCENTERALIGN ) y -= height / 2;
1890
1891 if( x + width > info.rcWork.right)
1892 {
1893 if( xanchor && x >= width - xanchor )
1894 x -= width - xanchor;
1895
1896 if( x + width > info.rcWork.right)
1897 x = info.rcWork.right - width;
1898 }
1899 if( x < info.rcWork.left ) x = info.rcWork.left;
1900
1901 if( y + height > info.rcWork.bottom)
1902 {
1903 if( yanchor && y >= height + yanchor )
1904 y -= height + yanchor;
1905
1906 if( y + height > info.rcWork.bottom)
1907 y = info.rcWork.bottom - height;
1908 }
1909 if( y < info.rcWork.top ) y = info.rcWork.top;
1910
1911 /* NOTE: In Windows, top menu popup is not owned. */
1912 menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
1913 WS_POPUP, x, y, width, height,
1914 hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
1915 (LPVOID)hmenu );
1916 if( !menu->hWnd ) return FALSE;
1917 if (!top_popup) {
1918 top_popup = menu->hWnd;
1919 top_popup_hmenu = hmenu;
1920 }
1921 /* Display the window */
1922
1923 SetWindowPos( menu->hWnd, HWND_TOPMOST, 0, 0, 0, 0,
1924 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
1925 UpdateWindow( menu->hWnd );
1926 return TRUE;
1927 }
1928
1929
1930 /***********************************************************************
1931 * MENU_EnsureMenuItemVisible
1932 */
1933 static void
1934 MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
1935 {
1936 if (lppop->bScrolling)
1937 {
1938 MENUITEM *item = &lppop->items[wIndex];
1939 UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
1940 UINT nOldPos = lppop->nScrollPos;
1941 RECT rc;
1942 UINT arrow_bitmap_height;
1943 BITMAP bmp;
1944
1945 GetClientRect(lppop->hWnd, &rc);
1946
1947 GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
1948 arrow_bitmap_height = bmp.bmHeight;
1949
1950 rc.top += arrow_bitmap_height;
1951 rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
1952
1953 nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
1954 if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
1955 {
1956
1957 lppop->nScrollPos = item->rect.bottom - nMaxHeight;
1958 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1959 MENU_DrawScrollArrows(lppop, hdc);
1960 }
1961 else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
1962 {
1963 lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
1964 ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
1965 MENU_DrawScrollArrows(lppop, hdc);
1966 }
1967 }
1968 }
1969
1970
1971 /***********************************************************************
1972 * MENU_SelectItem
1973 */
1974 static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
1975 BOOL sendMenuSelect, HMENU topmenu )
1976 {
1977 LPPOPUPMENU lppop;
1978 HDC hdc;
1979
1980 TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
1981
1982 lppop = MENU_GetMenu( hmenu );
1983 if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
1984
1985 if (lppop->FocusedItem == wIndex) return;
1986 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
1987 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
1988 if (!top_popup) {
1989 top_popup = lppop->hWnd;
1990 top_popup_hmenu = hmenu;
1991 }
1992
1993 SelectObject( hdc, get_menu_font(FALSE));
1994
1995 /* Clear previous highlighted item */
1996 if (lppop->FocusedItem != NO_SELECTED_ITEM)
1997 {
1998 lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
1999 MENU_DrawMenuItem(lppop->hWnd, hmenu, hwndOwner, hdc,&lppop->items[lppop->FocusedItem],
2000 lppop->Height, !(lppop->wFlags & MF_POPUP),
2001 ODA_SELECT );
2002 }
2003
2004 /* Highlight new item (if any) */
2005 lppop->FocusedItem = wIndex;
2006 if (lppop->FocusedItem != NO_SELECTED_ITEM)
2007 {
2008 if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
2009 lppop->items[wIndex].fState |= MF_HILITE;
2010 MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
2011 MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
2012 &lppop->items[wIndex], lppop->Height,
2013 !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2014 }
2015 if (sendMenuSelect)
2016 {
2017 MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2018 SendMessageW( hwndOwner, WM_MENUSELECT,
2019 MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2020 ip->fType | ip->fState |
2021 (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
2022 }
2023 }
2024 else if (sendMenuSelect) {
2025 if(topmenu){
2026 int pos;
2027 if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2028 POPUPMENU *ptm = MENU_GetMenu( topmenu );
2029 MENUITEM *ip = &ptm->items[pos];
2030 SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2031 ip->fType | ip->fState |
2032 (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2033 }
2034 }
2035 }
2036 ReleaseDC( lppop->hWnd, hdc );
2037 }
2038
2039
2040 /***********************************************************************
2041 * MENU_MoveSelection
2042 *
2043 * Moves currently selected item according to the offset parameter.
2044 * If there is no selection then it should select the last item if
2045 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
2046 */
2047 static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
2048 {
2049 INT i;
2050 POPUPMENU *menu;
2051
2052 TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2053
2054 menu = MENU_GetMenu( hmenu );
2055 if ((!menu) || (!menu->items)) return;
2056
2057 if ( menu->FocusedItem != NO_SELECTED_ITEM )
2058 {
2059 if( menu->nItems == 1 ) return; else
2060 for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
2061 ; i += offset)
2062 if (!(menu->items[i].fType & MF_SEPARATOR))
2063 {
2064 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2065 return;
2066 }
2067 }
2068
2069 for ( i = (offset > 0) ? 0 : menu->nItems - 1;
2070 i >= 0 && i < menu->nItems ; i += offset)
2071 if (!(menu->items[i].fType & MF_SEPARATOR))
2072 {
2073 MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
2074 return;
2075 }
2076 }
2077
2078
2079 /**********************************************************************
2080 * MENU_InsertItem
2081 *
2082 * Insert (allocate) a new item into a menu.
2083 */
2084 static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
2085 {
2086 MENUITEM *newItems;
2087 POPUPMENU *menu;
2088
2089 if (!(menu = MENU_GetMenu(hMenu)))
2090 return NULL;
2091
2092 /* Find where to insert new item */
2093
2094 if (flags & MF_BYPOSITION) {
2095 if (pos > menu->nItems)
2096 pos = menu->nItems;
2097 } else {
2098 if (!MENU_FindItem( &hMenu, &pos, flags ))
2099 pos = menu->nItems;
2100 else {
2101 if (!(menu = MENU_GetMenu( hMenu )))
2102 return NULL;
2103 }
2104 }
2105
2106 /* Make sure that MDI system buttons stay on the right side.
2107 * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
2108 * regardless of their id.
2109 */
2110 while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2111 (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
2112 pos--;
2113
2114 TRACE("inserting at %u flags %x\n", pos, flags);
2115
2116 /* Create new items array */
2117
2118 newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
2119 if (!newItems)
2120 {
2121 WARN("allocation failed\n" );
2122 return NULL;
2123 }
2124 if (menu->nItems > 0)
2125 {
2126 /* Copy the old array into the new one */
2127 if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
2128 if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
2129 (menu->nItems-pos)*sizeof(MENUITEM) );
2130 HeapFree( GetProcessHeap(), 0, menu->items );
2131 }
2132 menu->items = newItems;
2133 menu->nItems++;
2134 memset( &newItems[pos], 0, sizeof(*newItems) );
2135 menu->Height = 0; /* force size recalculate */
2136 return &newItems[pos];
2137 }
2138
2139
2140 /**********************************************************************
2141 * MENU_ParseResource
2142 *
2143 * Parse a standard menu resource and add items to the menu.
2144 * Return a pointer to the end of the resource.
2145 *
2146 * NOTE: flags is equivalent to the mtOption field
2147 */
2148 static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
2149 {
2150 WORD flags, id = 0;
2151 LPCWSTR str;
2152 BOOL end_flag;
2153
2154 do
2155 {
2156 flags = GET_WORD(res);
2157 end_flag = flags & MF_END;
2158 /* Remove MF_END because it has the same value as MF_HILITE */
2159 flags &= ~MF_END;
2160 res += sizeof(WORD);
2161 if (!(flags & MF_POPUP))
2162 {
2163 id = GET_WORD(res);
2164 res += sizeof(WORD);
2165 }
2166 str = (LPCWSTR)res;
2167 res += (strlenW(str) + 1) * sizeof(WCHAR);
2168 if (flags & MF_POPUP)
2169 {
2170 HMENU hSubMenu = CreatePopupMenu();
2171 if (!hSubMenu) return NULL;
2172 if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
2173 AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
2174 }
2175 else /* Not a popup */
2176 {
2177 AppendMenuW( hMenu, flags, id, *str ? str : NULL );
2178 }
2179 } while (!end_flag);
2180 return res;
2181 }
2182
2183
2184 /**********************************************************************
2185 * MENUEX_ParseResource
2186 *
2187 * Parse an extended menu resource and add items to the menu.
2188 * Return a pointer to the end of the resource.
2189 */
2190 static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
2191 {
2192 WORD resinfo;
2193 do {
2194 MENUITEMINFOW mii;
2195
2196 mii.cbSize = sizeof(mii);
2197 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
2198 mii.fType = GET_DWORD(res);
2199 res += sizeof(DWORD);
2200 mii.fState = GET_DWORD(res);
2201 res += sizeof(DWORD);
2202 mii.wID = GET_DWORD(res);
2203 res += sizeof(DWORD);
2204 resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte. */
2205 res += sizeof(WORD);
2206 /* Align the text on a word boundary. */
2207 res += (~((UINT_PTR)res - 1)) & 1;
2208 mii.dwTypeData = (LPWSTR) res;
2209 res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
2210 /* Align the following fields on a dword boundary. */
2211 res += (~((UINT_PTR)res - 1)) & 3;
2212
2213 TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
2214 mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
2215
2216 if (resinfo & 1) { /* Pop-up? */
2217 /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
2218 res += sizeof(DWORD);
2219 mii.hSubMenu = CreatePopupMenu();
2220 if (!mii.hSubMenu)
2221 return NULL;
2222 if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2223 DestroyMenu(mii.hSubMenu);
2224 return NULL;
2225 }
2226 mii.fMask |= MIIM_SUBMENU;
2227 mii.fType |= MF_POPUP;
2228 }
2229 else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
2230 {
2231 WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
2232 mii.wID, mii.fType);
2233 mii.fType |= MF_SEPARATOR;
2234 }
2235 InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
2236 } while (!(resinfo & MF_END));
2237 return res;
2238 }
2239
2240
2241 /***********************************************************************
2242 * MENU_GetSubPopup
2243 *
2244 * Return the handle of the selected sub-popup menu (if any).
2245 */
2246 static HMENU MENU_GetSubPopup( HMENU hmenu )
2247 {
2248 POPUPMENU *menu;
2249 MENUITEM *item;
2250
2251 menu = MENU_GetMenu( hmenu );
2252
2253 if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
2254
2255 item = &menu->items[menu->FocusedItem];
2256 if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
2257 return item->hSubMenu;
2258 return 0;
2259 }
2260
2261
2262 /***********************************************************************
2263 * MENU_HideSubPopups
2264 *
2265 * Hide the sub-popup menus of this menu.
2266 */
2267 static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2268 BOOL sendMenuSelect, UINT wFlags )
2269 {
2270 POPUPMENU *menu = MENU_GetMenu( hmenu );
2271
2272 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2273
2274 if (menu && top_popup)
2275 {
2276 HMENU hsubmenu;
2277 POPUPMENU *submenu;
2278 MENUITEM *item;
2279
2280 if (menu->FocusedItem != NO_SELECTED_ITEM)
2281 {
2282 item = &menu->items[menu->FocusedItem];
2283 if (!(item->fType & MF_POPUP) ||
2284 !(item->fState & MF_MOUSESELECT)) return;
2285 item->fState &= ~MF_MOUSESELECT;
2286 hsubmenu = item->hSubMenu;
2287 } else return;
2288
2289 if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2290 MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2291 MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2292 DestroyWindow( submenu->hWnd );
2293 submenu->hWnd = 0;
2294
2295 if (!(wFlags & TPM_NONOTIFY))
2296 SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
2297 MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
2298 }
2299 }
2300
2301
2302 /***********************************************************************
2303 * MENU_ShowSubPopup
2304 *
2305 * Display the sub-menu of the selected item of this menu.
2306 * Return the handle of the submenu, or hmenu if no submenu to display.
2307 */
2308 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2309 BOOL selectFirst, UINT wFlags )
2310 {
2311 RECT rect;
2312 POPUPMENU *menu;
2313 MENUITEM *item;
2314 HDC hdc;
2315
2316 TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2317
2318 if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
2319
2320 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
2321
2322 item = &menu->items[menu->FocusedItem];
2323 if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2324 return hmenu;
2325
2326 /* message must be sent before using item,
2327 because nearly everything may be changed by the application ! */
2328
2329 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
2330 if (!(wFlags & TPM_NONOTIFY))
2331 SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2332 MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
2333
2334 item = &menu->items[menu->FocusedItem];
2335 rect = item->rect;
2336
2337 /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2338 if (!(item->fState & MF_HILITE))
2339 {
2340 if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
2341 else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2342
2343 SelectObject( hdc, get_menu_font(FALSE));
2344
2345 item->fState |= MF_HILITE;
2346 MENU_DrawMenuItem( menu->hWnd, hmenu, hwndOwner, hdc, item, menu->Height, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2347 ReleaseDC( menu->hWnd, hdc );
2348 }
2349 if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2350 item->rect = rect;
2351
2352 item->fState |= MF_MOUSESELECT;
2353
2354 if (IS_SYSTEM_MENU(menu))
2355 {
2356 MENU_InitSysMenuPopup(item->hSubMenu,
2357 GetWindowLongW( menu->hWnd, GWL_STYLE ),
2358 GetClassLongW( menu->hWnd, GCL_STYLE));
2359
2360 NC_GetSysPopupPos( menu->hWnd, &rect );
2361 if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
2362 rect.top = rect.bottom;
2363 rect.right = GetSystemMetrics(SM_CXSIZE);
2364 rect.bottom = GetSystemMetrics(SM_CYSIZE);
2365 }
2366 else
2367 {
2368 GetWindowRect( menu->hWnd, &rect );
2369 if (menu->wFlags & MF_POPUP)
2370 {
2371 RECT rc = item->rect;
2372
2373 MENU_AdjustMenuItemRect(menu, &rc);
2374
2375 /* The first item in the popup menu has to be at the
2376 same y position as the focused menu item */
2377 if (wFlags & TPM_LAYOUTRTL)
2378 rect.left += GetSystemMetrics(SM_CXBORDER);
2379 else
2380 rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
2381 rect.top += rc.top - MENU_TOP_MARGIN;
2382 rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
2383 rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN
2384 - MENU_BOTTOM_MARGIN - GetSystemMetrics(SM_CYBORDER);
2385 }
2386 else
2387 {
2388 if (wFlags & TPM_LAYOUTRTL)
2389 rect.left = rect.right - item->rect.left;
2390 else
2391 rect.left += item->rect.left;
2392 rect.top += item->rect.bottom;
2393 rect.right = item->rect.right - item->rect.left;
2394 rect.bottom = item->rect.bottom - item->rect.top;
2395 }
2396 }
2397
2398 /* use default alignment for submenus */
2399 wFlags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
2400
2401 MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
2402 rect.left, rect.top, rect.right, rect.bottom );
2403 if (selectFirst)
2404 MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
2405 return item->hSubMenu;
2406 }
2407
2408
2409
2410 /**********************************************************************
2411 * MENU_IsMenuActive
2412 */
2413 HWND MENU_IsMenuActive(void)
2414 {
2415 return top_popup;
2416 }
2417
2418 /**********************************************************************
2419 * MENU_EndMenu
2420 *
2421 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
2422 *
2423 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
2424 */
2425 void MENU_EndMenu( HWND hwnd )
2426 {
2427 POPUPMENU *menu;
2428 menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2429 if (menu && hwnd == menu->hwndOwner) EndMenu();
2430 }
2431
2432 /***********************************************************************
2433 * MENU_PtMenu
2434 *
2435 * Walks menu chain trying to find a menu pt maps to.
2436 */
2437 static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
2438 {
2439 POPUPMENU *menu = MENU_GetMenu( hMenu );
2440 UINT item = menu->FocusedItem;
2441 HMENU ret;
2442
2443 /* try subpopup first (if any) */
2444 ret = (item != NO_SELECTED_ITEM &&
2445 (menu->items[item].fType & MF_POPUP) &&
2446 (menu->items[item].fState & MF_MOUSESELECT))
2447 ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
2448
2449 if (!ret) /* check the current window (avoiding WM_HITTEST) */
2450 {
2451 INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
2452 if( menu->wFlags & MF_POPUP )
2453 {
2454 if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
2455 }
2456 else if (ht == HTSYSMENU)
2457 ret = get_win_sys_menu( menu->hWnd );
2458 else if (ht == HTMENU)
2459 ret = GetMenu( menu->hWnd );
2460 }
2461 return ret;
2462 }
2463
2464 /***********************************************************************
2465 * MENU_ExecFocusedItem
2466 *
2467 * Execute a menu item (for instance when user pressed Enter).
2468 * Return the wID of the executed item. Otherwise, -1 indicating
2469 * that no menu item was executed, -2 if a popup is shown;
2470 * Have to receive the flags for the TrackPopupMenu options to avoid
2471 * sending unwanted message.
2472 *
2473 */
2474 static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
2475 {
2476 MENUITEM *item;
2477 POPUPMENU *menu = MENU_GetMenu( hMenu );
2478
2479 TRACE("%p hmenu=%p\n", pmt, hMenu);
2480
2481 if (!menu || !menu->nItems ||
2482 (menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
2483
2484 item = &menu->items[menu->FocusedItem];
2485
2486 TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
2487
2488 if (!(item->fType & MF_POPUP))
2489 {
2490 if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
2491 {
2492 /* If TPM_RETURNCMD is set you return the id, but
2493 do not send a message to the owner */
2494 if(!(wFlags & TPM_RETURNCMD))
2495 {
2496 if( menu->wFlags & MF_SYSMENU )
2497 PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2498 MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
2499 else
2500 {
2501 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2502 DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);
2503
2504 if (dwStyle & MNS_NOTIFYBYPOS)
2505 PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
2506 (LPARAM)hMenu);
2507 else
2508 PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
2509 }
2510 }
2511 return item->wID;
2512 }
2513 }
2514 else
2515 {
2516 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2517 return -2;
2518 }
2519
2520 return -1;
2521 }
2522
2523 /***********************************************************************
2524 * MENU_SwitchTracking
2525 *
2526 * Helper function for menu navigation routines.
2527 */
2528 static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
2529 {
2530 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2531 POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
2532
2533 TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2534
2535 if( pmt->hTopMenu != hPtMenu &&
2536 !((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
2537 {
2538 /* both are top level menus (system and menu-bar) */
2539 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2540 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
2541 pmt->hTopMenu = hPtMenu;
2542 }
2543 else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2544 MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
2545 }
2546
2547
2548 /***********************************************************************
2549 * MENU_ButtonDown
2550 *
2551 * Return TRUE if we can go on with menu tracking.
2552 */
2553 static BOOL MENU_ButtonDown( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2554 {
2555 TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2556
2557 if (hPtMenu)
2558 {
2559 UINT id = 0;
2560 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2561 MENUITEM *item;
2562
2563 if( IS_SYSTEM_MENU(ptmenu) )
2564 item = ptmenu->items;
2565 else
2566 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2567
2568 if( item )
2569 {
2570 if( ptmenu->FocusedItem != id )
2571 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2572
2573 /* If the popup menu is not already "popped" */
2574 if(!(item->fState & MF_MOUSESELECT ))
2575 {
2576 pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2577 }
2578
2579 return TRUE;
2580 }
2581 /* Else the click was on the menu bar, finish the tracking */
2582 }
2583 return FALSE;
2584 }
2585
2586 /***********************************************************************
2587 * MENU_ButtonUp
2588 *
2589 * Return the value of MENU_ExecFocusedItem if
2590 * the selected item was not a popup. Else open the popup.
2591 * A -1 return value indicates that we go on with menu tracking.
2592 *
2593 */
2594 static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
2595 {
2596 TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2597
2598 if (hPtMenu)
2599 {
2600 UINT id = 0;
2601 POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2602 MENUITEM *item;
2603
2604 if( IS_SYSTEM_MENU(ptmenu) )
2605 item = ptmenu->items;
2606 else
2607 item = MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2608
2609 if( item && (ptmenu->FocusedItem == id ))
2610 {
2611 debug_print_menuitem ("FocusedItem: ", item, "");
2612
2613 if( !(item->fType & MF_POPUP) )
2614 {
2615 INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2616 if (executedMenuId == -1 || executedMenuId == -2) return -1;
2617 return executedMenuId;
2618 }
2619
2620 /* If we are dealing with the top-level menu */
2621 /* and this is a click on an already "popped" item: */
2622 /* Stop the menu tracking and close the opened submenus */
2623 if((pmt->hTopMenu == hPtMenu) && ptmenu->bTimeToHide)
2624 return 0;
2625 }
2626 ptmenu->bTimeToHide = TRUE;
2627 }
2628 return -1;
2629 }
2630
2631
2632 /***********************************************************************
2633 * MENU_MouseMove
2634 *
2635 * Return TRUE if we can go on with menu tracking.
2636 */
2637 static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
2638 {
2639 UINT id = NO_SELECTED_ITEM;
2640 POPUPMENU *ptmenu = NULL;
2641
2642 if( hPtMenu )
2643 {
2644 ptmenu = MENU_GetMenu( hPtMenu );
2645 if( IS_SYSTEM_MENU(ptmenu) )
2646 id = 0;
2647 else
2648 MENU_FindItemByCoords( ptmenu, pmt->pt, &id );
2649 }
2650
2651 if( id == NO_SELECTED_ITEM )
2652 {
2653 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2654 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2655
2656 }
2657 else if( ptmenu->FocusedItem != id )
2658 {
2659 MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
2660 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
2661 }
2662 return TRUE;
2663 }
2664
2665
2666 /***********************************************************************
2667 * MENU_DoNextMenu
2668 *
2669 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
2670 */
2671 static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
2672 {
2673 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2674 BOOL atEnd = FALSE;
2675
2676 /* When skipping left, we need to do something special after the
2677 first menu. */
2678 if (vk == VK_LEFT && menu->FocusedItem == 0)
2679 {
2680 atEnd = TRUE;
2681 }
2682 /* When skipping right, for the non-system menu, we need to
2683 handle the last non-special menu item (ie skip any window
2684 icons such as MDI maximize, restore or close) */
2685 else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
2686 {
2687 UINT i = menu->FocusedItem + 1;
2688 while (i < menu->nItems) {
2689 if ((menu->items[i].wID >= SC_SIZE &&
2690 menu->items[i].wID <= SC_RESTORE)) {
2691 i++;
2692 } else break;
2693 }
2694 if (i == menu->nItems) {
2695 atEnd = TRUE;
2696 }
2697 }
2698 /* When skipping right, we need to cater for the system menu */
2699 else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
2700 {
2701 if (menu->FocusedItem == (menu->nItems - 1)) {
2702 atEnd = TRUE;
2703 }
2704 }
2705
2706 if( atEnd )
2707 {
2708 MDINEXTMENU next_menu;
2709 HMENU hNewMenu;
2710 HWND hNewWnd;
2711 UINT id = 0;
2712
2713 next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
2714 next_menu.hmenuNext = 0;
2715 next_menu.hwndNext = 0;
2716 SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
2717
2718 TRACE("%p [%p] -> %p [%p]\n",
2719 pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
2720
2721 if (!next_menu.hmenuNext || !next_menu.hwndNext)
2722 {
2723 DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
2724 hNewWnd = pmt->hOwnerWnd;
2725 if( IS_SYSTEM_MENU(menu) )
2726 {
2727 /* switch to the menu bar */
2728
2729 if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
2730
2731 if( vk == VK_LEFT )
2732 {
2733 menu = MENU_GetMenu( hNewMenu );
2734 id = menu->nItems - 1;
2735
2736 /* Skip backwards over any system predefined icons,
2737 eg. MDI close, restore etc icons */
2738 while ((id > 0) &&
2739 (menu->items[id].wID >= SC_SIZE &&
2740 menu->items[id].wID <= SC_RESTORE)) id--;
2741 }
2742 }
2743 else if (style & WS_SYSMENU )
2744 {
2745 /* switch to the system menu */
2746 hNewMenu = get_win_sys_menu( hNewWnd );
2747 }
2748 else return FALSE;
2749 }
2750 else /* application returned a new menu to switch to */
2751 {
2752 hNewMenu = next_menu.hmenuNext;
2753 hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
2754
2755 if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
2756 {
2757 DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
2758
2759 if (style & WS_SYSMENU &&
2760 GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
2761 {
2762 /* get the real system menu */
2763 hNewMenu = get_win_sys_menu(hNewWnd);
2764 }
2765 else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
2766 {
2767 /* FIXME: Not sure what to do here;
2768 * perhaps try to track hNewMenu as a popup? */
2769
2770 TRACE(" -- got confused.\n");
2771 return FALSE;
2772 }
2773 }
2774 else return FALSE;
2775 }
2776
2777 if( hNewMenu != pmt->hTopMenu )
2778 {
2779 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2780 FALSE, 0 );
2781 if( pmt->hCurrentMenu != pmt->hTopMenu )
2782 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2783 }
2784
2785 if( hNewWnd != pmt->hOwnerWnd )
2786 {
2787 pmt->hOwnerWnd = hNewWnd;
2788 set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
2789 }
2790
2791 pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2792 MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
2793
2794 return TRUE;
2795 }
2796 return FALSE;
2797 }
2798
2799 /***********************************************************************
2800 * MENU_SuspendPopup
2801 *
2802 * The idea is not to show the popup if the next input message is
2803 * going to hide it anyway.
2804 */
2805 static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT16 uMsg )
2806 {
2807 MSG msg;
2808
2809 msg.hwnd = pmt->hOwnerWnd;
2810
2811 PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
2812 pmt->trackFlags |= TF_SKIPREMOVE;
2813
2814 switch( uMsg )
2815 {
2816 case WM_KEYDOWN:
2817 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2818 if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
2819 {
2820 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
2821 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
2822 if( msg.message == WM_KEYDOWN &&
2823 (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
2824 {
2825 pmt->trackFlags |= TF_SUSPENDPOPUP;
2826 return TRUE;
2827 }
2828 }
2829 break;
2830 }
2831
2832 /* failures go through this */
2833 pmt->trackFlags &= ~TF_SUSPENDPOPUP;
2834 return FALSE;
2835 }
2836
2837 /***********************************************************************
2838 * MENU_KeyEscape
2839 *
2840 * Handle a VK_ESCAPE key event in a menu.
2841 */
2842 static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
2843 {
2844 BOOL bEndMenu = TRUE;
2845
2846 if (pmt->hCurrentMenu != pmt->hTopMenu)
2847 {
2848 POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);
2849
2850 if (menu->wFlags & MF_POPUP)
2851 {
2852 HMENU hmenutmp, hmenuprev;
2853
2854 hmenuprev = hmenutmp = pmt->hTopMenu;
2855
2856 /* close topmost popup */
2857 while (hmenutmp != pmt->hCurrentMenu)
2858 {
2859 hmenuprev = hmenutmp;
2860 hmenutmp = MENU_GetSubPopup( hmenuprev );
2861 }
2862
2863 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2864 pmt->hCurrentMenu = hmenuprev;
2865 bEndMenu = FALSE;
2866 }
2867 }
2868
2869 return bEndMenu;
2870 }
2871
2872 /***********************************************************************
2873 * MENU_KeyLeft
2874 *
2875 * Handle a VK_LEFT key event in a menu.
2876 */
2877 static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags )
2878 {
2879 POPUPMENU *menu;
2880 HMENU hmenutmp, hmenuprev;
2881 UINT prevcol;
2882
2883 hmenuprev = hmenutmp = pmt->hTopMenu;
2884 menu = MENU_GetMenu( hmenutmp );
2885
2886 /* Try to move 1 column left (if possible) */
2887 if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
2888 NO_SELECTED_ITEM ) {
2889
2890 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2891 prevcol, TRUE, 0 );
2892 return;
2893 }
2894
2895 /* close topmost popup */
2896 while (hmenutmp != pmt->hCurrentMenu)
2897 {
2898 hmenuprev = hmenutmp;
2899 hmenutmp = MENU_GetSubPopup( hmenuprev );
2900 }
2901
2902 MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2903 pmt->hCurrentMenu = hmenuprev;
2904
2905 if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
2906 {
2907 /* move menu bar selection if no more popups are left */
2908
2909 if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
2910 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
2911
2912 if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2913 {
2914 /* A sublevel menu was displayed - display the next one
2915 * unless there is another displacement coming up */
2916
2917 if( !MENU_SuspendPopup( pmt, WM_KEYDOWN ) )
2918 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2919 pmt->hTopMenu, TRUE, wFlags);
2920 }
2921 }
2922 }
2923
2924
2925 /***********************************************************************
2926 * MENU_KeyRight
2927 *
2928 * Handle a VK_RIGHT key event in a menu.
2929 */
2930 static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags )
2931 {
2932 HMENU hmenutmp;
2933 POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2934 UINT nextcol;
2935
2936 TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
2937 pmt->hCurrentMenu,
2938 debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
2939 pmt->hTopMenu, debugstr_w(menu->items[0].text) );
2940
2941 if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
2942 {
2943 /* If already displaying a popup, try to display sub-popup */
2944
2945 hmenutmp = pmt->hCurrentMenu;
2946 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
2947
2948 /* if subpopup was displayed then we are done */
2949 if (hmenutmp != pmt->hCurrentMenu) return;
2950 }
2951
2952 /* Check to see if there's another column */
2953 if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
2954 NO_SELECTED_ITEM ) {
2955 TRACE("Going to %d.\n", nextcol );
2956 MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2957 nextcol, TRUE, 0 );
2958 return;
2959 }
2960
2961 if (!(menu->wFlags & MF_POPUP)) /* menu bar tracking */
2962 {
2963 if( pmt->hCurrentMenu != pmt->hTopMenu )
2964 {
2965 MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2966 hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
2967 } else hmenutmp = 0;
2968
2969 /* try to move to the next item */
2970 if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
2971 MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );
2972
2973 if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
2974 if( !MENU_SuspendPopup(pmt, WM_KEYDOWN) )
2975 pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
2976 pmt->hTopMenu, TRUE, wFlags);
2977 }
2978 }
2979
2980 static void CALLBACK release_capture( BOOL __normal )
2981 {
2982 set_capture_window( 0, GUI_INMENUMODE, NULL );
2983 }
2984
2985 /***********************************************************************
2986 * MENU_TrackMenu
2987 *
2988 * Menu tracking code.
2989 */
2990 static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
2991 HWND hwnd, const RECT *lprect )
2992 {
2993 MSG msg;
2994 POPUPMENU *menu;
2995 BOOL fRemove;
2996 INT executedMenuId = -1;
2997 MTRACKER mt;
2998 BOOL enterIdleSent = FALSE;
2999 HWND capture_win;
3000
3001 mt.trackFlags = 0;
3002 mt.hCurrentMenu = hmenu;
3003 mt.hTopMenu = hmenu;
3004 mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
3005 mt.pt.x = x;
3006 mt.pt.y = y;
3007
3008 TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
3009 hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3010
3011 fEndMenu = FALSE;
3012 if (!(menu = MENU_GetMenu( hmenu )))
3013 {
3014 WARN("Invalid menu handle %p\n", hmenu);
3015 SetLastError(ERROR_INVALID_MENU_HANDLE);
3016 return FALSE;
3017 }
3018
3019 if (wFlags & TPM_BUTTONDOWN)
3020 {
3021 /* Get the result in order to start the tracking or not */
3022 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3023 fEndMenu = !fRemove;
3024 }
3025
3026 if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
3027
3028 /* owner may not be visible when tracking a popup, so use the menu itself */
3029 capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
3030 set_capture_window( capture_win, GUI_INMENUMODE, NULL );
3031
3032 __TRY while (!fEndMenu)
3033 {
3034 menu = MENU_GetMenu( mt.hCurrentMenu );
3035 if (!menu) /* sometimes happens if I do a window manager close */
3036 break;
3037
3038 /* we have to keep the message in the queue until it's
3039 * clear that menu loop is not over yet. */
3040
3041 for (;;)
3042 {
3043 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3044 {
3045 if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3046 /* remove the message from the queue */
3047 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3048 }
3049 else
3050 {
3051 if (!enterIdleSent)
3052 {
3053 HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3054 enterIdleSent = TRUE;
3055 SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
3056 }
3057 WaitMessage();
3058 }
3059 }
3060
3061 /* check if EndMenu() tried to cancel us, by posting this message */
3062 if(msg.message == WM_CANCELMODE)
3063 {
3064 /* we are now out of the loop */
3065 fEndMenu = TRUE;
3066
3067 /* remove the message from the queue */
3068 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3069
3070 /* break out of internal loop, ala ESCAPE */
3071 break;
3072 }
3073
3074 TranslateMessage( &msg );
3075 mt.pt = msg.pt;
3076
3077 if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
3078 enterIdleSent=FALSE;
3079
3080 fRemove = FALSE;
3081 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
3082 {
3083 /*
3084 * Use the mouse coordinates in lParam instead of those in the MSG
3085 * struct to properly handle synthetic messages. They are already
3086 * in screen coordinates.
3087 */
3088 mt.pt.x = (short)LOWORD(msg.lParam);
3089 mt.pt.y = (short)HIWORD(msg.lParam);
3090
3091 /* Find a menu for this mouse event */
3092 hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
3093
3094 switch(msg.message)
3095 {
3096 /* no WM_NC... messages in captured state */
3097
3098 case WM_RBUTTONDBLCLK:
3099 case WM_RBUTTONDOWN:
3100 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3101 /* fall through */
3102 case WM_LBUTTONDBLCLK:
3103 case WM_LBUTTONDOWN:
3104 /* If the message belongs to the menu, removes it from the queue */
3105 /* Else, end menu tracking */
3106 fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
3107 fEndMenu = !fRemove;
3108 break;
3109
3110 case WM_RBUTTONUP:
3111 if (!(wFlags & TPM_RIGHTBUTTON)) break;
3112 /* fall through */
3113 case WM_LBUTTONUP:
3114 /* Check if a menu was selected by the mouse */
3115 if (hmenu)
3116 {
3117 executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3118 TRACE("executedMenuId %d\n", executedMenuId);
3119
3120 /* End the loop if executedMenuId is an item ID */
3121 /* or if the job was done (executedMenuId = 0). */
3122 fEndMenu = fRemove = (executedMenuId != -1);
3123 }
3124 /* No menu was selected by the mouse */
3125 /* if the function was called by TrackPopupMenu, continue
3126 with the menu tracking. If not, stop it */
3127 else
3128 fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
3129
3130 break;
3131
3132 case WM_MOUSEMOVE:
3133 /* the selected menu item must be changed every time */
3134 /* the mouse moves. */
3135
3136 if (hmenu)
3137 fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3138
3139 } /* switch(msg.message) - mouse */
3140 }
3141 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
3142 {
3143 fRemove = TRUE; /* Keyboard messages are always removed */
3144 switch(msg.message)
3145 {
3146 case WM_KEYDOWN:
3147 case WM_SYSKEYDOWN:
3148 switch(msg.wParam)
3149 {
3150 case VK_MENU:
3151 case VK_F10:
3152 fEndMenu = TRUE;
3153 break;
3154
3155 case VK_HOME:
3156 case VK_END:
3157 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3158 NO_SELECTED_ITEM, FALSE, 0 );
3159 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3160 (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
3161 break;
3162
3163 case VK_UP:
3164 case VK_DOWN: /* If on menu bar, pull-down the menu */
3165
3166 menu = MENU_GetMenu( mt.hCurrentMenu );
3167 if (!(menu->wFlags & MF_POPUP))
3168 mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
3169 else /* otherwise try to move selection */
3170 MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
3171 (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
3172 break;
3173
3174 case VK_LEFT:
3175 MENU_KeyLeft( &mt, wFlags );
3176 break;
3177
3178 case VK_RIGHT:
3179 MENU_KeyRight( &mt, wFlags );
3180 break;
3181
3182 case VK_ESCAPE:
3183 fEndMenu = MENU_KeyEscape(&mt, wFlags);
3184 break;
3185
3186 case VK_F1:
3187 {
3188 HELPINFO hi;
3189 hi.cbSize = sizeof(HELPINFO);
3190 hi.iContextType = HELPINFO_MENUITEM;
3191 if (menu->FocusedItem == NO_SELECTED_ITEM)
3192 hi.iCtrlId = 0;
3193 else
3194 hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3195 hi.hItemHandle = hmenu;
3196 hi.dwContextId = menu->dwContextHelpID;
3197 hi.MousePos = msg.pt;
3198 SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3199 break;
3200 }
3201
3202 default:
3203 break;
3204 }
3205 break; /* WM_KEYDOWN */
3206
3207 case WM_CHAR:
3208 case WM_SYSCHAR:
3209 {
3210 UINT pos;
3211
3212 if (msg.wParam == '\r' || msg.wParam == ' ')
3213 {
3214 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3215 fEndMenu = (executedMenuId != -2);
3216
3217 break;
3218 }
3219
3220 /* Hack to avoid control chars. */
3221 /* We will find a better way real soon... */
3222 if (msg.wParam < 32) break;
3223
3224 pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3225 LOWORD(msg.wParam), FALSE );
3226 if (pos == (UINT)-2) fEndMenu = TRUE;
3227 else if (pos == (UINT)-1) MessageBeep(0);
3228 else
3229 {
3230 MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
3231 TRUE, 0 );
3232 executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3233 fEndMenu = (executedMenuId != -2);
3234 }
3235 }
3236 break;
3237 } /* switch(msg.message) - kbd */
3238 }
3239 else
3240 {
3241 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3242 DispatchMessageW( &msg );
3243 continue;
3244 }
3245
3246 if (!fEndMenu) fRemove = TRUE;
3247
3248 /* finally remove message from the queue */
3249
3250 if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3251 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3252 else mt.trackFlags &= ~TF_SKIPREMOVE;
3253 }
3254 __FINALLY( release_capture )
3255
3256 /* If dropdown is still painted and the close box is clicked on
3257 then the menu will be destroyed as part of the DispatchMessage above.
3258 This will then invalidate the menu handle in mt.hTopMenu. We should
3259 check for this first. */
3260 if( IsMenu( mt.hTopMenu ) )
3261 {
3262 menu = MENU_GetMenu( mt.hTopMenu );
3263
3264 if( IsWindow( mt.hOwnerWnd ) )
3265 {
3266 MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3267
3268 if (menu && (menu->wFlags & MF_POPUP))
3269 {
3270 DestroyWindow( menu->hWnd );
3271 menu->hWnd = 0;
3272
3273 if (!(wFlags & TPM_NONOTIFY))
3274 SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
3275 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3276 }
3277 MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3278 SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3279 }
3280
3281 /* Reset the variable for hiding menu */
3282 if( menu ) menu->bTimeToHide = FALSE;
3283 }
3284
3285 /* The return value is only used by TrackPopupMenu */
3286 if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3287 if (executedMenuId == -1) executedMenuId = 0;
3288 return executedMenuId;
3289 }
3290
3291 /***********************************************************************
3292 * MENU_InitTracking
3293 */
3294 static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
3295 {
3296 POPUPMENU *menu;
3297
3298 TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3299
3300 HideCaret(0);
3301
3302 /* This makes the menus of applications built with Delphi work.
3303 * It also enables menus to be displayed in more than one window,
3304 * but there are some bugs left that need to be fixed in this case.
3305 */
3306 if ((menu = MENU_GetMenu( hMenu ))) menu->hWnd = hWnd;
3307
3308 /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
3309 if (!(wFlags & TPM_NONOTIFY))
3310 SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3311
3312 SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3313
3314 if (!(wFlags & TPM_NONOTIFY))
3315 {
3316 SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3317 /* If an app changed/recreated menu bar entries in WM_INITMENU
3318 * menu sizes will be recalculated once the menu created/shown.
3319 */
3320 }
3321
3322 return TRUE;
3323 }
3324
3325 /***********************************************************************
3326 * MENU_ExitTracking
3327 */
3328 static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3329 {
3330 TRACE("hwnd=%p\n", hWnd);
3331
3332 SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3333 ShowCaret(0);
3334 top_popup = 0;
3335 top_popup_hmenu = NULL;
3336 return TRUE;
3337 }
3338
3339 /***********************************************************************
3340 * MENU_TrackMouseMenuBar
3341 *
3342 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
3343 */
3344 void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
3345 {
3346 HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3347 UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
3348
3349 TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3350
3351 if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3352 if (IsMenu(hMenu))
3353 {
3354 MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3355 MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3356 MENU_ExitTracking(hWnd, FALSE);
3357 }
3358 }
3359
3360
3361 /***********************************************************************
3362 * MENU_TrackKbdMenuBar
3363 *
3364 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
3365 */
3366 void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
3367 {
3368 UINT uItem = NO_SELECTED_ITEM;
3369 HMENU hTrackMenu;
3370 UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
3371
3372 TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
3373
3374 /* find window that has a menu */
3375
3376 while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
3377 if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
3378
3379 /* check if we have to track a system menu */
3380
3381 hTrackMenu = GetMenu( hwnd );
3382 if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
3383 {
3384 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3385 hTrackMenu = get_win_sys_menu( hwnd );
3386 uItem = 0;
3387 wParam |= HTSYSMENU; /* prevent item lookup */
3388 }
3389 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3390
3391 if (!IsMenu( hTrackMenu )) return;
3392
3393 MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
3394
3395 if( wChar && wChar != ' ' )
3396 {
3397 uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3398 if ( uItem >= (UINT)(-2) )
3399 {
3400 if( uItem == (UINT)(-1) ) MessageBeep(0);
3401 /* schedule end of menu tracking */
3402 wFlags |= TF_ENDMENU;
3403 goto track_menu;
3404 }
3405 }
3406
3407 MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3408
3409 if (!(wParam & HTSYSMENU) || wChar == ' ')
3410 {
3411 if( uItem == NO_SELECTED_ITEM )
3412 MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
3413 else
3414 PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3415 }
3416
3417 track_menu:
3418 MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3419 MENU_ExitTracking( hwnd, FALSE );
3420 }
3421
3422 /**********************************************************************
3423 * TrackPopupMenuEx (USER32.@)
3424 */
3425 BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
3426 HWND hWnd, LPTPMPARAMS lpTpm )
3427 {
3428 POPUPMENU *menu;
3429 BOOL ret = FALSE;
3430
3431 TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
3432 hMenu, wFlags, x, y, hWnd, lpTpm,
3433 lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
3434
3435 /* Parameter check */
3436 /* FIXME: this check is performed several times, here and in the called
3437 functions. That could be optimized */
3438 if (!(menu = MENU_GetMenu( hMenu )))
3439 {
3440 SetLastError( ERROR_INVALID_MENU_HANDLE );
3441 return FALSE;
3442 }
3443
3444 if (IsWindow(menu->hWnd))
3445 {
3446 SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
3447 return FALSE;
3448 }
3449
3450 MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3451
3452 /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
3453 if (!(wFlags & TPM_NONOTIFY))
3454 SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3455
3456 if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
3457 ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
3458 lpTpm ? &lpTpm->rcExclude : NULL );
3459 MENU_ExitTracking(hWnd, TRUE);
3460
3461 return ret;
3462 }
3463
3464 /**********************************************************************
3465 * TrackPopupMenu (USER32.@)
3466 *
3467 * Like the win32 API, the function return the command ID only if the
3468 * flag TPM_RETURNCMD is on.
3469 *
3470 */
3471 BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
3472 INT nReserved, HWND hWnd, const RECT *lpRect )
3473 {
3474 return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
3475 }
3476
3477 /***********************************************************************
3478 * PopupMenuWndProc
3479 *
3480 * NOTE: Windows has totally different (and undocumented) popup wndproc.
3481 */
3482 LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3483 {
3484 TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3485
3486 switch(message)
3487 {
3488 case WM_CREATE:
3489 {
3490 CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3491 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3492 return 0;
3493 }
3494
3495 case WM_MOUSEACTIVATE: /* We don't want to be activated */
3496 return MA_NOACTIVATE;
3497
3498 case WM_PAINT:
3499 {
3500 PAINTSTRUCT ps;
3501 BeginPaint( hwnd, &ps );
3502 MENU_DrawPopupMenu( hwnd, ps.hdc,
3503 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3504 EndPaint( hwnd, &ps );
3505 return 0;
3506 }
3507
3508 case WM_PRINTCLIENT:
3509 {
3510 MENU_DrawPopupMenu( hwnd, (HDC)wParam,
3511 (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3512 return 0;
3513 }
3514
3515 case WM_ERASEBKGND:
3516 return 1;
3517
3518 case WM_DESTROY:
3519 /* zero out global pointer in case resident popup window was destroyed. */
3520 if (hwnd == top_popup) {
3521 top_popup = 0;
3522 top_popup_hmenu = NULL;
3523 }
3524 break;
3525
3526 case WM_SHOWWINDOW:
3527
3528 if( wParam )
3529 {
3530 if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
3531 }
3532 else
3533 SetWindowLongPtrW( hwnd, 0, 0 );
3534 break;
3535
3536 case MM_SETMENUHANDLE:
3537 SetWindowLongPtrW( hwnd, 0, wParam );
3538 break;
3539
3540 case MM_GETMENUHANDLE:
3541 case MN_GETHMENU:
3542 return GetWindowLongPtrW( hwnd, 0 );
3543
3544 default:
3545 return DefWindowProcW( hwnd, message, wParam, lParam );
3546 }
3547 return 0;
3548 }
3549
3550
3551 /***********************************************************************
3552 * MENU_GetMenuBarHeight
3553 *
3554 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
3555 */
3556 UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
3557 INT orgX, INT orgY )
3558 {
3559 HDC hdc;
3560 RECT rectBar;
3561 LPPOPUPMENU lppop;
3562
3563 TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3564
3565 if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3566
3567 hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3568 SelectObject( hdc, get_menu_font(FALSE));
3569 SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3570 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3571 ReleaseDC( hwnd, hdc );
3572 return lppop->Height;
3573 }
3574
3575
3576 /*******************************************************************
3577 * ChangeMenuA (USER32.@)
3578 */
3579 BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
3580 UINT id, UINT flags )
3581 {
3582 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3583 if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
3584 id, data );
3585 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3586 if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
3587 id, data );
3588 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3589 flags & MF_BYPOSITION ? pos : id,
3590 flags & ~MF_REMOVE );
3591 /* Default: MF_INSERT */
3592 return InsertMenuA( hMenu, pos, flags, id, data );
3593 }
3594
3595
3596 /*******************************************************************
3597 * ChangeMenuW (USER32.@)
3598 */
3599 BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
3600 UINT id, UINT flags )
3601 {
3602 TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3603 if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
3604 id, data );
3605 if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
3606 if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
3607 id, data );
3608 if (flags & MF_REMOVE) return RemoveMenu( hMenu,
3609 flags & MF_BYPOSITION ? pos : id,
3610 flags & ~MF_REMOVE );
3611 /* Default: MF_INSERT */
3612 return InsertMenuW( hMenu, pos, flags, id, data );
3613 }
3614
3615
3616 /*******************************************************************
3617 * CheckMenuItem (USER32.@)
3618 */
3619 DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
3620 {
3621 MENUITEM *item;
3622 DWORD ret;
3623
3624 if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
3625 ret = item->fState & MF_CHECKED;
3626 if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
3627 else item->fState &= ~MF_CHECKED;
3628 return ret;
3629 }
3630
3631
3632 /**********************************************************************
3633 * EnableMenuItem (USER32.@)
3634 */
3635 BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
3636 {
3637 UINT oldflags;
3638 MENUITEM *item;
3639 POPUPMENU *menu;
3640
3641 TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
3642
3643 /* Get the Popupmenu to access the owner menu */
3644 if (!(menu = MENU_GetMenu(hMenu)))
3645 return (UINT)-1;
3646
3647 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3648 return (UINT)-1;
3649
3650 oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
3651 item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3652
3653 /* If the close item in the system menu change update the close button */
3654 if((item->wID == SC_CLOSE) && (oldflags != wFlags))
3655 {
3656 if (menu->hSysMenuOwner != 0)
3657 {
3658 RECT rc;
3659 POPUPMENU* parentMenu;
3660
3661 /* Get the parent menu to access*/
3662 if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
3663 return (UINT)-1;
3664
3665 /* Refresh the frame to reflect the change */
3666 WIN_GetRectangles( parentMenu->hWnd, COORDS_CLIENT, &rc, NULL );
3667 rc.bottom = 0;
3668 RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3669 }
3670 }
3671
3672 return oldflags;
3673 }
3674
3675
3676 /*******************************************************************
3677 * GetMenuStringA (USER32.@)
3678 */
3679 INT WINAPI GetMenuStringA(
3680 HMENU hMenu, /* [in] menuhandle */
3681 UINT wItemID, /* [in] menu item (dep. on wFlags) */
3682 LPSTR str, /* [out] outbuffer. If NULL, func returns entry length*/
3683 INT nMaxSiz, /* [in] length of buffer. if 0, func returns entry len*/
3684 UINT wFlags /* [in] MF_ flags */
3685 ) {
3686 MENUITEM *item;
3687
3688 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3689 if (str && nMaxSiz) str[0] = '\0';
3690 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3691 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3692 return 0;
3693 }
3694 if (!item->text) return 0;
3695 if (!str || !nMaxSiz) return strlenW(item->text);
3696 if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
3697 str[nMaxSiz-1] = 0;
3698 TRACE("returning %s\n", debugstr_a(str));
3699 return strlen(str);
3700 }
3701
3702
3703 /*******************************************************************
3704 * GetMenuStringW (USER32.@)
3705 */
3706 INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
3707 LPWSTR str, INT nMaxSiz, UINT wFlags )
3708 {
3709 MENUITEM *item;
3710
3711 TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3712 if (str && nMaxSiz) str[0] = '\0';
3713 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
3714 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
3715 return 0;
3716 }
3717 if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
3718 if( !(item->text)) {
3719 str[0] = 0;
3720 return 0;
3721 }
3722 lstrcpynW( str, item->text, nMaxSiz );
3723 TRACE("returning %s\n", debugstr_w(str));
3724 return strlenW(str);
3725 }
3726
3727
3728 /**********************************************************************
3729 * HiliteMenuItem (USER32.@)
3730 */
3731 BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
3732 UINT wHilite )
3733 {
3734 LPPOPUPMENU menu;
3735 TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
3736 if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3737 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3738 if (menu->FocusedItem == wItemID) return TRUE;
3739 MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3740 MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
3741 return TRUE;
3742 }
3743
3744
3745 /**********************************************************************
3746 * GetMenuState (USER32.@)
3747 */
3748 UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
3749 {
3750 MENUITEM *item;
3751 TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
3752 if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
3753 debug_print_menuitem (" item: ", item, "");
3754 if (item->fType & MF_POPUP)
3755 {
3756 POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
3757 if (!menu) return -1;
3758 else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
3759 }
3760 else
3761 {
3762 /* We used to (from way back then) mask the result to 0xff. */
3763 /* I don't know why and it seems wrong as the documented */
3764 /* return flag MF_SEPARATOR is outside that mask. */
3765 return (item->fType | item->fState);
3766 }
3767 }
3768
3769
3770 /**********************************************************************
3771 * GetMenuItemCount (USER32.@)
3772 */
3773 INT WINAPI GetMenuItemCount( HMENU hMenu )
3774 {
3775 LPPOPUPMENU menu = MENU_GetMenu(hMenu);
3776 if (!menu) return -1;
3777 TRACE("(%p) returning %d\n", hMenu, menu->nItems );
3778 return menu->nItems;
3779 }
3780
3781
3782 /**********************************************************************
3783 * GetMenuItemID (USER32.@)
3784 */
3785 UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
3786 {
3787 MENUITEM * lpmi;
3788
3789 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3790 if (lpmi->fType & MF_POPUP) return -1;
3791 return lpmi->wID;
3792
3793 }
3794
3795
3796 /**********************************************************************
3797 * MENU_mnu2mnuii
3798 *
3799 * Uses flags, id and text ptr, passed by InsertMenu() and
3800 * ModifyMenu() to setup a MenuItemInfo structure.
3801 */
3802 static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
3803 LPMENUITEMINFOW pmii)
3804 {
3805 ZeroMemory( pmii, sizeof( MENUITEMINFOW));
3806 pmii->cbSize = sizeof( MENUITEMINFOW);
3807 pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
3808 /* setting bitmap clears text and vice versa */
3809 if( IS_STRING_ITEM(flags)) {
3810 pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
3811 if( !str)
3812 flags |= MF_SEPARATOR;
3813 /* Item beginning with a backspace is a help item */
3814 /* FIXME: wrong place, this is only true in win16 */
3815 else if( *str == '\b') {
3816 flags |= MF_HELP;
3817 str++;
3818 }
3819 pmii->dwTypeData = (LPWSTR)str;
3820 } else if( flags & MFT_BITMAP){
3821 pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3822 pmii->hbmpItem = (HBITMAP)str;
3823 }
3824 if( flags & MF_OWNERDRAW){
3825 pmii->fMask |= MIIM_DATA;
3826 pmii->dwItemData = (ULONG_PTR) str;
3827 }
3828 if( flags & MF_POPUP) {
3829 pmii->fMask |= MIIM_SUBMENU;
3830 pmii->hSubMenu = (HMENU)id;
3831 }
3832 if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
3833 pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
3834 pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
3835 pmii->wID = (UINT)id;
3836 }
3837
3838
3839 /*******************************************************************
3840 * InsertMenuW (USER32.@)
3841 */
3842 BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3843 UINT_PTR id, LPCWSTR str )
3844 {
3845 MENUITEM *item;
3846 MENUITEMINFOW mii;
3847
3848 if (IS_STRING_ITEM(flags) && str)
3849 TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3850 hMenu, pos, flags, id, debugstr_w(str) );
3851 else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3852 hMenu, pos, flags, id, str );
3853
3854 if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3855 MENU_mnu2mnuii( flags, id, str, &mii);
3856 if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
3857 {
3858 RemoveMenu( hMenu, pos, flags );
3859 return FALSE;
3860 }
3861
3862 item->hCheckBit = item->hUnCheckBit = 0;
3863 return TRUE;
3864 }
3865
3866
3867 /*******************************************************************
3868 * InsertMenuA (USER32.@)
3869 */
3870 BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3871 UINT_PTR id, LPCSTR str )
3872 {
3873 BOOL ret = FALSE;
3874
3875 if (IS_STRING_ITEM(flags) && str)
3876 {
3877 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3878 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3879 if (newstr)
3880 {
3881 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3882 ret = InsertMenuW( hMenu, pos, flags, id, newstr );
3883 HeapFree( GetProcessHeap(), 0, newstr );
3884 }
3885 return ret;
3886 }
3887 else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
3888 }
3889
3890
3891 /*******************************************************************
3892 * AppendMenuA (USER32.@)
3893 */
3894 BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3895 UINT_PTR id, LPCSTR data )
3896 {
3897 return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
3898 }
3899
3900
3901 /*******************************************************************
3902 * AppendMenuW (USER32.@)
3903 */
3904 BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3905 UINT_PTR id, LPCWSTR data )
3906 {
3907 return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
3908 }
3909
3910
3911 /**********************************************************************
3912 * RemoveMenu (USER32.@)
3913 */
3914 BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3915 {
3916 LPPOPUPMENU menu;
3917 MENUITEM *item;
3918
3919 TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
3920 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3921 if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3922
3923 /* Remove item */
3924
3925 MENU_FreeItemData( item );
3926
3927 if (--menu->nItems == 0)
3928 {
3929 HeapFree( GetProcessHeap(), 0, menu->items );
3930 menu->items = NULL;
3931 }
3932 else
3933 {
3934 while(nPos < menu->nItems)
3935 {
3936 *item = *(item+1);
3937 item++;
3938 nPos++;
3939 }
3940 menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
3941 menu->nItems * sizeof(MENUITEM) );
3942 }
3943 return TRUE;
3944 }
3945
3946
3947 /**********************************************************************
3948 * DeleteMenu (USER32.@)
3949 */
3950 BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
3951 {
3952 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
3953 if (!item) return FALSE;
3954 if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
3955 /* nPos is now the position of the item */
3956 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
3957 return TRUE;
3958 }
3959
3960
3961 /*******************************************************************
3962 * ModifyMenuW (USER32.@)
3963 */
3964 BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
3965 UINT_PTR id, LPCWSTR str )
3966 {
3967 MENUITEM *item;
3968 MENUITEMINFOW mii;
3969
3970 if (IS_STRING_ITEM(flags))
3971 TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
3972 else
3973 TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
3974
3975 if (!(item = MENU_FindItem( &hMenu, &pos, flags ))) return FALSE;
3976 MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
3977 MENU_mnu2mnuii( flags, id, str, &mii);
3978 return SetMenuItemInfo_common( item, &mii, TRUE);
3979 }
3980
3981
3982 /*******************************************************************
3983 * ModifyMenuA (USER32.@)
3984 */
3985 BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
3986 UINT_PTR id, LPCSTR str )
3987 {
3988 BOOL ret = FALSE;
3989
3990 if (IS_STRING_ITEM(flags) && str)
3991 {
3992 INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
3993 LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
3994 if (newstr)
3995 {
3996 MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
3997 ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
3998 HeapFree( GetProcessHeap(), 0, newstr );
3999 }
4000 return ret;
4001 }
4002 else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
4003 }
4004
4005
4006 /**********************************************************************
4007 * CreatePopupMenu (USER32.@)
4008 */
4009 HMENU WINAPI CreatePopupMenu(void)
4010 {
4011 HMENU hmenu;
4012 POPUPMENU *menu;
4013
4014 if (!(hmenu = CreateMenu())) return 0;
4015 menu = MENU_GetMenu( hmenu );
4016 menu->wFlags |= MF_POPUP;
4017 menu->bTimeToHide = FALSE;
4018 return hmenu;
4019 }
4020
4021
4022 /**********************************************************************
4023 * GetMenuCheckMarkDimensions (USER.417)
4024 * GetMenuCheckMarkDimensions (USER32.@)
4025 */
4026 DWORD WINAPI GetMenuCheckMarkDimensions(void)
4027 {
4028 return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
4029 }
4030
4031
4032 /**********************************************************************
4033 * SetMenuItemBitmaps (USER32.@)
4034 */
4035 BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
4036 HBITMAP hNewUnCheck, HBITMAP hNewCheck)
4037 {
4038 MENUITEM *item;
4039
4040 if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
4041
4042 if (!hNewCheck && !hNewUnCheck)
4043 {
4044 item->fState &= ~MF_USECHECKBITMAPS;
4045 }
4046 else /* Install new bitmaps */
4047 {
4048 item->hCheckBit = hNewCheck;
4049 item->hUnCheckBit = hNewUnCheck;
4050 item->fState |= MF_USECHECKBITMAPS;
4051 }
4052 return TRUE;
4053 }
4054
4055
4056 /**********************************************************************
4057 * CreateMenu (USER32.@)
4058 */
4059 HMENU WINAPI CreateMenu(void)
4060 {
4061 HMENU hMenu;
4062 LPPOPUPMENU menu;
4063
4064 if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
4065 menu->FocusedItem = NO_SELECTED_ITEM;
4066 menu->bTimeToHide = FALSE;
4067
4068 if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );
4069
4070 TRACE("return %p\n", hMenu );
4071
4072 return hMenu;
4073 }
4074
4075
4076 /**********************************************************************
4077 * DestroyMenu (USER32.@)
4078 */
4079 BOOL WINAPI DestroyMenu( HMENU hMenu )
4080 {
4081 LPPOPUPMENU lppop;
4082
4083 TRACE("(%p)\n", hMenu);
4084
4085 if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
4086 if (lppop == OBJ_OTHER_PROCESS) return FALSE;
4087
4088 /* DestroyMenu should not destroy system menu popup owner */
4089 if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
4090 {
4091 DestroyWindow( lppop->hWnd );
4092 lppop->hWnd = 0;
4093 }
4094
4095 if (lppop->items) /* recursively destroy submenus */
4096 {
4097 int i;
4098 MENUITEM *item = lppop->items;
4099 for (i = lppop->nItems; i > 0; i--, item++)
4100 {
4101 if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
4102 MENU_FreeItemData( item );
4103 }
4104 HeapFree( GetProcessHeap(), 0, lppop->items );
4105 }
4106 HeapFree( GetProcessHeap(), 0, lppop );
4107 return TRUE;
4108 }
4109
4110
4111 /**********************************************************************
4112 * GetSystemMenu (USER32.@)
4113 */
4114 HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
4115 {
4116 WND *wndPtr = WIN_GetPtr( hWnd );
4117 HMENU retvalue = 0;
4118
4119 if (wndPtr == WND_DESKTOP) return 0;
4120 if (wndPtr == WND_OTHER_PROCESS)
4121 {
4122 if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
4123 }
4124 else if (wndPtr)
4125 {
4126 if (wndPtr->hSysMenu && bRevert)
4127 {
4128 DestroyMenu(wndPtr->hSysMenu);
4129 wndPtr->hSysMenu = 0;
4130 }
4131
4132 if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4133 wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
4134
4135 if( wndPtr->hSysMenu )
4136 {
4137 POPUPMENU *menu;
4138 retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4139
4140 /* Store the dummy sysmenu handle to facilitate the refresh */
4141 /* of the close button if the SC_CLOSE item change */
4142 menu = MENU_GetMenu(retvalue);
4143 if ( menu )
4144 menu->hSysMenuOwner = wndPtr->hSysMenu;
4145 }
4146 WIN_ReleasePtr( wndPtr );
4147 }
4148 return bRevert ? 0 : retvalue;
4149 }
4150
4151
4152 /*******************************************************************
4153 * SetSystemMenu (USER32.@)
4154 */
4155 BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
4156 {
4157 WND *wndPtr = WIN_GetPtr( hwnd );
4158
4159 if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
4160 {
4161 if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
4162 wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4163 WIN_ReleasePtr( wndPtr );
4164 return TRUE;
4165 }
4166 return FALSE;
4167 }
4168
4169
4170 /**********************************************************************
4171 * GetMenu (USER32.@)
4172 */
4173 HMENU WINAPI GetMenu( HWND hWnd )
4174 {
4175 HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4176 TRACE("for %p returning %p\n", hWnd, retvalue);
4177 return retvalue;
4178 }
4179
4180 /**********************************************************************
4181 * GetMenuBarInfo (USER32.@)
4182 */
4183 BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
4184 {
4185 FIXME( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );
4186 return FALSE;
4187 }
4188
4189 /**********************************************************************
4190 * MENU_SetMenu
4191 *
4192 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
4193 * SetWindowPos call that would result if SetMenu were called directly.
4194 */
4195 BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
4196 {
4197 TRACE("(%p, %p);\n", hWnd, hMenu);
4198
4199 if (hMenu && !IsMenu(hMenu))
4200 {
4201 WARN("hMenu %p is not a menu handle\n", hMenu);
4202 return FALSE;
4203 }
4204 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4205 return FALSE;
4206
4207 hWnd = WIN_GetFullHandle( hWnd );
4208 if (GetCapture() == hWnd)
4209 set_capture_window( 0, GUI_INMENUMODE, NULL ); /* release the capture */
4210
4211 if (hMenu != 0)
4212 {
4213 LPPOPUPMENU lpmenu;
4214
4215 if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4216
4217 lpmenu->hWnd = hWnd;
4218 lpmenu->Height = 0; /* Make sure we recalculate the size */
4219 }
4220 SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4221 return TRUE;
4222 }
4223
4224
4225 /**********************************************************************
4226 * SetMenu (USER32.@)
4227 */
4228 BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
4229 {
4230 if(!MENU_SetMenu(hWnd, hMenu))
4231 return FALSE;
4232
4233 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4234 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4235 return TRUE;
4236 }
4237
4238
4239 /**********************************************************************
4240 * GetSubMenu (USER32.@)
4241 */
4242 HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
4243 {
4244 MENUITEM * lpmi;
4245
4246 if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4247 if (!(lpmi->fType & MF_POPUP)) return 0;
4248 return lpmi->hSubMenu;
4249 }
4250
4251
4252 /**********************************************************************
4253 * DrawMenuBar (USER32.@)
4254 */
4255 BOOL WINAPI DrawMenuBar( HWND hWnd )
4256 {
4257 LPPOPUPMENU lppop;
4258 HMENU hMenu = GetMenu(hWnd);
4259
4260 if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
4261 return FALSE;
4262 if (!hMenu || !(lppop = MENU_GetMenu( hMenu ))) return FALSE;
4263
4264 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
4265 lppop->hwndOwner = hWnd;
4266 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
4267 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
4268 return TRUE;
4269 }
4270
4271 /***********************************************************************
4272 * DrawMenuBarTemp (USER32.@)
4273 *
4274 * UNDOCUMENTED !!
4275 *
4276 * called by W98SE desk.cpl Control Panel Applet
4277 *
4278 * Not 100% sure about the param names, but close.
4279 */
4280 DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4281 {
4282 LPPOPUPMENU lppop;
4283 UINT i,retvalue;
4284 HFONT hfontOld = 0;
4285 BOOL flat_menu = FALSE;
4286
4287 SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4288
4289 if (!hMenu)
4290 hMenu = GetMenu(hwnd);
4291
4292 if (!hFont)
4293 hFont = get_menu_font(FALSE);
4294
4295 lppop = MENU_GetMenu( hMenu );
4296 if (lppop == NULL || lprect == NULL)
4297 {
4298 retvalue = GetSystemMetrics(SM_CYMENU);
4299 goto END;
4300 }
4301
4302 TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);
4303
4304 hfontOld = SelectObject( hDC, hFont);
4305
4306 if (lppop->Height == 0)
4307 MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
4308
4309 lprect->bottom = lprect->top + lppop->Height;
4310
4311 FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4312
4313 SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
4314 MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
4315 LineTo( hDC, lprect->right, lprect->bottom );
4316
4317 if (lppop->nItems == 0)
4318 {
4319 retvalue = GetSystemMetrics(SM_CYMENU);
4320 goto END;
4321 }
4322
4323 for (i = 0; i < lppop->nItems; i++)
4324 {
4325 MENU_DrawMenuItem( hwnd, hMenu, hwnd,
4326 hDC, &lppop->items[i], lppop->Height, TRUE, ODA_DRAWENTIRE );
4327 }
4328 retvalue = lppop->Height;
4329
4330 END:
4331 if (hfontOld) SelectObject (hDC, hfontOld);
4332 return retvalue;
4333 }
4334
4335 /***********************************************************************
4336 * EndMenu (USER.187)
4337 * EndMenu (USER32.@)
4338 */
4339 BOOL WINAPI EndMenu(void)
4340 {
4341 /* if we are in the menu code, and it is active */
4342 if (!fEndMenu && top_popup)
4343 {
4344 /* terminate the menu handling code */
4345 fEndMenu = TRUE;
4346
4347 /* needs to be posted to wakeup the internal menu handler */
4348 /* which will now terminate the menu, in the event that */
4349 /* the main window was minimized, or lost focus, so we */
4350 /* don't end up with an orphaned menu */
4351 PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4352 }
4353 return fEndMenu;
4354 }
4355
4356
4357 /*****************************************************************
4358 * LoadMenuA (USER32.@)
4359 */
4360 HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
4361 {
4362 HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
4363 if (!hrsrc) return 0;
4364 return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
4365 }
4366
4367
4368 /*****************************************************************
4369 * LoadMenuW (USER32.@)
4370 */
4371 HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
4372 {
4373 HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
4374 if (!hrsrc) return 0;
4375 return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
4376 }
4377
4378
4379 /**********************************************************************
4380 * LoadMenuIndirectW (USER32.@)
4381 */
4382 HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
4383 {
4384 HMENU hMenu;
4385 WORD version, offset;
4386 LPCSTR p = template;
4387
4388 version = GET_WORD(p);
4389 p += sizeof(WORD);
4390 TRACE("%p, ver %d\n", template, version );
4391 switch (version)
4392 {
4393 case 0: /* standard format is version of 0 */
4394 offset = GET_WORD(p);
4395 p += sizeof(WORD) + offset;
4396 if (!(hMenu = CreateMenu())) return 0;
4397 if (!MENU_ParseResource( p, hMenu ))
4398 {
4399 DestroyMenu( hMenu );
4400 return 0;
4401 }
4402 return hMenu;
4403 case 1: /* extended format is version of 1 */
4404 offset = GET_WORD(p);
4405 p += sizeof(WORD) + offset;
4406 if (!(hMenu = CreateMenu())) return 0;
4407 if (!MENUEX_ParseResource( p, hMenu))
4408 {
4409 DestroyMenu( hMenu );
4410 return 0;
4411 }
4412 return hMenu;
4413 default:
4414 ERR("version %d not supported.\n", version);
4415 return 0;
4416 }
4417 }
4418
4419
4420 /**********************************************************************
4421 * LoadMenuIndirectA (USER32.@)
4422 */
4423 HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
4424 {
4425 return LoadMenuIndirectW( template );
4426 }
4427
4428
4429 /**********************************************************************
4430 * IsMenu (USER32.@)
4431 */
4432 BOOL WINAPI IsMenu(HMENU hmenu)
4433 {
4434 LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4435
4436 if (!menu)
4437 {
4438 SetLastError(ERROR_INVALID_MENU_HANDLE);
4439 return FALSE;
4440 }
4441 return TRUE;
4442 }
4443
4444 /**********************************************************************
4445 * GetMenuItemInfo_common
4446 */
4447
4448 static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4449 LPMENUITEMINFOW lpmii, BOOL unicode)
4450 {
4451 MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4452
4453 debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4454
4455 if (!menu) {
4456 SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
4457 return FALSE;
4458 }
4459
4460 if( lpmii->fMask & MIIM_TYPE) {
4461 if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4462 WARN("invalid combination of fMask bits used\n");
4463 /* this does not happen on Win9x/ME */
4464 SetLastError( ERROR_INVALID_PARAMETER);
4465 return FALSE;
4466 }
4467 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4468 if( menu->hbmpItem) lpmii->fType |= MFT_BITMAP;
4469 lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4470 if( lpmii->fType & MFT_BITMAP) {
4471 lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4472 lpmii->cch = 0;
4473 } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4474 /* this does not happen on Win9x/ME */
4475 lpmii->dwTypeData = 0;
4476 lpmii->cch = 0;
4477 }
4478 }
4479
4480 /* copy the text string */
4481 if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
4482 if( !menu->text ) {
4483 if(lpmii->dwTypeData && lpmii->cch) {
4484 lpmii->cch = 0;
4485 if( unicode)
4486 *((WCHAR *)lpmii->dwTypeData) = 0;
4487 else
4488 *((CHAR *)lpmii->dwTypeData) = 0;
4489 }
4490 } else {
4491 int len;
4492 if (unicode)
4493 {
4494 len = strlenW(menu->text);
4495 if(lpmii->dwTypeData && lpmii->cch)
4496 lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
4497 }
4498 else
4499 {
4500 len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
4501 0, NULL, NULL ) - 1;
4502 if(lpmii->dwTypeData && lpmii->cch)
4503 if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
4504 (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
4505 ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
4506 }
4507 /* if we've copied a substring we return its length */
4508 if(lpmii->dwTypeData && lpmii->cch)
4509 if (lpmii->cch <= len + 1)
4510 lpmii->cch--;
4511 else
4512 lpmii->cch = len;
4513 else {
4514 /* return length of string */
4515 /* not on Win9x/ME if fType & MFT_BITMAP */
4516 lpmii->cch = len;
4517 }
4518 }
4519 }
4520
4521 if (lpmii->fMask & MIIM_FTYPE)
4522 lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4523
4524 if (lpmii->fMask & MIIM_BITMAP)
4525 lpmii->hbmpItem = menu->hbmpItem;
4526
4527 if (lpmii->fMask & MIIM_STATE)
4528 lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
4529
4530 if (lpmii->fMask & MIIM_ID)
4531 lpmii->wID = menu->wID;
4532
4533 if (lpmii->fMask & MIIM_SUBMENU)
4534 lpmii->hSubMenu = menu->hSubMenu;
4535 else {
4536 /* hSubMenu is always cleared
4537 * (not on Win9x/ME ) */
4538 lpmii->hSubMenu = 0;
4539 }
4540
4541 if (lpmii->fMask & MIIM_CHECKMARKS) {
4542 lpmii->hbmpChecked = menu->hCheckBit;
4543 lpmii->hbmpUnchecked = menu->hUnCheckBit;
4544 }
4545 if (lpmii->fMask & MIIM_DATA)
4546 lpmii->dwItemData = menu->dwItemData;
4547
4548 return TRUE;
4549 }
4550
4551 /**********************************************************************
4552 * GetMenuItemInfoA (USER32.@)
4553 */
4554 BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
4555 LPMENUITEMINFOA lpmii)
4556 {
4557 BOOL ret;
4558 MENUITEMINFOA mii;
4559 if( lpmii->cbSize != sizeof( mii) &&
4560 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4561 SetLastError( ERROR_INVALID_PARAMETER);
4562 return FALSE;
4563 }
4564 memcpy( &mii, lpmii, lpmii->cbSize);
4565 mii.cbSize = sizeof( mii);
4566 ret = GetMenuItemInfo_common (hmenu, item, bypos,
4567 (LPMENUITEMINFOW)&mii, FALSE);
4568 mii.cbSize = lpmii->cbSize;
4569 memcpy( lpmii, &mii, mii.cbSize);
4570 return ret;
4571 }
4572
4573 /**********************************************************************
4574 * GetMenuItemInfoW (USER32.@)
4575 */
4576 BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
4577 LPMENUITEMINFOW lpmii)
4578 {
4579 BOOL ret;
4580 MENUITEMINFOW mii;
4581 if( lpmii->cbSize != sizeof( mii) &&
4582 lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
4583 SetLastError( ERROR_INVALID_PARAMETER);
4584 return FALSE;
4585 }
4586 memcpy( &mii, lpmii, lpmii->cbSize);
4587 mii.cbSize = sizeof( mii);
4588 ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
4589 mii.cbSize = lpmii->cbSize;
4590 memcpy( lpmii, &mii, mii.cbSize);
4591 return ret;
4592 }
4593
4594
4595 /* set a menu item text from a ASCII or Unicode string */
4596 static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4597 {
4598 if (!text)
4599 menu->text = NULL;
4600 else if (unicode)
4601 {
4602 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
4603 strcpyW( menu->text, text );
4604 }
4605 else
4606 {
4607 LPCSTR str = (LPCSTR)text;
4608 int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
4609 if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
4610 MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
4611 }
4612 }
4613
4614
4615 /**********************************************************************
4616 * MENU_depth
4617 *
4618 * detect if there are loops in the menu tree (or the depth is too large)
4619 */
4620 static int MENU_depth( POPUPMENU *pmenu, int depth)
4621 {
4622 int i;
4623 MENUITEM *item;
4624 int subdepth;
4625
4626 depth++;
4627 if( depth > MAXMENUDEPTH) return depth;
4628 item = pmenu->items;
4629 subdepth = depth;
4630 for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
4631 POPUPMENU *psubmenu = item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4632 if( psubmenu){
4633 int bdepth = MENU_depth( psubmenu, depth);
4634 if( bdepth > subdepth) subdepth = bdepth;
4635 }
4636 if( subdepth > MAXMENUDEPTH)
4637 TRACE("<- hmenu %p\n", item->hSubMenu);
4638 }
4639 return subdepth;
4640 }
4641
4642
4643 /**********************************************************************
4644 * SetMenuItemInfo_common
4645 *
4646 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
4647 * MIIM_BITMAP and MIIM_STRING flags instead.
4648 */
4649
4650 static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4651 const MENUITEMINFOW *lpmii,
4652 BOOL unicode)
4653 {
4654 if (!menu) return FALSE;
4655
4656 debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4657
4658 if (lpmii->fMask & MIIM_FTYPE ) {
4659 menu->fType &= ~MENUITEMINFO_TYPE_MASK;
4660 menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4661 }
4662 if (lpmii->fMask & MIIM_STRING ) {
4663 /* free the string when used */
4664 HeapFree(GetProcessHeap(), 0, menu->text);
4665 set_menu_item_text( menu, lpmii->dwTypeData, unicode );
4666 }
4667
4668 if (lpmii->fMask & MIIM_STATE)
4669 /* Other menu items having MFS_DEFAULT are not converted
4670 to normal items */
4671 menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
4672
4673 if (lpmii->fMask & MIIM_ID)
4674 menu->wID = lpmii->wID;
4675
4676 if (lpmii->fMask & MIIM_SUBMENU) {
4677 menu->hSubMenu = lpmii->hSubMenu;
4678 if (menu->hSubMenu) {
4679 POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4680 if (subMenu) {
4681 if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
4682 ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
4683 menu->hSubMenu = 0;
4684 return FALSE;
4685 }
4686 subMenu->wFlags |= MF_POPUP;
4687 menu->fType |= MF_POPUP;
4688 } else {
4689 SetLastError( ERROR_INVALID_PARAMETER);
4690 return FALSE;
4691 }
4692 }
4693 else
4694 menu->fType &= ~MF_POPUP;
4695 }
4696
4697 if (lpmii->fMask & MIIM_CHECKMARKS)
4698 {
4699 menu->hCheckBit = lpmii->hbmpChecked;
4700 menu->hUnCheckBit = lpmii->hbmpUnchecked;
4701 }
4702 if (lpmii->fMask & MIIM_DATA)
4703 menu->dwItemData = lpmii->dwItemData;
4704
4705 if (lpmii->fMask & MIIM_BITMAP)
4706 menu->hbmpItem = lpmii->hbmpItem;
4707
4708 if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
4709 menu->fType |= MFT_SEPARATOR;
4710
4711 debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
4712 return TRUE;
4713 }
4714
4715 /**********************************************************************
4716 * MENU_NormalizeMenuItemInfoStruct
4717 *
4718 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
4719 * check, copy and extend the MENUITEMINFO struct from the version that the application
4720 * supplied to the version used by wine source. */
4721 static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
4722 MENUITEMINFOW *pmii_out )
4723 {
4724 /* do we recognize the size? */
4725 if( pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
4726 pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) {
4727 SetLastError( ERROR_INVALID_PARAMETER);
4728 return FALSE;
4729 }
4730 /* copy the fields that we have */
4731 memcpy( pmii_out, pmii_in, pmii_in->cbSize);
4732 /* if the hbmpItem member is missing then extend */
4733 if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
4734 pmii_out->cbSize = sizeof( MENUITEMINFOW);
4735 pmii_out->hbmpItem = NULL;
4736 }
4737 /* test for invalid bit combinations */
4738 if( (pmii_out->fMask & MIIM_TYPE &&
4739 pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
4740 (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
4741 WARN("invalid combination of fMask bits used\n");
4742 /* this does not happen on Win9x/ME */
4743 SetLastError( ERROR_INVALID_PARAMETER);
4744 return FALSE;
4745 }
4746 /* convert old style (MIIM_TYPE) to the new */
4747 if( pmii_out->fMask & MIIM_TYPE){
4748 pmii_out->fMask |= MIIM_FTYPE;
4749 if( IS_STRING_ITEM(pmii_out->fType)){
4750 pmii_out->fMask |= MIIM_STRING;
4751 } else if( (pmii_out->fType) & MFT_BITMAP){
4752 pmii_out->fMask |= MIIM_BITMAP;
4753 pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4754 }
4755 }
4756 return TRUE;
4757 }
4758
4759 /**********************************************************************
4760 * SetMenuItemInfoA (USER32.@)
4761 */
4762 BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4763 const MENUITEMINFOA *lpmii)
4764 {
4765 MENUITEMINFOW mii;
4766
4767 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4768
4769 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4770
4771 return SetMenuItemInfo_common(MENU_FindItem(&hmenu, &item, bypos? MF_BYPOSITION : 0),
4772 &mii, FALSE);
4773 }
4774
4775 /**********************************************************************
4776 * SetMenuItemInfoW (USER32.@)
4777 */
4778 BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
4779 const MENUITEMINFOW *lpmii)
4780 {
4781 MENUITEMINFOW mii;
4782
4783 TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
4784
4785 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4786 return SetMenuItemInfo_common(MENU_FindItem(&hmenu,
4787 &item, bypos? MF_BYPOSITION : 0), &mii, TRUE);
4788 }
4789
4790 /**********************************************************************
4791 * SetMenuDefaultItem (USER32.@)
4792 *
4793 */
4794 BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
4795 {
4796 UINT i;
4797 POPUPMENU *menu;
4798 MENUITEM *item;
4799
4800 TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
4801
4802 if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
4803
4804 /* reset all default-item flags */
4805 item = menu->items;
4806 for (i = 0; i < menu->nItems; i++, item++)
4807 {
4808 item->fState &= ~MFS_DEFAULT;
4809 }
4810
4811 /* no default item */
4812 if ( -1 == uItem)
4813 {
4814 return TRUE;
4815 }
4816
4817 item = menu->items;
4818 if ( bypos )
4819 {
4820 if ( uItem >= menu->nItems ) return FALSE;
4821 item[uItem].fState |= MFS_DEFAULT;
4822 return TRUE;
4823 }
4824 else
4825 {
4826 for (i = 0; i < menu->nItems; i++, item++)
4827 {
4828 if (item->wID == uItem)
4829 {
4830 item->fState |= MFS_DEFAULT;
4831 return TRUE;
4832 }
4833 }
4834
4835 }
4836 return FALSE;
4837 }
4838
4839 /**********************************************************************
4840 * GetMenuDefaultItem (USER32.@)
4841 */
4842 UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
4843 {
4844 POPUPMENU *menu;
4845 MENUITEM * item;
4846 UINT i = 0;
4847
4848 TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
4849
4850 if (!(menu = MENU_GetMenu(hmenu))) return -1;
4851
4852 /* find default item */
4853 item = menu->items;
4854
4855 /* empty menu */
4856 if (! item) return -1;
4857
4858 while ( !( item->fState & MFS_DEFAULT ) )
4859 {
4860 i++; item++;
4861 if (i >= menu->nItems ) return -1;
4862 }
4863
4864 /* default: don't return disabled items */
4865 if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;
4866
4867 /* search rekursiv when needed */
4868 if ( (item->fType & MF_POPUP) && (flags & GMDI_GOINTOPOPUPS) )
4869 {
4870 UINT ret;
4871 ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
4872 if ( -1 != ret ) return ret;
4873
4874 /* when item not found in submenu, return the popup item */
4875 }
4876 return ( bypos ) ? i : item->wID;
4877
4878 }
4879
4880
4881 /**********************************************************************
4882 * InsertMenuItemA (USER32.@)
4883 */
4884 BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
4885 const MENUITEMINFOA *lpmii)
4886 {
4887 MENUITEM *item;
4888 MENUITEMINFOW mii;
4889
4890 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4891
4892 if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4893
4894 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4895 return SetMenuItemInfo_common(item, &mii, FALSE);
4896 }
4897
4898
4899 /**********************************************************************
4900 * InsertMenuItemW (USER32.@)
4901 */
4902 BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
4903 const MENUITEMINFOW *lpmii)
4904 {
4905 MENUITEM *item;
4906 MENUITEMINFOW mii;
4907
4908 TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);
4909
4910 if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4911
4912 item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
4913 return SetMenuItemInfo_common(item, &mii, TRUE);
4914 }
4915
4916 /**********************************************************************
4917 * CheckMenuRadioItem (USER32.@)
4918 */
4919
4920 BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
4921 UINT first, UINT last, UINT check,
4922 UINT bypos)
4923 {
4924 BOOL done = FALSE;
4925 UINT i;
4926 MENUITEM *mi_first = NULL, *mi_check;
4927 HMENU m_first, m_check;
4928
4929 for (i = first; i <= last; i++)
4930 {
4931 UINT pos = i;
4932
4933 if (!mi_first)
4934 {
4935 m_first = hMenu;
4936 mi_first = MENU_FindItem(&m_first, &pos, bypos);
4937 if (!mi_first) continue;
4938 mi_check = mi_first;
4939 m_check = m_first;
4940 }
4941 else
4942 {
4943 m_check = hMenu;
4944 mi_check = MENU_FindItem(&m_check, &pos, bypos);
4945 if (!mi_check) continue;
4946 }
4947
4948 if (m_first != m_check) continue;
4949 if (mi_check->fType == MFT_SEPARATOR) continue;
4950
4951 if (i == check)
4952 {
4953 mi_check->fType |= MFT_RADIOCHECK;
4954 mi_check->fState |= MFS_CHECKED;
4955 done = TRUE;
4956 }
4957 else
4958 {
4959 /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
4960 mi_check->fState &= ~MFS_CHECKED;
4961 }
4962 }
4963
4964 return done;
4965 }
4966
4967
4968 /**********************************************************************
4969 * GetMenuItemRect (USER32.@)
4970 *
4971 * ATTENTION: Here, the returned values in rect are the screen
4972 * coordinates of the item just like if the menu was
4973 * always on the upper left side of the application.
4974 *
4975 */
4976 BOOL WINAPI GetMenuItemRect (HWND hwnd, HMENU hMenu, UINT uItem,
4977 LPRECT rect)
4978 {
4979 POPUPMENU *itemMenu;
4980 MENUITEM *item;
4981 HWND referenceHwnd;
4982
4983 TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
4984
4985 item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
4986 referenceHwnd = hwnd;
4987
4988 if(!hwnd)
4989 {
4990 itemMenu = MENU_GetMenu(hMenu);
4991 if (itemMenu == NULL)
4992 return FALSE;
4993
4994 if(itemMenu->hWnd == 0)
4995 return FALSE;
4996 referenceHwnd = itemMenu->hWnd;
4997 }
4998
4999 if ((rect == NULL) || (item == NULL))
5000 return FALSE;
5001
5002 *rect = item->rect;
5003
5004 MapWindowPoints(referenceHwnd, 0, (LPPOINT)rect, 2);
5005
5006 return TRUE;
5007 }
5008
5009 /**********************************************************************
5010 * SetMenuInfo (USER32.@)
5011 *
5012 * FIXME
5013 * actually use the items to draw the menu
5014 * (recalculate and/or redraw)
5015 */
5016 static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5017 {
5018 POPUPMENU *menu;
5019 if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5020
5021 if (lpmi->fMask & MIM_BACKGROUND)
5022 menu->hbrBack = lpmi->hbrBack;
5023
5024 if (lpmi->fMask & MIM_HELPID)
5025 menu->dwContextHelpID = lpmi->dwContextHelpID;
5026
5027 if (lpmi->fMask & MIM_MAXHEIGHT)
5028 menu->cyMax = lpmi->cyMax;
5029
5030 if (lpmi->fMask & MIM_MENUDATA)
5031 menu->dwMenuData = lpmi->dwMenuData;
5032
5033 if (lpmi->fMask & MIM_STYLE)
5034 menu->dwStyle = lpmi->dwStyle;
5035
5036 if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
5037 int i;
5038 MENUITEM *item = menu->items;
5039 for( i = menu->nItems; i; i--, item++)
5040 if( item->fType & MF_POPUP)
5041 menu_SetMenuInfo( item->hSubMenu, lpmi);
5042 }
5043 return TRUE;
5044 }
5045
5046 BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
5047 {
5048 TRACE("(%p %p)\n", hMenu, lpmi);
5049 if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
5050 if( lpmi->fMask & MIM_STYLE) {
5051 if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
5052 if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
5053 if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5054 }
5055 return TRUE;
5056 }
5057 SetLastError( ERROR_INVALID_PARAMETER);
5058 return FALSE;
5059 }
5060
5061 /**********************************************************************
5062 * GetMenuInfo (USER32.@)
5063 *
5064 * NOTES
5065 * win98/NT5.0
5066 *
5067 */
5068 BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
5069 { POPUPMENU *menu;
5070
5071 TRACE("(%p %p)\n", hMenu, lpmi);
5072
5073 if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5074 {
5075
5076 if (lpmi->fMask & MIM_BACKGROUND)
5077 lpmi->hbrBack = menu->hbrBack;
5078
5079 if (lpmi->fMask & MIM_HELPID)
5080 lpmi->dwContextHelpID = menu->dwContextHelpID;
5081
5082 if (lpmi->fMask & MIM_MAXHEIGHT)
5083 lpmi->cyMax = menu->cyMax;
5084
5085 if (lpmi->fMask & MIM_MENUDATA)
5086 lpmi->dwMenuData = menu->dwMenuData;
5087
5088 if (lpmi->fMask & MIM_STYLE)
5089 lpmi->dwStyle = menu->dwStyle;
5090
5091 return TRUE;
5092 }
5093 SetLastError( ERROR_INVALID_PARAMETER);
5094 return FALSE;
5095 }
5096
5097
5098 /**********************************************************************
5099 * SetMenuContextHelpId (USER32.@)
5100 */
5101 BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5102 {
5103 LPPOPUPMENU menu;
5104
5105 TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5106
5107 if ((menu = MENU_GetMenu(hMenu)))
5108 {
5109 menu->dwContextHelpID = dwContextHelpID;
5110 return TRUE;
5111 }
5112 return FALSE;
5113 }
5114
5115
5116 /**********************************************************************
5117 * GetMenuContextHelpId (USER32.@)
5118 */
5119 DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5120 {
5121 LPPOPUPMENU menu;
5122
5123 TRACE("(%p)\n", hMenu);
5124
5125 if ((menu = MENU_GetMenu(hMenu)))
5126 {
5127 return menu->dwContextHelpID;
5128 }
5129 return 0;
5130 }
5131
5132 /**********************************************************************
5133 * MenuItemFromPoint (USER32.@)
5134 */
5135 INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5136 {
5137 POPUPMENU *menu = MENU_GetMenu(hMenu);
5138 UINT pos;
5139
5140 /*FIXME: Do we have to handle hWnd here? */
5141 if (!menu) return -1;
5142 if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1;
5143 return pos;
5144 }
5145
5146
5147 /**********************************************************************
5148 * translate_accelerator
5149 */
5150 static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
5151 BYTE fVirt, WORD key, WORD cmd )
5152 {
5153 INT mask = 0;
5154 UINT mesg = 0;
5155
5156 if (wParam != key) return FALSE;
5157
5158 if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
5159 if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5160 if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5161
5162 if (message == WM_CHAR || message == WM_SYSCHAR)
5163 {
5164 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5165 {
5166 TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5167 goto found;
5168 }
5169 }
5170 else
5171 {
5172 if(fVirt & FVIRTKEY)
5173 {
5174 TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5175 wParam, 0xff & HIWORD(lParam));
5176
5177 if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
5178 TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
5179 }
5180 else
5181 {
5182 if (!(lParam & 0x01000000)) /* no special_key */
5183 {
5184 if ((fVirt & FALT) && (lParam & 0x20000000))
5185 { /* ^^ ALT pressed */
5186 TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5187 goto found;
5188 }
5189 }
5190 }
5191 }
5192 return FALSE;
5193
5194 found:
5195 if (message == WM_KEYUP || message == WM_SYSKEYUP)
5196 mesg = 1;
5197 else
5198 {
5199 HMENU hMenu, hSubMenu, hSysMenu;
5200 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
5201
5202 hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5203 hSysMenu = get_win_sys_menu( hWnd );
5204
5205 /* find menu item and ask application to initialize it */
5206 /* 1. in the system menu */
5207 hSubMenu = hSysMenu;
5208 nPos = cmd;
5209 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5210 {
5211 if (GetCapture())
5212 mesg = 2;
5213 if (!IsWindowEnabled(hWnd))
5214 mesg = 3;
5215 else
5216 {
5217 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
5218 if(hSubMenu != hSysMenu)
5219 {
5220 nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
5221 TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
5222 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
5223 }
5224 uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5225 }
5226 }
5227 else /* 2. in the window's menu */
5228 {
5229 hSubMenu = hMenu;
5230 nPos = cmd;
5231 if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
5232 {
5233 if (GetCapture())
5234 mesg = 2;
5235 if (!IsWindowEnabled(hWnd))
5236 mesg = 3;
5237 else
5238 {
5239 SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
5240 if(hSubMenu != hMenu)
5241 {
5242 nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
5243 TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
5244 SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
5245 }
5246 uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5247 }
5248 }
5249 }
5250
5251 if (mesg == 0)
5252 {
5253 if (uSysStat != (UINT)-1)
5254 {
5255 if (uSysStat & (MF_DISABLED|MF_GRAYED))
5256 mesg=4;
5257 else
5258 mesg=WM_SYSCOMMAND;
5259 }
5260 else
5261 {
5262 if (uStat != (UINT)-1)
5263 {
5264 if (IsIconic(hWnd))
5265 mesg=5;
5266 else
5267 {
5268 if (uStat & (MF_DISABLED|MF_GRAYED))
5269 mesg=6;
5270 else
5271 mesg=WM_COMMAND;
5272 }
5273 }
5274 else
5275 mesg=WM_COMMAND;
5276 }
5277 }
5278 }
5279
5280 if( mesg==WM_COMMAND )
5281 {
5282 TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5283 SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5284 }
5285 else if( mesg==WM_SYSCOMMAND )
5286 {
5287 TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5288 SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5289 }
5290 else
5291 {
5292 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
5293 * #0: unknown (please report!)
5294 * #1: for WM_KEYUP,WM_SYSKEYUP
5295 * #2: mouse is captured
5296 * #3: window is disabled
5297 * #4: it's a disabled system menu option
5298 * #5: it's a menu option, but window is iconic
5299 * #6: it's a menu option, but disabled
5300 */
5301 TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
5302 if(mesg==0)
5303 ERR_(accel)(" unknown reason - please report!\n");
5304 }
5305 return TRUE;
5306 }
5307
5308 /**********************************************************************
5309 * TranslateAcceleratorA (USER32.@)
5310 * TranslateAccelerator (USER32.@)
5311 */
5312 INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5313 {
5314 switch (msg->message)
5315 {
5316 case WM_KEYDOWN:
5317 case WM_SYSKEYDOWN:
5318 return TranslateAcceleratorW( hWnd, hAccel, msg );
5319
5320 case WM_CHAR:
5321 case WM_SYSCHAR:
5322 {
5323 MSG msgW = *msg;
5324 char ch = LOWORD(msg->wParam);
5325 WCHAR wch;
5326 MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5327 msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
5328 return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5329 }
5330
5331 default:
5332 return 0;
5333 }
5334 }
5335
5336 /**********************************************************************
5337 * TranslateAcceleratorW (USER32.@)
5338 */
5339 INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
5340 {
5341 ACCEL data[32], *ptr = data;
5342 int i, count;
5343
5344 if (!hWnd) return 0;
5345
5346 if (msg->message != WM_KEYDOWN &&
5347 msg->message != WM_SYSKEYDOWN &&
5348 msg->message != WM_CHAR &&
5349 msg->message != WM_SYSCHAR)
5350 return 0;
5351
5352 TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5353 hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5354
5355 if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
5356 if (count > sizeof(data)/sizeof(data[0]))
5357 {
5358 if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
5359 }
5360 count = CopyAcceleratorTableW( hAccel, ptr, count );
5361 for (i = 0; i < count; i++)
5362 {
5363 if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5364 ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
5365 break;
5366 }
5367 if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
5368 return (i < count);
5369 }
5370
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.