#include "hdr/modern_commonheaders.h" #include "hdr/modern_commonprototypes.h" #include "m_api/m_skinbutton.h" #include "hdr/modern_clcpaint.h" #include #include #ifdef __MINGW32__ #include #endif #define BUTTON_POLLID 100 #define BUTTON_POLLDELAY 50 #define b2str(a) ((a) ? "True" : "False") void CustomizeToolbar(HWND); struct TBBUTTONDATA : public MButtonCtrl { char *szButtonID; // button id BOOL fSendOnDown; // send event on button pushed BOOL fHotMark; // button is hot marked (e.g. current state) BOOL fFocused; int nFontID; // internal font ID HANDLE ttbID; // control ID TCHAR szText[128]; // text on the button RECT rcMargins; // margins of inner content HANDLE hIcolibHandle; // handle of icon in iconlib XPTHANDLE hThemeButton; XPTHANDLE hThemeToolbar; }; static CRITICAL_SECTION csTips; static HWND hwndToolTips = NULL; static BOOL bThemed = FALSE; static HANDLE hButtonWindowList = NULL; static HANDLE hBkgChangedHook = NULL; static int OnIconLibIconChanged(WPARAM wParam, LPARAM lParam) { WindowList_BroadcastAsync(hButtonWindowList, MBM_REFRESHICOLIBICON, 0, 0); return 0; } static void InvalidateParentRect(HWND hwndChild, RECT *lpRect, BOOL fErase) { LONG lExStyle = GetWindowLongPtr(hwndChild,GWL_EXSTYLE); if (lExStyle & WS_EX_TRANSPARENT) { NMHDR hdr; hdr.hwndFrom = hwndChild; hdr.idFrom = 0; hdr.code = BUTTONNEEDREDRAW; SendMessage(GetParent(hwndChild),WM_NOTIFY,(WPARAM)hwndChild,(LPARAM)&hdr); } else InvalidateRect(hwndChild,lpRect,fErase); } 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(TBBUTTONDATA *bct, HDC hdcPaint , POINT *pOffset) { POINT offset = {0}; if (pOffset) offset = *pOffset; if ( !hdcPaint) return; //early exit RECT rcClient; GetClientRect(bct->hwnd, &rcClient); int width = rcClient.right - rcClient.left; int height = rcClient.bottom - rcClient.top; HBITMAP hbmMem; HBITMAP hbmOld = NULL; HDC hdcMem = pOffset ? hdcPaint : CreateCompatibleDC(hdcPaint); HFONT hOldFont = (HFONT)SelectObject(hdcMem, bct->hFont); if ( !pOffset) { hbmMem = ske_CreateDIB32(width, height); hbmOld = (HBITMAP)SelectObject(hdcMem, hbmMem); } else OffsetRect(&rcClient,offset.x,offset.y); if ( !g_CluiData.fDisableSkinEngine) { char szRequest[128]; /* painting */ mir_snprintf(szRequest,SIZEOF(szRequest),"Button,ID=%s,Hovered=%s,Pressed=%s,Focused=%s", bct->szButtonID, // ID b2str(bct->stateId == PBS_HOT), // Hovered b2str(bct->stateId == PBS_PRESSED || bct->bIsPushed == TRUE), // Pressed b2str(bct->fFocused)); // Focused SkinDrawGlyph(hdcMem,&rcClient,&rcClient,szRequest); } else if (xpt_IsThemed(bct->hThemeToolbar)) { RECT *rc = &rcClient; int state = IsWindowEnabled(bct->hwnd) ? /*(bct->stateId == PBS_PRESSED || bct->bIsPushed == TRUE) ? PBS_PRESSED :*/ (bct->stateId == PBS_NORMAL && bct->bIsDefault ? PBS_DEFAULTED : bct->stateId) : PBS_DISABLED; xpt_DrawTheme(bct->hThemeToolbar,bct->hwnd,hdcMem,TP_BUTTON, TBStateConvert2Flat(state), rc, rc); } else { HBRUSH hbr = NULL; if (bct->stateId == PBS_PRESSED || bct->stateId == PBS_HOT) hbr = GetSysColorBrush(COLOR_3DLIGHT); else { RECT btnRect; POINT pt = {0}; int ret; HWND hwndParent = GetParent(bct->hwnd); HDC dc = CreateCompatibleDC(NULL); HBITMAP memBM, oldBM; GetWindowRect(hwndParent,&btnRect); memBM = ske_CreateDIB32( btnRect.right-btnRect.left, btnRect.bottom-btnRect.top ); oldBM = (HBITMAP)SelectObject ( dc, memBM ); ret = SendMessage(hwndParent,WM_ERASEBKGND,(WPARAM)dc,0); GetWindowRect(bct->hwnd,&btnRect); ClientToScreen(hwndParent,&pt); OffsetRect(&btnRect,-pt.x,-pt.y); if (ret) BitBlt(hdcMem, 0, 0, btnRect.right-btnRect.left,btnRect.bottom-btnRect.top,dc,btnRect.left,btnRect.top,SRCCOPY); oldBM = (HBITMAP)SelectObject ( dc, oldBM ); DeleteObject(memBM); DeleteDC(dc); if ( !ret) { //WM_ERASEBKG return false need to paint HDC pdc = GetDC(hwndParent); HBRUSH oldBrush = (HBRUSH)GetCurrentObject( pdc, OBJ_BRUSH ); hbr = (HBRUSH)SendMessage(hwndParent, WM_CTLCOLORDLG, (WPARAM)pdc, (LPARAM)hwndParent); SelectObject(pdc,oldBrush); ReleaseDC(hwndParent,pdc); } } if (hbr) { FillRect(hdcMem, &rcClient, hbr); DeleteObject(hbr); } if (bct->stateId == PBS_HOT || bct->fFocused) { if (bct->bIsPushed) DrawEdge(hdcMem,&rcClient, EDGE_ETCHED,BF_RECT|BF_SOFT); else DrawEdge(hdcMem,&rcClient, BDR_RAISEDOUTER,BF_RECT|BF_SOFT|BF_FLAT); } else if (bct->stateId == PBS_PRESSED) DrawEdge(hdcMem, &rcClient, BDR_SUNKENOUTER,BF_RECT|BF_SOFT); } RECT rcTemp = rcClient; //content rect BYTE bPressed = (bct->stateId == PBS_PRESSED || bct->bIsPushed == TRUE)?1:0; HICON hHasIcon = bct->hIcon ? bct->hIcon : NULL; BOOL fHasText = (bct->szText[0] != '\0'); /* formatter */ RECT rcIcon; RECT rcText; if ( !g_CluiData.fDisableSkinEngine) { /* correct rect according to rcMargins */ rcTemp.left += bct->rcMargins.left; rcTemp.top += bct->rcMargins.top; rcTemp.bottom -= bct->rcMargins.bottom; rcTemp.right -= bct->rcMargins.right; } rcIcon = rcTemp; rcText = rcTemp; /* reposition button items */ if (hHasIcon && fHasText ) { rcIcon.right = rcIcon.left+16; /* CXSM_ICON */ rcText.left = rcIcon.right+2; } else if (hHasIcon) { rcIcon.left += (rcIcon.right-rcIcon.left)/2-8; rcIcon.right = rcIcon.left+16; } /* Check sizes*/ if (hHasIcon && (rcIcon.right>rcTemp.right || rcIcon.bottom>rcTemp.bottom || rcIcon.left < rcTemp.left || rcIcon.top < rcTemp.top)) hHasIcon = NULL; if (fHasText && (rcText.right>rcTemp.right || rcText.bottom>rcTemp.bottom || rcText.left < rcTemp.left || rcText.top < rcTemp.top)) fHasText = FALSE; if (hHasIcon) { /* center icon vertically */ rcIcon.top += (rcClient.bottom-rcClient.top)/2 - 8; /* CYSM_ICON/2 */ rcIcon.bottom = rcIcon.top + 16; /* CYSM_ICON */ /* draw it */ ske_DrawIconEx(hdcMem, rcIcon.left+bPressed, rcIcon.top+bPressed, hHasIcon, 16, 16, 0, NULL, DI_NORMAL); } if (fHasText) { BOOL bCentered = TRUE; SetBkMode(hdcMem,TRANSPARENT); if (bct->nFontID >= 0) g_clcPainter.ChangeToFont(hdcMem,NULL,bct->nFontID,NULL); RECT TextRequiredRect = rcText; ske_DrawText(hdcMem, bct->szText, -1, &TextRequiredRect, DT_CENTER | DT_VCENTER | DT_CALCRECT | DT_SINGLELINE); if (TextRequiredRect.right-TextRequiredRect.left>rcText.right-rcText.left) bCentered = FALSE; ske_DrawText(hdcMem, bct->szText, -1, &rcText, (bCentered ? DT_CENTER: 0) | DT_VCENTER | DT_SINGLELINE); ske_ResetTextEffect(hdcMem); } if ( !pOffset) BitBlt(hdcPaint, 0, 0, width,height,hdcMem, 0, 0, SRCCOPY); // better to use try/finally but looks like last one is Microsoft specific SelectObject(hdcMem,hOldFont); if ( !pOffset) { SelectObject(hdcMem,hbmOld); DeleteObject(hbmMem); DeleteDC(hdcMem); } } static LRESULT CALLBACK ToolbarButtonProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { TBBUTTONDATA *bct = (TBBUTTONDATA *) GetWindowLongPtr(hwndDlg, 0); switch (msg) { case WM_DESTROY: xpt_FreeThemeForWindow(hwndDlg); WindowList_Remove(hButtonWindowList, hwndDlg); break; // DONT! fall thru case WM_SETTEXT: lstrcpyn(bct->szText, (TCHAR *)lParam, SIZEOF(bct->szText)-1); bct->szText[SIZEOF(bct->szText)-1] = '\0'; break; case WM_SETFONT: // remember the font so we can use it later bct->hFont = (HFONT) wParam; // maybe we should redraw? bct->nFontID = (int) lParam - 1; break; case BUTTONSETSENDONDOWN: bct->fSendOnDown = (BOOL) lParam; break; case BUTTONSETMARGINS: if (lParam) bct->rcMargins = *(RECT*)lParam; else { RECT nillRect = {0}; bct->rcMargins = nillRect; } break; case BUTTONSETID: bct->szButtonID = (char *)lParam; break; case BUTTONDRAWINPARENT: if (IsWindowVisible(hwndDlg)) PaintWorker(bct, (HDC) wParam, (POINT*) lParam); break; case WM_NCPAINT: case WM_PAINT: if (g_CluiData.fDisableSkinEngine) { PAINTSTRUCT ps; HDC hdcPaint = BeginPaint(hwndDlg, &ps); if (hdcPaint) { PaintWorker(bct, hdcPaint, NULL); EndPaint(hwndDlg, &ps); } } ValidateRect(hwndDlg,NULL); bct->lResult = 0; return 1; case WM_CAPTURECHANGED: if ((HWND)lParam != bct->hwnd && bct->stateId != PBS_DISABLED) { // don't change states if disabled bct->stateId = PBS_NORMAL; InvalidateParentRect(bct->hwnd, NULL, TRUE); } break; case WM_LBUTTONDOWN: { int xPos = (( int )( short ) LOWORD( lParam )); int yPos = (( int )( short ) HIWORD( lParam )); POINT ptMouse = { xPos, yPos }; RECT rcClient; GetClientRect( bct->hwnd, &rcClient ); if ( !PtInRect( &rcClient, ptMouse )) { bct->fHotMark = FALSE; ReleaseCapture(); } else { if (bct->stateId != PBS_DISABLED && bct->stateId != PBS_PRESSED) { bct->stateId = PBS_PRESSED; bct->fHotMark = TRUE; InvalidateParentRect(bct->hwnd, NULL, TRUE); if (bct->fSendOnDown) { SendMessage(GetParent(hwndDlg), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndDlg), BN_CLICKED), (LPARAM) hwndDlg); bct->stateId = PBS_NORMAL; InvalidateParentRect(bct->hwnd, NULL, TRUE); } } SetCapture( bct->hwnd ); } } bct->lResult = 0; return 1; case WM_LBUTTONUP: if ( GetCapture() == bct->hwnd ) { POINT ptMouse = { LOWORD(lParam), HIWORD(lParam) }; RECT rcClient; GetClientRect( bct->hwnd, &rcClient ); if ( !PtInRect( &rcClient, ptMouse )) { bct->fHotMark = FALSE; ReleaseCapture(); break; } if (bct->bIsPushBtn) bct->bIsPushed = !bct->bIsPushed; if (bct->stateId != PBS_DISABLED) { // don't change states if disabled bct->stateId = PBS_HOT; InvalidateParentRect(bct->hwnd, NULL, TRUE); } if ( !bct->fSendOnDown) SendMessage(GetParent(hwndDlg), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwndDlg), BN_CLICKED), (LPARAM) hwndDlg); } bct->fHotMark = FALSE; bct->lResult = 0; return 1; case WM_MOUSEMOVE: { RECT rc; POINT pt; BOOL bPressed = (wParam & MK_LBUTTON) != 0; if ( bPressed && !bct->fHotMark ) break; GetWindowRect(hwndDlg, &rc); GetCursorPos(&pt); BOOL inClient = PtInRect(&rc, pt); if ( inClient ) { SetCapture( bct->hwnd ); if ( bct->stateId == PBS_NORMAL ) { bct->stateId = PBS_HOT; InvalidateParentRect(bct->hwnd, NULL, TRUE); } } if ( !inClient && bct->stateId == PBS_PRESSED ) { bct->stateId = PBS_HOT; InvalidateParentRect(bct->hwnd, NULL, TRUE); } else if ( inClient && bct->stateId == PBS_HOT && bPressed ) { if ( bct->fHotMark ) { bct->stateId = PBS_PRESSED; InvalidateParentRect(bct->hwnd, NULL, TRUE); } } else if ( !inClient && !bPressed) { bct->fHotMark = FALSE; ReleaseCapture(); } } bct->lResult = 0; return 1; case WM_NCHITTEST: { LRESULT lr = SendMessage(GetParent(hwndDlg), WM_NCHITTEST, wParam, lParam); if (lr == HTLEFT || lr == HTRIGHT || lr == HTBOTTOM || lr == HTTOP || lr == HTTOPLEFT || lr == HTTOPRIGHT || lr == HTBOTTOMLEFT || lr == HTBOTTOMRIGHT) { bct->lResult = HTTRANSPARENT; return 1; } } break; case BM_SETCHECK: if ( !bct->bIsPushBtn) break; if (wParam == BST_CHECKED) bct->bIsPushed = 1; else if (wParam == BST_UNCHECKED) bct->bIsPushed = 0; InvalidateRect(bct->hwnd, NULL, TRUE); bct->lResult = 0; return 1; case WM_ERASEBKGND: bct->lResult = 1; return 1; case MBM_SETICOLIBHANDLE: bct->hIcolibHandle = (HANDLE)lParam; bct->hIcon = (bct->hIcolibHandle) ? Skin_GetIconByHandle(bct->hIcolibHandle) : NULL; return 1; case MBM_REFRESHICOLIBICON: if (bct->hIcolibHandle) bct->hIcon = Skin_GetIconByHandle(bct->hIcolibHandle); else bct->hIcon = NULL; InvalidateRect(hwndDlg,NULL,TRUE); pcli->pfnInvalidateRect(GetParent(GetParent(hwndDlg)),NULL,TRUE); return 1; case MBM_UPDATETRANSPARENTFLAG: { LONG flag = GetWindowLongPtr(hwndDlg,GWL_EXSTYLE); LONG oldFlag = flag; if (lParam == 2) lParam = (g_CluiData.fDisableSkinEngine)?0:1; flag &= ~WS_EX_TRANSPARENT; if (lParam) flag |= WS_EX_TRANSPARENT; if (flag != oldFlag) { SetWindowLongPtr(hwndDlg,GWL_EXSTYLE,flag); RedrawWindow(hwndDlg,NULL,NULL,RDW_INVALIDATE|RDW_UPDATENOW); } } return 1; } return 0; } void MakeButtonSkinned(HWND hWnd) { MButtonCustomize Custom; Custom.cbLen = sizeof(TBBUTTONDATA); Custom.fnPainter = (pfnPainterFunc)PaintWorker; Custom.fnWindowProc = ToolbarButtonProc; SendMessage(hWnd, BUTTONSETCUSTOM, 0, (LPARAM)&Custom); TBBUTTONDATA* p = (TBBUTTONDATA*)GetWindowLongPtr(hWnd, 0); p->nFontID = -1; p->hThemeButton = xpt_AddThemeHandle(p->hwnd, L"BUTTON"); p->hThemeToolbar = xpt_AddThemeHandle(p->hwnd, L"TOOLBAR"); WindowList_Add(hButtonWindowList, hWnd, NULL); } static void CustomizeButton(HANDLE ttbid, HWND hWnd, LPARAM lParam) { if (ttbid == TTB_WINDOW_HANDLE) { CustomizeToolbar(hWnd); return; } MakeButtonSkinned(hWnd); TBBUTTONDATA* p = (TBBUTTONDATA*)GetWindowLongPtr(hWnd, 0); p->szButtonID, "Toolbar.MissingID"; p->ttbID = ttbid; SendMessage(hWnd, MBM_UPDATETRANSPARENTFLAG, 0, 2); } ///////////////////////////////////////////////////////////////////////////////////////// int Buttons_ModuleLoaded(WPARAM wParam, LPARAM lParam) { TopToolbar_SetCustomProc(CustomizeButton, 0); return 0; } int Buttons_OnSkinModeSettingsChanged(WPARAM wParam, LPARAM lParam) { WindowList_BroadcastAsync(hButtonWindowList, MBM_UPDATETRANSPARENTFLAG, 0, 2); return 0; } HRESULT ToolbarButtonLoadModule() { HookEvent(ME_SYSTEM_MODULESLOADED, Buttons_ModuleLoaded); hButtonWindowList = (HANDLE) CallService(MS_UTILS_ALLOCWINDOWLIST, 0, 0); hIconChangedHook = HookEvent(ME_SKIN2_ICONSCHANGED,OnIconLibIconChanged); hBkgChangedHook = HookEvent(ME_BACKGROUNDCONFIG_CHANGED,Buttons_OnSkinModeSettingsChanged); return S_OK; }