RESEND #3: user: Scrolling support for popup menus which don't fit
on the desktop
Phil Krylov
phil at newstar.rinet.ru
Fri Feb 17 10:46:11 CST 2006
Hi,
This is the 3rd time I send this patch. Alexandre, could you please just tell
me what's so wrong with it, that it can't be committed?
-- Ph.
dlls/user/menu.c | 228 +++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 214 insertions(+), 14 deletions(-)
d3502d2a37be6a93585b57c21ee964d7e9ca489b
diff --git a/dlls/user/menu.c b/dlls/user/menu.c
index fa22e2b..4aa1900 100644
--- a/dlls/user/menu.c
+++ b/dlls/user/menu.c
@@ -5,6 +5,7 @@
* Copyright 1994 Alexandre Julliard
* Copyright 1997 Morten Welinder
+ * Copyright 2006 Phil Krylov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -93,9 +94,12 @@ typedef struct {
UINT FocusedItem; /* Currently focused item */
HWND hwndOwner; /* window receiving the messages for ownerdraw */
BOOL bTimeToHide; /* Request hiding when receiving a second click in the top-level menu item */
+ BOOL bScrolling; /* Scroll arrows are active */
+ UINT nScrollPos; /* Current scroll position */
+ UINT nTotalHeight; /* Total height of menu items inside menu */
/* ------------ MENUINFO members ------ */
- DWORD dwStyle; /* Extended mennu style */
- UINT cyMax; /* max hight of the whole menu, 0 is screen hight */
+ DWORD dwStyle; /* Extended menu style */
+ UINT cyMax; /* max height of the whole menu, 0 is screen height */
HBRUSH hbrBack; /* brush for menu background */
DWORD dwContextHelpID;
DWORD dwMenuData; /* application defined value */
@@ -141,6 +145,10 @@ typedef struct
/* Space between 2 columns */
#define MENU_COL_SPACE 4
+ /* top and bottom margins for popup menus */
+#define MENU_TOP_MARGIN 3
+#define MENU_BOTTOM_MARGIN 2
+
/* (other menu->FocusedItem values give the position of the focused item) */
#define NO_SELECTED_ITEM 0xffff
@@ -355,6 +363,50 @@ static HBITMAP get_arrow_bitmap(void)
}
/***********************************************************************
+ * get_down_arrow_bitmap
+ */
+static HBITMAP get_down_arrow_bitmap(void)
+{
+ static HBITMAP arrow_bitmap;
+
+ if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
+ return arrow_bitmap;
+}
+
+/***********************************************************************
+ * get_down_arrow_inactive_bitmap
+ */
+static HBITMAP get_down_arrow_inactive_bitmap(void)
+{
+ static HBITMAP arrow_bitmap;
+
+ if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
+ return arrow_bitmap;
+}
+
+/***********************************************************************
+ * get_up_arrow_bitmap
+ */
+static HBITMAP get_up_arrow_bitmap(void)
+{
+ static HBITMAP arrow_bitmap;
+
+ if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
+ return arrow_bitmap;
+}
+
+/***********************************************************************
+ * get_up_arrow_inactive_bitmap
+ */
+static HBITMAP get_up_arrow_inactive_bitmap(void)
+{
+ static HBITMAP arrow_bitmap;
+
+ if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
+ return arrow_bitmap;
+}
+
+/***********************************************************************
* MENU_CopySysPopup
*
* Return the default system menu.
@@ -608,6 +660,28 @@ static void MENU_FreeItemData( MENUITEM*
}
/***********************************************************************
+ * MENU_AdjustMenuItemRect
+ *
+ * Adjust menu item rectangle according to scrolling state.
+ */
+static void
+MENU_AdjustMenuItemRect(const POPUPMENU *menu, LPRECT rect)
+{
+ if (menu->bScrolling)
+ {
+ UINT arrow_bitmap_width, arrow_bitmap_height;
+ BITMAP bmp;
+
+ GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
+ arrow_bitmap_width = bmp.bmWidth;
+ arrow_bitmap_height = bmp.bmHeight;
+ rect->top += arrow_bitmap_height - menu->nScrollPos;
+ rect->bottom += arrow_bitmap_height - menu->nScrollPos;
+ }
+}
+
+
+/***********************************************************************
* MENU_FindItemByCoords
*
* Find the item at the specified coordinates (screen coords). Does
@@ -620,14 +694,17 @@ static MENUITEM *MENU_FindItemByCoords(
MENUITEM *item;
UINT i;
RECT wrect;
+ RECT rect;
if (!GetWindowRect(menu->hWnd,&wrect)) return NULL;
pt.x -= wrect.left;pt.y -= wrect.top;
item = menu->items;
for (i = 0; i < menu->nItems; i++, item++)
{
- if ((pt.x >= item->rect.left) && (pt.x < item->rect.right) &&
- (pt.y >= item->rect.top) && (pt.y < item->rect.bottom))
+ rect = item->rect;
+ MENU_AdjustMenuItemRect(menu, &rect);
+ if ((pt.x >= rect.left) && (pt.x < rect.right) &&
+ (pt.y >= rect.top) && (pt.y < rect.bottom))
{
if (pos) *pos = i;
return item;
@@ -963,6 +1040,18 @@ static void MENU_CalcItemSize( HDC hdc,
/***********************************************************************
+ * MENU_GetMaxPopupHeight
+ */
+static UINT
+MENU_GetMaxPopupHeight(LPPOPUPMENU lppop)
+{
+ if (lppop->cyMax)
+ return lppop->cyMax;
+ return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
+}
+
+
+/***********************************************************************
* MENU_PopupMenuCalcSize
*
* Calculate the size of a popup menu.
@@ -972,7 +1061,7 @@ static void MENU_PopupMenuCalcSize( LPPO
MENUITEM *lpitem;
HDC hdc;
int start, i;
- int orgX, orgY, maxX, maxTab, maxTabWidth;
+ int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
lppop->Width = lppop->Height = 0;
if (lppop->nItems == 0) return;
@@ -992,7 +1081,7 @@ static void MENU_PopupMenuCalcSize( LPPO
orgX = maxX;
if( lpitem->fType & MF_MENUBREAK)
orgX += MENU_COL_SPACE;
- orgY = 3;
+ orgY = MENU_TOP_MARGIN;
maxTab = maxTabWidth = 0;
/* Parse items until column break or end of menu */
@@ -1028,9 +1117,22 @@ static void MENU_PopupMenuCalcSize( LPPO
lppop->Width = maxX;
/* space for 3d border */
- lppop->Height += 2;
+ lppop->Height += MENU_BOTTOM_MARGIN;
lppop->Width += 2;
+ /* Adjust popup height if it exceeds maximum */
+ maxHeight = MENU_GetMaxPopupHeight(lppop);
+ lppop->nTotalHeight = lppop->Height - MENU_TOP_MARGIN;
+ if (lppop->Height >= maxHeight)
+ {
+ lppop->Height = maxHeight;
+ lppop->bScrolling = TRUE;
+ }
+ else
+ {
+ lppop->bScrolling = FALSE;
+ }
+
ReleaseDC( 0, hdc );
}
@@ -1109,6 +1211,52 @@ static void MENU_MenuBarCalcSize( HDC hd
}
}
+
+/***********************************************************************
+ * MENU_DrawScrollArrows
+ *
+ * Draw scroll arrows.
+ */
+static void
+MENU_DrawScrollArrows(LPPOPUPMENU lppop, HDC hdc)
+{
+ HDC hdcMem = CreateCompatibleDC(hdc);
+ HBITMAP hOrigBitmap;
+ UINT arrow_bitmap_width, arrow_bitmap_height;
+ BITMAP bmp;
+ RECT rect;
+
+ GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
+ arrow_bitmap_width = bmp.bmWidth;
+ arrow_bitmap_height = bmp.bmHeight;
+
+
+ if (lppop->nScrollPos)
+ hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
+ else
+ hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
+ rect.left = 0;
+ rect.top = 0;
+ rect.right = lppop->Width;
+ rect.bottom = arrow_bitmap_height;
+ FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
+ BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2, 0,
+ arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
+ rect.top = lppop->Height - arrow_bitmap_height;
+ rect.bottom = lppop->Height;
+ FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
+ if (lppop->nScrollPos < lppop->nTotalHeight - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
+ SelectObject(hdcMem, get_down_arrow_bitmap());
+ else
+ SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
+ BitBlt(hdc, (lppop->Width - arrow_bitmap_width) / 2,
+ lppop->Height - arrow_bitmap_height,
+ arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
+ SelectObject(hdcMem, hOrigBitmap);
+ DeleteDC(hdcMem);
+}
+
+
/***********************************************************************
* MENU_DrawMenuItem
*
@@ -1182,6 +1330,7 @@ static void MENU_DrawMenuItem( HWND hwnd
dis.hwndItem = (HWND)hmenu;
dis.hDC = hdc;
dis.rcItem = lpitem->rect;
+ MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &dis.rcItem);
TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
"hwndItem=%p, hdc=%p, rcItem=%s\n", hwndOwner,
dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
@@ -1195,6 +1344,7 @@ static void MENU_DrawMenuItem( HWND hwnd
if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
rect = lpitem->rect;
+ MENU_AdjustMenuItemRect(MENU_GetMenu(hmenu), &rect);
if (!(lpitem->fType & MF_OWNERDRAW))
{
@@ -1487,9 +1637,9 @@ static void MENU_DrawPopupMenu( HWND hwn
else
DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
- /* draw menu items */
-
menu = MENU_GetMenu( hmenu );
+
+ /* draw menu items */
if (menu && menu->nItems)
{
MENUITEM *item;
@@ -1500,7 +1650,11 @@ static void MENU_DrawPopupMenu( HWND hwn
menu->Height, FALSE, ODA_DRAWENTIRE );
}
- } else
+
+ /* draw scroll arrows */
+ if (menu->bScrolling)
+ MENU_DrawScrollArrows(menu, hdc);
+ } else
{
SelectObject( hdc, hPrevBrush );
}
@@ -1567,6 +1721,7 @@ static BOOL MENU_ShowPopup( HWND hwndOwn
/* store the owner for DrawItem */
menu->hwndOwner = hwndOwner;
+ menu->nScrollPos = 0;
MENU_PopupMenuCalcSize( menu, hwndOwner );
/* adjust popup menu pos so that it fits within the desktop */
@@ -1610,6 +1765,47 @@ static BOOL MENU_ShowPopup( HWND hwndOwn
/***********************************************************************
+ * MENU_EnsureMenuItemVisible
+ */
+static void
+MENU_EnsureMenuItemVisible(LPPOPUPMENU lppop, UINT wIndex, HDC hdc)
+{
+ if (lppop->bScrolling)
+ {
+ MENUITEM *item = &lppop->items[wIndex];
+ UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
+ UINT nOldPos = lppop->nScrollPos;
+ RECT rc;
+ UINT arrow_bitmap_height;
+ BITMAP bmp;
+
+ GetClientRect(lppop->hWnd, &rc);
+
+ GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
+ arrow_bitmap_height = bmp.bmHeight;
+
+ rc.top += arrow_bitmap_height;
+ rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
+
+ nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
+ if (item->rect.bottom > lppop->nScrollPos + nMaxHeight)
+ {
+
+ lppop->nScrollPos = item->rect.bottom - nMaxHeight;
+ ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
+ MENU_DrawScrollArrows(lppop, hdc);
+ }
+ else if (item->rect.top - MENU_TOP_MARGIN < lppop->nScrollPos)
+ {
+ lppop->nScrollPos = item->rect.top - MENU_TOP_MARGIN;
+ ScrollWindow(lppop->hWnd, 0, nOldPos - lppop->nScrollPos, &rc, &rc);
+ MENU_DrawScrollArrows(lppop, hdc);
+ }
+ }
+}
+
+
+/***********************************************************************
* MENU_SelectItem
*/
static void MENU_SelectItem( HWND hwndOwner, HMENU hmenu, UINT wIndex,
@@ -1645,6 +1841,7 @@ static void MENU_SelectItem( HWND hwndOw
{
if(!(lppop->items[wIndex].fType & MF_SEPARATOR)) {
lppop->items[wIndex].fState |= MF_HILITE;
+ MENU_EnsureMenuItemVisible(lppop, wIndex, hdc);
MENU_DrawMenuItem( lppop->hWnd, hmenu, hwndOwner, hdc,
&lppop->items[wIndex], lppop->Height,
!(lppop->wFlags & MF_POPUP), ODA_SELECT );
@@ -2074,10 +2271,13 @@ static HMENU MENU_ShowSubPopup( HWND hwn
GetWindowRect( menu->hWnd, &rect );
if (menu->wFlags & MF_POPUP)
{
- rect.left += item->rect.right - GetSystemMetrics(SM_CXBORDER);
- rect.top += item->rect.top;
- rect.right = item->rect.left - item->rect.right + GetSystemMetrics(SM_CXBORDER);
- rect.bottom = item->rect.top - item->rect.bottom;
+ RECT rc = item->rect;
+
+ MENU_AdjustMenuItemRect(menu, &rc);
+ rect.left += rc.right - GetSystemMetrics(SM_CXBORDER);
+ rect.top += rc.top;
+ rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
+ rect.bottom = rc.top - rc.bottom;
}
else
{
--
1.0.GIT
More information about the wine-patches
mailing list