]> git.saurik.com Git - wxWidgets.git/blame - src/msw/button.cpp
Correct wxDataViewListModel::RowPrepended
[wxWidgets.git] / src / msw / button.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
f1e01716 2// Name: src/msw/button.cpp
2bda0e17
KB
3// Purpose: wxButton
4// Author: Julian Smart
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
6c9a19aa 8// Copyright: (c) Julian Smart
65571936 9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
edccf428
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
cd0b1709 19
2bda0e17
KB
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
edccf428 24 #pragma hdrstop
2bda0e17
KB
25#endif
26
1e6feb95
VZ
27#if wxUSE_BUTTON
28
f1e01716
WS
29#include "wx/button.h"
30
2bda0e17 31#ifndef WX_PRECOMP
bac409a0 32 #include "wx/app.h"
edccf428 33 #include "wx/brush.h"
4e938f5b 34 #include "wx/panel.h"
8a4df159 35 #include "wx/bmpbuttn.h"
fb39c7ec
RR
36 #include "wx/settings.h"
37 #include "wx/dcscreen.h"
61e6a2ab 38 #include "wx/dcclient.h"
b84aec03 39 #include "wx/toplevel.h"
233f10bf 40 #include "wx/imaglist.h"
2bda0e17
KB
41#endif
42
5f7bcb48 43#include "wx/stockitem.h"
2bda0e17 44#include "wx/msw/private.h"
533171c2 45#include "wx/msw/private/button.h"
d8c89c48
VZ
46#include "wx/msw/private/dc.h"
47
48using namespace wxMSWImpl;
2bda0e17 49
4e9da8b7
RD
50#if wxUSE_UXTHEME
51 #include "wx/msw/uxtheme.h"
52
53 // no need to include tmschema.h
54 #ifndef BP_PUSHBUTTON
55 #define BP_PUSHBUTTON 1
56
57 #define PBS_NORMAL 1
58 #define PBS_HOT 2
59 #define PBS_PRESSED 3
60 #define PBS_DISABLED 4
61 #define PBS_DEFAULTED 5
62
63 #define TMT_CONTENTMARGINS 3602
64 #endif
2352862a 65
233f10bf
VZ
66 // provide the necessary declarations ourselves if they're missing from
67 // headers
2352862a
VZ
68 #ifndef BCM_SETIMAGELIST
69 #define BCM_SETIMAGELIST 0x1602
70 #define BCM_SETTEXTMARGIN 0x1604
233f10bf
VZ
71
72 enum
73 {
74 BUTTON_IMAGELIST_ALIGN_LEFT,
75 BUTTON_IMAGELIST_ALIGN_RIGHT,
76 BUTTON_IMAGELIST_ALIGN_TOP,
77 BUTTON_IMAGELIST_ALIGN_BOTTOM
78 };
79
80 struct BUTTON_IMAGELIST
81 {
82 HIMAGELIST himl;
83 RECT margin;
84 UINT uAlign;
85 };
2352862a 86 #endif
4e9da8b7
RD
87#endif // wxUSE_UXTHEME
88
89#ifndef WM_THEMECHANGED
90 #define WM_THEMECHANGED 0x031A
91#endif
92
93#ifndef ODS_NOACCEL
94 #define ODS_NOACCEL 0x0100
95#endif
96
97#ifndef ODS_NOFOCUSRECT
98 #define ODS_NOFOCUSRECT 0x0200
99#endif
100
f915b92e
VZ
101#ifndef DT_HIDEPREFIX
102 #define DT_HIDEPREFIX 0x00100000
103#endif
104
233f10bf
VZ
105// ----------------------------------------------------------------------------
106// button image data
107// ----------------------------------------------------------------------------
108
109// we use different data classes for owner drawn buttons and for themed XP ones
110
111class wxButtonImageData
112{
113public:
114 wxButtonImageData() { }
8f4745fe 115 virtual ~wxButtonImageData() { }
233f10bf
VZ
116
117 virtual wxBitmap GetBitmap(wxButton::State which) const = 0;
118 virtual void SetBitmap(const wxBitmap& bitmap, wxButton::State which) = 0;
119
120 virtual wxSize GetBitmapMargins() const = 0;
121 virtual void SetBitmapMargins(wxCoord x, wxCoord y) = 0;
122
8f4745fe 123 virtual wxDirection GetBitmapPosition() const = 0;
233f10bf
VZ
124 virtual void SetBitmapPosition(wxDirection dir) = 0;
125
126private:
127 wxDECLARE_NO_COPY_CLASS(wxButtonImageData);
128};
129
130namespace
131{
132
8f4745fe
VZ
133// the gap between button edge and the interior area used by Windows for the
134// standard buttons
135const int OD_BUTTON_MARGIN = 4;
136
233f10bf
VZ
137class wxODButtonImageData : public wxButtonImageData
138{
139public:
8f4745fe
VZ
140 wxODButtonImageData(wxButton *btn)
141 {
142 m_dir = wxLEFT;
143
144 m_margin.x = btn->GetCharWidth();
145 m_margin.y = btn->GetCharHeight() / 2;
146 }
233f10bf
VZ
147
148 virtual wxBitmap GetBitmap(wxButton::State which) const
149 {
150 return m_bitmaps[which];
151 }
152
153 virtual void SetBitmap(const wxBitmap& bitmap, wxButton::State which)
154 {
155 m_bitmaps[which] = bitmap;
156 }
157
158 virtual wxSize GetBitmapMargins() const
159 {
8f4745fe 160 return m_margin + wxSize(OD_BUTTON_MARGIN, OD_BUTTON_MARGIN);
233f10bf
VZ
161 }
162
163 virtual void SetBitmapMargins(wxCoord x, wxCoord y)
164 {
165 m_margin = wxSize(x, y);
166 }
167
8f4745fe 168 virtual wxDirection GetBitmapPosition() const
233f10bf 169 {
8f4745fe 170 return m_dir;
233f10bf
VZ
171 }
172
173 virtual void SetBitmapPosition(wxDirection dir)
174 {
175 m_dir = dir;
176 }
177
178private:
179 // just store the values passed to us to be able to retrieve them later
180 // from the drawing code
181 wxBitmap m_bitmaps[wxButton::State_Max];
182 wxSize m_margin;
183 wxDirection m_dir;
184
185 wxDECLARE_NO_COPY_CLASS(wxODButtonImageData);
186};
187
188#if wxUSE_UXTHEME
189
190class wxXPButtonImageData : public wxButtonImageData
191{
192public:
193 // we must be constructed with the size of our images as we need to create
194 // the image list
195 wxXPButtonImageData(wxButton *btn, const wxSize& size)
196 : m_iml(size.x, size.y, true /* use mask */, wxButton::State_Max),
197 m_hwndBtn(GetHwndOf(btn))
198 {
199 m_data.himl = GetHimagelistOf(&m_iml);
200
201 // use default margins
202 m_data.margin.left =
203 m_data.margin.right = btn->GetCharWidth();
204 m_data.margin.top =
205 m_data.margin.bottom = btn->GetCharHeight() / 2;
206
207 // and default alignment
208 m_data.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT;
209 }
210
211 virtual wxBitmap GetBitmap(wxButton::State which) const
212 {
213 return m_iml.GetBitmap(which);
214 }
215
216 virtual void SetBitmap(const wxBitmap& bitmap, wxButton::State which)
217 {
218 const int imagesToAdd = which - m_iml.GetImageCount();
219 if ( imagesToAdd >= 0 )
220 {
221 if ( imagesToAdd > 0 )
222 {
223 const wxBitmap bmpNormal = GetBitmap(wxButton::State_Normal);
224 for ( int n = 0; n < imagesToAdd; n++ )
225 m_iml.Add(bmpNormal);
226 }
227
228 m_iml.Add(bitmap);
229 }
230 else // we already have this bitmap
231 {
232 m_iml.Replace(which, bitmap);
233 }
234
235 UpdateImageInfo();
236 }
237
238 virtual wxSize GetBitmapMargins() const
239 {
240 return wxSize(m_data.margin.left, m_data.margin.top);
241 }
242
243 virtual void SetBitmapMargins(wxCoord x, wxCoord y)
244 {
245 RECT& margin = m_data.margin;
246 margin.left =
247 margin.right = x;
248 margin.top =
249 margin.bottom = y;
250
251 if ( !::SendMessage(m_hwndBtn, BCM_SETTEXTMARGIN, 0, (LPARAM)&margin) )
252 {
253 wxLogDebug("SendMessage(BCM_SETTEXTMARGIN) failed");
254 }
255 }
256
8f4745fe 257 virtual wxDirection GetBitmapPosition() const
233f10bf 258 {
8f4745fe
VZ
259 switch ( m_data.uAlign )
260 {
261 default:
262 wxFAIL_MSG( "invalid image alignment" );
263 // fall through
264
265 case BUTTON_IMAGELIST_ALIGN_LEFT:
266 return wxLEFT;
267
268 case BUTTON_IMAGELIST_ALIGN_RIGHT:
269 return wxRIGHT;
270
271 case BUTTON_IMAGELIST_ALIGN_TOP:
272 return wxTOP;
273
274 case BUTTON_IMAGELIST_ALIGN_BOTTOM:
275 return wxBOTTOM;
276 }
233f10bf
VZ
277 }
278
279 virtual void SetBitmapPosition(wxDirection dir)
280 {
281 UINT alignNew;
282 switch ( dir )
283 {
284 default:
285 wxFAIL_MSG( "invalid direction" );
286 // fall through
287
288 case wxLEFT:
289 alignNew = BUTTON_IMAGELIST_ALIGN_LEFT;
290 break;
291
292 case wxRIGHT:
293 alignNew = BUTTON_IMAGELIST_ALIGN_RIGHT;
294 break;
295
296 case wxTOP:
297 alignNew = BUTTON_IMAGELIST_ALIGN_TOP;
298 break;
299
300 case wxBOTTOM:
301 alignNew = BUTTON_IMAGELIST_ALIGN_BOTTOM;
302 break;
303 }
304
305 if ( alignNew != m_data.uAlign )
306 {
307 m_data.uAlign = alignNew;
308 UpdateImageInfo();
309 }
310 }
311
312private:
313 void UpdateImageInfo()
314 {
315 if ( !::SendMessage(m_hwndBtn, BCM_SETIMAGELIST, 0, (LPARAM)&m_data) )
316 {
317 wxLogDebug("SendMessage(BCM_SETIMAGELIST) failed");
318 }
319 }
320
321 // we store image list separately to be able to use convenient wxImageList
322 // methods instead of working with raw HIMAGELIST
323 wxImageList m_iml;
324
325 // store the rest of the data in BCM_SETIMAGELIST-friendly form
326 BUTTON_IMAGELIST m_data;
327
328 // the button we're associated with
329 const HWND m_hwndBtn;
330
331
332 wxDECLARE_NO_COPY_CLASS(wxXPButtonImageData);
333};
334
335#endif // wxUSE_UXTHEME
336
337} // anonymous namespace
338
edccf428
VZ
339// ----------------------------------------------------------------------------
340// macros
341// ----------------------------------------------------------------------------
342
51596bcb 343#if wxUSE_EXTENDED_RTTI
067e9be6 344
f3291a82
SC
345WX_DEFINE_FLAGS( wxButtonStyle )
346
3ff066a4 347wxBEGIN_FLAGS( wxButtonStyle )
f3291a82
SC
348 // new style border flags, we put them first to
349 // use them for streaming out
3ff066a4
SC
350 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
351 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
352 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
353 wxFLAGS_MEMBER(wxBORDER_RAISED)
354 wxFLAGS_MEMBER(wxBORDER_STATIC)
355 wxFLAGS_MEMBER(wxBORDER_NONE)
fcf90ee1 356
f3291a82 357 // old style border flags
3ff066a4
SC
358 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
359 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
360 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
361 wxFLAGS_MEMBER(wxRAISED_BORDER)
362 wxFLAGS_MEMBER(wxSTATIC_BORDER)
cb0afb26 363 wxFLAGS_MEMBER(wxBORDER)
f3291a82 364
bc9fb572 365 // standard window styles
3ff066a4
SC
366 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
367 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
368 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
369 wxFLAGS_MEMBER(wxWANTS_CHARS)
cb0afb26 370 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
3ff066a4
SC
371 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
372 wxFLAGS_MEMBER(wxVSCROLL)
373 wxFLAGS_MEMBER(wxHSCROLL)
374
375 wxFLAGS_MEMBER(wxBU_LEFT)
376 wxFLAGS_MEMBER(wxBU_RIGHT)
377 wxFLAGS_MEMBER(wxBU_TOP)
378 wxFLAGS_MEMBER(wxBU_BOTTOM)
379 wxFLAGS_MEMBER(wxBU_EXACTFIT)
380wxEND_FLAGS( wxButtonStyle )
067e9be6 381
51596bcb
SC
382IMPLEMENT_DYNAMIC_CLASS_XTI(wxButton, wxControl,"wx/button.h")
383
3ff066a4 384wxBEGIN_PROPERTIES_TABLE(wxButton)
fcf90ee1 385 wxEVENT_PROPERTY( Click , wxEVT_COMMAND_BUTTON_CLICKED , wxCommandEvent)
067e9be6 386
fcf90ee1
WS
387 wxPROPERTY( Font , wxFont , SetFont , GetFont , EMPTY_MACROVALUE, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
388 wxPROPERTY( Label, wxString , SetLabel, GetLabel, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
067e9be6 389
af498247 390 wxPROPERTY_FLAGS( WindowStyle , wxButtonStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
51741307 391
3ff066a4 392wxEND_PROPERTIES_TABLE()
51596bcb 393
3ff066a4
SC
394wxBEGIN_HANDLERS_TABLE(wxButton)
395wxEND_HANDLERS_TABLE()
51596bcb 396
3ff066a4 397wxCONSTRUCTOR_6( wxButton , wxWindow* , Parent , wxWindowID , Id , wxString , Label , wxPoint , Position , wxSize , Size , long , WindowStyle )
51596bcb
SC
398
399
400#else
cd0b1709 401IMPLEMENT_DYNAMIC_CLASS(wxButton, wxControl)
51596bcb 402#endif
2bda0e17 403
edccf428
VZ
404// ============================================================================
405// implementation
406// ============================================================================
407
4af4dec6
VZ
408// ----------------------------------------------------------------------------
409// helper functions from wx/msw/private/button.h
410// ----------------------------------------------------------------------------
411
412void wxMSWButton::UpdateMultilineStyle(HWND hwnd, const wxString& label)
413{
414 // update BS_MULTILINE style depending on the new label (resetting it
415 // doesn't seem to do anything very useful but it shouldn't hurt and we do
416 // have to set it whenever the label becomes multi line as otherwise it
417 // wouldn't be shown correctly as we don't use BS_MULTILINE when creating
418 // the control unless it already has new lines in its label)
419 long styleOld = ::GetWindowLong(hwnd, GWL_STYLE),
420 styleNew;
421 if ( label.find(_T('\n')) != wxString::npos )
422 styleNew = styleOld | BS_MULTILINE;
423 else
424 styleNew = styleOld & ~BS_MULTILINE;
425
426 if ( styleNew != styleOld )
427 ::SetWindowLong(hwnd, GWL_STYLE, styleNew);
428}
429
433aca2d
VZ
430wxSize wxMSWButton::GetFittingSize(wxWindow *win, const wxSize& sizeLabel)
431{
432 // FIXME: this is pure guesswork, need to retrieve the real button margins
433 wxSize sizeBtn = sizeLabel;
434
435 sizeBtn.x += 3*win->GetCharWidth();
436 sizeBtn.y = 11*EDIT_HEIGHT_FROM_CHAR_HEIGHT(sizeLabel.y)/10;
437
438 return sizeBtn;
439}
440
4af4dec6
VZ
441wxSize wxMSWButton::ComputeBestSize(wxControl *btn)
442{
443 wxClientDC dc(btn);
444
433aca2d
VZ
445 wxSize sizeBtn;
446 dc.GetMultiLineTextExtent(btn->GetLabelText(), &sizeBtn.x, &sizeBtn.y);
4af4dec6 447
433aca2d 448 sizeBtn = GetFittingSize(btn, sizeBtn);
4af4dec6
VZ
449
450 // all buttons have at least the standard size unless the user explicitly
451 // wants them to be of smaller size and used wxBU_EXACTFIT style when
452 // creating the button
453 if ( !btn->HasFlag(wxBU_EXACTFIT) )
454 {
433aca2d
VZ
455 wxSize sizeDef = wxButton::GetDefaultSize();
456 if ( sizeBtn.x < sizeDef.x )
457 sizeBtn.x = sizeDef.x;
458 if ( sizeBtn.y < sizeDef.y )
459 sizeBtn.y = sizeDef.y;
4af4dec6
VZ
460 }
461
433aca2d
VZ
462 btn->CacheBestSize(sizeBtn);
463
464 return sizeBtn;
4af4dec6
VZ
465}
466
edccf428
VZ
467// ----------------------------------------------------------------------------
468// creation/destruction
469// ----------------------------------------------------------------------------
470
471bool wxButton::Create(wxWindow *parent,
472 wxWindowID id,
5f7bcb48 473 const wxString& lbl,
edccf428
VZ
474 const wxPoint& pos,
475 const wxSize& size,
476 long style,
477 const wxValidator& validator,
478 const wxString& name)
2bda0e17 479{
5f7bcb48
VS
480 wxString label(lbl);
481 if (label.empty() && wxIsStockID(id))
c87fc285 482 {
c4e1d0fc
VZ
483 // On Windows, some buttons aren't supposed to have mnemonics
484 label = wxGetStockLabel
485 (
486 id,
487 id == wxID_OK || id == wxID_CANCEL || id == wxID_CLOSE
488 ? wxSTOCK_NOFLAGS
489 : wxSTOCK_WITH_MNEMONIC
490 );
f1e01716
WS
491 }
492
5b2f31eb 493 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
fcf90ee1 494 return false;
edccf428 495
8292017c
VZ
496 WXDWORD exstyle;
497 WXDWORD msStyle = MSWGetStyle(style, &exstyle);
498
8292017c
VZ
499 // if the label contains several lines we must explicitly tell the button
500 // about it or it wouldn't draw it correctly ("\n"s would just appear as
501 // black boxes)
502 //
503 // NB: we do it here and not in MSWGetStyle() because we need the label
d94de683 504 // value and the label is not set yet when MSWGetStyle() is called
533171c2 505 msStyle |= wxMSWButton::GetMultilineStyle(label);
8292017c
VZ
506
507 return MSWCreateControl(_T("BUTTON"), msStyle, pos, size, label, exstyle);
5b2f31eb
VZ
508}
509
510wxButton::~wxButton()
511{
6c20e8f8
VZ
512 wxTopLevelWindow *tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
513 if ( tlw && tlw->GetTmpDefaultItem() == this )
789367e1
VZ
514 {
515 UnsetTmpDefault();
516 }
233f10bf
VZ
517
518 delete m_imageData;
5b2f31eb 519}
edccf428 520
5b2f31eb
VZ
521// ----------------------------------------------------------------------------
522// flags
523// ----------------------------------------------------------------------------
cd0b1709 524
5b2f31eb
VZ
525WXDWORD wxButton::MSWGetStyle(long style, WXDWORD *exstyle) const
526{
527 // buttons never have an external border, they draw their own one
528 WXDWORD msStyle = wxControl::MSWGetStyle
529 (
530 (style & ~wxBORDER_MASK) | wxBORDER_NONE, exstyle
531 );
f6bcfd97 532
5b2f31eb
VZ
533 // we must use WS_CLIPSIBLINGS with the buttons or they would draw over
534 // each other in any resizeable dialog which has more than one button in
535 // the bottom
536 msStyle |= WS_CLIPSIBLINGS;
b0766406 537
5b2f31eb
VZ
538 // don't use "else if" here: weird as it is, but you may combine wxBU_LEFT
539 // and wxBU_RIGHT to get BS_CENTER!
540 if ( style & wxBU_LEFT )
f6bcfd97 541 msStyle |= BS_LEFT;
5b2f31eb 542 if ( style & wxBU_RIGHT )
f6bcfd97 543 msStyle |= BS_RIGHT;
5b2f31eb 544 if ( style & wxBU_TOP )
f6bcfd97 545 msStyle |= BS_TOP;
5b2f31eb 546 if ( style & wxBU_BOTTOM )
f6bcfd97 547 msStyle |= BS_BOTTOM;
8cc4850c 548#ifndef __WXWINCE__
8a094d7b
JS
549 // flat 2d buttons
550 if ( style & wxNO_BORDER )
551 msStyle |= BS_FLAT;
8cc4850c 552#endif // __WXWINCE__
edccf428 553
5b2f31eb 554 return msStyle;
2bda0e17
KB
555}
556
d94de683
VZ
557void wxButton::SetLabel(const wxString& label)
558{
533171c2 559 wxMSWButton::UpdateMultilineStyle(GetHwnd(), label);
d94de683
VZ
560
561 wxButtonBase::SetLabel(label);
562}
563
edccf428
VZ
564// ----------------------------------------------------------------------------
565// size management including autosizing
566// ----------------------------------------------------------------------------
567
f68586e5 568wxSize wxButton::DoGetBestSize() const
2bda0e17 569{
233f10bf
VZ
570 wxSize size = wxMSWButton::ComputeBestSize(const_cast<wxButton *>(this));
571 if ( m_imageData )
572 {
573 const wxSize sizeBmp = m_imageData->GetBitmap(State_Normal).GetSize();
8f4745fe
VZ
574 const wxDirection dirBmp = m_imageData->GetBitmapPosition();
575 if ( dirBmp == wxLEFT || dirBmp == wxRIGHT )
233f10bf
VZ
576 {
577 size.x += sizeBmp.x;
578 if ( sizeBmp.y > size.y )
579 size.y = sizeBmp.y;
580 }
581 else // bitmap on top/below the text
582 {
583 size.y += sizeBmp.y;
584 if ( sizeBmp.x > size.x )
585 size.x = sizeBmp.x;
586 }
587
588 size += 2*m_imageData->GetBitmapMargins();
589
590 CacheBestSize(size);
591 }
592
593 return size;
2bda0e17
KB
594}
595
e1f36ff8 596/* static */
1e6feb95 597wxSize wxButtonBase::GetDefaultSize()
e1f36ff8 598{
8c3c31d4 599 static wxSize s_sizeBtn;
e1f36ff8 600
8c3c31d4
VZ
601 if ( s_sizeBtn.x == 0 )
602 {
603 wxScreenDC dc;
a756f210 604 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
8c3c31d4
VZ
605
606 // the size of a standard button in the dialog units is 50x14,
607 // translate this to pixels
608 // NB1: the multipliers come from the Windows convention
609 // NB2: the extra +1/+2 were needed to get the size be the same as the
610 // size of the buttons in the standard dialog - I don't know how
611 // this happens, but on my system this size is 75x23 in pixels and
612 // 23*8 isn't even divisible by 14... Would be nice to understand
613 // why these constants are needed though!
614 s_sizeBtn.x = (50 * (dc.GetCharWidth() + 1))/4;
615 s_sizeBtn.y = ((14 * dc.GetCharHeight()) + 2)/8;
616 }
e1f36ff8 617
8c3c31d4 618 return s_sizeBtn;
e1f36ff8
VZ
619}
620
4438caf4 621// ----------------------------------------------------------------------------
036da5e3 622// default button handling
4438caf4
VZ
623// ----------------------------------------------------------------------------
624
d78f09e2
VZ
625/*
626 "Everything you ever wanted to know about the default buttons" or "Why do we
627 have to do all this?"
628
629 In MSW the default button should be activated when the user presses Enter
630 and the current control doesn't process Enter itself somehow. This is
631 handled by ::DefWindowProc() (or maybe ::DefDialogProc()) using DM_SETDEFID
632 Another aspect of "defaultness" is that the default button has different
633 appearance: this is due to BS_DEFPUSHBUTTON style which is completely
7fb1b2b4
VZ
634 separate from DM_SETDEFID stuff (!). Also note that BS_DEFPUSHBUTTON should
635 be unset if our parent window is not active so it should be unset whenever
636 we lose activation and set back when we regain it.
d78f09e2
VZ
637
638 Final complication is that when a button is active, it should be the default
639 one, i.e. pressing Enter on a button always activates it and not another
640 one.
641
642 We handle this by maintaining a permanent and a temporary default items in
643 wxControlContainer (both may be NULL). When a button becomes the current
644 control (i.e. gets focus) it sets itself as the temporary default which
645 ensures that it has the right appearance and that Enter will be redirected
646 to it. When the button loses focus, it unsets the temporary default and so
647 the default item will be the permanent default -- that is the default button
648 if any had been set or none otherwise, which is just what we want.
649
7fb1b2b4
VZ
650 NB: all this is quite complicated by now and the worst is that normally
651 it shouldn't be necessary at all as for the normal Windows programs
652 DefWindowProc() and IsDialogMessage() take care of all this
77ffb593 653 automatically -- however in wxWidgets programs this doesn't work for
7fb1b2b4
VZ
654 nested hierarchies (i.e. a notebook inside a notebook) for unknown
655 reason and so we have to reproduce all this code ourselves. It would be
656 very nice if we could avoid doing it.
d78f09e2
VZ
657 */
658
036da5e3 659// set this button as the (permanently) default one in its panel
94aff5ff 660wxWindow *wxButton::SetDefault()
2bda0e17 661{
77ffb593 662 // set this one as the default button both for wxWidgets ...
94aff5ff 663 wxWindow *winOldDefault = wxButtonBase::SetDefault();
036da5e3 664
7fb1b2b4 665 // ... and Windows
fcf90ee1
WS
666 SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), false);
667 SetDefaultStyle(this, true);
ebc0b155
VZ
668
669 return winOldDefault;
036da5e3
VZ
670}
671
627c8d89
VZ
672// return the top level parent window if it's not being deleted yet, otherwise
673// return NULL
05c5f281
VZ
674static wxTopLevelWindow *GetTLWParentIfNotBeingDeleted(wxWindow *win)
675{
627c8d89 676 for ( ;; )
05c5f281 677 {
627c8d89
VZ
678 // IsTopLevel() will return false for a wxTLW being deleted, so we also
679 // need the parent test for this case
680 wxWindow * const parent = win->GetParent();
681 if ( !parent || win->IsTopLevel() )
9617f65b
VZ
682 {
683 if ( win->IsBeingDeleted() )
684 return NULL;
627c8d89 685
05c5f281 686 break;
9617f65b 687 }
627c8d89
VZ
688
689 win = parent;
05c5f281
VZ
690 }
691
692 wxASSERT_MSG( win, _T("button without top level parent?") );
693
627c8d89
VZ
694 wxTopLevelWindow * const tlw = wxDynamicCast(win, wxTopLevelWindow);
695 wxASSERT_MSG( tlw, _T("logic error in GetTLWParentIfNotBeingDeleted()") );
696
697 return tlw;
05c5f281
VZ
698}
699
7fb1b2b4 700// set this button as being currently default
036da5e3
VZ
701void wxButton::SetTmpDefault()
702{
05c5f281
VZ
703 wxTopLevelWindow * const tlw = GetTLWParentIfNotBeingDeleted(GetParent());
704 if ( !tlw )
705 return;
036da5e3 706
6c20e8f8
VZ
707 wxWindow *winOldDefault = tlw->GetDefaultItem();
708 tlw->SetTmpDefaultItem(this);
7fb1b2b4 709
fcf90ee1
WS
710 SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), false);
711 SetDefaultStyle(this, true);
036da5e3
VZ
712}
713
7fb1b2b4 714// unset this button as currently default, it may still stay permanent default
036da5e3
VZ
715void wxButton::UnsetTmpDefault()
716{
05c5f281
VZ
717 wxTopLevelWindow * const tlw = GetTLWParentIfNotBeingDeleted(GetParent());
718 if ( !tlw )
719 return;
036da5e3 720
6c20e8f8 721 tlw->SetTmpDefaultItem(NULL);
036da5e3 722
6c20e8f8 723 wxWindow *winOldDefault = tlw->GetDefaultItem();
7fb1b2b4 724
fcf90ee1
WS
725 SetDefaultStyle(this, false);
726 SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), true);
036da5e3 727}
8ed57d93 728
036da5e3
VZ
729/* static */
730void
7fb1b2b4 731wxButton::SetDefaultStyle(wxButton *btn, bool on)
036da5e3 732{
7fb1b2b4
VZ
733 // we may be called with NULL pointer -- simpler to do the check here than
734 // in the caller which does wxDynamicCast()
735 if ( !btn )
736 return;
737
738 // first, let DefDlgProc() know about the new default button
739 if ( on )
5d1d2d46 740 {
7fb1b2b4
VZ
741 // we shouldn't set BS_DEFPUSHBUTTON for any button if we don't have
742 // focus at all any more
743 if ( !wxTheApp->IsActive() )
744 return;
cd0b1709 745
6c20e8f8
VZ
746 wxWindow * const tlw = wxGetTopLevelParent(btn);
747 wxCHECK_RET( tlw, _T("button without top level window?") );
cef55d64 748
6c20e8f8 749 ::SendMessage(GetHwndOf(tlw), DM_SETDEFID, btn->GetId(), 0L);
cef55d64
VZ
750
751 // sending DM_SETDEFID also changes the button style to
752 // BS_DEFPUSHBUTTON so there is nothing more to do
5d1d2d46
VZ
753 }
754
7fb1b2b4
VZ
755 // then also change the style as needed
756 long style = ::GetWindowLong(GetHwndOf(btn), GWL_STYLE);
757 if ( !(style & BS_DEFPUSHBUTTON) == on )
be4017f8 758 {
7fb1b2b4
VZ
759 // don't do it with the owner drawn buttons because it will
760 // reset BS_OWNERDRAW style bit too (as BS_OWNERDRAW &
761 // BS_DEFPUSHBUTTON != 0)!
036da5e3
VZ
762 if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
763 {
7fb1b2b4
VZ
764 ::SendMessage(GetHwndOf(btn), BM_SETSTYLE,
765 on ? style | BS_DEFPUSHBUTTON
766 : style & ~BS_DEFPUSHBUTTON,
767 1L /* redraw */);
036da5e3 768 }
7fb1b2b4 769 else // owner drawn
036da5e3 770 {
7fb1b2b4
VZ
771 // redraw the button - it will notice itself that it's
772 // [not] the default one [any longer]
773 btn->Refresh();
036da5e3 774 }
be4017f8 775 }
7fb1b2b4 776 //else: already has correct style
2bda0e17
KB
777}
778
edccf428
VZ
779// ----------------------------------------------------------------------------
780// helpers
781// ----------------------------------------------------------------------------
782
783bool wxButton::SendClickEvent()
2bda0e17 784{
edccf428
VZ
785 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
786 event.SetEventObject(this);
787
788 return ProcessCommand(event);
2bda0e17
KB
789}
790
edccf428 791void wxButton::Command(wxCommandEvent & event)
2bda0e17 792{
edccf428 793 ProcessCommand(event);
2bda0e17
KB
794}
795
edccf428
VZ
796// ----------------------------------------------------------------------------
797// event/message handlers
798// ----------------------------------------------------------------------------
2bda0e17 799
33ac7e6f 800bool wxButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
edccf428 801{
fcf90ee1 802 bool processed = false;
57c0af52 803 switch ( param )
edccf428 804 {
5aab763c
RD
805 // NOTE: Apparently older versions (NT 4?) of the common controls send
806 // BN_DOUBLECLICKED but not a second BN_CLICKED for owner-drawn
87b6002d 807 // buttons, so in order to send two EVT_BUTTON events we should
5aab763c
RD
808 // catch both types. Currently (Feb 2003) up-to-date versions of
809 // win98, win2k and winXP all send two BN_CLICKED messages for
810 // all button types, so we don't catch BN_DOUBLECLICKED anymore
811 // in order to not get 3 EVT_BUTTON events. If this is a problem
812 // then we need to figure out which version of the comctl32 changed
813 // this behaviour and test for it.
814
a95e38c0
VZ
815 case 1: // message came from an accelerator
816 case BN_CLICKED: // normal buttons send this
57c0af52
VZ
817 processed = SendClickEvent();
818 break;
edccf428 819 }
2bda0e17 820
edccf428 821 return processed;
2bda0e17
KB
822}
823
c140b7e7 824WXLRESULT wxButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
678cd6de 825{
87b6002d 826 // when we receive focus, we want to temporarily become the default button in
036da5e3
VZ
827 // our parent panel so that pressing "Enter" would activate us -- and when
828 // losing it we should restore the previous default button as well
be4017f8 829 if ( nMsg == WM_SETFOCUS )
678cd6de 830 {
036da5e3 831 SetTmpDefault();
be4017f8 832
036da5e3
VZ
833 // let the default processing take place too
834 }
835 else if ( nMsg == WM_KILLFOCUS )
836 {
837 UnsetTmpDefault();
678cd6de 838 }
a95e38c0
VZ
839 else if ( nMsg == WM_LBUTTONDBLCLK )
840 {
f6bcfd97
BP
841 // emulate a click event to force an owner-drawn button to change its
842 // appearance - without this, it won't do it
843 (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN, wParam, lParam);
844
036da5e3 845 // and continue with processing the message normally as well
a95e38c0 846 }
4e9da8b7
RD
847#if wxUSE_UXTHEME
848 else if ( nMsg == WM_THEMECHANGED )
849 {
850 // need to recalculate the best size here
851 // as the theme size might have changed
852 InvalidateBestSize();
853 }
854 else if ( wxUxThemeEngine::GetIfActive() )
855 {
856 // we need to Refresh() if mouse has entered or left window
857 // so we can update the hot tracking state
858 // must use m_mouseInWindow here instead of IsMouseInWindow()
859 // since we need to know the first time the mouse enters the window
860 // and IsMouseInWindow() would return true in this case
861 if ( ( nMsg == WM_MOUSEMOVE && !m_mouseInWindow ) ||
862 nMsg == WM_MOUSELEAVE )
863 {
864 Refresh();
865 }
866 }
867#endif // wxUSE_UXTHEME
678cd6de
VZ
868
869 // let the base class do all real processing
870 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
871}
cd0b1709 872
233f10bf
VZ
873// ----------------------------------------------------------------------------
874// button images
875// ----------------------------------------------------------------------------
876
877wxBitmap wxButton::DoGetBitmap(State which) const
878{
879 return m_imageData ? m_imageData->GetBitmap(which) : wxBitmap();
880}
881
882void wxButton::DoSetBitmap(const wxBitmap& bitmap, State which)
883{
884 // allocate the image data when the first bitmap is set
885 if ( !m_imageData )
886 {
887#if wxUSE_UXTHEME
888 if ( wxUxThemeEngine::GetIfActive() )
8f4745fe 889 {
233f10bf 890 m_imageData = new wxXPButtonImageData(this, bitmap.GetSize());
8f4745fe 891 }
233f10bf
VZ
892 else
893#endif // wxUSE_UXTHEME
8f4745fe
VZ
894 {
895 m_imageData = new wxODButtonImageData(this);
896 MakeOwnerDrawn();
897 }
233f10bf
VZ
898
899 // if a bitmap was assigned to the bitmap, its best size must be
900 // changed to account for it
901 InvalidateBestSize();
902 }
903
904 m_imageData->SetBitmap(bitmap, which);
905}
906
907void wxButton::DoSetBitmapMargins(wxCoord x, wxCoord y)
908{
909 wxCHECK_RET( m_imageData, "SetBitmap() must be called first" );
910
911 m_imageData->SetBitmapMargins(x, y);
912}
913
914void wxButton::DoSetBitmapPosition(wxDirection dir)
915{
916 wxCHECK_RET( m_imageData, "SetBitmap() must be called first" );
917
918 m_imageData->SetBitmapPosition(dir);
919}
920
cd0b1709
VZ
921// ----------------------------------------------------------------------------
922// owner-drawn buttons support
923// ----------------------------------------------------------------------------
924
cd0b1709 925// drawing helpers
d8c89c48
VZ
926namespace
927{
cd0b1709 928
d8c89c48
VZ
929void DrawButtonText(HDC hdc,
930 RECT *pRect,
931 const wxString& text,
932 COLORREF col,
933 int flags)
cd0b1709 934{
d8c89c48
VZ
935 wxTextColoursChanger changeFg(hdc, col, CLR_INVALID);
936 wxBkModeChanger changeBkMode(hdc, wxBRUSHSTYLE_TRANSPARENT);
cd0b1709 937
f915b92e
VZ
938 // center text horizontally in any case
939 flags |= DT_CENTER;
940
cbbb6724
VZ
941 if ( text.find(_T('\n')) != wxString::npos )
942 {
943 // draw multiline label
944
945 // first we need to compute its bounding rect
946 RECT rc;
947 ::CopyRect(&rc, pRect);
e0a050e3
VS
948 ::DrawText(hdc, text.wx_str(), text.length(), &rc,
949 DT_CENTER | DT_CALCRECT);
cbbb6724
VZ
950
951 // now center this rect inside the entire button area
952 const LONG w = rc.right - rc.left;
953 const LONG h = rc.bottom - rc.top;
954 rc.left = (pRect->right - pRect->left)/2 - w/2;
955 rc.right = rc.left+w;
956 rc.top = (pRect->bottom - pRect->top)/2 - h/2;
957 rc.bottom = rc.top+h;
958
f915b92e 959 ::DrawText(hdc, text.wx_str(), text.length(), &rc, flags);
cbbb6724
VZ
960 }
961 else // single line label
962 {
f915b92e
VZ
963 // centre text vertically too (notice that we must have DT_SINGLELINE
964 // for DT_VCENTER to work)
e0a050e3 965 ::DrawText(hdc, text.wx_str(), text.length(), pRect,
f915b92e 966 flags | DT_SINGLELINE | DT_VCENTER);
cbbb6724 967 }
cd0b1709
VZ
968}
969
d8c89c48 970void DrawRect(HDC hdc, const RECT& r)
cd0b1709 971{
4676948b
JS
972 wxDrawLine(hdc, r.left, r.top, r.right, r.top);
973 wxDrawLine(hdc, r.right, r.top, r.right, r.bottom);
974 wxDrawLine(hdc, r.right, r.bottom, r.left, r.bottom);
975 wxDrawLine(hdc, r.left, r.bottom, r.left, r.top);
cd0b1709
VZ
976}
977
cd0b1709
VZ
978/*
979 The button frame looks like this normally:
980
981 WWWWWWWWWWWWWWWWWWB
16162a64
GRG
982 WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
983 WH GB H = light grey (LIGHT)
984 WH GB G = dark grey (SHADOW)
985 WH GB B = black (DKSHADOW)
986 WH GB
cd0b1709
VZ
987 WGGGGGGGGGGGGGGGGGB
988 BBBBBBBBBBBBBBBBBBB
989
990 When the button is selected, the button becomes like this (the total button
991 size doesn't change):
992
993 BBBBBBBBBBBBBBBBBBB
994 BWWWWWWWWWWWWWWWWBB
16162a64
GRG
995 BWHHHHHHHHHHHHHHGBB
996 BWH GBB
997 BWH GBB
cd0b1709
VZ
998 BWGGGGGGGGGGGGGGGBB
999 BBBBBBBBBBBBBBBBBBB
1000 BBBBBBBBBBBBBBBBBBB
1001
1002 When the button is pushed (while selected) it is like:
1003
1004 BBBBBBBBBBBBBBBBBBB
1005 BGGGGGGGGGGGGGGGGGB
1006 BG GB
1007 BG GB
1008 BG GB
16162a64 1009 BG GB
cd0b1709
VZ
1010 BGGGGGGGGGGGGGGGGGB
1011 BBBBBBBBBBBBBBBBBBB
1012*/
8f4745fe 1013void DrawButtonFrame(HDC hdc, RECT& rectBtn,
d8c89c48 1014 bool selected, bool pushed)
cd0b1709
VZ
1015{
1016 RECT r;
1017 CopyRect(&r, &rectBtn);
1018
9c956c0a
VZ
1019 AutoHPEN hpenBlack(GetSysColor(COLOR_3DDKSHADOW)),
1020 hpenGrey(GetSysColor(COLOR_3DSHADOW)),
1021 hpenLightGr(GetSysColor(COLOR_3DLIGHT)),
1022 hpenWhite(GetSysColor(COLOR_3DHILIGHT));
cd0b1709 1023
9c956c0a 1024 SelectInHDC selectPen(hdc, hpenBlack);
cd0b1709
VZ
1025
1026 r.right--;
1027 r.bottom--;
1028
1029 if ( pushed )
1030 {
1031 DrawRect(hdc, r);
1032
1033 (void)SelectObject(hdc, hpenGrey);
fcf90ee1 1034 ::InflateRect(&r, -1, -1);
cd0b1709
VZ
1035
1036 DrawRect(hdc, r);
1037 }
1038 else // !pushed
1039 {
1040 if ( selected )
1041 {
1042 DrawRect(hdc, r);
1043
fcf90ee1 1044 ::InflateRect(&r, -1, -1);
cd0b1709
VZ
1045 }
1046
4676948b
JS
1047 wxDrawLine(hdc, r.left, r.bottom, r.right, r.bottom);
1048 wxDrawLine(hdc, r.right, r.bottom, r.right, r.top - 1);
cd0b1709
VZ
1049
1050 (void)SelectObject(hdc, hpenWhite);
4676948b
JS
1051 wxDrawLine(hdc, r.left, r.bottom - 1, r.left, r.top);
1052 wxDrawLine(hdc, r.left, r.top, r.right, r.top);
cd0b1709 1053
16162a64 1054 (void)SelectObject(hdc, hpenLightGr);
4676948b
JS
1055 wxDrawLine(hdc, r.left + 1, r.bottom - 2, r.left + 1, r.top + 1);
1056 wxDrawLine(hdc, r.left + 1, r.top + 1, r.right - 1, r.top + 1);
16162a64 1057
cd0b1709 1058 (void)SelectObject(hdc, hpenGrey);
4676948b
JS
1059 wxDrawLine(hdc, r.left + 1, r.bottom - 1, r.right - 1, r.bottom - 1);
1060 wxDrawLine(hdc, r.right - 1, r.bottom - 1, r.right - 1, r.top);
cd0b1709 1061 }
8f4745fe
VZ
1062
1063 InflateRect(&rectBtn, -OD_BUTTON_MARGIN, -OD_BUTTON_MARGIN);
cd0b1709
VZ
1064}
1065
4e9da8b7 1066#if wxUSE_UXTHEME
8f4745fe 1067void MSWDrawXPBackground(wxButton *button, HDC hdc, RECT& rectBtn, UINT state)
cd0b1709 1068{
4e9da8b7
RD
1069 wxUxThemeHandle theme(button, L"BUTTON");
1070 int iState;
1071
1072 if ( state & ODS_SELECTED )
1073 {
1074 iState = PBS_PRESSED;
1075 }
1076 else if ( button->HasCapture() || button->IsMouseInWindow() )
1077 {
1078 iState = PBS_HOT;
1079 }
1080 else if ( state & ODS_FOCUS )
1081 {
1082 iState = PBS_DEFAULTED;
1083 }
1084 else if ( state & ODS_DISABLED )
1085 {
1086 iState = PBS_DISABLED;
1087 }
1088 else
1089 {
1090 iState = PBS_NORMAL;
1091 }
cd0b1709 1092
4e9da8b7
RD
1093 // draw parent background if needed
1094 if ( wxUxThemeEngine::Get()->IsThemeBackgroundPartiallyTransparent(theme,
1095 BP_PUSHBUTTON,
1096 iState) )
1097 {
1098 wxUxThemeEngine::Get()->DrawThemeParentBackground(GetHwndOf(button), hdc, &rectBtn);
1099 }
1100
1101 // draw background
1102 wxUxThemeEngine::Get()->DrawThemeBackground(theme, hdc, BP_PUSHBUTTON, iState,
1103 &rectBtn, NULL);
1104
1105 // calculate content area margins
1106 MARGINS margins;
1107 wxUxThemeEngine::Get()->GetThemeMargins(theme, hdc, BP_PUSHBUTTON, iState,
1108 TMT_CONTENTMARGINS, &rectBtn, &margins);
8f4745fe 1109 ::InflateRect(&rectBtn, -margins.cxLeftWidth, -margins.cyTopHeight);
4e9da8b7
RD
1110
1111 if ( button->UseBgCol() )
1112 {
1113 COLORREF colBg = wxColourToRGB(button->GetBackgroundColour());
9c956c0a 1114 AutoHBRUSH hbrushBackground(colBg);
4e9da8b7
RD
1115
1116 // don't overwrite the focus rect
8f4745fe
VZ
1117 RECT rectClient;
1118 ::CopyRect(&rectClient, &rectBtn);
4e9da8b7
RD
1119 ::InflateRect(&rectClient, -1, -1);
1120 FillRect(hdc, &rectClient, hbrushBackground);
4e9da8b7
RD
1121 }
1122}
1123#endif // wxUSE_UXTHEME
1124
d8c89c48
VZ
1125} // anonymous namespace
1126
1127// ----------------------------------------------------------------------------
1128// owner drawn buttons support
1129// ----------------------------------------------------------------------------
1130
1131void wxButton::MakeOwnerDrawn()
1132{
1133 long style = GetWindowLong(GetHwnd(), GWL_STYLE);
1134 if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
1135 {
1136 // make it so
1137 style |= BS_OWNERDRAW;
1138 SetWindowLong(GetHwnd(), GWL_STYLE, style);
1139 }
1140}
1141
1142bool wxButton::SetBackgroundColour(const wxColour &colour)
1143{
1144 if ( !wxControl::SetBackgroundColour(colour) )
1145 {
1146 // nothing to do
1147 return false;
1148 }
1149
1150 MakeOwnerDrawn();
1151
1152 Refresh();
1153
1154 return true;
1155}
1156
1157bool wxButton::SetForegroundColour(const wxColour &colour)
1158{
1159 if ( !wxControl::SetForegroundColour(colour) )
1160 {
1161 // nothing to do
1162 return false;
1163 }
1164
1165 MakeOwnerDrawn();
1166
1167 Refresh();
1168
1169 return true;
1170}
1171
4e9da8b7
RD
1172bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis)
1173{
1174 LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)wxdis;
cd0b1709 1175 HDC hdc = lpDIS->hDC;
8f4745fe 1176
cd0b1709 1177 UINT state = lpDIS->itemState;
8f4745fe
VZ
1178 bool pushed = (SendMessage(GetHwnd(), BM_GETSTATE, 0, 0) & BST_PUSHED) != 0;
1179
4e9da8b7
RD
1180 RECT rectBtn;
1181 CopyRect(&rectBtn, &lpDIS->rcItem);
cd0b1709 1182
8f4745fe 1183 // draw the button background
4e9da8b7
RD
1184#if wxUSE_UXTHEME
1185 if ( wxUxThemeEngine::GetIfActive() )
1186 {
8f4745fe 1187 MSWDrawXPBackground(this, hdc, rectBtn, state);
4e9da8b7
RD
1188 }
1189 else
1190#endif // wxUSE_UXTHEME
1191 {
1192 COLORREF colBg = wxColourToRGB(GetBackgroundColour());
cd0b1709 1193
4e9da8b7 1194 // first, draw the background
9c956c0a 1195 AutoHBRUSH hbrushBackground(colBg);
4e9da8b7 1196 FillRect(hdc, &rectBtn, hbrushBackground);
cd0b1709 1197
4e9da8b7
RD
1198 // draw the border for the current state
1199 bool selected = (state & ODS_SELECTED) != 0;
1200 if ( !selected )
cd0b1709 1201 {
8f4745fe
VZ
1202 wxTopLevelWindow *
1203 tlw = wxDynamicCast(wxGetTopLevelParent(this), wxTopLevelWindow);
6c20e8f8 1204 if ( tlw )
4e9da8b7 1205 {
6c20e8f8 1206 selected = tlw->GetDefaultItem() == this;
4e9da8b7 1207 }
cd0b1709 1208 }
cd0b1709 1209
4e9da8b7 1210 DrawButtonFrame(hdc, rectBtn, selected, pushed);
8f4745fe 1211 }
cd0b1709 1212
8f4745fe
VZ
1213 // draw the focus rectangle if we need it
1214 if ( (state & ODS_FOCUS) && !(state & ODS_NOFOCUSRECT) )
1215 {
1216 DrawFocusRect(hdc, &rectBtn);
cd0b1709 1217
8f4745fe
VZ
1218#if wxUSE_UXTHEME
1219 if ( !wxUxThemeEngine::GetIfActive() )
1220#endif // wxUSE_UXTHEME
1221 {
1222 if ( pushed )
1223 {
1224 // the label is shifted by 1 pixel to create "pushed" effect
1225 OffsetRect(&rectBtn, 1, 1);
1226 }
4e9da8b7 1227 }
8f4745fe
VZ
1228 }
1229
cd0b1709 1230
8f4745fe
VZ
1231 // draw the image, if any
1232 if ( m_imageData )
1233 {
1234 wxBitmap bmp = m_imageData->GetBitmap(State_Normal);
1235 const wxSize sizeBmp = bmp.GetSize();
1236 const wxSize margin = m_imageData->GetBitmapMargins();
1237 const wxSize sizeBmpWithMargins(sizeBmp + 2*margin);
1238 wxRect rectButton(wxRectFromRECT(rectBtn));
1239
1240 // for simplicity, we start with centred rectangle and then move it to
1241 // the appropriate edge
1242 wxRect rectBitmap = wxRect(sizeBmp).CentreIn(rectButton);
1243 switch ( m_imageData->GetBitmapPosition() )
4e9da8b7 1244 {
8f4745fe
VZ
1245 default:
1246 wxFAIL_MSG( "invalid direction" );
1247 // fall through
1248
1249 case wxLEFT:
1250 rectBitmap.x = rectButton.x + margin.x;
1251 rectButton.x += sizeBmpWithMargins.x;
1252 rectButton.width -= sizeBmpWithMargins.x;
1253 break;
1254
1255 case wxRIGHT:
1256 rectBitmap.x = rectButton.GetRight() - sizeBmp.x - margin.x;
1257 rectButton.width -= sizeBmpWithMargins.x;
1258 break;
1259
1260 case wxTOP:
1261 rectBitmap.y = rectButton.y + margin.y;
1262 rectButton.y += sizeBmpWithMargins.y;
1263 rectButton.height -= sizeBmpWithMargins.y;
1264 break;
1265
1266 case wxBOTTOM:
1267 rectBitmap.y = rectButton.GetBottom() - sizeBmp.y - margin.y;
1268 rectButton.height -= sizeBmpWithMargins.y;
1269 break;
4e9da8b7 1270 }
8f4745fe
VZ
1271
1272 wxDCTemp dst((WXHDC)hdc);
1273 dst.DrawBitmap(bmp, rectBitmap.GetPosition(), true);
1274
1275 wxCopyRectToRECT(rectButton, rectBtn);
9750fc42
VZ
1276 }
1277
8f4745fe
VZ
1278
1279 // finally draw the label
f915b92e
VZ
1280 COLORREF colFg = state & ODS_DISABLED
1281 ? ::GetSysColor(COLOR_GRAYTEXT)
1282 : wxColourToRGB(GetForegroundColour());
1283
1284 // notice that DT_HIDEPREFIX doesn't work on old (pre-Windows 2000) systems
1285 // but by happy coincidence ODS_NOACCEL is not used under them neither so
1286 // DT_HIDEPREFIX should never be used there
1287 DrawButtonText(hdc, &rectBtn, GetLabel(), colFg,
1288 state & ODS_NOACCEL ? DT_HIDEPREFIX : 0);
cd0b1709 1289
fcf90ee1 1290 return true;
cd0b1709
VZ
1291}
1292
1e6feb95 1293#endif // wxUSE_BUTTON
4af4dec6 1294