#include "api__gen_ml.h" #include "main.h" #include "./skinnedmenuwnd.h" #include "./skinnedmenu.h" #include "./skinning.h" #include "./ml_imagelist.h" #include "./colors.h" #include "./resource.h" #include "../winamp/wa_dlg.h" #define MENU_BORDER_WIDTH 3 static HMLIMGLST hmlilCheck = NULL; static INT imageCheckMark = -1; static INT imageRadioMark = -1; static INT imageExpandArrow = -1; // menu hit test codes #define MHF_NOWHERE 0xFFFFFFFF #define MHF_SCROLLUP 0xFFFFFFFD #define MHF_SCROLLDOWN 0xFFFFFFFC #define MN_SIZEWINDOW 0x01E2 #define MN_SELECTITEM 0x01E5 // wParam - item position or MHF_XXX #define MN_LBUTTONDOWN 0x01ED // wParam - item position or MHF_XXX #define MN_LBUTTONUP 0x01EF // wParam - item position or MHF_XXX #define MN_LBUTTONDBLCLK 0x01F1 // ? // menu timer id #define MTID_OPENSUBMENU 0x0000FFFE #define MTID_SCROLLUP 0xFFFFFFFD #define MTID_SCROLLDOWN 0xFFFFFFFC #define MTID_EX_UNBLOCKDRAW 0x0001980 extern HMLIMGFLTRMNGR hmlifMngr; // default gen_ml fitler manager #define SMIF_BLOCKDRAW 0x00000001 #define SMIF_REMOVEREFLECTOR 0x00000002 static HBRUSH SkinnedMenuWnd_GetBackBrush(HMENU hMenu) { MENUINFO mi = {0}; mi.cbSize = sizeof(MENUINFO); mi.fMask = MIM_BACKGROUND; if (NULL == hMenu || !GetMenuInfo(hMenu, &mi)) mi.hbrBack = NULL; return (NULL != mi.hbrBack) ? mi.hbrBack : GetSysColorBrush(COLOR_MENU); } static INT SkinnedMenuWnd_AddPngResource(HMLIMGLST imageList, INT resourceId) { MLIMAGESOURCE_I src; ZeroMemory(&src, sizeof(MLIMAGESOURCE_I)); src.type = SRC_TYPE_PNG_I; src.hInst = plugin.hDllInstance; src.lpszName = MAKEINTRESOURCEW(resourceId); return MLImageListI_Add(hmlilCheck, &src, MLIF_FILTER1_UID, 0); } static HBITMAP SkinnedMenuWnd_LoadPngResource(INT resourceId, COLORREF rgbBk, COLORREF rgbFg) { MLIMAGESOURCE_I imageSource; ZeroMemory(&imageSource, sizeof(MLIMAGESOURCE_I)); imageSource.type = SRC_TYPE_PNG_I; imageSource.hInst = plugin.hDllInstance; imageSource.lpszName = MAKEINTRESOURCEW(resourceId); HBITMAP hbmp = MLImageLoaderI_LoadDib(&imageSource); if (NULL != hbmp) MLImageFilterI_Apply(hmlifMngr, &MLIF_FILTER1_UID, hbmp, rgbBk, rgbFg, NULL); return hbmp; } SkinnedMenuWnd::SkinnedMenuWnd(UINT menuExStyle, HMLIMGLST hmlil, INT forcedWidth, MENUCUSTOMIZEPROC _customProc, ULONG_PTR customParam) : SkinnedWnd(FALSE) { if (FAILED(SkinnedMenuThreadInfo::GetInstance(TRUE, &threadInfo))) threadInfo = NULL; hMenu = NULL; hOwner = NULL; this->menuExStyle = menuExStyle; this->hmlil = hmlil; this->lineWidth = forcedWidth; bRestoreShadow = FALSE; hBoldFont = NULL; menuFlags = 0; backBrush = NULL; borderPen = NULL; menuOrigBrush = NULL; skinnedItems = NULL; skinnedItemCount = 0; skinnedItemCursor = 0; prevSelectedItem = 0; shortcutCX = 0; textCX = 0; scrollBitmap = NULL; disabledScrollBitmap = NULL; this->customProc = _customProc; this->customParam = customParam; } SkinnedMenuWnd::~SkinnedMenuWnd(void) { SetOwnerWindow(NULL); if (hMenu) { MENUINFO mi = {0}; mi.cbSize = sizeof(MENUINFO); mi.fMask = MIM_BACKGROUND; if (GetMenuInfo(hMenu, &mi)) { mi.fMask = 0; if (menuOrigBrush != mi.hbrBack) { mi.hbrBack = menuOrigBrush; mi.fMask |= MIM_BACKGROUND; } if (0 != mi.fMask) SetMenuInfo(hMenu, &mi); } if (NULL != skinnedItems && skinnedItemCount > 0) { MENUITEMINFOW mii = {0}; mii.cbSize = sizeof(MENUITEMINFOW); for (INT i = 0; i < skinnedItemCount; i++) { SkinnedItemRecord *record = &skinnedItems[i]; if(FALSE == record->failed) { mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_BITMAP; if (FALSE != GetMenuItemInfoW(hMenu, i, TRUE, &mii)) { mii.fMask = 0; if (FALSE != record->skinned) { mii.fMask |= (MIIM_FTYPE | MIIM_BITMAP); mii.fType &= ~MFT_OWNERDRAW; record->skinned = FALSE; } if (record->itemId != record->originalId && record->itemId == mii.wID) { mii.fMask |= MIIM_ID; mii.wID = record->originalId; } if (NULL != threadInfo) { threadInfo->ReleaseId(record->itemId); if (record->itemId != record->originalId) threadInfo->ReleaseId(record->originalId); } if (0 != mii.fMask) { if (FALSE == SetMenuItemInfoW(hMenu, i, TRUE, &mii)) { } } } } } } if (NULL != threadInfo) threadInfo->UnregisterMenu(hMenu); } if (NULL != skinnedItems) free(skinnedItems); if (hwnd && bRestoreShadow) { SetClassLongPtrW(hwnd, GCL_STYLE, GetClassLongPtrW(hwnd, GCL_STYLE) | 0x00020000/*CS_DROPSHADOW*/); } if (NULL != hBoldFont) DeleteObject(hBoldFont); if (NULL != backBrush) DeleteObject(backBrush); if (NULL != borderPen) DeleteObject(borderPen); if (NULL != scrollBitmap) DeleteObject(scrollBitmap); if (NULL != disabledScrollBitmap) DeleteObject(disabledScrollBitmap); if (NULL != threadInfo) { threadInfo->RemoveValidationHook(this); threadInfo->Release(); } } HMENU SkinnedMenuWnd::GetMenuHandle() { return hMenu; } HWND SkinnedMenuWnd::GetOwnerWindow() { return hOwner; } HWND SkinnedMenuWnd::SetOwnerWindow(HWND hwndOwner) { if (hOwner == hwndOwner) return hOwner; HWND prevOwner = hOwner; if (NULL != hOwner && 0 != (SMIF_REMOVEREFLECTOR & menuFlags)) { RemoveReflector(hOwner); } menuFlags &= ~SMIF_REMOVEREFLECTOR; hOwner = hwndOwner; if (NULL != hOwner && S_OK == InstallReflector(hOwner)) { menuFlags |= SMIF_REMOVEREFLECTOR; } return prevOwner; } BOOL SkinnedMenuWnd::AttachMenu(HMENU hMenuToAttach) { MENUINFO mi = {0}; MENUITEMINFOW mii = {0}; if (NULL != hMenu || NULL == hMenuToAttach) { return FALSE; } hMenu = hMenuToAttach; mi.cbSize = sizeof(MENUINFO); mi.fMask = MIM_BACKGROUND; if (GetMenuInfo(hMenu, &mi)) { menuOrigBrush = mi.hbrBack; mi.fMask = 0; if (NULL == mi.hbrBack) { COLORREF rgb; if (0 != (SMS_SYSCOLORS & menuExStyle) || FAILED(MLGetSkinColor(MLSO_MENU, MP_BACKGROUND, MBS_NORMAL, &rgb))) { rgb = GetSysColor(COLOR_MENU); } backBrush = CreateSolidBrush(rgb); mi.hbrBack = backBrush; mi.fMask |= MIM_BACKGROUND; } if (0 != mi.fMask) SetMenuInfo(hMenu, &mi); } if (NULL != threadInfo) threadInfo->RegisterMenu(hMenu, hwnd); mii.cbSize = sizeof(MENUITEMINFOW); INT count = GetMenuItemCount(hMenu); if (count > 0) { skinnedItems = (SkinnedItemRecord*)calloc(count, sizeof(SkinnedItemRecord)); if (NULL != skinnedItems) { skinnedItemCount = count; } } if (NULL == skinnedItems) return FALSE; skinnedItemCursor = 0; for (int i = 0; i < count; i++) { SkinnedItemRecord *record = &skinnedItems[i]; mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_BITMAP; // MIIM_BITMAP - keep it... this forces menu to call WM_MEASUREITEM if (FALSE != GetMenuItemInfoW(hMenu, i, TRUE, &mii)) { record->originalId = mii.wID; record->itemId = record->originalId; if (NULL != threadInfo) threadInfo->ClaimId(record->originalId); if (0 == (MFT_OWNERDRAW & mii.fType)) { mii.fType |= MFT_OWNERDRAW; mii.fMask &= ~MIIM_ID; // copes with separators and popup menus (menu and menuex types) if (0 == mii.wID || (UINT)-1 == mii.wID || 65535 == mii.wID) { if (NULL != threadInfo) { mii.wID = threadInfo->GetAvailableId(); if ((unsigned int)-1 != mii.wID) { record->itemId = mii.wID; mii.fMask |= MIIM_ID; } else mii.wID = record->itemId; } } record->skinned = TRUE; if (FALSE == SetMenuItemInfoW(hMenu, i, TRUE, &mii)) { record->skinned = FALSE; record->itemId = record->originalId; } else { if (record->itemId != record->originalId && NULL != threadInfo) { threadInfo->ClaimId(record->itemId); } } } } else { record->failed = TRUE; } } return TRUE; } BOOL SkinnedMenuWnd::Attach(HWND hwndMenu, HWND hwndOwner) { menuFlags &= ~SMIF_BLOCKDRAW; if(!__super::Attach(hwndMenu)) return FALSE; SetOwnerWindow(hwndOwner); DWORD windowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); DWORD windowStyleEx = GetWindowLongPtrW(hwnd, GWL_EXSTYLE); DWORD newStyle = windowStyle & ~(WS_BORDER | WS_THICKFRAME | WS_DLGFRAME); if (newStyle != windowStyle) SetWindowLongPtr(hwnd, GWL_STYLE, newStyle); newStyle = windowStyleEx & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE); if (newStyle != windowStyleEx) SetWindowLongPtr(hwnd, GWL_EXSTYLE, newStyle); if (0 == (SMS_SYSCOLORS & menuExStyle)) { SetStyle(SWS_USESKINCOLORS, FALSE); } SetType(SKINNEDWND_TYPE_POPUPMENU); if (!hmlilCheck) hmlilCheck = MLImageListI_Create(16, 16, MLILC_COLOR24_I, 2, 1, 2, hmlifMngr); if ((SMS_FORCEWIDTH & menuExStyle) && lineWidth > 0) { lineWidth -= ((GetSystemMetrics(SM_CXMENUCHECK) - 1) + WASABI_API_APP->getScaleX(MENU_BORDER_WIDTH*2)); if (lineWidth < 0) lineWidth = 0; } else lineWidth = 0; lineHeight = GetLineHeight(); if (!hmlil || !MLImageListI_GetImageSize(hmlil, &imageWidth, &imageHeight)) { imageWidth = WASABI_API_APP->getScaleX(24); imageHeight = 0; } if (hmlilCheck) { INT imageCX, imageCY; if(MLImageListI_GetImageSize(hmlilCheck, &imageCX, &imageCY)) { if (imageWidth < imageCX) imageWidth = imageCX; if (imageWidth < WASABI_API_APP->getScaleX(25)) imageWidth = WASABI_API_APP->getScaleX(25); // clamp to a min width to better match the OS if (imageHeight < imageCY) imageHeight = imageCY; } } if (lineHeight < (imageHeight + WASABI_API_APP->getScaleY(4))) lineHeight = (imageHeight + WASABI_API_APP->getScaleY(4)); if ((SMS_DISABLESHADOW & menuExStyle)) { UINT cs = GetClassLongPtrW(hwnd, GCL_STYLE); if (0x00020000/*CS_DROPSHADOW*/ & cs) { bRestoreShadow = TRUE; SetClassLongPtrW(hwnd, GCL_STYLE, cs & ~0x00020000/*CS_DROPSHADOW*/); } } return TRUE; } HPEN SkinnedMenuWnd::GetBorderPen(void) { if (NULL == borderPen) { COLORREF rgb; if (0 != (SMS_SYSCOLORS & menuExStyle)) rgb = GetSysColor(COLOR_GRAYTEXT); else MLGetSkinColor(MLSO_MENU, MP_FRAME, 0, &rgb); borderPen = CreatePen(PS_SOLID, 0, rgb); } return borderPen; } INT SkinnedMenuWnd::GetLineHeight() { INT h = 0; HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE); if (hdc) { HFONT hf = GetMenuFont(TRUE); if (NULL != hf) { TEXTMETRICW tm = {0}; HFONT hfo = (HFONT)SelectObject(hdc, hf); if (GetTextMetricsW(hdc, &tm)) h = tm.tmHeight + WASABI_API_APP->getScaleY(4); SelectObject(hdc, hfo); } ReleaseDC(hwnd, hdc); } return h; } HFONT SkinnedMenuWnd::GetMenuFont(BOOL fBold) { HFONT hFont = NULL; if (SMS_USESKINFONT & menuExStyle) { hFont = (HFONT)MlStockObjects_Get(SKIN_FONT); } if (NULL == hFont) hFont = (HFONT)MlStockObjects_Get(DEFAULT_FONT); if (FALSE != fBold) { if (NULL == hBoldFont) { LOGFONTW lf = {0}; if (sizeof(LOGFONTW) == GetObjectW(hFont, sizeof(LOGFONTW), &lf)) { if (lf.lfWeight < FW_BOLD) lf.lfWeight = FW_BOLD; hBoldFont = CreateFontIndirectW(&lf); } } if (NULL != hBoldFont) { hFont = hBoldFont; } } return hFont; } INT SkinnedMenuWnd::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS *pncsp) { DWORD windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); DWORD windowStyleEx = GetWindowLongPtr(hwnd, GWL_EXSTYLE); DWORD newStyle = windowStyle & ~(WS_BORDER | WS_THICKFRAME | WS_DLGFRAME); if (newStyle != windowStyle) SetWindowLongPtr(hwnd, GWL_STYLE, newStyle); newStyle = windowStyleEx & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE); if (newStyle != windowStyleEx) SetWindowLongPtr(hwnd, GWL_EXSTYLE, newStyle); LRESULT result = CallPrevWndProc(WM_NCCALCSIZE, (WPARAM)bCalcValidRects, (LPARAM)pncsp); InflateRect(&pncsp->rgrc[0], WASABI_API_APP->getScaleX(-MENU_BORDER_WIDTH), WASABI_API_APP->getScaleY(-MENU_BORDER_WIDTH)); if (bCalcValidRects) { InflateRect(&pncsp->rgrc[1], WASABI_API_APP->getScaleX(-MENU_BORDER_WIDTH), WASABI_API_APP->getScaleY(-MENU_BORDER_WIDTH)); InflateRect(&pncsp->rgrc[2], WASABI_API_APP->getScaleX(-MENU_BORDER_WIDTH), WASABI_API_APP->getScaleY(-MENU_BORDER_WIDTH)); } return (INT)result; } void SkinnedMenuWnd::PaintScrollButton(HDC hdc, const RECT *prc, UINT scrollButton, BOOL buttonState) { COLORREF rgbBk, rgbFg; MLGetSkinColor(MLSO_MENU, MP_BACKGROUND, MBS_NORMAL, &rgbBk); MLGetSkinColor(MLSO_MENU, MP_TEXT, MTS_NORMAL, &rgbFg); HBITMAP hbmp; if (0 == (MENU_BUTTON_STATE_DISABLED & buttonState)) { if (NULL == scrollBitmap) scrollBitmap = SkinnedMenuWnd_LoadPngResource(IDB_MENU_SCROLLARROW, rgbBk, rgbFg); hbmp = scrollBitmap; } else { if (NULL == disabledScrollBitmap) disabledScrollBitmap = SkinnedMenuWnd_LoadPngResource(IDB_MENU_SCROLLARROW_DISABLED, rgbBk, rgbFg); hbmp = disabledScrollBitmap; } BOOL imageFailed = TRUE; if (NULL != hbmp) { DIBSECTION bitmapInfo; if (!GetObjectW(hbmp, sizeof(bitmapInfo), &bitmapInfo)) ZeroMemory(&bitmapInfo, sizeof(bitmapInfo)); INT h = abs(bitmapInfo.dsBm.bmHeight); INT w = bitmapInfo.dsBm.bmWidth; if (h > 0 && w > 0) { INT x, y, nWidth, nHeight; x = prc->left + ((prc->right - prc->left) - w) / 2; y = prc->top + ((prc->bottom - prc->top) - h) / 2; if (MENU_BUTTON_SCROLLDOWN == scrollButton) { nWidth = -w; nHeight = h; x += w; } else { nWidth = w; nHeight = -h; y += h; } SetBkColor(hdc, rgbBk); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, prc, NULL, 0, NULL); StretchDIBits(hdc, x, y, nWidth, nHeight, 0, 0, w, h, bitmapInfo.dsBm.bmBits, (BITMAPINFO*)&bitmapInfo.dsBmih, DIB_RGB_COLORS, SRCCOPY); imageFailed = FALSE; } } if (imageFailed) ExtTextOut(hdc, 0, 0, ETO_OPAQUE, prc, NULL, 0, NULL); } BOOL SkinnedMenuWnd::DrawScrollButton(HDC hdc, UINT scrollButton) { RECT rc, rcWindow, rcPaint; if (0 == scrollButton || 0 == GetWindowRect(hwnd, &rcWindow) || 0 == GetClientRect(hwnd, &rc)) { return FALSE; } MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&rc, 2); HDC hdcOwned = NULL; if (NULL == hdc) { hdcOwned = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_WINDOW | DCX_CLIPSIBLINGS); hdc = hdcOwned; if (NULL == hdcOwned) return FALSE; } rcPaint.left = rc.left - rcWindow.left; rcPaint.right = rc.right - rcWindow.left; BOOL scrollEnabled; POINT ptTest; ptTest.x = rc.left + (rc.right - rc.left) / 2; if (0 != (MENU_BUTTON_SCROLLUP & scrollButton)) { rcPaint.top = MENU_BORDER_WIDTH; rcPaint.bottom = rc.top - rcWindow.top; if (rcPaint.bottom > rcPaint.top && rcPaint.right > rcPaint.left) { ptTest.y = rc.top; scrollEnabled = (MenuItemFromPoint(hwnd, hMenu, ptTest) > 0); PaintScrollButton(hdc, &rcPaint, MENU_BUTTON_SCROLLUP, (scrollEnabled) ? 0 : MENU_BUTTON_STATE_DISABLED); } } if (0 != (MENU_BUTTON_SCROLLDOWN & scrollButton)) { rcPaint.top = rc.bottom - rcWindow.top; rcPaint.bottom = (rcWindow.bottom - rcWindow.top) - MENU_BORDER_WIDTH; if (rcPaint.bottom > rcPaint.top && rcPaint.right > rcPaint.left) { ptTest.y = rc.bottom - WASABI_API_APP->getScaleY(1); INT last = MenuItemFromPoint(hwnd, hMenu, ptTest); scrollEnabled = FALSE; if (last >= 0) { INT count = GetMenuItemCount(hMenu); if (last != (count - 1)) scrollEnabled = TRUE; } PaintScrollButton(hdc, &rcPaint, MENU_BUTTON_SCROLLDOWN, (scrollEnabled) ? 0 : MENU_BUTTON_STATE_DISABLED); } } if (NULL != hdcOwned) ReleaseDC(hwnd, hdcOwned); return TRUE; } void SkinnedMenuWnd::DrawBorder(HDC hdc) { RECT rc, rcWindow, rp; GetClientRect(hwnd, &rc); GetWindowRect(hwnd, &rcWindow); MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&rc, 2); OffsetRect(&rc, -rcWindow.left, -rcWindow.top); OffsetRect(&rcWindow, -rcWindow.left, -rcWindow.top); SkinnedWnd::DrawBorder(hdc, &rcWindow, BORDER_FLAT, GetBorderPen()); HBRUSH brushBk = SkinnedMenuWnd_GetBackBrush(hMenu); SetRect(&rp, rcWindow.left + 1, rcWindow.top + 1, rc.left, rcWindow.bottom - 1); FillRect(hdc, &rp, brushBk); SetRect(&rp, rc.left, rcWindow.top + 1, rc.right, rc.top); FillRect(hdc, &rp, brushBk); SetRect(&rp, rc.right, rcWindow.top + 1, rcWindow.right - 1, rcWindow.bottom - 1); FillRect(hdc, &rp, brushBk); SetRect(&rp, rc.left, rc.bottom, rc.right, rcWindow.bottom - 1); FillRect(hdc, &rp, brushBk); if ((rc.top - rcWindow.top) > MENU_BORDER_WIDTH || (rcWindow.bottom - rc.bottom) > MENU_BORDER_WIDTH) DrawScrollButton(hdc, MENU_BUTTON_SCROLLUP | MENU_BUTTON_SCROLLDOWN); } BOOL SkinnedMenuWnd::IsSkinnedItem(UINT itemId) { if (NULL == skinnedItems) return TRUE; if (skinnedItemCursor >= skinnedItemCount) skinnedItemCursor = 0; INT start = skinnedItemCursor; while(itemId != skinnedItems[skinnedItemCursor].itemId) { skinnedItemCursor++; if (skinnedItemCursor == skinnedItemCount) skinnedItemCursor = 0; if (skinnedItemCursor == start) { skinnedItemCursor = 0; return FALSE; } } return skinnedItems[skinnedItemCursor].skinned; } static void SkinnedMenuWnd_DrawFrame(HDC hdc, const RECT *prc, INT width, COLORREF rgbFrame) { if (width > 0) { COLORREF rgbOld = SetBkColor(hdc, rgbFrame); RECT rcPart; SetRect(&rcPart, prc->left, prc->top, prc->right, prc->top + width); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcPart, NULL, 0, NULL); SetRect(&rcPart, prc->left, prc->bottom - width, prc->right, prc->bottom); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcPart, NULL, 0, NULL); SetRect(&rcPart, prc->left, prc->top + width, prc->left + width, prc->bottom - width); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcPart, NULL, 0, NULL); SetRect(&rcPart, prc->right - width, prc->top + width, prc->right, prc->bottom - width); ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcPart, NULL, 0, NULL); if (rgbOld != rgbFrame) SetBkColor(hdc, rgbOld); } } BOOL SkinnedMenuWnd::OnReflectedDrawItem(DRAWITEMSTRUCT *pdis) { if (0 != (SMIF_BLOCKDRAW & menuFlags)) { ExcludeClipRect(pdis->hDC, pdis->rcItem.left, pdis->rcItem.top, pdis->rcItem.right, pdis->rcItem.bottom); } if (!IsSkinnedItem(pdis->itemID)) return FALSE; MENUITEMINFOW mii = {0}; wchar_t szText[256] = {0}; INT imageCX, imageCY, realIndex; LONG imageTop; mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_STATE | MIIM_SUBMENU; mii.cch = ARRAYSIZE(szText); mii.dwTypeData = szText; if (!GetMenuItemInfoW((HMENU)pdis->hwndItem, pdis->itemID, FALSE, &mii)) mii.cch = 0; COLORREF rgbText, rgbTextBk, rgbTextFrame; if (0 != (SMS_SYSCOLORS & menuExStyle)) { INT foreIndex = (0 != (MFS_GRAYED & mii.fState)) ? COLOR_GRAYTEXT : COLOR_MENUTEXT; INT backIndex = (0 != (ODS_SELECTED & pdis->itemState)) ? COLOR_HIGHLIGHT : COLOR_MENU; rgbText = GetSysColor(foreIndex); rgbTextBk = GetSysColor(backIndex); rgbTextFrame = rgbTextBk; } else { MLGetSkinColor(MLSO_MENU, MP_BACKGROUND, (0 != (ODS_SELECTED & pdis->itemState)) ? MBS_SELECTED : MBS_NORMAL, &rgbTextBk); MLGetSkinColor(MLSO_MENU, MP_TEXT, (0 != (MFS_GRAYED & mii.fState)) ? MTS_DISABLED : ((0 != (ODS_SELECTED & pdis->itemState)) ? MBS_SELECTED : MBS_NORMAL), &rgbText); rgbTextFrame = rgbTextBk; if (0 != (ODS_SELECTED & pdis->itemState)) { MLGetSkinColor(MLSO_MENU, MP_BACKGROUND, MBS_SELECTEDFRAME, &rgbTextFrame); } } COLORREF origText = SetTextColor(pdis->hDC, rgbText); COLORREF origTextBk = SetBkColor(pdis->hDC, rgbTextBk); if (0 != (MFT_MENUBARBREAK & mii.fType)) { RECT rect; if (FALSE != GetClientRect(hwnd, &rect)) { COLORREF lineColor, prevColor; HBRUSH brush = SkinnedMenuWnd_GetBackBrush(hMenu); if(NULL == brush) brush = GetSysColorBrush(COLOR_WINDOW); rect.right = pdis->rcItem.left - WASABI_API_APP->getScaleX(1); rect.left = pdis->rcItem.left - WASABI_API_APP->getScaleX(2); FillRect(pdis->hDC, &rect, brush); if (0 != (SMS_SYSCOLORS & menuExStyle) || FAILED(MLGetSkinColor(MLSO_MENU, MP_SEPARATOR, 0, &lineColor))) { lineColor = GetSysColor(COLOR_3DSHADOW); } prevColor = SetBkColor(pdis->hDC, lineColor); OffsetRect(&rect, -1, 0); ExtTextOut(pdis->hDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); SetBkColor(pdis->hDC, prevColor); } } if (NULL != customProc) { INT customResult = customProc(MLMENU_ACTION_DRAWITEM, (HMENU)pdis->hwndItem, pdis->hDC, (LPARAM)pdis, customParam); if (MLMENU_WANT_DRAWPART != customResult && FALSE != customResult) return TRUE; } INT type = ((MFT_STRING | MFT_SEPARATOR) & mii.fType); switch(type) { case MFT_STRING: if (NULL == customProc || FALSE == customProc(MLMENU_ACTION_DRAWBACK, (HMENU)pdis->hwndItem, pdis->hDC, (LPARAM)pdis, customParam)) { ExtTextOutW(pdis->hDC, 0, 0, ETO_OPAQUE, &pdis->rcItem, NULL, 0, NULL); if (rgbTextFrame != rgbTextBk) { SkinnedMenuWnd_DrawFrame(pdis->hDC, &pdis->rcItem, 1, rgbTextFrame); } } if (NULL == customProc || FALSE == customProc(MLMENU_ACTION_DRAWICON, (HMENU)pdis->hwndItem, pdis->hDC, (LPARAM)pdis, customParam)) { if (hmlil) { if (MLImageListI_GetImageSize(hmlil, &imageCX, &imageCY)) { imageTop = pdis->rcItem.top + (pdis->rcItem.bottom - pdis->rcItem.top - imageCY) / 2; if (imageTop < pdis->rcItem.top) imageTop = pdis->rcItem.top; INT index = MLImageListI_GetIndexFromTag(hmlil, pdis->itemID); if (-1 != index) { HIMAGELIST himl = MLImageListI_GetRealList(hmlil); realIndex = MLImageListI_GetRealIndex(hmlil, index, rgbTextBk, rgbText); if (-1 != realIndex) { ImageList_Draw(himl, realIndex, pdis->hDC, pdis->rcItem.left + WASABI_API_APP->getScaleX(1) + (imageWidth - imageCX) / 2, imageTop, ILD_NORMAL); } } } } else if ((MFS_CHECKED & mii.fState) && hmlilCheck) { if (0 != (MFT_RADIOCHECK & mii.fType)) { if (-1 == imageRadioMark) imageRadioMark = SkinnedMenuWnd_AddPngResource(hmlilCheck, IDB_MENU_RADIOMARK); } else { if (-1 == imageCheckMark) imageCheckMark = SkinnedMenuWnd_AddPngResource(hmlilCheck, IDB_MENU_CHECKMARK); } if (MLImageListI_GetImageSize(hmlilCheck, &imageCX, &imageCY)) { imageTop = pdis->rcItem.top + (pdis->rcItem.bottom - pdis->rcItem.top - imageCY) / 2; if (imageTop < pdis->rcItem.top) imageTop = pdis->rcItem.top; HIMAGELIST himl = MLImageListI_GetRealList(hmlilCheck); realIndex = MLImageListI_GetRealIndex(hmlilCheck, (MFT_RADIOCHECK & mii.fType) ? imageRadioMark : imageCheckMark, rgbTextBk, rgbText); if (-1 != realIndex) { ImageList_Draw(himl, realIndex, pdis->hDC, pdis->rcItem.left + WASABI_API_APP->getScaleX(1) + (imageWidth - imageCX) / 2, imageTop, ILD_NORMAL); } } } } if (NULL != mii.hSubMenu && hmlilCheck) { if (-1 == imageExpandArrow) imageExpandArrow = SkinnedMenuWnd_AddPngResource(hmlilCheck, IDB_MENU_EXPANDARROW); if (MLImageListI_GetImageSize(hmlilCheck, &imageCX, &imageCY)) { imageTop = pdis->rcItem.top + (pdis->rcItem.bottom - pdis->rcItem.top - imageCY) / 2; if (imageTop < pdis->rcItem.top) imageTop = pdis->rcItem.top; HIMAGELIST himl = MLImageListI_GetRealList(hmlilCheck); realIndex = MLImageListI_GetRealIndex(hmlilCheck, imageExpandArrow, rgbTextBk, rgbText); if (-1 != realIndex) ImageList_Draw(himl, realIndex, pdis->hDC, pdis->rcItem.right - imageCX - WASABI_API_APP->getScaleX(1), imageTop, ILD_NORMAL); } } if (NULL == customProc || FALSE == customProc(MLMENU_ACTION_DRAWTEXT, (HMENU)pdis->hwndItem, pdis->hDC, (LPARAM)pdis, customParam)) { if (mii.cch) { RECT rt; CopyRect(&rt, &pdis->rcItem); if (imageWidth && imageHeight) rt.left += imageWidth + WASABI_API_APP->getScaleX(6); rt.right -= imageWidth; HFONT hFont = GetMenuFont(FALSE); HFONT originalFont = (NULL != hFont) ? (HFONT)SelectObject(pdis->hDC, hFont) : NULL; INT originalBkMode = SetBkMode(pdis->hDC, TRANSPARENT); UINT len = mii.cch; if (len > 0) { while(--len > 0 && L'\t' != mii.dwTypeData[len]); if (0 == len) len = mii.cch; } BOOL showPrefix = FALSE; if (!SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &showPrefix, 0)) showPrefix = FALSE; if (!showPrefix && 0 == (ODS_NOACCEL & pdis->itemState)) showPrefix = TRUE; UINT drawtextFlags = DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOCLIP; if (!showPrefix) drawtextFlags |= 0x000100000; /*DT_HIDEPREFIX*/ if (0 != (MFS_DEFAULT & mii.fState)) { SetTextColor(pdis->hDC, BlendColors(rgbText, rgbTextBk, 110)); OffsetRect(&rt, 1,0); DrawTextW(pdis->hDC, mii.dwTypeData, len, &rt, drawtextFlags); OffsetRect(&rt, -1,0); SetTextColor(pdis->hDC, rgbText); } DrawTextW(pdis->hDC, mii.dwTypeData, len, &rt, drawtextFlags); if (mii.cch > (len + 1)) { len++; rt.left = rt.right - shortcutCX; SetTextColor(pdis->hDC, BlendColors(rgbText, rgbTextBk, 192)); DrawTextW(pdis->hDC, mii.dwTypeData + len, mii.cch - len, &rt, DT_LEFT | DT_VCENTER | 0x000100000/*DT_HIDEPREFIX*/ | DT_SINGLELINE | DT_NOCLIP); } SelectObject(pdis->hDC, originalFont); if (TRANSPARENT != originalBkMode) SetBkMode(pdis->hDC, originalBkMode); } } ExcludeClipRect(pdis->hDC, pdis->rcItem.left + WASABI_API_APP->getScaleX(23), pdis->rcItem.top, pdis->rcItem.right, pdis->rcItem.bottom); break; case MFT_SEPARATOR: { COLORREF rgbSeparator; HPEN hPen, originalPen; INT y = (pdis->rcItem.top + (pdis->rcItem.bottom - pdis->rcItem.top) / 2); if (0 != (SMS_SYSCOLORS & menuExStyle) || FAILED(MLGetSkinColor(MLSO_MENU, MP_SEPARATOR, 0, &rgbSeparator))) { rgbSeparator = GetSysColor(COLOR_3DSHADOW/*COLOR_GRAYTEXT*/); } hPen = CreatePen(PS_SOLID, 0, rgbSeparator); originalPen = (HPEN)SelectObject(pdis->hDC, hPen); ExtTextOutW(pdis->hDC, 0, 0, ETO_OPAQUE, &pdis->rcItem, NULL, 0, NULL); MoveToEx(pdis->hDC, pdis->rcItem.left + WASABI_API_APP->getScaleX(23), y, NULL); LineTo(pdis->hDC, pdis->rcItem.right, y); SelectObject(pdis->hDC, originalPen); DeleteObject(hPen); // draws a edge on the separator so it looks more like the OS when trying to 'fake it' if (0 != (SMS_SYSCOLORS & menuExStyle)) { RECT bottomRect = pdis->rcItem; HBRUSH brush = GetSysColorBrush(COLOR_3DHIGHLIGHT); y += WASABI_API_APP->getScaleY(1); bottomRect.top = y; bottomRect.bottom = y + WASABI_API_APP->getScaleY(1); FillRect(pdis->hDC,&bottomRect, brush); } } ExcludeClipRect(pdis->hDC, pdis->rcItem.left + WASABI_API_APP->getScaleX(23), pdis->rcItem.top, pdis->rcItem.right, pdis->rcItem.bottom); break; } { COLORREF rgbSeparator; if (0 != (SMS_SYSCOLORS & menuExStyle) || FAILED(MLGetSkinColor(MLSO_MENU, MP_SEPARATOR, 0, &rgbSeparator))) { rgbSeparator = GetSysColor(COLOR_3DSHADOW/*COLOR_GRAYTEXT*/); } HPEN hPen = CreatePen(PS_SOLID, 0, rgbSeparator); HPEN originalPen = (HPEN)SelectObject(pdis->hDC, hPen); MoveToEx(pdis->hDC, pdis->rcItem.left + WASABI_API_APP->getScaleX(22), pdis->rcItem.top, NULL); LineTo(pdis->hDC, pdis->rcItem.left + WASABI_API_APP->getScaleX(22), pdis->rcItem.top + (pdis->rcItem.bottom - pdis->rcItem.top)); SelectObject(pdis->hDC, originalPen); DeleteObject(hPen); } if (origText != rgbText) SetTextColor(pdis->hDC, origText); if (origTextBk != rgbTextBk) SetBkColor(pdis->hDC, origTextBk); return TRUE; } BOOL SkinnedMenuWnd::OnReflectedMeasureItem(MEASUREITEMSTRUCT *pmis) { pmis->itemHeight = lineHeight; pmis->itemWidth = lineWidth; if (!IsSkinnedItem(pmis->itemID)) return FALSE; HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE); if (NULL == hdc) return FALSE; MENUITEMINFOW mii = {0}; wchar_t szText[128] = {0}; mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_STATE; mii.cch = ARRAYSIZE(szText); mii.dwTypeData = szText; if (!GetMenuItemInfoW(hMenu, pmis->itemID, FALSE, &mii)) mii.cch = 0; HFONT originalFont = (HFONT)SelectObject(hdc, GetMenuFont(0 != (MFS_DEFAULT & mii.fState))); if (NULL == customProc || FALSE == customProc(MLMENU_ACTION_MEASUREITEM, hMenu, hdc, (LPARAM)pmis, customParam)) { if (0 == lineWidth || 0 == lineHeight) { INT type = ((MFT_STRING | MFT_SEPARATOR) & mii.fType); switch(type) { case MFT_STRING: if (mii.cch != 0) { SIZE sz; UINT len = mii.cch; while(--len > 0 && L'\t' != mii.dwTypeData[len]); if (0 == len) len = mii.cch; if (len != mii.cch) { szText[len] = L' '; if (GetTextExtentPoint32W(hdc, szText + len, mii.cch - len, &sz) && shortcutCX < sz.cx) { shortcutCX = sz.cx; } } if (GetTextExtentPoint32W(hdc, szText, len, &sz)) { if (textCX <= sz.cx) textCX = sz.cx; if (lineHeight < sz.cy) pmis->itemHeight = sz.cy + WASABI_API_APP->getScaleY(2); if (0 == lineWidth) pmis->itemWidth = textCX + shortcutCX + 8; } } if(imageHeight > (INT)(pmis->itemHeight + WASABI_API_APP->getScaleY(2))) pmis->itemHeight = imageHeight + WASABI_API_APP->getScaleY(2); if (0 == lineWidth && imageWidth) pmis->itemWidth += imageWidth; pmis->itemWidth -= (GetSystemMetrics(SM_CXMENUCHECK) - imageWidth - WASABI_API_APP->getScaleX(36)); break; case MFT_SEPARATOR: pmis->itemHeight = WASABI_API_APP->getScaleY(7); break; } } } SelectObject(hdc, originalFont); ReleaseDC(hwnd, hdc); return TRUE; } void SkinnedMenuWnd::OnNcPaint(HRGN rgnUpdate) { if (0 != (SMIF_BLOCKDRAW & menuFlags)) return; UINT flags = DCX_PARENTCLIP | DCX_WINDOW | DCX_CLIPSIBLINGS | DCX_INTERSECTUPDATE | DCX_VALIDATE; HDC hdc = GetDCEx(hwnd, ((HRGN)NULLREGION != rgnUpdate) ? rgnUpdate : NULL, flags); if (NULL == hdc) return; DrawBorder(hdc); ReleaseDC(hwnd, hdc); } LRESULT SkinnedMenuWnd::OnEraseBackground(HDC hdc) { HBRUSH brush; brush = SkinnedMenuWnd_GetBackBrush(hMenu); if (NULL != brush) { RECT rect; if (FALSE != GetClientRect(hwnd, &rect) && FALSE != FillRect(hdc, &rect, brush)) { return 1; } } return __super::WindowProc(WM_ERASEBKGND, (WPARAM)hdc, 0L); } void SkinnedMenuWnd::OnPrint(HDC hdc, UINT options) { if (0 != (PRF_CHECKVISIBLE & options)) { DWORD windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); if (0 == (WS_VISIBLE & windowStyle)) { return; } } if (0 != (PRF_NONCLIENT & options)) { DrawBorder(hdc); } if (0 == ((PRF_ERASEBKGND | PRF_CLIENT) & options)) { return; } POINT ptOrig; RECT rc, rcWindow; if (GetClientRect(hwnd, &rc) && GetWindowRect(hwnd, &rcWindow)) { MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&rc, 2); } else { SetRectEmpty(&rc); SetRectEmpty(&rcWindow); } INT clipRegionCode; HRGN clipRegion = CreateRectRgn(0, 0, 0, 0); clipRegionCode = (NULL != clipRegion) ? GetClipRgn(hdc, clipRegion) : -1; OffsetViewportOrgEx(hdc, rc.left - rcWindow.left, rc.top - rcWindow.top, &ptOrig); if (-1 != clipRegionCode) { IntersectClipRect(hdc, 0, 0, rc.right - rc.left, rc.bottom - rc.top); } OffsetRect(&rc, -rc.left, -rc.top); if (0 != (PRF_ERASEBKGND & options)) { MENUINFO mi = {0}; mi.cbSize = sizeof(MENUINFO); mi.fMask = MIM_BACKGROUND; HBRUSH brushBk = NULL; if (GetMenuInfo(hMenu, &mi) && mi.hbrBack) brushBk = mi.hbrBack; if (NULL == brushBk) brushBk = GetSysColorBrush(COLOR_WINDOW); FillRect(hdc, &rc, brushBk); } if (0 != (PRF_CLIENT & options)) { menuFlags &= ~SMIF_BLOCKDRAW; SendMessage(hwnd, WM_PRINTCLIENT, (WPARAM)hdc, (LPARAM)(~(PRF_NONCLIENT | PRF_ERASEBKGND) & options)); menuFlags |= SMIF_BLOCKDRAW; } if (-1 != clipRegionCode) { SelectClipRgn(hdc, (0 != clipRegionCode) ? clipRegion : NULL); } if (NULL != clipRegion) DeleteObject(clipRegion); SetViewportOrgEx(hdc, ptOrig.x, ptOrig.y, NULL); SetTimer(hwnd, MTID_EX_UNBLOCKDRAW, 250, NULL); } LRESULT SkinnedMenuWnd::OnMenuSelect(UINT selectedItem) { if (((UINT)-1) != selectedItem) menuFlags &= ~SMIF_BLOCKDRAW; UINT updateScroll = 0; if (MHF_SCROLLUP == prevSelectedItem) updateScroll |= MENU_BUTTON_SCROLLUP; else if (MHF_SCROLLDOWN == prevSelectedItem) updateScroll |= MENU_BUTTON_SCROLLDOWN; switch(selectedItem) { case MHF_SCROLLUP: updateScroll |= MENU_BUTTON_SCROLLUP; break; case MHF_SCROLLDOWN: updateScroll |= MENU_BUTTON_SCROLLDOWN; break; } RECT rc; GetClientRect(hwnd, &rc); MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&rc, 2); rc.top += WASABI_API_APP->getScaleY(1); INT item = MenuItemFromPoint(hwnd, hMenu, *((POINT*)&rc)); LRESULT result; BOOL fInvalidate = FALSE; if (0 != updateScroll) { DWORD windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); result = __super::WindowProc(MN_SELECTITEM, selectedItem, 0L); SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle); if (MenuItemFromPoint(hwnd, hMenu, *((POINT*)&rc)) != item) { updateScroll = MENU_BUTTON_SCROLLUP | MENU_BUTTON_SCROLLDOWN; fInvalidate = TRUE; } } else { result = __super::WindowProc(MN_SELECTITEM, selectedItem, 0L); } prevSelectedItem = selectedItem; if (0 != updateScroll) { DrawScrollButton(NULL, updateScroll); if (FALSE != fInvalidate) { InvalidateRect(hwnd, NULL, FALSE); } } return result; } LRESULT SkinnedMenuWnd::CallHookedWindowProc(UINT uItem, BOOL fByPosition, UINT uMsg, WPARAM wParam, LPARAM lParam) { MENUITEMINFOW mii = {0}; mii.cbSize = sizeof(mii); mii.fMask = MIIM_SUBMENU; if (FALSE != GetMenuItemInfoW(hMenu, uItem, fByPosition, &mii) && NULL != mii.hSubMenu) { SkinnedMenu sm; sm.InitializeHook(NULL, (menuExStyle & ~SMS_FORCEWIDTH), hmlil, 0, customProc, customParam); return __super::WindowProc(uMsg, wParam, lParam); } return __super::WindowProc(uMsg, wParam, lParam); } INT SkinnedMenuWnd::FindHiliteItem(HMENU hMenu) { MENUITEMINFOW mii = {0}; mii.cbSize = sizeof(MENUITEMINFOW); mii.fMask = MIIM_STATE; INT count = GetMenuItemCount(hMenu); for (INT i = 0; i < count; i++) { if (0 != GetMenuItemInfoW(hMenu, i, TRUE, &mii)) { if (MFS_HILITE == ((MFS_HILITE | MFS_DISABLED | MFS_GRAYED) & mii.fState)) return i; } } return -1; } LRESULT SkinnedMenuWnd::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_ERASEBKGND: return OnEraseBackground((HDC)wParam); case REFLECTED_DRAWITEM: if (OnReflectedDrawItem((DRAWITEMSTRUCT*)((REFLECTPARAM*)lParam)->lParam)) { ((REFLECTPARAM*)lParam)->result = TRUE; return TRUE; } break; case REFLECTED_MEASUREITEM: if (OnReflectedMeasureItem((MEASUREITEMSTRUCT*)((REFLECTPARAM*)lParam)->lParam)) { ((REFLECTPARAM*)lParam)->result = TRUE; return TRUE; } break; case MN_SIZEWINDOW: { BOOL validateOwner = FALSE; if (NULL == hMenu) { textCX = 0; shortcutCX = 0; HMENU menuToAttach = (HMENU)SendMessageW(hwnd, MN_GETHMENU, 0, 0L); if (FALSE != AttachMenu(menuToAttach)) validateOwner = TRUE; } if (NULL != threadInfo) { threadInfo->SetActiveMeasureMenu(hMenu); if (FALSE != validateOwner && FALSE == threadInfo->SetValidationHook(this)) { validateOwner = FALSE; } } LRESULT result = __super::WindowProc(uMsg, wParam, lParam); if (NULL != threadInfo) { threadInfo->SetActiveMeasureMenu(NULL); if (FALSE != validateOwner) threadInfo->RemoveValidationHook(this); } return result; } break; case MN_SELECTITEM: return OnMenuSelect((UINT)wParam); case MN_LBUTTONDBLCLK: case MN_LBUTTONDOWN: case MN_LBUTTONUP: menuFlags &= ~SMIF_BLOCKDRAW; switch(wParam) { case MHF_SCROLLUP: case MHF_SCROLLDOWN: { LRESULT result = __super::WindowProc(uMsg, wParam, lParam); DrawScrollButton(NULL, MENU_BUTTON_SCROLLDOWN | MENU_BUTTON_SCROLLUP); return result; } break; default: if (wParam >= 0) { return CallHookedWindowProc((UINT)wParam, TRUE, uMsg, wParam, lParam); } break; } break; case WM_TIMER: switch(wParam) { case MTID_OPENSUBMENU: { POINT pt; INT iItem; if (GetCursorPos(&pt) && -1 != (iItem = MenuItemFromPoint(NULL, hMenu, pt))) { CallHookedWindowProc(iItem, TRUE, uMsg, wParam, lParam); return 0; } } break; case MHF_SCROLLUP: case MHF_SCROLLDOWN: __super::WindowProc(uMsg, wParam, lParam); DrawScrollButton(NULL, MENU_BUTTON_SCROLLDOWN | MENU_BUTTON_SCROLLUP); return 0; case MTID_EX_UNBLOCKDRAW: KillTimer(hwnd, wParam); menuFlags &= ~SMIF_BLOCKDRAW; return 0; } break; case WM_KEYDOWN: menuFlags &= ~SMIF_BLOCKDRAW; switch(wParam) { case VK_RETURN: case VK_RIGHT: { INT iItem = FindHiliteItem(hMenu); if (-1 != iItem) return CallHookedWindowProc(iItem, TRUE, uMsg, wParam, lParam); } break; case VK_UP: { RECT rc; GetClientRect(hwnd, &rc); MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&rc, 2); rc.top += 1; INT iItem = MenuItemFromPoint(hwnd, hMenu, *((POINT*)&rc)); if (iItem >= 0) { MENUITEMINFOW mii = {0}; mii.cbSize = sizeof(MENUITEMINFOW); mii.fMask = MIIM_STATE; if (GetMenuItemInfoW(hMenu, iItem, TRUE, &mii) && 0 != (MFS_HILITE & mii.fState)) { DWORD windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); if (0 != (WS_VISIBLE & windowStyle)) SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); __super::WindowProc(uMsg, wParam, lParam); if (0 != (WS_VISIBLE & windowStyle)) { windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); if (0 == (WS_VISIBLE & windowStyle)) { windowStyle |= WS_VISIBLE; SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle); } INT iNew = MenuItemFromPoint(hwnd, hMenu, *((POINT*)&rc)); if (iNew != iItem) { DrawScrollButton(NULL, MENU_BUTTON_SCROLLUP | MENU_BUTTON_SCROLLDOWN); InvalidateRect(hwnd, NULL, FALSE); } else { int iHilite = GetMenuItemCount(hMenu); while(0 < iHilite--) { if (FALSE != GetMenuItemInfoW(hMenu, iHilite, TRUE, &mii) && 0 != (MFS_HILITE & mii.fState)) { break; } } if (FALSE != GetMenuItemRect(hwnd, hMenu, iItem, &rc)) { MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rc, 2); InvalidateRect(hwnd, &rc, FALSE); } if (iHilite != iItem && FALSE != GetMenuItemRect(hwnd, hMenu, iHilite, &rc)) { MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rc, 2); InvalidateRect(hwnd, &rc, FALSE); } } } return 0; } } } break; case VK_DOWN: { RECT rc; GetClientRect(hwnd, &rc); MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&rc, 2); rc.top = rc.bottom - 1; INT item = MenuItemFromPoint(hwnd, hMenu, *((POINT*)&rc)); if (item >= 0) { MENUITEMINFOW mii = {0}; mii.cbSize = sizeof(MENUITEMINFOW); mii.fMask = MIIM_STATE; if (GetMenuItemInfoW(hMenu, item, TRUE, &mii) && MFS_HILITE == ((MFS_HILITE | MFS_DISABLED | MFS_GRAYED) & mii.fState)) { DWORD windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); __super::WindowProc(uMsg, wParam, lParam); SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle); INT iNew = MenuItemFromPoint(hwnd, hMenu, *((POINT*)&rc)); if (iNew != item) { DrawScrollButton(NULL, MENU_BUTTON_SCROLLUP | MENU_BUTTON_SCROLLDOWN); InvalidateRect(hwnd, NULL, FALSE); } return 0; } } } break; } break; } return __super::WindowProc(uMsg, wParam, lParam); }