]> git.saurik.com Git - wxWidgets.git/blame - src/msw/button.cpp
1. warning in gtk/menu.cpp 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 {
678cd6de 240 case 1: // means that the message came from an accelerator
57c0af52
VZ
241 case BN_CLICKED:
242 processed = SendClickEvent();
243 break;
edccf428 244 }
2bda0e17 245
edccf428 246 return processed;
2bda0e17
KB
247}
248
678cd6de
VZ
249long wxButton::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
250{
be4017f8
VZ
251 // when we receive focus, we want to become the default button in our
252 // parent panel
253 if ( nMsg == WM_SETFOCUS )
678cd6de 254 {
be4017f8
VZ
255 SetDefault();
256
257 // let the default processign take place too
678cd6de
VZ
258 }
259
260 // let the base class do all real processing
261 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
262}
cd0b1709
VZ
263
264// ----------------------------------------------------------------------------
265// owner-drawn buttons support
266// ----------------------------------------------------------------------------
267
268#ifdef __WIN32__
269
270// drawing helpers
271
272static void DrawButtonText(HDC hdc,
273 RECT *pRect,
274 const wxString& text,
275 COLORREF col)
276{
277 COLORREF colOld = SetTextColor(hdc, col);
278 int modeOld = SetBkMode(hdc, TRANSPARENT);
279
280 DrawText(hdc, text, text.length(), pRect,
281 DT_CENTER | DT_VCENTER | DT_SINGLELINE);
282
283 SetBkMode(hdc, modeOld);
284 SetTextColor(hdc, colOld);
285}
286
287static void DrawRect(HDC hdc, const RECT& r)
288{
289 MoveToEx(hdc, r.left, r.top, NULL);
290 LineTo(hdc, r.right, r.top);
291 LineTo(hdc, r.right, r.bottom);
292 LineTo(hdc, r.left, r.bottom);
293 LineTo(hdc, r.left, r.top);
294}
295
296void wxButton::MakeOwnerDrawn()
297{
298 long style = GetWindowLong(GetHwnd(), GWL_STYLE);
9750fc42 299 if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
cd0b1709
VZ
300 {
301 // make it so
302 style |= BS_OWNERDRAW;
303 SetWindowLong(GetHwnd(), GWL_STYLE, style);
304 }
305}
306
307bool wxButton::SetBackgroundColour(const wxColour &colour)
308{
309 if ( !wxControl::SetBackgroundColour(colour) )
310 {
311 // nothing to do
312 return FALSE;
313 }
314
315 MakeOwnerDrawn();
316
317 Refresh();
318
319 return TRUE;
320}
321
322bool wxButton::SetForegroundColour(const wxColour &colour)
323{
324 if ( !wxControl::SetForegroundColour(colour) )
325 {
326 // nothing to do
327 return FALSE;
328 }
329
330 MakeOwnerDrawn();
331
332 Refresh();
333
334 return TRUE;
335}
336
337/*
338 The button frame looks like this normally:
339
340 WWWWWWWWWWWWWWWWWWB
16162a64
GRG
341 WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
342 WH GB H = light grey (LIGHT)
343 WH GB G = dark grey (SHADOW)
344 WH GB B = black (DKSHADOW)
345 WH GB
cd0b1709
VZ
346 WGGGGGGGGGGGGGGGGGB
347 BBBBBBBBBBBBBBBBBBB
348
349 When the button is selected, the button becomes like this (the total button
350 size doesn't change):
351
352 BBBBBBBBBBBBBBBBBBB
353 BWWWWWWWWWWWWWWWWBB
16162a64
GRG
354 BWHHHHHHHHHHHHHHGBB
355 BWH GBB
356 BWH GBB
cd0b1709
VZ
357 BWGGGGGGGGGGGGGGGBB
358 BBBBBBBBBBBBBBBBBBB
359 BBBBBBBBBBBBBBBBBBB
360
361 When the button is pushed (while selected) it is like:
362
363 BBBBBBBBBBBBBBBBBBB
364 BGGGGGGGGGGGGGGGGGB
365 BG GB
366 BG GB
367 BG GB
16162a64 368 BG GB
cd0b1709
VZ
369 BGGGGGGGGGGGGGGGGGB
370 BBBBBBBBBBBBBBBBBBB
371*/
372
373static void DrawButtonFrame(HDC hdc, const RECT& rectBtn,
374 bool selected, bool pushed)
375{
376 RECT r;
377 CopyRect(&r, &rectBtn);
378
16162a64
GRG
379 HPEN hpenBlack = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DDKSHADOW)),
380 hpenGrey = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW)),
381 hpenLightGr = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DLIGHT)),
382 hpenWhite = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT));
cd0b1709
VZ
383
384 HPEN hpenOld = (HPEN)SelectObject(hdc, hpenBlack);
385
386 r.right--;
387 r.bottom--;
388
389 if ( pushed )
390 {
391 DrawRect(hdc, r);
392
393 (void)SelectObject(hdc, hpenGrey);
394 InflateRect(&r, -1, -1);
395
396 DrawRect(hdc, r);
397 }
398 else // !pushed
399 {
400 if ( selected )
401 {
402 DrawRect(hdc, r);
403
404 InflateRect(&r, -1, -1);
405 }
406
407 MoveToEx(hdc, r.left, r.bottom, NULL);
408 LineTo(hdc, r.right, r.bottom);
409 LineTo(hdc, r.right, r.top - 1);
410
411 (void)SelectObject(hdc, hpenWhite);
412 MoveToEx(hdc, r.left, r.bottom - 1, NULL);
413 LineTo(hdc, r.left, r.top);
414 LineTo(hdc, r.right, r.top);
415
16162a64
GRG
416 (void)SelectObject(hdc, hpenLightGr);
417 MoveToEx(hdc, r.left + 1, r.bottom - 2, NULL);
418 LineTo(hdc, r.left + 1, r.top + 1);
419 LineTo(hdc, r.right - 1, r.top + 1);
420
cd0b1709
VZ
421 (void)SelectObject(hdc, hpenGrey);
422 MoveToEx(hdc, r.left + 1, r.bottom - 1, NULL);
423 LineTo(hdc, r.right - 1, r.bottom - 1);
424 LineTo(hdc, r.right - 1, r.top);
425 }
426
427 (void)SelectObject(hdc, hpenOld);
428 DeleteObject(hpenWhite);
16162a64 429 DeleteObject(hpenLightGr);
cd0b1709
VZ
430 DeleteObject(hpenGrey);
431 DeleteObject(hpenBlack);
432}
433
434bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis)
435{
436 LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)wxdis;
437
438 RECT rectBtn;
439 CopyRect(&rectBtn, &lpDIS->rcItem);
440
441 COLORREF colBg = wxColourToRGB(GetBackgroundColour()),
442 colFg = wxColourToRGB(GetForegroundColour());
443
444 HDC hdc = lpDIS->hDC;
445 UINT state = lpDIS->itemState;
446
447 // first, draw the background
448 HBRUSH hbrushBackground = ::CreateSolidBrush(colBg);
449
450 FillRect(hdc, &rectBtn, hbrushBackground);
451
452 // draw the border for the current state
453 bool selected = (state & ODS_SELECTED) != 0;
454 if ( !selected )
455 {
456 wxPanel *panel = wxDynamicCast(GetParent(), wxPanel);
457 if ( panel )
458 {
459 selected = panel->GetDefaultItem() == this;
460 }
461 }
462 bool pushed = (SendMessage(GetHwnd(), BM_GETSTATE, 0, 0) & BST_PUSHED) != 0;
463
464 DrawButtonFrame(hdc, rectBtn, selected, pushed);
465
466 // draw the focus rect if needed
467 if ( state & ODS_FOCUS )
468 {
469 RECT rectFocus;
470 CopyRect(&rectFocus, &rectBtn);
471
472 // I don't know where does this constant come from, but this is how
473 // Windows draws them
474 InflateRect(&rectFocus, -4, -4);
475
476 DrawFocusRect(hdc, &rectFocus);
477 }
478
9750fc42
VZ
479 if ( pushed )
480 {
481 // the label is shifted by 1 pixel to create "pushed" effect
482 OffsetRect(&rectBtn, 1, 1);
483 }
484
cd0b1709
VZ
485 DrawButtonText(hdc, &rectBtn, GetLabel(),
486 state & ODS_DISABLED ? GetSysColor(COLOR_GRAYTEXT)
487 : colFg);
488
489 ::DeleteObject(hbrushBackground);
490
cd0b1709
VZ
491 return TRUE;
492}
493
494#endif // __WIN32__