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