#include "wx/dcscreen.h"
#include "wx/dcclient.h"
#include "wx/toplevel.h"
+ #include "wx/msw/wrapcctl.h"
+ #include "wx/msw/private.h"
+ #include "wx/msw/missing.h"
#endif
#include "wx/imaglist.h"
#include "wx/stockitem.h"
-#include "wx/msw/private.h"
#include "wx/msw/private/button.h"
#include "wx/msw/private/dc.h"
#include "wx/private/window.h"
-#include "wx/msw/missing.h"
+
+#if wxUSE_MARKUP
+ #include "wx/generic/private/markuptext.h"
+#endif // wxUSE_MARKUP
using namespace wxMSWImpl;
#define BCM_SETSHIELD 0x160c
#endif
+#if wxUSE_UXTHEME
+extern wxWindowMSW *wxWindowBeingErased; // From src/msw/window.cpp
+#endif // wxUSE_UXTHEME
+
// ----------------------------------------------------------------------------
// button image data
// ----------------------------------------------------------------------------
wxODButtonImageData(wxButton *btn, const wxBitmap& bitmap)
{
SetBitmap(bitmap, wxButton::State_Normal);
+ SetBitmap(bitmap.ConvertToDisabled(), wxButton::State_Disabled);
m_dir = wxLEFT;
wxButton::State_Max),
m_hwndBtn(GetHwndOf(btn))
{
- // initialize all bitmaps to normal state
+ // initialize all bitmaps except for the disabled one to normal state
for ( int n = 0; n < wxButton::State_Max; n++ )
{
- m_iml.Add(bitmap);
+ m_iml.Add(n == wxButton::State_Disabled ? bitmap.ConvertToDisabled()
+ : bitmap);
}
m_data.himl = GetHimagelistOf(&m_iml);
- // use default margins
+ // no margins by default
m_data.margin.left =
- m_data.margin.right = btn->GetCharWidth();
+ m_data.margin.right =
m_data.margin.top =
- m_data.margin.bottom = btn->GetCharHeight() / 2;
+ m_data.margin.bottom = 0;
- // and default alignment
+ // use default alignment
m_data.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT;
UpdateImageInfo();
// macros
// ----------------------------------------------------------------------------
-#if wxUSE_EXTENDED_RTTI
-
-WX_DEFINE_FLAGS( wxButtonStyle )
-
-wxBEGIN_FLAGS( wxButtonStyle )
- // new style border flags, we put them first to
- // use them for streaming out
- wxFLAGS_MEMBER(wxBORDER_SIMPLE)
- wxFLAGS_MEMBER(wxBORDER_SUNKEN)
- wxFLAGS_MEMBER(wxBORDER_DOUBLE)
- wxFLAGS_MEMBER(wxBORDER_RAISED)
- wxFLAGS_MEMBER(wxBORDER_STATIC)
- wxFLAGS_MEMBER(wxBORDER_NONE)
-
- // old style border flags
- wxFLAGS_MEMBER(wxSIMPLE_BORDER)
- wxFLAGS_MEMBER(wxSUNKEN_BORDER)
- wxFLAGS_MEMBER(wxDOUBLE_BORDER)
- wxFLAGS_MEMBER(wxRAISED_BORDER)
- wxFLAGS_MEMBER(wxSTATIC_BORDER)
- wxFLAGS_MEMBER(wxBORDER)
-
- // standard window styles
- wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
- wxFLAGS_MEMBER(wxCLIP_CHILDREN)
- wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
- wxFLAGS_MEMBER(wxWANTS_CHARS)
- wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
- wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
- wxFLAGS_MEMBER(wxVSCROLL)
- wxFLAGS_MEMBER(wxHSCROLL)
-
- wxFLAGS_MEMBER(wxBU_LEFT)
- wxFLAGS_MEMBER(wxBU_RIGHT)
- wxFLAGS_MEMBER(wxBU_TOP)
- wxFLAGS_MEMBER(wxBU_BOTTOM)
- wxFLAGS_MEMBER(wxBU_EXACTFIT)
-wxEND_FLAGS( wxButtonStyle )
-
-IMPLEMENT_DYNAMIC_CLASS_XTI(wxButton, wxControl,"wx/button.h")
-
-wxBEGIN_PROPERTIES_TABLE(wxButton)
- wxEVENT_PROPERTY( Click , wxEVT_COMMAND_BUTTON_CLICKED , wxCommandEvent)
-
- wxPROPERTY( Font , wxFont , SetFont , GetFont , EMPTY_MACROVALUE, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
- wxPROPERTY( Label, wxString , SetLabel, GetLabel, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
-
- wxPROPERTY_FLAGS( WindowStyle , wxButtonStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
-
-wxEND_PROPERTIES_TABLE()
-
-wxBEGIN_HANDLERS_TABLE(wxButton)
-wxEND_HANDLERS_TABLE()
-
-wxCONSTRUCTOR_6( wxButton , wxWindow* , Parent , wxWindowID , Id , wxString , Label , wxPoint , Position , wxSize , Size , long , WindowStyle )
-
-
-#else
-IMPLEMENT_DYNAMIC_CLASS(wxButton, wxControl)
-#endif
-
// ============================================================================
// implementation
// ============================================================================
wxSize sizeBtn = sizeLabel;
sizeBtn.x += 3*win->GetCharWidth();
- sizeBtn.y = 11*EDIT_HEIGHT_FROM_CHAR_HEIGHT(sizeLabel.y)/10;
+ sizeBtn.y += win->GetCharHeight()/2;
// account for the shield UAC icon if we have it
if ( flags & Size_AuthNeeded )
return sizeBtn;
}
-wxSize wxMSWButton::ComputeBestSize(wxControl *btn, int flags)
+wxSize wxMSWButton::ComputeBestFittingSize(wxControl *btn, int flags)
{
wxClientDC dc(btn);
wxSize sizeBtn;
dc.GetMultiLineTextExtent(btn->GetLabelText(), &sizeBtn.x, &sizeBtn.y);
- sizeBtn = GetFittingSize(btn, sizeBtn, flags);
+ return GetFittingSize(btn, sizeBtn, flags);
+}
+
+wxSize wxMSWButton::IncreaseToStdSizeAndCache(wxControl *btn, const wxSize& size)
+{
+ wxSize sizeBtn(size);
+
+ // All buttons have at least the standard height and, unless the user
+ // explicitly wants them to be as small as possible and used wxBU_EXACTFIT
+ // style to indicate this, of at least the standard width too.
+ //
+ // Notice that we really want to make all buttons equally high, otherwise
+ // they look ugly and the existing code using wxBU_EXACTFIT only uses it to
+ // control width and not height.
- // all buttons have at least the standard size unless the user explicitly
- // wants them to be of smaller size and used wxBU_EXACTFIT style when
- // creating the button
+ // The 50x14 button size is documented in the "Recommended sizing and
+ // spacing" section of MSDN layout article.
+ //
+ // Note that we intentionally don't use GetDefaultSize() here, because
+ // it's inexact -- dialog units depend on this dialog's font.
+ const wxSize sizeDef = btn->ConvertDialogToPixels(wxSize(50, 14));
if ( !btn->HasFlag(wxBU_EXACTFIT) )
{
- // The size of a standard button in the dialog units is 50x14, use it.
- // Note that we intentionally don't use GetDefaultSize() here, because
- // it's inexact -- dialog units depend on this dialog's font.
- wxSize sizeDef = btn->ConvertDialogToPixels(wxSize(50, 14));
if ( sizeBtn.x < sizeDef.x )
sizeBtn.x = sizeDef.x;
- if ( sizeBtn.y < sizeDef.y )
- sizeBtn.y = sizeDef.y;
}
+ if ( sizeBtn.y < sizeDef.y )
+ sizeBtn.y = sizeDef.y;
btn->CacheBestSize(sizeBtn);
const wxValidator& validator,
const wxString& name)
{
- m_authNeeded = false;
-
wxString label(lbl);
if (label.empty() && wxIsStockID(id))
{
}
delete m_imageData;
+#if wxUSE_MARKUP
+ delete m_markupText;
+#endif // wxUSE_MARKUP
}
// ----------------------------------------------------------------------------
);
// we must use WS_CLIPSIBLINGS with the buttons or they would draw over
- // each other in any resizeable dialog which has more than one button in
+ // each other in any resizable dialog which has more than one button in
// the bottom
msStyle |= WS_CLIPSIBLINGS;
wxMSWButton::UpdateMultilineStyle(GetHwnd(), label);
wxButtonBase::SetLabel(label);
+
+#if wxUSE_MARKUP
+ // If we have a plain text label, we shouldn't be using markup any longer.
+ if ( m_markupText )
+ {
+ delete m_markupText;
+ m_markupText = NULL;
+
+ // Unfortunately we don't really know whether we can reset the button
+ // to be non-owner-drawn or not: if we had made it owner-drawn just
+ // because of a call to SetLabelMarkup(), we could, but not if there
+ // were [also] calls to Set{Fore,Back}groundColour(). If it's really a
+ // problem to have button remain owner-drawn forever just because it
+ // had markup label once, we should record the reason for our current
+ // owner-drawnness and check it here.
+ }
+#endif // wxUSE_MARKUP
}
// ----------------------------------------------------------------------------
void wxButton::AdjustForBitmapSize(wxSize &size) const
{
- if ( !m_imageData )
- return;
+ wxCHECK_RET( m_imageData, wxT("shouldn't be called if no image") );
// account for the bitmap size
const wxSize sizeBmp = m_imageData->GetBitmap(State_Normal).GetSize();
wxSize wxButton::DoGetBestSize() const
{
+ wxButton * const self = const_cast<wxButton *>(this);
+
wxSize size;
- // account for the text part if we have it or if we don't have any image at
- // all (buttons initially created with empty label should still have a non
- // zero size)
- if ( ShowsLabel() || !m_imageData )
+ // Account for the text part if we have it.
+ if ( ShowsLabel() )
{
int flags = 0;
if ( GetAuthNeeded() )
flags |= wxMSWButton::Size_AuthNeeded;
- size = wxMSWButton::ComputeBestSize(const_cast<wxButton *>(this), flags);
+#if wxUSE_MARKUP
+ if ( m_markupText )
+ {
+ wxClientDC dc(self);
+ size = wxMSWButton::GetFittingSize(self,
+ m_markupText->Measure(dc),
+ flags);
+ }
+ else // Normal plain text (but possibly multiline) label.
+#endif // wxUSE_MARKUP
+ {
+ size = wxMSWButton::ComputeBestFittingSize(self, flags);
+ }
}
if ( m_imageData )
- {
AdjustForBitmapSize(size);
- CacheBestSize(size);
- }
-
- return size;
+ return wxMSWButton::IncreaseToStdSizeAndCache(self, size);
}
/* static */
void wxButton::DoSetBitmap(const wxBitmap& bitmap, State which)
{
+#if wxUSE_UXTHEME
+ wxXPButtonImageData *oldData = NULL;
+#endif // wxUSE_UXTHEME
+
+ // Check if we already had bitmaps of different size.
+ if ( m_imageData &&
+ bitmap.GetSize() != m_imageData->GetBitmap(State_Normal).GetSize() )
+ {
+ wxASSERT_MSG( which == State_Normal,
+ "Must set normal bitmap with the new size first" );
+
+#if wxUSE_UXTHEME
+ if ( ShowsLabel() && wxUxThemeEngine::GetIfActive() )
+ {
+ // We can't change the size of the images stored in wxImageList
+ // in wxXPButtonImageData::m_iml so force recreating it below but
+ // keep the current data to copy its values into the new one.
+ oldData = static_cast<wxXPButtonImageData *>(m_imageData);
+ m_imageData = NULL;
+ }
+#endif // wxUSE_UXTHEME
+ //else: wxODButtonImageData doesn't require anything special
+ }
+
// allocate the image data when the first bitmap is set
if ( !m_imageData )
{
if ( ShowsLabel() && wxUxThemeEngine::GetIfActive() )
{
m_imageData = new wxXPButtonImageData(this, bitmap);
+
+ if ( oldData )
+ {
+ // Preserve the old values in case the user changed them.
+ m_imageData->SetBitmapPosition(oldData->GetBitmapPosition());
+
+ const wxSize oldMargins = oldData->GetBitmapMargins();
+ m_imageData->SetBitmapMargins(oldMargins.x, oldMargins.y);
+
+ // No need to preserve the bitmaps though as they were of wrong
+ // size anyhow.
+
+ delete oldData;
+ }
}
else
#endif // wxUSE_UXTHEME
InvalidateBestSize();
}
+// ----------------------------------------------------------------------------
+// markup support
+// ----------------------------------------------------------------------------
+
+#if wxUSE_MARKUP
+
+bool wxButton::DoSetLabelMarkup(const wxString& markup)
+{
+ if ( !wxButtonBase::DoSetLabelMarkup(markup) )
+ return false;
+
+ if ( !m_markupText )
+ {
+ m_markupText = new wxMarkupText(markup);
+ MakeOwnerDrawn();
+ }
+ else
+ {
+ // We are already owner-drawn so just update the text.
+ m_markupText->SetMarkup(markup);
+ }
+
+ Refresh();
+
+ return true;
+}
+
+#endif // wxUSE_MARKUP
+
// ----------------------------------------------------------------------------
// owner-drawn buttons support
// ----------------------------------------------------------------------------
void DrawButtonText(HDC hdc,
RECT *pRect,
- const wxString& text,
- COLORREF col,
+ wxButton *btn,
int flags)
{
- wxTextColoursChanger changeFg(hdc, col, CLR_INVALID);
- wxBkModeChanger changeBkMode(hdc, wxBRUSHSTYLE_TRANSPARENT);
-
- // center text horizontally in any case
- flags |= DT_CENTER;
+ const wxString text = btn->GetLabel();
if ( text.find(wxT('\n')) != wxString::npos )
{
// draw multiline label
+ // center text horizontally in any case
+ flags |= DT_CENTER;
+
// first we need to compute its bounding rect
RECT rc;
::CopyRect(&rc, pRect);
}
else // single line label
{
- // centre text vertically too (notice that we must have DT_SINGLELINE
- // for DT_VCENTER to work)
+ // translate wx button flags to alignment flags for DrawText()
+ if ( btn->HasFlag(wxBU_RIGHT) )
+ {
+ flags |= DT_RIGHT;
+ }
+ else if ( !btn->HasFlag(wxBU_LEFT) )
+ {
+ flags |= DT_CENTER;
+ }
+ //else: DT_LEFT is the default anyhow (and its value is 0 too)
+
+ if ( btn->HasFlag(wxBU_BOTTOM) )
+ {
+ flags |= DT_BOTTOM;
+ }
+ else if ( !btn->HasFlag(wxBU_TOP) )
+ {
+ flags |= DT_VCENTER;
+ }
+ //else: as above, DT_TOP is the default
+
+ // notice that we must have DT_SINGLELINE for vertical alignment flags
+ // to work
::DrawText(hdc, text.wx_str(), text.length(), pRect,
- flags | DT_SINGLELINE | DT_VCENTER);
+ flags | DT_SINGLELINE );
}
}
iState
) )
{
+ // Set this button as the one whose background is being erased: this
+ // allows our WM_ERASEBKGND handler used by DrawThemeParentBackground()
+ // to correctly align the background brush with this window instead of
+ // the parent window to which WM_ERASEBKGND is sent. Notice that this
+ // doesn't work with custom user-defined EVT_ERASE_BACKGROUND handlers
+ // as they won't be aligned but unfortunately all the attempts to fix
+ // it by shifting DC origin before calling DrawThemeParentBackground()
+ // failed to work so we at least do this, even though this is far from
+ // being the perfect solution.
+ wxWindowBeingErased = button;
+
engine->DrawThemeParentBackground(GetHwndOf(button), hdc, &rectBtn);
+
+ wxWindowBeingErased = NULL;
}
// draw background
? ::GetSysColor(COLOR_GRAYTEXT)
: wxColourToRGB(GetForegroundColour());
- // notice that DT_HIDEPREFIX doesn't work on old (pre-Windows 2000)
- // systems but by happy coincidence ODS_NOACCEL is not used under them
- // neither so DT_HIDEPREFIX should never be used there
- DrawButtonText(hdc, &rectBtn, GetLabel(), colFg,
- state & ODS_NOACCEL ? DT_HIDEPREFIX : 0);
+ wxTextColoursChanger changeFg(hdc, colFg, CLR_INVALID);
+ wxBkModeChanger changeBkMode(hdc, wxBRUSHSTYLE_TRANSPARENT);
+
+#if wxUSE_MARKUP
+ if ( m_markupText )
+ {
+ wxDCTemp dc((WXHDC)hdc);
+ dc.SetTextForeground(wxColour(colFg));
+ dc.SetFont(GetFont());
+
+ m_markupText->Render(dc, wxRectFromRECT(rectBtn),
+ state & ODS_NOACCEL
+ ? wxMarkupText::Render_Default
+ : wxMarkupText::Render_ShowAccels);
+ }
+ else // Plain text label
+#endif // wxUSE_MARKUP
+ {
+ // notice that DT_HIDEPREFIX doesn't work on old (pre-Windows 2000)
+ // systems but by happy coincidence ODS_NOACCEL is not used under
+ // them neither so DT_HIDEPREFIX should never be used there
+ DrawButtonText(hdc, &rectBtn, this,
+ state & ODS_NOACCEL ? DT_HIDEPREFIX : 0);
+ }
}
return true;