+// ----------------------------------------------------------------------------
+// owner-drawn buttons support
+// ----------------------------------------------------------------------------
+
+#ifdef __WIN32__
+
+// drawing helpers
+
+static void DrawButtonText(HDC hdc,
+ RECT *pRect,
+ const wxString& text,
+ COLORREF col)
+{
+ COLORREF colOld = SetTextColor(hdc, col);
+ int modeOld = SetBkMode(hdc, TRANSPARENT);
+
+ if ( text.find(_T('\n')) != wxString::npos )
+ {
+ // draw multiline label
+
+ // first we need to compute its bounding rect
+ RECT rc;
+ ::CopyRect(&rc, pRect);
+ ::DrawText(hdc, text, text.length(), &rc, DT_CENTER | DT_CALCRECT);
+
+ // now center this rect inside the entire button area
+ const LONG w = rc.right - rc.left;
+ const LONG h = rc.bottom - rc.top;
+ rc.left = (pRect->right - pRect->left)/2 - w/2;
+ rc.right = rc.left+w;
+ rc.top = (pRect->bottom - pRect->top)/2 - h/2;
+ rc.bottom = rc.top+h;
+
+ ::DrawText(hdc, text, text.length(), &rc, DT_CENTER);
+ }
+ else // single line label
+ {
+ // Note: we must have DT_SINGLELINE for DT_VCENTER to work.
+ ::DrawText(hdc, text, text.length(), pRect,
+ DT_SINGLELINE | DT_CENTER | DT_VCENTER);
+ }
+
+ SetBkMode(hdc, modeOld);
+ SetTextColor(hdc, colOld);
+}
+
+static void DrawRect(HDC hdc, const RECT& r)
+{
+ wxDrawLine(hdc, r.left, r.top, r.right, r.top);
+ wxDrawLine(hdc, r.right, r.top, r.right, r.bottom);
+ wxDrawLine(hdc, r.right, r.bottom, r.left, r.bottom);
+ wxDrawLine(hdc, r.left, r.bottom, r.left, r.top);
+}
+
+void wxButton::MakeOwnerDrawn()
+{
+ long style = GetWindowLong(GetHwnd(), GWL_STYLE);
+ if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
+ {
+ // make it so
+ style |= BS_OWNERDRAW;
+ SetWindowLong(GetHwnd(), GWL_STYLE, style);
+ }
+}
+
+bool wxButton::SetBackgroundColour(const wxColour &colour)
+{
+ if ( !wxControl::SetBackgroundColour(colour) )
+ {
+ // nothing to do
+ return false;
+ }
+
+ MakeOwnerDrawn();
+
+ Refresh();
+
+ return true;
+}
+
+bool wxButton::SetForegroundColour(const wxColour &colour)
+{
+ if ( !wxControl::SetForegroundColour(colour) )
+ {
+ // nothing to do
+ return false;
+ }
+
+ MakeOwnerDrawn();
+
+ Refresh();
+
+ return true;
+}
+
+/*
+ The button frame looks like this normally:
+
+ WWWWWWWWWWWWWWWWWWB
+ WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
+ WH GB H = light grey (LIGHT)
+ WH GB G = dark grey (SHADOW)
+ WH GB B = black (DKSHADOW)
+ WH GB
+ WGGGGGGGGGGGGGGGGGB
+ BBBBBBBBBBBBBBBBBBB
+
+ When the button is selected, the button becomes like this (the total button
+ size doesn't change):
+
+ BBBBBBBBBBBBBBBBBBB
+ BWWWWWWWWWWWWWWWWBB
+ BWHHHHHHHHHHHHHHGBB
+ BWH GBB
+ BWH GBB
+ BWGGGGGGGGGGGGGGGBB
+ BBBBBBBBBBBBBBBBBBB
+ BBBBBBBBBBBBBBBBBBB
+
+ When the button is pushed (while selected) it is like:
+
+ BBBBBBBBBBBBBBBBBBB
+ BGGGGGGGGGGGGGGGGGB
+ BG GB
+ BG GB
+ BG GB
+ BG GB
+ BGGGGGGGGGGGGGGGGGB
+ BBBBBBBBBBBBBBBBBBB
+*/
+
+static void DrawButtonFrame(HDC hdc, const RECT& rectBtn,
+ bool selected, bool pushed)
+{
+ RECT r;
+ CopyRect(&r, &rectBtn);
+
+ HPEN hpenBlack = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DDKSHADOW)),
+ hpenGrey = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW)),
+ hpenLightGr = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DLIGHT)),
+ hpenWhite = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT));
+
+ HPEN hpenOld = (HPEN)SelectObject(hdc, hpenBlack);
+
+ r.right--;
+ r.bottom--;
+
+ if ( pushed )
+ {
+ DrawRect(hdc, r);
+
+ (void)SelectObject(hdc, hpenGrey);
+ ::InflateRect(&r, -1, -1);
+
+ DrawRect(hdc, r);
+ }
+ else // !pushed
+ {
+ if ( selected )
+ {
+ DrawRect(hdc, r);
+
+ ::InflateRect(&r, -1, -1);
+ }
+
+ wxDrawLine(hdc, r.left, r.bottom, r.right, r.bottom);
+ wxDrawLine(hdc, r.right, r.bottom, r.right, r.top - 1);
+
+ (void)SelectObject(hdc, hpenWhite);
+ wxDrawLine(hdc, r.left, r.bottom - 1, r.left, r.top);
+ wxDrawLine(hdc, r.left, r.top, r.right, r.top);
+
+ (void)SelectObject(hdc, hpenLightGr);
+ wxDrawLine(hdc, r.left + 1, r.bottom - 2, r.left + 1, r.top + 1);
+ wxDrawLine(hdc, r.left + 1, r.top + 1, r.right - 1, r.top + 1);
+
+ (void)SelectObject(hdc, hpenGrey);
+ wxDrawLine(hdc, r.left + 1, r.bottom - 1, r.right - 1, r.bottom - 1);
+ wxDrawLine(hdc, r.right - 1, r.bottom - 1, r.right - 1, r.top);
+ }
+
+ (void)SelectObject(hdc, hpenOld);
+ DeleteObject(hpenWhite);
+ DeleteObject(hpenLightGr);
+ DeleteObject(hpenGrey);
+ DeleteObject(hpenBlack);
+}
+
+#if wxUSE_UXTHEME
+static
+void MSWDrawXPBackground(wxButton *button, WXDRAWITEMSTRUCT *wxdis)
+{
+ LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)wxdis;
+ HDC hdc = lpDIS->hDC;
+ UINT state = lpDIS->itemState;
+ RECT rectBtn;
+ CopyRect(&rectBtn, &lpDIS->rcItem);
+
+ wxUxThemeHandle theme(button, L"BUTTON");
+ int iState;
+
+ if ( state & ODS_SELECTED )
+ {
+ iState = PBS_PRESSED;
+ }
+ else if ( button->HasCapture() || button->IsMouseInWindow() )
+ {
+ iState = PBS_HOT;
+ }
+ else if ( state & ODS_FOCUS )
+ {
+ iState = PBS_DEFAULTED;
+ }
+ else if ( state & ODS_DISABLED )
+ {
+ iState = PBS_DISABLED;
+ }
+ else
+ {
+ iState = PBS_NORMAL;
+ }
+
+ // draw parent background if needed
+ if ( wxUxThemeEngine::Get()->IsThemeBackgroundPartiallyTransparent(theme,
+ BP_PUSHBUTTON,
+ iState) )
+ {
+ wxUxThemeEngine::Get()->DrawThemeParentBackground(GetHwndOf(button), hdc, &rectBtn);
+ }
+
+ // draw background
+ wxUxThemeEngine::Get()->DrawThemeBackground(theme, hdc, BP_PUSHBUTTON, iState,
+ &rectBtn, NULL);
+
+ // calculate content area margins
+ MARGINS margins;
+ wxUxThemeEngine::Get()->GetThemeMargins(theme, hdc, BP_PUSHBUTTON, iState,
+ TMT_CONTENTMARGINS, &rectBtn, &margins);
+ RECT rectClient;
+ ::CopyRect(&rectClient, &rectBtn);
+ ::InflateRect(&rectClient, -margins.cxLeftWidth, -margins.cyTopHeight);
+
+ // if focused and !nofocus rect
+ if ( (state & ODS_FOCUS) && !(state & ODS_NOFOCUSRECT) )
+ {
+ DrawFocusRect(hdc, &rectClient);
+ }
+
+ if ( button->UseBgCol() )
+ {
+ COLORREF colBg = wxColourToRGB(button->GetBackgroundColour());
+ HBRUSH hbrushBackground = ::CreateSolidBrush(colBg);
+
+ // don't overwrite the focus rect
+ ::InflateRect(&rectClient, -1, -1);
+ FillRect(hdc, &rectClient, hbrushBackground);
+ ::DeleteObject(hbrushBackground);
+ }
+}
+#endif // wxUSE_UXTHEME
+
+bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis)
+{
+ LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)wxdis;
+ HDC hdc = lpDIS->hDC;
+ UINT state = lpDIS->itemState;
+ RECT rectBtn;
+ CopyRect(&rectBtn, &lpDIS->rcItem);
+
+#if wxUSE_UXTHEME
+ if ( wxUxThemeEngine::GetIfActive() )
+ {
+ MSWDrawXPBackground(this, wxdis);
+ }
+ else
+#endif // wxUSE_UXTHEME
+ {
+ COLORREF colBg = wxColourToRGB(GetBackgroundColour());
+
+ // first, draw the background
+ HBRUSH hbrushBackground = ::CreateSolidBrush(colBg);
+ FillRect(hdc, &rectBtn, hbrushBackground);
+ ::DeleteObject(hbrushBackground);
+
+ // draw the border for the current state
+ bool selected = (state & ODS_SELECTED) != 0;
+ if ( !selected )
+ {
+ wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
+ if ( tlw )
+ {
+ selected = tlw->GetDefaultItem() == this;
+ }
+ }
+ bool pushed = (SendMessage(GetHwnd(), BM_GETSTATE, 0, 0) & BST_PUSHED) != 0;
+
+ DrawButtonFrame(hdc, rectBtn, selected, pushed);
+
+ // if focused and !nofocus rect
+ if ( (state & ODS_FOCUS) && !(state & ODS_NOFOCUSRECT) )
+ {
+ RECT rectFocus;
+ CopyRect(&rectFocus, &rectBtn);
+
+ // I don't know where does this constant come from, but this is how
+ // Windows draws them
+ InflateRect(&rectFocus, -4, -4);
+
+ DrawFocusRect(hdc, &rectFocus);
+ }
+
+ if ( pushed )
+ {
+ // the label is shifted by 1 pixel to create "pushed" effect
+ OffsetRect(&rectBtn, 1, 1);
+ }
+ }
+
+ COLORREF colFg = wxColourToRGB(GetForegroundColour());
+ if ( state & ODS_DISABLED ) colFg = GetSysColor(COLOR_GRAYTEXT) ;
+ wxString label = GetLabel();
+ if ( state & ODS_NOACCEL ) label = GetLabelText() ;
+ DrawButtonText(hdc, &rectBtn, label, colFg);
+
+ return true;
+}
+
+#endif // __WIN32__
+
+#endif // wxUSE_BUTTON