]> git.saurik.com Git - wxWidgets.git/blame - src/msw/button.cpp
removed assert which could be provoked by correct code
[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$
8// Copyright: (c) Julian Smart and Markus Holzem
edccf428 9// Licence: wxWindows license
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
edccf428
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
cd0b1709 19
2bda0e17 20#ifdef __GNUG__
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
edccf428
VZ
34 #include "wx/button.h"
35 #include "wx/brush.h"
4e938f5b 36 #include "wx/panel.h"
8a4df159 37 #include "wx/bmpbuttn.h"
fb39c7ec
RR
38 #include "wx/settings.h"
39 #include "wx/dcscreen.h"
2bda0e17
KB
40#endif
41
42#include "wx/msw/private.h"
43
edccf428
VZ
44// ----------------------------------------------------------------------------
45// macros
46// ----------------------------------------------------------------------------
47
cd0b1709 48IMPLEMENT_DYNAMIC_CLASS(wxButton, wxControl)
2bda0e17 49
edccf428
VZ
50// this macro tries to adjust the default button height to a reasonable value
51// using the char height as the base
1c4a764c 52#define BUTTON_HEIGHT_FROM_CHAR_HEIGHT(cy) (11*EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy)/10)
2bda0e17 53
edccf428
VZ
54// ============================================================================
55// implementation
56// ============================================================================
57
58// ----------------------------------------------------------------------------
59// creation/destruction
60// ----------------------------------------------------------------------------
61
62bool wxButton::Create(wxWindow *parent,
63 wxWindowID id,
64 const wxString& label,
65 const wxPoint& pos,
66 const wxSize& size,
67 long style,
68 const wxValidator& validator,
69 const wxString& name)
2bda0e17 70{
8d99be5f 71 if ( !CreateBase(parent, id, pos, size, style, validator, name) )
edccf428
VZ
72 return FALSE;
73
edccf428
VZ
74 parent->AddChild((wxButton *)this);
75
cd0b1709
VZ
76 m_backgroundColour = parent->GetBackgroundColour();
77 m_foregroundColour = parent->GetForegroundColour();
78
f6bcfd97
BP
79 long msStyle = WS_VISIBLE | WS_TABSTOP | WS_CHILD /* | WS_CLIPSIBLINGS */ ;
80
b0766406
JS
81 if ( m_windowStyle & wxCLIP_SIBLINGS )
82 msStyle |= WS_CLIPSIBLINGS;
83
f6bcfd97
BP
84#ifdef __WIN32__
85 if(m_windowStyle & wxBU_LEFT)
86 msStyle |= BS_LEFT;
87 if(m_windowStyle & wxBU_RIGHT)
88 msStyle |= BS_RIGHT;
89 if(m_windowStyle & wxBU_TOP)
90 msStyle |= BS_TOP;
91 if(m_windowStyle & wxBU_BOTTOM)
92 msStyle |= BS_BOTTOM;
93#endif
edccf428
VZ
94
95 m_hWnd = (WXHWND)CreateWindowEx
96 (
97 MakeExtendedStyle(m_windowStyle),
223d09f6 98 wxT("BUTTON"),
edccf428 99 label,
f6bcfd97 100 msStyle,
a77aa9d6 101 0, 0, 0, 0,
edccf428
VZ
102 GetWinHwnd(parent),
103 (HMENU)m_windowId,
104 wxGetInstance(),
105 NULL
106 );
107
f6bcfd97
BP
108 if (m_hWnd == 0)
109 {
110 wxString msg;
111#ifdef __WIN16__
112 msg.Printf(wxT("CreateWindowEx failed"));
113#else
114 msg.Printf(wxT("CreateWindowEx failed with error number %ld"), (long) GetLastError());
115#endif
116 wxFAIL_MSG(msg);
117 }
118
edccf428
VZ
119 // Subclass again for purposes of dialog editing mode
120 SubclassWin(m_hWnd);
121
122 SetFont(parent->GetFont());
123
124 SetSize(pos.x, pos.y, size.x, size.y);
125
2bda0e17 126 return TRUE;
2bda0e17
KB
127}
128
edccf428 129wxButton::~wxButton()
2bda0e17 130{
edccf428
VZ
131 wxPanel *panel = wxDynamicCast(GetParent(), wxPanel);
132 if ( panel )
133 {
134 if ( panel->GetDefaultItem() == this )
135 {
136 // don't leave the panel with invalid default item
137 panel->SetDefaultItem(NULL);
138 }
139 }
2bda0e17
KB
140}
141
edccf428
VZ
142// ----------------------------------------------------------------------------
143// size management including autosizing
144// ----------------------------------------------------------------------------
145
f68586e5 146wxSize wxButton::DoGetBestSize() const
2bda0e17 147{
4438caf4
VZ
148 wxString label = wxGetWindowText(GetHWND());
149 int wBtn;
150 GetTextExtent(label, &wBtn, NULL);
edccf428 151
4438caf4
VZ
152 int wChar, hChar;
153 wxGetCharSize(GetHWND(), &wChar, &hChar, &GetFont());
154
155 // add a margin - the button is wider than just its label
156 wBtn += 3*wChar;
157
158 // the button height is proportional to the height of the font used
159 int hBtn = BUTTON_HEIGHT_FROM_CHAR_HEIGHT(hChar);
edccf428 160
a63f2bec
VS
161 wxSize sz = GetDefaultSize();
162 if (wBtn > sz.x) sz.x = wBtn;
163 if (hBtn > sz.y) sz.y = hBtn;
164
165 return sz;
2bda0e17
KB
166}
167
e1f36ff8 168/* static */
1e6feb95 169wxSize wxButtonBase::GetDefaultSize()
e1f36ff8 170{
8c3c31d4 171 static wxSize s_sizeBtn;
e1f36ff8 172
8c3c31d4
VZ
173 if ( s_sizeBtn.x == 0 )
174 {
175 wxScreenDC dc;
176 dc.SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
177
178 // the size of a standard button in the dialog units is 50x14,
179 // translate this to pixels
180 // NB1: the multipliers come from the Windows convention
181 // NB2: the extra +1/+2 were needed to get the size be the same as the
182 // size of the buttons in the standard dialog - I don't know how
183 // this happens, but on my system this size is 75x23 in pixels and
184 // 23*8 isn't even divisible by 14... Would be nice to understand
185 // why these constants are needed though!
186 s_sizeBtn.x = (50 * (dc.GetCharWidth() + 1))/4;
187 s_sizeBtn.y = ((14 * dc.GetCharHeight()) + 2)/8;
188 }
e1f36ff8 189
8c3c31d4 190 return s_sizeBtn;
e1f36ff8
VZ
191}
192
4438caf4
VZ
193// ----------------------------------------------------------------------------
194// set this button as the default one in its panel
195// ----------------------------------------------------------------------------
196
edccf428 197void wxButton::SetDefault()
2bda0e17 198{
edccf428 199 wxWindow *parent = GetParent();
5d1d2d46 200 wxButton *btnOldDefault = NULL;
edccf428
VZ
201 wxPanel *panel = wxDynamicCast(parent, wxPanel);
202 if ( panel )
5d1d2d46
VZ
203 {
204 btnOldDefault = panel->GetDefaultItem();
edccf428 205 panel->SetDefaultItem(this);
5d1d2d46 206 }
2bda0e17 207
edccf428
VZ
208 if ( parent )
209 {
210 SendMessage(GetWinHwnd(parent), DM_SETDEFID, m_windowId, 0L);
211 }
8ed57d93 212
cd0b1709 213 if ( btnOldDefault )
5d1d2d46
VZ
214 {
215 // remove the BS_DEFPUSHBUTTON style from the other button
216 long style = GetWindowLong(GetHwndOf(btnOldDefault), GWL_STYLE);
cd0b1709
VZ
217
218 // don't do it with the owner drawn buttons because it will reset
219 // BS_OWNERDRAW style bit too (BS_OWNERDRAW & BS_DEFPUSHBUTTON != 0)!
be4017f8 220 if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
cd0b1709
VZ
221 {
222 style &= ~BS_DEFPUSHBUTTON;
223 SendMessage(GetHwndOf(btnOldDefault), BM_SETSTYLE, style, 1L);
224 }
be4017f8
VZ
225 else
226 {
227 // redraw the button - it will notice itself that it's not the
228 // default one any longer
229 btnOldDefault->Refresh();
230 }
5d1d2d46
VZ
231 }
232
233 // set this button as the default
234 long style = GetWindowLong(GetHwnd(), GWL_STYLE);
be4017f8
VZ
235 if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
236 {
237 style |= BS_DEFPUSHBUTTON;
238 SendMessage(GetHwnd(), BM_SETSTYLE, style, 1L);
239 }
2bda0e17
KB
240}
241
edccf428
VZ
242// ----------------------------------------------------------------------------
243// helpers
244// ----------------------------------------------------------------------------
245
246bool wxButton::SendClickEvent()
2bda0e17 247{
edccf428
VZ
248 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
249 event.SetEventObject(this);
250
251 return ProcessCommand(event);
2bda0e17
KB
252}
253
edccf428 254void wxButton::Command(wxCommandEvent & event)
2bda0e17 255{
edccf428 256 ProcessCommand(event);
2bda0e17
KB
257}
258
edccf428
VZ
259// ----------------------------------------------------------------------------
260// event/message handlers
261// ----------------------------------------------------------------------------
2bda0e17 262
33ac7e6f 263bool wxButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
edccf428
VZ
264{
265 bool processed = FALSE;
57c0af52 266 switch ( param )
edccf428 267 {
a95e38c0
VZ
268 case 1: // message came from an accelerator
269 case BN_CLICKED: // normal buttons send this
270 case BN_DOUBLECLICKED: // owner-drawn ones also send this
57c0af52
VZ
271 processed = SendClickEvent();
272 break;
edccf428 273 }
2bda0e17 274
edccf428 275 return processed;
2bda0e17
KB
276}
277
678cd6de
VZ
278long wxButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
279{
be4017f8
VZ
280 // when we receive focus, we want to become the default button in our
281 // parent panel
282 if ( nMsg == WM_SETFOCUS )
678cd6de 283 {
be4017f8
VZ
284 SetDefault();
285
286 // let the default processign take place too
678cd6de 287 }
a95e38c0
VZ
288 else if ( nMsg == WM_LBUTTONDBLCLK )
289 {
f6bcfd97
BP
290 // emulate a click event to force an owner-drawn button to change its
291 // appearance - without this, it won't do it
292 (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN, wParam, lParam);
293
294 // and conitnue with processing the message normally as well
a95e38c0 295 }
678cd6de
VZ
296
297 // let the base class do all real processing
298 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
299}
cd0b1709
VZ
300
301// ----------------------------------------------------------------------------
302// owner-drawn buttons support
303// ----------------------------------------------------------------------------
304
305#ifdef __WIN32__
306
307// drawing helpers
308
309static void DrawButtonText(HDC hdc,
310 RECT *pRect,
311 const wxString& text,
312 COLORREF col)
313{
314 COLORREF colOld = SetTextColor(hdc, col);
315 int modeOld = SetBkMode(hdc, TRANSPARENT);
316
317 DrawText(hdc, text, text.length(), pRect,
318 DT_CENTER | DT_VCENTER | DT_SINGLELINE);
319
320 SetBkMode(hdc, modeOld);
321 SetTextColor(hdc, colOld);
322}
323
324static void DrawRect(HDC hdc, const RECT& r)
325{
326 MoveToEx(hdc, r.left, r.top, NULL);
327 LineTo(hdc, r.right, r.top);
328 LineTo(hdc, r.right, r.bottom);
329 LineTo(hdc, r.left, r.bottom);
330 LineTo(hdc, r.left, r.top);
331}
332
333void wxButton::MakeOwnerDrawn()
334{
335 long style = GetWindowLong(GetHwnd(), GWL_STYLE);
9750fc42 336 if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
cd0b1709
VZ
337 {
338 // make it so
339 style |= BS_OWNERDRAW;
340 SetWindowLong(GetHwnd(), GWL_STYLE, style);
341 }
342}
343
344bool wxButton::SetBackgroundColour(const wxColour &colour)
345{
346 if ( !wxControl::SetBackgroundColour(colour) )
347 {
348 // nothing to do
349 return FALSE;
350 }
351
352 MakeOwnerDrawn();
353
354 Refresh();
355
356 return TRUE;
357}
358
359bool wxButton::SetForegroundColour(const wxColour &colour)
360{
361 if ( !wxControl::SetForegroundColour(colour) )
362 {
363 // nothing to do
364 return FALSE;
365 }
366
367 MakeOwnerDrawn();
368
369 Refresh();
370
371 return TRUE;
372}
373
374/*
375 The button frame looks like this normally:
376
377 WWWWWWWWWWWWWWWWWWB
16162a64
GRG
378 WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
379 WH GB H = light grey (LIGHT)
380 WH GB G = dark grey (SHADOW)
381 WH GB B = black (DKSHADOW)
382 WH GB
cd0b1709
VZ
383 WGGGGGGGGGGGGGGGGGB
384 BBBBBBBBBBBBBBBBBBB
385
386 When the button is selected, the button becomes like this (the total button
387 size doesn't change):
388
389 BBBBBBBBBBBBBBBBBBB
390 BWWWWWWWWWWWWWWWWBB
16162a64
GRG
391 BWHHHHHHHHHHHHHHGBB
392 BWH GBB
393 BWH GBB
cd0b1709
VZ
394 BWGGGGGGGGGGGGGGGBB
395 BBBBBBBBBBBBBBBBBBB
396 BBBBBBBBBBBBBBBBBBB
397
398 When the button is pushed (while selected) it is like:
399
400 BBBBBBBBBBBBBBBBBBB
401 BGGGGGGGGGGGGGGGGGB
402 BG GB
403 BG GB
404 BG GB
16162a64 405 BG GB
cd0b1709
VZ
406 BGGGGGGGGGGGGGGGGGB
407 BBBBBBBBBBBBBBBBBBB
408*/
409
410static void DrawButtonFrame(HDC hdc, const RECT& rectBtn,
411 bool selected, bool pushed)
412{
413 RECT r;
414 CopyRect(&r, &rectBtn);
415
16162a64
GRG
416 HPEN hpenBlack = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DDKSHADOW)),
417 hpenGrey = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW)),
418 hpenLightGr = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DLIGHT)),
419 hpenWhite = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT));
cd0b1709
VZ
420
421 HPEN hpenOld = (HPEN)SelectObject(hdc, hpenBlack);
422
423 r.right--;
424 r.bottom--;
425
426 if ( pushed )
427 {
428 DrawRect(hdc, r);
429
430 (void)SelectObject(hdc, hpenGrey);
431 InflateRect(&r, -1, -1);
432
433 DrawRect(hdc, r);
434 }
435 else // !pushed
436 {
437 if ( selected )
438 {
439 DrawRect(hdc, r);
440
441 InflateRect(&r, -1, -1);
442 }
443
444 MoveToEx(hdc, r.left, r.bottom, NULL);
445 LineTo(hdc, r.right, r.bottom);
446 LineTo(hdc, r.right, r.top - 1);
447
448 (void)SelectObject(hdc, hpenWhite);
449 MoveToEx(hdc, r.left, r.bottom - 1, NULL);
450 LineTo(hdc, r.left, r.top);
451 LineTo(hdc, r.right, r.top);
452
16162a64
GRG
453 (void)SelectObject(hdc, hpenLightGr);
454 MoveToEx(hdc, r.left + 1, r.bottom - 2, NULL);
455 LineTo(hdc, r.left + 1, r.top + 1);
456 LineTo(hdc, r.right - 1, r.top + 1);
457
cd0b1709
VZ
458 (void)SelectObject(hdc, hpenGrey);
459 MoveToEx(hdc, r.left + 1, r.bottom - 1, NULL);
460 LineTo(hdc, r.right - 1, r.bottom - 1);
461 LineTo(hdc, r.right - 1, r.top);
462 }
463
464 (void)SelectObject(hdc, hpenOld);
465 DeleteObject(hpenWhite);
16162a64 466 DeleteObject(hpenLightGr);
cd0b1709
VZ
467 DeleteObject(hpenGrey);
468 DeleteObject(hpenBlack);
469}
470
471bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis)
472{
473 LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)wxdis;
474
475 RECT rectBtn;
476 CopyRect(&rectBtn, &lpDIS->rcItem);
477
478 COLORREF colBg = wxColourToRGB(GetBackgroundColour()),
479 colFg = wxColourToRGB(GetForegroundColour());
480
481 HDC hdc = lpDIS->hDC;
482 UINT state = lpDIS->itemState;
483
484 // first, draw the background
485 HBRUSH hbrushBackground = ::CreateSolidBrush(colBg);
486
487 FillRect(hdc, &rectBtn, hbrushBackground);
488
489 // draw the border for the current state
490 bool selected = (state & ODS_SELECTED) != 0;
491 if ( !selected )
492 {
493 wxPanel *panel = wxDynamicCast(GetParent(), wxPanel);
494 if ( panel )
495 {
496 selected = panel->GetDefaultItem() == this;
497 }
498 }
499 bool pushed = (SendMessage(GetHwnd(), BM_GETSTATE, 0, 0) & BST_PUSHED) != 0;
500
501 DrawButtonFrame(hdc, rectBtn, selected, pushed);
502
503 // draw the focus rect if needed
504 if ( state & ODS_FOCUS )
505 {
506 RECT rectFocus;
507 CopyRect(&rectFocus, &rectBtn);
508
509 // I don't know where does this constant come from, but this is how
510 // Windows draws them
511 InflateRect(&rectFocus, -4, -4);
512
513 DrawFocusRect(hdc, &rectFocus);
514 }
515
9750fc42
VZ
516 if ( pushed )
517 {
518 // the label is shifted by 1 pixel to create "pushed" effect
519 OffsetRect(&rectBtn, 1, 1);
520 }
521
cd0b1709
VZ
522 DrawButtonText(hdc, &rectBtn, GetLabel(),
523 state & ODS_DISABLED ? GetSysColor(COLOR_GRAYTEXT)
524 : colFg);
525
526 ::DeleteObject(hbrushBackground);
527
cd0b1709
VZ
528 return TRUE;
529}
530
531#endif // __WIN32__
1e6feb95
VZ
532
533#endif // wxUSE_BUTTON
534