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