]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/msw/bmpbuttn.cpp
Style listbox now shows current style
[wxWidgets.git] / src / msw / bmpbuttn.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/msw/bmpbuttn.cpp
3// Purpose: wxBitmapButton
4// Author: Julian Smart
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
15#ifdef __BORLANDC__
16 #pragma hdrstop
17#endif
18
19#if wxUSE_BMPBUTTON
20
21#include "wx/bmpbuttn.h"
22
23#ifndef WX_PRECOMP
24 #include "wx/log.h"
25 #include "wx/dcmemory.h"
26 #include "wx/image.h"
27#endif
28
29#include "wx/msw/private.h"
30
31#include "wx/msw/uxtheme.h"
32
33#if wxUSE_UXTHEME
34 // no need to include tmschema.h
35 #ifndef BP_PUSHBUTTON
36 #define BP_PUSHBUTTON 1
37
38 #define PBS_NORMAL 1
39 #define PBS_HOT 2
40 #define PBS_PRESSED 3
41 #define PBS_DISABLED 4
42 #define PBS_DEFAULTED 5
43
44 #define TMT_CONTENTMARGINS 3602
45 #endif
46#endif // wxUSE_UXTHEME
47
48#ifndef ODS_NOFOCUSRECT
49 #define ODS_NOFOCUSRECT 0x0200
50#endif
51
52// ----------------------------------------------------------------------------
53// macros
54// ----------------------------------------------------------------------------
55
56#if wxUSE_EXTENDED_RTTI
57
58WX_DEFINE_FLAGS( wxBitmapButtonStyle )
59
60wxBEGIN_FLAGS( wxBitmapButtonStyle )
61 // new style border flags, we put them first to
62 // use them for streaming out
63 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
64 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
65 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
66 wxFLAGS_MEMBER(wxBORDER_RAISED)
67 wxFLAGS_MEMBER(wxBORDER_STATIC)
68 wxFLAGS_MEMBER(wxBORDER_NONE)
69
70 // old style border flags
71 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
72 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
73 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
74 wxFLAGS_MEMBER(wxRAISED_BORDER)
75 wxFLAGS_MEMBER(wxSTATIC_BORDER)
76 wxFLAGS_MEMBER(wxBORDER)
77
78 // standard window styles
79 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
80 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
81 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
82 wxFLAGS_MEMBER(wxWANTS_CHARS)
83 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
84 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
85 wxFLAGS_MEMBER(wxVSCROLL)
86 wxFLAGS_MEMBER(wxHSCROLL)
87
88 wxFLAGS_MEMBER(wxBU_AUTODRAW)
89 wxFLAGS_MEMBER(wxBU_LEFT)
90 wxFLAGS_MEMBER(wxBU_RIGHT)
91 wxFLAGS_MEMBER(wxBU_TOP)
92 wxFLAGS_MEMBER(wxBU_BOTTOM)
93wxEND_FLAGS( wxBitmapButtonStyle )
94
95IMPLEMENT_DYNAMIC_CLASS_XTI(wxBitmapButton, wxButton,"wx/bmpbuttn.h")
96
97wxBEGIN_PROPERTIES_TABLE(wxBitmapButton)
98 wxPROPERTY_FLAGS( WindowStyle , wxBitmapButtonStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
99wxEND_PROPERTIES_TABLE()
100
101wxBEGIN_HANDLERS_TABLE(wxBitmapButton)
102wxEND_HANDLERS_TABLE()
103
104wxCONSTRUCTOR_5( wxBitmapButton , wxWindow* , Parent , wxWindowID , Id , wxBitmap , Bitmap , wxPoint , Position , wxSize , Size )
105
106#else
107IMPLEMENT_DYNAMIC_CLASS(wxBitmapButton, wxButton)
108#endif
109
110BEGIN_EVENT_TABLE(wxBitmapButton, wxBitmapButtonBase)
111 EVT_SYS_COLOUR_CHANGED(wxBitmapButton::OnSysColourChanged)
112 EVT_ENTER_WINDOW(wxBitmapButton::OnMouseEnterOrLeave)
113 EVT_LEAVE_WINDOW(wxBitmapButton::OnMouseEnterOrLeave)
114END_EVENT_TABLE()
115
116/*
117TODO PROPERTIES :
118
119long "style" , wxBU_AUTODRAW
120bool "default" , 0
121bitmap "selected" ,
122bitmap "focus" ,
123bitmap "disabled" ,
124*/
125
126bool wxBitmapButton::Create(wxWindow *parent, wxWindowID id,
127 const wxBitmap& bitmap,
128 const wxPoint& pos,
129 const wxSize& size, long style,
130 const wxValidator& wxVALIDATOR_PARAM(validator),
131 const wxString& name)
132{
133 m_bmpNormal = bitmap;
134 SetName(name);
135
136#if wxUSE_VALIDATORS
137 SetValidator(validator);
138#endif // wxUSE_VALIDATORS
139
140 parent->AddChild(this);
141
142 m_windowStyle = style;
143
144 if ( style & wxBU_AUTODRAW )
145 {
146 m_marginX =
147 m_marginY = 4;
148 }
149
150 if (id == wxID_ANY)
151 m_windowId = NewControlId();
152 else
153 m_windowId = id;
154
155 long msStyle = WS_VISIBLE | WS_TABSTOP | WS_CHILD | BS_OWNERDRAW ;
156
157 if ( m_windowStyle & wxCLIP_SIBLINGS )
158 msStyle |= WS_CLIPSIBLINGS;
159
160#ifdef __WIN32__
161 if(m_windowStyle & wxBU_LEFT)
162 msStyle |= BS_LEFT;
163 if(m_windowStyle & wxBU_RIGHT)
164 msStyle |= BS_RIGHT;
165 if(m_windowStyle & wxBU_TOP)
166 msStyle |= BS_TOP;
167 if(m_windowStyle & wxBU_BOTTOM)
168 msStyle |= BS_BOTTOM;
169#endif
170
171 m_hWnd = (WXHWND) CreateWindowEx(
172 0,
173 wxT("BUTTON"),
174 wxEmptyString,
175 msStyle,
176 0, 0, 0, 0,
177 GetWinHwnd(parent),
178 (HMENU)m_windowId,
179 wxGetInstance(),
180 NULL
181 );
182
183 // Subclass again for purposes of dialog editing mode
184 SubclassWin(m_hWnd);
185
186 SetPosition(pos);
187 SetBestSize(size);
188
189 return true;
190}
191
192bool wxBitmapButton::SetBackgroundColour(const wxColour& colour)
193{
194 if ( !wxBitmapButtonBase::SetBackgroundColour(colour) )
195 {
196 // didn't change
197 return false;
198 }
199
200 // invalidate the brush, it will be recreated the next time it's needed
201 m_brushDisabled = wxNullBrush;
202
203 return true;
204}
205
206void wxBitmapButton::OnSysColourChanged(wxSysColourChangedEvent& event)
207{
208 m_brushDisabled = wxNullBrush;
209
210 if ( !IsEnabled() )
211 {
212 // this change affects our current state
213 Refresh();
214 }
215
216 event.Skip();
217}
218
219void wxBitmapButton::OnMouseEnterOrLeave(wxMouseEvent& event)
220{
221 if ( IsEnabled() && m_bmpHover.Ok() )
222 Refresh();
223
224 event.Skip();
225}
226
227void wxBitmapButton::OnSetBitmap()
228{
229 // if the focus bitmap is specified but hover one isn't, use the focus
230 // bitmap for hovering as well if this is consistent with the current
231 // Windows version look and feel
232 //
233 // rationale: this is compatible with the old wxGTK behaviour and also
234 // makes it much easier to do "the right thing" for all platforms (some of
235 // them, such as Windows XP, have "hot" buttons while others don't)
236 if ( !m_bmpHover.Ok() &&
237 m_bmpFocus.Ok() &&
238 wxUxThemeEngine::GetIfActive() )
239 {
240 m_bmpHover = m_bmpFocus;
241 }
242
243 // this will redraw us
244 wxBitmapButtonBase::OnSetBitmap();
245}
246
247#if wxUSE_UXTHEME
248static
249void MSWDrawXPBackground(wxButton *button, WXDRAWITEMSTRUCT *wxdis)
250{
251 LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)wxdis;
252 HDC hdc = lpDIS->hDC;
253 UINT state = lpDIS->itemState;
254 RECT rectBtn;
255 CopyRect(&rectBtn, &lpDIS->rcItem);
256
257 wxUxThemeHandle theme(button, L"BUTTON");
258 int iState;
259
260 if ( state & ODS_SELECTED )
261 {
262 iState = PBS_PRESSED;
263 }
264 else if ( button->HasCapture() || button->IsMouseInWindow() )
265 {
266 iState = PBS_HOT;
267 }
268 else if ( state & ODS_FOCUS )
269 {
270 iState = PBS_DEFAULTED;
271 }
272 else if ( state & ODS_DISABLED )
273 {
274 iState = PBS_DISABLED;
275 }
276 else
277 {
278 iState = PBS_NORMAL;
279 }
280
281 // draw parent background if needed
282 if ( wxUxThemeEngine::Get()->IsThemeBackgroundPartiallyTransparent(theme,
283 BP_PUSHBUTTON,
284 iState) )
285 {
286 wxUxThemeEngine::Get()->DrawThemeParentBackground(GetHwndOf(button), hdc, &rectBtn);
287 }
288
289 // draw background
290 wxUxThemeEngine::Get()->DrawThemeBackground(theme, hdc, BP_PUSHBUTTON, iState,
291 &rectBtn, NULL);
292
293 // calculate content area margins
294 MARGINS margins;
295 wxUxThemeEngine::Get()->GetThemeMargins(theme, hdc, BP_PUSHBUTTON, iState,
296 TMT_CONTENTMARGINS, &rectBtn, &margins);
297 RECT rectClient;
298 ::CopyRect(&rectClient, &rectBtn);
299 ::InflateRect(&rectClient, -margins.cxLeftWidth, -margins.cyTopHeight);
300
301 // if focused and !nofocus rect
302 if ( (state & ODS_FOCUS) && !(state & ODS_NOFOCUSRECT) )
303 {
304 DrawFocusRect(hdc, &rectClient);
305 }
306
307 if ( button->UseBgCol() )
308 {
309 COLORREF colBg = wxColourToRGB(button->GetBackgroundColour());
310 HBRUSH hbrushBackground = ::CreateSolidBrush(colBg);
311
312 // don't overwrite the focus rect
313 ::InflateRect(&rectClient, -1, -1);
314 FillRect(hdc, &rectClient, hbrushBackground);
315 ::DeleteObject(hbrushBackground);
316 }
317}
318#endif // wxUSE_UXTHEME
319
320// VZ: should be at the very least less than wxDEFAULT_BUTTON_MARGIN
321#define FOCUS_MARGIN 3
322
323bool wxBitmapButton::MSWOnDraw(WXDRAWITEMSTRUCT *item)
324{
325#ifndef __WXWINCE__
326 long style = GetWindowLong((HWND) GetHWND(), GWL_STYLE);
327 if (style & BS_BITMAP)
328 {
329 // Let default procedure draw the bitmap, which is defined
330 // in the Windows resource.
331 return false;
332 }
333#endif
334
335 LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT) item;
336 HDC hDC = lpDIS->hDC;
337 UINT state = lpDIS->itemState;
338 bool isSelected = (state & ODS_SELECTED) != 0;
339 bool autoDraw = (GetWindowStyleFlag() & wxBU_AUTODRAW) != 0;
340
341
342 // choose the bitmap to use depending on the button state
343 wxBitmap *bitmap;
344
345 if ( isSelected && m_bmpSelected.Ok() )
346 bitmap = &m_bmpSelected;
347 else if ( m_bmpHover.Ok() && IsMouseInWindow() )
348 bitmap = &m_bmpHover;
349 else if ((state & ODS_FOCUS) && m_bmpFocus.Ok())
350 bitmap = &m_bmpFocus;
351 else if ((state & ODS_DISABLED) && m_bmpDisabled.Ok())
352 bitmap = &m_bmpDisabled;
353 else
354 bitmap = &m_bmpNormal;
355
356 if ( !bitmap->Ok() )
357 return false;
358
359 // centre the bitmap in the control area
360 int x = lpDIS->rcItem.left;
361 int y = lpDIS->rcItem.top;
362 int width = lpDIS->rcItem.right - x;
363 int height = lpDIS->rcItem.bottom - y;
364 int wBmp = bitmap->GetWidth();
365 int hBmp = bitmap->GetHeight();
366
367#if wxUSE_UXTHEME
368 if ( autoDraw && wxUxThemeEngine::GetIfActive() )
369 {
370 MSWDrawXPBackground(this, item);
371 wxUxThemeHandle theme(this, L"BUTTON");
372
373 // calculate content area margins
374 // assuming here that each state is the same size
375 MARGINS margins;
376 wxUxThemeEngine::Get()->GetThemeMargins(theme, NULL,
377 BP_PUSHBUTTON, PBS_NORMAL,
378 TMT_CONTENTMARGINS, NULL,
379 &margins);
380 int marginX = margins.cxLeftWidth + 1;
381 int marginY = margins.cyTopHeight + 1;
382 int x1,y1;
383
384 if ( m_windowStyle & wxBU_LEFT )
385 {
386 x1 = x + marginX;
387 }
388 else if ( m_windowStyle & wxBU_RIGHT )
389 {
390 x1 = x + (width - wBmp) - marginX;
391 }
392 else
393 {
394 x1 = x + (width - wBmp) / 2;
395 }
396
397 if ( m_windowStyle & wxBU_TOP )
398 {
399 y1 = y + marginY;
400 }
401 else if ( m_windowStyle & wxBU_BOTTOM )
402 {
403 y1 = y + (height - hBmp) - marginY;
404 }
405 else
406 {
407 y1 = y + (height - hBmp) / 2;
408 }
409
410 // draw the bitmap
411 wxClientDC dst;
412 dst.SetHDC((WXHDC) hDC, false);
413 dst.DrawBitmap(*bitmap, x1, y1, true);
414
415 return true;
416 }
417#endif // wxUSE_UXTHEME
418
419 int x1,y1;
420
421 if(m_windowStyle & wxBU_LEFT)
422 x1 = x + (FOCUS_MARGIN+1);
423 else if(m_windowStyle & wxBU_RIGHT)
424 x1 = x + (width - wBmp) - (FOCUS_MARGIN+1);
425 else
426 x1 = x + (width - wBmp) / 2;
427
428 if(m_windowStyle & wxBU_TOP)
429 y1 = y + (FOCUS_MARGIN+1);
430 else if(m_windowStyle & wxBU_BOTTOM)
431 y1 = y + (height - hBmp) - (FOCUS_MARGIN+1);
432 else
433 y1 = y + (height - hBmp) / 2;
434
435 if ( isSelected && autoDraw )
436 {
437 x1++;
438 y1++;
439 }
440
441 // draw the face, if auto-drawing
442 if ( autoDraw )
443 {
444 DrawFace((WXHDC) hDC,
445 lpDIS->rcItem.left, lpDIS->rcItem.top,
446 lpDIS->rcItem.right, lpDIS->rcItem.bottom,
447 isSelected);
448 }
449
450 // draw the bitmap
451 wxDCTemp dst((WXHDC)hDC);
452 dst.DrawBitmap(*bitmap, x1, y1, true);
453
454 // draw focus / disabled state, if auto-drawing
455 if ( (state & ODS_DISABLED) && autoDraw )
456 {
457 DrawButtonDisable((WXHDC) hDC,
458 lpDIS->rcItem.left, lpDIS->rcItem.top,
459 lpDIS->rcItem.right, lpDIS->rcItem.bottom,
460 true);
461 }
462 else if ( (state & ODS_FOCUS) && autoDraw )
463 {
464 DrawButtonFocus((WXHDC) hDC,
465 lpDIS->rcItem.left,
466 lpDIS->rcItem.top,
467 lpDIS->rcItem.right,
468 lpDIS->rcItem.bottom,
469 isSelected);
470 }
471
472 return true;
473}
474
475// GRG Feb/2000, support for bmp buttons with Win95/98 standard LNF
476
477void wxBitmapButton::DrawFace( WXHDC dc, int left, int top,
478 int right, int bottom, bool sel )
479{
480 HPEN oldp;
481 HPEN penHiLight;
482 HPEN penLight;
483 HPEN penShadow;
484 HPEN penDkShadow;
485 HBRUSH brushFace;
486
487 // create needed pens and brush
488 penHiLight = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_3DHILIGHT));
489 penLight = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_3DLIGHT));
490 penShadow = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_3DSHADOW));
491 penDkShadow = CreatePen(PS_SOLID, 0, GetSysColor(COLOR_3DDKSHADOW));
492 brushFace = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
493
494 // draw the rectangle
495 RECT rect;
496 rect.left = left;
497 rect.right = right;
498 rect.top = top;
499 rect.bottom = bottom;
500 FillRect((HDC) dc, &rect, brushFace);
501
502 // draw the border
503 oldp = (HPEN) SelectObject( (HDC) dc, sel? penDkShadow : penHiLight);
504
505 wxDrawLine((HDC) dc, left, top, right-1, top);
506 wxDrawLine((HDC) dc, left, top+1, left, bottom-1);
507
508 SelectObject( (HDC) dc, sel? penShadow : penLight);
509 wxDrawLine((HDC) dc, left+1, top+1, right-2, top+1);
510 wxDrawLine((HDC) dc, left+1, top+2, left+1, bottom-2);
511
512 SelectObject( (HDC) dc, sel? penLight : penShadow);
513 wxDrawLine((HDC) dc, left+1, bottom-2, right-1, bottom-2);
514 wxDrawLine((HDC) dc, right-2, bottom-3, right-2, top);
515
516 SelectObject( (HDC) dc, sel? penHiLight : penDkShadow);
517 wxDrawLine((HDC) dc, left, bottom-1, right+2, bottom-1);
518 wxDrawLine((HDC) dc, right-1, bottom-2, right-1, top-1);
519
520 // delete allocated resources
521 SelectObject((HDC) dc,oldp);
522 DeleteObject(penHiLight);
523 DeleteObject(penLight);
524 DeleteObject(penShadow);
525 DeleteObject(penDkShadow);
526 DeleteObject(brushFace);
527}
528
529void wxBitmapButton::DrawButtonFocus( WXHDC dc, int left, int top, int right,
530 int bottom, bool WXUNUSED(sel) )
531{
532 RECT rect;
533 rect.left = left;
534 rect.top = top;
535 rect.right = right;
536 rect.bottom = bottom;
537 InflateRect( &rect, - FOCUS_MARGIN, - FOCUS_MARGIN );
538
539 // GRG: the focus rectangle should not move when the button is pushed!
540/*
541 if ( sel )
542 OffsetRect( &rect, 1, 1 );
543*/
544
545 DrawFocusRect( (HDC) dc, &rect );
546}
547
548void
549wxBitmapButton::DrawButtonDisable( WXHDC dc,
550 int left, int top, int right, int bottom,
551 bool with_marg )
552{
553 if ( !m_brushDisabled.Ok() )
554 {
555 // draw a bitmap with two black and two background colour pixels
556 wxBitmap bmp(2, 2);
557 wxMemoryDC dc;
558 dc.SelectObject(bmp);
559 dc.SetPen(*wxBLACK_PEN);
560 dc.DrawPoint(0, 0);
561 dc.DrawPoint(1, 1);
562 dc.SetPen(GetBackgroundColour());
563 dc.DrawPoint(0, 1);
564 dc.DrawPoint(1, 0);
565
566 m_brushDisabled = wxBrush(bmp);
567 }
568
569 SelectInHDC selectBrush((HDC)dc, GetHbrushOf(m_brushDisabled));
570
571 // ROP for "dest |= pattern" operation -- as it doesn't have a standard
572 // name, give it our own
573 static const DWORD PATTERNPAINT = 0xFA0089UL;
574
575 if ( with_marg )
576 {
577 left += m_marginX;
578 top += m_marginY;
579 right -= 2 * m_marginX;
580 bottom -= 2 * m_marginY;
581 }
582
583 ::PatBlt( (HDC) dc, left, top, right, bottom, PATTERNPAINT);
584}
585
586void wxBitmapButton::SetDefault()
587{
588 wxButton::SetDefault();
589}
590
591wxSize wxBitmapButton::DoGetBestSize() const
592{
593 if ( m_bmpNormal.Ok() )
594 {
595 int width = m_bmpNormal.GetWidth(),
596 height = m_bmpNormal.GetHeight();
597 int marginH = 0,
598 marginV = 0;
599
600#if wxUSE_UXTHEME
601 if ( wxUxThemeEngine::GetIfActive() )
602 {
603 wxUxThemeHandle theme((wxBitmapButton *)this, L"BUTTON");
604
605 MARGINS margins;
606 wxUxThemeEngine::Get()->GetThemeMargins(theme, NULL,
607 BP_PUSHBUTTON, PBS_NORMAL,
608 TMT_CONTENTMARGINS, NULL,
609 &margins);
610
611 // XP doesn't draw themed buttons correctly when the client area is
612 // smaller than 8x8 - enforce this minimum size for small bitmaps
613 if ( width < 8 )
614 width = 8;
615 if ( height < 8 )
616 height = 8;
617
618 // don't add margins for the borderless buttons, they don't need
619 // them and it just makes them appear larger than needed
620 if ( !HasFlag(wxBORDER_NONE) )
621 {
622 // we need 2 extra pixels for the focus rectangle, without them
623 // it's overwritten by the bitmap itself
624 marginH = margins.cxLeftWidth + margins.cxRightWidth + 2;
625 marginV = margins.cyTopHeight + margins.cyBottomHeight + 2;
626 }
627 }
628 else
629#endif // wxUSE_UXTHEME
630 {
631 if ( !HasFlag(wxBORDER_NONE) )
632 {
633 marginH = 2*m_marginX;
634 marginV = 2*m_marginY;
635 }
636 }
637
638 wxSize best(width + marginH, height + marginV);
639 CacheBestSize(best);
640 return best;
641 }
642
643 // no idea what our best size should be, defer to the base class
644 return wxBitmapButtonBase::DoGetBestSize();
645}
646
647#endif // wxUSE_BMPBUTTON