4 static char RCSId[] = "$Id$";
5 static char Copyright[] = "Copyright Martin Ayotte, 1993";
6 static char Copyright2[] = "Copyright Alexandre Julliard, 1994";
9 * Note: the style MF_MOUSESELECT is used to mark popup items that
10 * have been selected, i.e. their popup menu is currently displayed.
11 * This is probably not the meaning this style has in MS-Windows.
21 #include "sysmetrics.h"
22 #include "prototypes.h"
28 /* #define DEBUG_MENU */
29 /* #undef DEBUG_MENU */
30 /* #define DEBUG_MENUCALC */
31 /* #undef DEBUG_MENUCALC */
32 /* #define DEBUG_MENUSHORTCUT */
33 /* #undef DEBUG_MENUSHORTCUT */
37 /* Dimension of the menu bitmaps */
38 static WORD check_bitmap_width = 0, check_bitmap_height = 0;
39 static WORD arrow_bitmap_width = 0, arrow_bitmap_height = 0;
41 /* Flag set by EndMenu() to force an exit from menu tracking */
42 static BOOL fEndMenuCalled = FALSE;
44 /* Space between 2 menu bar items */
45 #define MENU_BAR_ITEMS_SPACE 16
47 /* Minimum width of a tab character */
48 #define MENU_TAB_SPACE 8
50 /* Height of a separator item */
51 #define SEPARATOR_HEIGHT 5
53 /* Values for menu->FocusedItem */
54 /* (other values give the position of the focused item) */
55 #define NO_SELECTED_ITEM 0xffff
56 #define SYSMENU_SELECTED 0xfffe /* Only valid on menu-bars */
58 #define IS_STRING_ITEM(flags) (!((flags) & (MF_BITMAP | MF_OWNERDRAW | \
59 MF_MENUBARBREAK | MF_MENUBREAK | MF_SEPARATOR)))
62 extern void NC_DrawSysButton(HWND hwnd, HDC hdc, BOOL down); /* nonclient.c */
63 extern void CURSOR_SetWinCursor( HWND hwnd, HCURSOR hcursor ); /* cursor.c */
64 extern BOOL GRAPH_DrawBitmap( HDC hdc, HBITMAP hbitmap, int xdest, int ydest,
65 int xsrc, int ysrc, int width, int height,
66 int rop ); /* graphics.c */
68 extern HINSTANCE hSysRes;
70 static HBITMAP hStdCheck = 0;
71 static HBITMAP hStdMnArrow = 0;
74 WORD * ParseMenuResource(WORD *first_item, int level, HMENU hMenu);
77 /***********************************************************************
80 * Menus initialisation.
88 if (!(hStdCheck = LoadBitmap( 0, MAKEINTRESOURCE(OBM_CHECK) )))
90 GetObject( hStdCheck, sizeof(BITMAP), (LPSTR)&bm );
91 check_bitmap_width = bm.bmWidth;
92 check_bitmap_height = bm.bmHeight;
93 if (!(hStdMnArrow = LoadBitmap( 0, MAKEINTRESOURCE(OBM_MNARROW) )))
95 GetObject( hStdMnArrow, sizeof(BITMAP), (LPSTR)&bm );
96 arrow_bitmap_width = bm.bmWidth;
97 arrow_bitmap_height = bm.bmHeight;
103 /***********************************************************************
106 * Check whether the window owning the menu bar has a system menu.
108 static BOOL MENU_HasSysMenu( POPUPMENU *menu )
112 if (menu->wFlags & MF_POPUP) return FALSE;
113 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return FALSE;
114 return (wndPtr->dwStyle & WS_SYSMENU) != 0;
118 /***********************************************************************
121 * Check whether the point (in screen coords) is in the system menu
122 * of the window owning the given menu.
124 static BOOL MENU_IsInSysMenu( POPUPMENU *menu, POINT pt )
128 if (menu->wFlags & MF_POPUP) return FALSE;
129 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return FALSE;
130 if (!(wndPtr->dwStyle & WS_SYSMENU)) return FALSE;
131 if ((pt.x < wndPtr->rectClient.left) ||
132 (pt.x >= wndPtr->rectClient.left+SYSMETRICS_CXSIZE+SYSMETRICS_CXBORDER))
134 if ((pt.y >= wndPtr->rectClient.top - menu->Height) ||
135 (pt.y < wndPtr->rectClient.top - menu->Height -
136 SYSMETRICS_CYSIZE - SYSMETRICS_CYBORDER)) return FALSE;
141 /***********************************************************************
144 * Find a menu item. Return a pointer on the item, and modifies *hmenu
145 * in case the item was in a sub-menu.
147 static MENUITEM *MENU_FindItem( HMENU *hmenu, WORD *nPos, WORD wFlags )
153 if (!(menu = (POPUPMENU *) USER_HEAP_ADDR(*hmenu))) return NULL;
154 item = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
155 if (wFlags & MF_BYPOSITION)
157 if (*nPos >= menu->nItems) return NULL;
162 for (i = 0; i < menu->nItems; i++, item++)
164 if (item->item_id == *nPos)
169 else if (item->item_flags & MF_POPUP)
171 HMENU hsubmenu = (HMENU)item->item_id;
172 MENUITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
185 /***********************************************************************
186 * MENU_FindItemByCoords
188 * Find the item at the specified coordinates (screen coords).
190 static MENUITEM *MENU_FindItemByCoords( POPUPMENU *menu, int x, int y, WORD *pos )
196 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return NULL;
197 x -= wndPtr->rectWindow.left;
198 y -= wndPtr->rectWindow.top;
199 item = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
200 for (i = 0; i < menu->nItems; i++, item++)
202 if ((x >= item->rect.left) && (x < item->rect.right) &&
203 (y >= item->rect.top) && (y < item->rect.bottom))
213 /***********************************************************************
216 * Find the menu item selected by a key press.
217 * Return item id, -1 if none, -2 if we should close the menu.
219 static WORD MENU_FindItemByKey( HWND hwndOwner, HMENU hmenu, WORD key )
226 menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
227 lpitem = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
229 for (i = 0; i < menu->nItems; i++, lpitem++)
231 if (IS_STRING_ITEM(lpitem->item_flags))
233 char *p = strchr( lpitem->item_text, '&' );
234 if (p && (p[1] != '&') && (toupper(p[1]) == key)) return i;
237 menuchar = SendMessage( hwndOwner, WM_MENUCHAR, key,
238 MAKELONG( menu->wFlags, hmenu ) );
239 if (HIWORD(menuchar) == 2) return LOWORD(menuchar);
240 if (HIWORD(menuchar) == 1) return -2;
245 /***********************************************************************
248 * Calculate the size of the menu item and store it in lpitem->rect.
250 static void MENU_CalcItemSize( HDC hdc, LPMENUITEM lpitem, HWND hwndOwner,
251 int orgX, int orgY, BOOL menuBar )
256 SetRect( &lpitem->rect, orgX, orgY, orgX, orgY );
259 if (lpitem->item_flags & MF_SEPARATOR)
261 lpitem->rect.bottom += SEPARATOR_HEIGHT;
267 lpitem->rect.right += 2 * check_bitmap_width;
268 if (lpitem->item_flags & MF_POPUP)
269 lpitem->rect.right += arrow_bitmap_width;
272 if (lpitem->item_flags & MF_BITMAP)
275 GetObject( (HBITMAP)lpitem->hText, sizeof(BITMAP), (LPSTR)&bm );
276 lpitem->rect.right += bm.bmWidth;
277 lpitem->rect.bottom += bm.bmHeight;
281 /* If we get here, then it is a text item */
283 dwSize = (lpitem->item_text == NULL) ? 0 : GetTextExtent( hdc, lpitem->item_text, strlen(lpitem->item_text));
284 lpitem->rect.right += LOWORD(dwSize);
285 lpitem->rect.bottom += max( HIWORD(dwSize), SYSMETRICS_CYMENU );
287 if (menuBar) lpitem->rect.right += MENU_BAR_ITEMS_SPACE;
288 else if ((p = strchr( lpitem->item_text, '\t' )) != NULL)
290 /* Item contains a tab (only meaningful in popup menus) */
291 lpitem->xTab = check_bitmap_width + MENU_TAB_SPACE +
292 LOWORD( GetTextExtent( hdc, lpitem->item_text,
293 (int)(p - lpitem->item_text) ));
294 lpitem->rect.right += MENU_TAB_SPACE;
298 if (strchr( lpitem->item_text, '\b' ))
299 lpitem->rect.right += MENU_TAB_SPACE;
300 lpitem->xTab = lpitem->rect.right - check_bitmap_width
301 - arrow_bitmap_width;
306 /***********************************************************************
307 * MENU_PopupMenuCalcSize
309 * Calculate the size of a popup menu.
311 static void MENU_PopupMenuCalcSize( LPPOPUPMENU lppop, HWND hwndOwner )
313 LPMENUITEM items, lpitem;
316 int orgX, orgY, maxX, maxTab, maxTabWidth;
318 lppop->Width = lppop->Height = 0;
319 if (lppop->nItems == 0) return;
320 items = (MENUITEM *)USER_HEAP_ADDR( lppop->hItems );
323 while (start < lppop->nItems)
325 lpitem = &items[start];
328 maxTab = maxTabWidth = 0;
330 /* Parse items until column break or end of menu */
331 for (i = start; i < lppop->nItems; i++, lpitem++)
334 (lpitem->item_flags & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
335 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, FALSE );
336 maxX = max( maxX, lpitem->rect.right );
337 orgY = lpitem->rect.bottom;
340 maxTab = max( maxTab, lpitem->xTab );
341 maxTabWidth = max(maxTabWidth,lpitem->rect.right-lpitem->xTab);
345 /* Finish the column (set all items to the largest width found) */
346 maxX = max( maxX, maxTab + maxTabWidth );
347 for (lpitem = &items[start]; start < i; start++, lpitem++)
349 lpitem->rect.right = maxX;
350 if (lpitem->xTab) lpitem->xTab = maxTab;
352 lppop->Height = max( lppop->Height, orgY );
360 /***********************************************************************
361 * MENU_MenuBarCalcSize
363 * Calculate the size of the menu bar.
365 static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect, LPPOPUPMENU lppop,
368 LPMENUITEM lpitem, items;
369 int start, i, orgX, orgY, maxY, helpPos;
371 if ((lprect == NULL) || (lppop == NULL)) return;
372 if (lppop->nItems == 0) return;
373 dprintf_menucalc(stddeb,"MenuBarCalcSize left=%d top=%d right=%d bottom=%d !\n",
374 lprect->left, lprect->top, lprect->right, lprect->bottom);
375 items = (MENUITEM *)USER_HEAP_ADDR( lppop->hItems );
376 lppop->Width = lprect->right - lprect->left;
381 while (start < lppop->nItems)
383 lpitem = &items[start];
387 /* Parse items until line break or end of menu */
388 for (i = start; i < lppop->nItems; i++, lpitem++)
390 if ((helpPos == -1) && (lpitem->item_flags & MF_HELP)) helpPos = i;
392 (lpitem->item_flags & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
393 MENU_CalcItemSize( hdc, lpitem, hwndOwner, orgX, orgY, TRUE );
394 if (lpitem->rect.right > lprect->right)
396 if (i != start) break;
397 else lpitem->rect.right = lprect->right;
399 maxY = max( maxY, lpitem->rect.bottom );
400 orgX = lpitem->rect.right;
403 /* Finish the line (set all items to the largest height found) */
404 while (start < i) items[start++].rect.bottom = maxY;
407 lprect->bottom = maxY;
408 lppop->Height = lprect->bottom - lprect->top;
410 /* Flush right all items between the MF_HELP and the last item */
411 /* (if several lines, only move the last line) */
414 lpitem = &items[lppop->nItems-1];
415 orgY = lpitem->rect.top;
416 orgX = lprect->right;
417 for (i = lppop->nItems - 1; i >= helpPos; i--, lpitem--)
419 if (lpitem->rect.top != orgY) break; /* Other line */
420 if (lpitem->rect.right >= orgX) break; /* Too far right already */
421 lpitem->rect.left += orgX - lpitem->rect.right;
422 lpitem->rect.right = orgX;
423 orgX = lpitem->rect.left;
429 /***********************************************************************
432 * Draw a single menu item.
434 static void MENU_DrawMenuItem( HDC hdc, LPMENUITEM lpitem,
435 WORD height, BOOL menuBar )
439 if (menuBar && (lpitem->item_flags & MF_SEPARATOR)) return;
442 /* Draw the background */
444 if (lpitem->item_flags & MF_HILITE)
445 FillRect( hdc, &rect, sysColorObjects.hbrushHighlight );
446 else FillRect( hdc, &rect, sysColorObjects.hbrushMenu );
447 SetBkMode( hdc, TRANSPARENT );
449 /* Draw the separator bar (if any) */
451 if (!menuBar && (lpitem->item_flags & MF_MENUBARBREAK))
453 SelectObject( hdc, sysColorObjects.hpenWindowFrame );
454 MoveTo( hdc, rect.left, 0 );
455 LineTo( hdc, rect.left, height );
457 if (lpitem->item_flags & MF_SEPARATOR)
459 SelectObject( hdc, sysColorObjects.hpenWindowFrame );
460 MoveTo( hdc, rect.left, rect.top + SEPARATOR_HEIGHT/2 );
461 LineTo( hdc, rect.right, rect.top + SEPARATOR_HEIGHT/2 );
467 if (lpitem->item_flags & MF_HILITE)
469 if (lpitem->item_flags & MF_GRAYED)
470 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
472 SetTextColor( hdc, GetSysColor( COLOR_HIGHLIGHTTEXT ) );
473 SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
477 if (lpitem->item_flags & MF_GRAYED)
478 SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
480 SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
481 SetBkColor( hdc, GetSysColor( COLOR_MENU ) );
486 /* Draw the check mark */
488 if (lpitem->item_flags & MF_CHECKED)
490 GRAPH_DrawBitmap(hdc, lpitem->hCheckBit ? lpitem->hCheckBit :
491 hStdCheck, rect.left,
492 (rect.top+rect.bottom-check_bitmap_height) / 2,
493 0, 0, check_bitmap_width, check_bitmap_height,
496 else if (lpitem->hUnCheckBit != 0) /* Not checked */
498 GRAPH_DrawBitmap(hdc, lpitem->hUnCheckBit, rect.left,
499 (rect.top+rect.bottom-check_bitmap_height) / 2,
500 0, 0, check_bitmap_width, check_bitmap_height,
504 /* Draw the popup-menu arrow */
506 if (lpitem->item_flags & MF_POPUP)
508 GRAPH_DrawBitmap( hdc, hStdMnArrow,
509 rect.right-arrow_bitmap_width-1,
510 (rect.top+rect.bottom-arrow_bitmap_height) / 2,
511 0, 0, arrow_bitmap_width, arrow_bitmap_height,
515 rect.left += check_bitmap_width;
516 rect.right -= arrow_bitmap_width;
519 /* Draw the item text or bitmap */
521 if (lpitem->item_flags & MF_BITMAP)
523 GRAPH_DrawBitmap( hdc, (HBITMAP)lpitem->hText, rect.left, rect.top,
524 0, 0, rect.right-rect.left, rect.bottom-rect.top,
528 /* No bitmap - process text if present */
529 else if ((lpitem->item_text) != ((char *) NULL))
535 rect.left += MENU_BAR_ITEMS_SPACE / 2;
536 rect.right -= MENU_BAR_ITEMS_SPACE / 2;
537 i = strlen( lpitem->item_text );
541 for (i = 0; lpitem->item_text[i]; i++)
542 if ((lpitem->item_text[i] == '\t') ||
543 (lpitem->item_text[i] == '\b')) break;
546 DrawText( hdc, lpitem->item_text, i, &rect,
547 DT_LEFT | DT_VCENTER | DT_SINGLELINE );
549 if (lpitem->item_text[i]) /* There's a tab or flush-right char */
551 if (lpitem->item_text[i] == '\t')
553 rect.left = lpitem->xTab;
554 DrawText( hdc, lpitem->item_text + i + 1, -1, &rect,
555 DT_LEFT | DT_VCENTER | DT_SINGLELINE );
557 else DrawText( hdc, lpitem->item_text + i + 1, -1, &rect,
558 DT_RIGHT | DT_VCENTER | DT_SINGLELINE );
564 /***********************************************************************
567 * Paint a popup menu.
569 static void MENU_DrawPopupMenu( HWND hwnd, HDC hdc, HMENU hmenu )
576 GetClientRect( hwnd, &rect );
577 FillRect( hdc, &rect, sysColorObjects.hbrushMenu );
578 menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
579 if (!menu || !menu->nItems) return;
580 item = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
581 for (i = menu->nItems; i > 0; i--, item++)
582 MENU_DrawMenuItem( hdc, item, menu->Height, FALSE );
586 /***********************************************************************
589 * Paint a menu bar. Returns the height of the menu bar.
591 WORD MENU_DrawMenuBar(HDC hDC, LPRECT lprect, HWND hwnd, BOOL suppress_draw)
596 WND *wndPtr = WIN_FindWndPtr( hwnd );
598 lppop = (LPPOPUPMENU) USER_HEAP_ADDR( wndPtr->wIDmenu );
599 if (lppop == NULL || lprect == NULL) return SYSMETRICS_CYMENU;
600 dprintf_menu(stddeb,"MENU_DrawMenuBar(%04X, %p, %p); !\n",
602 if (lppop->Height == 0) MENU_MenuBarCalcSize(hDC, lprect, lppop, hwnd);
603 lprect->bottom = lprect->top + lppop->Height;
604 if (suppress_draw) return lppop->Height;
606 FillRect(hDC, lprect, sysColorObjects.hbrushMenu );
607 SelectObject( hDC, sysColorObjects.hpenWindowFrame );
608 MoveTo( hDC, lprect->left, lprect->bottom );
609 LineTo( hDC, lprect->right, lprect->bottom );
611 if (lppop->nItems == 0) return SYSMETRICS_CYMENU;
612 lpitem = (MENUITEM *) USER_HEAP_ADDR( lppop->hItems );
613 for (i = 0; i < lppop->nItems; i++, lpitem++)
615 MENU_DrawMenuItem( hDC, lpitem, lppop->Height, TRUE );
617 return lppop->Height;
621 /***********************************************************************
624 * Display a popup menu.
626 static BOOL MENU_ShowPopup(HWND hwndOwner, HMENU hmenu, WORD id, int x, int y)
630 if (!(menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu ))) return FALSE;
631 if (menu->FocusedItem != NO_SELECTED_ITEM)
633 MENUITEM *item = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
634 item[menu->FocusedItem].item_flags &= ~(MF_HILITE | MF_MOUSESELECT);
635 menu->FocusedItem = NO_SELECTED_ITEM;
637 SendMessage( hwndOwner, WM_INITMENUPOPUP, hmenu,
638 MAKELONG( id, (menu->wFlags & MF_POPUP) ? 1 : 0 ));
639 MENU_PopupMenuCalcSize( menu, hwndOwner );
642 WND *wndPtr = WIN_FindWndPtr( hwndOwner );
643 if (!wndPtr) return FALSE;
644 menu->hWnd = CreateWindow( POPUPMENU_CLASS_NAME, "",
645 WS_POPUP | WS_BORDER, x, y,
646 menu->Width + 2*SYSMETRICS_CXBORDER,
647 menu->Height + 2*SYSMETRICS_CYBORDER,
648 0, 0, wndPtr->hInstance,
649 (LPSTR)(DWORD)hmenu );
650 if (!menu->hWnd) return FALSE;
652 else SetWindowPos( menu->hWnd, 0, x, y,
653 menu->Width + 2*SYSMETRICS_CXBORDER,
654 menu->Height + 2*SYSMETRICS_CYBORDER,
655 SWP_NOACTIVATE | SWP_NOZORDER );
657 /* Display the window */
659 SetWindowPos( menu->hWnd, HWND_TOP, 0, 0, 0, 0,
660 SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE );
661 UpdateWindow( menu->hWnd );
666 /***********************************************************************
669 static void MENU_SelectItem( HMENU hmenu, WORD wIndex )
675 lppop = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
676 if (!lppop->nItems) return;
677 items = (MENUITEM *) USER_HEAP_ADDR( lppop->hItems );
678 if ((wIndex != NO_SELECTED_ITEM) &&
679 (wIndex != SYSMENU_SELECTED) &&
680 (items[wIndex].item_flags & MF_SEPARATOR))
681 wIndex = NO_SELECTED_ITEM;
682 if (lppop->FocusedItem == wIndex) return;
683 if (lppop->wFlags & MF_POPUP) hdc = GetDC( lppop->hWnd );
684 else hdc = GetDCEx( lppop->hWnd, 0, DCX_CACHE | DCX_WINDOW);
686 /* Clear previous highlighted item */
687 if (lppop->FocusedItem != NO_SELECTED_ITEM)
689 if (lppop->FocusedItem == SYSMENU_SELECTED)
690 NC_DrawSysButton( lppop->hWnd, hdc, FALSE );
693 items[lppop->FocusedItem].item_flags &=~(MF_HILITE|MF_MOUSESELECT);
694 MENU_DrawMenuItem( hdc, &items[lppop->FocusedItem], lppop->Height,
695 !(lppop->wFlags & MF_POPUP) );
699 /* Highlight new item (if any) */
700 lppop->FocusedItem = wIndex;
701 if (lppop->FocusedItem != NO_SELECTED_ITEM)
703 if (lppop->FocusedItem == SYSMENU_SELECTED)
704 NC_DrawSysButton( lppop->hWnd, hdc, TRUE );
707 items[lppop->FocusedItem].item_flags |= MF_HILITE;
708 MENU_DrawMenuItem( hdc, &items[lppop->FocusedItem], lppop->Height,
709 !(lppop->wFlags & MF_POPUP) );
710 SendMessage(lppop->hWnd, WM_MENUSELECT,
711 items[lppop->FocusedItem].item_id,
712 MAKELONG( hmenu, items[lppop->FocusedItem].item_flags));
715 ReleaseDC( lppop->hWnd, hdc );
719 /***********************************************************************
720 * MENU_SelectNextItem
722 static void MENU_SelectNextItem( HMENU hmenu )
728 menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
729 if (!menu->nItems) return;
730 items = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
731 if ((menu->FocusedItem != NO_SELECTED_ITEM) &&
732 (menu->FocusedItem != SYSMENU_SELECTED))
734 for (i = menu->FocusedItem+1; i < menu->nItems; i++)
736 if (!(items[i].item_flags & MF_SEPARATOR))
738 MENU_SelectItem( hmenu, i );
742 if (MENU_HasSysMenu( menu ))
744 MENU_SelectItem( hmenu, SYSMENU_SELECTED );
748 for (i = 0; i < menu->nItems; i++)
750 if (!(items[i].item_flags & MF_SEPARATOR))
752 MENU_SelectItem( hmenu, i );
756 if (MENU_HasSysMenu( menu )) MENU_SelectItem( hmenu, SYSMENU_SELECTED );
760 /***********************************************************************
761 * MENU_SelectPrevItem
763 static void MENU_SelectPrevItem( HMENU hmenu )
769 menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
770 if (!menu->nItems) return;
771 items = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
772 if ((menu->FocusedItem != NO_SELECTED_ITEM) &&
773 (menu->FocusedItem != SYSMENU_SELECTED))
775 for (i = menu->FocusedItem - 1; i >= 0; i--)
777 if (!(items[i].item_flags & MF_SEPARATOR))
779 MENU_SelectItem( hmenu, i );
783 if (MENU_HasSysMenu( menu ))
785 MENU_SelectItem( hmenu, SYSMENU_SELECTED );
789 for (i = menu->nItems - 1; i > 0; i--)
791 if (!(items[i].item_flags & MF_SEPARATOR))
793 MENU_SelectItem( hmenu, i );
797 if (MENU_HasSysMenu( menu )) MENU_SelectItem( hmenu, SYSMENU_SELECTED );
801 /***********************************************************************
804 * Return the handle of the selected sub-popup menu (if any).
806 static HMENU MENU_GetSubPopup( HMENU hmenu )
811 menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
812 if (menu->FocusedItem == NO_SELECTED_ITEM) return 0;
813 else if (menu->FocusedItem == SYSMENU_SELECTED)
814 return GetSystemMenu( menu->hWnd, FALSE );
816 item = ((MENUITEM *)USER_HEAP_ADDR( menu->hItems )) + menu->FocusedItem;
817 if (!(item->item_flags & MF_POPUP) || !(item->item_flags & MF_MOUSESELECT))
819 return item->item_id;
823 /***********************************************************************
826 * Hide the sub-popup menus of this menu.
828 static void MENU_HideSubPopups( HMENU hmenu )
831 POPUPMENU *menu, *submenu;
834 if (!(menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu ))) return;
835 if (menu->FocusedItem == NO_SELECTED_ITEM) return;
836 if (menu->FocusedItem == SYSMENU_SELECTED)
838 hsubmenu = GetSystemMenu( menu->hWnd, FALSE );
842 item = ((MENUITEM *)USER_HEAP_ADDR(menu->hItems)) + menu->FocusedItem;
843 if (!(item->item_flags & MF_POPUP) ||
844 !(item->item_flags & MF_MOUSESELECT)) return;
845 item->item_flags &= ~MF_MOUSESELECT;
846 hsubmenu = item->item_id;
848 submenu = (POPUPMENU *) USER_HEAP_ADDR( hsubmenu );
849 MENU_HideSubPopups( hsubmenu );
850 if (submenu->hWnd) ShowWindow( submenu->hWnd, SW_HIDE );
851 MENU_SelectItem( hsubmenu, NO_SELECTED_ITEM );
855 /***********************************************************************
858 * Display the sub-menu of the selected item of this menu.
859 * Return the handle of the submenu, or hmenu if no submenu to display.
861 static HMENU MENU_ShowSubPopup( HWND hwndOwner, HMENU hmenu, BOOL selectFirst )
867 if (!(menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu ))) return hmenu;
868 if (!(wndPtr = WIN_FindWndPtr( menu->hWnd ))) return hmenu;
869 if (menu->FocusedItem == NO_SELECTED_ITEM) return hmenu;
870 if (menu->FocusedItem == SYSMENU_SELECTED)
872 MENU_ShowPopup(hwndOwner, wndPtr->hSysMenu, 0, wndPtr->rectClient.left,
873 wndPtr->rectClient.top - menu->Height - 2*SYSMETRICS_CYBORDER);
874 if (selectFirst) MENU_SelectNextItem( wndPtr->hSysMenu );
875 return wndPtr->hSysMenu;
877 item = ((MENUITEM *)USER_HEAP_ADDR( menu->hItems )) + menu->FocusedItem;
878 if (!(item->item_flags & MF_POPUP) ||
879 (item->item_flags & (MF_GRAYED | MF_DISABLED))) return hmenu;
880 item->item_flags |= MF_MOUSESELECT;
881 if (menu->wFlags & MF_POPUP)
883 MENU_ShowPopup( hwndOwner, (HMENU)item->item_id, menu->FocusedItem,
884 wndPtr->rectWindow.left + item->rect.right-arrow_bitmap_width,
885 wndPtr->rectWindow.top + item->rect.top );
889 MENU_ShowPopup( hwndOwner, (HMENU)item->item_id, menu->FocusedItem,
890 wndPtr->rectWindow.left + item->rect.left,
891 wndPtr->rectWindow.top + item->rect.bottom );
893 if (selectFirst) MENU_SelectNextItem( (HMENU)item->item_id );
894 return (HMENU)item->item_id;
898 /***********************************************************************
899 * MENU_FindMenuByCoords
901 * Find the menu containing a given point (in screen coords).
903 static HMENU MENU_FindMenuByCoords( HMENU hmenu, POINT pt )
908 if (!(hwnd = WindowFromPoint( pt ))) return 0;
911 menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
912 if (menu->hWnd == hwnd)
914 if (!(menu->wFlags & MF_POPUP))
916 /* Make sure it's in the menu bar (or in system menu) */
917 WND *wndPtr = WIN_FindWndPtr( menu->hWnd );
918 if ((pt.x < wndPtr->rectClient.left) ||
919 (pt.x >= wndPtr->rectClient.right) ||
920 (pt.y >= wndPtr->rectClient.top)) return 0;
921 if (pt.y < wndPtr->rectClient.top - menu->Height)
923 if (!MENU_IsInSysMenu( menu, pt )) return 0;
925 /* else it's in the menu bar */
929 hmenu = MENU_GetSubPopup( hmenu );
935 /***********************************************************************
936 * MENU_ExecFocusedItem
938 * Execute a menu item (for instance when user pressed Enter).
939 * Return TRUE if we can go on with menu tracking.
941 static BOOL MENU_ExecFocusedItem( HWND hwndOwner, HMENU hmenu,
942 HMENU *hmenuCurrent )
945 POPUPMENU *menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
946 if (!menu || !menu->nItems || (menu->FocusedItem == NO_SELECTED_ITEM) ||
947 (menu->FocusedItem == SYSMENU_SELECTED)) return TRUE;
948 item = ((MENUITEM *)USER_HEAP_ADDR( menu->hItems )) + menu->FocusedItem;
949 if (!(item->item_flags & MF_POPUP))
951 if (!(item->item_flags & (MF_GRAYED | MF_DISABLED)))
953 PostMessage( hwndOwner, (menu->wFlags & MF_SYSMENU) ?
954 WM_SYSCOMMAND : WM_COMMAND, item->item_id, 0 );
961 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
967 /***********************************************************************
970 * Handle a button-down event in a menu. Point is in screen coords.
971 * hmenuCurrent is the top-most visible popup.
972 * Return TRUE if we can go on with menu tracking.
974 static BOOL MENU_ButtonDown( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
981 if (!hmenu) return FALSE; /* Outside all menus */
982 menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
983 item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
984 if (!item) /* Maybe in system menu */
986 if (!MENU_IsInSysMenu( menu, pt )) return FALSE;
987 id = SYSMENU_SELECTED;
990 if (menu->FocusedItem == id)
992 if (id == SYSMENU_SELECTED) return FALSE;
993 if (item->item_flags & MF_POPUP)
995 if (item->item_flags & MF_MOUSESELECT)
997 if (menu->wFlags & MF_POPUP)
999 MENU_HideSubPopups( hmenu );
1000 *hmenuCurrent = hmenu;
1004 else *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1009 MENU_HideSubPopups( hmenu );
1010 MENU_SelectItem( hmenu, id );
1011 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1017 /***********************************************************************
1020 * Handle a button-up event in a menu. Point is in screen coords.
1021 * hmenuCurrent is the top-most visible popup.
1022 * Return TRUE if we can go on with menu tracking.
1024 static BOOL MENU_ButtonUp( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1032 if (!hmenu) return FALSE; /* Outside all menus */
1033 menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
1034 item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1035 if (!item) /* Maybe in system menu */
1037 if (!MENU_IsInSysMenu( menu, pt )) return FALSE;
1038 id = SYSMENU_SELECTED;
1039 hsubmenu = GetSystemMenu( menu->hWnd, FALSE );
1042 if (menu->FocusedItem != id) return FALSE;
1044 if (id != SYSMENU_SELECTED)
1046 if (!(item->item_flags & MF_POPUP))
1048 return MENU_ExecFocusedItem( hwndOwner, hmenu, hmenuCurrent );
1050 hsubmenu = item->item_id;
1052 /* Select first item of sub-popup */
1053 MENU_SelectItem( hsubmenu, NO_SELECTED_ITEM );
1054 MENU_SelectNextItem( hsubmenu );
1059 /***********************************************************************
1062 * Handle a motion event in a menu. Point is in screen coords.
1063 * hmenuCurrent is the top-most visible popup.
1064 * Return TRUE if we can go on with menu tracking.
1066 static BOOL MENU_MouseMove( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent,
1070 POPUPMENU *menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
1071 WORD id = NO_SELECTED_ITEM;
1075 item = MENU_FindItemByCoords( menu, pt.x, pt.y, &id );
1076 if (!item) /* Maybe in system menu */
1078 if (!MENU_IsInSysMenu( menu, pt ))
1079 id = NO_SELECTED_ITEM; /* Outside all items */
1080 else id = SYSMENU_SELECTED;
1083 if (id == NO_SELECTED_ITEM)
1085 MENU_SelectItem( *hmenuCurrent, NO_SELECTED_ITEM );
1087 else if (menu->FocusedItem != id)
1089 MENU_HideSubPopups( hmenu );
1090 MENU_SelectItem( hmenu, id );
1091 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, FALSE );
1097 /***********************************************************************
1100 * Handle a VK_LEFT key event in a menu.
1101 * hmenuCurrent is the top-most visible popup.
1103 static void MENU_KeyLeft( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent )
1106 HMENU hmenutmp, hmenuprev;
1108 menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
1109 hmenuprev = hmenutmp = hmenu;
1110 while (hmenutmp != *hmenuCurrent)
1112 hmenutmp = MENU_GetSubPopup( hmenuprev );
1113 if (hmenutmp != *hmenuCurrent) hmenuprev = hmenutmp;
1115 MENU_HideSubPopups( hmenuprev );
1117 if ((hmenuprev == hmenu) && !(menu->wFlags & MF_POPUP))
1119 /* Select previous item on the menu bar */
1120 MENU_SelectPrevItem( hmenu );
1121 if (*hmenuCurrent != hmenu)
1123 /* A popup menu was displayed -> display the next one */
1124 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1127 else *hmenuCurrent = hmenuprev;
1131 /***********************************************************************
1134 * Handle a VK_RIGHT key event in a menu.
1135 * hmenuCurrent is the top-most visible popup.
1137 static void MENU_KeyRight( HWND hwndOwner, HMENU hmenu, HMENU *hmenuCurrent )
1142 menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
1144 if ((menu->wFlags & MF_POPUP) || (*hmenuCurrent != hmenu))
1146 /* If already displaying a popup, try to display sub-popup */
1147 hmenutmp = MENU_ShowSubPopup( hwndOwner, *hmenuCurrent, TRUE );
1148 if (hmenutmp != *hmenuCurrent) /* Sub-popup displayed */
1150 *hmenuCurrent = hmenutmp;
1155 /* If on menu-bar, go to next item */
1156 if (!(menu->wFlags & MF_POPUP))
1158 MENU_HideSubPopups( hmenu );
1159 MENU_SelectNextItem( hmenu );
1160 if (*hmenuCurrent != hmenu)
1162 /* A popup menu was displayed -> display the next one */
1163 *hmenuCurrent = MENU_ShowSubPopup( hwndOwner, hmenu, TRUE );
1166 else if (*hmenuCurrent != hmenu) /* Hide last level popup */
1169 hmenuprev = hmenutmp = hmenu;
1170 while (hmenutmp != *hmenuCurrent)
1172 hmenutmp = MENU_GetSubPopup( hmenuprev );
1173 if (hmenutmp != *hmenuCurrent) hmenuprev = hmenutmp;
1175 MENU_HideSubPopups( hmenuprev );
1176 *hmenuCurrent = hmenuprev;
1181 /***********************************************************************
1184 * Menu tracking code.
1185 * If 'x' and 'y' are not 0, we simulate a button-down event at (x,y)
1186 * before beginning tracking. This is to help menu-bar tracking.
1188 static BOOL MENU_TrackMenu( HMENU hmenu, WORD wFlags, int x, int y,
1189 HWND hwnd, LPRECT lprect )
1193 HMENU hmenuCurrent = hmenu;
1194 BOOL fClosed = FALSE;
1197 fEndMenuCalled = FALSE;
1198 if (!(menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu ))) return FALSE;
1201 POINT pt = { x, y };
1202 MENU_ButtonDown( hwnd, hmenu, &hmenuCurrent, pt );
1208 if (!MSG_InternalGetMessage( &msg, 0, hwnd, MSGF_MENU, 0, TRUE ))
1211 if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
1213 /* Find the sub-popup for this mouse event (if any) */
1214 HMENU hsubmenu = MENU_FindMenuByCoords( hmenu, msg.pt );
1218 case WM_RBUTTONDOWN:
1219 case WM_NCRBUTTONDOWN:
1220 if (!(wFlags & TPM_RIGHTBUTTON)) break;
1222 case WM_LBUTTONDOWN:
1223 case WM_NCLBUTTONDOWN:
1224 fClosed = !MENU_ButtonDown( hwnd, hsubmenu,
1225 &hmenuCurrent, msg.pt );
1229 case WM_NCRBUTTONUP:
1230 if (!(wFlags & TPM_RIGHTBUTTON)) break;
1233 case WM_NCLBUTTONUP:
1234 /* If outside all menus but inside lprect, ignore it */
1235 if (!hsubmenu && lprect && PtInRect( lprect, msg.pt )) break;
1236 fClosed = !MENU_ButtonUp( hwnd, hsubmenu,
1237 &hmenuCurrent, msg.pt );
1241 case WM_NCMOUSEMOVE:
1242 if ((msg.wParam & MK_LBUTTON) ||
1243 ((wFlags & TPM_RIGHTBUTTON) && (msg.wParam & MK_RBUTTON)))
1245 fClosed = !MENU_MouseMove( hwnd, hsubmenu,
1246 &hmenuCurrent, msg.pt );
1251 else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
1259 MENU_SelectItem( hmenuCurrent, NO_SELECTED_ITEM );
1260 MENU_SelectNextItem( hmenuCurrent );
1264 MENU_SelectItem( hmenuCurrent, NO_SELECTED_ITEM );
1265 MENU_SelectPrevItem( hmenuCurrent );
1269 MENU_SelectPrevItem( hmenuCurrent );
1273 /* If on menu bar, pull-down the menu */
1274 if (!(menu->wFlags & MF_POPUP) && (hmenuCurrent == hmenu))
1275 hmenuCurrent = MENU_ShowSubPopup( hwnd, hmenu, TRUE );
1277 MENU_SelectNextItem( hmenuCurrent );
1281 MENU_KeyLeft( hwnd, hmenu, &hmenuCurrent );
1285 MENU_KeyRight( hwnd, hmenu, &hmenuCurrent );
1290 fClosed = !MENU_ExecFocusedItem( hwnd, hmenuCurrent,
1301 break; /* WM_KEYDOWN */
1311 break; /* WM_SYSKEYDOWN */
1315 /* Hack to avoid control chars. */
1316 /* We will find a better way real soon... */
1317 if ((msg.wParam <= 32) || (msg.wParam >= 127)) break;
1318 pos = MENU_FindItemByKey( hwnd, hmenuCurrent, msg.wParam );
1319 if (pos == (WORD)-2) fClosed = TRUE;
1320 else if (pos == (WORD)-1) MessageBeep(0);
1323 MENU_SelectItem( hmenuCurrent, pos );
1324 fClosed = !MENU_ExecFocusedItem( hwnd, hmenuCurrent,
1329 break; /* WM_CHAR */
1330 } /* switch(msg.message) */
1334 DispatchMessage( &msg );
1336 if (fEndMenuCalled) fClosed = TRUE;
1338 if (!fClosed) /* Remove the message from the queue */
1339 PeekMessage( &msg, 0, 0, 0, PM_REMOVE );
1342 MENU_HideSubPopups( hmenu );
1343 if (menu->wFlags & MF_POPUP) ShowWindow( menu->hWnd, SW_HIDE );
1344 MENU_SelectItem( hmenu, NO_SELECTED_ITEM );
1345 fEndMenuCalled = FALSE;
1350 /***********************************************************************
1351 * MENU_TrackMouseMenuBar
1353 * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
1355 void MENU_TrackMouseMenuBar( HWND hwnd, POINT pt )
1357 WND *wndPtr = WIN_FindWndPtr( hwnd );
1358 SendMessage( hwnd, WM_ENTERMENULOOP, 0, 0 );
1359 MENU_TrackMenu( (HMENU)wndPtr->wIDmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON,
1360 pt.x, pt.y, hwnd, NULL );
1361 SendMessage( hwnd, WM_EXITMENULOOP, 0, 0 );
1365 /***********************************************************************
1366 * MENU_TrackKbdMenuBar
1368 * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
1370 void MENU_TrackKbdMenuBar( HWND hwnd, WORD wParam )
1372 WND *wndPtr = WIN_FindWndPtr( hwnd );
1373 SendMessage( hwnd, WM_ENTERMENULOOP, 0, 0 );
1374 /* Select first selectable item */
1375 MENU_SelectItem( wndPtr->wIDmenu, NO_SELECTED_ITEM );
1376 MENU_SelectNextItem( (HMENU)wndPtr->wIDmenu );
1377 MENU_TrackMenu( (HMENU)wndPtr->wIDmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON,
1379 SendMessage( hwnd, WM_EXITMENULOOP, 0, 0 );
1383 /**********************************************************************
1384 * TrackPopupMenu [USER.416]
1386 BOOL TrackPopupMenu( HMENU hMenu, WORD wFlags, short x, short y,
1387 short nReserved, HWND hWnd, LPRECT lpRect )
1389 if (!MENU_ShowPopup( hWnd, hMenu, 0, x, y )) return FALSE;
1390 return MENU_TrackMenu( hMenu, wFlags, 0, 0, hWnd, lpRect );
1394 /***********************************************************************
1397 LONG PopupMenuWndProc( HWND hwnd, WORD message, WORD wParam, LONG lParam )
1403 CREATESTRUCT *createStruct = (CREATESTRUCT *)lParam;
1404 HMENU hmenu = (HMENU) ((int)createStruct->lpCreateParams & 0xffff);
1405 SetWindowWord( hwnd, 0, hmenu );
1409 case WM_MOUSEACTIVATE: /* We don't want to be activated */
1410 return MA_NOACTIVATE;
1415 BeginPaint( hwnd, &ps );
1416 MENU_DrawPopupMenu( hwnd, ps.hdc,
1417 (HMENU)GetWindowWord( hwnd, 0 ) );
1418 EndPaint( hwnd, &ps );
1423 return DefWindowProc(hwnd, message, wParam, lParam);
1429 /***********************************************************************
1430 * MENU_GetMenuBarHeight
1432 * Compute the size of the menu bar height. Used by NC_HandleNCCalcSize().
1434 WORD MENU_GetMenuBarHeight( HWND hwnd, WORD menubarWidth, int orgX, int orgY )
1441 if (!(wndPtr = WIN_FindWndPtr( hwnd ))) return 0;
1442 if (!(lppop = (LPPOPUPMENU)USER_HEAP_ADDR( wndPtr->wIDmenu ))) return 0;
1443 hdc = GetDC( hwnd );
1444 SetRect( &rectBar, orgX, orgY, orgX+menubarWidth, orgY+SYSMETRICS_CYMENU );
1445 MENU_MenuBarCalcSize( hdc, &rectBar, lppop, hwnd );
1446 ReleaseDC( hwnd, hdc );
1447 return lppop->Height;
1451 /**********************************************************************
1452 * ChangeMenu [USER.153]
1454 BOOL ChangeMenu(HMENU hMenu, WORD nPos, LPSTR lpNewItem,
1455 WORD wItemID, WORD wFlags)
1457 if (wFlags & MF_APPEND)
1458 return AppendMenu(hMenu, wFlags, wItemID, lpNewItem);
1459 if (wFlags & MF_DELETE)
1460 return DeleteMenu(hMenu, wItemID, wFlags);
1461 if (wFlags & MF_INSERT)
1462 return InsertMenu(hMenu, nPos, wFlags, wItemID, lpNewItem);
1463 if (wFlags & MF_CHANGE)
1464 return ModifyMenu(hMenu, nPos, wFlags, wItemID, lpNewItem);
1465 if (wFlags & MF_REMOVE)
1466 return RemoveMenu(hMenu, wItemID, wFlags);
1471 /**********************************************************************
1472 * CheckMenuItem [USER.154]
1474 BOOL CheckMenuItem(HMENU hMenu, WORD wItemID, WORD wFlags)
1477 dprintf_menu(stddeb,"CheckMenuItem (%04X, %04X, %04X) !\n",
1478 hMenu, wItemID, wFlags);
1479 if (!(lpitem = MENU_FindItem(&hMenu, &wItemID, wFlags))) return FALSE;
1480 if (wFlags & MF_CHECKED) lpitem->item_flags |= MF_CHECKED;
1481 else lpitem->item_flags &= ~MF_CHECKED;
1486 /**********************************************************************
1487 * EnableMenuItem [USER.155]
1489 BOOL EnableMenuItem(HMENU hMenu, WORD wItemID, WORD wFlags)
1492 dprintf_menu(stddeb,"EnableMenuItem (%04X, %04X, %04X) !\n",
1493 hMenu, wItemID, wFlags);
1494 if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return FALSE;
1496 /* We can't have MF_GRAYED and MF_DISABLED together */
1497 if (wFlags & MF_GRAYED)
1499 lpitem->item_flags = (lpitem->item_flags & ~MF_DISABLED) | MF_GRAYED;
1501 else if (wFlags & MF_DISABLED)
1503 lpitem->item_flags = (lpitem->item_flags & ~MF_GRAYED) | MF_DISABLED;
1505 else /* MF_ENABLED */
1507 lpitem->item_flags &= ~(MF_GRAYED | MF_DISABLED);
1513 /**********************************************************************
1514 * GetMenuString [USER.161]
1516 int GetMenuString(HMENU hMenu, WORD wItemID,
1517 LPSTR str, short nMaxSiz, WORD wFlags)
1521 dprintf_menu(stddeb,"GetMenuString(%04X, %04X, %p, %d, %04X);\n",
1522 hMenu, wItemID, str, nMaxSiz, wFlags);
1523 if (str == NULL) return FALSE;
1524 lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags );
1525 if (lpitem != NULL) {
1526 if (lpitem->item_text != NULL) {
1527 maxsiz = min(nMaxSiz - 1, strlen(lpitem->item_text));
1528 strncpy(str, lpitem->item_text, maxsiz + 1);
1532 dprintf_menu(stddeb,"GetMenuString // Found !\n");
1539 /**********************************************************************
1540 * HiliteMenuItem [USER.162]
1542 BOOL HiliteMenuItem(HWND hWnd, HMENU hMenu, WORD wItemID, WORD wHilite)
1546 dprintf_menu(stddeb,"HiliteMenuItem(%04X, %04X, %04X, %04X);\n",
1547 hWnd, hMenu, wItemID, wHilite);
1548 if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wHilite ))) return FALSE;
1549 if (!(menu = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu))) return FALSE;
1550 if (menu->FocusedItem == wItemID) return TRUE;
1551 MENU_HideSubPopups( hMenu );
1552 MENU_SelectItem( hMenu, wItemID );
1557 /**********************************************************************
1558 * GetMenuState [USER.250]
1560 WORD GetMenuState(HMENU hMenu, WORD wItemID, WORD wFlags)
1563 dprintf_menu(stddeb,"GetMenuState(%04X, %04X, %04X);\n",
1564 hMenu, wItemID, wFlags);
1565 if (!(lpitem = MENU_FindItem( &hMenu, &wItemID, wFlags ))) return -1;
1566 if (lpitem->item_flags & MF_POPUP)
1568 POPUPMENU *menu = (POPUPMENU *) USER_HEAP_ADDR( lpitem->item_id );
1569 if (!menu) return -1;
1570 else return (menu->nItems << 8) | (menu->wFlags & 0xff);
1572 else return lpitem->item_flags;
1576 /**********************************************************************
1577 * GetMenuItemCount [USER.263]
1579 WORD GetMenuItemCount(HMENU hMenu)
1582 dprintf_menu(stddeb,"GetMenuItemCount(%04X);\n", hMenu);
1583 menu = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu);
1584 if (menu == NULL) return (WORD)-1;
1585 dprintf_menu(stddeb,"GetMenuItemCount(%04X) return %d \n",
1586 hMenu, menu->nItems);
1587 return menu->nItems;
1591 /**********************************************************************
1592 * GetMenuItemID [USER.264]
1594 WORD GetMenuItemID(HMENU hMenu, int nPos)
1599 dprintf_menu(stddeb,"GetMenuItemID(%04X, %d);\n", hMenu, nPos);
1600 if (!(menu = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu))) return -1;
1601 if ((nPos < 0) || (nPos >= menu->nItems)) return -1;
1602 item = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
1603 if (item[nPos].item_flags & MF_POPUP) return -1;
1604 return item[nPos].item_id;
1608 /**********************************************************************
1609 * InsertMenu [USER.410]
1611 BOOL InsertMenu(HMENU hMenu, WORD nPos, WORD wFlags, WORD wItemID, LPSTR lpNewItem)
1614 MENUITEM *lpitem, *newItems;
1617 if (IS_STRING_ITEM(wFlags))
1619 dprintf_menu(stddeb,"InsertMenu (%04X, %04X, %04X, %04X, '%s') !\n",
1620 hMenu, nPos, wFlags, wItemID, lpNewItem);
1623 dprintf_menu(stddeb,"InsertMenu (%04X, %04X, %04X, %04X, %p) !\n",
1624 hMenu, nPos, wFlags, wItemID, lpNewItem);
1626 /* Find where to insert new item */
1628 if ((wFlags & MF_BYPOSITION) && (nPos == (WORD)-1))
1630 /* Special case: append to menu */
1631 if (!(menu = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu))) return FALSE;
1632 nPos = menu->nItems;
1636 if (!MENU_FindItem( &hMenu, &nPos, wFlags )) return FALSE;
1637 if (!(menu = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu))) return FALSE;
1640 /* Create new items array */
1642 hNewItems = USER_HEAP_ALLOC( GMEM_MOVEABLE,
1643 sizeof(MENUITEM) * (menu->nItems+1) );
1644 if (!hNewItems) return FALSE;
1645 newItems = (MENUITEM *) USER_HEAP_ADDR( hNewItems );
1646 if (menu->nItems > 0)
1648 /* Copy the old array into the new */
1649 MENUITEM *oldItems = (MENUITEM *) USER_HEAP_ADDR( menu->hItems );
1650 if (nPos > 0) memcpy( newItems, oldItems, nPos * sizeof(MENUITEM) );
1651 if (nPos < menu->nItems) memcpy( &newItems[nPos+1], &oldItems[nPos],
1652 (menu->nItems-nPos)*sizeof(MENUITEM) );
1654 USER_HEAP_FREE( menu->hItems );
1656 menu->hItems = hNewItems;
1659 /* Store the new item data */
1661 lpitem = &newItems[nPos];
1662 lpitem->item_flags = wFlags & ~(MF_HILITE | MF_MOUSESELECT);
1663 lpitem->item_id = wItemID;
1665 if (IS_STRING_ITEM(wFlags))
1667 /* Item beginning with a backspace is a help item */
1668 if (lpNewItem[0] == '\b')
1670 lpitem->item_flags |= MF_HELP;
1673 lpitem->hText = USER_HEAP_ALLOC( GMEM_MOVEABLE, strlen(lpNewItem)+1 );
1674 lpitem->item_text = (char *)USER_HEAP_ADDR( lpitem->hText );
1675 strcpy( lpitem->item_text, lpNewItem );
1677 else if (wFlags & MF_BITMAP) lpitem->hText = LOWORD((DWORD)lpNewItem);
1678 else lpitem->item_text = lpNewItem;
1680 if (wFlags & MF_POPUP) /* Set the MF_POPUP flag on the popup-menu */
1681 ((POPUPMENU *)USER_HEAP_ADDR(wItemID))->wFlags |= MF_POPUP;
1683 SetRectEmpty( &lpitem->rect );
1684 lpitem->hCheckBit = hStdCheck;
1685 lpitem->hUnCheckBit = 0;
1690 /**********************************************************************
1691 * AppendMenu [USER.411]
1693 BOOL AppendMenu(HMENU hMenu, WORD wFlags, WORD wItemID, LPSTR lpNewItem)
1695 return InsertMenu( hMenu, -1, wFlags | MF_BYPOSITION, wItemID, lpNewItem );
1699 /**********************************************************************
1700 * RemoveMenu [USER.412]
1702 BOOL RemoveMenu(HMENU hMenu, WORD nPos, WORD wFlags)
1706 dprintf_menu(stddeb,"RemoveMenu (%04X, %04X, %04X) !\n",
1707 hMenu, nPos, wFlags);
1708 if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
1709 if (!(menu = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu))) return FALSE;
1713 if (IS_STRING_ITEM(lpitem->item_flags)) USER_HEAP_FREE( lpitem->hText );
1714 if (--menu->nItems == 0)
1716 USER_HEAP_FREE( menu->hItems );
1721 while(nPos < menu->nItems)
1723 *lpitem = *(lpitem+1);
1727 menu->hItems = USER_HEAP_REALLOC( menu->hItems,
1728 menu->nItems * sizeof(MENUITEM),
1735 /**********************************************************************
1736 * DeleteMenu [USER.413]
1738 BOOL DeleteMenu(HMENU hMenu, WORD nPos, WORD wFlags)
1740 MENUITEM *item = MENU_FindItem( &hMenu, &nPos, wFlags );
1741 if (!item) return FALSE;
1742 if (item->item_flags & MF_POPUP) DestroyMenu( item->item_id );
1743 /* nPos is now the position of the item */
1744 RemoveMenu( hMenu, nPos, wFlags | MF_BYPOSITION );
1749 /**********************************************************************
1750 * ModifyMenu [USER.414]
1752 BOOL ModifyMenu(HMENU hMenu, WORD nPos, WORD wFlags, WORD wItemID, LPSTR lpNewItem)
1755 if (IS_STRING_ITEM(wFlags))
1756 dprintf_menu(stddeb,"ModifyMenu (%04X, %04X, %04X, %04X, '%s') !\n",
1757 hMenu, nPos, wFlags, wItemID, lpNewItem);
1759 dprintf_menu(stddeb,"ModifyMenu (%04X, %04X, %04X, %04X, %p) !\n",
1760 hMenu, nPos, wFlags, wItemID, lpNewItem);
1761 if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
1763 if (IS_STRING_ITEM(lpitem->item_flags)) USER_HEAP_FREE( lpitem->hText );
1764 lpitem->item_flags = wFlags & ~(MF_HILITE | MF_MOUSESELECT);
1765 lpitem->item_id = wItemID;
1767 if (IS_STRING_ITEM(wFlags))
1769 lpitem->hText = USER_HEAP_ALLOC( GMEM_MOVEABLE, strlen(lpNewItem)+1 );
1770 lpitem->item_text = (char *)USER_HEAP_ADDR( lpitem->hText );
1771 strcpy( lpitem->item_text, lpNewItem );
1773 else if (wFlags & MF_BITMAP) lpitem->hText = LOWORD((DWORD)lpNewItem);
1774 else lpitem->item_text = lpNewItem;
1775 SetRectEmpty( &lpitem->rect );
1780 /**********************************************************************
1781 * CreatePopupMenu [USER.415]
1783 HMENU CreatePopupMenu()
1788 if (!(hmenu = CreateMenu())) return 0;
1789 menu = (POPUPMENU *) USER_HEAP_ADDR( hmenu );
1790 menu->wFlags |= MF_POPUP;
1795 /**********************************************************************
1796 * GetMenuCheckMarkDimensions [USER.417]
1798 DWORD GetMenuCheckMarkDimensions()
1800 return MAKELONG( check_bitmap_width, check_bitmap_height );
1804 /**********************************************************************
1805 * SetMenuItemBitmaps [USER.418]
1807 BOOL SetMenuItemBitmaps(HMENU hMenu, WORD nPos, WORD wFlags,
1808 HBITMAP hNewCheck, HBITMAP hNewUnCheck)
1811 dprintf_menu(stddeb,"SetMenuItemBitmaps (%04X, %04X, %04X, %04X, %08X) !\n",
1812 hMenu, nPos, wFlags, hNewCheck, hNewUnCheck);
1813 if (!(lpitem = MENU_FindItem( &hMenu, &nPos, wFlags ))) return FALSE;
1815 if (!hNewCheck && !hNewUnCheck)
1817 /* If both are NULL, restore default bitmaps */
1818 lpitem->hCheckBit = hStdCheck;
1819 lpitem->hUnCheckBit = 0;
1820 lpitem->item_flags &= ~MF_USECHECKBITMAPS;
1822 else /* Install new bitmaps */
1824 lpitem->hCheckBit = hNewCheck;
1825 lpitem->hUnCheckBit = hNewUnCheck;
1826 lpitem->item_flags |= MF_USECHECKBITMAPS;
1832 /**********************************************************************
1833 * CreateMenu [USER.151]
1839 dprintf_menu(stddeb,"CreateMenu !\n");
1840 if (!(hMenu = USER_HEAP_ALLOC( GMEM_MOVEABLE, sizeof(POPUPMENU) )))
1842 menu = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu);
1845 menu->wMagic = MENU_MAGIC;
1852 menu->FocusedItem = NO_SELECTED_ITEM;
1853 dprintf_menu(stddeb,"CreateMenu // return %04X\n", hMenu);
1858 /**********************************************************************
1859 * DestroyMenu [USER.152]
1861 BOOL DestroyMenu(HMENU hMenu)
1864 dprintf_menu(stddeb,"DestroyMenu (%04X) !\n", hMenu);
1865 if (hMenu == 0) return FALSE;
1866 lppop = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu);
1867 if (lppop == NULL) return FALSE;
1868 if ((lppop->wFlags & MF_POPUP) && lppop->hWnd)
1869 DestroyWindow( lppop->hWnd );
1874 MENUITEM *item = (MENUITEM *) USER_HEAP_ADDR( lppop->hItems );
1875 for (i = lppop->nItems; i > 0; i--, item++)
1877 if (item->item_flags & MF_POPUP)
1878 DestroyMenu( item->item_id );
1880 USER_HEAP_FREE( lppop->hItems );
1882 USER_HEAP_FREE( hMenu );
1883 dprintf_menu(stddeb,"DestroyMenu (%04X) // End !\n", hMenu);
1887 /**********************************************************************
1888 * GetSystemMenu [USER.156]
1890 HMENU GetSystemMenu(HWND hWnd, BOOL bRevert)
1893 wndPtr = WIN_FindWndPtr(hWnd);
1895 return wndPtr->hSysMenu;
1898 DestroyMenu(wndPtr->hSysMenu);
1899 wndPtr->hSysMenu = CopySysMenu();
1901 return wndPtr->hSysMenu;
1904 /**********************************************************************
1905 * SetSystemMenu [USER.280]
1907 BOOL SetSystemMenu(HWND hWnd, HMENU newHmenu)
1911 if ((wndPtr = WIN_FindWndPtr(hWnd)) != NULL) wndPtr->hSysMenu = newHmenu;
1916 /**********************************************************************
1917 * GetMenu [USER.157]
1919 HMENU GetMenu(HWND hWnd)
1921 WND * wndPtr = WIN_FindWndPtr(hWnd);
1922 if (wndPtr == NULL) return 0;
1923 return wndPtr->wIDmenu;
1927 /**********************************************************************
1928 * SetMenu [USER.158]
1930 BOOL SetMenu(HWND hWnd, HMENU hMenu)
1933 WND * wndPtr = WIN_FindWndPtr(hWnd);
1934 if (wndPtr == NULL) {
1935 fprintf(stderr,"SetMenu(%04X, %04X) // Bad window handle !\n",
1939 dprintf_menu(stddeb,"SetMenu(%04X, %04X);\n", hWnd, hMenu);
1940 if (GetCapture() == hWnd) ReleaseCapture();
1941 wndPtr->wIDmenu = hMenu;
1944 lpmenu = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu);
1945 if (lpmenu == NULL) {
1946 fprintf(stderr,"SetMenu(%04X, %04X) // Bad menu handle !\n",
1950 lpmenu->hWnd = hWnd;
1951 lpmenu->wFlags &= ~MF_POPUP; /* Can't be a popup */
1952 lpmenu->Height = 0; /* Make sure we recalculate the size */
1954 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
1955 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
1961 /**********************************************************************
1962 * GetSubMenu [USER.159]
1964 HMENU GetSubMenu(HMENU hMenu, short nPos)
1968 dprintf_menu(stddeb,"GetSubMenu (%04X, %04X) !\n", hMenu, nPos);
1969 if (!(lppop = (LPPOPUPMENU) USER_HEAP_ADDR(hMenu))) return 0;
1970 if ((WORD)nPos >= lppop->nItems) return 0;
1971 lpitem = (MENUITEM *) USER_HEAP_ADDR( lppop->hItems );
1972 if (!(lpitem[nPos].item_flags & MF_POPUP)) return 0;
1973 return lpitem[nPos].item_id;
1977 /**********************************************************************
1978 * DrawMenuBar [USER.160]
1980 void DrawMenuBar(HWND hWnd)
1984 dprintf_menu(stddeb,"DrawMenuBar (%04X)\n", hWnd);
1985 wndPtr = WIN_FindWndPtr(hWnd);
1986 if (wndPtr != NULL && (wndPtr->dwStyle & WS_CHILD) == 0 &&
1987 wndPtr->wIDmenu != 0) {
1988 dprintf_menu(stddeb,"DrawMenuBar wIDmenu=%04X \n",
1990 lppop = (LPPOPUPMENU) USER_HEAP_ADDR(wndPtr->wIDmenu);
1991 if (lppop == NULL) return;
1993 lppop->Height = 0; /* Make sure we call MENU_MenuBarCalcSize */
1994 SetWindowPos( hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
1995 SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
2000 /***********************************************************************
2001 * EndMenu (USER.187)
2005 /* Note: this won't work when we have multiple tasks... */
2006 fEndMenuCalled = TRUE;
2010 /***********************************************************************
2011 * LookupMenuHandle (USER.217)
2013 HMENU LookupMenuHandle( HMENU hmenu, INT id )
2015 if (!MENU_FindItem( &hmenu, &id, MF_BYCOMMAND )) return 0;
2020 /**********************************************************************
2021 * LoadMenuIndirect [USER.220]
2023 HMENU LoadMenuIndirect(LPSTR menu_template)
2026 MENU_HEADER *menu_desc;
2027 dprintf_menu(stddeb,"LoadMenuIndirect: menu_template '%p'\n",
2029 hMenu = CreateMenu();
2030 menu_desc = (MENU_HEADER *)menu_template;
2031 ParseMenuResource((WORD *)(menu_desc + 1), 0, hMenu);
2036 /**********************************************************************
2037 * CopySysMenu (Internal)
2043 extern unsigned char sysres_MENU_SYSMENU[];
2045 hMenu=LoadMenuIndirect(sysres_MENU_SYSMENU);
2047 dprintf_menu(stddeb,"No SYSMENU\n");
2050 menu = (POPUPMENU*) USER_HEAP_ADDR(hMenu);
2051 menu->wFlags |= MF_SYSMENU|MF_POPUP;
2052 dprintf_menu(stddeb,"CopySysMenu hMenu=%04X !\n", hMenu);
2057 /**********************************************************************
2058 * ParseMenuResource (from Resource or Template)
2060 WORD * ParseMenuResource(WORD *first_item, int level, HMENU hMenu)
2068 next_item = first_item;
2073 if (*item & MF_POPUP) {
2074 MENU_POPUPITEM *popup_item = (MENU_POPUPITEM *) item;
2075 next_item = (WORD *) (popup_item->item_text +
2076 strlen(popup_item->item_text) + 1);
2077 hSubMenu = CreatePopupMenu();
2078 next_item = ParseMenuResource(next_item, level, hSubMenu);
2079 AppendMenu(hMenu, popup_item->item_flags,
2080 hSubMenu, popup_item->item_text);
2083 MENUITEMTEMPLATE *normal_item = (MENUITEMTEMPLATE *) item;
2084 next_item = (WORD *) (normal_item->item_text +
2085 strlen(normal_item->item_text) + 1);
2086 if (strlen(normal_item->item_text) == 0 && normal_item->item_id == 0)
2087 normal_item->item_flags |= MF_SEPARATOR;
2088 AppendMenu(hMenu, normal_item->item_flags,
2089 normal_item->item_id, normal_item->item_text);
2092 while (!(*item & MF_END));
2097 /**********************************************************************
2100 BOOL IsMenu( HMENU hmenu )
2103 if (!(menu = (LPPOPUPMENU) USER_HEAP_ADDR( hmenu ))) return FALSE;
2104 return (menu->wMagic == MENU_MAGIC);