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