]> git.saurik.com Git - wxWidgets.git/blame - src/msw/button.cpp
use custom class which doesn't redraw everything on resize instead of SysTabCtl32...
[wxWidgets.git] / src / msw / button.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
cd0b1709 2// Name: 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
14f355c2 20#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
edccf428 21 #pragma implementation "button.h"
2bda0e17
KB
22#endif
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
edccf428 28 #pragma hdrstop
2bda0e17
KB
29#endif
30
1e6feb95
VZ
31#if wxUSE_BUTTON
32
2bda0e17 33#ifndef WX_PRECOMP
bac409a0 34 #include "wx/app.h"
edccf428
VZ
35 #include "wx/button.h"
36 #include "wx/brush.h"
4e938f5b 37 #include "wx/panel.h"
8a4df159 38 #include "wx/bmpbuttn.h"
fb39c7ec
RR
39 #include "wx/settings.h"
40 #include "wx/dcscreen.h"
2bda0e17
KB
41#endif
42
5f7bcb48 43#include "wx/stockitem.h"
2bda0e17
KB
44#include "wx/msw/private.h"
45
edccf428
VZ
46// ----------------------------------------------------------------------------
47// macros
48// ----------------------------------------------------------------------------
49
51596bcb 50#if wxUSE_EXTENDED_RTTI
067e9be6 51
f3291a82
SC
52WX_DEFINE_FLAGS( wxButtonStyle )
53
3ff066a4 54wxBEGIN_FLAGS( wxButtonStyle )
f3291a82
SC
55 // new style border flags, we put them first to
56 // use them for streaming out
3ff066a4
SC
57 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
58 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
59 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
60 wxFLAGS_MEMBER(wxBORDER_RAISED)
61 wxFLAGS_MEMBER(wxBORDER_STATIC)
62 wxFLAGS_MEMBER(wxBORDER_NONE)
fcf90ee1 63
f3291a82 64 // old style border flags
3ff066a4
SC
65 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
66 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
67 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
68 wxFLAGS_MEMBER(wxRAISED_BORDER)
69 wxFLAGS_MEMBER(wxSTATIC_BORDER)
cb0afb26 70 wxFLAGS_MEMBER(wxBORDER)
f3291a82 71
bc9fb572 72 // standard window styles
3ff066a4
SC
73 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
74 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
75 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
76 wxFLAGS_MEMBER(wxWANTS_CHARS)
cb0afb26 77 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
3ff066a4
SC
78 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
79 wxFLAGS_MEMBER(wxVSCROLL)
80 wxFLAGS_MEMBER(wxHSCROLL)
81
82 wxFLAGS_MEMBER(wxBU_LEFT)
83 wxFLAGS_MEMBER(wxBU_RIGHT)
84 wxFLAGS_MEMBER(wxBU_TOP)
85 wxFLAGS_MEMBER(wxBU_BOTTOM)
86 wxFLAGS_MEMBER(wxBU_EXACTFIT)
87wxEND_FLAGS( wxButtonStyle )
067e9be6 88
51596bcb
SC
89IMPLEMENT_DYNAMIC_CLASS_XTI(wxButton, wxControl,"wx/button.h")
90
3ff066a4 91wxBEGIN_PROPERTIES_TABLE(wxButton)
fcf90ee1 92 wxEVENT_PROPERTY( Click , wxEVT_COMMAND_BUTTON_CLICKED , wxCommandEvent)
067e9be6 93
fcf90ee1
WS
94 wxPROPERTY( Font , wxFont , SetFont , GetFont , EMPTY_MACROVALUE, 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
95 wxPROPERTY( Label, wxString , SetLabel, GetLabel, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
067e9be6 96
af498247 97 wxPROPERTY_FLAGS( WindowStyle , wxButtonStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
51741307 98
3ff066a4 99wxEND_PROPERTIES_TABLE()
51596bcb 100
3ff066a4
SC
101wxBEGIN_HANDLERS_TABLE(wxButton)
102wxEND_HANDLERS_TABLE()
51596bcb 103
3ff066a4 104wxCONSTRUCTOR_6( wxButton , wxWindow* , Parent , wxWindowID , Id , wxString , Label , wxPoint , Position , wxSize , Size , long , WindowStyle )
51596bcb
SC
105
106
107#else
cd0b1709 108IMPLEMENT_DYNAMIC_CLASS(wxButton, wxControl)
51596bcb 109#endif
2bda0e17 110
edccf428
VZ
111// this macro tries to adjust the default button height to a reasonable value
112// using the char height as the base
1c4a764c 113#define BUTTON_HEIGHT_FROM_CHAR_HEIGHT(cy) (11*EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy)/10)
2bda0e17 114
edccf428
VZ
115// ============================================================================
116// implementation
117// ============================================================================
118
119// ----------------------------------------------------------------------------
120// creation/destruction
121// ----------------------------------------------------------------------------
122
123bool wxButton::Create(wxWindow *parent,
124 wxWindowID id,
5f7bcb48 125 const wxString& lbl,
edccf428
VZ
126 const wxPoint& pos,
127 const wxSize& size,
128 long style,
129 const wxValidator& validator,
130 const wxString& name)
2bda0e17 131{
5f7bcb48
VS
132 wxString label(lbl);
133 if (label.empty() && wxIsStockID(id))
134 label = wxGetStockLabel(id);
135
5b2f31eb 136 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
fcf90ee1 137 return false;
edccf428 138
8292017c
VZ
139 WXDWORD exstyle;
140 WXDWORD msStyle = MSWGetStyle(style, &exstyle);
141
142#ifdef __WIN32__
143 // if the label contains several lines we must explicitly tell the button
144 // about it or it wouldn't draw it correctly ("\n"s would just appear as
145 // black boxes)
146 //
147 // NB: we do it here and not in MSWGetStyle() because we need the label
148 // value and m_label is not set yet when MSWGetStyle() is called;
149 // besides changing BS_MULTILINE during run-time is pointless anyhow
150 if ( label.find(_T('\n')) != wxString::npos )
151 {
152 msStyle |= BS_MULTILINE;
153 }
154#endif // __WIN32__
155
156 return MSWCreateControl(_T("BUTTON"), msStyle, pos, size, label, exstyle);
5b2f31eb
VZ
157}
158
159wxButton::~wxButton()
160{
161}
edccf428 162
5b2f31eb
VZ
163// ----------------------------------------------------------------------------
164// flags
165// ----------------------------------------------------------------------------
cd0b1709 166
5b2f31eb
VZ
167WXDWORD wxButton::MSWGetStyle(long style, WXDWORD *exstyle) const
168{
169 // buttons never have an external border, they draw their own one
170 WXDWORD msStyle = wxControl::MSWGetStyle
171 (
172 (style & ~wxBORDER_MASK) | wxBORDER_NONE, exstyle
173 );
f6bcfd97 174
5b2f31eb
VZ
175 // we must use WS_CLIPSIBLINGS with the buttons or they would draw over
176 // each other in any resizeable dialog which has more than one button in
177 // the bottom
178 msStyle |= WS_CLIPSIBLINGS;
b0766406 179
f6bcfd97 180#ifdef __WIN32__
5b2f31eb
VZ
181 // don't use "else if" here: weird as it is, but you may combine wxBU_LEFT
182 // and wxBU_RIGHT to get BS_CENTER!
183 if ( style & wxBU_LEFT )
f6bcfd97 184 msStyle |= BS_LEFT;
5b2f31eb 185 if ( style & wxBU_RIGHT )
f6bcfd97 186 msStyle |= BS_RIGHT;
5b2f31eb 187 if ( style & wxBU_TOP )
f6bcfd97 188 msStyle |= BS_TOP;
5b2f31eb 189 if ( style & wxBU_BOTTOM )
f6bcfd97 190 msStyle |= BS_BOTTOM;
8cc4850c 191#ifndef __WXWINCE__
8a094d7b
JS
192 // flat 2d buttons
193 if ( style & wxNO_BORDER )
194 msStyle |= BS_FLAT;
8cc4850c 195#endif // __WXWINCE__
5b2f31eb 196#endif // __WIN32__
edccf428 197
5b2f31eb 198 return msStyle;
2bda0e17
KB
199}
200
edccf428
VZ
201// ----------------------------------------------------------------------------
202// size management including autosizing
203// ----------------------------------------------------------------------------
204
f68586e5 205wxSize wxButton::DoGetBestSize() const
2bda0e17 206{
4438caf4 207 int wBtn;
1e023926 208 GetTextExtent(wxGetWindowText(GetHWND()), &wBtn, NULL);
edccf428 209
4438caf4 210 int wChar, hChar;
7a5e53ab 211 wxGetCharSize(GetHWND(), &wChar, &hChar, GetFont());
4438caf4 212
1e023926 213 // add a margin -- the button is wider than just its label
4438caf4
VZ
214 wBtn += 3*wChar;
215
216 // the button height is proportional to the height of the font used
217 int hBtn = BUTTON_HEIGHT_FROM_CHAR_HEIGHT(hChar);
edccf428 218
1e023926
VZ
219 // all buttons have at least the standard size unless the user explicitly
220 // wants them to be of smaller size and used wxBU_EXACTFIT style when
221 // creating the button
222 if ( !HasFlag(wxBU_EXACTFIT) )
188b9f2e
RD
223 {
224 wxSize sz = GetDefaultSize();
1e023926
VZ
225 if (wBtn > sz.x)
226 sz.x = wBtn;
227 if (hBtn > sz.y)
228 sz.y = hBtn;
229
188b9f2e
RD
230 return sz;
231 }
a63f2bec 232
1e023926 233 return wxSize(wBtn, hBtn);
2bda0e17
KB
234}
235
e1f36ff8 236/* static */
1e6feb95 237wxSize wxButtonBase::GetDefaultSize()
e1f36ff8 238{
8c3c31d4 239 static wxSize s_sizeBtn;
e1f36ff8 240
8c3c31d4
VZ
241 if ( s_sizeBtn.x == 0 )
242 {
243 wxScreenDC dc;
a756f210 244 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
8c3c31d4
VZ
245
246 // the size of a standard button in the dialog units is 50x14,
247 // translate this to pixels
248 // NB1: the multipliers come from the Windows convention
249 // NB2: the extra +1/+2 were needed to get the size be the same as the
250 // size of the buttons in the standard dialog - I don't know how
251 // this happens, but on my system this size is 75x23 in pixels and
252 // 23*8 isn't even divisible by 14... Would be nice to understand
253 // why these constants are needed though!
254 s_sizeBtn.x = (50 * (dc.GetCharWidth() + 1))/4;
255 s_sizeBtn.y = ((14 * dc.GetCharHeight()) + 2)/8;
256 }
e1f36ff8 257
8c3c31d4 258 return s_sizeBtn;
e1f36ff8
VZ
259}
260
4438caf4 261// ----------------------------------------------------------------------------
036da5e3 262// default button handling
4438caf4
VZ
263// ----------------------------------------------------------------------------
264
d78f09e2
VZ
265/*
266 "Everything you ever wanted to know about the default buttons" or "Why do we
267 have to do all this?"
268
269 In MSW the default button should be activated when the user presses Enter
270 and the current control doesn't process Enter itself somehow. This is
271 handled by ::DefWindowProc() (or maybe ::DefDialogProc()) using DM_SETDEFID
272 Another aspect of "defaultness" is that the default button has different
273 appearance: this is due to BS_DEFPUSHBUTTON style which is completely
7fb1b2b4
VZ
274 separate from DM_SETDEFID stuff (!). Also note that BS_DEFPUSHBUTTON should
275 be unset if our parent window is not active so it should be unset whenever
276 we lose activation and set back when we regain it.
d78f09e2
VZ
277
278 Final complication is that when a button is active, it should be the default
279 one, i.e. pressing Enter on a button always activates it and not another
280 one.
281
282 We handle this by maintaining a permanent and a temporary default items in
283 wxControlContainer (both may be NULL). When a button becomes the current
284 control (i.e. gets focus) it sets itself as the temporary default which
285 ensures that it has the right appearance and that Enter will be redirected
286 to it. When the button loses focus, it unsets the temporary default and so
287 the default item will be the permanent default -- that is the default button
288 if any had been set or none otherwise, which is just what we want.
289
7fb1b2b4
VZ
290 NB: all this is quite complicated by now and the worst is that normally
291 it shouldn't be necessary at all as for the normal Windows programs
292 DefWindowProc() and IsDialogMessage() take care of all this
77ffb593 293 automatically -- however in wxWidgets programs this doesn't work for
7fb1b2b4
VZ
294 nested hierarchies (i.e. a notebook inside a notebook) for unknown
295 reason and so we have to reproduce all this code ourselves. It would be
296 very nice if we could avoid doing it.
d78f09e2
VZ
297 */
298
036da5e3 299// set this button as the (permanently) default one in its panel
edccf428 300void wxButton::SetDefault()
2bda0e17 301{
edccf428 302 wxWindow *parent = GetParent();
2bda0e17 303
036da5e3
VZ
304 wxCHECK_RET( parent, _T("button without parent?") );
305
77ffb593 306 // set this one as the default button both for wxWidgets ...
036da5e3 307 wxWindow *winOldDefault = parent->SetDefaultItem(this);
036da5e3 308
7fb1b2b4 309 // ... and Windows
fcf90ee1
WS
310 SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), false);
311 SetDefaultStyle(this, true);
036da5e3
VZ
312}
313
7fb1b2b4 314// set this button as being currently default
036da5e3
VZ
315void wxButton::SetTmpDefault()
316{
317 wxWindow *parent = GetParent();
318
319 wxCHECK_RET( parent, _T("button without parent?") );
320
321 wxWindow *winOldDefault = parent->GetDefaultItem();
322 parent->SetTmpDefaultItem(this);
7fb1b2b4 323
fcf90ee1
WS
324 SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), false);
325 SetDefaultStyle(this, true);
036da5e3
VZ
326}
327
7fb1b2b4 328// unset this button as currently default, it may still stay permanent default
036da5e3
VZ
329void wxButton::UnsetTmpDefault()
330{
331 wxWindow *parent = GetParent();
332
333 wxCHECK_RET( parent, _T("button without parent?") );
334
335 parent->SetTmpDefaultItem(NULL);
336
337 wxWindow *winOldDefault = parent->GetDefaultItem();
7fb1b2b4 338
fcf90ee1
WS
339 SetDefaultStyle(this, false);
340 SetDefaultStyle(wxDynamicCast(winOldDefault, wxButton), true);
036da5e3 341}
8ed57d93 342
036da5e3
VZ
343/* static */
344void
7fb1b2b4 345wxButton::SetDefaultStyle(wxButton *btn, bool on)
036da5e3 346{
7fb1b2b4
VZ
347 // we may be called with NULL pointer -- simpler to do the check here than
348 // in the caller which does wxDynamicCast()
349 if ( !btn )
350 return;
351
352 // first, let DefDlgProc() know about the new default button
353 if ( on )
5d1d2d46 354 {
7fb1b2b4
VZ
355 // we shouldn't set BS_DEFPUSHBUTTON for any button if we don't have
356 // focus at all any more
357 if ( !wxTheApp->IsActive() )
358 return;
cd0b1709 359
7fb1b2b4
VZ
360 // look for a panel-like window
361 wxWindow *win = btn->GetParent();
362 while ( win && !win->HasFlag(wxTAB_TRAVERSAL) )
363 win = win->GetParent();
364
365 if ( win )
be4017f8 366 {
7fb1b2b4
VZ
367 ::SendMessage(GetHwndOf(win), DM_SETDEFID, btn->GetId(), 0L);
368
369 // sending DM_SETDEFID also changes the button style to
370 // BS_DEFPUSHBUTTON so there is nothing more to do
be4017f8 371 }
5d1d2d46
VZ
372 }
373
7fb1b2b4
VZ
374 // then also change the style as needed
375 long style = ::GetWindowLong(GetHwndOf(btn), GWL_STYLE);
376 if ( !(style & BS_DEFPUSHBUTTON) == on )
be4017f8 377 {
7fb1b2b4
VZ
378 // don't do it with the owner drawn buttons because it will
379 // reset BS_OWNERDRAW style bit too (as BS_OWNERDRAW &
380 // BS_DEFPUSHBUTTON != 0)!
036da5e3
VZ
381 if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
382 {
7fb1b2b4
VZ
383 ::SendMessage(GetHwndOf(btn), BM_SETSTYLE,
384 on ? style | BS_DEFPUSHBUTTON
385 : style & ~BS_DEFPUSHBUTTON,
386 1L /* redraw */);
036da5e3 387 }
7fb1b2b4 388 else // owner drawn
036da5e3 389 {
7fb1b2b4
VZ
390 // redraw the button - it will notice itself that it's
391 // [not] the default one [any longer]
392 btn->Refresh();
036da5e3 393 }
be4017f8 394 }
7fb1b2b4 395 //else: already has correct style
2bda0e17
KB
396}
397
edccf428
VZ
398// ----------------------------------------------------------------------------
399// helpers
400// ----------------------------------------------------------------------------
401
402bool wxButton::SendClickEvent()
2bda0e17 403{
edccf428
VZ
404 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
405 event.SetEventObject(this);
406
407 return ProcessCommand(event);
2bda0e17
KB
408}
409
edccf428 410void wxButton::Command(wxCommandEvent & event)
2bda0e17 411{
edccf428 412 ProcessCommand(event);
2bda0e17
KB
413}
414
edccf428
VZ
415// ----------------------------------------------------------------------------
416// event/message handlers
417// ----------------------------------------------------------------------------
2bda0e17 418
33ac7e6f 419bool wxButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
edccf428 420{
fcf90ee1 421 bool processed = false;
57c0af52 422 switch ( param )
edccf428 423 {
5aab763c
RD
424 // NOTE: Apparently older versions (NT 4?) of the common controls send
425 // BN_DOUBLECLICKED but not a second BN_CLICKED for owner-drawn
87b6002d 426 // buttons, so in order to send two EVT_BUTTON events we should
5aab763c
RD
427 // catch both types. Currently (Feb 2003) up-to-date versions of
428 // win98, win2k and winXP all send two BN_CLICKED messages for
429 // all button types, so we don't catch BN_DOUBLECLICKED anymore
430 // in order to not get 3 EVT_BUTTON events. If this is a problem
431 // then we need to figure out which version of the comctl32 changed
432 // this behaviour and test for it.
433
a95e38c0
VZ
434 case 1: // message came from an accelerator
435 case BN_CLICKED: // normal buttons send this
57c0af52
VZ
436 processed = SendClickEvent();
437 break;
edccf428 438 }
2bda0e17 439
edccf428 440 return processed;
2bda0e17
KB
441}
442
c140b7e7 443WXLRESULT wxButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
678cd6de 444{
87b6002d 445 // when we receive focus, we want to temporarily become the default button in
036da5e3
VZ
446 // our parent panel so that pressing "Enter" would activate us -- and when
447 // losing it we should restore the previous default button as well
be4017f8 448 if ( nMsg == WM_SETFOCUS )
678cd6de 449 {
036da5e3 450 SetTmpDefault();
be4017f8 451
036da5e3
VZ
452 // let the default processing take place too
453 }
454 else if ( nMsg == WM_KILLFOCUS )
455 {
456 UnsetTmpDefault();
678cd6de 457 }
a95e38c0
VZ
458 else if ( nMsg == WM_LBUTTONDBLCLK )
459 {
f6bcfd97
BP
460 // emulate a click event to force an owner-drawn button to change its
461 // appearance - without this, it won't do it
462 (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN, wParam, lParam);
463
036da5e3 464 // and continue with processing the message normally as well
a95e38c0 465 }
678cd6de
VZ
466
467 // let the base class do all real processing
468 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
469}
cd0b1709
VZ
470
471// ----------------------------------------------------------------------------
472// owner-drawn buttons support
473// ----------------------------------------------------------------------------
474
475#ifdef __WIN32__
476
477// drawing helpers
478
479static void DrawButtonText(HDC hdc,
480 RECT *pRect,
481 const wxString& text,
482 COLORREF col)
483{
484 COLORREF colOld = SetTextColor(hdc, col);
485 int modeOld = SetBkMode(hdc, TRANSPARENT);
486
91f2c154
JS
487 // Note: we must have DT_SINGLELINE for DT_VCENTER to work.
488 ::DrawText(hdc, text, text.length(), pRect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
cd0b1709
VZ
489
490 SetBkMode(hdc, modeOld);
491 SetTextColor(hdc, colOld);
492}
493
494static void DrawRect(HDC hdc, const RECT& r)
495{
4676948b
JS
496 wxDrawLine(hdc, r.left, r.top, r.right, r.top);
497 wxDrawLine(hdc, r.right, r.top, r.right, r.bottom);
498 wxDrawLine(hdc, r.right, r.bottom, r.left, r.bottom);
499 wxDrawLine(hdc, r.left, r.bottom, r.left, r.top);
cd0b1709
VZ
500}
501
502void wxButton::MakeOwnerDrawn()
503{
504 long style = GetWindowLong(GetHwnd(), GWL_STYLE);
9750fc42 505 if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
cd0b1709
VZ
506 {
507 // make it so
508 style |= BS_OWNERDRAW;
509 SetWindowLong(GetHwnd(), GWL_STYLE, style);
510 }
511}
512
513bool wxButton::SetBackgroundColour(const wxColour &colour)
514{
515 if ( !wxControl::SetBackgroundColour(colour) )
516 {
517 // nothing to do
fcf90ee1 518 return false;
cd0b1709
VZ
519 }
520
521 MakeOwnerDrawn();
522
523 Refresh();
524
fcf90ee1 525 return true;
cd0b1709
VZ
526}
527
528bool wxButton::SetForegroundColour(const wxColour &colour)
529{
530 if ( !wxControl::SetForegroundColour(colour) )
531 {
532 // nothing to do
fcf90ee1 533 return false;
cd0b1709
VZ
534 }
535
536 MakeOwnerDrawn();
537
538 Refresh();
539
fcf90ee1 540 return true;
cd0b1709
VZ
541}
542
543/*
544 The button frame looks like this normally:
545
546 WWWWWWWWWWWWWWWWWWB
16162a64
GRG
547 WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
548 WH GB H = light grey (LIGHT)
549 WH GB G = dark grey (SHADOW)
550 WH GB B = black (DKSHADOW)
551 WH GB
cd0b1709
VZ
552 WGGGGGGGGGGGGGGGGGB
553 BBBBBBBBBBBBBBBBBBB
554
555 When the button is selected, the button becomes like this (the total button
556 size doesn't change):
557
558 BBBBBBBBBBBBBBBBBBB
559 BWWWWWWWWWWWWWWWWBB
16162a64
GRG
560 BWHHHHHHHHHHHHHHGBB
561 BWH GBB
562 BWH GBB
cd0b1709
VZ
563 BWGGGGGGGGGGGGGGGBB
564 BBBBBBBBBBBBBBBBBBB
565 BBBBBBBBBBBBBBBBBBB
566
567 When the button is pushed (while selected) it is like:
568
569 BBBBBBBBBBBBBBBBBBB
570 BGGGGGGGGGGGGGGGGGB
571 BG GB
572 BG GB
573 BG GB
16162a64 574 BG GB
cd0b1709
VZ
575 BGGGGGGGGGGGGGGGGGB
576 BBBBBBBBBBBBBBBBBBB
577*/
578
579static void DrawButtonFrame(HDC hdc, const RECT& rectBtn,
580 bool selected, bool pushed)
581{
582 RECT r;
583 CopyRect(&r, &rectBtn);
584
16162a64
GRG
585 HPEN hpenBlack = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DDKSHADOW)),
586 hpenGrey = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW)),
587 hpenLightGr = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DLIGHT)),
588 hpenWhite = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT));
cd0b1709
VZ
589
590 HPEN hpenOld = (HPEN)SelectObject(hdc, hpenBlack);
591
592 r.right--;
593 r.bottom--;
594
595 if ( pushed )
596 {
597 DrawRect(hdc, r);
598
599 (void)SelectObject(hdc, hpenGrey);
fcf90ee1 600 ::InflateRect(&r, -1, -1);
cd0b1709
VZ
601
602 DrawRect(hdc, r);
603 }
604 else // !pushed
605 {
606 if ( selected )
607 {
608 DrawRect(hdc, r);
609
fcf90ee1 610 ::InflateRect(&r, -1, -1);
cd0b1709
VZ
611 }
612
4676948b
JS
613 wxDrawLine(hdc, r.left, r.bottom, r.right, r.bottom);
614 wxDrawLine(hdc, r.right, r.bottom, r.right, r.top - 1);
cd0b1709
VZ
615
616 (void)SelectObject(hdc, hpenWhite);
4676948b
JS
617 wxDrawLine(hdc, r.left, r.bottom - 1, r.left, r.top);
618 wxDrawLine(hdc, r.left, r.top, r.right, r.top);
cd0b1709 619
16162a64 620 (void)SelectObject(hdc, hpenLightGr);
4676948b
JS
621 wxDrawLine(hdc, r.left + 1, r.bottom - 2, r.left + 1, r.top + 1);
622 wxDrawLine(hdc, r.left + 1, r.top + 1, r.right - 1, r.top + 1);
16162a64 623
cd0b1709 624 (void)SelectObject(hdc, hpenGrey);
4676948b
JS
625 wxDrawLine(hdc, r.left + 1, r.bottom - 1, r.right - 1, r.bottom - 1);
626 wxDrawLine(hdc, r.right - 1, r.bottom - 1, r.right - 1, r.top);
cd0b1709
VZ
627 }
628
629 (void)SelectObject(hdc, hpenOld);
630 DeleteObject(hpenWhite);
16162a64 631 DeleteObject(hpenLightGr);
cd0b1709
VZ
632 DeleteObject(hpenGrey);
633 DeleteObject(hpenBlack);
634}
635
636bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis)
637{
638 LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)wxdis;
639
640 RECT rectBtn;
641 CopyRect(&rectBtn, &lpDIS->rcItem);
642
643 COLORREF colBg = wxColourToRGB(GetBackgroundColour()),
644 colFg = wxColourToRGB(GetForegroundColour());
645
646 HDC hdc = lpDIS->hDC;
647 UINT state = lpDIS->itemState;
648
649 // first, draw the background
650 HBRUSH hbrushBackground = ::CreateSolidBrush(colBg);
651
652 FillRect(hdc, &rectBtn, hbrushBackground);
653
654 // draw the border for the current state
655 bool selected = (state & ODS_SELECTED) != 0;
656 if ( !selected )
657 {
658 wxPanel *panel = wxDynamicCast(GetParent(), wxPanel);
659 if ( panel )
660 {
661 selected = panel->GetDefaultItem() == this;
662 }
663 }
664 bool pushed = (SendMessage(GetHwnd(), BM_GETSTATE, 0, 0) & BST_PUSHED) != 0;
665
666 DrawButtonFrame(hdc, rectBtn, selected, pushed);
667
668 // draw the focus rect if needed
669 if ( state & ODS_FOCUS )
670 {
671 RECT rectFocus;
672 CopyRect(&rectFocus, &rectBtn);
673
674 // I don't know where does this constant come from, but this is how
675 // Windows draws them
676 InflateRect(&rectFocus, -4, -4);
677
678 DrawFocusRect(hdc, &rectFocus);
679 }
680
9750fc42
VZ
681 if ( pushed )
682 {
683 // the label is shifted by 1 pixel to create "pushed" effect
684 OffsetRect(&rectBtn, 1, 1);
685 }
686
cd0b1709
VZ
687 DrawButtonText(hdc, &rectBtn, GetLabel(),
688 state & ODS_DISABLED ? GetSysColor(COLOR_GRAYTEXT)
689 : colFg);
690
691 ::DeleteObject(hbrushBackground);
692
fcf90ee1 693 return true;
cd0b1709
VZ
694}
695
696#endif // __WIN32__
1e6feb95
VZ
697
698#endif // wxUSE_BUTTON
699