]>
git.saurik.com Git - wxWidgets.git/blob - src/msw/bmpbuttn.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/bmpbuttn.cpp
3 // Purpose: wxBitmapButton
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
21 #include "wx/bmpbuttn.h"
25 #include "wx/dcmemory.h"
29 #include "wx/msw/private.h"
30 #include "wx/msw/dc.h" // for wxDCTemp
32 #include "wx/msw/uxtheme.h"
35 // no need to include tmschema.h
37 #define BP_PUSHBUTTON 1
42 #define PBS_DISABLED 4
43 #define PBS_DEFAULTED 5
45 #define TMT_CONTENTMARGINS 3602
47 #endif // wxUSE_UXTHEME
49 #ifndef ODS_NOFOCUSRECT
50 #define ODS_NOFOCUSRECT 0x0200
53 // ----------------------------------------------------------------------------
55 // ----------------------------------------------------------------------------
57 #if wxUSE_EXTENDED_RTTI
59 WX_DEFINE_FLAGS( wxBitmapButtonStyle
)
61 wxBEGIN_FLAGS( wxBitmapButtonStyle
)
62 // new style border flags, we put them first to
63 // use them for streaming out
64 wxFLAGS_MEMBER(wxBORDER_SIMPLE
)
65 wxFLAGS_MEMBER(wxBORDER_SUNKEN
)
66 wxFLAGS_MEMBER(wxBORDER_DOUBLE
)
67 wxFLAGS_MEMBER(wxBORDER_RAISED
)
68 wxFLAGS_MEMBER(wxBORDER_STATIC
)
69 wxFLAGS_MEMBER(wxBORDER_NONE
)
71 // old style border flags
72 wxFLAGS_MEMBER(wxSIMPLE_BORDER
)
73 wxFLAGS_MEMBER(wxSUNKEN_BORDER
)
74 wxFLAGS_MEMBER(wxDOUBLE_BORDER
)
75 wxFLAGS_MEMBER(wxRAISED_BORDER
)
76 wxFLAGS_MEMBER(wxSTATIC_BORDER
)
77 wxFLAGS_MEMBER(wxBORDER
)
79 // standard window styles
80 wxFLAGS_MEMBER(wxTAB_TRAVERSAL
)
81 wxFLAGS_MEMBER(wxCLIP_CHILDREN
)
82 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
)
83 wxFLAGS_MEMBER(wxWANTS_CHARS
)
84 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
)
85 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB
)
86 wxFLAGS_MEMBER(wxVSCROLL
)
87 wxFLAGS_MEMBER(wxHSCROLL
)
89 wxFLAGS_MEMBER(wxBU_AUTODRAW
)
90 wxFLAGS_MEMBER(wxBU_LEFT
)
91 wxFLAGS_MEMBER(wxBU_RIGHT
)
92 wxFLAGS_MEMBER(wxBU_TOP
)
93 wxFLAGS_MEMBER(wxBU_BOTTOM
)
94 wxEND_FLAGS( wxBitmapButtonStyle
)
96 IMPLEMENT_DYNAMIC_CLASS_XTI(wxBitmapButton
, wxButton
,"wx/bmpbuttn.h")
98 wxBEGIN_PROPERTIES_TABLE(wxBitmapButton
)
99 wxPROPERTY_FLAGS( WindowStyle
, wxBitmapButtonStyle
, long , SetWindowStyleFlag
, GetWindowStyleFlag
, EMPTY_MACROVALUE
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
100 wxEND_PROPERTIES_TABLE()
102 wxBEGIN_HANDLERS_TABLE(wxBitmapButton
)
103 wxEND_HANDLERS_TABLE()
105 wxCONSTRUCTOR_5( wxBitmapButton
, wxWindow
* , Parent
, wxWindowID
, Id
, wxBitmap
, Bitmap
, wxPoint
, Position
, wxSize
, Size
)
108 IMPLEMENT_DYNAMIC_CLASS(wxBitmapButton
, wxButton
)
111 BEGIN_EVENT_TABLE(wxBitmapButton
, wxBitmapButtonBase
)
112 EVT_SYS_COLOUR_CHANGED(wxBitmapButton::OnSysColourChanged
)
113 EVT_ENTER_WINDOW(wxBitmapButton::OnMouseEnterOrLeave
)
114 EVT_LEAVE_WINDOW(wxBitmapButton::OnMouseEnterOrLeave
)
120 long "style" , wxBU_AUTODRAW
127 bool wxBitmapButton::Create(wxWindow
*parent
, wxWindowID id
,
128 const wxBitmap
& bitmap
,
130 const wxSize
& size
, long style
,
131 const wxValidator
& wxVALIDATOR_PARAM(validator
),
132 const wxString
& name
)
134 SetBitmapLabel(bitmap
);
138 SetValidator(validator
);
139 #endif // wxUSE_VALIDATORS
141 parent
->AddChild(this);
143 m_windowStyle
= style
;
145 if ( style
& wxBU_AUTODRAW
)
152 m_windowId
= NewControlId();
156 long msStyle
= WS_VISIBLE
| WS_TABSTOP
| WS_CHILD
| BS_OWNERDRAW
;
158 if ( m_windowStyle
& wxCLIP_SIBLINGS
)
159 msStyle
|= WS_CLIPSIBLINGS
;
162 if(m_windowStyle
& wxBU_LEFT
)
164 if(m_windowStyle
& wxBU_RIGHT
)
166 if(m_windowStyle
& wxBU_TOP
)
168 if(m_windowStyle
& wxBU_BOTTOM
)
169 msStyle
|= BS_BOTTOM
;
172 m_hWnd
= (WXHWND
) CreateWindowEx(
179 (HMENU
)wxUIntToPtr(m_windowId
.GetValue()),
184 // Subclass again for purposes of dialog editing mode
188 SetInitialSize(size
);
193 bool wxBitmapButton::SetBackgroundColour(const wxColour
& colour
)
195 if ( !wxBitmapButtonBase::SetBackgroundColour(colour
) )
201 // invalidate the brush, it will be recreated the next time it's needed
202 m_brushDisabled
= wxNullBrush
;
207 void wxBitmapButton::OnSysColourChanged(wxSysColourChangedEvent
& event
)
209 m_brushDisabled
= wxNullBrush
;
213 // this change affects our current state
220 void wxBitmapButton::OnMouseEnterOrLeave(wxMouseEvent
& event
)
222 if ( IsEnabled() && m_bmpHover
.Ok() )
228 void wxBitmapButton::SetBitmapLabel(const wxBitmap
& bitmap
)
231 if ( !HasFlag(wxBU_AUTODRAW
) && !m_disabledSetByUser
&& bitmap
.IsOk() )
233 m_bmpDisabled
= wxBitmap(bitmap
.ConvertToImage().ConvertToGreyscale());
235 #endif // wxUSE_IMAGE
237 wxBitmapButtonBase::SetBitmapLabel(bitmap
);
240 void wxBitmapButton::SetBitmapFocus(const wxBitmap
& focus
)
242 // if the focus bitmap is specified but hover one isn't, use the focus
243 // bitmap for hovering as well if this is consistent with the current
244 // Windows version look and feel
246 // rationale: this is compatible with the old wxGTK behaviour and also
247 // makes it much easier to do "the right thing" for all platforms (some of
248 // them, such as Windows XP, have "hot" buttons while others don't)
249 if ( focus
.Ok() && !m_hoverSetByUser
)
250 m_bmpHover
= m_bmpFocus
;
252 wxBitmapButtonBase::SetBitmapFocus(focus
);
255 void wxBitmapButton::SetBitmapDisabled(const wxBitmap
& disabled
)
257 if ( disabled
.IsOk() )
258 m_disabledSetByUser
= true;
260 wxBitmapButtonBase::SetBitmapDisabled(disabled
);
263 void wxBitmapButton::SetBitmapHover(const wxBitmap
& hover
)
266 m_hoverSetByUser
= true;
268 wxBitmapButtonBase::SetBitmapHover(hover
);
273 void MSWDrawXPBackground(wxButton
*button
, WXDRAWITEMSTRUCT
*wxdis
)
275 LPDRAWITEMSTRUCT lpDIS
= (LPDRAWITEMSTRUCT
)wxdis
;
276 HDC hdc
= lpDIS
->hDC
;
277 UINT state
= lpDIS
->itemState
;
279 CopyRect(&rectBtn
, &lpDIS
->rcItem
);
281 wxUxThemeHandle
theme(button
, L
"BUTTON");
284 if ( state
& ODS_SELECTED
)
286 iState
= PBS_PRESSED
;
288 else if ( button
->HasCapture() || button
->IsMouseInWindow() )
292 else if ( state
& ODS_FOCUS
)
294 iState
= PBS_DEFAULTED
;
296 else if ( state
& ODS_DISABLED
)
298 iState
= PBS_DISABLED
;
305 // draw parent background if needed
306 if ( wxUxThemeEngine::Get()->IsThemeBackgroundPartiallyTransparent(theme
,
310 wxUxThemeEngine::Get()->DrawThemeParentBackground(GetHwndOf(button
), hdc
, &rectBtn
);
314 wxUxThemeEngine::Get()->DrawThemeBackground(theme
, hdc
, BP_PUSHBUTTON
, iState
,
317 // calculate content area margins
319 wxUxThemeEngine::Get()->GetThemeMargins(theme
, hdc
, BP_PUSHBUTTON
, iState
,
320 TMT_CONTENTMARGINS
, &rectBtn
, &margins
);
322 ::CopyRect(&rectClient
, &rectBtn
);
323 ::InflateRect(&rectClient
, -margins
.cxLeftWidth
, -margins
.cyTopHeight
);
325 // if focused and !nofocus rect
326 if ( (state
& ODS_FOCUS
) && !(state
& ODS_NOFOCUSRECT
) )
328 DrawFocusRect(hdc
, &rectClient
);
331 if ( button
->UseBgCol() )
333 COLORREF colBg
= wxColourToRGB(button
->GetBackgroundColour());
334 HBRUSH hbrushBackground
= ::CreateSolidBrush(colBg
);
336 // don't overwrite the focus rect
337 ::InflateRect(&rectClient
, -1, -1);
338 FillRect(hdc
, &rectClient
, hbrushBackground
);
339 ::DeleteObject(hbrushBackground
);
342 #endif // wxUSE_UXTHEME
344 // VZ: should be at the very least less than wxDEFAULT_BUTTON_MARGIN
345 #define FOCUS_MARGIN 3
347 bool wxBitmapButton::MSWOnDraw(WXDRAWITEMSTRUCT
*item
)
350 long style
= GetWindowLong((HWND
) GetHWND(), GWL_STYLE
);
351 if (style
& BS_BITMAP
)
353 // Let default procedure draw the bitmap, which is defined
354 // in the Windows resource.
359 LPDRAWITEMSTRUCT lpDIS
= (LPDRAWITEMSTRUCT
) item
;
360 HDC hDC
= lpDIS
->hDC
;
361 UINT state
= lpDIS
->itemState
;
362 bool isSelected
= (state
& ODS_SELECTED
) != 0;
363 bool autoDraw
= (GetWindowStyleFlag() & wxBU_AUTODRAW
) != 0;
366 // choose the bitmap to use depending on the button state
369 if ( isSelected
&& m_bmpSelected
.Ok() )
370 bitmap
= &m_bmpSelected
;
371 else if ( m_bmpHover
.Ok() && IsMouseInWindow() )
372 bitmap
= &m_bmpHover
;
373 else if ((state
& ODS_FOCUS
) && m_bmpFocus
.Ok())
374 bitmap
= &m_bmpFocus
;
375 else if ((state
& ODS_DISABLED
) && m_bmpDisabled
.Ok())
376 bitmap
= &m_bmpDisabled
;
378 bitmap
= &m_bmpNormal
;
383 // centre the bitmap in the control area
384 int x
= lpDIS
->rcItem
.left
;
385 int y
= lpDIS
->rcItem
.top
;
386 int width
= lpDIS
->rcItem
.right
- x
;
387 int height
= lpDIS
->rcItem
.bottom
- y
;
388 int wBmp
= bitmap
->GetWidth();
389 int hBmp
= bitmap
->GetHeight();
392 if ( autoDraw
&& wxUxThemeEngine::GetIfActive() )
394 MSWDrawXPBackground(this, item
);
395 wxUxThemeHandle
theme(this, L
"BUTTON");
397 // calculate content area margins
398 // assuming here that each state is the same size
400 wxUxThemeEngine::Get()->GetThemeMargins(theme
, NULL
,
401 BP_PUSHBUTTON
, PBS_NORMAL
,
402 TMT_CONTENTMARGINS
, NULL
,
404 int marginX
= margins
.cxLeftWidth
+ 1;
405 int marginY
= margins
.cyTopHeight
+ 1;
408 if ( m_windowStyle
& wxBU_LEFT
)
412 else if ( m_windowStyle
& wxBU_RIGHT
)
414 x1
= x
+ (width
- wBmp
) - marginX
;
418 x1
= x
+ (width
- wBmp
) / 2;
421 if ( m_windowStyle
& wxBU_TOP
)
425 else if ( m_windowStyle
& wxBU_BOTTOM
)
427 y1
= y
+ (height
- hBmp
) - marginY
;
431 y1
= y
+ (height
- hBmp
) / 2;
435 wxDCTemp
dst((WXHDC
)hDC
);
436 dst
.DrawBitmap(*bitmap
, x1
, y1
, true);
440 #endif // wxUSE_UXTHEME
444 if(m_windowStyle
& wxBU_LEFT
)
445 x1
= x
+ (FOCUS_MARGIN
+1);
446 else if(m_windowStyle
& wxBU_RIGHT
)
447 x1
= x
+ (width
- wBmp
) - (FOCUS_MARGIN
+1);
449 x1
= x
+ (width
- wBmp
) / 2;
451 if(m_windowStyle
& wxBU_TOP
)
452 y1
= y
+ (FOCUS_MARGIN
+1);
453 else if(m_windowStyle
& wxBU_BOTTOM
)
454 y1
= y
+ (height
- hBmp
) - (FOCUS_MARGIN
+1);
456 y1
= y
+ (height
- hBmp
) / 2;
458 if ( isSelected
&& autoDraw
)
464 // draw the face, if auto-drawing
467 DrawFace((WXHDC
) hDC
,
468 lpDIS
->rcItem
.left
, lpDIS
->rcItem
.top
,
469 lpDIS
->rcItem
.right
, lpDIS
->rcItem
.bottom
,
474 wxDCTemp
dst((WXHDC
)hDC
);
475 dst
.DrawBitmap(*bitmap
, x1
, y1
, true);
477 // draw focus / disabled state, if auto-drawing
478 if ( (state
& ODS_DISABLED
) && autoDraw
)
480 DrawButtonDisable((WXHDC
) hDC
,
481 lpDIS
->rcItem
.left
, lpDIS
->rcItem
.top
,
482 lpDIS
->rcItem
.right
, lpDIS
->rcItem
.bottom
,
485 else if ( (state
& ODS_FOCUS
) && autoDraw
)
487 DrawButtonFocus((WXHDC
) hDC
,
491 lpDIS
->rcItem
.bottom
,
498 // GRG Feb/2000, support for bmp buttons with Win95/98 standard LNF
500 void wxBitmapButton::DrawFace( WXHDC dc
, int left
, int top
,
501 int right
, int bottom
, bool sel
)
510 // create needed pens and brush
511 penHiLight
= CreatePen(PS_SOLID
, 0, GetSysColor(COLOR_3DHILIGHT
));
512 penLight
= CreatePen(PS_SOLID
, 0, GetSysColor(COLOR_3DLIGHT
));
513 penShadow
= CreatePen(PS_SOLID
, 0, GetSysColor(COLOR_3DSHADOW
));
514 penDkShadow
= CreatePen(PS_SOLID
, 0, GetSysColor(COLOR_3DDKSHADOW
));
515 brushFace
= CreateSolidBrush(GetSysColor(COLOR_BTNFACE
));
517 // draw the rectangle
522 rect
.bottom
= bottom
;
523 FillRect((HDC
) dc
, &rect
, brushFace
);
526 oldp
= (HPEN
) SelectObject( (HDC
) dc
, sel
? penDkShadow
: penHiLight
);
528 wxDrawLine((HDC
) dc
, left
, top
, right
-1, top
);
529 wxDrawLine((HDC
) dc
, left
, top
+1, left
, bottom
-1);
531 SelectObject( (HDC
) dc
, sel
? penShadow
: penLight
);
532 wxDrawLine((HDC
) dc
, left
+1, top
+1, right
-2, top
+1);
533 wxDrawLine((HDC
) dc
, left
+1, top
+2, left
+1, bottom
-2);
535 SelectObject( (HDC
) dc
, sel
? penLight
: penShadow
);
536 wxDrawLine((HDC
) dc
, left
+1, bottom
-2, right
-1, bottom
-2);
537 wxDrawLine((HDC
) dc
, right
-2, bottom
-3, right
-2, top
);
539 SelectObject( (HDC
) dc
, sel
? penHiLight
: penDkShadow
);
540 wxDrawLine((HDC
) dc
, left
, bottom
-1, right
+2, bottom
-1);
541 wxDrawLine((HDC
) dc
, right
-1, bottom
-2, right
-1, top
-1);
543 // delete allocated resources
544 SelectObject((HDC
) dc
,oldp
);
545 DeleteObject(penHiLight
);
546 DeleteObject(penLight
);
547 DeleteObject(penShadow
);
548 DeleteObject(penDkShadow
);
549 DeleteObject(brushFace
);
552 void wxBitmapButton::DrawButtonFocus( WXHDC dc
, int left
, int top
, int right
,
553 int bottom
, bool WXUNUSED(sel
) )
559 rect
.bottom
= bottom
;
560 InflateRect( &rect
, - FOCUS_MARGIN
, - FOCUS_MARGIN
);
562 // GRG: the focus rectangle should not move when the button is pushed!
565 OffsetRect( &rect, 1, 1 );
568 DrawFocusRect( (HDC
) dc
, &rect
);
572 wxBitmapButton::DrawButtonDisable( WXHDC dc
,
573 int left
, int top
, int right
, int bottom
,
576 if ( !m_brushDisabled
.Ok() )
578 // draw a bitmap with two black and two background colour pixels
581 dc
.SelectObject(bmp
);
582 dc
.SetPen(*wxBLACK_PEN
);
585 dc
.SetPen(GetBackgroundColour());
589 m_brushDisabled
= wxBrush(bmp
);
592 SelectInHDC
selectBrush((HDC
)dc
, GetHbrushOf(m_brushDisabled
));
594 // ROP for "dest |= pattern" operation -- as it doesn't have a standard
595 // name, give it our own
596 static const DWORD PATTERNPAINT
= 0xFA0089UL
;
602 right
-= 2 * m_marginX
;
603 bottom
-= 2 * m_marginY
;
606 ::PatBlt( (HDC
) dc
, left
, top
, right
, bottom
, PATTERNPAINT
);
609 wxSize
wxBitmapButton::DoGetBestSize() const
611 if ( m_bmpNormal
.Ok() )
613 int width
= m_bmpNormal
.GetWidth(),
614 height
= m_bmpNormal
.GetHeight();
619 if ( wxUxThemeEngine::GetIfActive() )
621 wxUxThemeHandle
theme((wxBitmapButton
*)this, L
"BUTTON");
624 wxUxThemeEngine::Get()->GetThemeMargins(theme
, NULL
,
625 BP_PUSHBUTTON
, PBS_NORMAL
,
626 TMT_CONTENTMARGINS
, NULL
,
629 // XP doesn't draw themed buttons correctly when the client area is
630 // smaller than 8x8 - enforce this minimum size for small bitmaps
636 // don't add margins for the borderless buttons, they don't need
637 // them and it just makes them appear larger than needed
638 if ( !HasFlag(wxBORDER_NONE
) )
640 // we need 2 extra pixels for the focus rectangle, without them
641 // it's overwritten by the bitmap itself
642 marginH
= margins
.cxLeftWidth
+ margins
.cxRightWidth
+ 2;
643 marginV
= margins
.cyTopHeight
+ margins
.cyBottomHeight
+ 2;
647 #endif // wxUSE_UXTHEME
649 if ( !HasFlag(wxBORDER_NONE
) )
651 marginH
= 2*m_marginX
;
652 marginV
= 2*m_marginY
;
656 wxSize
best(width
+ marginH
, height
+ marginV
);
661 // no idea what our best size should be, defer to the base class
662 return wxBitmapButtonBase::DoGetBestSize();
665 #endif // wxUSE_BMPBUTTON