/* Miranda IM Copyright (C) 2002 Robert Rainwater This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include extern HINSTANCE g_hInst; extern LONG g_cxsmIcon, g_cysmIcon; extern StatusItems_t *StatusItems; extern ImageItem *g_glyphItem; struct MButtonExtension : public MButtonCtrl { HICON hIconPrivate; TCHAR szText[128]; SIZE sLabel; HIMAGELIST hIml; int iIcon; BOOL bSendOnDown; ButtonItem *buttonItem; LONG lastGlyphMetrics[4]; }; // Used for our own cheap TrackMouseEvent #define BUTTON_POLLID 100 #define BUTTON_POLLDELAY 50 static int TBStateConvert2Flat(int state) { switch (state) { case PBS_NORMAL: return TS_NORMAL; case PBS_HOT: return TS_HOT; case PBS_PRESSED: return TS_PRESSED; case PBS_DISABLED: return TS_DISABLED; case PBS_DEFAULTED: return TS_NORMAL; } return TS_NORMAL; } static void PaintWorker(MButtonExtension *ctl, HDC hdcPaint) { if (hdcPaint) { HDC hdcMem; HBITMAP hbmMem; HBITMAP hbmOld = 0; RECT rcClient; HFONT hOldFont = 0; int xOffset = 0; GetClientRect(ctl->hwnd, &rcClient); hdcMem = CreateCompatibleDC(hdcPaint); hbmMem = CreateCompatibleBitmap(hdcPaint, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); hbmOld = reinterpret_cast(SelectObject(hdcMem, hbmMem)); hOldFont = reinterpret_cast(SelectObject(hdcMem, ctl->hFont)); // If its a push button, check to see if it should stay pressed if (ctl->bIsPushBtn && ctl->bIsPushed) ctl->stateId = PBS_PRESSED; // Draw the flat button if (ctl->bIsFlat) { if (ctl->hThemeToolbar && ctl->bIsThemed) { RECT rc = rcClient; int state = IsWindowEnabled(ctl->hwnd) ? (ctl->stateId == PBS_NORMAL && ctl->bIsDefault ? PBS_DEFAULTED : ctl->stateId) : PBS_DISABLED; SkinDrawBg(ctl->hwnd, hdcMem); if (API::pfnIsThemeBackgroundPartiallyTransparent(ctl->hThemeToolbar, TP_BUTTON, TBStateConvert2Flat(state))) { API::pfnDrawThemeParentBackground(ctl->hwnd, hdcMem, &rc); } API::pfnDrawThemeBackground(ctl->hThemeToolbar, hdcMem, TP_BUTTON, TBStateConvert2Flat(state), &rc, &rc); } else { HBRUSH hbr; RECT rc = rcClient; if(ctl->buttonItem) { RECT rcParent; POINT pt; HWND hwndParent = pcli->hwndContactList; ImageItem *imgItem = ctl->stateId == PBS_HOT ? ctl->buttonItem->imgHover : (ctl->stateId == PBS_PRESSED ? ctl->buttonItem->imgPressed : ctl->buttonItem->imgNormal); LONG *glyphMetrics = ctl->stateId == PBS_HOT ? ctl->buttonItem->hoverGlyphMetrics : (ctl->stateId == PBS_PRESSED ? ctl->buttonItem->pressedGlyphMetrics : ctl->buttonItem->normalGlyphMetrics); GetWindowRect(ctl->hwnd, &rcParent); pt.x = rcParent.left; pt.y = rcParent.top; ScreenToClient(pcli->hwndContactList, &pt); BitBlt(hdcMem, 0, 0, rc.right, rc.bottom, cfg::dat.hdcBg, pt.x, pt.y, SRCCOPY); if(imgItem) DrawAlpha(hdcMem, &rc, 0, 0, 0, 0, 0, 0, 0, imgItem); if(g_glyphItem) { API::pfnAlphaBlend(hdcMem, (rc.right - glyphMetrics[2]) / 2, (rc.bottom - glyphMetrics[3]) / 2, glyphMetrics[2], glyphMetrics[3], g_glyphItem->hdc, glyphMetrics[0], glyphMetrics[1], glyphMetrics[2], glyphMetrics[3], g_glyphItem->bf); } } else if(ctl->bIsSkinned) { // skinned RECT rcParent; POINT pt; HWND hwndParent = pcli->hwndContactList; StatusItems_t *item; int item_id; GetWindowRect(ctl->hwnd, &rcParent); pt.x = rcParent.left; pt.y = rcParent.top; ScreenToClient(pcli->hwndContactList, &pt); if(HIWORD(ctl->bIsSkinned)) item_id = ctl->stateId == PBS_HOT ? ID_EXTBKTBBUTTONMOUSEOVER : (ctl->stateId == PBS_PRESSED ? ID_EXTBKTBBUTTONSPRESSED : ID_EXTBKTBBUTTONSNPRESSED); else item_id = ctl->stateId == PBS_HOT ? ID_EXTBKBUTTONSMOUSEOVER : (ctl->stateId == PBS_PRESSED ? ID_EXTBKBUTTONSPRESSED : ID_EXTBKBUTTONSNPRESSED); item = &StatusItems[item_id - ID_STATUS_OFFLINE]; SetTextColor(hdcMem, item->TEXTCOLOR); if(item->IGNORED) { if(pt.y < 10 || cfg::dat.bWallpaperMode) BitBlt(hdcMem, 0, 0, rc.right, rc.bottom, cfg::dat.hdcBg, pt.x, pt.y, SRCCOPY); else FillRect(hdcMem, &rc, GetSysColorBrush(COLOR_3DFACE)); } else { if(pt.y < 10 || cfg::dat.bWallpaperMode) BitBlt(hdcMem, 0, 0, rc.right, rc.bottom, cfg::dat.hdcBg, pt.x, pt.y, SRCCOPY); else FillRect(hdcMem, &rc, GetSysColorBrush(COLOR_3DFACE)); rc.top += item->MARGIN_TOP; rc.bottom -= item->MARGIN_BOTTOM; rc.left += item->MARGIN_LEFT; rc.right -= item->MARGIN_RIGHT; DrawAlpha(hdcMem, &rc, item->COLOR, item->ALPHA, item->COLOR2, item->COLOR2_TRANSPARENT, item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem); } } else { if (ctl->stateId == PBS_PRESSED || ctl->stateId == PBS_HOT) hbr = GetSysColorBrush(COLOR_3DFACE); else { HDC dc; HWND hwndParent; hwndParent = GetParent(ctl->hwnd); dc = GetDC(hwndParent); hbr = (HBRUSH) SendMessage(hwndParent, WM_CTLCOLORDLG, (WPARAM) dc, (LPARAM) hwndParent); ReleaseDC(hwndParent, dc); } if (hbr) { FillRect(hdcMem, &rc, hbr); DeleteObject(hbr); } } if(!ctl->bIsSkinned && ctl->buttonItem == 0) { if (ctl->stateId == PBS_HOT || ctl->focus) { if (ctl->bIsPushed) DrawEdge(hdcMem, &rc, EDGE_ETCHED, BF_RECT | BF_SOFT); else DrawEdge(hdcMem, &rc, BDR_RAISEDOUTER, BF_RECT | BF_SOFT); } else if (ctl->stateId == PBS_PRESSED) DrawEdge(hdcMem, &rc, BDR_SUNKENOUTER, BF_RECT | BF_SOFT); } } } else { // Draw background/border if (ctl->hThemeButton && ctl->bIsThemed) { int state = IsWindowEnabled(ctl->hwnd) ? (ctl->stateId == PBS_NORMAL && ctl->bIsDefault ? PBS_DEFAULTED : ctl->stateId) : PBS_DISABLED; POINT pt; RECT rcParent; GetWindowRect(ctl->hwnd, &rcParent); pt.x = rcParent.left; pt.y = rcParent.top; ScreenToClient(pcli->hwndContactList, &pt); BitBlt(hdcMem, 0, 0, rcClient.right, rcClient.bottom, cfg::dat.hdcBg, pt.x, pt.y, SRCCOPY); if (API::pfnIsThemeBackgroundPartiallyTransparent(ctl->hThemeButton, BP_PUSHBUTTON, state)) { API::pfnDrawThemeParentBackground(ctl->hwnd, hdcMem, &rcClient); } API::pfnDrawThemeBackground(ctl->hThemeButton, hdcMem, BP_PUSHBUTTON, state, &rcClient, &rcClient); } else { UINT uState = DFCS_BUTTONPUSH | ((ctl->stateId == PBS_HOT) ? DFCS_HOT : 0) | ((ctl->stateId == PBS_PRESSED) ? DFCS_PUSHED : 0); if (ctl->bIsDefault && ctl->stateId == PBS_NORMAL) uState |= DLGC_DEFPUSHBUTTON; DrawFrameControl(hdcMem, &rcClient, DFC_BUTTON, uState); } // Draw focus rectangle if button has focus if (ctl->focus) { RECT focusRect = rcClient; InflateRect(&focusRect, -3, -3); DrawFocusRect(hdcMem, &focusRect); } } // If we have an icon or a bitmap, ignore text and only draw the image on the button if (ctl->hIcon || ctl->hIconPrivate || ctl->iIcon) { int ix = (rcClient.right - rcClient.left) / 2 - (g_cxsmIcon / 2); int iy = (rcClient.bottom - rcClient.top) / 2 - (g_cxsmIcon / 2); HICON hIconNew = ctl->hIconPrivate != 0 ? ctl->hIconPrivate : ctl->hIcon; if (lstrlen(ctl->szText) == 0) { if (ctl->iIcon) ImageList_DrawEx(ctl->hIml, ctl->iIcon, hdcMem, ix, iy, g_cxsmIcon, g_cysmIcon, CLR_NONE, CLR_NONE, ILD_NORMAL); else DrawState(hdcMem, NULL, NULL, (LPARAM) hIconNew, 0, ix, iy, g_cxsmIcon, g_cysmIcon, IsWindowEnabled(ctl->hwnd) ? DST_ICON | DSS_NORMAL : DST_ICON | DSS_DISABLED); ctl->sLabel.cx = ctl->sLabel.cy = 0; } else { GetTextExtentPoint32(hdcMem, ctl->szText, lstrlen(ctl->szText), &ctl->sLabel); if(g_cxsmIcon + ctl->sLabel.cx + 8 > rcClient.right - rcClient.left) ctl->sLabel.cx = (rcClient.right - rcClient.left) - g_cxsmIcon - 8; else ctl->sLabel.cx += 4; ix = (rcClient.right - rcClient.left) / 2 - ((g_cxsmIcon + ctl->sLabel.cx) / 2); if (ctl->iIcon) ImageList_DrawEx(ctl->hIml, ctl->iIcon, hdcMem, ix, iy, g_cxsmIcon, g_cysmIcon, CLR_NONE, CLR_NONE, ILD_NORMAL); else DrawState(hdcMem, NULL, NULL, (LPARAM) hIconNew, 0, ix, iy, g_cxsmIcon, g_cysmIcon, IsWindowEnabled(ctl->hwnd) ? DST_ICON | DSS_NORMAL : DST_ICON | DSS_DISABLED); xOffset = ix + g_cxsmIcon + 4; } } else if (ctl->hBitmap) { BITMAP bminfo; int ix, iy; GetObject(ctl->hBitmap, sizeof(bminfo), &bminfo); ix = (rcClient.right - rcClient.left) / 2 - (bminfo.bmWidth / 2); iy = (rcClient.bottom - rcClient.top) / 2 - (bminfo.bmHeight / 2); if (ctl->stateId == PBS_PRESSED) { ix++; iy++; } DrawState(hdcMem, NULL, NULL, (LPARAM) ctl->hBitmap, 0, ix, iy, bminfo.bmWidth, bminfo.bmHeight, IsWindowEnabled(ctl->hwnd) ? DST_BITMAP : DST_BITMAP | DSS_DISABLED); } if (GetWindowTextLength(ctl->hwnd)) { // Draw the text and optinally the arrow RECT rcText; CopyRect(&rcText, &rcClient); SetBkMode(hdcMem, TRANSPARENT); // XP w/themes doesn't used the glossy disabled text. Is it always using COLOR_GRAYTEXT? Seems so. if(!ctl->bIsSkinned) SetTextColor(hdcMem, IsWindowEnabled(ctl->hwnd) || !ctl->hThemeButton ? GetSysColor(COLOR_BTNTEXT) : GetSysColor(COLOR_GRAYTEXT)); if (ctl->arrow) DrawState(hdcMem, NULL, NULL, (LPARAM) ctl->arrow, 0, rcClient.right - rcClient.left - 5 - g_cxsmIcon + (!ctl->hThemeButton && ctl->stateId == PBS_PRESSED ? 1 : 0), (rcClient.bottom - rcClient.top) / 2 - g_cysmIcon / 2 + (!ctl->hThemeButton && ctl->stateId == PBS_PRESSED ? 1 : 0), g_cxsmIcon, g_cysmIcon, IsWindowEnabled(ctl->hwnd) ? DST_ICON : DST_ICON | DSS_DISABLED); DrawState(hdcMem, NULL, NULL, (LPARAM) ctl->szText, 0, xOffset + (!ctl->hThemeButton && ctl->stateId == PBS_PRESSED ? 1 : 0), ctl->hThemeButton ? (rcText.bottom - rcText.top - ctl->sLabel.cy) / 2 + 1 : (rcText.bottom - rcText.top - ctl->sLabel.cy) / 2 + (ctl->stateId == PBS_PRESSED ? 1 : 0), ctl->sLabel.cx, ctl->sLabel.cy, IsWindowEnabled(ctl->hwnd) || ctl->hThemeButton ? DST_PREFIXTEXT | DSS_NORMAL : DST_PREFIXTEXT | DSS_DISABLED); } if (hOldFont) SelectObject(hdcMem, hOldFont); BitBlt(hdcPaint, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hdcMem, 0, 0, SRCCOPY); SelectObject(hdcMem, hbmOld); DeleteObject(hbmMem); DeleteDC(hdcMem); DeleteObject(hbmOld); } } static LRESULT CALLBACK TSButtonWndProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { MButtonExtension *bct = (MButtonExtension*) GetWindowLongPtr(hwndDlg, 0); switch (msg) { case WM_DESTROY: if (bct && bct->hIconPrivate) DestroyIcon(bct->hIconPrivate); break; case WM_SETTEXT: lstrcpyn(bct->szText, (TCHAR *)lParam, 127); bct->szText[127] = 0; break; case WM_SYSKEYUP: if (bct->stateId != PBS_DISABLED && bct->cHot && bct->cHot == tolower((int) wParam)) { if (!bct->bSendOnDown) SendMessage(GetParent(hwndDlg), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndDlg), BN_CLICKED), (LPARAM) hwndDlg); return 0; } break; case BM_GETIMAGE: if (wParam == IMAGE_ICON) return (LRESULT)(bct->hIconPrivate ? bct->hIconPrivate : bct->hIcon); break; case BM_SETIMAGE: if(!lParam) break; bct->hIml = 0; bct->iIcon = 0; if (wParam == IMAGE_ICON) { ICONINFO ii = {0}; BITMAP bm = {0}; if (bct->hIconPrivate) { DestroyIcon(bct->hIconPrivate); bct->hIconPrivate = 0; } GetIconInfo((HICON) lParam, &ii); GetObject(ii.hbmColor, sizeof(bm), &bm); if (bm.bmWidth > g_cxsmIcon || bm.bmHeight > g_cysmIcon) { HIMAGELIST hImageList; hImageList = ImageList_Create(g_cxsmIcon, g_cysmIcon, IsWinVerXPPlus() ? ILC_COLOR32 | ILC_MASK : ILC_COLOR16 | ILC_MASK, 1, 0); ImageList_AddIcon(hImageList, (HICON) lParam); bct->hIconPrivate = ImageList_GetIcon(hImageList, 0, ILD_NORMAL); ImageList_RemoveAll(hImageList); ImageList_Destroy(hImageList); bct->hIcon = 0; } else { bct->hIcon = (HICON) lParam; bct->hIconPrivate = 0; } DeleteObject(ii.hbmMask); DeleteObject(ii.hbmColor); bct->hBitmap = NULL; InvalidateRect(bct->hwnd, NULL, TRUE); } else if (wParam == IMAGE_BITMAP) { bct->hBitmap = (HBITMAP) lParam; if (bct->hIconPrivate) DestroyIcon(bct->hIconPrivate); bct->hIcon = bct->hIconPrivate = NULL; InvalidateRect(bct->hwnd, NULL, TRUE); } return 1; case BUTTONSETIMLICON: if (bct->hIconPrivate) DestroyIcon(bct->hIconPrivate); bct->hIml = (HIMAGELIST) wParam; bct->iIcon = (int) lParam; bct->hIcon = bct->hIconPrivate = 0; InvalidateRect(bct->hwnd, NULL, TRUE); break; case BUTTONSETSKINNED: bct->bIsSkinned = lParam != 0; bct->bIsThemed = bct->bIsSkinned ? FALSE : bct->bIsThemed; InvalidateRect(bct->hwnd, NULL, TRUE); break; case BUTTONSETBTNITEM: bct->buttonItem = (ButtonItem *)lParam; break; case BUTTONSETASMENUACTION: bct->bSendOnDown = wParam ? TRUE : FALSE; break; case WM_LBUTTONDOWN: if (bct->stateId != PBS_DISABLED && bct->stateId != PBS_PRESSED) { if (bct->bSendOnDown) { SendMessage(GetParent(hwndDlg), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndDlg), BN_CLICKED), (LPARAM) hwndDlg); bct->stateId = PBS_NORMAL; InvalidateRect(bct->hwnd, NULL, TRUE); } } break; case WM_LBUTTONUP: if (!bct->bSendOnDown) SendMessage(GetParent(hwndDlg), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndDlg), BN_CLICKED), (LPARAM) hwndDlg); break; case WM_NCHITTEST: switch( SendMessage(GetParent(hwndDlg), WM_NCHITTEST, wParam, lParam)) { case HTLEFT: case HTRIGHT: case HTBOTTOM: case HTTOP: case HTTOPLEFT: case HTTOPRIGHT: case HTBOTTOMLEFT: case HTBOTTOMRIGHT: return HTTRANSPARENT; } } return 0; } void CustomizeButton(HWND hWnd, bool bIsSkinned, bool bIsThemed, bool bIsFlat) { MButtonCustomize Custom; Custom.cbLen = sizeof(MButtonExtension); Custom.fnPainter = (pfnPainterFunc)PaintWorker; Custom.fnWindowProc = TSButtonWndProc; SendMessage(hWnd, BUTTONSETCUSTOM, 0, (LPARAM)&Custom); SendMessage(hWnd, BUTTONSETSKINNED, bIsSkinned, 0); SendMessage(hWnd, BUTTONSETASTHEMEDBTN, bIsThemed, 0); SendMessage(hWnd, BUTTONSETASFLATBTN, bIsFlat, 0); }