source: trunk/src/user32/menu.c@ 10367

Last change on this file since 10367 was 10190, checked in by sandervl, 22 years ago

KOMH: DBCS updates/fixes

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