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