summaryrefslogtreecommitdiff
path: root/plugins/CommonLibs/ThemedImageCheckbox.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/CommonLibs/ThemedImageCheckbox.cpp')
-rw-r--r--plugins/CommonLibs/ThemedImageCheckbox.cpp382
1 files changed, 382 insertions, 0 deletions
diff --git a/plugins/CommonLibs/ThemedImageCheckbox.cpp b/plugins/CommonLibs/ThemedImageCheckbox.cpp
new file mode 100644
index 0000000000..ea0fbcea0a
--- /dev/null
+++ b/plugins/CommonLibs/ThemedImageCheckbox.cpp
@@ -0,0 +1,382 @@
+/*
+ ThemedImageCheckbox.cpp
+ Copyright (c) 2007 Chervov Dmitry
+
+ 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 "Common.h"
+#include "ThemedImageCheckbox.h"
+#include "Themes.h"
+#include "win2k.h"
+
+#define WM_THEMECHANGED 0x031A
+
+#define CG_CHECKBOX_VERTINDENT 2
+#define CG_CHECKBOX_INDENT 1
+#define CG_CHECKBOX_WIDTH 16
+#define CG_IMAGE_INDENT 7
+#define CG_ADDITIONAL_WIDTH 3
+
+// states
+#define CGS_UNCHECKED BST_UNCHECKED
+#define CGS_CHECKED BST_CHECKED
+#define CGS_INDETERMINATE BST_INDETERMINATE
+#define CGS_PRESSED BST_PUSHED // values above and including CGS_PRESSED must coincide with BST_ constants for BM_GETSTATE to work properly
+#define CGS_HOVERED 8
+
+// state masks
+#define CGSM_ISCHECKED 3 // mask for BM_GETCHECK
+#define CGSM_GETSTATE 7 // mask to get only valid values for BM_GETSTATE
+
+#ifndef lengthof
+#define lengthof(s) (sizeof(s) / sizeof(*s))
+#endif
+
+class CCheckboxData
+{
+public:
+ CCheckboxData(): OldWndProc(NULL), Style(0), State(0), hBitmap(NULL), hIcon(NULL) {};
+
+ WNDPROC OldWndProc;
+ int Style; // BS_CHECKBOX, BS_AUTOCHECKBOX, BS_3STATE or BS_AUTO3STATE
+ int State;
+ HBITMAP hBitmap;
+ HICON hIcon;
+};
+
+static int CALLBACK CheckboxWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
+{
+ CCheckboxData *dat = (CCheckboxData*)GetWindowLong(hWnd, GWL_USERDATA);
+ if (!dat)
+ {
+ return 0;
+ }
+ switch (Msg)
+ {
+ case BM_CLICK:
+ {
+ SendMessage(hWnd, WM_LBUTTONDOWN, 0, 0);
+ SendMessage(hWnd, WM_LBUTTONUP, 0, 0);
+ return 0;
+ } break;
+ case BM_GETCHECK:
+ {
+ return dat->State & CGSM_ISCHECKED;
+ } break;
+ case BM_SETCHECK:
+ {
+ if ((wParam != BST_UNCHECKED && wParam != BST_CHECKED && wParam != BST_INDETERMINATE) || (wParam == BST_INDETERMINATE && dat->Style != BS_3STATE && dat->Style != BS_AUTO3STATE))
+ { // invalid value
+ wParam = BST_CHECKED;
+ }
+ dat->State &= ~CGSM_ISCHECKED;
+ dat->State |= wParam;
+ InvalidateRect(hWnd, NULL, false);
+ SendMessage(GetParent(hWnd), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hWnd), BN_CLICKED), (LPARAM)hWnd);
+ return 0;
+ } break;
+ case BM_SETSTATE:
+ {
+ if (wParam)
+ {
+ dat->State |= CGS_PRESSED;
+ } else
+ {
+ dat->State &= ~CGS_PRESSED;
+ }
+ InvalidateRect(hWnd, NULL, false);
+ return 0;
+ } break;
+ case BM_GETSTATE:
+ {
+ return (dat->State & CGSM_GETSTATE) | ((GetFocus() == hWnd) ? BST_FOCUS : 0);
+ } break;
+ case BM_SETIMAGE:
+ {
+ int PrevHandle = 0;
+ switch (wParam)
+ {
+ case IMAGE_BITMAP:
+ {
+ PrevHandle = (int)dat->hBitmap;
+ dat->hBitmap = (HBITMAP)lParam;
+ } break;
+ case IMAGE_ICON:
+ {
+ PrevHandle = (int)dat->hIcon;
+ dat->hIcon = (HICON)lParam;
+ } break;
+ default:
+ {
+ return 0;
+ }
+ }
+ InvalidateRect(hWnd, NULL, false);
+ return PrevHandle;
+ } break;
+ case BM_GETIMAGE:
+ {
+ switch (wParam)
+ {
+ case IMAGE_BITMAP:
+ {
+ return (int)dat->hBitmap;
+ } break;
+ case IMAGE_ICON:
+ {
+ return (int)dat->hIcon;
+ } break;
+ }
+ return 0;
+ } break;
+ case WM_GETDLGCODE:
+ {
+ return DLGC_BUTTON;
+ } break;
+ case WM_THEMECHANGED:
+ case WM_ENABLE:
+ {
+ InvalidateRect(hWnd, NULL, false);
+ return 0;
+ } break;
+ case WM_KEYDOWN:
+ {
+ if (wParam == VK_SPACE)
+ {
+ SendMessage(hWnd, BM_SETSTATE, true, 0);
+ }
+ return 0;
+ } break;
+ case WM_KEYUP:
+ {
+ if (wParam == VK_SPACE)
+ {
+ SendMessage(hWnd, BM_SETCHECK, (SendMessage(hWnd, BM_GETCHECK, 0, 0) + 1) % ((dat->Style == BS_AUTO3STATE) ? 3 : 2), 0);
+ SendMessage(hWnd, BM_SETSTATE, false, 0);
+ }
+ return 0;
+ } break;
+ case WM_CAPTURECHANGED:
+ {
+ SendMessage(hWnd, BM_SETSTATE, false, 0);
+ return 0;
+ } break;
+ case WM_ERASEBKGND:
+ {
+ return true;
+ } break;
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONDBLCLK:
+ {
+ SetFocus(hWnd);
+ SendMessage(hWnd, BM_SETSTATE, true, 0);
+ SetCapture(hWnd);
+ return 0;
+ } break;
+ case WM_LBUTTONUP:
+ {
+ if (GetCapture() == hWnd)
+ {
+ ReleaseCapture();
+ }
+ SendMessage(hWnd, BM_SETSTATE, false, 0);
+ if (dat->State & CGS_HOVERED && (dat->Style == BS_AUTOCHECKBOX || dat->Style == BS_AUTO3STATE))
+ {
+ SendMessage(hWnd, BM_SETCHECK, (SendMessage(hWnd, BM_GETCHECK, 0, 0) + 1) % ((dat->Style == BS_AUTO3STATE) ? 3 : 2), 0);
+ }
+ return 0;
+ } break;
+ case WM_MOUSEMOVE:
+ {
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(tme);
+ tme.dwFlags = TME_LEAVE;
+ tme.dwHoverTime = HOVER_DEFAULT;
+ tme.hwndTrack = hWnd;
+ _TrackMouseEvent(&tme);
+
+ POINT pt;
+ GetCursorPos(&pt);
+ if ((WindowFromPoint(pt) == hWnd) ^ ((dat->State & CGS_HOVERED) != 0))
+ {
+ dat->State ^= CGS_HOVERED;
+ InvalidateRect(hWnd, NULL, false);
+ }
+ return 0;
+ } break;
+ case WM_MOUSELEAVE:
+ {
+ if (dat->State & CGS_HOVERED)
+ {
+ dat->State &= ~CGS_HOVERED;
+ InvalidateRect(hWnd, NULL, false);
+ }
+ return 0;
+ } break;
+ case WM_SETFOCUS:
+ case WM_KILLFOCUS:
+ case WM_SYSCOLORCHANGE:
+ {
+ InvalidateRect(hWnd, NULL, false);
+ return 0;
+ } break;
+ case WM_PAINT:
+ {
+ HDC hdc;
+ PAINTSTRUCT ps;
+ hdc = BeginPaint(hWnd, &ps);
+ RECT rc;
+ GetClientRect(hWnd, &rc);
+ HDC hdcMem = CreateCompatibleDC(hdc);
+ HBITMAP hbmMem = CreateCompatibleBitmap(hdc, rc.right, rc.bottom);
+ HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, hbmMem);
+ HTHEME hTheme = pOpenThemeData ? pOpenThemeData(hWnd, L"BUTTON") : NULL;
+ if (hTheme && pDrawThemeParentBackground)
+ {
+ pDrawThemeParentBackground(hWnd, hdcMem, NULL);
+ } else
+ {
+ FillRect(hdcMem, &rc, GetSysColorBrush(COLOR_3DFACE));
+ }
+ int StateID = 0;
+#define CBSCHECK_UNCHECKED 1
+#define CBSCHECK_CHECKED 5
+#define CBSCHECK_MIXED 9
+#define CBSSTATE_NORMAL 0
+#define CBSSTATE_HOT 1
+#define CBSSTATE_PRESSED 2
+#define CBSSTATE_DISABLED 3
+ switch (SendMessage(hWnd, BM_GETCHECK, 0, 0))
+ {
+ case BST_CHECKED:
+ {
+ StateID += CBSCHECK_CHECKED;
+ } break;
+ case BST_UNCHECKED:
+ {
+ StateID += CBSCHECK_UNCHECKED;
+ } break;
+ case BST_INDETERMINATE:
+ {
+ StateID += CBSCHECK_MIXED;
+ } break;
+ }
+ if (!IsWindowEnabled(hWnd))
+ {
+ StateID += CBSSTATE_DISABLED;
+ } else if (dat->State & CGS_PRESSED && (GetCapture() != hWnd || dat->State & CGS_HOVERED))
+ {
+ StateID += CBSSTATE_PRESSED;
+ } else if (dat->State & CGS_PRESSED || dat->State & CGS_HOVERED)
+ {
+ StateID += CBSSTATE_HOT;
+ }
+ rc.left += CG_CHECKBOX_INDENT;
+ rc.right = rc.left + CG_CHECKBOX_WIDTH; // left-align the image in the client area
+ rc.top += CG_CHECKBOX_VERTINDENT;
+ rc.bottom = rc.top + CG_CHECKBOX_WIDTH; // exact rc dimensions are necessary for DrawFrameControl to draw correctly
+ if (hTheme && pDrawThemeBackground)
+ {
+ pDrawThemeBackground(hTheme, hdcMem, BP_CHECKBOX, StateID, &rc, &rc);
+ } else
+ {
+ int dfcStates[] =
+ {0, 0, DFCS_PUSHED, DFCS_INACTIVE,
+ DFCS_CHECKED, DFCS_CHECKED, DFCS_CHECKED | DFCS_PUSHED, DFCS_CHECKED | DFCS_INACTIVE,
+ DFCS_BUTTON3STATE | DFCS_CHECKED, DFCS_BUTTON3STATE | DFCS_CHECKED, DFCS_BUTTON3STATE | DFCS_INACTIVE | DFCS_CHECKED | DFCS_PUSHED, DFCS_BUTTON3STATE | DFCS_INACTIVE | DFCS_CHECKED | DFCS_PUSHED};
+ _ASSERT(StateID >= 1 && StateID <= lengthof(dfcStates));
+ DrawFrameControl(hdcMem, &rc, DFC_BUTTON, dfcStates[StateID - 1]);
+ }
+
+ GetClientRect(hWnd, &rc);
+ RECT rcImage = rc;
+ LPARAM hImage = NULL;
+ DWORD DSFlags;
+ HIMAGELIST hImageList = NULL;
+ if (dat->hBitmap)
+ {
+ BITMAP bminfo;
+ GetObject(dat->hBitmap, sizeof(bminfo), &bminfo);
+ rcImage.right = bminfo.bmWidth;
+ rcImage.bottom = bminfo.bmHeight;
+ DSFlags = DST_BITMAP;
+ hImage = (LPARAM)dat->hBitmap;
+ } else
+ {
+ rcImage.right = GetSystemMetrics(SM_CXSMICON);
+ rcImage.bottom = GetSystemMetrics(SM_CYSMICON);
+ DSFlags = DST_ICON;
+ if (dat->hIcon)
+ {
+ hImageList = ImageList_Create(rcImage.right, rcImage.bottom, IsWinVerXPPlus() ? ILC_COLOR32 | ILC_MASK : ILC_COLOR16 | ILC_MASK, 1, 0);
+ ImageList_AddIcon(hImageList, dat->hIcon);
+ hImage = (LPARAM)ImageList_GetIcon(hImageList, 0, ILD_NORMAL);
+ }
+ } // rcImage.right and rcImage.bottom are width and height, not absolute coordinates
+ rcImage.left += CG_CHECKBOX_INDENT + CG_CHECKBOX_WIDTH + CG_IMAGE_INDENT;
+ rcImage.top += (rc.bottom - rcImage.bottom) / 2;
+ DrawState(hdcMem, NULL, NULL, hImage, 0, rcImage.left, rcImage.top, rcImage.right, rcImage.bottom, DSFlags | (IsWindowEnabled(hWnd) ? DSS_NORMAL : DSS_DISABLED));
+ if (hImageList)
+ {
+ ImageList_RemoveAll(hImageList);
+ ImageList_Destroy(hImageList);
+ DestroyIcon((HICON)hImage);
+ }
+ if (GetFocus() == hWnd)
+ {
+ rcImage.right += rcImage.left;
+ rcImage.bottom += rcImage.top;
+ InflateRect(&rcImage, 1, 1);
+ DrawFocusRect(hdcMem, &rcImage);
+ }
+ if (hTheme && pCloseThemeData)
+ {
+ pCloseThemeData(hTheme);
+ }
+ BitBlt(hdc, 0, 0, rc.right, rc.bottom, hdcMem, 0, 0, SRCCOPY);
+ SelectObject(hdcMem, hbmOld);
+ DeleteObject(hbmMem);
+ DeleteDC(hdcMem);
+ EndPaint(hWnd, &ps);
+ return 0;
+ } break;
+ case WM_DESTROY:
+ {
+ SetWindowLong(hWnd, GWL_USERDATA, NULL);
+ CallWindowProc(dat->OldWndProc, hWnd, Msg, wParam, lParam);
+ delete dat;
+ return 0;
+ } break;
+ }
+ return CallWindowProc(dat->OldWndProc, hWnd, Msg, wParam, lParam);
+}
+
+int MakeThemedImageCheckbox(HWND hWndCheckbox)
+{ // workaround to make checkbox with BS_ICON or BS_BITMAP work with windows themes enabled
+ CCheckboxData *dat = new CCheckboxData();
+ dat->OldWndProc = (WNDPROC)GetWindowLong(hWndCheckbox, GWL_WNDPROC);
+ dat->State = SendMessage(hWndCheckbox, BM_GETSTATE, 0, 0);
+ long Style = GetWindowLong(hWndCheckbox, GWL_STYLE);
+ _ASSERT(Style & BS_ICON || Style & BS_BITMAP);
+ dat->Style = Style & (BS_CHECKBOX | BS_AUTOCHECKBOX | BS_3STATE | BS_AUTO3STATE);
+ _ASSERT(dat->Style == BS_CHECKBOX || dat->Style == BS_AUTOCHECKBOX || dat->Style == BS_3STATE || dat->Style == BS_AUTO3STATE);
+ Style &= ~(BS_CHECKBOX | BS_AUTOCHECKBOX | BS_3STATE | BS_AUTO3STATE);
+ Style |= BS_OWNERDRAW;
+ SetWindowLong(hWndCheckbox, GWL_STYLE, Style);
+ SetWindowLong(hWndCheckbox, GWL_USERDATA, (LONG)dat);
+ SetWindowLong(hWndCheckbox, GWL_WNDPROC, (LONG)CheckboxWndProc);
+ return 0;
+}