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