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