+WXLRESULT wxButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
+{
+ // when we receive focus, we want to temporarily become the default button in
+ // our parent panel so that pressing "Enter" would activate us -- and when
+ // losing it we should restore the previous default button as well
+ if ( nMsg == WM_SETFOCUS )
+ {
+ SetTmpDefault();
+
+ // let the default processing take place too
+ }
+ else if ( nMsg == WM_KILLFOCUS )
+ {
+ UnsetTmpDefault();
+ }
+ else if ( nMsg == WM_LBUTTONDBLCLK )
+ {
+ // emulate a click event to force an owner-drawn button to change its
+ // appearance - without this, it won't do it
+ (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN, wParam, lParam);
+
+ // and continue with processing the message normally as well
+ }
+#if wxUSE_UXTHEME
+ else if ( nMsg == WM_THEMECHANGED )
+ {
+ // need to recalculate the best size here
+ // as the theme size might have changed
+ InvalidateBestSize();
+ }
+#endif // wxUSE_UXTHEME
+ // must use m_mouseInWindow here instead of IsMouseInWindow()
+ // since we need to know the first time the mouse enters the window
+ // and IsMouseInWindow() would return true in this case
+ else if ( (nMsg == WM_MOUSEMOVE && !m_mouseInWindow) ||
+ nMsg == WM_MOUSELEAVE )
+ {
+ if (
+ IsEnabled() &&
+ (
+#if wxUSE_UXTHEME
+ wxUxThemeEngine::GetIfActive() ||
+#endif // wxUSE_UXTHEME
+ (m_imageData && m_imageData->GetBitmap(State_Current).IsOk())
+ )
+ )
+ {
+ Refresh();
+ }
+ }
+
+ // let the base class do all real processing
+ return wxControl::MSWWindowProc(nMsg, wParam, lParam);
+}
+
+// ----------------------------------------------------------------------------
+// authentication needed handling
+// ----------------------------------------------------------------------------
+
+bool wxButton::DoGetAuthNeeded() const
+{
+ return m_authNeeded;
+}
+
+void wxButton::DoSetAuthNeeded(bool show)
+{
+ // show/hide UAC symbol on Windows Vista and later
+ if ( wxGetWinVersion() >= wxWinVersion_6 )
+ {
+ m_authNeeded = show;
+ ::SendMessage(GetHwnd(), BCM_SETSHIELD, 0, show);
+ InvalidateBestSize();
+ }
+}
+
+// ----------------------------------------------------------------------------
+// button images
+// ----------------------------------------------------------------------------
+
+wxBitmap wxButton::DoGetBitmap(State which) const
+{
+ return m_imageData ? m_imageData->GetBitmap(which) : wxBitmap();
+}
+
+void wxButton::DoSetBitmap(const wxBitmap& bitmap, State which)
+{
+ // allocate the image data when the first bitmap is set
+ if ( !m_imageData )
+ {
+#if wxUSE_UXTHEME
+ // using image list doesn't work correctly if we don't have any label
+ // (even if we use BUTTON_IMAGELIST_ALIGN_CENTER alignment and
+ // BS_BITMAP style), at least under Windows 2003 so use owner drawn
+ // strategy for bitmap-only buttons
+ if ( ShowsLabel() && wxUxThemeEngine::GetIfActive() )
+ {
+ m_imageData = new wxXPButtonImageData(this, bitmap);
+ }
+ else
+#endif // wxUSE_UXTHEME
+ {
+ m_imageData = new wxODButtonImageData(this, bitmap);
+ MakeOwnerDrawn();
+ }
+ }
+ else
+ {
+ m_imageData->SetBitmap(bitmap, which);
+ }
+
+ // it should be enough to only invalidate the best size when the normal
+ // bitmap changes as all bitmaps assigned to the button should be of the
+ // same size anyhow
+ if ( which == State_Normal )
+ InvalidateBestSize();
+
+ Refresh();
+}
+
+wxSize wxButton::DoGetBitmapMargins() const
+{
+ return m_imageData ? m_imageData->GetBitmapMargins() : wxSize(0, 0);
+}
+
+void wxButton::DoSetBitmapMargins(wxCoord x, wxCoord y)
+{
+ wxCHECK_RET( m_imageData, "SetBitmap() must be called first" );
+
+ m_imageData->SetBitmapMargins(x, y);
+ InvalidateBestSize();
+}
+
+void wxButton::DoSetBitmapPosition(wxDirection dir)
+{
+ wxCHECK_RET( m_imageData, "SetBitmap() must be called first" );
+
+ m_imageData->SetBitmapPosition(dir);
+ InvalidateBestSize();
+}
+
+// ----------------------------------------------------------------------------
+// owner-drawn buttons support
+// ----------------------------------------------------------------------------
+
+// drawing helpers
+namespace
+{
+
+// return the button state using both the ODS_XXX flags specified in state
+// parameter and the current button state
+wxButton::State GetButtonState(wxButton *btn, UINT state)
+{
+ if ( state & ODS_DISABLED )
+ return wxButton::State_Disabled;
+
+ if ( state & ODS_SELECTED )
+ return wxButton::State_Pressed;
+
+ if ( btn->HasCapture() || btn->IsMouseInWindow() )
+ return wxButton::State_Current;
+
+ if ( state & ODS_FOCUS )
+ return wxButton::State_Focused;
+
+ return wxButton::State_Normal;
+}
+
+void DrawButtonText(HDC hdc,
+ RECT *pRect,
+ const wxString& text,
+ COLORREF col,
+ int flags)
+{
+ wxTextColoursChanger changeFg(hdc, col, CLR_INVALID);
+ wxBkModeChanger changeBkMode(hdc, wxBRUSHSTYLE_TRANSPARENT);
+
+ // center text horizontally in any case
+ flags |= DT_CENTER;
+
+ if ( text.find(wxT('\n')) != wxString::npos )
+ {
+ // draw multiline label
+
+ // first we need to compute its bounding rect
+ RECT rc;
+ ::CopyRect(&rc, pRect);
+ ::DrawText(hdc, text.wx_str(), 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.wx_str(), text.length(), &rc, flags);
+ }
+ else // single line label
+ {
+ // centre text vertically too (notice that we must have DT_SINGLELINE
+ // for DT_VCENTER to work)
+ ::DrawText(hdc, text.wx_str(), text.length(), pRect,
+ flags | DT_SINGLELINE | DT_VCENTER);
+ }
+}
+
+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);
+}
+
+/*
+ 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
+*/
+void DrawButtonFrame(HDC hdc, RECT& rectBtn,
+ bool selected, bool pushed)
+{
+ RECT r;
+ CopyRect(&r, &rectBtn);
+
+ AutoHPEN hpenBlack(GetSysColor(COLOR_3DDKSHADOW)),
+ hpenGrey(GetSysColor(COLOR_3DSHADOW)),
+ hpenLightGr(GetSysColor(COLOR_3DLIGHT)),
+ hpenWhite(GetSysColor(COLOR_3DHILIGHT));
+
+ SelectInHDC selectPen(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);
+ }
+
+ InflateRect(&rectBtn, -OD_BUTTON_MARGIN, -OD_BUTTON_MARGIN);
+}
+
+#if wxUSE_UXTHEME
+void DrawXPBackground(wxButton *button, HDC hdc, RECT& rectBtn, UINT state)
+{
+ wxUxThemeHandle theme(button, L"BUTTON");
+
+ // this array is indexed by wxButton::State values and so must be kept in
+ // sync with it
+ static const int uxStates[] =
+ {
+ PBS_NORMAL, PBS_HOT, PBS_PRESSED, PBS_DISABLED, PBS_DEFAULTED
+ };
+
+ int iState = uxStates[GetButtonState(button, state)];
+
+ wxUxThemeEngine * const engine = wxUxThemeEngine::Get();
+
+ // draw parent background if needed
+ if ( engine->IsThemeBackgroundPartiallyTransparent
+ (
+ theme,
+ BP_PUSHBUTTON,
+ iState
+ ) )
+ {
+ engine->DrawThemeParentBackground(GetHwndOf(button), hdc, &rectBtn);
+ }
+
+ // draw background
+ engine->DrawThemeBackground(theme, hdc, BP_PUSHBUTTON, iState,
+ &rectBtn, NULL);
+
+ // calculate content area margins
+ MARGINS margins;
+ engine->GetThemeMargins(theme, hdc, BP_PUSHBUTTON, iState,
+ TMT_CONTENTMARGINS, &rectBtn, &margins);
+ ::InflateRect(&rectBtn, -margins.cxLeftWidth, -margins.cyTopHeight);
+ ::InflateRect(&rectBtn, -XP_BUTTON_EXTRA_MARGIN, -XP_BUTTON_EXTRA_MARGIN);
+
+ if ( button->UseBgCol() )
+ {
+ COLORREF colBg = wxColourToRGB(button->GetBackgroundColour());
+ AutoHBRUSH hbrushBackground(colBg);
+
+ // don't overwrite the focus rect
+ RECT rectClient;
+ ::CopyRect(&rectClient, &rectBtn);
+ ::InflateRect(&rectClient, -1, -1);
+ FillRect(hdc, &rectClient, hbrushBackground);
+ }
+}
+#endif // wxUSE_UXTHEME
+
+} // anonymous namespace
+
+// ----------------------------------------------------------------------------
+// owner drawn buttons support
+// ----------------------------------------------------------------------------
+
+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)