menu.c 167 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1
/*
Alexandre Julliard's avatar
Alexandre Julliard committed
2 3 4 5
 * Menu functions
 *
 * Copyright 1993 Martin Ayotte
 * Copyright 1994 Alexandre Julliard
Alexandre Julliard's avatar
Alexandre Julliard committed
6
 * Copyright 1997 Morten Welinder
7
 * Copyright 2005 Maxime Bellengé
8
 * Copyright 2006 Phil Krylov
9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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
Alexandre Julliard's avatar
Alexandre Julliard committed
23
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
24 25 26 27 28

/*
 * Note: the style MF_MOUSESELECT is used to mark popup items that
 * have been selected, i.e. their popup menu is currently displayed.
 * This is probably not the meaning this style has in MS-Windows.
29
 *
30 31 32 33 34
 * Note 2: where there is a difference, these menu API's are according
 * the behavior of Windows 2k and Windows XP. Known differences with
 * Windows 9x/ME are documented in the comments, in case an application
 * is found to depend on the old behavior.
 * 
35 36 37 38 39
 * TODO:
 *    implements styles :
 *        - MNS_AUTODISMISS
 *        - MNS_DRAGDROP
 *        - MNS_MODELESS
Alexandre Julliard's avatar
Alexandre Julliard committed
40
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
41

42
#include "config.h"
43 44
#include "wine/port.h"

45
#include <stdarg.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
46
#include <string.h>
47

48 49
#define OEMRESOURCE

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"
Alexandre Julliard's avatar
Alexandre Julliard committed
61

62 63
WINE_DEFAULT_DEBUG_CHANNEL(menu);
WINE_DECLARE_DEBUG_CHANNEL(accel);
Alexandre Julliard's avatar
Alexandre Julliard committed
64

Alexandre Julliard's avatar
Alexandre Julliard committed
65
/* Menu item structure */
Alexandre Julliard's avatar
Alexandre Julliard committed
66 67
typedef struct {
    /* ----------- MENUITEMINFO Stuff ----------- */
68
    UINT fType;			/* Item type. */
69
    UINT fState;		/* Item state.  */
70
    UINT_PTR wID;		/* Item id.  */
71
    HMENU hSubMenu;		/* Pop-up menu.  */
72
    HBITMAP hCheckBit;		/* Bitmap when checked.  */
73
    HBITMAP hUnCheckBit;	/* Bitmap when unchecked.  */
74
    LPWSTR text;		/* Item text. */
75 76
    ULONG_PTR dwItemData;	/* Application defined.  */
    LPWSTR dwTypeData;		/* depends on fMask */
77
    HBITMAP hbmpItem;		/* bitmap */
Alexandre Julliard's avatar
Alexandre Julliard committed
78
    /* ----------- Wine stuff ----------- */
79 80
    RECT      rect;             /* Item area (relative to the items_rect).
                                 * See MENU_AdjustMenuItemRect(). */
81
    UINT      xTab;		/* X position of text after Tab */
Rein Klazes's avatar
Rein Klazes committed
82 83
    SIZE   bmpsize;             /* size needed for the HBMMENU_CALLBACK
                                 * bitmap */ 
Alexandre Julliard's avatar
Alexandre Julliard committed
84 85 86
} MENUITEM;

/* Popup menu structure */
Alexandre Julliard's avatar
Alexandre Julliard committed
87
typedef struct {
88
    struct user_object obj;
Alexandre Julliard's avatar
Alexandre Julliard committed
89 90 91
    WORD        wFlags;       /* Menu flags (MF_POPUP, MF_SYSMENU) */
    WORD	Width;        /* Width of the whole menu */
    WORD	Height;       /* Height of the whole menu */
92
    UINT        nItems;       /* Number of items in the menu */
93 94 95
    HWND        hWnd;         /* Window containing the menu */
    MENUITEM    *items;       /* Array of menu items */
    UINT        FocusedItem;  /* Currently focused item */
96
    HWND	hwndOwner;    /* window receiving the messages for ownerdraw */
97 98 99
    BOOL        bScrolling;   /* Scroll arrows are active */
    UINT        nScrollPos;   /* Current scroll position */
    UINT        nTotalHeight; /* Total height of menu items inside menu */
100
    RECT        items_rect;   /* Rectangle within which the items lie.  Excludes margins and scroll arrows */
101
    /* ------------ MENUINFO members ------ */
102 103
    DWORD	dwStyle;	/* Extended menu style */
    UINT	cyMax;		/* max height of the whole menu, 0 is screen height */
104 105 106
    HBRUSH	hbrBack;	/* brush for menu background */
    DWORD	dwContextHelpID;
    DWORD	dwMenuData;	/* application defined value */
107
    HMENU       hSysMenuOwner;  /* Handle to the dummy sys menu holder */
108
    WORD        textOffset;     /* Offset of text when items have both bitmaps and text */
Alexandre Julliard's avatar
Alexandre Julliard committed
109 110
} POPUPMENU, *LPPOPUPMENU;

Alexandre Julliard's avatar
Alexandre Julliard committed
111 112
/* internal flags for menu tracking */

113 114 115
#define TF_ENDMENU              0x10000
#define TF_SUSPENDPOPUP         0x20000
#define TF_SKIPREMOVE           0x40000
116
#define TF_RCVD_BTN_UP          0x80000
Alexandre Julliard's avatar
Alexandre Julliard committed
117 118 119

typedef struct
{
120 121 122 123 124
    UINT	trackFlags;
    HMENU	hCurrentMenu; /* current submenu (can be equal to hTopMenu)*/
    HMENU	hTopMenu;     /* initial menu */
    HWND	hOwnerWnd;    /* where notifications are sent */
    POINT	pt;
Alexandre Julliard's avatar
Alexandre Julliard committed
125 126
} MTRACKER;

Alexandre Julliard's avatar
Alexandre Julliard committed
127 128 129
#define ITEM_PREV		-1
#define ITEM_NEXT		 1

Alexandre Julliard's avatar
Alexandre Julliard committed
130 131 132
  /* Internal MENU_TrackMenu() flags */
#define TPM_INTERNAL		0xF0000000
#define TPM_BUTTONDOWN		0x40000000		/* menu was clicked before tracking */
133
#define TPM_POPUPMENU           0x20000000              /* menu is a popup menu */
Alexandre Julliard's avatar
Alexandre Julliard committed
134

135 136 137
  /* Space between 2 columns */
#define MENU_COL_SPACE 4

138 139
  /* Margins for popup menus */
#define MENU_MARGIN 3
140

141 142 143 144 145
/* maximum allowed depth of any branch in the menu tree.
 * This value is slightly larger than in windows (25) to
 * stay on the safe side. */
#define MAXMENUDEPTH 30

Alexandre Julliard's avatar
Alexandre Julliard committed
146
  /* (other menu->FocusedItem values give the position of the focused item) */
Alexandre Julliard's avatar
Alexandre Julliard committed
147 148
#define NO_SELECTED_ITEM  0xffff

Alexandre Julliard's avatar
Alexandre Julliard committed
149 150 151
#define MENU_ITEM_TYPE(flags) \
  ((flags) & (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR))

152
/* macro to test that flags do not indicate bitmap, ownerdraw or separator */
Alexandre Julliard's avatar
Alexandre Julliard committed
153
#define IS_STRING_ITEM(flags) (MENU_ITEM_TYPE ((flags)) == MF_STRING)
154
#define IS_MAGIC_BITMAP(id)     ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
Alexandre Julliard's avatar
Alexandre Julliard committed
155

Alexandre Julliard's avatar
Alexandre Julliard committed
156
#define IS_SYSTEM_MENU(menu)  \
157
	(!((menu)->wFlags & MF_POPUP) && ((menu)->wFlags & MF_SYSMENU))
Alexandre Julliard's avatar
Alexandre Julliard committed
158

159 160 161 162 163
#define MENUITEMINFO_TYPE_MASK \
		(MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
		MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
		MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
#define TYPE_MASK  (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
Alexandre Julliard's avatar
Alexandre Julliard committed
164
#define STATE_MASK (~TYPE_MASK)
165
#define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
Alexandre Julliard's avatar
Alexandre Julliard committed
166

167 168
#define WIN_ALLOWED_MENU(style) ((style & (WS_CHILD | WS_POPUP)) != WS_CHILD)

169 170
static SIZE     menucharsize;
static UINT     ODitemheight; /* default owner drawn item height */      
171

Alexandre Julliard's avatar
Alexandre Julliard committed
172
/* Use global popup window because there's no way 2 menus can
173
 * be tracked at the same time.  */
174
static HWND top_popup;
175
static HMENU top_popup_hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
176

Alexandre Julliard's avatar
Alexandre Julliard committed
177
  /* Flag set by EndMenu() to force an exit from menu tracking */
178
static BOOL fEndMenu = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
179

180
DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont);
181

182 183
static BOOL SetMenuItemInfo_common( MENUITEM *, const MENUITEMINFOW *, BOOL);

184 185 186 187 188
/*********************************************************************
 * menu class descriptor
 */
const struct builtin_class_descr MENU_builtin_class =
{
189
    (LPCWSTR)POPUPMENU_CLASS_ATOM,  /* name */
190
    CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS,  /* style */
191
    WINPROC_MENU,                  /* proc */
192
    sizeof(HMENU),                 /* extra */
193
    IDC_ARROW,                     /* cursor */
194
    (HBRUSH)(COLOR_MENU+1)         /* brush */
195 196
};

Alexandre Julliard's avatar
Alexandre Julliard committed
197

Alexandre Julliard's avatar
Alexandre Julliard committed
198 199 200 201 202 203
/***********************************************************************
 *           debug_print_menuitem
 *
 * Print a menuitem in readable form.
 */

Alexandre Julliard's avatar
Alexandre Julliard committed
204
#define debug_print_menuitem(pre, mp, post) \
205
    do { if (TRACE_ON(menu)) do_debug_print_menuitem(pre, mp, post); } while (0)
Alexandre Julliard's avatar
Alexandre Julliard committed
206

Alexandre Julliard's avatar
Alexandre Julliard committed
207
#define MENUOUT(text) \
208
  TRACE("%s%s", (count++ ? "," : ""), (text))
Alexandre Julliard's avatar
Alexandre Julliard committed
209 210 211 212 213 214

#define MENUFLAG(bit,text) \
  do { \
    if (flags & (bit)) { flags &= ~(bit); MENUOUT ((text)); } \
  } while (0)

215
static void do_debug_print_menuitem(const char *prefix, const MENUITEM *mp,
Alexandre Julliard's avatar
Alexandre Julliard committed
216
				    const char *postfix)
Alexandre Julliard's avatar
Alexandre Julliard committed
217
{
218
    static const char * const hbmmenus[] = { "HBMMENU_CALLBACK", "", "HBMMENU_SYSTEM",
219
    "HBMMENU_MBAR_RESTORE", "HBMMENU_MBAR_MINIMIZE", "UNKNOWN BITMAP", "HBMMENU_MBAR_CLOSE",
220 221
    "HBMMENU_MBAR_CLOSE_D", "HBMMENU_MBAR_MINIMIZE_D", "HBMMENU_POPUP_CLOSE",
    "HBMMENU_POPUP_RESTORE", "HBMMENU_POPUP_MAXIMIZE", "HBMMENU_POPUP_MINIMIZE"};
222
    TRACE("%s ", prefix);
Alexandre Julliard's avatar
Alexandre Julliard committed
223
    if (mp) {
224
        UINT flags = mp->fType;
225
        TRACE( "{ ID=0x%lx", mp->wID);
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
        if ( mp->hSubMenu)
            TRACE( ", Sub=%p", mp->hSubMenu);
        if (flags) {
            int count = 0;
            TRACE( ", fType=");
            MENUFLAG( MFT_SEPARATOR, "sep");
            MENUFLAG( MFT_OWNERDRAW, "own");
            MENUFLAG( MFT_BITMAP, "bit");
            MENUFLAG(MF_POPUP, "pop");
            MENUFLAG(MFT_MENUBARBREAK, "barbrk");
            MENUFLAG(MFT_MENUBREAK, "brk");
            MENUFLAG(MFT_RADIOCHECK, "radio");
            MENUFLAG(MFT_RIGHTORDER, "rorder");
            MENUFLAG(MF_SYSMENU, "sys");
            MENUFLAG(MFT_RIGHTJUSTIFY, "right");  /* same as MF_HELP */
            if (flags)
                TRACE( "+0x%x", flags);
        }
        flags = mp->fState;
        if (flags) {
            int count = 0;
            TRACE( ", State=");
            MENUFLAG(MFS_GRAYED, "grey");
            MENUFLAG(MFS_DEFAULT, "default");
            MENUFLAG(MFS_DISABLED, "dis");
            MENUFLAG(MFS_CHECKED, "check");
            MENUFLAG(MFS_HILITE, "hi");
            MENUFLAG(MF_USECHECKBITMAPS, "usebit");
            MENUFLAG(MF_MOUSESELECT, "mouse");
            if (flags)
                TRACE( "+0x%x", flags);
        }
        if (mp->hCheckBit)
            TRACE( ", Chk=%p", mp->hCheckBit);
        if (mp->hUnCheckBit)
            TRACE( ", Unc=%p", mp->hUnCheckBit);
        if (mp->text)
            TRACE( ", Text=%s", debugstr_w(mp->text));
        if (mp->dwItemData)
            TRACE( ", ItemData=0x%08lx", mp->dwItemData);
        if (mp->hbmpItem)
        {
            if( IS_MAGIC_BITMAP(mp->hbmpItem))
                TRACE( ", hbitmap=%s", hbmmenus[ (INT_PTR)mp->hbmpItem + 1]);
            else
                TRACE( ", hbitmap=%p", mp->hbmpItem);
        }
        TRACE( " }");
    } else
        TRACE( "NULL");
276
    TRACE(" %s\n", postfix);
Alexandre Julliard's avatar
Alexandre Julliard committed
277 278 279 280 281
}

#undef MENUOUT
#undef MENUFLAG

282 283 284 285 286 287

/***********************************************************************
 *           MENU_GetMenu
 *
 * Validate the given menu handle and returns the menu structure pointer.
 */
288
static POPUPMENU *MENU_GetMenu(HMENU hMenu)
289
{
290 291 292
    POPUPMENU *menu = get_user_handle_ptr( hMenu, USER_MENU );

    if (menu == OBJ_OTHER_PROCESS)
293
    {
294 295
        WARN( "other process menu %p?\n", hMenu);
        return NULL;
296
    }
297 298
    if (menu) release_user_handle_ptr( menu );  /* FIXME! */
    else WARN("invalid menu handle=%p\n", hMenu);
299 300 301
    return menu;
}

302 303 304 305 306 307 308 309
/***********************************************************************
 *           get_win_sys_menu
 *
 * Get the system menu of a window
 */
static HMENU get_win_sys_menu( HWND hwnd )
{
    HMENU ret = 0;
310
    WND *win = WIN_GetPtr( hwnd );
311
    if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
312 313
    {
        ret = win->hSysMenu;
314
        WIN_ReleasePtr( win );
315 316 317 318
    }
    return ret;
}

319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
/***********************************************************************
 *           get_menu_font
 */
static HFONT get_menu_font( BOOL bold )
{
    static HFONT hMenuFont, hMenuFontBold;

    HFONT ret = bold ? hMenuFontBold : hMenuFont;

    if (!ret)
    {
        NONCLIENTMETRICSW ncm;
        HFONT prev;

        ncm.cbSize = sizeof(NONCLIENTMETRICSW);
        SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSW), &ncm, 0);

        if (bold)
        {
            ncm.lfMenuFont.lfWeight += 300;
            if (ncm.lfMenuFont.lfWeight > 1000) ncm.lfMenuFont.lfWeight = 1000;
        }
        if (!(ret = CreateFontIndirectW( &ncm.lfMenuFont ))) return 0;
        prev = InterlockedCompareExchangePointer( (void **)(bold ? &hMenuFontBold : &hMenuFont),
                                                  ret, NULL );
        if (prev)
        {
            /* another thread beat us to it */
            DeleteObject( ret );
            ret = prev;
        }
    }
    return ret;
}

/***********************************************************************
 *           get_arrow_bitmap
 */
static HBITMAP get_arrow_bitmap(void)
{
    static HBITMAP arrow_bitmap;

    if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
    return arrow_bitmap;
}

365 366
static inline UINT get_scroll_arrow_height(const POPUPMENU *menu)
{
367
    return menucharsize.cy + 4;
368 369
}

Alexandre Julliard's avatar
Alexandre Julliard committed
370 371
/***********************************************************************
 *           MENU_CopySysPopup
Alexandre Julliard's avatar
Alexandre Julliard committed
372
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
373
 * Return the default system menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
374
 */
375
static HMENU MENU_CopySysPopup(BOOL mdi)
Alexandre Julliard's avatar
Alexandre Julliard committed
376
{
377
    static const WCHAR sysmenuW[] = {'S','Y','S','M','E','N','U',0};
378 379
    static const WCHAR sysmenumdiW[] = {'S','Y','S','M','E','N','U','M','D','I',0};
    HMENU hMenu = LoadMenuW(user32_module, (mdi ? sysmenumdiW : sysmenuW));
Alexandre Julliard's avatar
Alexandre Julliard committed
380

Alexandre Julliard's avatar
Alexandre Julliard committed
381
    if( hMenu ) {
382 383
        MENUINFO minfo;
        MENUITEMINFOW miteminfo;
384
        POPUPMENU* menu = MENU_GetMenu(hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
385
        menu->wFlags |= MF_SYSMENU | MF_POPUP;
386
        /* decorate the menu with bitmaps */
387 388 389 390 391 392 393 394 395 396 397 398 399 400
        minfo.cbSize = sizeof( MENUINFO);
        minfo.dwStyle = MNS_CHECKORBMP;
        minfo.fMask = MIM_STYLE;
        SetMenuInfo( hMenu, &minfo);
        miteminfo.cbSize = sizeof( MENUITEMINFOW);
        miteminfo.fMask = MIIM_BITMAP;
        miteminfo.hbmpItem = HBMMENU_POPUP_CLOSE;
        SetMenuItemInfoW( hMenu, SC_CLOSE, FALSE, &miteminfo);
        miteminfo.hbmpItem = HBMMENU_POPUP_RESTORE;
        SetMenuItemInfoW( hMenu, SC_RESTORE, FALSE, &miteminfo);
        miteminfo.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
        SetMenuItemInfoW( hMenu, SC_MAXIMIZE, FALSE, &miteminfo);
        miteminfo.hbmpItem = HBMMENU_POPUP_MINIMIZE;
        SetMenuItemInfoW( hMenu, SC_MINIMIZE, FALSE, &miteminfo);
401
	SetMenuDefaultItem(hMenu, SC_CLOSE, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
402
    }
403
    else
404
	ERR("Unable to load default system menu\n" );
Alexandre Julliard's avatar
Alexandre Julliard committed
405

406
    TRACE("returning %p (mdi=%d).\n", hMenu, mdi );
Alexandre Julliard's avatar
Alexandre Julliard committed
407 408

    return hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
409 410 411
}


Alexandre Julliard's avatar
Alexandre Julliard committed
412 413
/**********************************************************************
 *           MENU_GetSysMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
414
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
415
 * Create a copy of the system menu. System menu in Windows is
416
 * a special menu bar with the single entry - system menu popup.
417 418
 * This popup is presented to the outside world as a "system menu".
 * However, the real system menu handle is sometimes seen in the
419
 * WM_MENUSELECT parameters (and Word 6 likes it this way).
Alexandre Julliard's avatar
Alexandre Julliard committed
420
 */
421
static HMENU MENU_GetSysMenu( HWND hWnd, HMENU hPopupMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
422
{
423
    HMENU hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
424

425
    TRACE("loading system menu, hWnd %p, hPopupMenu %p\n", hWnd, hPopupMenu);
426
    if ((hMenu = CreateMenu()))
Alexandre Julliard's avatar
Alexandre Julliard committed
427
    {
428
	POPUPMENU *menu = MENU_GetMenu(hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
429
	menu->wFlags = MF_SYSMENU;
430
	menu->hWnd = WIN_GetFullHandle( hWnd );
431
	TRACE("hWnd %p (hMenu %p)\n", menu->hWnd, hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
432

433
	if (!hPopupMenu)
434 435 436 437 438 439
        {
            if (GetWindowLongW(hWnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
	        hPopupMenu = MENU_CopySysPopup(TRUE);
            else
	        hPopupMenu = MENU_CopySysPopup(FALSE);
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
440

Alexandre Julliard's avatar
Alexandre Julliard committed
441 442
	if (hPopupMenu)
	{
443 444 445
            if (GetClassLongW(hWnd, GCL_STYLE) & CS_NOCLOSE)
                DeleteMenu(hPopupMenu, SC_CLOSE, MF_BYCOMMAND);

446
	    InsertMenuW( hMenu, -1, MF_SYSMENU | MF_POPUP | MF_BYPOSITION,
447
                         (UINT_PTR)hPopupMenu, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
448

Alexandre Julliard's avatar
Alexandre Julliard committed
449 450
            menu->items[0].fType = MF_SYSMENU | MF_POPUP;
            menu->items[0].fState = 0;
451
            if ((menu = MENU_GetMenu(hPopupMenu))) menu->wFlags |= MF_SYSMENU;
Alexandre Julliard's avatar
Alexandre Julliard committed
452

453
	    TRACE("hMenu=%p (hPopup %p)\n", hMenu, hPopupMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
454 455
	    return hMenu;
	}
456
	DestroyMenu( hMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
457
    }
458
    ERR("failed to load system menu!\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
459
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
460 461 462
}


Alexandre Julliard's avatar
Alexandre Julliard committed
463 464 465 466 467
/***********************************************************************
 *           MENU_InitSysMenuPopup
 *
 * Grey the appropriate items in System menu.
 */
468
static void MENU_InitSysMenuPopup( HMENU hmenu, DWORD style, DWORD clsStyle )
Alexandre Julliard's avatar
Alexandre Julliard committed
469
{
470
    BOOL gray;
Alexandre Julliard's avatar
Alexandre Julliard committed
471 472

    gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
473
    EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
Alexandre Julliard's avatar
Alexandre Julliard committed
474
    gray = ((style & WS_MAXIMIZE) != 0);
475
    EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
Alexandre Julliard's avatar
Alexandre Julliard committed
476
    gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
477
    EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
Alexandre Julliard's avatar
Alexandre Julliard committed
478
    gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
479
    EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
Alexandre Julliard's avatar
Alexandre Julliard committed
480
    gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
481
    EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
Alexandre Julliard's avatar
Alexandre Julliard committed
482
    gray = (clsStyle & CS_NOCLOSE) != 0;
483 484 485 486

    /* The menu item must keep its state if it's disabled */
    if(gray)
	EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
Alexandre Julliard's avatar
Alexandre Julliard committed
487 488 489
}


Alexandre Julliard's avatar
Alexandre Julliard committed
490 491
/******************************************************************************
 *
492 493
 *   UINT  MENU_GetStartOfNextColumn(
 *     HMENU  hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
494 495 496
 *
 *****************************************************************************/

497 498
static UINT  MENU_GetStartOfNextColumn(
    HMENU  hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
499
{
500 501
    POPUPMENU *menu = MENU_GetMenu(hMenu);
    UINT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
502 503 504 505

    if(!menu)
	return NO_SELECTED_ITEM;

506
    i = menu->FocusedItem + 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
507 508 509 510
    if( i == NO_SELECTED_ITEM )
	return i;

    for( ; i < menu->nItems; ++i ) {
511
	if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
Alexandre Julliard's avatar
Alexandre Julliard committed
512 513 514 515 516 517 518 519 520
	    return i;
    }

    return NO_SELECTED_ITEM;
}


/******************************************************************************
 *
521 522
 *   UINT  MENU_GetStartOfPrevColumn(
 *     HMENU  hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
523 524 525
 *
 *****************************************************************************/

526 527
static UINT  MENU_GetStartOfPrevColumn(
    HMENU  hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
528
{
529
    POPUPMENU *menu = MENU_GetMenu(hMenu);
530
    UINT  i;
Alexandre Julliard's avatar
Alexandre Julliard committed
531 532 533 534 535 536 537 538 539 540

    if( !menu )
	return NO_SELECTED_ITEM;

    if( menu->FocusedItem == 0 || menu->FocusedItem == NO_SELECTED_ITEM )
	return NO_SELECTED_ITEM;

    /* Find the start of the column */

    for(i = menu->FocusedItem; i != 0 &&
541
	 !(menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
Alexandre Julliard's avatar
Alexandre Julliard committed
542 543 544 545 546 547
	--i); /* empty */

    if(i == 0)
	return NO_SELECTED_ITEM;

    for(--i; i != 0; --i) {
548
	if (menu->items[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
Alexandre Julliard's avatar
Alexandre Julliard committed
549 550 551
	    break;
    }

552
    TRACE("ret %d.\n", i );
Alexandre Julliard's avatar
Alexandre Julliard committed
553 554 555 556 557 558

    return i;
}



Alexandre Julliard's avatar
Alexandre Julliard committed
559 560 561 562 563 564
/***********************************************************************
 *           MENU_FindItem
 *
 * Find a menu item. Return a pointer on the item, and modifies *hmenu
 * in case the item was in a sub-menu.
 */
565
static MENUITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
566 567
{
    POPUPMENU *menu;
568
    MENUITEM *fallback = NULL;
569
    UINT fallback_pos = 0;
570
    UINT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
571

572
    if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
573 574 575
    if (wFlags & MF_BYPOSITION)
    {
	if (*nPos >= menu->nItems) return NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
576
	return &menu->items[*nPos];
Alexandre Julliard's avatar
Alexandre Julliard committed
577 578 579
    }
    else
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
580
        MENUITEM *item = menu->items;
Alexandre Julliard's avatar
Alexandre Julliard committed
581 582
	for (i = 0; i < menu->nItems; i++, item++)
	{
583
	    if (item->fType & MF_POPUP)
Alexandre Julliard's avatar
Alexandre Julliard committed
584
	    {
585
		HMENU hsubmenu = item->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
586 587 588 589 590 591
		MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
		if (subitem)
		{
		    *hmenu = hsubmenu;
		    return subitem;
		}
592 593 594 595 596 597
		else if (item->wID == *nPos)
		{
		    /* fallback to this item if nothing else found */
		    fallback_pos = i;
		    fallback = item;
		}
598 599 600 601 602
	    }
	    else if (item->wID == *nPos)
	    {
		*nPos = i;
		return item;
Alexandre Julliard's avatar
Alexandre Julliard committed
603 604 605
	    }
	}
    }
606 607 608 609

    if (fallback)
        *nPos = fallback_pos;

610
    return fallback;
Alexandre Julliard's avatar
Alexandre Julliard committed
611 612
}

613 614 615
/***********************************************************************
 *           MENU_FindSubMenu
 *
616
 * Find a Sub menu. Return the position of the submenu, and modifies
617 618 619
 * *hmenu in case it is found in another sub-menu.
 * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
 */
620
static UINT MENU_FindSubMenu( HMENU *hmenu, HMENU hSubTarget )
621 622 623 624
{
    POPUPMENU *menu;
    UINT i;
    MENUITEM *item;
625
    if (((*hmenu)==(HMENU)0xffff) ||
626
            (!(menu = MENU_GetMenu(*hmenu))))
627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
        return NO_SELECTED_ITEM;
    item = menu->items;
    for (i = 0; i < menu->nItems; i++, item++) {
        if(!(item->fType & MF_POPUP)) continue;
        if (item->hSubMenu == hSubTarget) {
            return i;
        }
        else  {
            HMENU hsubmenu = item->hSubMenu;
            UINT pos = MENU_FindSubMenu( &hsubmenu, hSubTarget );
            if (pos != NO_SELECTED_ITEM) {
                *hmenu = hsubmenu;
                return pos;
            }
        }
    }
    return NO_SELECTED_ITEM;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
646 647 648 649 650 651
/***********************************************************************
 *           MENU_FreeItemData
 */
static void MENU_FreeItemData( MENUITEM* item )
{
    /* delete text */
652
    HeapFree( GetProcessHeap(), 0, item->text );
Alexandre Julliard's avatar
Alexandre Julliard committed
653
}
Alexandre Julliard's avatar
Alexandre Julliard committed
654

655 656 657 658 659 660 661 662
/***********************************************************************
 *           MENU_AdjustMenuItemRect
 *
 * Adjust menu item rectangle according to scrolling state.
 */
static void
MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
{
663
    INT scroll_offset = menu->bScrolling ? menu->nScrollPos : 0;
664

665
    OffsetRect( rect, menu->items_rect.left, menu->items_rect.top - scroll_offset );
666 667
}

668 669 670 671 672 673 674 675
enum hittest
{
    ht_nowhere,     /* outside the menu */
    ht_border,      /* anywhere that's not an item or a scroll arrow */
    ht_item,        /* a menu item */
    ht_scroll_up,   /* scroll up arrow */
    ht_scroll_down  /* scroll down arrow */
};
676

Alexandre Julliard's avatar
Alexandre Julliard committed
677 678 679
/***********************************************************************
 *           MENU_FindItemByCoords
 *
680 681
 * Find the item at the specified coordinates (screen coords). Does
 * not work for child windows and therefore should not be called for
Alexandre Julliard's avatar
Alexandre Julliard committed
682
 * an arbitrary system menu.
683 684 685 686 687 688
 *
 * Returns a hittest code.  *pos will contain the position of the
 * item or NO_SELECTED_ITEM.  If the hittest code is ht_scroll_up
 * or ht_scroll_down then *pos will contain the position of the
 * item that's just outside the items_rect - ie, the one that would
 * be scrolled completely into view.
Alexandre Julliard's avatar
Alexandre Julliard committed
689
 */
690
static enum hittest MENU_FindItemByCoords( const POPUPMENU *menu, POINT pt, UINT *pos )
Alexandre Julliard's avatar
Alexandre Julliard committed
691 692
{
    MENUITEM *item;
693
    UINT i;
694
    RECT rect;
695 696 697 698 699 700
    enum hittest ht = ht_border;

    *pos = NO_SELECTED_ITEM;

    if (!GetWindowRect(menu->hWnd, &rect) || !PtInRect(&rect, pt))
        return ht_nowhere;
Alexandre Julliard's avatar
Alexandre Julliard committed
701

702 703
    if (GetWindowLongW( menu->hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) pt.x = rect.right - 1 - pt.x;
    else pt.x -= rect.left;
704
    pt.y -= rect.top;
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723

    if (!PtInRect(&menu->items_rect, pt))
    {
        if (!menu->bScrolling || pt.x < menu->items_rect.left || pt.x >= menu->items_rect.right)
            return ht_border;

        /* On a scroll arrow.  Update pt so that it points to the item just outside items_rect */
        if (pt.y < menu->items_rect.top)
        {
            ht = ht_scroll_up;
            pt.y = menu->items_rect.top - 1;
        }
        else
        {
            ht = ht_scroll_down;
            pt.y = menu->items_rect.bottom;
        }
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
724
    item = menu->items;
Alexandre Julliard's avatar
Alexandre Julliard committed
725 726
    for (i = 0; i < menu->nItems; i++, item++)
    {
727 728
        rect = item->rect;
        MENU_AdjustMenuItemRect(menu, &rect);
729 730 731 732 733 734
        if (PtInRect(&rect, pt))
        {
            *pos = i;
            if (ht != ht_scroll_up && ht != ht_scroll_down) ht = ht_item;
            break;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
735
    }
736 737

    return ht;
Alexandre Julliard's avatar
Alexandre Julliard committed
738 739 740 741 742 743 744 745 746
}


/***********************************************************************
 *           MENU_FindItemByKey
 *
 * Find the menu item selected by a key press.
 * Return item id, -1 if none, -2 if we should close the menu.
 */
747
static UINT MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu,
748
                                WCHAR key, BOOL forceMenuChar )
Alexandre Julliard's avatar
Alexandre Julliard committed
749
{
750
    TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)key, key, hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
751

752
    if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(hwndOwner), 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
753

Alexandre Julliard's avatar
Alexandre Julliard committed
754
    if (hmenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
755
    {
756
	POPUPMENU *menu = MENU_GetMenu( hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
757
	MENUITEM *item = menu->items;
758
	LRESULT menuchar;
Alexandre Julliard's avatar
Alexandre Julliard committed
759 760

	if( !forceMenuChar )
Alexandre Julliard's avatar
Alexandre Julliard committed
761
	{
762
	     UINT i;
763
	     BOOL cjk = GetSystemMetrics( SM_DBCSENABLED );
Alexandre Julliard's avatar
Alexandre Julliard committed
764 765 766

	     for (i = 0; i < menu->nItems; i++, item++)
	     {
767
		if( item->text)
Alexandre Julliard's avatar
Alexandre Julliard committed
768
		{
769
		    const WCHAR *p = item->text - 2;
770 771
		    do
		    {
772 773 774
			const WCHAR *q = p + 2;
			p = strchrW (q, '&');
			if (!p && cjk) p = strchrW (q, '\036'); /* Japanese Win16 */
775 776
		    }
		    while (p != NULL && p [1] == '&');
777
		    if (p && (toupperW(p[1]) == toupperW(key))) return i;
Alexandre Julliard's avatar
Alexandre Julliard committed
778 779
		}
	     }
Alexandre Julliard's avatar
Alexandre Julliard committed
780
	}
781
	menuchar = SendMessageW( hwndOwner, WM_MENUCHAR,
782
                                 MAKEWPARAM( key, menu->wFlags ), (LPARAM)hmenu );
783 784
	if (HIWORD(menuchar) == MNC_EXECUTE) return LOWORD(menuchar);
	if (HIWORD(menuchar) == MNC_CLOSE) return (UINT)(-2);
Alexandre Julliard's avatar
Alexandre Julliard committed
785
    }
786
    return (UINT)(-1);
Alexandre Julliard's avatar
Alexandre Julliard committed
787
}
788 789


790
/***********************************************************************
791
 *           MENU_GetBitmapItemSize
792
 *
793
 * Get the size of a bitmap item.
794
 */
Rein Klazes's avatar
Rein Klazes committed
795 796
static void MENU_GetBitmapItemSize( MENUITEM *lpitem, SIZE *size,
                                    HWND hwndOwner)
797 798
{
    BITMAP bm;
Rein Klazes's avatar
Rein Klazes committed
799
    HBITMAP bmp = lpitem->hbmpItem;
800 801

    size->cx = size->cy = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
802

803
    /* check if there is a magic menu item associated with this item */
Rein Klazes's avatar
Rein Klazes committed
804
    switch( (INT_PTR) bmp )
805
    {
Rein Klazes's avatar
Rein Klazes committed
806 807 808 809 810 811 812 813 814
    case (INT_PTR)HBMMENU_CALLBACK:
        {
            MEASUREITEMSTRUCT measItem;
            measItem.CtlType = ODT_MENU;
            measItem.CtlID = 0;
            measItem.itemID = lpitem->wID;
            measItem.itemWidth = lpitem->rect.right - lpitem->rect.left;
            measItem.itemHeight = lpitem->rect.bottom - lpitem->rect.top;
            measItem.itemData = lpitem->dwItemData;
815
            SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&measItem);
Rein Klazes's avatar
Rein Klazes committed
816 817 818 819 820
            size->cx = measItem.itemWidth;
            size->cy = measItem.itemHeight;
            return;
        }
        break;
821
    case (INT_PTR)HBMMENU_SYSTEM:
Rein Klazes's avatar
Rein Klazes committed
822
        if (lpitem->dwItemData)
823
        {
Rein Klazes's avatar
Rein Klazes committed
824
            bmp = (HBITMAP)lpitem->dwItemData;
825
            break;
826
        }
827 828 829 830 831 832 833 834 835 836 837 838 839
        /* fall through */
    case (INT_PTR)HBMMENU_MBAR_RESTORE:
    case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
    case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
    case (INT_PTR)HBMMENU_MBAR_CLOSE:
    case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
        size->cx = GetSystemMetrics( SM_CYMENU ) - 4;
        size->cy = size->cx;
        return;
    case (INT_PTR)HBMMENU_POPUP_CLOSE:
    case (INT_PTR)HBMMENU_POPUP_RESTORE:
    case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
    case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
840 841
        size->cx = GetSystemMetrics( SM_CXMENUSIZE);
        size->cy = GetSystemMetrics( SM_CYMENUSIZE);
842
        return;
843
    }
844
    if (GetObjectW(bmp, sizeof(bm), &bm ))
845 846 847 848 849 850 851 852 853 854 855
    {
        size->cx = bm.bmWidth;
        size->cy = bm.bmHeight;
    }
}

/***********************************************************************
 *           MENU_DrawBitmapItem
 *
 * Draw a bitmap item.
 */
Rein Klazes's avatar
Rein Klazes committed
856
static void MENU_DrawBitmapItem( HDC hdc, MENUITEM *lpitem, const RECT *rect,
857
                                 POPUPMENU *menu, HWND hwndOwner, UINT odaction )
858
{
859 860 861
    BITMAP bm;
    DWORD rop;
    HDC hdcMem;
862
    HBITMAP bmp;
863 864 865 866
    int w = rect->right - rect->left;
    int h = rect->bottom - rect->top;
    int bmp_xoffset = 0;
    int left, top;
867 868
    HBITMAP hbmToDraw = lpitem->hbmpItem;
    bmp = hbmToDraw;
869 870

    /* Check if there is a magic menu item associated with this item */
871
    if (IS_MAGIC_BITMAP(hbmToDraw))
872 873
    {
        UINT flags = 0;
874
        WCHAR bmchr = 0;
875 876
        RECT r;

877
	switch((INT_PTR)hbmToDraw)
878
        {
879
        case (INT_PTR)HBMMENU_SYSTEM:
880 881 882
            if (lpitem->dwItemData)
            {
                bmp = (HBITMAP)lpitem->dwItemData;
883
                if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
884 885 886
            }
            else
            {
887 888 889
                static HBITMAP hBmpSysMenu;

                if (!hBmpSysMenu) hBmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
890
                bmp = hBmpSysMenu;
891
                if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
892 893 894 895 896
                /* only use right half of the bitmap */
                bmp_xoffset = bm.bmWidth / 2;
                bm.bmWidth -= bmp_xoffset;
            }
            goto got_bitmap;
897
        case (INT_PTR)HBMMENU_MBAR_RESTORE:
898 899
            flags = DFCS_CAPTIONRESTORE;
            break;
900
        case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
901 902
            flags = DFCS_CAPTIONMIN;
            break;
903
        case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
904 905
            flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
            break;
906
        case (INT_PTR)HBMMENU_MBAR_CLOSE:
907 908
            flags = DFCS_CAPTIONCLOSE;
            break;
909
        case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
910 911
            flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
            break;
912
        case (INT_PTR)HBMMENU_CALLBACK:
Rein Klazes's avatar
Rein Klazes committed
913 914 915 916 917 918 919 920 921 922 923
            {
                DRAWITEMSTRUCT drawItem;
                drawItem.CtlType = ODT_MENU;
                drawItem.CtlID = 0;
                drawItem.itemID = lpitem->wID;
                drawItem.itemAction = odaction;
                drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
                drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
                drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
                drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
                drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
924
                drawItem.hwndItem = (HWND)menu->obj.handle;
Rein Klazes's avatar
Rein Klazes committed
925 926 927 928 929 930 931
                drawItem.hDC = hdc;
                drawItem.itemData = lpitem->dwItemData;
                drawItem.rcItem = *rect;
                SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
                return;
            }
            break;
932
        case (INT_PTR)HBMMENU_POPUP_CLOSE:
933 934
            bmchr = 0x72;
            break;
935
        case (INT_PTR)HBMMENU_POPUP_RESTORE:
936 937
            bmchr = 0x32;
            break;
938
        case (INT_PTR)HBMMENU_POPUP_MAXIMIZE:
939 940
            bmchr = 0x31;
            break;
941
        case (INT_PTR)HBMMENU_POPUP_MINIMIZE:
942 943
            bmchr = 0x30;
            break;
944
        default:
945
            FIXME("Magic %p not implemented\n", hbmToDraw);
946 947
            return;
        }
948 949
        if (bmchr)
        {
950 951
            /* draw the magic bitmaps using marlett font characters */
            /* FIXME: fontsize and the position (x,y) could probably be better */
952 953 954 955
            HFONT hfont, hfontsav;
            LOGFONTW logfont = { 0, 0, 0, 0, FW_NORMAL,
                0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0,
                { 'M','a','r','l','e','t','t',0 } };
956
            logfont.lfHeight =  min( h, w) - 5 ;
957 958 959
            TRACE(" height %d rect %s\n", logfont.lfHeight, wine_dbgstr_rect( rect));
            hfont = CreateFontIndirectW( &logfont);
            hfontsav = SelectObject(hdc, hfont);
960
            TextOutW( hdc,  rect->left, rect->top + 2, &bmchr, 1);
961 962 963 964 965 966 967 968 969 970
            SelectObject(hdc, hfontsav);
            DeleteObject( hfont);
        }
        else
        {
            r = *rect;
            InflateRect( &r, -1, -1 );
            if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED;
            DrawFrameControl( hdc, &r, DFC_CAPTION, flags );
        }
971
        return;
972 973
    }

974
    if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
975 976 977 978 979 980 981 982

 got_bitmap:
    hdcMem = CreateCompatibleDC( hdc );
    SelectObject( hdcMem, bmp );

    /* handle fontsize > bitmap_height */
    top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
    left=rect->left;
983 984
    rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
    if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
985
        SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
986 987
    BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
    DeleteDC( hdcMem );
988
}
Alexandre Julliard's avatar
Alexandre Julliard committed
989

990

Alexandre Julliard's avatar
Alexandre Julliard committed
991 992 993 994 995
/***********************************************************************
 *           MENU_CalcItemSize
 *
 * Calculate the size of the menu item and store it in lpitem->rect.
 */
996
static void MENU_CalcItemSize( HDC hdc, MENUITEM *lpitem, HWND hwndOwner,
997
			       INT orgX, INT orgY, BOOL menuBar, POPUPMENU* lppop )
Alexandre Julliard's avatar
Alexandre Julliard committed
998
{
999
    WCHAR *p;
1000
    UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
1001 1002
    UINT arrow_bitmap_width;
    BITMAP bm;
1003
    INT itemheight;
Alexandre Julliard's avatar
Alexandre Julliard committed
1004

1005
    TRACE("dc=%p owner=%p (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
1006
    debug_print_menuitem("MENU_CalcItemSize: menuitem:", lpitem,
Alexandre Julliard's avatar
Alexandre Julliard committed
1007
			 (menuBar ? " (MenuBar)" : ""));
Alexandre Julliard's avatar
Alexandre Julliard committed
1008

1009 1010 1011
    GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
    arrow_bitmap_width = bm.bmWidth;

Rein Klazes's avatar
Rein Klazes committed
1012 1013 1014 1015 1016 1017 1018 1019
    /* not done in Menu_Init: GetDialogBaseUnits() breaks there */
    if( !menucharsize.cx ) {
        menucharsize.cx = GdiGetCharDimensions( hdc, NULL, &menucharsize.cy );
        /* Win95/98/ME will use menucharsize.cy here. Testing is possible
         * but it is unlikely an application will depend on that */
        ODitemheight = HIWORD( GetDialogBaseUnits());
    }

1020
    SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
Alexandre Julliard's avatar
Alexandre Julliard committed
1021

Alexandre Julliard's avatar
Alexandre Julliard committed
1022
    if (lpitem->fType & MF_OWNERDRAW)
Alexandre Julliard's avatar
Alexandre Julliard committed
1023
    {
1024
        MEASUREITEMSTRUCT mis;
Alexandre Julliard's avatar
Alexandre Julliard committed
1025
        mis.CtlType    = ODT_MENU;
1026
        mis.CtlID      = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1027
        mis.itemID     = lpitem->wID;
1028
        mis.itemData   = lpitem->dwItemData;
1029
        mis.itemHeight = ODitemheight;
1030
        mis.itemWidth  = 0;
1031
        SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
1032
        /* Tests reveal that Windows ( Win95 through WinXP) adds twice the average
1033 1034 1035 1036 1037 1038 1039 1040
         * width of a menufont character to the width of an owner-drawn menu. 
         */
        lpitem->rect.right += mis.itemWidth + 2 * menucharsize.cx;
        if (menuBar) {
            /* under at least win95 you seem to be given a standard
               height for the menu and the height value is ignored */
            lpitem->rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
        } else
1041
            lpitem->rect.bottom += mis.itemHeight;
1042

1043
        TRACE("id=%04lx size=%dx%d\n",
1044 1045 1046
                lpitem->wID, lpitem->rect.right-lpitem->rect.left,
                lpitem->rect.bottom-lpitem->rect.top);
        return;
1047
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1048

Alexandre Julliard's avatar
Alexandre Julliard committed
1049
    if (lpitem->fType & MF_SEPARATOR)
Alexandre Julliard's avatar
Alexandre Julliard committed
1050
    {
Rein Klazes's avatar
Rein Klazes committed
1051 1052 1053 1054
        lpitem->rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;
        if( !menuBar)
            lpitem->rect.right += arrow_bitmap_width + menucharsize.cx;
        return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1055 1056
    }

1057
    itemheight = 0;
1058
    lpitem->xTab = 0;
1059

Rein Klazes's avatar
Rein Klazes committed
1060 1061 1062
    if (!menuBar) {
        if (lpitem->hbmpItem) {
            SIZE size;
Rein Klazes's avatar
Rein Klazes committed
1063

Rein Klazes's avatar
Rein Klazes committed
1064 1065 1066 1067
            MENU_GetBitmapItemSize(lpitem, &size, hwndOwner);
            /* Keep the size of the bitmap in callback mode to be able
             * to draw it correctly */
            lpitem->bmpsize = size;
1068
            lppop->textOffset = max( lppop->textOffset, size.cx);
Rein Klazes's avatar
Rein Klazes committed
1069 1070 1071 1072 1073
            lpitem->rect.right += size.cx + 2;
            itemheight = size.cy + 2;
        }
        if( !(lppop->dwStyle & MNS_NOCHECK))
            lpitem->rect.right += check_bitmap_width; 
1074 1075 1076
        lpitem->rect.right += 4 + menucharsize.cx;
        lpitem->xTab = lpitem->rect.right;
        lpitem->rect.right += arrow_bitmap_width;
Rein Klazes's avatar
Rein Klazes committed
1077
    } else if (lpitem->hbmpItem) { /* menuBar */
1078
        SIZE size;
1079

Rein Klazes's avatar
Rein Klazes committed
1080 1081
        MENU_GetBitmapItemSize( lpitem, &size, hwndOwner );
        lpitem->bmpsize = size;
1082
        lpitem->rect.right  += size.cx;
1083 1084
        if( lpitem->text) lpitem->rect.right  += 2;
        itemheight = size.cy;
Alexandre Julliard's avatar
Alexandre Julliard committed
1085
    }
1086

1087
    /* it must be a text item - unless it's the system menu */
1088
    if (!(lpitem->fType & MF_SYSMENU) && lpitem->text) {
1089
        HFONT hfontOld = NULL;
1090 1091
        RECT rc = lpitem->rect;
        LONG txtheight, txtwidth;
Alexandre Julliard's avatar
Alexandre Julliard committed
1092

1093 1094 1095
	if ( lpitem->fState & MFS_DEFAULT ) {
	     hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
	}
1096 1097 1098 1099 1100 1101
        if (menuBar) {
            txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
                    DT_SINGLELINE|DT_CALCRECT); 
            lpitem->rect.right  += rc.right - rc.left;
            itemheight = max( max( itemheight, txtheight),
                    GetSystemMetrics( SM_CYMENU) - 1);
Rein Klazes's avatar
Rein Klazes committed
1102
            lpitem->rect.right +=  2 * menucharsize.cx;
1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116
        } else {
            if ((p = strchrW( lpitem->text, '\t' )) != NULL) {
                RECT tmprc = rc;
                LONG tmpheight;
                int n = (int)( p - lpitem->text);
                /* Item contains a tab (only meaningful in popup menus) */
                /* get text size before the tab */
                txtheight = DrawTextW( hdc, lpitem->text, n, &rc,
                        DT_SINGLELINE|DT_CALCRECT);
                txtwidth = rc.right - rc.left;
                p += 1; /* advance past the Tab */
                /* get text size after the tab */
                tmpheight = DrawTextW( hdc, p, -1, &tmprc,
                        DT_SINGLELINE|DT_CALCRECT);
1117
                lpitem->xTab += txtwidth;
1118 1119 1120 1121 1122 1123 1124
                txtheight = max( txtheight, tmpheight);
                txtwidth += menucharsize.cx + /* space for the tab */
                    tmprc.right - tmprc.left; /* space for the short cut */
            } else {
                txtheight = DrawTextW( hdc, lpitem->text, -1, &rc,
                        DT_SINGLELINE|DT_CALCRECT);
                txtwidth = rc.right - rc.left;
1125
                lpitem->xTab += txtwidth;
1126 1127 1128 1129 1130
            }
            lpitem->rect.right  += 2 + txtwidth;
            itemheight = max( itemheight,
                    max( txtheight + 2, menucharsize.cy + 4));
        }
1131
	if (hfontOld) SelectObject (hdc, hfontOld);
1132 1133
    } else if( menuBar) {
        itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
Alexandre Julliard's avatar
Alexandre Julliard committed
1134
    }
1135
    lpitem->rect.bottom += itemheight;
1136
    TRACE("%s\n", wine_dbgstr_rect( &lpitem->rect));
Alexandre Julliard's avatar
Alexandre Julliard committed
1137 1138 1139 1140 1141 1142 1143
}

/***********************************************************************
 *           MENU_PopupMenuCalcSize
 *
 * Calculate the size of a popup menu.
 */
1144
static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, UINT max_height )
Alexandre Julliard's avatar
Alexandre Julliard committed
1145
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1146
    MENUITEM *lpitem;
1147
    HDC hdc;
1148
    UINT start, i;
1149
    BOOL textandbmp = FALSE, multi_col = FALSE;
1150
    int orgX, orgY, maxTab, maxTabWidth;
Alexandre Julliard's avatar
Alexandre Julliard committed
1151 1152

    lppop->Width = lppop->Height = 0;
1153
    SetRectEmpty(&lppop->items_rect);
1154

Alexandre Julliard's avatar
Alexandre Julliard committed
1155
    if (lppop->nItems == 0) return;
1156
    hdc = GetDC( 0 );
1157

1158
    SelectObject( hdc, get_menu_font(FALSE));
1159

Alexandre Julliard's avatar
Alexandre Julliard committed
1160
    start = 0;
1161

1162
    lppop->textOffset = 0;
1163

Alexandre Julliard's avatar
Alexandre Julliard committed
1164
    while (start < lppop->nItems)
Alexandre Julliard's avatar
Alexandre Julliard committed
1165
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1166
	lpitem = &lppop->items[start];
1167
	orgX = lppop->items_rect.right;
1168
        if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
1169
            orgX += MENU_COL_SPACE; 
1170
	orgY = lppop->items_rect.top;
Alexandre Julliard's avatar
Alexandre Julliard committed
1171

Alexandre Julliard's avatar
Alexandre Julliard committed
1172
	maxTab = maxTabWidth = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1173
	  /* Parse items until column break or end of menu */
Alexandre Julliard's avatar
Alexandre Julliard committed
1174
	for (i = start; i < lppop->nItems; i++, lpitem++)
Alexandre Julliard's avatar
Alexandre Julliard committed
1175
	{
1176 1177 1178 1179 1180
            if (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))
            {
                multi_col = TRUE;
                if (i != start) break;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
1181

1182
	    MENU_CalcItemSize( hdc, lpitem, lppop->hwndOwner, orgX, orgY, FALSE, lppop );
1183
	    lppop->items_rect.right = max( lppop->items_rect.right, lpitem->rect.right );
Alexandre Julliard's avatar
Alexandre Julliard committed
1184
	    orgY = lpitem->rect.bottom;
Alexandre Julliard's avatar
Alexandre Julliard committed
1185
	    if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
Alexandre Julliard's avatar
Alexandre Julliard committed
1186
	    {
1187 1188
		maxTab = max( maxTab, lpitem->xTab );
		maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
Alexandre Julliard's avatar
Alexandre Julliard committed
1189
	    }
1190
            if( lpitem->text && lpitem->hbmpItem) textandbmp = TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1191 1192 1193
	}

	  /* Finish the column (set all items to the largest width found) */
1194
	lppop->items_rect.right = max( lppop->items_rect.right, maxTab + maxTabWidth );
Alexandre Julliard's avatar
Alexandre Julliard committed
1195
	for (lpitem = &lppop->items[start]; start < i; start++, lpitem++)
Alexandre Julliard's avatar
Alexandre Julliard committed
1196
	{
1197
	    lpitem->rect.right = lppop->items_rect.right;
Alexandre Julliard's avatar
Alexandre Julliard committed
1198
	    if (IS_STRING_ITEM(lpitem->fType) && lpitem->xTab)
1199
		lpitem->xTab = maxTab;
1200

Alexandre Julliard's avatar
Alexandre Julliard committed
1201
	}
1202
	lppop->items_rect.bottom = max( lppop->items_rect.bottom, orgY );
Alexandre Julliard's avatar
Alexandre Julliard committed
1203 1204
    }

1205 1206 1207 1208 1209 1210
    /* if none of the items have both text and bitmap then
     * the text and bitmaps are all aligned on the left. If there is at
     * least one item with both text and bitmap then bitmaps are
     * on the left and texts left aligned with the right hand side
     * of the bitmaps */
    if( !textandbmp) lppop->textOffset = 0;
1211

1212 1213
    lppop->nTotalHeight = lppop->items_rect.bottom;

1214 1215
    /* space for the border */
    OffsetRect(&lppop->items_rect, MENU_MARGIN, MENU_MARGIN);
1216 1217
    lppop->Height = lppop->items_rect.bottom + MENU_MARGIN;
    lppop->Width = lppop->items_rect.right + MENU_MARGIN;
Alexandre Julliard's avatar
Alexandre Julliard committed
1218

1219
    /* Adjust popup height if it exceeds maximum */
1220
    if (lppop->Height >= max_height)
1221
    {
1222
        lppop->Height = max_height;
1223
        lppop->bScrolling = !multi_col;
1224
        /* When the scroll arrows are present, don't add the top/bottom margin as well */
1225 1226 1227 1228 1229
        if (lppop->bScrolling)
        {
            lppop->items_rect.top = get_scroll_arrow_height(lppop);
            lppop->items_rect.bottom = lppop->Height - get_scroll_arrow_height(lppop);
        }
1230 1231 1232 1233 1234 1235
    }
    else
    {
        lppop->bScrolling = FALSE;
    }

1236
    ReleaseDC( 0, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
1237 1238 1239 1240 1241 1242
}


/***********************************************************************
 *           MENU_MenuBarCalcSize
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1243
 * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
Alexandre Julliard's avatar
Alexandre Julliard committed
1244 1245 1246
 * height is off by 1 pixel which causes lengthy window relocations when
 * active document window is maximized/restored.
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1247 1248
 * Calculate the size of the menu bar.
 */
1249 1250
static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect,
                                  LPPOPUPMENU lppop, HWND hwndOwner )
Alexandre Julliard's avatar
Alexandre Julliard committed
1251
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1252
    MENUITEM *lpitem;
1253
    UINT start, i, helpPos;
1254
    int orgX, orgY;
Alexandre Julliard's avatar
Alexandre Julliard committed
1255 1256 1257

    if ((lprect == NULL) || (lppop == NULL)) return;
    if (lppop->nItems == 0) return;
1258
    TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
1259 1260 1261
    /* Start with a 1 pixel top border.
       This corresponds to the difference between SM_CYMENU and SM_CYMENUSIZE. */
    SetRect(&lppop->items_rect, 0, 0, lprect->right - lprect->left, 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
1262
    start = 0;
1263
    helpPos = ~0U;
1264
    lppop->textOffset = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1265
    while (start < lppop->nItems)
Alexandre Julliard's avatar
Alexandre Julliard committed
1266
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1267
	lpitem = &lppop->items[start];
1268 1269
	orgX = lppop->items_rect.left;
	orgY = lppop->items_rect.bottom;
Alexandre Julliard's avatar
Alexandre Julliard committed
1270 1271

	  /* Parse items until line break or end of menu */
Alexandre Julliard's avatar
Alexandre Julliard committed
1272
	for (i = start; i < lppop->nItems; i++, lpitem++)
Alexandre Julliard's avatar
Alexandre Julliard committed
1273
	{
1274
	    if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i;
Alexandre Julliard's avatar
Alexandre Julliard committed
1275
	    if ((i != start) &&
Alexandre Julliard's avatar
Alexandre Julliard committed
1276
		(lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1277

1278
	    TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY );
Alexandre Julliard's avatar
Alexandre Julliard committed
1279
	    debug_print_menuitem ("  item: ", lpitem, "");
1280
	    MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE, lppop );
1281

1282
	    if (lpitem->rect.right > lppop->items_rect.right)
Alexandre Julliard's avatar
Alexandre Julliard committed
1283
	    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1284
		if (i != start) break;
1285
		else lpitem->rect.right = lppop->items_rect.right;
Alexandre Julliard's avatar
Alexandre Julliard committed
1286
	    }
1287
	    lppop->items_rect.bottom = max( lppop->items_rect.bottom, lpitem->rect.bottom );
Alexandre Julliard's avatar
Alexandre Julliard committed
1288 1289 1290 1291
	    orgX = lpitem->rect.right;
	}

	  /* Finish the line (set all items to the largest height found) */
1292
	while (start < i) lppop->items[start++].rect.bottom = lppop->items_rect.bottom;
Alexandre Julliard's avatar
Alexandre Julliard committed
1293 1294
    }

1295 1296 1297 1298
    OffsetRect(&lppop->items_rect, lprect->left, lprect->top);
    lppop->Width = lppop->items_rect.right - lppop->items_rect.left;
    lppop->Height = lppop->items_rect.bottom - lppop->items_rect.top;
    lprect->bottom = lppop->items_rect.bottom;
Alexandre Julliard's avatar
Alexandre Julliard committed
1299

1300 1301
    /* Flush right all items between the MF_RIGHTJUSTIFY and */
    /* the last item (if several lines, only move the last line) */
1302
    if (helpPos == ~0U) return;
1303 1304
    lpitem = &lppop->items[lppop->nItems-1];
    orgY = lpitem->rect.top;
1305
    orgX = lprect->right - lprect->left;
1306 1307 1308 1309 1310 1311
    for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--) {
        if (lpitem->rect.top != orgY) break;	/* Other line */
        if (lpitem->rect.right >= orgX) break;	/* Too far right already */
        lpitem->rect.left += orgX - lpitem->rect.right;
        lpitem->rect.right = orgX;
        orgX = lpitem->rect.left;
Alexandre Julliard's avatar
Alexandre Julliard committed
1312
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1313 1314
}

1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350
static void draw_scroll_arrow(HDC hdc, int x, int top, int height, BOOL up, BOOL enabled)
{
    RECT rect, light_rect;
    HBRUSH brush = GetSysColorBrush( enabled ? COLOR_BTNTEXT : COLOR_BTNSHADOW );
    HBRUSH light = GetSysColorBrush( COLOR_3DLIGHT );

    if (!up)
    {
        top = top + height;
        if (!enabled)
        {
            SetRect( &rect, x + 1, top, x + 2, top + 1);
            FillRect( hdc, &rect, light );
        }
        top--;
    }

    SetRect( &rect, x, top, x + 1, top + 1);
    while (height--)
    {
        FillRect( hdc, &rect, brush );
        if (!enabled && !up && height)
        {
            SetRect( &light_rect, rect.right, rect.top, rect.right + 2, rect.bottom );
            FillRect( hdc, &light_rect, light );
        }
        InflateRect( &rect, 1, 0 );
        OffsetRect( &rect, 0, up ? 1 : -1 );
    }

    if (!enabled && up)
    {
        rect.left += 2;
        FillRect( hdc, &rect, light );
    }
}
1351 1352 1353 1354 1355 1356 1357

/***********************************************************************
 *           MENU_DrawScrollArrows
 *
 * Draw scroll arrows.
 */
static void
1358
MENU_DrawScrollArrows(const POPUPMENU *menu, HDC hdc)
1359
{
1360 1361 1362 1363 1364 1365 1366 1367
    UINT full_height = get_scroll_arrow_height( menu );
    UINT arrow_height = full_height / 3;
    BOOL at_end = menu->nScrollPos + menu->items_rect.bottom - menu->items_rect.top == menu->nTotalHeight;

    draw_scroll_arrow( hdc, menu->Width / 3, arrow_height, arrow_height,
                       TRUE, menu->nScrollPos != 0);
    draw_scroll_arrow( hdc, menu->Width / 3, menu->Height - 2 * arrow_height, arrow_height,
                       FALSE, !at_end );
1368 1369 1370
}


1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389
/***********************************************************************
 *           draw_popup_arrow
 *
 * Draws the popup-menu arrow.
 */
static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
        UINT arrow_bitmap_height)
{
    HDC hdcMem = CreateCompatibleDC( hdc );
    HBITMAP hOrigBitmap;

    hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
    BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
            (rect.top + rect.bottom - arrow_bitmap_height) / 2,
            arrow_bitmap_width, arrow_bitmap_height,
            hdcMem, 0, 0, SRCCOPY );
    SelectObject( hdcMem, hOrigBitmap );
    DeleteDC( hdcMem );
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1390 1391 1392 1393 1394
/***********************************************************************
 *           MENU_DrawMenuItem
 *
 * Draw a single menu item.
 */
1395
static void MENU_DrawMenuItem( HWND hwnd, POPUPMENU *menu, HWND hwndOwner, HDC hdc,
1396
                               MENUITEM *lpitem, BOOL menuBar, UINT odaction )
Alexandre Julliard's avatar
Alexandre Julliard committed
1397
{
1398
    RECT rect, bmprc;
1399 1400
    BOOL flat_menu = FALSE;
    int bkgnd;
1401
    UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
1402
    HRGN old_clip = NULL, clip;
Alexandre Julliard's avatar
Alexandre Julliard committed
1403

Alexandre Julliard's avatar
Alexandre Julliard committed
1404
    debug_print_menuitem("MENU_DrawMenuItem: ", lpitem, "");
Alexandre Julliard's avatar
Alexandre Julliard committed
1405

1406 1407 1408 1409 1410 1411 1412
    if (!menuBar) {
        BITMAP bmp;
        GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
        arrow_bitmap_width = bmp.bmWidth;
        arrow_bitmap_height = bmp.bmHeight;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
1413
    if (lpitem->fType & MF_SYSMENU)
Alexandre Julliard's avatar
Alexandre Julliard committed
1414
    {
1415 1416
	if( !IsIconic(hwnd) )
	    NC_DrawSysButton( hwnd, hdc, lpitem->fState & (MF_HILITE | MF_MOUSESELECT) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1417 1418 1419
	return;
    }

1420 1421 1422 1423 1424 1425
    TRACE( "rect=%s\n", wine_dbgstr_rect( &lpitem->rect ) );
    rect = lpitem->rect;
    MENU_AdjustMenuItemRect( menu, &rect );
    if (!IntersectRect( &bmprc, &rect, &menu->items_rect )) /* bmprc is used as a dummy */
        return;

1426 1427 1428
    SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
    bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
  
1429 1430 1431 1432
      /* Setup colors */

    if (lpitem->fState & MF_HILITE)
    {
1433
        if(menuBar && !flat_menu) {
1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449
	    SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
            SetBkColor(hdc, GetSysColor(COLOR_MENU));
	} else {
	    if(lpitem->fState & MF_GRAYED)
		SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
	    else
		SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
            SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
	}
    }
    else
    {
	if (lpitem->fState & MF_GRAYED)
	    SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
	else
	    SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
1450
	SetBkColor( hdc, GetSysColor( bkgnd ) );
1451 1452
    }

1453 1454 1455 1456 1457 1458 1459 1460 1461 1462
    old_clip = CreateRectRgn( 0, 0, 0, 0 );
    if (GetClipRgn( hdc, old_clip ) <= 0)
    {
        DeleteObject( old_clip );
        old_clip = NULL;
    }
    clip = CreateRectRgnIndirect( &menu->items_rect );
    ExtSelectClipRgn( hdc, clip, RGN_AND );
    DeleteObject( clip );

Alexandre Julliard's avatar
Alexandre Julliard committed
1463
    if (lpitem->fType & MF_OWNERDRAW)
Alexandre Julliard's avatar
Alexandre Julliard committed
1464
    {
1465 1466 1467 1468 1469 1470 1471 1472 1473
        /*
        ** Experimentation under Windows reveals that an owner-drawn
        ** menu is given the rectangle which includes the space it requested
        ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
        ** and a popup-menu arrow.  This is the value of lpitem->rect.
        ** Windows will leave all drawing to the application except for
        ** the popup-menu arrow.  Windows always draws that itself, after
        ** the menu owner has finished drawing.
        */
1474
        DRAWITEMSTRUCT dis;
1475
        COLORREF old_bk, old_text;
Alexandre Julliard's avatar
Alexandre Julliard committed
1476

Alexandre Julliard's avatar
Alexandre Julliard committed
1477
        dis.CtlType   = ODT_MENU;
1478
	dis.CtlID     = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1479
        dis.itemID    = lpitem->wID;
1480
        dis.itemData  = lpitem->dwItemData;
Alexandre Julliard's avatar
Alexandre Julliard committed
1481
        dis.itemState = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1482
        if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
1483
        if (lpitem->fState & MF_GRAYED)  dis.itemState |= ODS_GRAYED|ODS_DISABLED;
Alexandre Julliard's avatar
Alexandre Julliard committed
1484
        if (lpitem->fState & MF_HILITE)  dis.itemState |= ODS_SELECTED;
Alexandre Julliard's avatar
Alexandre Julliard committed
1485
        dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
1486
        dis.hwndItem   = (HWND)menu->obj.handle;
Alexandre Julliard's avatar
Alexandre Julliard committed
1487
        dis.hDC        = hdc;
1488
        dis.rcItem     = rect;
1489
        TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
1490
	      "hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
1491
	      dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
1492
	      dis.hDC, wine_dbgstr_rect( &dis.rcItem));
1493 1494
        old_bk = GetBkColor( hdc );
        old_text = GetTextColor( hdc );
1495
        SendMessageW( hwndOwner, WM_DRAWITEM, 0, (LPARAM)&dis );
1496
        /* Draw the popup-menu arrow */
1497 1498
        SetBkColor( hdc, old_bk );
        SetTextColor( hdc, old_text );
1499 1500 1501
        if (lpitem->fType & MF_POPUP)
            draw_popup_arrow( hdc, rect, arrow_bitmap_width,
                    arrow_bitmap_height);
1502
        goto done;
Alexandre Julliard's avatar
Alexandre Julliard committed
1503
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1504

1505
    if (menuBar && (lpitem->fType & MF_SEPARATOR)) goto done;
Alexandre Julliard's avatar
Alexandre Julliard committed
1506

1507
    if (lpitem->fState & MF_HILITE)
1508
    {
1509 1510 1511 1512 1513 1514 1515
        if (flat_menu)
        {
            InflateRect (&rect, -1, -1);
            FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
            InflateRect (&rect, 1, 1);
            FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
        }
1516
        else
1517 1518 1519 1520 1521 1522
        {
            if(menuBar)
                DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
            else
                FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
        }
1523
    }
1524 1525
    else
        FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1526

1527
    SetBkMode( hdc, TRANSPARENT );
Alexandre Julliard's avatar
Alexandre Julliard committed
1528

1529 1530
    /* vertical separator */
    if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
Alexandre Julliard's avatar
Alexandre Julliard committed
1531
    {
1532 1533 1534
        HPEN oldPen;
        RECT rc = rect;

1535
        rc.left -= MENU_COL_SPACE / 2 + 1;
1536
        rc.top = 3;
1537
        rc.bottom = menu->Height - 3;
1538
        if (flat_menu)
1539
        {
1540 1541 1542 1543
            oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
            MoveToEx( hdc, rc.left, rc.top, NULL );
            LineTo( hdc, rc.left, rc.bottom );
            SelectObject( hdc, oldPen );
1544
        }
1545 1546 1547
        else
            DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
    }
1548

1549 1550 1551 1552 1553 1554
    /* horizontal separator */
    if (lpitem->fType & MF_SEPARATOR)
    {
        HPEN oldPen;
        RECT rc = rect;

1555
        InflateRect( &rc, -1, 0 );
Rein Klazes's avatar
Rein Klazes committed
1556
        rc.top = ( rc.top + rc.bottom) / 2;
1557
        if (flat_menu)
1558
        {
1559 1560 1561 1562
            oldPen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_BTNSHADOW) );
            MoveToEx( hdc, rc.left, rc.top, NULL );
            LineTo( hdc, rc.right, rc.top );
            SelectObject( hdc, oldPen );
1563
        }
1564 1565
        else
            DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
1566
        goto done;
Alexandre Julliard's avatar
Alexandre Julliard committed
1567 1568
    }

1569 1570
	/* helper lines for debugging */
/*	FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
1571
	SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
1572
	MoveToEx( hdc, rect.left, (rect.top + rect.bottom)/2, NULL );
1573 1574 1575
	LineTo( hdc, rect.right, (rect.top + rect.bottom)/2 );
*/

Rein Klazes's avatar
Rein Klazes committed
1576 1577 1578 1579 1580 1581 1582 1583 1584
    if (lpitem->hbmpItem) {
        /* calculate the bitmap rectangle in coordinates relative
         * to the item rectangle */
        if( menuBar) {
            if( lpitem->hbmpItem == HBMMENU_CALLBACK)
                bmprc.left = 3;
            else 
                bmprc.left = lpitem->text ? menucharsize.cx : 0;          
        }
1585 1586 1587 1588 1589 1590
        else if (menu->dwStyle & MNS_NOCHECK)
            bmprc.left = 4;
        else if (menu->dwStyle & MNS_CHECKORBMP)
            bmprc.left = 2;
        else
            bmprc.left = 4 + GetSystemMetrics(SM_CXMENUCHECK);
Rein Klazes's avatar
Rein Klazes committed
1591 1592 1593 1594
        bmprc.right =  bmprc.left + lpitem->bmpsize.cx;
        if( menuBar && !(lpitem->hbmpItem == HBMMENU_CALLBACK))
            bmprc.top = 0;
        else
1595
            bmprc.top = (rect.bottom - rect.top -
Rein Klazes's avatar
Rein Klazes committed
1596 1597 1598 1599
                    lpitem->bmpsize.cy) / 2; 
        bmprc.bottom =  bmprc.top + lpitem->bmpsize.cy;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
1600 1601
    if (!menuBar)
    {
1602 1603
        HBITMAP bm;
        INT y = rect.top + rect.bottom;
1604
        BOOL checked = FALSE;
1605 1606
        UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
        UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
1607 1608 1609 1610 1611
        /* Draw the check mark
         *
         * FIXME:
         * Custom checkmark bitmaps are monochrome but not always 1bpp.
         */
1612 1613
        if (!(menu->dwStyle & MNS_NOCHECK))
        {
Rein Klazes's avatar
Rein Klazes committed
1614 1615 1616 1617 1618 1619 1620
            bm = (lpitem->fState & MF_CHECKED) ? lpitem->hCheckBit :
                lpitem->hUnCheckBit;
            if (bm)  /* we have a custom bitmap */
            {
                HDC hdcMem = CreateCompatibleDC( hdc );

                SelectObject( hdcMem, bm );
1621
                BitBlt( hdc, rect.left, (y - check_bitmap_height) / 2,
Rein Klazes's avatar
Rein Klazes committed
1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638
                        check_bitmap_width, check_bitmap_height,
                        hdcMem, 0, 0, SRCCOPY );
                DeleteDC( hdcMem );
                checked = TRUE;
            }
            else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
            {
                RECT r;
                HBITMAP bm = CreateBitmap( check_bitmap_width,
                        check_bitmap_height, 1, 1, NULL );
                HDC hdcMem = CreateCompatibleDC( hdc );

                SelectObject( hdcMem, bm );
                SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
                DrawFrameControl( hdcMem, &r, DFC_MENU,
                        (lpitem->fType & MFT_RADIOCHECK) ?
                        DFCS_MENUBULLET : DFCS_MENUCHECK );
1639
                BitBlt( hdc, rect.left, (y - r.bottom) / 2, r.right, r.bottom,
Rein Klazes's avatar
Rein Klazes committed
1640 1641 1642 1643 1644
                        hdcMem, 0, 0, SRCCOPY );
                DeleteDC( hdcMem );
                DeleteObject( bm );
                checked = TRUE;
            }
1645
        }
1646 1647
        if (lpitem->hbmpItem && !(checked && (menu->dwStyle & MNS_CHECKORBMP)))
        {
Rein Klazes's avatar
Rein Klazes committed
1648 1649
            POINT origorg;
            /* some applications make this assumption on the DC's origin */
1650
            SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1651
            MENU_DrawBitmapItem( hdc, lpitem, &bmprc, menu, hwndOwner, odaction );
Rein Klazes's avatar
Rein Klazes committed
1652
            SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1653
        }
1654 1655 1656 1657
	/* Draw the popup-menu arrow */
        if (lpitem->fType & MF_POPUP)
            draw_popup_arrow( hdc, rect, arrow_bitmap_width,
                    arrow_bitmap_height);
Rein Klazes's avatar
Rein Klazes committed
1658 1659 1660
	rect.left += 4;
        if( !(menu->dwStyle & MNS_NOCHECK))
            rect.left += check_bitmap_width;
Alexandre Julliard's avatar
Alexandre Julliard committed
1661 1662
	rect.right -= arrow_bitmap_width;
    }
1663
    else if (lpitem->hbmpItem)
1664
    {   /* Draw the bitmap */
Rein Klazes's avatar
Rein Klazes committed
1665
        POINT origorg;
1666

1667
        SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
1668
        MENU_DrawBitmapItem( hdc, lpitem, &bmprc, menu, hwndOwner, odaction );
Rein Klazes's avatar
Rein Klazes committed
1669
        SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
1670 1671 1672
    }
    /* process text if present */
    if (lpitem->text)
Alexandre Julliard's avatar
Alexandre Julliard committed
1673
    {
1674
	int i;
1675
	HFONT hfontOld = 0;
1676

1677 1678 1679
	UINT uFormat = (menuBar) ?
			DT_CENTER | DT_VCENTER | DT_SINGLELINE :
			DT_LEFT | DT_VCENTER | DT_SINGLELINE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1680

Rein Klazes's avatar
Rein Klazes committed
1681
        if( !(menu->dwStyle & MNS_CHECKORBMP))
1682
            rect.left += menu->textOffset;
Rein Klazes's avatar
Rein Klazes committed
1683

1684 1685
	if ( lpitem->fState & MFS_DEFAULT )
	{
1686
	     hfontOld = SelectObject( hdc, get_menu_font(TRUE) );
1687 1688
	}

Rein Klazes's avatar
Rein Klazes committed
1689 1690 1691 1692 1693 1694
	if (menuBar) {
            if( lpitem->hbmpItem)
                rect.left += lpitem->bmpsize.cx;
            if( !(lpitem->hbmpItem == HBMMENU_CALLBACK))
                rect.left += menucharsize.cx;
            rect.right -= menucharsize.cx;
Alexandre Julliard's avatar
Alexandre Julliard committed
1695 1696
	}

1697 1698 1699 1700
	for (i = 0; lpitem->text[i]; i++)
	    if ((lpitem->text[i] == '\t') || (lpitem->text[i] == '\b'))
	        break;

1701
	if(lpitem->fState & MF_GRAYED)
1702
	{
1703
	    if (!(lpitem->fState & MF_HILITE) )
1704
	    {
1705
		++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
1706
		SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1707
		DrawTextW( hdc, lpitem->text, i, &rect, uFormat );
1708
		--rect.left; --rect.top; --rect.right; --rect.bottom;
Alexandre Julliard's avatar
Alexandre Julliard committed
1709
	    }
1710
	    SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
Alexandre Julliard's avatar
Alexandre Julliard committed
1711
	}
1712

1713
	DrawTextW( hdc, lpitem->text, i, &rect, uFormat);
Alexandre Julliard's avatar
Alexandre Julliard committed
1714

1715
	/* paint the shortcut text */
1716
	if (!menuBar && lpitem->text[i])  /* There's a tab or flush-right char */
Alexandre Julliard's avatar
Alexandre Julliard committed
1717
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
1718
	    if (lpitem->text[i] == '\t')
Alexandre Julliard's avatar
Alexandre Julliard committed
1719 1720
	    {
		rect.left = lpitem->xTab;
1721
		uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1722
	    }
1723
	    else
1724
	    {
1725
		rect.right = lpitem->xTab;
1726 1727 1728
		uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
	    }

1729
	    if(lpitem->fState & MF_GRAYED)
1730 1731 1732 1733 1734
	    {
		if (!(lpitem->fState & MF_HILITE) )
		{
		    ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
		    SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
1735
		    DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
1736 1737 1738 1739
		    --rect.left; --rect.top; --rect.right; --rect.bottom;
		}
		SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
	    }
1740
	    DrawTextW( hdc, lpitem->text + i + 1, -1, &rect, uFormat );
Alexandre Julliard's avatar
Alexandre Julliard committed
1741
	}
1742

1743
	if (hfontOld)
1744
	    SelectObject (hdc, hfontOld);
Alexandre Julliard's avatar
Alexandre Julliard committed
1745
    }
1746 1747 1748 1749

done:
    ExtSelectClipRgn( hdc, old_clip, RGN_COPY );
    if (old_clip) DeleteObject( old_clip );
Alexandre Julliard's avatar
Alexandre Julliard committed
1750 1751 1752
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1753
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
1754 1755 1756
 *           MENU_DrawPopupMenu
 *
 * Paint a popup menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
1757
 */
1758
static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
1759
{
1760
    HBRUSH hPrevBrush, brush = GetSysColorBrush( COLOR_MENU );
1761
    RECT rect;
1762
    POPUPMENU *menu = MENU_GetMenu( hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
1763

1764
    TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
1765

1766
    GetClientRect( hwnd, &rect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1767

1768 1769 1770
    if (menu && menu->hbrBack) brush = menu->hbrBack;
    if ((hPrevBrush = SelectObject( hdc, brush ))
        && SelectObject( hdc, get_menu_font(FALSE) ))
Alexandre Julliard's avatar
Alexandre Julliard committed
1771
    {
1772
	HPEN hPrevPen;
1773

1774
	Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
Alexandre Julliard's avatar
Alexandre Julliard committed
1775

1776
	hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
Alexandre Julliard's avatar
Alexandre Julliard committed
1777 1778
	if( hPrevPen )
	{
1779
	    BOOL flat_menu = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1780

1781 1782 1783 1784 1785
	    SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
	    if (flat_menu)
		FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
	    else
		DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
Alexandre Julliard's avatar
Alexandre Julliard committed
1786

1787
            if (menu)
1788
            {
1789
                TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
1790
                /* draw menu items */
1791
                if (menu->nItems)
1792 1793 1794 1795 1796
                {
                    MENUITEM *item;
                    UINT u;

                    item = menu->items;
1797
                    for (u = menu->nItems; u > 0; u--, item++)
1798
                        MENU_DrawMenuItem( hwnd, menu, menu->hwndOwner, hdc,
1799
                                           item, FALSE, ODA_DRAWENTIRE );
1800 1801 1802 1803 1804
                }
                /* draw scroll arrows */
                if (menu->bScrolling)
                    MENU_DrawScrollArrows(menu, hdc);
            }
1805
 	} else
1806 1807 1808
	{
	    SelectObject( hdc, hPrevBrush );
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1809
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1810
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1811

Alexandre Julliard's avatar
Alexandre Julliard committed
1812 1813 1814 1815
/***********************************************************************
 *           MENU_DrawMenuBar
 *
 * Paint a menu bar. Returns the height of the menu bar.
1816
 * called from [windows/nonclient.c]
Alexandre Julliard's avatar
Alexandre Julliard committed
1817
 */
1818
UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd )
Alexandre Julliard's avatar
Alexandre Julliard committed
1819 1820
{
    LPPOPUPMENU lppop;
1821
    HMENU hMenu = GetMenu(hwnd);
1822

1823
    lppop = MENU_GetMenu( hMenu );
1824 1825
    if (lppop == NULL || lprect == NULL)
    {
1826
        return GetSystemMetrics(SM_CYMENU);
1827
    }
1828

1829
    return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
1830
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1831

1832

1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867
/***********************************************************************
 *           MENU_InitPopup
 *
 * Popup menu initialization before WM_ENTERMENULOOP.
 */
static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags )
{
    POPUPMENU *menu;
    DWORD ex_style = 0;

    TRACE("owner=%p hmenu=%p\n", hwndOwner, hmenu);

    if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;

    /* store the owner for DrawItem */
    if (!IsWindow( hwndOwner ))
    {
        SetLastError( ERROR_INVALID_WINDOW_HANDLE );
        return FALSE;
    }
    menu->hwndOwner = hwndOwner;

    if (flags & TPM_LAYOUTRTL)
        ex_style = WS_EX_LAYOUTRTL;

    /* NOTE: In Windows, top menu popup is not owned. */
    menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL,
                                WS_POPUP, 0, 0, 0, 0,
                                hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
                                (LPVOID)hmenu );
    if( !menu->hWnd ) return FALSE;
    return TRUE;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1868 1869 1870 1871 1872
/***********************************************************************
 *           MENU_ShowPopup
 *
 * Display a popup menu.
 */
1873
static BOOL MENU_ShowPopup( HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
1874
                              INT x, INT y, INT xanchor, INT yanchor )
Alexandre Julliard's avatar
Alexandre Julliard committed
1875
{
1876
    POPUPMENU *menu;
1877 1878 1879
    POINT pt;
    HMONITOR monitor;
    MONITORINFO info;
1880
    UINT max_height;
Alexandre Julliard's avatar
Alexandre Julliard committed
1881

1882 1883
    TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
          hwndOwner, hmenu, id, x, y, xanchor, yanchor);
1884

1885
    if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1886 1887
    if (menu->FocusedItem != NO_SELECTED_ITEM)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1888
	menu->items[menu->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
Alexandre Julliard's avatar
Alexandre Julliard committed
1889 1890
	menu->FocusedItem = NO_SELECTED_ITEM;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1891

1892
    menu->nScrollPos = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1893

1894 1895 1896 1897 1898 1899
    /* FIXME: should use item rect */
    pt.x = x;
    pt.y = y;
    monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
    info.cbSize = sizeof(info);
    GetMonitorInfoW( monitor, &info );
1900

1901 1902 1903 1904 1905 1906 1907 1908
    max_height = info.rcWork.bottom - info.rcWork.top;
    if (menu->cyMax)
        max_height = min( max_height, menu->cyMax );

    MENU_PopupMenuCalcSize( menu, max_height );

    /* adjust popup menu pos so that it fits within the desktop */

1909 1910 1911
    if (flags & TPM_LAYOUTRTL)
        flags ^= TPM_RIGHTALIGN;

1912 1913
    if( flags & TPM_RIGHTALIGN ) x -= menu->Width;
    if( flags & TPM_CENTERALIGN ) x -= menu->Width / 2;
1914

1915 1916
    if( flags & TPM_BOTTOMALIGN ) y -= menu->Height;
    if( flags & TPM_VCENTERALIGN ) y -= menu->Height / 2;
1917

1918
    if( x + menu->Width > info.rcWork.right)
1919
    {
1920 1921
        if( xanchor && x >= menu->Width - xanchor )
            x -= menu->Width - xanchor;
1922

1923 1924
        if( x + menu->Width > info.rcWork.right)
            x = info.rcWork.right - menu->Width;
1925
    }
1926
    if( x < info.rcWork.left ) x = info.rcWork.left;
Alexandre Julliard's avatar
Alexandre Julliard committed
1927

1928
    if( y + menu->Height > info.rcWork.bottom)
1929
    {
1930 1931
        if( yanchor && y >= menu->Height + yanchor )
            y -= menu->Height + yanchor;
1932

1933 1934
        if( y + menu->Height > info.rcWork.bottom)
            y = info.rcWork.bottom - menu->Height;
1935
    }
1936
    if( y < info.rcWork.top ) y = info.rcWork.top;
Alexandre Julliard's avatar
Alexandre Julliard committed
1937

1938 1939 1940 1941
    if (!top_popup) {
        top_popup = menu->hWnd;
        top_popup_hmenu = hmenu;
    }
1942 1943
    /* Display the window */

1944
    SetWindowPos( menu->hWnd, HWND_TOPMOST, x, y, menu->Width, menu->Height,
1945
                  SWP_SHOWWINDOW | SWP_NOACTIVATE );
1946 1947
    UpdateWindow( menu->hWnd );
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1948 1949 1950
}


1951 1952 1953 1954 1955 1956 1957 1958 1959 1960
/***********************************************************************
 *           MENU_EnsureMenuItemVisible
 */
static void
MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
{
    if (lppop->bScrolling)
    {
        MENUITEM *item = &lppop->items[wIndex];
        UINT nOldPos = lppop->nScrollPos;
1961 1962
        const RECT *rc = &lppop->items_rect;
        UINT scroll_height = rc->bottom - rc->top;
1963

1964
        if (item->rect.bottom > lppop->nScrollPos + scroll_height)
1965
        {
1966
            lppop->nScrollPos = item->rect.bottom - scroll_height;
1967
            ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, rc, rc);
1968
        }
1969
        else if (item->rect.top < lppop->nScrollPos)
1970
        {
1971
            lppop->nScrollPos = item->rect.top;
1972
            ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, rc, rc);
1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991
        }

        /* Invalidate the scroll arrows if necessary */
        if (nOldPos != lppop->nScrollPos)
        {
            RECT arrow_rect = lppop->items_rect;
            if (nOldPos == 0 || lppop->nScrollPos == 0)
            {
                arrow_rect.top = 0;
                arrow_rect.bottom = lppop->items_rect.top;
                InvalidateRect(lppop->hWnd, &arrow_rect, FALSE);
            }
            if (nOldPos + scroll_height == lppop->nTotalHeight ||
                lppop->nScrollPos + scroll_height == lppop->nTotalHeight)
            {
                arrow_rect.top = lppop->items_rect.bottom;
                arrow_rect.bottom = lppop->Height;
                InvalidateRect(lppop->hWnd, &arrow_rect, FALSE);
            }
1992 1993 1994 1995 1996
        }
    }
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1997 1998 1999
/***********************************************************************
 *           MENU_SelectItem
 */
2000
static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
2001
                             BOOL sendMenuSelect, HMENU topmenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2002
{
Alexandre Julliard's avatar
Alexandre Julliard committed
2003
    LPPOPUPMENU lppop;
2004
    HDC hdc;
Alexandre Julliard's avatar
Alexandre Julliard committed
2005

2006
    TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
2007

2008
    lppop = MENU_GetMenu( hmenu );
2009
    if ((!lppop) || (!lppop->nItems) || (!lppop->hWnd)) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
2010

Alexandre Julliard's avatar
Alexandre Julliard committed
2011
    if (lppop->FocusedItem == wIndex) return;
2012 2013
    if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
    else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2014 2015 2016 2017
    if (!top_popup) {
        top_popup = lppop->hWnd;
        top_popup_hmenu = hmenu;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2018

2019
    SelectObject( hdc, get_menu_font(FALSE));
2020

Alexandre Julliard's avatar
Alexandre Julliard committed
2021
      /* Clear previous highlighted item */
2022
    if (lppop->FocusedItem != NO_SELECTED_ITEM)
Alexandre Julliard's avatar
Alexandre Julliard committed
2023
    {
2024
        lppop->items[lppop->FocusedItem].fState &= ~(MF_HILITE|MF_MOUSESELECT);
2025
        MENU_DrawMenuItem( lppop->hWnd, lppop, hwndOwner, hdc, &lppop->items[lppop->FocusedItem],
2026
                           !(lppop->wFlags & MF_POPUP), ODA_SELECT );
Alexandre Julliard's avatar
Alexandre Julliard committed
2027 2028 2029 2030
    }

      /* Highlight new item (if any) */
    lppop->FocusedItem = wIndex;
2031
    if (lppop->FocusedItem != NO_SELECTED_ITEM)
Alexandre Julliard's avatar
Alexandre Julliard committed
2032
    {
2033 2034
        if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
            lppop->items[wIndex].fState |= MF_HILITE;
2035
            MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
2036
            MENU_DrawMenuItem( lppop->hWnd, lppop, hwndOwner, hdc, &lppop->items[wIndex],
2037
                               !(lppop->wFlags & MF_POPUP), ODA_SELECT );
2038
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
2039
        if (sendMenuSelect)
Alexandre Julliard's avatar
Alexandre Julliard committed
2040 2041
        {
            MENUITEM *ip = &lppop->items[lppop->FocusedItem];
2042
	    SendMessageW( hwndOwner, WM_MENUSELECT,
2043
                     MAKEWPARAM(ip->fType & MF_POPUP ? wIndex: ip->wID,
2044
                     ip->fType | ip->fState |
2045
                     (lppop->wFlags & MF_SYSMENU)), (LPARAM)hmenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
2046
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
2047
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2048
    else if (sendMenuSelect) {
2049 2050 2051
        if(topmenu){
            int pos;
            if((pos=MENU_FindSubMenu(&topmenu, hmenu))!=NO_SELECTED_ITEM){
2052
                POPUPMENU *ptm = MENU_GetMenu( topmenu );
2053
                MENUITEM *ip = &ptm->items[pos];
2054
                SendMessageW( hwndOwner, WM_MENUSELECT, MAKEWPARAM(pos,
2055
                         ip->fType | ip->fState |
2056
                         (ptm->wFlags & MF_SYSMENU)), (LPARAM)topmenu);
2057 2058
            }
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
2059
    }
2060
    ReleaseDC( lppop->hWnd, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
2061 2062 2063 2064
}


/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2065
 *           MENU_MoveSelection
Alexandre Julliard's avatar
Alexandre Julliard committed
2066
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2067 2068 2069
 * Moves currently selected item according to the offset parameter.
 * If there is no selection then it should select the last item if
 * offset is ITEM_PREV or the first item if offset is ITEM_NEXT.
Alexandre Julliard's avatar
Alexandre Julliard committed
2070
 */
2071
static void MENU_MoveSelection( HWND hwndOwner, HMENU hmenu, INT offset )
Alexandre Julliard's avatar
Alexandre Julliard committed
2072
{
2073
    INT i;
Alexandre Julliard's avatar
Alexandre Julliard committed
2074 2075
    POPUPMENU *menu;

2076
    TRACE("hwnd=%p hmenu=%p off=0x%04x\n", hwndOwner, hmenu, offset);
2077

2078 2079
    menu = MENU_GetMenu( hmenu );
    if ((!menu) || (!menu->items)) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
2080 2081

    if ( menu->FocusedItem != NO_SELECTED_ITEM )
Alexandre Julliard's avatar
Alexandre Julliard committed
2082
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2083
	if( menu->nItems == 1 ) return; else
2084
	for (i = menu->FocusedItem + offset ; i >= 0 && i < menu->nItems
Alexandre Julliard's avatar
Alexandre Julliard committed
2085
					    ; i += offset)
Alexandre Julliard's avatar
Alexandre Julliard committed
2086
	    if (!(menu->items[i].fType & MF_SEPARATOR))
Alexandre Julliard's avatar
Alexandre Julliard committed
2087
	    {
2088
		MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2089
		return;
Alexandre Julliard's avatar
Alexandre Julliard committed
2090
	    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2091
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2092

2093
    for ( i = (offset > 0) ? 0 : menu->nItems - 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2094
		  i >= 0 && i < menu->nItems ; i += offset)
Alexandre Julliard's avatar
Alexandre Julliard committed
2095
	if (!(menu->items[i].fType & MF_SEPARATOR))
Alexandre Julliard's avatar
Alexandre Julliard committed
2096
	{
2097
	    MENU_SelectItem( hwndOwner, hmenu, i, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2098 2099
	    return;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
2100 2101 2102
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2103 2104 2105
/**********************************************************************
 *         MENU_InsertItem
 *
2106
 * Insert (allocate) a new item into a menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
2107
 */
2108
static MENUITEM *MENU_InsertItem( HMENU hMenu, UINT pos, UINT flags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2109 2110 2111 2112
{
    MENUITEM *newItems;
    POPUPMENU *menu;

2113
    if (!(menu = MENU_GetMenu(hMenu)))
Alexandre Julliard's avatar
Alexandre Julliard committed
2114 2115 2116 2117
        return NULL;

    /* Find where to insert new item */

2118
    if (flags & MF_BYPOSITION) {
2119
        if (pos > menu->nItems)
2120
            pos = menu->nItems;
2121
    } else {
2122
        if (!MENU_FindItem( &hMenu, &pos, flags ))
2123 2124 2125 2126
            pos = menu->nItems;
        else {
            if (!(menu = MENU_GetMenu( hMenu )))
                return NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
2127 2128 2129
        }
    }

2130 2131 2132 2133
    /* Make sure that MDI system buttons stay on the right side.
     * Note: XP treats only bitmap handles 1 - 6 as "magic" ones
     * regardless of their id.
     */
2134
    while (pos > 0 && (INT_PTR)menu->items[pos - 1].hbmpItem >= (INT_PTR)HBMMENU_SYSTEM &&
2135 2136 2137
           (INT_PTR)menu->items[pos - 1].hbmpItem <= (INT_PTR)HBMMENU_MBAR_CLOSE_D)
        pos--;

2138
    TRACE("inserting at %u flags %x\n", pos, flags);
2139

Alexandre Julliard's avatar
Alexandre Julliard committed
2140 2141
    /* Create new items array */

2142
    newItems = HeapAlloc( GetProcessHeap(), 0, sizeof(MENUITEM) * (menu->nItems+1) );
Alexandre Julliard's avatar
Alexandre Julliard committed
2143
    if (!newItems)
Alexandre Julliard's avatar
Alexandre Julliard committed
2144
    {
2145
        WARN("allocation failed\n" );
Alexandre Julliard's avatar
Alexandre Julliard committed
2146 2147 2148 2149
        return NULL;
    }
    if (menu->nItems > 0)
    {
2150
	  /* Copy the old array into the new one */
Alexandre Julliard's avatar
Alexandre Julliard committed
2151 2152
	if (pos > 0) memcpy( newItems, menu->items, pos * sizeof(MENUITEM) );
	if (pos < menu->nItems) memcpy( &newItems[pos+1], &menu->items[pos],
Alexandre Julliard's avatar
Alexandre Julliard committed
2153
					(menu->nItems-pos)*sizeof(MENUITEM) );
2154
        HeapFree( GetProcessHeap(), 0, menu->items );
Alexandre Julliard's avatar
Alexandre Julliard committed
2155
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2156
    menu->items = newItems;
Alexandre Julliard's avatar
Alexandre Julliard committed
2157
    menu->nItems++;
Alexandre Julliard's avatar
Alexandre Julliard committed
2158
    memset( &newItems[pos], 0, sizeof(*newItems) );
2159
    menu->Height = 0; /* force size recalculate */
Alexandre Julliard's avatar
Alexandre Julliard committed
2160 2161 2162 2163 2164 2165 2166
    return &newItems[pos];
}


/**********************************************************************
 *         MENU_ParseResource
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2167
 * Parse a standard menu resource and add items to the menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
2168
 * Return a pointer to the end of the resource.
2169 2170
 *
 * NOTE: flags is equivalent to the mtOption field
Alexandre Julliard's avatar
Alexandre Julliard committed
2171
 */
2172
static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2173 2174
{
    WORD flags, id = 0;
2175
    LPCWSTR str;
2176
    BOOL end_flag;
Alexandre Julliard's avatar
Alexandre Julliard committed
2177 2178 2179

    do
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2180
        flags = GET_WORD(res);
2181 2182 2183
        end_flag = flags & MF_END;
        /* Remove MF_END because it has the same value as MF_HILITE */
        flags &= ~MF_END;
Alexandre Julliard's avatar
Alexandre Julliard committed
2184 2185 2186
        res += sizeof(WORD);
        if (!(flags & MF_POPUP))
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
2187
            id = GET_WORD(res);
Alexandre Julliard's avatar
Alexandre Julliard committed
2188 2189
            res += sizeof(WORD);
        }
2190 2191
        str = (LPCWSTR)res;
        res += (strlenW(str) + 1) * sizeof(WCHAR);
Alexandre Julliard's avatar
Alexandre Julliard committed
2192 2193
        if (flags & MF_POPUP)
        {
2194
            HMENU hSubMenu = CreatePopupMenu();
Alexandre Julliard's avatar
Alexandre Julliard committed
2195
            if (!hSubMenu) return NULL;
2196 2197
            if (!(res = MENU_ParseResource( res, hSubMenu ))) return NULL;
            AppendMenuW( hMenu, flags, (UINT_PTR)hSubMenu, str );
Alexandre Julliard's avatar
Alexandre Julliard committed
2198
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
2199
        else  /* Not a popup */
Alexandre Julliard's avatar
Alexandre Julliard committed
2200
        {
2201
            AppendMenuW( hMenu, flags, id, *str ? str : NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
2202
        }
2203
    } while (!end_flag);
Alexandre Julliard's avatar
Alexandre Julliard committed
2204 2205 2206 2207
    return res;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2208 2209 2210 2211 2212 2213
/**********************************************************************
 *         MENUEX_ParseResource
 *
 * Parse an extended menu resource and add items to the menu.
 * Return a pointer to the end of the resource.
 */
2214
static LPCSTR MENUEX_ParseResource( LPCSTR res, HMENU hMenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
2215 2216
{
    WORD resinfo;
Alexandre Julliard's avatar
Alexandre Julliard committed
2217
    do {
2218
	MENUITEMINFOW mii;
Alexandre Julliard's avatar
Alexandre Julliard committed
2219

Alexandre Julliard's avatar
Alexandre Julliard committed
2220 2221 2222
	mii.cbSize = sizeof(mii);
	mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
	mii.fType = GET_DWORD(res);
Alexandre Julliard's avatar
Alexandre Julliard committed
2223
        res += sizeof(DWORD);
Alexandre Julliard's avatar
Alexandre Julliard committed
2224
	mii.fState = GET_DWORD(res);
Alexandre Julliard's avatar
Alexandre Julliard committed
2225
        res += sizeof(DWORD);
Alexandre Julliard's avatar
Alexandre Julliard committed
2226
	mii.wID = GET_DWORD(res);
Alexandre Julliard's avatar
Alexandre Julliard committed
2227 2228 2229 2230
        res += sizeof(DWORD);
	resinfo = GET_WORD(res); /* FIXME: for 16-bit apps this is a byte.  */
        res += sizeof(WORD);
	/* Align the text on a word boundary.  */
2231
	res += (~((UINT_PTR)res - 1)) & 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2232
	mii.dwTypeData = (LPWSTR) res;
2233
	res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
Alexandre Julliard's avatar
Alexandre Julliard committed
2234
	/* Align the following fields on a dword boundary.  */
2235
	res += (~((UINT_PTR)res - 1)) & 3;
Alexandre Julliard's avatar
Alexandre Julliard committed
2236

2237 2238
        TRACE("Menu item: [%08x,%08x,%04x,%04x,%s]\n",
              mii.fType, mii.fState, mii.wID, resinfo, debugstr_w(mii.dwTypeData));
Alexandre Julliard's avatar
Alexandre Julliard committed
2239

Alexandre Julliard's avatar
Alexandre Julliard committed
2240
	if (resinfo & 1) {	/* Pop-up? */
2241
	    /* DWORD helpid = GET_DWORD(res); FIXME: use this.  */
Alexandre Julliard's avatar
Alexandre Julliard committed
2242
	    res += sizeof(DWORD);
2243
	    mii.hSubMenu = CreatePopupMenu();
Alexandre Julliard's avatar
Alexandre Julliard committed
2244 2245 2246
	    if (!mii.hSubMenu)
		return NULL;
	    if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) {
2247
		DestroyMenu(mii.hSubMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
2248
                return NULL;
2249
	    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2250 2251
	    mii.fMask |= MIIM_SUBMENU;
	    mii.fType |= MF_POPUP;
Alexandre Julliard's avatar
Alexandre Julliard committed
2252
        }
2253 2254 2255 2256 2257 2258
	else if(!*mii.dwTypeData && !(mii.fType & MF_SEPARATOR))
	{
	    WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
		mii.wID, mii.fType);
	    mii.fType |= MF_SEPARATOR;
	}
2259
	InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
Alexandre Julliard's avatar
Alexandre Julliard committed
2260 2261 2262 2263 2264
    } while (!(resinfo & MF_END));
    return res;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2265 2266 2267 2268 2269
/***********************************************************************
 *           MENU_GetSubPopup
 *
 * Return the handle of the selected sub-popup menu (if any).
 */
2270
static HMENU MENU_GetSubPopup( HMENU hmenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2271
{
Alexandre Julliard's avatar
Alexandre Julliard committed
2272 2273 2274
    POPUPMENU *menu;
    MENUITEM *item;

2275
    menu = MENU_GetMenu( hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2276

2277
    if ((!menu) || (menu->FocusedItem == NO_SELECTED_ITEM)) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2278

Alexandre Julliard's avatar
Alexandre Julliard committed
2279
    item = &menu->items[menu->FocusedItem];
Alexandre Julliard's avatar
Alexandre Julliard committed
2280 2281
    if ((item->fType & MF_POPUP) && (item->fState & MF_MOUSESELECT))
        return item->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
2282
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2283 2284 2285
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2286 2287 2288 2289 2290
/***********************************************************************
 *           MENU_HideSubPopups
 *
 * Hide the sub-popup menus of this menu.
 */
2291
static void MENU_HideSubPopups( HWND hwndOwner, HMENU hmenu,
2292
                                BOOL sendMenuSelect, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2293
{
2294
    POPUPMENU *menu = MENU_GetMenu( hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2295

2296
    TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, sendMenuSelect);
2297

2298
    if (menu && top_popup)
Alexandre Julliard's avatar
Alexandre Julliard committed
2299
    {
2300
	HMENU hsubmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
2301 2302 2303 2304 2305 2306
	POPUPMENU *submenu;
	MENUITEM *item;

	if (menu->FocusedItem != NO_SELECTED_ITEM)
	{
	    item = &menu->items[menu->FocusedItem];
Alexandre Julliard's avatar
Alexandre Julliard committed
2307 2308 2309 2310
	    if (!(item->fType & MF_POPUP) ||
		!(item->fState & MF_MOUSESELECT)) return;
	    item->fState &= ~MF_MOUSESELECT;
	    hsubmenu = item->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
2311 2312
	} else return;

2313
	if (!(submenu = MENU_GetMenu( hsubmenu ))) return;
2314
	MENU_HideSubPopups( hwndOwner, hsubmenu, FALSE, wFlags );
2315
	MENU_SelectItem( hwndOwner, hsubmenu, NO_SELECTED_ITEM, sendMenuSelect, 0 );
2316 2317
        DestroyWindow( submenu->hWnd );
        submenu->hWnd = 0;
2318 2319 2320 2321

        if (!(wFlags & TPM_NONOTIFY))
           SendMessageW( hwndOwner, WM_UNINITMENUPOPUP, (WPARAM)hsubmenu,
                         MAKELPARAM(0, IS_SYSTEM_MENU(submenu)) );
Alexandre Julliard's avatar
Alexandre Julliard committed
2322
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2323 2324 2325
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2326 2327 2328 2329 2330 2331
/***********************************************************************
 *           MENU_ShowSubPopup
 *
 * Display the sub-menu of the selected item of this menu.
 * Return the handle of the submenu, or hmenu if no submenu to display.
 */
2332
static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu,
2333
                                  BOOL selectFirst, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2334
{
2335
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
2336 2337
    POPUPMENU *menu;
    MENUITEM *item;
2338
    HDC hdc;
Alexandre Julliard's avatar
Alexandre Julliard committed
2339

2340
    TRACE("owner=%p hmenu=%p 0x%04x\n", hwndOwner, hmenu, selectFirst);
2341

2342
    if (!(menu = MENU_GetMenu( hmenu ))) return hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
2343

2344
    if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
2345

Alexandre Julliard's avatar
Alexandre Julliard committed
2346
    item = &menu->items[menu->FocusedItem];
2347
    if (!(item->fType & MF_POPUP) || (item->fState & (MF_GRAYED | MF_DISABLED)))
2348
        return hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
2349

2350 2351
    /* message must be sent before using item,
       because nearly everything may be changed by the application ! */
Alexandre Julliard's avatar
Alexandre Julliard committed
2352

2353 2354
    /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
    if (!(wFlags & TPM_NONOTIFY))
2355
       SendMessageW( hwndOwner, WM_INITMENUPOPUP, (WPARAM)item->hSubMenu,
2356
                     MAKELPARAM( menu->FocusedItem, IS_SYSTEM_MENU(menu) ));
Alexandre Julliard's avatar
Alexandre Julliard committed
2357

Alexandre Julliard's avatar
Alexandre Julliard committed
2358 2359 2360
    item = &menu->items[menu->FocusedItem];
    rect = item->rect;

2361
    /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
2362
    if (!(item->fState & MF_HILITE))
Alexandre Julliard's avatar
Alexandre Julliard committed
2363
    {
2364 2365
        if (menu->wFlags & MF_POPUP) hdc = GetDC( menu->hWnd );
        else hdc = GetDCEx( menu->hWnd, 0, DCX_CACHE | DCX_WINDOW);
2366

2367
        SelectObject( hdc, get_menu_font(FALSE));
2368

Alexandre Julliard's avatar
Alexandre Julliard committed
2369
        item->fState |= MF_HILITE;
2370
        MENU_DrawMenuItem( menu->hWnd, menu, hwndOwner, hdc, item, !(menu->wFlags & MF_POPUP), ODA_DRAWENTIRE );
2371
        ReleaseDC( menu->hWnd, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
2372 2373
    }
    if (!item->rect.top && !item->rect.left && !item->rect.bottom && !item->rect.right)
2374
        item->rect = rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
2375

Alexandre Julliard's avatar
Alexandre Julliard committed
2376
    item->fState |= MF_MOUSESELECT;
Alexandre Julliard's avatar
Alexandre Julliard committed
2377 2378

    if (IS_SYSTEM_MENU(menu))
Alexandre Julliard's avatar
Alexandre Julliard committed
2379
    {
2380
	MENU_InitSysMenuPopup(item->hSubMenu,
2381 2382
                              GetWindowLongW( menu->hWnd, GWL_STYLE ),
                              GetClassLongW( menu->hWnd, GCL_STYLE));
Alexandre Julliard's avatar
Alexandre Julliard committed
2383

2384
	NC_GetSysPopupPos( menu->hWnd, &rect );
2385
	if (wFlags & TPM_LAYOUTRTL) rect.left = rect.right;
Alexandre Julliard's avatar
Alexandre Julliard committed
2386
	rect.top = rect.bottom;
2387 2388
	rect.right = GetSystemMetrics(SM_CXSIZE);
        rect.bottom = GetSystemMetrics(SM_CYSIZE);
Alexandre Julliard's avatar
Alexandre Julliard committed
2389 2390 2391
    }
    else
    {
2392 2393 2394
        RECT item_rect = item->rect;

        MENU_AdjustMenuItemRect(menu, &item_rect);
2395
        GetWindowRect( menu->hWnd, &rect );
2396

Alexandre Julliard's avatar
Alexandre Julliard committed
2397 2398
	if (menu->wFlags & MF_POPUP)
	{
2399 2400
	    /* The first item in the popup menu has to be at the
	       same y position as the focused menu item */
2401 2402 2403
            if (wFlags & TPM_LAYOUTRTL)
                rect.left += GetSystemMetrics(SM_CXBORDER);
	    else
2404 2405 2406 2407
                rect.left += item_rect.right - GetSystemMetrics(SM_CXBORDER);
	    rect.top += item_rect.top - MENU_MARGIN;
	    rect.right = item_rect.left - item_rect.right + GetSystemMetrics(SM_CXBORDER);
	    rect.bottom = item_rect.top - item_rect.bottom - 2 * MENU_MARGIN;
Alexandre Julliard's avatar
Alexandre Julliard committed
2408 2409 2410
	}
	else
	{
2411
            if (wFlags & TPM_LAYOUTRTL)
2412
                rect.left = rect.right - item_rect.left;
2413
	    else
2414 2415 2416 2417
                rect.left += item_rect.left;
	    rect.top += item_rect.bottom;
	    rect.right = item_rect.right - item_rect.left;
	    rect.bottom = item_rect.bottom - item_rect.top;
Alexandre Julliard's avatar
Alexandre Julliard committed
2418
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
2419
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2420

2421 2422 2423
    /* use default alignment for submenus */
    wFlags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);

2424 2425
    MENU_InitPopup( hwndOwner, item->hSubMenu, wFlags );

2426
    MENU_ShowPopup( hwndOwner, item->hSubMenu, menu->FocusedItem, wFlags,
Alexandre Julliard's avatar
Alexandre Julliard committed
2427
		    rect.left, rect.top, rect.right, rect.bottom );
Alexandre Julliard's avatar
Alexandre Julliard committed
2428
    if (selectFirst)
Alexandre Julliard's avatar
Alexandre Julliard committed
2429 2430
        MENU_MoveSelection( hwndOwner, item->hSubMenu, ITEM_NEXT );
    return item->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
2431
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2432

2433 2434 2435 2436 2437


/**********************************************************************
 *         MENU_IsMenuActive
 */
2438
HWND MENU_IsMenuActive(void)
2439
{
2440
    return top_popup;
2441 2442
}

2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453
/**********************************************************************
 *         MENU_EndMenu
 *
 * Calls EndMenu() if the hwnd parameter belongs to the menu owner
 *
 * Does the (menu stuff) of the default window handling of WM_CANCELMODE
 */
void MENU_EndMenu( HWND hwnd )
{
    POPUPMENU *menu;
    menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
2454
    if (menu && (hwnd == menu->hWnd || hwnd == menu->hwndOwner)) EndMenu();
2455 2456
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2457
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2458
 *           MENU_PtMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
2459
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2460 2461
 * Walks menu chain trying to find a menu pt maps to.
 */
2462
static HMENU MENU_PtMenu( HMENU hMenu, POINT pt )
Alexandre Julliard's avatar
Alexandre Julliard committed
2463
{
2464
   POPUPMENU *menu = MENU_GetMenu( hMenu );
2465 2466
   UINT item = menu->FocusedItem;
   HMENU ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
2467 2468

   /* try subpopup first (if any) */
2469 2470 2471 2472
   ret = (item != NO_SELECTED_ITEM &&
          (menu->items[item].fType & MF_POPUP) &&
          (menu->items[item].fState & MF_MOUSESELECT))
        ? MENU_PtMenu(menu->items[item].hSubMenu, pt) : 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2473

2474
   if (!ret)  /* check the current window (avoiding WM_HITTEST) */
Alexandre Julliard's avatar
Alexandre Julliard committed
2475
   {
2476 2477 2478 2479 2480 2481 2482 2483 2484
       INT ht = NC_HandleNCHitTest( menu->hWnd, pt );
       if( menu->wFlags & MF_POPUP )
       {
           if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
       }
       else if (ht == HTSYSMENU)
           ret = get_win_sys_menu( menu->hWnd );
       else if (ht == HTMENU)
           ret = GetMenu( menu->hWnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
2485
   }
2486
   return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
2487
}
2488

Alexandre Julliard's avatar
Alexandre Julliard committed
2489 2490 2491 2492
/***********************************************************************
 *           MENU_ExecFocusedItem
 *
 * Execute a menu item (for instance when user pressed Enter).
2493
 * Return the wID of the executed item. Otherwise, -1 indicating
2494
 * that no menu item was executed, -2 if a popup is shown;
2495 2496 2497
 * Have to receive the flags for the TrackPopupMenu options to avoid
 * sending unwanted message.
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2498
 */
2499
static INT MENU_ExecFocusedItem( MTRACKER* pmt, HMENU hMenu, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2500 2501
{
    MENUITEM *item;
2502
    POPUPMENU *menu = MENU_GetMenu( hMenu );
2503

2504
    TRACE("%p hmenu=%p\n", pmt, hMenu);
2505

2506
    if (!menu || !menu->nItems ||
2507
	(menu->FocusedItem == NO_SELECTED_ITEM)) return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2508

Alexandre Julliard's avatar
Alexandre Julliard committed
2509
    item = &menu->items[menu->FocusedItem];
Alexandre Julliard's avatar
Alexandre Julliard committed
2510

2511
    TRACE("hMenu %p wID %08lx hSubMenu %p fType %04x\n", hMenu, item->wID, item->hSubMenu, item->fType);
Alexandre Julliard's avatar
Alexandre Julliard committed
2512 2513

    if (!(item->fType & MF_POPUP))
Alexandre Julliard's avatar
Alexandre Julliard committed
2514
    {
2515
	if (!(item->fState & (MF_GRAYED | MF_DISABLED)) && !(item->fType & MF_SEPARATOR))
Alexandre Julliard's avatar
Alexandre Julliard committed
2516
	{
2517 2518
	    /* If TPM_RETURNCMD is set you return the id, but
	       do not send a message to the owner */
2519
	    if(!(wFlags & TPM_RETURNCMD))
Alexandre Julliard's avatar
Alexandre Julliard committed
2520
	    {
2521
		if( menu->wFlags & MF_SYSMENU )
2522
		    PostMessageW( pmt->hOwnerWnd, WM_SYSCOMMAND, item->wID,
2523 2524
				  MAKELPARAM((INT16)pmt->pt.x, (INT16)pmt->pt.y) );
		else
2525
		{
2526 2527 2528 2529
                    POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
                    DWORD dwStyle = menu->dwStyle | (topmenu ? topmenu->dwStyle : 0);

                    if (dwStyle & MNS_NOTIFYBYPOS)
2530 2531 2532 2533 2534
                        PostMessageW( pmt->hOwnerWnd, WM_MENUCOMMAND, menu->FocusedItem,
                                      (LPARAM)hMenu);
                    else
                        PostMessageW( pmt->hOwnerWnd, WM_COMMAND, item->wID, 0 );
		}
Alexandre Julliard's avatar
Alexandre Julliard committed
2535
	    }
2536
	    return item->wID;
Alexandre Julliard's avatar
Alexandre Julliard committed
2537
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
2538 2539
    }
    else
2540
    {
2541
	pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hMenu, TRUE, wFlags);
2542 2543
	return -2;
    }
2544

2545
    return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2546 2547
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2548
/***********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
2549
 *           MENU_SwitchTracking
Alexandre Julliard's avatar
Alexandre Julliard committed
2550
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2551
 * Helper function for menu navigation routines.
Alexandre Julliard's avatar
Alexandre Julliard committed
2552
 */
2553
static void MENU_SwitchTracking( MTRACKER* pmt, HMENU hPtMenu, UINT id, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2554
{
2555 2556
    POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
    POPUPMENU *topmenu = MENU_GetMenu( pmt->hTopMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2557

2558
    TRACE("%p hmenu=%p 0x%04x\n", pmt, hPtMenu, id);
2559

Alexandre Julliard's avatar
Alexandre Julliard committed
2560 2561
    if( pmt->hTopMenu != hPtMenu &&
	!((ptmenu->wFlags | topmenu->wFlags) & MF_POPUP) )
Alexandre Julliard's avatar
Alexandre Julliard committed
2562
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2563
	/* both are top level menus (system and menu-bar) */
2564
	MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
2565
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2566 2567
        pmt->hTopMenu = hPtMenu;
    }
2568
    else MENU_HideSubPopups( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
2569
    MENU_SelectItem( pmt->hOwnerWnd, hPtMenu, id, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2570 2571 2572 2573 2574 2575 2576 2577
}


/***********************************************************************
 *           MENU_ButtonDown
 *
 * Return TRUE if we can go on with menu tracking.
 */
2578
static BOOL MENU_ButtonDown( MTRACKER* pmt, UINT message, HMENU hPtMenu, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2579
{
2580
    TRACE("%p hPtMenu=%p\n", pmt, hPtMenu);
2581

Alexandre Julliard's avatar
Alexandre Julliard committed
2582
    if (hPtMenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
2583
    {
2584 2585
        UINT pos;
        POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
2586
        enum hittest ht = ht_item;
Alexandre Julliard's avatar
Alexandre Julliard committed
2587

2588
        if( IS_SYSTEM_MENU(ptmenu) )
2589 2590
        {
            if (message == WM_LBUTTONDBLCLK) return FALSE;
2591
            pos = 0;
2592
        }
2593 2594
        else
            ht = MENU_FindItemByCoords( ptmenu, pmt->pt, &pos );
Alexandre Julliard's avatar
Alexandre Julliard committed
2595

2596 2597 2598 2599
        if (pos != NO_SELECTED_ITEM)
        {
            if (ptmenu->FocusedItem != pos)
                MENU_SwitchTracking( pmt, hPtMenu, pos, wFlags );
Alexandre Julliard's avatar
Alexandre Julliard committed
2600

2601 2602 2603 2604
            /* If the popup menu is not already "popped" */
            if (!(ptmenu->items[pos].fState & MF_MOUSESELECT))
                pmt->hCurrentMenu = MENU_ShowSubPopup( pmt->hOwnerWnd, hPtMenu, FALSE, wFlags );
        }
2605

2606 2607 2608
        /* A click on an item or anywhere on a popup keeps tracking going */
        if (ht == ht_item || ((ptmenu->wFlags & MF_POPUP) && ht != ht_nowhere))
            return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2609
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2610
    return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2611 2612
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2613 2614 2615
/***********************************************************************
 *           MENU_ButtonUp
 *
2616 2617
 * Return the value of MENU_ExecFocusedItem if
 * the selected item was not a popup. Else open the popup.
2618
 * A -1 return value indicates that we go on with menu tracking.
2619
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
2620
 */
2621
static INT MENU_ButtonUp( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags)
Alexandre Julliard's avatar
Alexandre Julliard committed
2622
{
2623
    TRACE("%p hmenu=%p\n", pmt, hPtMenu);
2624

Alexandre Julliard's avatar
Alexandre Julliard committed
2625
    if (hPtMenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
2626
    {
2627
	UINT pos;
2628
	POPUPMENU *ptmenu = MENU_GetMenu( hPtMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2629

Alexandre Julliard's avatar
Alexandre Julliard committed
2630
        if( IS_SYSTEM_MENU(ptmenu) )
2631 2632 2633
            pos = 0;
        else if (MENU_FindItemByCoords( ptmenu, pmt->pt, &pos ) != ht_item)
            pos = NO_SELECTED_ITEM;
Alexandre Julliard's avatar
Alexandre Julliard committed
2634

2635
        if (pos != NO_SELECTED_ITEM && (ptmenu->FocusedItem == pos))
Alexandre Julliard's avatar
Alexandre Julliard committed
2636
	{
2637
            debug_print_menuitem ("FocusedItem: ", &ptmenu->items[pos], "");
2638

2639
	    if (!(ptmenu->items[pos].fType & MF_POPUP))
2640 2641
	    {
	        INT executedMenuId = MENU_ExecFocusedItem( pmt, hPtMenu, wFlags);
2642 2643
                if (executedMenuId == -1 || executedMenuId == -2) return -1;
                return executedMenuId;
2644
	    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2645

2646
	    /* If we are dealing with the menu bar                  */
2647 2648
	    /* and this is a click on an already "popped" item:     */
	    /* Stop the menu tracking and close the opened submenus */
2649
            if(((pmt->hTopMenu == hPtMenu) || IS_SYSTEM_MENU(ptmenu)) && (pmt->trackFlags & TF_RCVD_BTN_UP))
2650
		return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
2651
	}
2652
        if( GetMenu(ptmenu->hWnd) == hPtMenu || IS_SYSTEM_MENU(ptmenu) )
2653 2654
        {
            if (pos == NO_SELECTED_ITEM) return 0;
2655
            pmt->trackFlags |= TF_RCVD_BTN_UP;
2656
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
2657
    }
2658
    return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
2659 2660 2661
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2662 2663 2664 2665 2666
/***********************************************************************
 *           MENU_MouseMove
 *
 * Return TRUE if we can go on with menu tracking.
 */
2667
static BOOL MENU_MouseMove( MTRACKER* pmt, HMENU hPtMenu, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2668
{
2669
    UINT id = NO_SELECTED_ITEM;
Alexandre Julliard's avatar
Alexandre Julliard committed
2670
    POPUPMENU *ptmenu = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
2671

Alexandre Julliard's avatar
Alexandre Julliard committed
2672
    if( hPtMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2673
    {
2674
	ptmenu = MENU_GetMenu( hPtMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2675 2676
        if( IS_SYSTEM_MENU(ptmenu) )
	    id = 0;
2677 2678
        else if (MENU_FindItemByCoords( ptmenu, pmt->pt, &id ) != ht_item)
            id = NO_SELECTED_ITEM;
2679
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2680 2681

    if( id == NO_SELECTED_ITEM )
Alexandre Julliard's avatar
Alexandre Julliard committed
2682
    {
2683
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2684
			 NO_SELECTED_ITEM, TRUE, pmt->hTopMenu);
2685

Alexandre Julliard's avatar
Alexandre Julliard committed
2686
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2687
    else if( ptmenu->FocusedItem != id )
Alexandre Julliard's avatar
Alexandre Julliard committed
2688
    {
2689 2690
        MENU_SwitchTracking( pmt, hPtMenu, id, wFlags );
        pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hPtMenu, FALSE, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
2691 2692
    }
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2693 2694
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2695

Alexandre Julliard's avatar
Alexandre Julliard committed
2696 2697
/***********************************************************************
 *           MENU_DoNextMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
2698 2699
 *
 * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
Alexandre Julliard's avatar
Alexandre Julliard committed
2700
 */
2701
static LRESULT MENU_DoNextMenu( MTRACKER* pmt, UINT vk, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2702
{
2703
    POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2704
    BOOL atEnd = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2705

2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716
    /* When skipping left, we need to do something special after the
       first menu.                                                  */
    if (vk == VK_LEFT && menu->FocusedItem == 0)
    {
        atEnd = TRUE;
    }
    /* When skipping right, for the non-system menu, we need to
       handle the last non-special menu item (ie skip any window
       icons such as MDI maximize, restore or close)             */
    else if ((vk == VK_RIGHT) && !IS_SYSTEM_MENU(menu))
    {
2717
        UINT i = menu->FocusedItem + 1;
2718
        while (i < menu->nItems) {
2719 2720 2721 2722 2723
            if ((menu->items[i].wID >= SC_SIZE &&
                 menu->items[i].wID <= SC_RESTORE)) {
                i++;
            } else break;
        }
2724
        if (i == menu->nItems) {
2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736
            atEnd = TRUE;
        }
    }
    /* When skipping right, we need to cater for the system menu */
    else if ((vk == VK_RIGHT) && IS_SYSTEM_MENU(menu))
    {
        if (menu->FocusedItem == (menu->nItems - 1)) {
            atEnd = TRUE;
        }
    }

    if( atEnd )
Alexandre Julliard's avatar
Alexandre Julliard committed
2737
    {
2738
        MDINEXTMENU next_menu;
2739 2740 2741
	HMENU hNewMenu;
	HWND  hNewWnd;
	UINT  id = 0;
2742 2743 2744 2745 2746

        next_menu.hmenuIn = (IS_SYSTEM_MENU(menu)) ? GetSubMenu(pmt->hTopMenu,0) : pmt->hTopMenu;
        next_menu.hmenuNext = 0;
        next_menu.hwndNext = 0;
        SendMessageW( pmt->hOwnerWnd, WM_NEXTMENU, vk, (LPARAM)&next_menu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2747

2748
        TRACE("%p [%p] -> %p [%p]\n",
2749
              pmt->hCurrentMenu, pmt->hOwnerWnd, next_menu.hmenuNext, next_menu.hwndNext );
Alexandre Julliard's avatar
Alexandre Julliard committed
2750

2751
	if (!next_menu.hmenuNext || !next_menu.hwndNext)
Alexandre Julliard's avatar
Alexandre Julliard committed
2752
	{
2753
            DWORD style = GetWindowLongW( pmt->hOwnerWnd, GWL_STYLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2754 2755 2756 2757
	    hNewWnd = pmt->hOwnerWnd;
	    if( IS_SYSTEM_MENU(menu) )
	    {
		/* switch to the menu bar */
Alexandre Julliard's avatar
Alexandre Julliard committed
2758

2759
                if(style & WS_CHILD || !(hNewMenu = GetMenu(hNewWnd))) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2760

Alexandre Julliard's avatar
Alexandre Julliard committed
2761 2762
	        if( vk == VK_LEFT )
	        {
2763
		    menu = MENU_GetMenu( hNewMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
2764
		    id = menu->nItems - 1;
2765 2766 2767 2768 2769 2770

                    /* Skip backwards over any system predefined icons,
                       eg. MDI close, restore etc icons                 */
                    while ((id > 0) &&
                           (menu->items[id].wID >= SC_SIZE &&
                            menu->items[id].wID <= SC_RESTORE)) id--;
Alexandre Julliard's avatar
Alexandre Julliard committed
2771 2772
	        }
	    }
2773
	    else if (style & WS_SYSMENU )
Alexandre Julliard's avatar
Alexandre Julliard committed
2774 2775
	    {
		/* switch to the system menu */
2776
	        hNewMenu = get_win_sys_menu( hNewWnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
2777
	    }
2778
            else return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2779 2780 2781
	}
	else    /* application returned a new menu to switch to */
	{
2782
            hNewMenu = next_menu.hmenuNext;
2783
            hNewWnd = WIN_GetFullHandle( next_menu.hwndNext );
Alexandre Julliard's avatar
Alexandre Julliard committed
2784

2785
	    if( IsMenu(hNewMenu) && IsWindow(hNewWnd) )
Alexandre Julliard's avatar
Alexandre Julliard committed
2786
	    {
2787
                DWORD style = GetWindowLongW( hNewWnd, GWL_STYLE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2788

2789 2790
		if (style & WS_SYSMENU &&
		    GetSubMenu(get_win_sys_menu(hNewWnd), 0) == hNewMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2791 2792
		{
	            /* get the real system menu */
2793
		    hNewMenu =  get_win_sys_menu(hNewWnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
2794
		}
2795
	        else if (style & WS_CHILD || GetMenu(hNewWnd) != hNewMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2796
		{
2797 2798
		    /* FIXME: Not sure what to do here;
		     * perhaps try to track hNewMenu as a popup? */
Alexandre Julliard's avatar
Alexandre Julliard committed
2799

2800
		    TRACE(" -- got confused.\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
2801 2802 2803 2804 2805 2806 2807
		    return FALSE;
		}
	    }
	    else return FALSE;
	}

	if( hNewMenu != pmt->hTopMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2808
	{
2809
	    MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, NO_SELECTED_ITEM,
2810
                    FALSE, 0 );
2811
	    if( pmt->hCurrentMenu != pmt->hTopMenu )
2812
		MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
Alexandre Julliard's avatar
Alexandre Julliard committed
2813 2814
	}

Alexandre Julliard's avatar
Alexandre Julliard committed
2815 2816 2817
	if( hNewWnd != pmt->hOwnerWnd )
	{
	    pmt->hOwnerWnd = hNewWnd;
2818
            set_capture_window( pmt->hOwnerWnd, GUI_INMENUMODE, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
2819
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
2820

Alexandre Julliard's avatar
Alexandre Julliard committed
2821
	pmt->hTopMenu = pmt->hCurrentMenu = hNewMenu; /* all subpopups are hidden */
2822
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hTopMenu, id, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2823

Alexandre Julliard's avatar
Alexandre Julliard committed
2824 2825 2826 2827
	return TRUE;
    }
    return FALSE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2828

Alexandre Julliard's avatar
Alexandre Julliard committed
2829 2830 2831 2832 2833 2834
/***********************************************************************
 *           MENU_SuspendPopup
 *
 * The idea is not to show the popup if the next input message is
 * going to hide it anyway.
 */
2835
static BOOL MENU_SuspendPopup( MTRACKER* pmt, UINT uMsg )
Alexandre Julliard's avatar
Alexandre Julliard committed
2836
{
2837
    MSG msg;
Alexandre Julliard's avatar
Alexandre Julliard committed
2838

Alexandre Julliard's avatar
Alexandre Julliard committed
2839
    msg.hwnd = pmt->hOwnerWnd;
Alexandre Julliard's avatar
Alexandre Julliard committed
2840

2841
    PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE);
Alexandre Julliard's avatar
Alexandre Julliard committed
2842 2843 2844 2845 2846
    pmt->trackFlags |= TF_SKIPREMOVE;

    switch( uMsg )
    {
	case WM_KEYDOWN:
2847
	     PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
Alexandre Julliard's avatar
Alexandre Julliard committed
2848 2849
	     if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
	     {
2850 2851
		 PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
	         PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
Alexandre Julliard's avatar
Alexandre Julliard committed
2852 2853 2854 2855 2856 2857 2858 2859
	         if( msg.message == WM_KEYDOWN &&
		    (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
	         {
		     pmt->trackFlags |= TF_SUSPENDPOPUP;
		     return TRUE;
	         }
	     }
	     break;
Alexandre Julliard's avatar
Alexandre Julliard committed
2860
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2861

Alexandre Julliard's avatar
Alexandre Julliard committed
2862 2863 2864
    /* failures go through this */
    pmt->trackFlags &= ~TF_SUSPENDPOPUP;
    return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2865
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2866

2867 2868 2869
/***********************************************************************
 *           MENU_KeyEscape
 *
2870
 * Handle a VK_ESCAPE key event in a menu.
2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892
 */
static BOOL MENU_KeyEscape(MTRACKER* pmt, UINT wFlags)
{
    BOOL bEndMenu = TRUE;

    if (pmt->hCurrentMenu != pmt->hTopMenu)
    {
        POPUPMENU *menu = MENU_GetMenu(pmt->hCurrentMenu);

        if (menu->wFlags & MF_POPUP)
        {
            HMENU hmenutmp, hmenuprev;

            hmenuprev = hmenutmp = pmt->hTopMenu;

            /* close topmost popup */
            while (hmenutmp != pmt->hCurrentMenu)
            {
                hmenuprev = hmenutmp;
                hmenutmp = MENU_GetSubPopup( hmenuprev );
            }

2893
            MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2894 2895 2896 2897 2898 2899 2900 2901
            pmt->hCurrentMenu = hmenuprev;
            bEndMenu = FALSE;
        }
    }

    return bEndMenu;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
2902 2903 2904
/***********************************************************************
 *           MENU_KeyLeft
 *
2905
 * Handle a VK_LEFT key event in a menu.
Alexandre Julliard's avatar
Alexandre Julliard committed
2906
 */
2907
static void MENU_KeyLeft( MTRACKER* pmt, UINT wFlags, UINT msg )
Alexandre Julliard's avatar
Alexandre Julliard committed
2908
{
Alexandre Julliard's avatar
Alexandre Julliard committed
2909
    POPUPMENU *menu;
2910 2911
    HMENU hmenutmp, hmenuprev;
    UINT  prevcol;
Alexandre Julliard's avatar
Alexandre Julliard committed
2912

Alexandre Julliard's avatar
Alexandre Julliard committed
2913
    hmenuprev = hmenutmp = pmt->hTopMenu;
2914
    menu = MENU_GetMenu( hmenutmp );
Alexandre Julliard's avatar
Alexandre Julliard committed
2915

Alexandre Julliard's avatar
Alexandre Julliard committed
2916
    /* Try to move 1 column left (if possible) */
2917
    if( (prevcol = MENU_GetStartOfPrevColumn( pmt->hCurrentMenu )) !=
Alexandre Julliard's avatar
Alexandre Julliard committed
2918
	NO_SELECTED_ITEM ) {
2919

Alexandre Julliard's avatar
Alexandre Julliard committed
2920
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2921
			 prevcol, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2922 2923 2924
	return;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
2925 2926
    /* close topmost popup */
    while (hmenutmp != pmt->hCurrentMenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
2927
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2928
	hmenuprev = hmenutmp;
Alexandre Julliard's avatar
Alexandre Julliard committed
2929 2930
	hmenutmp = MENU_GetSubPopup( hmenuprev );
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2931

2932
    MENU_HideSubPopups( pmt->hOwnerWnd, hmenuprev, TRUE, wFlags );
2933
    pmt->hCurrentMenu = hmenuprev;
Alexandre Julliard's avatar
Alexandre Julliard committed
2934 2935

    if ( (hmenuprev == pmt->hTopMenu) && !(menu->wFlags & MF_POPUP) )
Alexandre Julliard's avatar
Alexandre Julliard committed
2936
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2937
	/* move menu bar selection if no more popups are left */
Alexandre Julliard's avatar
Alexandre Julliard committed
2938

2939
	if( !MENU_DoNextMenu( pmt, VK_LEFT, wFlags ) )
Alexandre Julliard's avatar
Alexandre Julliard committed
2940
	     MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_PREV );
Alexandre Julliard's avatar
Alexandre Julliard committed
2941

Alexandre Julliard's avatar
Alexandre Julliard committed
2942
	if ( hmenuprev != hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
Alexandre Julliard's avatar
Alexandre Julliard committed
2943
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
2944 2945 2946
	   /* A sublevel menu was displayed - display the next one
	    * unless there is another displacement coming up */

2947
	    if( !MENU_SuspendPopup( pmt, msg ) )
2948 2949
		pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
						pmt->hTopMenu, TRUE, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
2950
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
2951
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2952 2953 2954
}


Alexandre Julliard's avatar
Alexandre Julliard committed
2955 2956 2957 2958 2959
/***********************************************************************
 *           MENU_KeyRight
 *
 * Handle a VK_RIGHT key event in a menu.
 */
2960
static void MENU_KeyRight( MTRACKER* pmt, UINT wFlags, UINT msg )
Alexandre Julliard's avatar
Alexandre Julliard committed
2961
{
2962
    HMENU hmenutmp;
2963
    POPUPMENU *menu = MENU_GetMenu( pmt->hTopMenu );
2964
    UINT  nextcol;
Alexandre Julliard's avatar
Alexandre Julliard committed
2965

2966 2967 2968 2969
    TRACE("MENU_KeyRight called, cur %p (%s), top %p (%s).\n",
          pmt->hCurrentMenu,
          debugstr_w((MENU_GetMenu(pmt->hCurrentMenu))->items[0].text),
          pmt->hTopMenu, debugstr_w(menu->items[0].text) );
Alexandre Julliard's avatar
Alexandre Julliard committed
2970

Alexandre Julliard's avatar
Alexandre Julliard committed
2971
    if ( (menu->wFlags & MF_POPUP) || (pmt->hCurrentMenu != pmt->hTopMenu))
Alexandre Julliard's avatar
Alexandre Julliard committed
2972
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2973
	/* If already displaying a popup, try to display sub-popup */
Alexandre Julliard's avatar
Alexandre Julliard committed
2974

Alexandre Julliard's avatar
Alexandre Julliard committed
2975
	hmenutmp = pmt->hCurrentMenu;
2976
	pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd, hmenutmp, TRUE, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
2977

Alexandre Julliard's avatar
Alexandre Julliard committed
2978 2979
	/* if subpopup was displayed then we are done */
	if (hmenutmp != pmt->hCurrentMenu) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
2980
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
2981

Alexandre Julliard's avatar
Alexandre Julliard committed
2982
    /* Check to see if there's another column */
2983
    if( (nextcol = MENU_GetStartOfNextColumn( pmt->hCurrentMenu )) !=
Alexandre Julliard's avatar
Alexandre Julliard committed
2984
	NO_SELECTED_ITEM ) {
2985
	TRACE("Going to %d.\n", nextcol );
Alexandre Julliard's avatar
Alexandre Julliard committed
2986
	MENU_SelectItem( pmt->hOwnerWnd, pmt->hCurrentMenu,
2987
			 nextcol, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
2988 2989 2990
	return;
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
2991
    if (!(menu->wFlags & MF_POPUP))	/* menu bar tracking */
Alexandre Julliard's avatar
Alexandre Julliard committed
2992
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
2993
	if( pmt->hCurrentMenu != pmt->hTopMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
2994
	{
2995
	    MENU_HideSubPopups( pmt->hOwnerWnd, pmt->hTopMenu, FALSE, wFlags );
Alexandre Julliard's avatar
Alexandre Julliard committed
2996 2997 2998 2999
	    hmenutmp = pmt->hCurrentMenu = pmt->hTopMenu;
	} else hmenutmp = 0;

	/* try to move to the next item */
3000
	if( !MENU_DoNextMenu( pmt, VK_RIGHT, wFlags ) )
Alexandre Julliard's avatar
Alexandre Julliard committed
3001 3002 3003
	     MENU_MoveSelection( pmt->hOwnerWnd, pmt->hTopMenu, ITEM_NEXT );

	if( hmenutmp || pmt->trackFlags & TF_SUSPENDPOPUP )
3004
	    if( !MENU_SuspendPopup( pmt, msg ) )
3005
		pmt->hCurrentMenu = MENU_ShowSubPopup(pmt->hOwnerWnd,
3006
						       pmt->hTopMenu, TRUE, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
3007 3008
    }
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3009

3010 3011 3012 3013 3014
static void CALLBACK release_capture( BOOL __normal )
{
    set_capture_window( 0, GUI_INMENUMODE, NULL );
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3015 3016 3017 3018 3019
/***********************************************************************
 *           MENU_TrackMenu
 *
 * Menu tracking code.
 */
3020 3021
static BOOL MENU_TrackMenu( HMENU hmenu, UINT wFlags, INT x, INT y,
                            HWND hwnd, const RECT *lprect )
Alexandre Julliard's avatar
Alexandre Julliard committed
3022
{
3023
    MSG msg;
Alexandre Julliard's avatar
Alexandre Julliard committed
3024
    POPUPMENU *menu;
3025
    BOOL fRemove;
3026
    INT executedMenuId = -1;
3027
    MTRACKER mt;
3028
    BOOL enterIdleSent = FALSE;
3029
    HWND capture_win;
3030 3031 3032 3033

    mt.trackFlags = 0;
    mt.hCurrentMenu = hmenu;
    mt.hTopMenu = hmenu;
3034
    mt.hOwnerWnd = WIN_GetFullHandle( hwnd );
3035 3036
    mt.pt.x = x;
    mt.pt.y = y;
Alexandre Julliard's avatar
Alexandre Julliard committed
3037

3038 3039
    TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%p %s\n",
          hmenu, wFlags, x, y, hwnd, wine_dbgstr_rect( lprect));
3040

3041 3042
    if (!(menu = MENU_GetMenu( hmenu )))
    {
3043
        WARN("Invalid menu handle %p\n", hmenu);
3044 3045 3046
        SetLastError(ERROR_INVALID_MENU_HANDLE);
        return FALSE;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3047

3048
    if (wFlags & TPM_BUTTONDOWN)
3049 3050
    {
	/* Get the result in order to start the tracking or not */
3051
	fRemove = MENU_ButtonDown( &mt, WM_LBUTTONDOWN, hmenu, wFlags );
3052
	fEndMenu = !fRemove;
3053
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3054

3055 3056
    if (wFlags & TF_ENDMENU) fEndMenu = TRUE;

3057 3058 3059
    /* owner may not be visible when tracking a popup, so use the menu itself */
    capture_win = (wFlags & TPM_POPUPMENU) ? menu->hWnd : mt.hOwnerWnd;
    set_capture_window( capture_win, GUI_INMENUMODE, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
3060

3061 3062 3063
    if ((wFlags & TPM_POPUPMENU) && menu->nItems == 0)
        return FALSE;

3064
    __TRY while (!fEndMenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
3065
    {
3066
	menu = MENU_GetMenu( mt.hCurrentMenu );
3067 3068
	if (!menu) /* sometimes happens if I do a window manager close */
	    break;
Alexandre Julliard's avatar
Alexandre Julliard committed
3069

Alexandre Julliard's avatar
Alexandre Julliard committed
3070
	/* we have to keep the message in the queue until it's
Alexandre Julliard's avatar
Alexandre Julliard committed
3071
	 * clear that menu loop is not over yet. */
Alexandre Julliard's avatar
Alexandre Julliard committed
3072

3073 3074
        for (;;)
        {
3075
            if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
3076
            {
3077
                if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
3078
                /* remove the message from the queue */
3079
                PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3080 3081 3082 3083 3084
            }
            else
            {
                if (!enterIdleSent)
                {
3085
                    HWND win = menu->wFlags & MF_POPUP ? menu->hWnd : 0;
3086 3087 3088 3089 3090 3091
                    enterIdleSent = TRUE;
                    SendMessageW( mt.hOwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM)win );
                }
                WaitMessage();
            }
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
3092

3093
	/* check if EndMenu() tried to cancel us, by posting this message */
3094
        if(msg.message == WM_CANCELMODE)
3095 3096 3097 3098 3099
	{
	    /* we are now out of the loop */
    	    fEndMenu = TRUE;

	    /* remove the message from the queue */
3100
	    PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3101

3102 3103 3104 3105
	    /* break out of internal loop, ala ESCAPE */
	    break;
	}

3106
        mt.pt = msg.pt;
Alexandre Julliard's avatar
Alexandre Julliard committed
3107

3108 3109 3110
	if ( (msg.hwnd==menu->hWnd) || (msg.message!=WM_TIMER) )
	  enterIdleSent=FALSE;

Alexandre Julliard's avatar
Alexandre Julliard committed
3111
        fRemove = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3112
	if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
Alexandre Julliard's avatar
Alexandre Julliard committed
3113
	{
3114
            /*
3115 3116 3117
             * Use the mouse coordinates in lParam instead of those in the MSG
             * struct to properly handle synthetic messages. They are already
             * in screen coordinates.
3118
             */
3119 3120
            mt.pt.x = (short)LOWORD(msg.lParam);
            mt.pt.y = (short)HIWORD(msg.lParam);
3121

Alexandre Julliard's avatar
Alexandre Julliard committed
3122
	    /* Find a menu for this mouse event */
3123
	    hmenu = MENU_PtMenu( mt.hTopMenu, mt.pt );
Alexandre Julliard's avatar
Alexandre Julliard committed
3124

Alexandre Julliard's avatar
Alexandre Julliard committed
3125
	    switch(msg.message)
Alexandre Julliard's avatar
Alexandre Julliard committed
3126
	    {
Alexandre Julliard's avatar
Alexandre Julliard committed
3127 3128
		/* no WM_NC... messages in captured state */

Alexandre Julliard's avatar
Alexandre Julliard committed
3129 3130 3131 3132 3133 3134
		case WM_RBUTTONDBLCLK:
		case WM_RBUTTONDOWN:
		    if (!(wFlags & TPM_RIGHTBUTTON)) break;
		    /* fall through */
		case WM_LBUTTONDBLCLK:
		case WM_LBUTTONDOWN:
3135 3136
		    /* If the message belongs to the menu, removes it from the queue */
		    /* Else, end menu tracking */
3137
		    fRemove = MENU_ButtonDown( &mt, msg.message, hmenu, wFlags );
3138
		    fEndMenu = !fRemove;
Alexandre Julliard's avatar
Alexandre Julliard committed
3139
		    break;
3140

Alexandre Julliard's avatar
Alexandre Julliard committed
3141 3142 3143 3144
		case WM_RBUTTONUP:
		    if (!(wFlags & TPM_RIGHTBUTTON)) break;
		    /* fall through */
		case WM_LBUTTONUP:
3145 3146
		    /* Check if a menu was selected by the mouse */
		    if (hmenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
3147
		    {
3148
                        executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
3149
                        TRACE("executedMenuId %d\n", executedMenuId);
3150 3151

			/* End the loop if executedMenuId is an item ID */
3152 3153
			/* or if the job was done (executedMenuId = 0). */
                        fEndMenu = fRemove = (executedMenuId != -1);
Alexandre Julliard's avatar
Alexandre Julliard committed
3154
		    }
3155 3156 3157 3158
                    /* No menu was selected by the mouse */
                    /* if the function was called by TrackPopupMenu, continue
                       with the menu tracking. If not, stop it */
                    else
3159
                        fEndMenu = !(wFlags & TPM_POPUPMENU);
3160

Alexandre Julliard's avatar
Alexandre Julliard committed
3161
		    break;
3162

Alexandre Julliard's avatar
Alexandre Julliard committed
3163
		case WM_MOUSEMOVE:
3164 3165
                    /* the selected menu item must be changed every time */
		     /* the mouse moves. */
3166

3167
                    if (hmenu)
3168
			fEndMenu |= !MENU_MouseMove( &mt, hmenu, wFlags );
3169

Alexandre Julliard's avatar
Alexandre Julliard committed
3170
	    } /* switch(msg.message) - mouse */
Alexandre Julliard's avatar
Alexandre Julliard committed
3171
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
3172
	else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
Alexandre Julliard's avatar
Alexandre Julliard committed
3173
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
3174
            fRemove = TRUE;  /* Keyboard messages are always removed */
Alexandre Julliard's avatar
Alexandre Julliard committed
3175
	    switch(msg.message)
Alexandre Julliard's avatar
Alexandre Julliard committed
3176 3177
	    {
	    case WM_KEYDOWN:
3178
	    case WM_SYSKEYDOWN:
Alexandre Julliard's avatar
Alexandre Julliard committed
3179
		switch(msg.wParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
3180
		{
3181
		case VK_MENU:
3182
		case VK_F10:
3183 3184 3185
		    fEndMenu = TRUE;
		    break;

Alexandre Julliard's avatar
Alexandre Julliard committed
3186 3187
		case VK_HOME:
		case VK_END:
3188
		    MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu,
3189
				     NO_SELECTED_ITEM, FALSE, 0 );
3190
		    MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu,
Alexandre Julliard's avatar
Alexandre Julliard committed
3191
				       (msg.wParam == VK_HOME)? ITEM_NEXT : ITEM_PREV );
Alexandre Julliard's avatar
Alexandre Julliard committed
3192 3193
		    break;

3194
		case VK_UP:
Alexandre Julliard's avatar
Alexandre Julliard committed
3195 3196
		case VK_DOWN: /* If on menu bar, pull-down the menu */

3197
		    menu = MENU_GetMenu( mt.hCurrentMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
3198
		    if (!(menu->wFlags & MF_POPUP))
3199
			mt.hCurrentMenu = MENU_ShowSubPopup(mt.hOwnerWnd, mt.hTopMenu, TRUE, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
3200
		    else      /* otherwise try to move selection */
3201 3202
			MENU_MoveSelection( mt.hOwnerWnd, mt.hCurrentMenu, 
                                       (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
Alexandre Julliard's avatar
Alexandre Julliard committed
3203 3204 3205
		    break;

		case VK_LEFT:
3206
		    MENU_KeyLeft( &mt, wFlags, msg.message );
Alexandre Julliard's avatar
Alexandre Julliard committed
3207
		    break;
3208

Alexandre Julliard's avatar
Alexandre Julliard committed
3209
		case VK_RIGHT:
3210
		    MENU_KeyRight( &mt, wFlags, msg.message );
Alexandre Julliard's avatar
Alexandre Julliard committed
3211
		    break;
3212

Alexandre Julliard's avatar
Alexandre Julliard committed
3213
		case VK_ESCAPE:
3214
                    fEndMenu = MENU_KeyEscape(&mt, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
3215 3216
		    break;

3217 3218 3219 3220 3221
		case VK_F1:
		    {
			HELPINFO hi;
			hi.cbSize = sizeof(HELPINFO);
			hi.iContextType = HELPINFO_MENUITEM;
3222
			if (menu->FocusedItem == NO_SELECTED_ITEM)
3223
			    hi.iCtrlId = 0;
3224 3225
		        else
			    hi.iCtrlId = menu->items[menu->FocusedItem].wID;
3226 3227 3228
			hi.hItemHandle = hmenu;
			hi.dwContextId = menu->dwContextHelpID;
			hi.MousePos = msg.pt;
3229
			SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
3230 3231 3232
			break;
		    }

Alexandre Julliard's avatar
Alexandre Julliard committed
3233
		default:
3234
		    TranslateMessage( &msg );
Alexandre Julliard's avatar
Alexandre Julliard committed
3235 3236 3237 3238 3239
		    break;
		}
		break;  /* WM_KEYDOWN */

	    case WM_CHAR:
3240
	    case WM_SYSCHAR:
Alexandre Julliard's avatar
Alexandre Julliard committed
3241
		{
3242
		    UINT	pos;
Alexandre Julliard's avatar
Alexandre Julliard committed
3243

3244 3245
		    if (msg.wParam == '\r' || msg.wParam == ' ')
		    {
3246
                        executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3247
                        fEndMenu = (executedMenuId != -2);
3248

3249 3250 3251
			break;
		    }

Alexandre Julliard's avatar
Alexandre Julliard committed
3252 3253
		      /* Hack to avoid control chars. */
		      /* We will find a better way real soon... */
3254
		    if (msg.wParam < 32) break;
Alexandre Julliard's avatar
Alexandre Julliard committed
3255

3256
		    pos = MENU_FindItemByKey( mt.hOwnerWnd, mt.hCurrentMenu,
3257
                                              LOWORD(msg.wParam), FALSE );
3258 3259
		    if (pos == (UINT)-2) fEndMenu = TRUE;
		    else if (pos == (UINT)-1) MessageBeep(0);
Alexandre Julliard's avatar
Alexandre Julliard committed
3260 3261
		    else
		    {
3262 3263
			MENU_SelectItem( mt.hOwnerWnd, mt.hCurrentMenu, pos,
                                TRUE, 0 );
3264
                        executedMenuId = MENU_ExecFocusedItem(&mt,mt.hCurrentMenu, wFlags);
3265
                        fEndMenu = (executedMenuId != -2);
Alexandre Julliard's avatar
Alexandre Julliard committed
3266
		    }
3267
		}
Alexandre Julliard's avatar
Alexandre Julliard committed
3268 3269
		break;
	    }  /* switch(msg.message) - kbd */
Alexandre Julliard's avatar
Alexandre Julliard committed
3270 3271 3272
	}
	else
	{
3273
	    PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
3274
	    DispatchMessageW( &msg );
3275
	    continue;
Alexandre Julliard's avatar
Alexandre Julliard committed
3276 3277
	}

Alexandre Julliard's avatar
Alexandre Julliard committed
3278 3279 3280 3281 3282
	if (!fEndMenu) fRemove = TRUE;

	/* finally remove message from the queue */

        if (fRemove && !(mt.trackFlags & TF_SKIPREMOVE) )
3283
	    PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
Alexandre Julliard's avatar
Alexandre Julliard committed
3284
	else mt.trackFlags &= ~TF_SKIPREMOVE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3285
    }
3286
    __FINALLY( release_capture )
3287

3288 3289 3290 3291 3292
    /* If dropdown is still painted and the close box is clicked on
       then the menu will be destroyed as part of the DispatchMessage above.
       This will then invalidate the menu handle in mt.hTopMenu. We should
       check for this first.  */
    if( IsMenu( mt.hTopMenu ) )
Alexandre Julliard's avatar
Alexandre Julliard committed
3293
    {
3294
	menu = MENU_GetMenu( mt.hTopMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
3295

3296 3297
        if( IsWindow( mt.hOwnerWnd ) )
        {
3298
	    MENU_HideSubPopups( mt.hOwnerWnd, mt.hTopMenu, FALSE, wFlags );
3299

3300
	    if (menu && (menu->wFlags & MF_POPUP))
3301
	    {
3302 3303
                DestroyWindow( menu->hWnd );
                menu->hWnd = 0;
3304 3305 3306 3307

                if (!(wFlags & TPM_NONOTIFY))
                   SendMessageW( mt.hOwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.hTopMenu,
                                 MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
3308 3309
	    }
	    MENU_SelectItem( mt.hOwnerWnd, mt.hTopMenu, NO_SELECTED_ITEM, FALSE, 0 );
3310
	    SendMessageW( mt.hOwnerWnd, WM_MENUSELECT, MAKEWPARAM(0,0xffff), 0 );
3311
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
3312
    }
3313

3314
    SetLastError( ERROR_SUCCESS );
3315
    /* The return value is only used by TrackPopupMenu */
3316
    if (!(wFlags & TPM_RETURNCMD)) return TRUE;
3317
    if (executedMenuId == -1) executedMenuId = 0;
3318
    return executedMenuId;
Alexandre Julliard's avatar
Alexandre Julliard committed
3319
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3320

Alexandre Julliard's avatar
Alexandre Julliard committed
3321 3322 3323
/***********************************************************************
 *           MENU_InitTracking
 */
3324
static BOOL MENU_InitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
Alexandre Julliard's avatar
Alexandre Julliard committed
3325
{
3326 3327
    POPUPMENU *menu;
    
3328
    TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
3329

3330
    HideCaret(0);
3331

3332 3333
    if (!(menu = MENU_GetMenu( hMenu ))) return FALSE;

3334 3335 3336 3337
    /* This makes the menus of applications built with Delphi work.
     * It also enables menus to be displayed in more than one window,
     * but there are some bugs left that need to be fixed in this case.
     */
3338 3339 3340 3341 3342 3343 3344 3345
    if (!bPopup) menu->hWnd = hWnd;
    if (!top_popup)
    {
        top_popup = menu->hWnd;
        top_popup_hmenu = hMenu;
    }

    fEndMenu = FALSE;
3346

3347 3348
    /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
    if (!(wFlags & TPM_NONOTIFY))
3349
       SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
3350

3351
    SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
3352 3353

    if (!(wFlags & TPM_NONOTIFY))
3354
    {
3355
       SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
3356 3357 3358
       /* If an app changed/recreated menu bar entries in WM_INITMENU
        * menu sizes will be recalculated once the menu created/shown.
        */
3359
    }
3360

3361 3362
    return TRUE;
}
3363

3364 3365 3366
/***********************************************************************
 *           MENU_ExitTracking
 */
3367
static BOOL MENU_ExitTracking(HWND hWnd, BOOL bPopup)
3368
{
3369
    TRACE("hwnd=%p\n", hWnd);
3370

3371
    SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
3372
    ShowCaret(0);
3373
    top_popup = 0;
3374
    top_popup_hmenu = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
3375 3376 3377
    return TRUE;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3378 3379 3380 3381 3382
/***********************************************************************
 *           MENU_TrackMouseMenuBar
 *
 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
 */
3383
void MENU_TrackMouseMenuBar( HWND hWnd, INT ht, POINT pt )
Alexandre Julliard's avatar
Alexandre Julliard committed
3384
{
3385
    HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3386
    UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
Alexandre Julliard's avatar
Alexandre Julliard committed
3387

3388
    TRACE("wnd=%p ht=0x%04x %s\n", hWnd, ht, wine_dbgstr_point( &pt));
3389

3390
    if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
3391
    if (IsMenu(hMenu))
Alexandre Julliard's avatar
Alexandre Julliard committed
3392
    {
3393
	MENU_InitTracking( hWnd, hMenu, FALSE, wFlags );
3394 3395 3396

        /* fetch the window menu again, it may have changed */
        hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd );
3397
	MENU_TrackMenu( hMenu, wFlags, pt.x, pt.y, hWnd, NULL );
3398
	MENU_ExitTracking(hWnd, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
3399
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3400 3401 3402 3403 3404 3405 3406 3407
}


/***********************************************************************
 *           MENU_TrackKbdMenuBar
 *
 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
 */
3408
void MENU_TrackKbdMenuBar( HWND hwnd, UINT wParam, WCHAR wChar)
Alexandre Julliard's avatar
Alexandre Julliard committed
3409
{
3410 3411
    UINT uItem = NO_SELECTED_ITEM;
    HMENU hTrackMenu;
3412
    UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
Alexandre Julliard's avatar
Alexandre Julliard committed
3413

3414 3415
    TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);

Alexandre Julliard's avatar
Alexandre Julliard committed
3416
    /* find window that has a menu */
3417

3418 3419
    while (!WIN_ALLOWED_MENU(GetWindowLongW( hwnd, GWL_STYLE )))
        if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
3420

Alexandre Julliard's avatar
Alexandre Julliard committed
3421
    /* check if we have to track a system menu */
3422 3423

    hTrackMenu = GetMenu( hwnd );
3424
    if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
Alexandre Julliard's avatar
Alexandre Julliard committed
3425
    {
3426
        if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
3427 3428 3429
        hTrackMenu = get_win_sys_menu( hwnd );
        uItem = 0;
        wParam |= HTSYSMENU; /* prevent item lookup */
Alexandre Julliard's avatar
Alexandre Julliard committed
3430
    }
3431
    if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
Alexandre Julliard's avatar
Alexandre Julliard committed
3432

3433 3434 3435
    if (!IsMenu( hTrackMenu )) return;

    MENU_InitTracking( hwnd, hTrackMenu, FALSE, wFlags );
Alexandre Julliard's avatar
Alexandre Julliard committed
3436

3437 3438 3439
    /* fetch the window menu again, it may have changed */
    hTrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : GetMenu( hwnd );

3440
    if( wChar && wChar != ' ' )
3441
    {
3442
        uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
3443
        if ( uItem >= (UINT)(-2) )
Alexandre Julliard's avatar
Alexandre Julliard committed
3444
        {
3445
            if( uItem == (UINT)(-1) ) MessageBeep(0);
3446
            /* schedule end of menu tracking */
3447
            wFlags |= TF_ENDMENU;
3448
            goto track_menu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3449
        }
3450
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3451

3452
    MENU_SelectItem( hwnd, hTrackMenu, uItem, TRUE, 0 );
3453

3454
    if (!(wParam & HTSYSMENU) || wChar == ' ')
3455 3456 3457 3458
    {
        if( uItem == NO_SELECTED_ITEM )
            MENU_MoveSelection( hwnd, hTrackMenu, ITEM_NEXT );
        else
3459
            PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
3460
    }
3461 3462 3463

track_menu:
    MENU_TrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
3464
    MENU_ExitTracking( hwnd, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
3465 3466
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3467
/**********************************************************************
3468
 *           TrackPopupMenuEx   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3469
 */
3470 3471
BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, INT x, INT y,
                              HWND hWnd, LPTPMPARAMS lpTpm )
Alexandre Julliard's avatar
Alexandre Julliard committed
3472
{
3473
    POPUPMENU *menu;
3474
    BOOL ret = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3475

3476 3477 3478 3479 3480 3481 3482
    TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
            hMenu, wFlags, x, y, hWnd, lpTpm,
            lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );

    /* Parameter check */
    /* FIXME: this check is performed several times, here and in the called
       functions. That could be optimized */
3483
    if (!(menu = MENU_GetMenu( hMenu )))
3484 3485 3486 3487
    {
        SetLastError( ERROR_INVALID_MENU_HANDLE );
        return FALSE;
    }
3488

3489 3490 3491 3492 3493 3494
    if (IsWindow(menu->hWnd))
    {
        SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
        return FALSE;
    }

3495 3496 3497
    if (MENU_InitPopup( hWnd, hMenu, wFlags ))
    {
        MENU_InitTracking(hWnd, hMenu, TRUE, wFlags);
3498

3499 3500 3501
        /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
        if (!(wFlags & TPM_NONOTIFY))
            SendMessageW( hWnd, WM_INITMENUPOPUP, (WPARAM)hMenu, 0);
3502

3503 3504 3505 3506
        if (menu->wFlags & MF_SYSMENU)
            MENU_InitSysMenuPopup( hMenu, GetWindowLongW( hWnd, GWL_STYLE ),
                                   GetClassLongW( hWnd, GCL_STYLE));

3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520
        if (MENU_ShowPopup( hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
            ret = MENU_TrackMenu( hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
                                  lpTpm ? &lpTpm->rcExclude : NULL );
        MENU_ExitTracking(hWnd, TRUE);

        if (menu->hWnd)
        {
            DestroyWindow( menu->hWnd );
            menu->hWnd = 0;

            if (!(wFlags & TPM_NONOTIFY))
               SendMessageW( hWnd, WM_UNINITMENUPOPUP, (WPARAM)hMenu,
                             MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
        }
3521
        SetLastError(0);
3522
    }
3523

Alexandre Julliard's avatar
Alexandre Julliard committed
3524
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
3525 3526
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3527
/**********************************************************************
3528 3529 3530 3531 3532
 *           TrackPopupMenu   (USER32.@)
 *
 * Like the win32 API, the function return the command ID only if the
 * flag TPM_RETURNCMD is on.
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
3533
 */
3534 3535
BOOL WINAPI TrackPopupMenu( HMENU hMenu, UINT wFlags, INT x, INT y,
                            INT nReserved, HWND hWnd, const RECT *lpRect )
Alexandre Julliard's avatar
Alexandre Julliard committed
3536
{
3537
    return TrackPopupMenuEx( hMenu, wFlags, x, y, hWnd, NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
3538
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3539

Alexandre Julliard's avatar
Alexandre Julliard committed
3540 3541
/***********************************************************************
 *           PopupMenuWndProc
Alexandre Julliard's avatar
Alexandre Julliard committed
3542 3543
 *
 * NOTE: Windows has totally different (and undocumented) popup wndproc.
Alexandre Julliard's avatar
Alexandre Julliard committed
3544
 */
3545
LRESULT WINAPI PopupMenuWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
3546
{
3547
    TRACE("hwnd=%p msg=0x%04x wp=0x%04lx lp=0x%08lx\n", hwnd, message, wParam, lParam);
3548

Alexandre Julliard's avatar
Alexandre Julliard committed
3549 3550 3551 3552
    switch(message)
    {
    case WM_CREATE:
	{
3553
	    CREATESTRUCTW *cs = (CREATESTRUCTW*)lParam;
3554
	    SetWindowLongPtrW( hwnd, 0, (LONG_PTR)cs->lpCreateParams );
3555
            return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3556 3557 3558
	}

    case WM_MOUSEACTIVATE:  /* We don't want to be activated */
3559
        return MA_NOACTIVATE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3560 3561 3562

    case WM_PAINT:
	{
3563 3564
	    PAINTSTRUCT ps;
	    BeginPaint( hwnd, &ps );
Alexandre Julliard's avatar
Alexandre Julliard committed
3565
	    MENU_DrawPopupMenu( hwnd, ps.hdc,
3566
                                (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
3567
	    EndPaint( hwnd, &ps );
3568
            return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3569
	}
3570 3571 3572 3573 3574 3575 3576 3577

    case WM_PRINTCLIENT:
	{
	    MENU_DrawPopupMenu( hwnd, (HDC)wParam,
                                (HMENU)GetWindowLongPtrW( hwnd, 0 ) );
            return 0;
        }

Alexandre Julliard's avatar
Alexandre Julliard committed
3578
    case WM_ERASEBKGND:
3579
        return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3580

Alexandre Julliard's avatar
Alexandre Julliard committed
3581
    case WM_DESTROY:
3582
        /* zero out global pointer in case resident popup window was destroyed. */
3583 3584 3585 3586
        if (hwnd == top_popup) {
            top_popup = 0;
            top_popup_hmenu = NULL;
        }
3587
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
3588

Alexandre Julliard's avatar
Alexandre Julliard committed
3589 3590 3591 3592
    case WM_SHOWWINDOW:

	if( wParam )
	{
3593
            if (!GetWindowLongPtrW( hwnd, 0 )) ERR("no menu to display\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
3594 3595
	}
	else
3596
            SetWindowLongPtrW( hwnd, 0, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
3597 3598
	break;

3599
    case MN_GETHMENU:
3600
        return GetWindowLongPtrW( hwnd, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
3601

Alexandre Julliard's avatar
Alexandre Julliard committed
3602
    default:
3603
        return DefWindowProcW( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
3604
    }
3605
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3606
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3607

Alexandre Julliard's avatar
Alexandre Julliard committed
3608

Alexandre Julliard's avatar
Alexandre Julliard committed
3609 3610 3611 3612 3613
/***********************************************************************
 *           MENU_GetMenuBarHeight
 *
 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
 */
3614 3615
UINT MENU_GetMenuBarHeight( HWND hwnd, UINT menubarWidth,
                              INT orgX, INT orgY )
Alexandre Julliard's avatar
Alexandre Julliard committed
3616
{
3617 3618
    HDC hdc;
    RECT rectBar;
Alexandre Julliard's avatar
Alexandre Julliard committed
3619 3620
    LPPOPUPMENU lppop;

3621
    TRACE("HWND %p, width %d, at (%d, %d).\n", hwnd, menubarWidth, orgX, orgY );
3622

3623
    if (!(lppop = MENU_GetMenu( GetMenu(hwnd) ))) return 0;
3624

3625
    hdc = GetDCEx( hwnd, 0, DCX_CACHE | DCX_WINDOW );
3626
    SelectObject( hdc, get_menu_font(FALSE));
3627
    SetRect(&rectBar, orgX, orgY, orgX+menubarWidth, orgY+GetSystemMetrics(SM_CYMENU));
3628
    MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
3629
    ReleaseDC( hwnd, hdc );
3630
    return lppop->Height;
Alexandre Julliard's avatar
Alexandre Julliard committed
3631 3632 3633
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3634
/*******************************************************************
3635
 *         ChangeMenuA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3636
 */
3637 3638
BOOL WINAPI ChangeMenuA( HMENU hMenu, UINT pos, LPCSTR data,
                             UINT id, UINT flags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3639
{
3640
    TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3641
    if (flags & MF_APPEND) return AppendMenuA( hMenu, flags & ~MF_APPEND,
Alexandre Julliard's avatar
Alexandre Julliard committed
3642
                                                 id, data );
3643 3644
    if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
    if (flags & MF_CHANGE) return ModifyMenuA(hMenu, pos, flags & ~MF_CHANGE,
Alexandre Julliard's avatar
Alexandre Julliard committed
3645
                                                id, data );
3646
    if (flags & MF_REMOVE) return RemoveMenu( hMenu,
Alexandre Julliard's avatar
Alexandre Julliard committed
3647 3648 3649
                                              flags & MF_BYPOSITION ? pos : id,
                                              flags & ~MF_REMOVE );
    /* Default: MF_INSERT */
3650
    return InsertMenuA( hMenu, pos, flags, id, data );
Alexandre Julliard's avatar
Alexandre Julliard committed
3651 3652 3653 3654
}


/*******************************************************************
3655
 *         ChangeMenuW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3656
 */
3657 3658
BOOL WINAPI ChangeMenuW( HMENU hMenu, UINT pos, LPCWSTR data,
                             UINT id, UINT flags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3659
{
3660
    TRACE("menu=%p pos=%d data=%p id=%08x flags=%08x\n", hMenu, pos, data, id, flags );
3661
    if (flags & MF_APPEND) return AppendMenuW( hMenu, flags & ~MF_APPEND,
Alexandre Julliard's avatar
Alexandre Julliard committed
3662
                                                 id, data );
3663 3664
    if (flags & MF_DELETE) return DeleteMenu(hMenu, pos, flags & ~MF_DELETE);
    if (flags & MF_CHANGE) return ModifyMenuW(hMenu, pos, flags & ~MF_CHANGE,
Alexandre Julliard's avatar
Alexandre Julliard committed
3665
                                                id, data );
3666
    if (flags & MF_REMOVE) return RemoveMenu( hMenu,
Alexandre Julliard's avatar
Alexandre Julliard committed
3667 3668 3669
                                              flags & MF_BYPOSITION ? pos : id,
                                              flags & ~MF_REMOVE );
    /* Default: MF_INSERT */
3670
    return InsertMenuW( hMenu, pos, flags, id, data );
Alexandre Julliard's avatar
Alexandre Julliard committed
3671
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3672 3673


Alexandre Julliard's avatar
Alexandre Julliard committed
3674
/*******************************************************************
3675
 *         CheckMenuItem    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3676
 */
3677
DWORD WINAPI CheckMenuItem( HMENU hMenu, UINT id, UINT flags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3678
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3679
    MENUITEM *item;
Alexandre Julliard's avatar
Alexandre Julliard committed
3680
    DWORD ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
3681 3682

    if (!(item = MENU_FindItem( &hMenu, &id, flags ))) return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3683 3684 3685
    ret = item->fState & MF_CHECKED;
    if (flags & MF_CHECKED) item->fState |= MF_CHECKED;
    else item->fState &= ~MF_CHECKED;
Alexandre Julliard's avatar
Alexandre Julliard committed
3686
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
3687 3688 3689
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3690
/**********************************************************************
3691
 *         EnableMenuItem    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3692
 */
3693
BOOL WINAPI EnableMenuItem( HMENU hMenu, UINT wItemID, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3694
{
3695
    UINT    oldflags;
Alexandre Julliard's avatar
Alexandre Julliard committed
3696
    MENUITEM *item;
3697
    POPUPMENU *menu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3698

3699
    TRACE("(%p, %04x, %04x) !\n", hMenu, wItemID, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
3700

3701
    /* Get the Popupmenu to access the owner menu */
3702
    if (!(menu = MENU_GetMenu(hMenu)))
3703 3704
	return (UINT)-1;

Alexandre Julliard's avatar
Alexandre Julliard committed
3705
    if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags )))
3706
	return (UINT)-1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3707 3708 3709

    oldflags = item->fState & (MF_GRAYED | MF_DISABLED);
    item->fState ^= (oldflags ^ wFlags) & (MF_GRAYED | MF_DISABLED);
3710

3711 3712 3713 3714
    /* If the close item in the system menu change update the close button */
    if((item->wID == SC_CLOSE) && (oldflags != wFlags))
    {
	if (menu->hSysMenuOwner != 0)
3715
	{
3716
            RECT rc;
3717
	    POPUPMENU* parentMenu;
3718

3719 3720 3721
	    /* Get the parent menu to access*/
	    if (!(parentMenu = MENU_GetMenu(menu->hSysMenuOwner)))
		return (UINT)-1;
3722

3723
            /* Refresh the frame to reflect the change */
3724
            WIN_GetRectangles( parentMenu->hWnd, COORDS_CLIENT, &rc, NULL );
3725 3726
            rc.bottom = 0;
            RedrawWindow(parentMenu->hWnd, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN);
3727
	}
3728
    }
3729

Alexandre Julliard's avatar
Alexandre Julliard committed
3730
    return oldflags;
Alexandre Julliard's avatar
Alexandre Julliard committed
3731 3732 3733
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3734
/*******************************************************************
3735
 *         GetMenuStringA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3736
 */
3737
INT WINAPI GetMenuStringA(
3738 3739 3740 3741
	HMENU hMenu,	/* [in] menuhandle */
	UINT wItemID,	/* [in] menu item (dep. on wFlags) */
	LPSTR str,	/* [out] outbuffer. If NULL, func returns entry length*/
	INT nMaxSiz,	/* [in] length of buffer. if 0, func returns entry len*/
3742
	UINT wFlags	/* [in] MF_ flags */
3743
) {
Alexandre Julliard's avatar
Alexandre Julliard committed
3744
    MENUITEM *item;
Alexandre Julliard's avatar
Alexandre Julliard committed
3745

3746
    TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3747
    if (str && nMaxSiz) str[0] = '\0';
3748 3749 3750 3751
    if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
        SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
        return 0;
    }
3752
    if (!item->text) return 0;
3753
    if (!str || !nMaxSiz) return WideCharToMultiByte( CP_ACP, 0, item->text, -1, NULL, 0, NULL, NULL );
3754 3755
    if (!WideCharToMultiByte( CP_ACP, 0, item->text, -1, str, nMaxSiz, NULL, NULL ))
        str[nMaxSiz-1] = 0;
3756
    TRACE("returning %s\n", debugstr_a(str));
Alexandre Julliard's avatar
Alexandre Julliard committed
3757
    return strlen(str);
Alexandre Julliard's avatar
Alexandre Julliard committed
3758 3759 3760
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3761
/*******************************************************************
3762
 *         GetMenuStringW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3763
 */
3764 3765
INT WINAPI GetMenuStringW( HMENU hMenu, UINT wItemID,
                               LPWSTR str, INT nMaxSiz, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3766 3767 3768
{
    MENUITEM *item;

3769
    TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, wItemID, str, nMaxSiz, wFlags );
3770
    if (str && nMaxSiz) str[0] = '\0';
3771 3772 3773 3774 3775 3776 3777 3778 3779
    if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) {
        SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
        return 0;
    }
    if (!str || !nMaxSiz) return item->text ? strlenW(item->text) : 0;
    if( !(item->text)) {
        str[0] = 0;
        return 0;
    }
3780
    lstrcpynW( str, item->text, nMaxSiz );
3781
    TRACE("returning %s\n", debugstr_w(str));
3782
    return strlenW(str);
Alexandre Julliard's avatar
Alexandre Julliard committed
3783 3784 3785
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3786
/**********************************************************************
3787
 *         HiliteMenuItem    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3788
 */
3789 3790
BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
                                UINT wHilite )
Alexandre Julliard's avatar
Alexandre Julliard committed
3791
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3792
    LPPOPUPMENU menu;
3793
    TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
Alexandre Julliard's avatar
Alexandre Julliard committed
3794
    if (!MENU_FindItem( &hMenu, &wItemID, wHilite )) return FALSE;
3795
    if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3796
    if (menu->FocusedItem == wItemID) return TRUE;
3797
    MENU_HideSubPopups( hWnd, hMenu, FALSE, 0 );
3798
    MENU_SelectItem( hWnd, hMenu, wItemID, TRUE, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
3799
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3800 3801 3802
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3803
/**********************************************************************
3804
 *         GetMenuState    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3805
 */
3806
UINT WINAPI GetMenuState( HMENU hMenu, UINT wItemID, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3807
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3808
    MENUITEM *item;
3809
    TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, wItemID, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
3810
    if (!(item = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3811
    debug_print_menuitem ("  item: ", item, "");
Alexandre Julliard's avatar
Alexandre Julliard committed
3812
    if (item->fType & MF_POPUP)
Alexandre Julliard's avatar
Alexandre Julliard committed
3813
    {
3814
	POPUPMENU *menu = MENU_GetMenu( item->hSubMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
3815
	if (!menu) return -1;
Alexandre Julliard's avatar
Alexandre Julliard committed
3816
	else return (menu->nItems << 8) | ((item->fState|item->fType) & 0xff);
Alexandre Julliard's avatar
Alexandre Julliard committed
3817
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3818
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
3819
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
3820 3821 3822 3823
	/* We used to (from way back then) mask the result to 0xff.  */
	/* I don't know why and it seems wrong as the documented */
	/* return flag MF_SEPARATOR is outside that mask.  */
	return (item->fType | item->fState);
Alexandre Julliard's avatar
Alexandre Julliard committed
3824
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3825 3826 3827
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3828
/**********************************************************************
3829
 *         GetMenuItemCount    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3830
 */
3831
INT WINAPI GetMenuItemCount( HMENU hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
3832
{
3833 3834
    LPPOPUPMENU	menu = MENU_GetMenu(hMenu);
    if (!menu) return -1;
3835
    TRACE("(%p) returning %d\n", hMenu, menu->nItems );
Alexandre Julliard's avatar
Alexandre Julliard committed
3836 3837 3838
    return menu->nItems;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
3839 3840

/**********************************************************************
3841
 *         GetMenuItemID    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3842
 */
3843
UINT WINAPI GetMenuItemID( HMENU hMenu, INT nPos )
Alexandre Julliard's avatar
Alexandre Julliard committed
3844
{
3845
    MENUITEM * lpmi;
Alexandre Julliard's avatar
Alexandre Julliard committed
3846

3847
    if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
3848 3849
    if (lpmi->fType & MF_POPUP) return -1;
    return lpmi->wID;
Alexandre Julliard's avatar
Alexandre Julliard committed
3850

3851
}
Alexandre Julliard's avatar
Alexandre Julliard committed
3852

Alexandre Julliard's avatar
Alexandre Julliard committed
3853

3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879
/**********************************************************************
 *         MENU_mnu2mnuii
 *
 * Uses flags, id and text ptr, passed by InsertMenu() and
 * ModifyMenu() to setup a MenuItemInfo structure.
 */
static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str,
        LPMENUITEMINFOW pmii)
{
    ZeroMemory( pmii, sizeof( MENUITEMINFOW));
    pmii->cbSize = sizeof( MENUITEMINFOW);
    pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
    /* setting bitmap clears text and vice versa */
    if( IS_STRING_ITEM(flags)) {
        pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
        if( !str)
            flags |= MF_SEPARATOR;
        /* Item beginning with a backspace is a help item */
        /* FIXME: wrong place, this is only true in win16 */
        else if( *str == '\b') {
            flags |= MF_HELP;
            str++;
        }
        pmii->dwTypeData = (LPWSTR)str;
    } else if( flags & MFT_BITMAP){
        pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
3880
        pmii->hbmpItem = (HBITMAP)str;
3881 3882 3883 3884 3885
    }
    if( flags & MF_OWNERDRAW){
        pmii->fMask |= MIIM_DATA;
        pmii->dwItemData = (ULONG_PTR) str;
    }
3886
    if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) {
3887 3888 3889 3890 3891 3892 3893 3894 3895 3896
        pmii->fMask |= MIIM_SUBMENU;
        pmii->hSubMenu = (HMENU)id;
    }
    if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
    pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
    pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
    pmii->wID = (UINT)id;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3897
/*******************************************************************
3898
 *         InsertMenuW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3899
 */
3900
BOOL WINAPI InsertMenuW( HMENU hMenu, UINT pos, UINT flags,
3901
                         UINT_PTR id, LPCWSTR str )
Alexandre Julliard's avatar
Alexandre Julliard committed
3902
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3903
    MENUITEM *item;
3904
    MENUITEMINFOW mii;
Alexandre Julliard's avatar
Alexandre Julliard committed
3905

Alexandre Julliard's avatar
Alexandre Julliard committed
3906
    if (IS_STRING_ITEM(flags) && str)
3907
        TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %s\n",
3908
              hMenu, pos, flags, id, debugstr_w(str) );
3909
    else TRACE("hMenu %p, pos %d, flags %08x, id %04lx, str %p (not a string)\n",
3910
               hMenu, pos, flags, id, str );
Alexandre Julliard's avatar
Alexandre Julliard committed
3911

Alexandre Julliard's avatar
Alexandre Julliard committed
3912
    if (!(item = MENU_InsertItem( hMenu, pos, flags ))) return FALSE;
3913 3914
    MENU_mnu2mnuii( flags, id, str, &mii);
    if (!(SetMenuItemInfo_common( item, &mii, TRUE)))
Alexandre Julliard's avatar
Alexandre Julliard committed
3915
    {
3916
        RemoveMenu( hMenu, pos, flags );
Alexandre Julliard's avatar
Alexandre Julliard committed
3917 3918
        return FALSE;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
3919

Alexandre Julliard's avatar
Alexandre Julliard committed
3920
    item->hCheckBit = item->hUnCheckBit = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
3921
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3922 3923 3924
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3925
/*******************************************************************
3926
 *         InsertMenuA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3927
 */
3928
BOOL WINAPI InsertMenuA( HMENU hMenu, UINT pos, UINT flags,
3929
                         UINT_PTR id, LPCSTR str )
Alexandre Julliard's avatar
Alexandre Julliard committed
3930
{
3931
    BOOL ret = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
3932 3933 3934

    if (IS_STRING_ITEM(flags) && str)
    {
3935 3936 3937 3938 3939 3940 3941 3942
        INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
        LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
        if (newstr)
        {
            MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
            ret = InsertMenuW( hMenu, pos, flags, id, newstr );
            HeapFree( GetProcessHeap(), 0, newstr );
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
3943 3944
        return ret;
    }
3945
    else return InsertMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
Alexandre Julliard's avatar
Alexandre Julliard committed
3946 3947 3948 3949
}


/*******************************************************************
3950
 *         AppendMenuA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3951
 */
3952
BOOL WINAPI AppendMenuA( HMENU hMenu, UINT flags,
3953
                         UINT_PTR id, LPCSTR data )
Alexandre Julliard's avatar
Alexandre Julliard committed
3954
{
3955
    return InsertMenuA( hMenu, -1, flags | MF_BYPOSITION, id, data );
Alexandre Julliard's avatar
Alexandre Julliard committed
3956 3957 3958 3959
}


/*******************************************************************
3960
 *         AppendMenuW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3961
 */
3962
BOOL WINAPI AppendMenuW( HMENU hMenu, UINT flags,
3963
                         UINT_PTR id, LPCWSTR data )
Alexandre Julliard's avatar
Alexandre Julliard committed
3964
{
3965
    return InsertMenuW( hMenu, -1, flags | MF_BYPOSITION, id, data );
Alexandre Julliard's avatar
Alexandre Julliard committed
3966 3967 3968
}


Alexandre Julliard's avatar
Alexandre Julliard committed
3969
/**********************************************************************
3970
 *         RemoveMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
3971
 */
3972
BOOL WINAPI RemoveMenu( HMENU hMenu, UINT nPos, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
3973
{
Alexandre Julliard's avatar
Alexandre Julliard committed
3974
    LPPOPUPMENU	menu;
Alexandre Julliard's avatar
Alexandre Julliard committed
3975 3976
    MENUITEM *item;

3977
    TRACE("(menu=%p pos=%04x flags=%04x)\n",hMenu, nPos, wFlags);
Alexandre Julliard's avatar
Alexandre Julliard committed
3978
    if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
3979
    if (!(menu = MENU_GetMenu(hMenu))) return FALSE;
3980

Alexandre Julliard's avatar
Alexandre Julliard committed
3981 3982
      /* Remove item */

Alexandre Julliard's avatar
Alexandre Julliard committed
3983 3984
    MENU_FreeItemData( item );

Alexandre Julliard's avatar
Alexandre Julliard committed
3985 3986
    if (--menu->nItems == 0)
    {
3987
        HeapFree( GetProcessHeap(), 0, menu->items );
Alexandre Julliard's avatar
Alexandre Julliard committed
3988
        menu->items = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
3989 3990 3991 3992 3993
    }
    else
    {
	while(nPos < menu->nItems)
	{
Alexandre Julliard's avatar
Alexandre Julliard committed
3994 3995
	    *item = *(item+1);
	    item++;
Alexandre Julliard's avatar
Alexandre Julliard committed
3996 3997
	    nPos++;
	}
3998
        menu->items = HeapReAlloc( GetProcessHeap(), 0, menu->items,
Alexandre Julliard's avatar
Alexandre Julliard committed
3999
                                   menu->nItems * sizeof(MENUITEM) );
Alexandre Julliard's avatar
Alexandre Julliard committed
4000 4001
    }
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4002 4003 4004
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4005
/**********************************************************************
4006
 *         DeleteMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4007
 */
4008
BOOL WINAPI DeleteMenu( HMENU hMenu, UINT nPos, UINT wFlags )
Alexandre Julliard's avatar
Alexandre Julliard committed
4009
{
Alexandre Julliard's avatar
Alexandre Julliard committed
4010 4011
    MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
    if (!item) return FALSE;
4012
    if (item->fType & MF_POPUP) DestroyMenu( item->hSubMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
4013
      /* nPos is now the position of the item */
4014
    RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
Alexandre Julliard's avatar
Alexandre Julliard committed
4015 4016 4017 4018
    return TRUE;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4019
/*******************************************************************
4020
 *         ModifyMenuW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4021
 */
4022
BOOL WINAPI ModifyMenuW( HMENU hMenu, UINT pos, UINT flags,
4023
                         UINT_PTR id, LPCWSTR str )
Alexandre Julliard's avatar
Alexandre Julliard committed
4024
{
Alexandre Julliard's avatar
Alexandre Julliard committed
4025
    MENUITEM *item;
4026
    MENUITEMINFOW mii;
Alexandre Julliard's avatar
Alexandre Julliard committed
4027 4028

    if (IS_STRING_ITEM(flags))
4029
        TRACE("%p %d %04x %04lx %s\n", hMenu, pos, flags, id, debugstr_w(str) );
Alexandre Julliard's avatar
Alexandre Julliard committed
4030
    else
4031
        TRACE("%p %d %04x %04lx %p\n", hMenu, pos, flags, id, str );
Alexandre Julliard's avatar
Alexandre Julliard committed
4032

4033 4034 4035 4036 4037 4038
    if (!(item = MENU_FindItem( &hMenu, &pos, flags )))
    {
        /* workaround for Word 95: pretend that SC_TASKLIST item exists */
        if (pos == SC_TASKLIST && !(flags & MF_BYPOSITION)) return TRUE;
        return FALSE;
    }
4039
    MENU_GetMenu(hMenu)->Height = 0; /* force size recalculate */
4040 4041
    MENU_mnu2mnuii( flags, id, str, &mii);
    return SetMenuItemInfo_common( item, &mii, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
4042 4043 4044 4045
}


/*******************************************************************
4046
 *         ModifyMenuA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4047
 */
4048
BOOL WINAPI ModifyMenuA( HMENU hMenu, UINT pos, UINT flags,
4049
                         UINT_PTR id, LPCSTR str )
Alexandre Julliard's avatar
Alexandre Julliard committed
4050
{
4051
    BOOL ret = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4052

Alexandre Julliard's avatar
Alexandre Julliard committed
4053 4054
    if (IS_STRING_ITEM(flags) && str)
    {
4055 4056 4057 4058 4059 4060 4061 4062
        INT len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
        LPWSTR newstr = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
        if (newstr)
        {
            MultiByteToWideChar( CP_ACP, 0, str, -1, newstr, len );
            ret = ModifyMenuW( hMenu, pos, flags, id, newstr );
            HeapFree( GetProcessHeap(), 0, newstr );
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
4063 4064
        return ret;
    }
4065
    else return ModifyMenuW( hMenu, pos, flags, id, (LPCWSTR)str );
Alexandre Julliard's avatar
Alexandre Julliard committed
4066 4067 4068
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4069
/**********************************************************************
4070
 *         CreatePopupMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4071
 */
4072
HMENU WINAPI CreatePopupMenu(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
4073
{
4074
    HMENU hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4075 4076
    POPUPMENU *menu;

4077
    if (!(hmenu = CreateMenu())) return 0;
4078
    menu = MENU_GetMenu( hmenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
4079 4080
    menu->wFlags |= MF_POPUP;
    return hmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4081 4082 4083 4084
}


/**********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
4085 4086
 *         GetMenuCheckMarkDimensions    (USER.417)
 *         GetMenuCheckMarkDimensions    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4087
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
4088
DWORD WINAPI GetMenuCheckMarkDimensions(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
4089
{
4090
    return MAKELONG( GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK) );
Alexandre Julliard's avatar
Alexandre Julliard committed
4091 4092 4093
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4094
/**********************************************************************
4095
 *         SetMenuItemBitmaps    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4096
 */
4097 4098
BOOL WINAPI SetMenuItemBitmaps( HMENU hMenu, UINT nPos, UINT wFlags,
                                    HBITMAP hNewUnCheck, HBITMAP hNewCheck)
Alexandre Julliard's avatar
Alexandre Julliard committed
4099
{
Alexandre Julliard's avatar
Alexandre Julliard committed
4100
    MENUITEM *item;
4101

Alexandre Julliard's avatar
Alexandre Julliard committed
4102
    if (!(item = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4103 4104 4105

    if (!hNewCheck && !hNewUnCheck)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
4106
	item->fState &= ~MF_USECHECKBITMAPS;
Alexandre Julliard's avatar
Alexandre Julliard committed
4107 4108 4109
    }
    else  /* Install new bitmaps */
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
4110 4111 4112
	item->hCheckBit = hNewCheck;
	item->hUnCheckBit = hNewUnCheck;
	item->fState |= MF_USECHECKBITMAPS;
Alexandre Julliard's avatar
Alexandre Julliard committed
4113 4114
    }
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4115 4116 4117
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4118
/**********************************************************************
4119
 *         CreateMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4120
 */
4121
HMENU WINAPI CreateMenu(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
4122
{
4123
    HMENU hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4124
    LPPOPUPMENU menu;
4125

4126
    if (!(menu = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*menu) ))) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
4127
    menu->FocusedItem = NO_SELECTED_ITEM;
4128

4129 4130
    if (!(hMenu = alloc_user_handle( &menu->obj, USER_MENU ))) HeapFree( GetProcessHeap(), 0, menu );

4131
    TRACE("return %p\n", hMenu );
4132

Alexandre Julliard's avatar
Alexandre Julliard committed
4133
    return hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4134 4135 4136
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4137
/**********************************************************************
4138
 *         DestroyMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4139
 */
4140
BOOL WINAPI DestroyMenu( HMENU hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
4141
{
4142
    LPPOPUPMENU lppop;
Alexandre Julliard's avatar
Alexandre Julliard committed
4143

4144
    TRACE("(%p)\n", hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
4145

4146 4147
    if (!(lppop = free_user_handle( hMenu, USER_MENU ))) return FALSE;
    if (lppop == OBJ_OTHER_PROCESS) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4148

4149 4150 4151 4152 4153 4154
    /* DestroyMenu should not destroy system menu popup owner */
    if ((lppop->wFlags & (MF_POPUP | MF_SYSMENU)) == MF_POPUP && lppop->hWnd)
    {
        DestroyWindow( lppop->hWnd );
        lppop->hWnd = 0;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4155

4156 4157 4158 4159 4160
    if (lppop->items) /* recursively destroy submenus */
    {
        int i;
        MENUITEM *item = lppop->items;
        for (i = lppop->nItems; i > 0; i--, item++)
4161
        {
4162 4163
            if (item->fType & MF_POPUP) DestroyMenu(item->hSubMenu);
            MENU_FreeItemData( item );
4164
        }
4165
        HeapFree( GetProcessHeap(), 0, lppop->items );
Alexandre Julliard's avatar
Alexandre Julliard committed
4166
    }
4167
    HeapFree( GetProcessHeap(), 0, lppop );
4168
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4169 4170
}

Alexandre Julliard's avatar
Alexandre Julliard committed
4171

Alexandre Julliard's avatar
Alexandre Julliard committed
4172
/**********************************************************************
4173
 *         GetSystemMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4174
 */
4175
HMENU WINAPI GetSystemMenu( HWND hWnd, BOOL bRevert )
Alexandre Julliard's avatar
Alexandre Julliard committed
4176
{
4177
    WND *wndPtr = WIN_GetPtr( hWnd );
4178
    HMENU retvalue = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
4179

4180
    if (wndPtr == WND_DESKTOP) return 0;
4181 4182 4183 4184 4185
    if (wndPtr == WND_OTHER_PROCESS)
    {
        if (IsWindow( hWnd )) FIXME( "not supported on other process window %p\n", hWnd );
    }
    else if (wndPtr)
Alexandre Julliard's avatar
Alexandre Julliard committed
4186
    {
4187
	if (wndPtr->hSysMenu && bRevert)
Alexandre Julliard's avatar
Alexandre Julliard committed
4188
	{
4189 4190
            DestroyMenu(wndPtr->hSysMenu);
            wndPtr->hSysMenu = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
4191 4192 4193
	}

	if(!wndPtr->hSysMenu && (wndPtr->dwStyle & WS_SYSMENU) )
4194
	    wndPtr->hSysMenu = MENU_GetSysMenu( hWnd, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
4195 4196

	if( wndPtr->hSysMenu )
4197
        {
4198
	    POPUPMENU *menu;
4199
	    retvalue = GetSubMenu(wndPtr->hSysMenu, 0);
4200 4201 4202

	    /* Store the dummy sysmenu handle to facilitate the refresh */
	    /* of the close button if the SC_CLOSE item change */
4203 4204
	    menu = MENU_GetMenu(retvalue);
	    if ( menu )
4205
	       menu->hSysMenuOwner = wndPtr->hSysMenu;
4206
        }
4207
        WIN_ReleasePtr( wndPtr );
Alexandre Julliard's avatar
Alexandre Julliard committed
4208
    }
4209
    return bRevert ? 0 : retvalue;
Alexandre Julliard's avatar
Alexandre Julliard committed
4210 4211
}

Alexandre Julliard's avatar
Alexandre Julliard committed
4212

Alexandre Julliard's avatar
Alexandre Julliard committed
4213
/*******************************************************************
4214
 *         SetSystemMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4215
 */
4216
BOOL WINAPI SetSystemMenu( HWND hwnd, HMENU hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
4217
{
4218
    WND *wndPtr = WIN_GetPtr( hwnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
4219

4220
    if (wndPtr && wndPtr != WND_OTHER_PROCESS && wndPtr != WND_DESKTOP)
Alexandre Julliard's avatar
Alexandre Julliard committed
4221
    {
4222
	if (wndPtr->hSysMenu) DestroyMenu( wndPtr->hSysMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
4223
	wndPtr->hSysMenu = MENU_GetSysMenu( hwnd, hMenu );
4224
        WIN_ReleasePtr( wndPtr );
Alexandre Julliard's avatar
Alexandre Julliard committed
4225 4226 4227
	return TRUE;
    }
    return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4228 4229
}

Alexandre Julliard's avatar
Alexandre Julliard committed
4230

Alexandre Julliard's avatar
Alexandre Julliard committed
4231
/**********************************************************************
4232
 *         GetMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4233
 */
4234
HMENU WINAPI GetMenu( HWND hWnd )
4235
{
4236
    HMENU retvalue = (HMENU)GetWindowLongPtrW( hWnd, GWLP_ID );
4237
    TRACE("for %p returning %p\n", hWnd, retvalue);
4238
    return retvalue;
Alexandre Julliard's avatar
Alexandre Julliard committed
4239 4240
}

4241 4242 4243 4244 4245
/**********************************************************************
 *         GetMenuBarInfo    (USER32.@)
 */
BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
{
4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325
    POPUPMENU *menu;
    HMENU hmenu = NULL;
    ATOM class_atom;

    TRACE( "(%p,0x%08x,0x%08x,%p)\n", hwnd, idObject, idItem, pmbi );

    switch (idObject)
    {
    case OBJID_CLIENT:
        class_atom = GetClassLongW(hwnd, GCW_ATOM);
        if (!class_atom)
            return FALSE;
        if (class_atom != POPUPMENU_CLASS_ATOM)
        {
            WARN("called on invalid window: %d\n", class_atom);
            SetLastError(ERROR_INVALID_MENU_HANDLE);
            return FALSE;
        }

        hmenu = (HMENU)GetWindowLongPtrW(hwnd, 0);
        break;
    case OBJID_MENU:
        hmenu = GetMenu(hwnd);
        break;
    case OBJID_SYSMENU:
        hmenu = GetSystemMenu(hwnd, FALSE);
        break;
    default:
        return FALSE;
    }

    if (!hmenu)
        return FALSE;

    if (pmbi->cbSize != sizeof(MENUBARINFO))
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    menu = MENU_GetMenu(hmenu);
    if (!menu)
        return FALSE;
    if (idItem < 0 || idItem > menu->nItems)
        return FALSE;

    if (!menu->Height)
    {
        SetRectEmpty(&pmbi->rcBar);
    }
    else if (idItem == 0)
    {
        GetMenuItemRect(hwnd, hmenu, 0, &pmbi->rcBar);
        pmbi->rcBar.right = pmbi->rcBar.left + menu->Width;
        pmbi->rcBar.bottom = pmbi->rcBar.top + menu->Height;
    }
    else
    {
        GetMenuItemRect(hwnd, hmenu, idItem - 1, &pmbi->rcBar);
    }

    pmbi->hMenu = hmenu;
    pmbi->hwndMenu = NULL;
    pmbi->fBarFocused = top_popup_hmenu == hmenu;
    if (idItem)
    {
        pmbi->fFocused = menu->FocusedItem == idItem - 1;
        if (pmbi->fFocused && (menu->items[idItem - 1].fType & MF_POPUP))
        {
            menu = MENU_GetMenu(menu->items[idItem - 1].hSubMenu);
            if (menu)
                pmbi->hwndMenu = menu->hWnd;
        }
    }
    else
    {
        pmbi->fFocused = pmbi->fBarFocused;
    }

    return TRUE;
4326
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4327

Alexandre Julliard's avatar
Alexandre Julliard committed
4328
/**********************************************************************
4329 4330 4331 4332
 *         MENU_SetMenu
 *
 * Helper for SetMenu. Also called by WIN_CreateWindowEx to avoid the
 * SetWindowPos call that would result if SetMenu were called directly.
Alexandre Julliard's avatar
Alexandre Julliard committed
4333
 */
4334
BOOL MENU_SetMenu( HWND hWnd, HMENU hMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
4335
{
4336
    TRACE("(%p, %p);\n", hWnd, hMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
4337

4338 4339
    if (hMenu && !IsMenu(hMenu))
    {
4340
        WARN("hMenu %p is not a menu handle\n", hMenu);
4341
        return FALSE;
4342
    }
4343 4344
    if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
        return FALSE;
4345

4346
    hWnd = WIN_GetFullHandle( hWnd );
4347 4348
    if (GetCapture() == hWnd)
        set_capture_window( 0, GUI_INMENUMODE, NULL );  /* release the capture */
Alexandre Julliard's avatar
Alexandre Julliard committed
4349

4350 4351 4352
    if (hMenu != 0)
    {
        LPPOPUPMENU lpmenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4353

4354
        if (!(lpmenu = MENU_GetMenu(hMenu))) return FALSE;
4355

4356 4357
        lpmenu->hWnd = hWnd;
        lpmenu->Height = 0;  /* Make sure we recalculate the size */
Alexandre Julliard's avatar
Alexandre Julliard committed
4358
    }
4359
    SetWindowLongPtrW( hWnd, GWLP_ID, (LONG_PTR)hMenu );
4360
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4361 4362 4363
}


4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376
/**********************************************************************
 *         SetMenu    (USER32.@)
 */
BOOL WINAPI SetMenu( HWND hWnd, HMENU hMenu )
{   
    if(!MENU_SetMenu(hWnd, hMenu))
        return FALSE;
 
    SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
                  SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
    return TRUE;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
4377

Alexandre Julliard's avatar
Alexandre Julliard committed
4378
/**********************************************************************
4379
 *         GetSubMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4380
 */
4381
HMENU WINAPI GetSubMenu( HMENU hMenu, INT nPos )
Alexandre Julliard's avatar
Alexandre Julliard committed
4382
{
4383
    MENUITEM * lpmi;
Alexandre Julliard's avatar
Alexandre Julliard committed
4384

4385
    if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return 0;
4386 4387
    if (!(lpmi->fType & MF_POPUP)) return 0;
    return lpmi->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4388 4389 4390
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4391
/**********************************************************************
4392
 *         DrawMenuBar    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4393
 */
4394
BOOL WINAPI DrawMenuBar( HWND hWnd )
Alexandre Julliard's avatar
Alexandre Julliard committed
4395 4396
{
    LPPOPUPMENU lppop;
4397
    HMENU hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4398

4399
    if (!IsWindow( hWnd ))
4400
        return FALSE;
4401 4402
    if (!WIN_ALLOWED_MENU(GetWindowLongW( hWnd, GWL_STYLE )))
        return TRUE;
4403

4404 4405 4406 4407 4408 4409 4410
    if ((hMenu = GetMenu( hWnd )) && (lppop = MENU_GetMenu( hMenu ))) {
        lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
        lppop->hwndOwner = hWnd;
    }

    return SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
                         SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
Alexandre Julliard's avatar
Alexandre Julliard committed
4411 4412
}

4413 4414
/***********************************************************************
 *           DrawMenuBarTemp   (USER32.@)
4415 4416
 *
 * UNDOCUMENTED !!
4417
 *
4418 4419 4420
 * called by W98SE desk.cpl Control Panel Applet
 *
 * Not 100% sure about the param names, but close.
4421
 */
4422
DWORD WINAPI DrawMenuBarTemp(HWND hwnd, HDC hDC, LPRECT lprect, HMENU hMenu, HFONT hFont)
4423
{
4424 4425 4426
    LPPOPUPMENU lppop;
    UINT i,retvalue;
    HFONT hfontOld = 0;
4427 4428 4429
    BOOL flat_menu = FALSE;

    SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
4430 4431 4432 4433 4434

    if (!hMenu)
        hMenu = GetMenu(hwnd);

    if (!hFont)
4435
        hFont = get_menu_font(FALSE);
4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452

    lppop = MENU_GetMenu( hMenu );
    if (lppop == NULL || lprect == NULL)
    {
        retvalue = GetSystemMetrics(SM_CYMENU);
        goto END;
    }

    TRACE("(%p, %p, %p, %p, %p)\n", hwnd, hDC, lprect, hMenu, hFont);

    hfontOld = SelectObject( hDC, hFont);

    if (lppop->Height == 0)
        MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);

    lprect->bottom = lprect->top + lppop->Height;

4453
    FillRect(hDC, lprect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU) );
4454

4455 4456 4457
    SelectObject( hDC, SYSCOLOR_GetPen(COLOR_3DFACE));
    MoveToEx( hDC, lprect->left, lprect->bottom, NULL );
    LineTo( hDC, lprect->right, lprect->bottom );
4458 4459 4460 4461 4462 4463 4464 4465

    if (lppop->nItems == 0)
    {
        retvalue = GetSystemMetrics(SM_CYMENU);
        goto END;
    }

    for (i = 0; i < lppop->nItems; i++)
4466
        MENU_DrawMenuItem( hwnd, lppop, hwnd, hDC, &lppop->items[i], TRUE, ODA_DRAWENTIRE );
4467

4468 4469 4470 4471 4472
    retvalue = lppop->Height;

END:
    if (hfontOld) SelectObject (hDC, hfontOld);
    return retvalue;
4473
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4474

Alexandre Julliard's avatar
Alexandre Julliard committed
4475
/***********************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
4476 4477
 *           EndMenu   (USER.187)
 *           EndMenu   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4478
 */
4479
BOOL WINAPI EndMenu(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
4480
{
4481
    /* if we are in the menu code, and it is active */
4482
    if (!fEndMenu && top_popup)
4483 4484 4485 4486 4487 4488 4489 4490
    {
	/* terminate the menu handling code */
        fEndMenu = TRUE;

	/* needs to be posted to wakeup the internal menu handler */
	/* which will now terminate the menu, in the event that */
	/* the main window was minimized, or lost focus, so we */
	/* don't end up with an orphaned menu */
4491
        PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
4492
    }
4493
    return fEndMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4494 4495 4496
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4497
/*****************************************************************
4498
 *        LoadMenuA   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4499
 */
4500
HMENU WINAPI LoadMenuA( HINSTANCE instance, LPCSTR name )
Alexandre Julliard's avatar
Alexandre Julliard committed
4501
{
4502
    HRSRC hrsrc = FindResourceA( instance, name, (LPSTR)RT_MENU );
Alexandre Julliard's avatar
Alexandre Julliard committed
4503
    if (!hrsrc) return 0;
4504
    return LoadMenuIndirectA( LoadResource( instance, hrsrc ));
Alexandre Julliard's avatar
Alexandre Julliard committed
4505 4506 4507 4508
}


/*****************************************************************
4509
 *        LoadMenuW   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4510
 */
4511
HMENU WINAPI LoadMenuW( HINSTANCE instance, LPCWSTR name )
Alexandre Julliard's avatar
Alexandre Julliard committed
4512
{
4513
    HRSRC hrsrc = FindResourceW( instance, name, (LPWSTR)RT_MENU );
Alexandre Julliard's avatar
Alexandre Julliard committed
4514
    if (!hrsrc) return 0;
4515
    return LoadMenuIndirectW( LoadResource( instance, hrsrc ));
Alexandre Julliard's avatar
Alexandre Julliard committed
4516 4517 4518
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4519
/**********************************************************************
4520
 *	    LoadMenuIndirectW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4521
 */
4522
HMENU WINAPI LoadMenuIndirectW( LPCVOID template )
Alexandre Julliard's avatar
Alexandre Julliard committed
4523
{
4524
    HMENU hMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4525
    WORD version, offset;
4526
    LPCSTR p = template;
Alexandre Julliard's avatar
Alexandre Julliard committed
4527 4528 4529

    version = GET_WORD(p);
    p += sizeof(WORD);
4530
    TRACE("%p, ver %d\n", template, version );
Alexandre Julliard's avatar
Alexandre Julliard committed
4531
    switch (version)
4532
    {
4533
      case 0: /* standard format is version of 0 */
Alexandre Julliard's avatar
Alexandre Julliard committed
4534 4535
	offset = GET_WORD(p);
	p += sizeof(WORD) + offset;
4536
	if (!(hMenu = CreateMenu())) return 0;
4537
	if (!MENU_ParseResource( p, hMenu ))
Alexandre Julliard's avatar
Alexandre Julliard committed
4538
	  {
4539
	    DestroyMenu( hMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
4540 4541 4542
	    return 0;
	  }
	return hMenu;
4543
      case 1: /* extended format is version of 1 */
Alexandre Julliard's avatar
Alexandre Julliard committed
4544 4545
	offset = GET_WORD(p);
	p += sizeof(WORD) + offset;
4546
	if (!(hMenu = CreateMenu())) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
4547 4548
	if (!MENUEX_ParseResource( p, hMenu))
	  {
4549
	    DestroyMenu( hMenu );
Alexandre Julliard's avatar
Alexandre Julliard committed
4550 4551 4552 4553
	    return 0;
	  }
	return hMenu;
      default:
4554
        ERR("version %d not supported.\n", version);
Alexandre Julliard's avatar
Alexandre Julliard committed
4555
        return 0;
4556
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4557 4558 4559 4560
}


/**********************************************************************
4561
 *	    LoadMenuIndirectA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4562
 */
4563
HMENU WINAPI LoadMenuIndirectA( LPCVOID template )
Alexandre Julliard's avatar
Alexandre Julliard committed
4564
{
4565
    return LoadMenuIndirectW( template );
Alexandre Julliard's avatar
Alexandre Julliard committed
4566 4567 4568
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4569
/**********************************************************************
4570
 *		IsMenu    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4571
 */
4572
BOOL WINAPI IsMenu(HMENU hmenu)
Alexandre Julliard's avatar
Alexandre Julliard committed
4573
{
4574
    LPPOPUPMENU menu = MENU_GetMenu(hmenu);
4575 4576 4577 4578 4579 4580 4581

    if (!menu)
    {
        SetLastError(ERROR_INVALID_MENU_HANDLE);
        return FALSE;
    }
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4582 4583
}

Alexandre Julliard's avatar
Alexandre Julliard committed
4584
/**********************************************************************
4585
 *		GetMenuItemInfo_common
Alexandre Julliard's avatar
Alexandre Julliard committed
4586 4587
 */

4588
static BOOL GetMenuItemInfo_common ( HMENU hmenu, UINT item, BOOL bypos,
4589
					LPMENUITEMINFOW lpmii, BOOL unicode)
Alexandre Julliard's avatar
Alexandre Julliard committed
4590
{
4591
    MENUITEM *menu = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
4592

4593
    debug_print_menuitem("GetMenuItemInfo_common: ", menu, "");
4594

4595 4596 4597 4598
    if (!menu) {
        SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
        return FALSE;
    }
4599

4600 4601
    if( lpmii->fMask & MIIM_TYPE) {
        if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) {
4602 4603
            WARN("invalid combination of fMask bits used\n");
            /* this does not happen on Win9x/ME */
4604 4605 4606
            SetLastError( ERROR_INVALID_PARAMETER);
            return FALSE;
        }
4607
	lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4608 4609
        if (menu->hbmpItem && !IS_MAGIC_BITMAP(menu->hbmpItem))
            lpmii->fType |= MFT_BITMAP;
4610
	lpmii->hbmpItem = menu->hbmpItem; /* not on Win9x/ME */
4611 4612
        if( lpmii->fType & MFT_BITMAP) {
	    lpmii->dwTypeData = (LPWSTR) menu->hbmpItem;
4613
	    lpmii->cch = 0;
4614
        } else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) {
4615
            /* this does not happen on Win9x/ME */
4616 4617 4618
	    lpmii->dwTypeData = 0;
	    lpmii->cch = 0;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
4619
    }
4620

4621
    /* copy the text string */
4622 4623 4624 4625 4626 4627 4628 4629
    if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) {
         if( !menu->text ) {
                if(lpmii->dwTypeData && lpmii->cch) {
                    if( unicode)
                        *((WCHAR *)lpmii->dwTypeData) = 0;
                    else
                        *((CHAR *)lpmii->dwTypeData) = 0;
                }
4630
                lpmii->cch = 0;
4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648
         } else {
            int len;
            if (unicode)
            {
                len = strlenW(menu->text);
                if(lpmii->dwTypeData && lpmii->cch)
                    lstrcpynW(lpmii->dwTypeData, menu->text, lpmii->cch);
            }
            else
            {
                len = WideCharToMultiByte( CP_ACP, 0, menu->text, -1, NULL,
                        0, NULL, NULL ) - 1;
                if(lpmii->dwTypeData && lpmii->cch)
                    if (!WideCharToMultiByte( CP_ACP, 0, menu->text, -1,
                            (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
                        ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
            }
            /* if we've copied a substring we return its length */
4649
            if(lpmii->dwTypeData && lpmii->cch)
4650 4651 4652 4653
                if (lpmii->cch <= len + 1)
                    lpmii->cch--;
                else
                    lpmii->cch = len;
4654 4655 4656
            else {
                /* return length of string */
                /* not on Win9x/ME if fType & MFT_BITMAP */
4657
                lpmii->cch = len;
4658
            }
4659
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
4660
    }
4661 4662

    if (lpmii->fMask & MIIM_FTYPE)
4663
	lpmii->fType = menu->fType & MENUITEMINFO_TYPE_MASK;
4664 4665 4666 4667

    if (lpmii->fMask & MIIM_BITMAP)
	lpmii->hbmpItem = menu->hbmpItem;

Alexandre Julliard's avatar
Alexandre Julliard committed
4668
    if (lpmii->fMask & MIIM_STATE)
4669
	lpmii->fState = menu->fState & MENUITEMINFO_STATE_MASK;
Alexandre Julliard's avatar
Alexandre Julliard committed
4670

Alexandre Julliard's avatar
Alexandre Julliard committed
4671 4672
    if (lpmii->fMask & MIIM_ID)
	lpmii->wID = menu->wID;
Alexandre Julliard's avatar
Alexandre Julliard committed
4673

Alexandre Julliard's avatar
Alexandre Julliard committed
4674 4675
    if (lpmii->fMask & MIIM_SUBMENU)
	lpmii->hSubMenu = menu->hSubMenu;
4676 4677 4678 4679 4680
    else {
        /* hSubMenu is always cleared 
         * (not on Win9x/ME ) */
        lpmii->hSubMenu = 0;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4681

Alexandre Julliard's avatar
Alexandre Julliard committed
4682 4683 4684
    if (lpmii->fMask & MIIM_CHECKMARKS) {
	lpmii->hbmpChecked = menu->hCheckBit;
	lpmii->hbmpUnchecked = menu->hUnCheckBit;
Alexandre Julliard's avatar
Alexandre Julliard committed
4685
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4686 4687
    if (lpmii->fMask & MIIM_DATA)
	lpmii->dwItemData = menu->dwItemData;
Alexandre Julliard's avatar
Alexandre Julliard committed
4688 4689 4690 4691 4692

  return TRUE;
}

/**********************************************************************
4693
 *		GetMenuItemInfoA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4694
 */
4695 4696
BOOL WINAPI GetMenuItemInfoA( HMENU hmenu, UINT item, BOOL bypos,
                                  LPMENUITEMINFOA lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
4697
{
4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711
    BOOL ret;
    MENUITEMINFOA mii;
    if( lpmii->cbSize != sizeof( mii) &&
            lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
        SetLastError( ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    memcpy( &mii, lpmii, lpmii->cbSize);
    mii.cbSize = sizeof( mii);
    ret = GetMenuItemInfo_common (hmenu, item, bypos,
                                    (LPMENUITEMINFOW)&mii, FALSE);
    mii.cbSize = lpmii->cbSize;
    memcpy( lpmii, &mii, mii.cbSize);
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
4712 4713 4714
}

/**********************************************************************
4715
 *		GetMenuItemInfoW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4716
 */
4717 4718
BOOL WINAPI GetMenuItemInfoW( HMENU hmenu, UINT item, BOOL bypos,
                                  LPMENUITEMINFOW lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
4719
{
4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732
    BOOL ret;
    MENUITEMINFOW mii;
    if( lpmii->cbSize != sizeof( mii) &&
            lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) {
        SetLastError( ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    memcpy( &mii, lpmii, lpmii->cbSize);
    mii.cbSize = sizeof( mii);
    ret = GetMenuItemInfo_common (hmenu, item, bypos, &mii, TRUE);
    mii.cbSize = lpmii->cbSize;
    memcpy( lpmii, &mii, mii.cbSize);
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
4733 4734
}

4735 4736

/* set a menu item text from a ASCII or Unicode string */
4737
static inline void set_menu_item_text( MENUITEM *menu, LPCWSTR text, BOOL unicode )
4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755
{
    if (!text)
        menu->text = NULL;
    else if (unicode)
    {
        if ((menu->text = HeapAlloc( GetProcessHeap(), 0, (strlenW(text)+1) * sizeof(WCHAR) )))
            strcpyW( menu->text, text );
    }
    else
    {
        LPCSTR str = (LPCSTR)text;
        int len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
        if ((menu->text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
            MultiByteToWideChar( CP_ACP, 0, str, -1, menu->text, len );
    }
}


4756 4757 4758 4759 4760 4761 4762
/**********************************************************************
 *		MENU_depth
 *
 * detect if there are loops in the menu tree (or the depth is too large)
 */
static int MENU_depth( POPUPMENU *pmenu, int depth)
{
4763
    UINT i;
4764
    MENUITEM *item;
4765
    int subdepth;
4766 4767 4768 4769

    depth++;
    if( depth > MAXMENUDEPTH) return depth;
    item = pmenu->items;
4770 4771 4772
    subdepth = depth;
    for( i = 0; i < pmenu->nItems && subdepth <= MAXMENUDEPTH; i++, item++){
        POPUPMENU *psubmenu =  item->hSubMenu ? MENU_GetMenu( item->hSubMenu) : NULL;
4773 4774
        if( psubmenu){
            int bdepth = MENU_depth( psubmenu, depth);
4775
            if( bdepth > subdepth) subdepth = bdepth;
4776
        }
4777 4778
        if( subdepth > MAXMENUDEPTH)
            TRACE("<- hmenu %p\n", item->hSubMenu);
4779
    }
4780
    return subdepth;
4781 4782 4783
}


Alexandre Julliard's avatar
Alexandre Julliard committed
4784
/**********************************************************************
4785
 *		SetMenuItemInfo_common
4786 4787 4788
 *
 * Note: does not support the MIIM_TYPE flag. Use the MIIM_FTYPE,
 * MIIM_BITMAP and MIIM_STRING flags instead.
Alexandre Julliard's avatar
Alexandre Julliard committed
4789 4790
 */

4791
static BOOL SetMenuItemInfo_common(MENUITEM * menu,
4792
				       const MENUITEMINFOW *lpmii,
4793
				       BOOL unicode)
Alexandre Julliard's avatar
Alexandre Julliard committed
4794 4795 4796
{
    if (!menu) return FALSE;

4797
    debug_print_menuitem("SetMenuItemInfo_common from: ", menu, "");
4798

4799
    if (lpmii->fMask & MIIM_FTYPE ) {
4800 4801
        menu->fType &= ~MENUITEMINFO_TYPE_MASK;
        menu->fType |= lpmii->fType & MENUITEMINFO_TYPE_MASK;
4802 4803
    }
    if (lpmii->fMask & MIIM_STRING ) {
4804 4805 4806
        /* free the string when used */
        HeapFree(GetProcessHeap(), 0, menu->text);
        set_menu_item_text( menu, lpmii->dwTypeData, unicode );
Alexandre Julliard's avatar
Alexandre Julliard committed
4807
    }
4808

Alexandre Julliard's avatar
Alexandre Julliard committed
4809
    if (lpmii->fMask & MIIM_STATE)
4810 4811 4812
         /* Other menu items having MFS_DEFAULT are not converted
           to normal items */
         menu->fState = lpmii->fState & MENUITEMINFO_STATE_MASK;
Alexandre Julliard's avatar
Alexandre Julliard committed
4813 4814 4815 4816

    if (lpmii->fMask & MIIM_ID)
	menu->wID = lpmii->wID;

Alexandre Julliard's avatar
Alexandre Julliard committed
4817
    if (lpmii->fMask & MIIM_SUBMENU) {
Alexandre Julliard's avatar
Alexandre Julliard committed
4818
	menu->hSubMenu = lpmii->hSubMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
4819
	if (menu->hSubMenu) {
4820
	    POPUPMENU *subMenu = MENU_GetMenu(menu->hSubMenu);
4821
	    if (subMenu) {
4822 4823 4824 4825 4826
                if( MENU_depth( subMenu, 0) > MAXMENUDEPTH) {
                    ERR( "Loop detected in menu hierarchy or maximum menu depth exceeded!\n");
                    menu->hSubMenu = 0;
                    return FALSE;
                }
Alexandre Julliard's avatar
Alexandre Julliard committed
4827 4828
		subMenu->wFlags |= MF_POPUP;
		menu->fType |= MF_POPUP;
4829
	    } else {
4830 4831 4832
                SetLastError( ERROR_INVALID_PARAMETER);
                return FALSE;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
4833 4834 4835 4836
	}
	else
	    menu->fType &= ~MF_POPUP;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
4837 4838 4839 4840 4841 4842 4843 4844 4845

    if (lpmii->fMask & MIIM_CHECKMARKS)
    {
	menu->hCheckBit = lpmii->hbmpChecked;
	menu->hUnCheckBit = lpmii->hbmpUnchecked;
    }
    if (lpmii->fMask & MIIM_DATA)
	menu->dwItemData = lpmii->dwItemData;

4846 4847 4848
    if (lpmii->fMask & MIIM_BITMAP)
	menu->hbmpItem = lpmii->hbmpItem;

4849 4850 4851
    if( !menu->text && !(menu->fType & MFT_OWNERDRAW) && !menu->hbmpItem)
        menu->fType |= MFT_SEPARATOR;

4852
    debug_print_menuitem("SetMenuItemInfo_common to : ", menu, "");
Alexandre Julliard's avatar
Alexandre Julliard committed
4853 4854
    return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
4855

4856 4857 4858 4859 4860 4861 4862 4863 4864 4865
/**********************************************************************
 *		MENU_NormalizeMenuItemInfoStruct
 *
 * Helper for SetMenuItemInfo and InsertMenuItemInfo:
 * check, copy and extend the MENUITEMINFO struct from the version that the application
 * supplied to the version used by wine source. */
static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
                                              MENUITEMINFOW *pmii_out )
{
    /* do we recognize the size? */
4866 4867
    if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
            pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) {
4868 4869 4870 4871 4872 4873 4874 4875 4876 4877
        SetLastError( ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    /* copy the fields that we have */
    memcpy( pmii_out, pmii_in, pmii_in->cbSize);
    /* if the hbmpItem member is missing then extend */
    if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
        pmii_out->cbSize = sizeof( MENUITEMINFOW);
        pmii_out->hbmpItem = NULL;
    }
4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893
    /* test for invalid bit combinations */
    if( (pmii_out->fMask & MIIM_TYPE &&
         pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
        (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
        WARN("invalid combination of fMask bits used\n");
        /* this does not happen on Win9x/ME */
        SetLastError( ERROR_INVALID_PARAMETER);
        return FALSE;
    }
    /* convert old style (MIIM_TYPE) to the new */
    if( pmii_out->fMask & MIIM_TYPE){
        pmii_out->fMask |= MIIM_FTYPE;
        if( IS_STRING_ITEM(pmii_out->fType)){
            pmii_out->fMask |= MIIM_STRING;
        } else if( (pmii_out->fType) & MFT_BITMAP){
            pmii_out->fMask |= MIIM_BITMAP;
4894
            pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
4895 4896
        }
    }
4897 4898 4899
    return TRUE;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
4900
/**********************************************************************
4901
 *		SetMenuItemInfoA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4902
 */
4903
BOOL WINAPI SetMenuItemInfoA(HMENU hmenu, UINT item, BOOL bypos,
4904
                                 const MENUITEMINFOA *lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
4905
{
4906
    MENUITEM *menuitem;
4907
    MENUITEMINFOW mii;
4908 4909 4910

    TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);

4911
    if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
4912

4913 4914 4915 4916 4917 4918 4919
    if (!(menuitem = MENU_FindItem( &hmenu, &item, bypos? MF_BYPOSITION : 0 )))
    {
        /* workaround for Word 95: pretend that SC_TASKLIST item exists */
        if (item == SC_TASKLIST && !bypos) return TRUE;
        return FALSE;
    }
    return SetMenuItemInfo_common( menuitem, &mii, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
4920 4921 4922
}

/**********************************************************************
4923
 *		SetMenuItemInfoW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4924
 */
4925 4926
BOOL WINAPI SetMenuItemInfoW(HMENU hmenu, UINT item, BOOL bypos,
                                 const MENUITEMINFOW *lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
4927
{
4928
    MENUITEM *menuitem;
4929
    MENUITEMINFOW mii;
4930 4931 4932

    TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);

4933
    if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
4934 4935 4936 4937 4938 4939 4940
    if (!(menuitem = MENU_FindItem( &hmenu, &item, bypos? MF_BYPOSITION : 0 )))
    {
        /* workaround for Word 95: pretend that SC_TASKLIST item exists */
        if (item == SC_TASKLIST && !bypos) return TRUE;
        return FALSE;
    }
    return SetMenuItemInfo_common( menuitem, &mii, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
4941 4942 4943
}

/**********************************************************************
4944
 *		SetMenuDefaultItem    (USER32.@)
4945
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
4946
 */
4947
BOOL WINAPI SetMenuDefaultItem(HMENU hmenu, UINT uItem, UINT bypos)
Alexandre Julliard's avatar
Alexandre Julliard committed
4948
{
4949 4950 4951
	UINT i;
	POPUPMENU *menu;
	MENUITEM *item;
4952

4953
	TRACE("(%p,%d,%d)\n", hmenu, uItem, bypos);
Alexandre Julliard's avatar
Alexandre Julliard committed
4954

4955
	if (!(menu = MENU_GetMenu(hmenu))) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4956

4957 4958 4959 4960 4961 4962
	/* reset all default-item flags */
	item = menu->items;
	for (i = 0; i < menu->nItems; i++, item++)
	{
	    item->fState &= ~MFS_DEFAULT;
	}
4963

4964 4965 4966 4967 4968
	/* no default item */
	if ( -1 == uItem)
	{
	    return TRUE;
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
4969

4970 4971 4972 4973 4974 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986
	item = menu->items;
	if ( bypos )
	{
	    if ( uItem >= menu->nItems ) return FALSE;
	    item[uItem].fState |= MFS_DEFAULT;
	    return TRUE;
	}
	else
	{
	    for (i = 0; i < menu->nItems; i++, item++)
	    {
		if (item->wID == uItem)
		{
		     item->fState |= MFS_DEFAULT;
		     return TRUE;
		}
	    }
4987

4988 4989
	}
	return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
4990 4991
}

Alexandre Julliard's avatar
Alexandre Julliard committed
4992
/**********************************************************************
4993
 *		GetMenuDefaultItem    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
4994
 */
4995
UINT WINAPI GetMenuDefaultItem(HMENU hmenu, UINT bypos, UINT flags)
Alexandre Julliard's avatar
Alexandre Julliard committed
4996
{
4997 4998 4999
	POPUPMENU *menu;
	MENUITEM * item;
	UINT i = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
5000

5001
	TRACE("(%p,%d,%d)\n", hmenu, bypos, flags);
5002

5003
	if (!(menu = MENU_GetMenu(hmenu))) return -1;
5004 5005 5006

	/* find default item */
	item = menu->items;
5007

5008 5009
	/* empty menu */
	if (! item) return -1;
5010

5011 5012 5013 5014 5015
	while ( !( item->fState & MFS_DEFAULT ) )
	{
	    i++; item++;
	    if  (i >= menu->nItems ) return -1;
	}
5016

5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029
	/* default: don't return disabled items */
	if ( (!(GMDI_USEDISABLED & flags)) && (item->fState & MFS_DISABLED )) return -1;

	/* search rekursiv when needed */
	if ( (item->fType & MF_POPUP) &&  (flags & GMDI_GOINTOPOPUPS) )
	{
	    UINT ret;
	    ret = GetMenuDefaultItem( item->hSubMenu, bypos, flags );
	    if ( -1 != ret ) return ret;

	    /* when item not found in submenu, return the popup item */
	}
	return ( bypos ) ? i : item->wID;
Alexandre Julliard's avatar
Alexandre Julliard committed
5030 5031 5032

}

Alexandre Julliard's avatar
Alexandre Julliard committed
5033 5034

/**********************************************************************
5035
 *		InsertMenuItemA    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
5036
 */
5037 5038
BOOL WINAPI InsertMenuItemA(HMENU hMenu, UINT uItem, BOOL bypos,
                                const MENUITEMINFOA *lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
5039
{
5040
    MENUITEM *item;
5041
    MENUITEMINFOW mii;
5042 5043 5044

    TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);

5045
    if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
5046 5047

    item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
5048
    return SetMenuItemInfo_common(item, &mii, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
5049 5050 5051 5052
}


/**********************************************************************
5053
 *		InsertMenuItemW    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
5054
 */
5055 5056
BOOL WINAPI InsertMenuItemW(HMENU hMenu, UINT uItem, BOOL bypos,
                                const MENUITEMINFOW *lpmii)
Alexandre Julliard's avatar
Alexandre Julliard committed
5057
{
5058
    MENUITEM *item;
5059
    MENUITEMINFOW mii;
5060 5061 5062

    TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, bypos, lpmii);

5063
    if (!MENU_NormalizeMenuItemInfoStruct( lpmii, &mii )) return FALSE;
5064 5065

    item = MENU_InsertItem(hMenu, uItem, bypos ? MF_BYPOSITION : 0 );
5066
    return SetMenuItemInfo_common(item, &mii, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
5067
}
Alexandre Julliard's avatar
Alexandre Julliard committed
5068 5069

/**********************************************************************
5070
 *		CheckMenuRadioItem    (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
5071 5072
 */

5073 5074 5075
BOOL WINAPI CheckMenuRadioItem(HMENU hMenu,
				   UINT first, UINT last, UINT check,
				   UINT bypos)
Alexandre Julliard's avatar
Alexandre Julliard committed
5076
{
5077 5078 5079 5080
    BOOL done = FALSE;
    UINT i;
    MENUITEM *mi_first = NULL, *mi_check;
    HMENU m_first, m_check;
Alexandre Julliard's avatar
Alexandre Julliard committed
5081

5082 5083 5084
    for (i = first; i <= last; i++)
    {
        UINT pos = i;
Alexandre Julliard's avatar
Alexandre Julliard committed
5085

5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099
        if (!mi_first)
        {
            m_first = hMenu;
            mi_first = MENU_FindItem(&m_first, &pos, bypos);
            if (!mi_first) continue;
            mi_check = mi_first;
            m_check = m_first;
        }
        else
        {
            m_check = hMenu;
            mi_check = MENU_FindItem(&m_check, &pos, bypos);
            if (!mi_check) continue;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
5100

5101 5102 5103 5104 5105 5106 5107 5108 5109 5110 5111 5112 5113 5114 5115
        if (m_first != m_check) continue;
        if (mi_check->fType == MFT_SEPARATOR) continue;

        if (i == check)
        {
            mi_check->fType |= MFT_RADIOCHECK;
            mi_check->fState |= MFS_CHECKED;
            done = TRUE;
        }
        else
        {
            /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
            mi_check->fState &= ~MFS_CHECKED;
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
5116

5117
    return done;
Alexandre Julliard's avatar
Alexandre Julliard committed
5118 5119 5120 5121
}


/**********************************************************************
5122
 *		GetMenuItemRect    (USER32.@)
5123
 *
5124 5125
 *      ATTENTION: Here, the returned values in rect are the screen
 *                 coordinates of the item just like if the menu was
5126
 *                 always on the upper left side of the application.
5127
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
5128
 */
5129
BOOL WINAPI GetMenuItemRect(HWND hwnd, HMENU hMenu, UINT uItem, RECT *rect)
Alexandre Julliard's avatar
Alexandre Julliard committed
5130
{
5131
     POPUPMENU *menu;
Alexandre Julliard's avatar
Alexandre Julliard committed
5132 5133
     MENUITEM *item;

5134
     TRACE("(%p,%p,%d,%p)\n", hwnd, hMenu, uItem, rect);
Alexandre Julliard's avatar
Alexandre Julliard committed
5135 5136

     item = MENU_FindItem (&hMenu, &uItem, MF_BYPOSITION);
5137 5138
     if ((rect == NULL) || (item == NULL))
         return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
5139

5140 5141
     menu = MENU_GetMenu(hMenu);
     if (!menu) return FALSE;
5142

5143 5144
     if (!hwnd) hwnd = menu->hWnd;
     if (!hwnd) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
5145 5146 5147

     *rect = item->rect;

5148
     OffsetRect(rect, menu->items_rect.left, menu->items_rect.top);
5149
     MapWindowPoints(hwnd, 0, (POINT *)rect, 2);
5150

Alexandre Julliard's avatar
Alexandre Julliard committed
5151 5152 5153
     return TRUE;
}

5154
/**********************************************************************
5155
 *		SetMenuInfo    (USER32.@)
5156 5157 5158
 *
 * FIXME
 *	actually use the items to draw the menu
5159
 *      (recalculate and/or redraw)
5160
 */
5161
static BOOL menu_SetMenuInfo( HMENU hMenu, LPCMENUINFO lpmi)
5162 5163
{
    POPUPMENU *menu;
5164
    if( !(menu = MENU_GetMenu(hMenu))) return FALSE;
5165

5166 5167
    if (lpmi->fMask & MIM_BACKGROUND)
        menu->hbrBack = lpmi->hbrBack;
5168

5169 5170
    if (lpmi->fMask & MIM_HELPID)
        menu->dwContextHelpID = lpmi->dwContextHelpID;
5171

5172 5173
    if (lpmi->fMask & MIM_MAXHEIGHT)
        menu->cyMax = lpmi->cyMax;
5174

5175 5176
    if (lpmi->fMask & MIM_MENUDATA)
        menu->dwMenuData = lpmi->dwMenuData;
5177

5178 5179
    if (lpmi->fMask & MIM_STYLE)
        menu->dwStyle = lpmi->dwStyle;
5180

5181 5182 5183 5184 5185 5186 5187 5188 5189
    if( lpmi->fMask & MIM_APPLYTOSUBMENUS) {
        int i;
        MENUITEM *item = menu->items;
        for( i = menu->nItems; i; i--, item++)
            if( item->fType & MF_POPUP)
                menu_SetMenuInfo( item->hSubMenu, lpmi);
    }
    return TRUE;
}
5190

5191 5192 5193 5194 5195 5196 5197 5198
BOOL WINAPI SetMenuInfo (HMENU hMenu, LPCMENUINFO lpmi)
{
    TRACE("(%p %p)\n", hMenu, lpmi);
    if( lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu_SetMenuInfo( hMenu, lpmi))) {
	if( lpmi->fMask & MIM_STYLE) {
	    if (lpmi->dwStyle & MNS_AUTODISMISS) FIXME("MNS_AUTODISMISS unimplemented\n");
	    if (lpmi->dwStyle & MNS_DRAGDROP) FIXME("MNS_DRAGDROP unimplemented\n");
	    if (lpmi->dwStyle & MNS_MODELESS) FIXME("MNS_MODELESS unimplemented\n");
5199
	}
5200
        return TRUE;
5201
    }
5202
    SetLastError( ERROR_INVALID_PARAMETER);
5203 5204 5205 5206
    return FALSE;
}

/**********************************************************************
5207
 *		GetMenuInfo    (USER32.@)
5208 5209 5210 5211 5212 5213 5214 5215
 *
 *  NOTES
 *	win98/NT5.0
 *
 */
BOOL WINAPI GetMenuInfo (HMENU hMenu, LPMENUINFO lpmi)
{   POPUPMENU *menu;

5216
    TRACE("(%p %p)\n", hMenu, lpmi);
5217

5218
    if (lpmi && (lpmi->cbSize == sizeof( MENUINFO)) && (menu = MENU_GetMenu(hMenu)))
5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237
    {

	if (lpmi->fMask & MIM_BACKGROUND)
	    lpmi->hbrBack = menu->hbrBack;

	if (lpmi->fMask & MIM_HELPID)
	    lpmi->dwContextHelpID = menu->dwContextHelpID;

	if (lpmi->fMask & MIM_MAXHEIGHT)
	    lpmi->cyMax = menu->cyMax;

	if (lpmi->fMask & MIM_MENUDATA)
	    lpmi->dwMenuData = menu->dwMenuData;

	if (lpmi->fMask & MIM_STYLE)
	    lpmi->dwStyle = menu->dwStyle;

	return TRUE;
    }
5238
    SetLastError( ERROR_INVALID_PARAMETER);
5239 5240 5241
    return FALSE;
}

5242 5243

/**********************************************************************
5244
 *         SetMenuContextHelpId    (USER32.@)
5245
 */
5246
BOOL WINAPI SetMenuContextHelpId( HMENU hMenu, DWORD dwContextHelpID)
5247
{
5248 5249
    LPPOPUPMENU menu;

5250
    TRACE("(%p 0x%08x)\n", hMenu, dwContextHelpID);
5251

5252
    if ((menu = MENU_GetMenu(hMenu)))
5253 5254 5255 5256 5257
    {
	menu->dwContextHelpID = dwContextHelpID;
	return TRUE;
    }
    return FALSE;
5258 5259
}

5260

5261
/**********************************************************************
5262
 *         GetMenuContextHelpId    (USER32.@)
5263
 */
5264
DWORD WINAPI GetMenuContextHelpId( HMENU hMenu )
5265
{
5266 5267
    LPPOPUPMENU menu;

5268
    TRACE("(%p)\n", hMenu);
5269

5270
    if ((menu = MENU_GetMenu(hMenu)))
5271 5272 5273 5274
    {
	return menu->dwContextHelpID;
    }
    return 0;
5275
}
5276 5277

/**********************************************************************
5278
 *         MenuItemFromPoint    (USER32.@)
5279
 */
5280
INT WINAPI MenuItemFromPoint(HWND hWnd, HMENU hMenu, POINT ptScreen)
5281
{
5282 5283 5284 5285
    POPUPMENU *menu = MENU_GetMenu(hMenu);
    UINT pos;

    /*FIXME: Do we have to handle hWnd here? */
5286
    if (!menu) return -1;
5287
    if (MENU_FindItemByCoords( menu, ptScreen, &pos ) != ht_item) return -1;
5288
    return pos;
5289
}
5290 5291


5292 5293 5294 5295 5296 5297 5298 5299 5300 5301
/**********************************************************************
 *      CalcMenuBar     (USER32.@)
 */
DWORD WINAPI CalcMenuBar(HWND hwnd, DWORD left, DWORD right, DWORD top, RECT *rect)
{
    FIXME("(%p, %d, %d, %d, %p): stub\n", hwnd, left, right, top, rect);
    return 0;
}


5302 5303 5304 5305 5306 5307
/**********************************************************************
 *           translate_accelerator
 */
static BOOL translate_accelerator( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
                                   BYTE fVirt, WORD key, WORD cmd )
{
5308
    INT mask = 0;
5309 5310 5311 5312
    UINT mesg = 0;

    if (wParam != key) return FALSE;

5313 5314
    if (GetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
    if (GetKeyState(VK_MENU) & 0x8000) mask |= FALT;
5315
    if (GetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
5316

5317
    if (message == WM_CHAR || message == WM_SYSCHAR)
5318
    {
5319
        if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
5320
        {
5321
            TRACE_(accel)("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
5322 5323 5324 5325 5326 5327 5328
            goto found;
        }
    }
    else
    {
        if(fVirt & FVIRTKEY)
        {
5329
            TRACE_(accel)("found accel for virt_key %04lx (scan %04x)\n",
5330
                          wParam, 0xff & HIWORD(lParam));
5331

5332 5333 5334 5335 5336 5337 5338 5339 5340
            if(mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
            TRACE_(accel)(", but incorrect SHIFT/CTRL/ALT-state\n");
        }
        else
        {
            if (!(lParam & 0x01000000))  /* no special_key */
            {
                if ((fVirt & FALT) && (lParam & 0x20000000))
                {                              /* ^^ ALT pressed */
5341
                    TRACE_(accel)("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
5342 5343 5344 5345 5346 5347 5348 5349 5350 5351 5352 5353 5354 5355 5356
                    goto found;
                }
            }
        }
    }
    return FALSE;

 found:
    if (message == WM_KEYUP || message == WM_SYSKEYUP)
        mesg = 1;
    else
    {
        HMENU hMenu, hSubMenu, hSysMenu;
        UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;

5357
        hMenu = (GetWindowLongW( hWnd, GWL_STYLE ) & WS_CHILD) ? 0 : GetMenu(hWnd);
5358
        hSysMenu = get_win_sys_menu( hWnd );
5359 5360 5361 5362 5363 5364 5365

        /* find menu item and ask application to initialize it */
        /* 1. in the system menu */
        hSubMenu = hSysMenu;
        nPos = cmd;
        if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
        {
5366 5367 5368 5369 5370
            if (GetCapture())
                mesg = 2;
            if (!IsWindowEnabled(hWnd))
                mesg = 3;
            else
5371
            {
5372 5373 5374 5375 5376 5377 5378 5379
                SendMessageW(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
                if(hSubMenu != hSysMenu)
                {
                    nPos = MENU_FindSubMenu(&hSysMenu, hSubMenu);
                    TRACE_(accel)("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
                    SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
                }
                uSysStat = GetMenuState(GetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
5380 5381 5382 5383 5384 5385 5386 5387
            }
        }
        else /* 2. in the window's menu */
        {
            hSubMenu = hMenu;
            nPos = cmd;
            if(MENU_FindItem(&hSubMenu, &nPos, MF_BYCOMMAND))
            {
5388 5389 5390 5391 5392
                if (GetCapture())
                    mesg = 2;
                if (!IsWindowEnabled(hWnd))
                    mesg = 3;
                else
5393
                {
5394 5395 5396 5397 5398 5399 5400 5401
                    SendMessageW(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
                    if(hSubMenu != hMenu)
                    {
                        nPos = MENU_FindSubMenu(&hMenu, hSubMenu);
                        TRACE_(accel)("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
                        SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
                    }
                    uStat = GetMenuState(hMenu, cmd, MF_BYCOMMAND);
5402 5403 5404 5405
                }
            }
        }

5406
        if (mesg == 0)
5407
        {
5408
            if (uSysStat != (UINT)-1)
5409
            {
5410 5411
                if (uSysStat & (MF_DISABLED|MF_GRAYED))
                    mesg=4;
5412
                else
5413 5414 5415 5416 5417
                    mesg=WM_SYSCOMMAND;
            }
            else
            {
                if (uStat != (UINT)-1)
5418
                {
5419 5420
                    if (IsIconic(hWnd))
                        mesg=5;
5421
                    else
5422 5423 5424 5425 5426 5427
                    {
                        if (uStat & (MF_DISABLED|MF_GRAYED))
                            mesg=6;
                        else
                            mesg=WM_COMMAND;
                    }
5428
                }
5429 5430
                else
                    mesg=WM_COMMAND;
5431 5432 5433 5434 5435 5436 5437
            }
        }
    }

    if( mesg==WM_COMMAND )
    {
        TRACE_(accel)(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
5438
        SendMessageW(hWnd, mesg, 0x10000 | cmd, 0L);
5439 5440 5441 5442
    }
    else if( mesg==WM_SYSCOMMAND )
    {
        TRACE_(accel)(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
5443
        SendMessageW(hWnd, mesg, cmd, 0x00010000L);
5444 5445 5446
    }
    else
    {
5447
        /*  some reasons for NOT sending the WM_{SYS}COMMAND message:
5448 5449 5450
         *   #0: unknown (please report!)
         *   #1: for WM_KEYUP,WM_SYSKEYUP
         *   #2: mouse is captured
5451
         *   #3: window is disabled
5452 5453 5454 5455 5456 5457
         *   #4: it's a disabled system menu option
         *   #5: it's a menu option, but window is iconic
         *   #6: it's a menu option, but disabled
         */
        TRACE_(accel)(", but won't send WM_{SYS}COMMAND, reason is #%d\n",mesg);
        if(mesg==0)
5458
            ERR_(accel)(" unknown reason - please report!\n");
5459 5460 5461 5462 5463
    }
    return TRUE;
}

/**********************************************************************
5464
 *      TranslateAcceleratorA     (USER32.@)
5465
 *      TranslateAccelerator      (USER32.@)
5466
 */
5467
INT WINAPI TranslateAcceleratorA( HWND hWnd, HACCEL hAccel, LPMSG msg )
5468
{
5469
    switch (msg->message)
5470
    {
5471 5472
    case WM_KEYDOWN:
    case WM_SYSKEYDOWN:
5473
        return TranslateAcceleratorW( hWnd, hAccel, msg );
5474 5475 5476 5477

    case WM_CHAR:
    case WM_SYSCHAR:
        {
5478 5479
            MSG msgW = *msg;
            char ch = LOWORD(msg->wParam);
5480 5481
            WCHAR wch;
            MultiByteToWideChar(CP_ACP, 0, &ch, 1, &wch, 1);
5482 5483
            msgW.wParam = MAKEWPARAM(wch, HIWORD(msg->wParam));
            return TranslateAcceleratorW( hWnd, hAccel, &msgW );
5484 5485 5486
        }

    default:
5487 5488
        return 0;
    }
5489 5490 5491 5492 5493 5494 5495
}

/**********************************************************************
 *      TranslateAcceleratorW     (USER32.@)
 */
INT WINAPI TranslateAcceleratorW( HWND hWnd, HACCEL hAccel, LPMSG msg )
{
5496 5497
    ACCEL data[32], *ptr = data;
    int i, count;
5498

5499
    if (!hWnd) return 0;
5500

5501 5502 5503 5504
    if (msg->message != WM_KEYDOWN &&
        msg->message != WM_SYSKEYDOWN &&
        msg->message != WM_CHAR &&
        msg->message != WM_SYSCHAR)
5505 5506
        return 0;

5507
    TRACE_(accel)("hAccel %p, hWnd %p, msg->hwnd %p, msg->message %04x, wParam %08lx, lParam %08lx\n",
5508
                  hAccel,hWnd,msg->hwnd,msg->message,msg->wParam,msg->lParam);
5509 5510 5511 5512 5513 5514 5515 5516

    if (!(count = CopyAcceleratorTableW( hAccel, NULL, 0 ))) return 0;
    if (count > sizeof(data)/sizeof(data[0]))
    {
        if (!(ptr = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*ptr) ))) return 0;
    }
    count = CopyAcceleratorTableW( hAccel, ptr, count );
    for (i = 0; i < count; i++)
5517 5518
    {
        if (translate_accelerator( hWnd, msg->message, msg->wParam, msg->lParam,
5519 5520 5521 5522 5523
                                   ptr[i].fVirt, ptr[i].key, ptr[i].cmd))
            break;
    }
    if (ptr != data) HeapFree( GetProcessHeap(), 0, ptr );
    return (i < count);
5524
}