]> git.saurik.com Git - wxWidgets.git/blame - src/msw/button.cpp
Various changes to make pop up menus work
[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{
5b2f31eb 71 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
edccf428
VZ
72 return FALSE;
73
5b2f31eb
VZ
74 return MSWCreateControl(_T("BUTTON"), label, pos, size, style);
75}
76
77wxButton::~wxButton()
78{
79}
edccf428 80
5b2f31eb
VZ
81// ----------------------------------------------------------------------------
82// flags
83// ----------------------------------------------------------------------------
cd0b1709 84
5b2f31eb
VZ
85WXDWORD wxButton::MSWGetStyle(long style, WXDWORD *exstyle) const
86{
87 // buttons never have an external border, they draw their own one
88 WXDWORD msStyle = wxControl::MSWGetStyle
89 (
90 (style & ~wxBORDER_MASK) | wxBORDER_NONE, exstyle
91 );
f6bcfd97 92
5b2f31eb
VZ
93 // we must use WS_CLIPSIBLINGS with the buttons or they would draw over
94 // each other in any resizeable dialog which has more than one button in
95 // the bottom
96 msStyle |= WS_CLIPSIBLINGS;
b0766406 97
f6bcfd97 98#ifdef __WIN32__
5b2f31eb
VZ
99 // don't use "else if" here: weird as it is, but you may combine wxBU_LEFT
100 // and wxBU_RIGHT to get BS_CENTER!
101 if ( style & wxBU_LEFT )
f6bcfd97 102 msStyle |= BS_LEFT;
5b2f31eb 103 if ( style & wxBU_RIGHT )
f6bcfd97 104 msStyle |= BS_RIGHT;
5b2f31eb 105 if ( style & wxBU_TOP )
f6bcfd97 106 msStyle |= BS_TOP;
5b2f31eb 107 if ( style & wxBU_BOTTOM )
f6bcfd97 108 msStyle |= BS_BOTTOM;
5b2f31eb 109#endif // __WIN32__
edccf428 110
5b2f31eb 111 return msStyle;
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 140/* static */
1e6feb95 141wxSize wxButtonBase::GetDefaultSize()
e1f36ff8 142{
8c3c31d4 143 static wxSize s_sizeBtn;
e1f36ff8 144
8c3c31d4
VZ
145 if ( s_sizeBtn.x == 0 )
146 {
147 wxScreenDC dc;
a756f210 148 dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
8c3c31d4
VZ
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();
456bc6d9
VZ
172 wxButton *btnOldDefault;
173 if ( parent )
5d1d2d46 174 {
456bc6d9
VZ
175 wxWindow *winOldDefault = parent->SetDefaultItem(this);
176 btnOldDefault = wxDynamicCast(winOldDefault, wxButton);
2bda0e17 177
456bc6d9
VZ
178 ::SendMessage(GetWinHwnd(parent), DM_SETDEFID, m_windowId, 0L);
179 }
180 else // is a button without parent really normal?
edccf428 181 {
456bc6d9 182 btnOldDefault = NULL;
edccf428 183 }
8ed57d93 184
456bc6d9 185 if ( btnOldDefault && btnOldDefault != this )
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
33ac7e6f 235bool wxButton::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
edccf428
VZ
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 {
f6bcfd97
BP
262 // emulate a click event to force an owner-drawn button to change its
263 // appearance - without this, it won't do it
264 (void)wxControl::MSWWindowProc(WM_LBUTTONDOWN, wParam, lParam);
265
266 // and conitnue with processing the message normally as well
a95e38c0 267 }
678cd6de
VZ
268
269 // let the base class do all real processing
270 return wxControl::MSWWindowProc(nMsg, wParam, lParam);
271}
cd0b1709
VZ
272
273// ----------------------------------------------------------------------------
274// owner-drawn buttons support
275// ----------------------------------------------------------------------------
276
277#ifdef __WIN32__
278
279// drawing helpers
280
281static void DrawButtonText(HDC hdc,
282 RECT *pRect,
283 const wxString& text,
284 COLORREF col)
285{
286 COLORREF colOld = SetTextColor(hdc, col);
287 int modeOld = SetBkMode(hdc, TRANSPARENT);
288
289 DrawText(hdc, text, text.length(), pRect,
290 DT_CENTER | DT_VCENTER | DT_SINGLELINE);
291
292 SetBkMode(hdc, modeOld);
293 SetTextColor(hdc, colOld);
294}
295
296static void DrawRect(HDC hdc, const RECT& r)
297{
298 MoveToEx(hdc, r.left, r.top, NULL);
299 LineTo(hdc, r.right, r.top);
300 LineTo(hdc, r.right, r.bottom);
301 LineTo(hdc, r.left, r.bottom);
302 LineTo(hdc, r.left, r.top);
303}
304
305void wxButton::MakeOwnerDrawn()
306{
307 long style = GetWindowLong(GetHwnd(), GWL_STYLE);
9750fc42 308 if ( (style & BS_OWNERDRAW) != BS_OWNERDRAW )
cd0b1709
VZ
309 {
310 // make it so
311 style |= BS_OWNERDRAW;
312 SetWindowLong(GetHwnd(), GWL_STYLE, style);
313 }
314}
315
316bool wxButton::SetBackgroundColour(const wxColour &colour)
317{
318 if ( !wxControl::SetBackgroundColour(colour) )
319 {
320 // nothing to do
321 return FALSE;
322 }
323
324 MakeOwnerDrawn();
325
326 Refresh();
327
328 return TRUE;
329}
330
331bool wxButton::SetForegroundColour(const wxColour &colour)
332{
333 if ( !wxControl::SetForegroundColour(colour) )
334 {
335 // nothing to do
336 return FALSE;
337 }
338
339 MakeOwnerDrawn();
340
341 Refresh();
342
343 return TRUE;
344}
345
346/*
347 The button frame looks like this normally:
348
349 WWWWWWWWWWWWWWWWWWB
16162a64
GRG
350 WHHHHHHHHHHHHHHHHGB W = white (HILIGHT)
351 WH GB H = light grey (LIGHT)
352 WH GB G = dark grey (SHADOW)
353 WH GB B = black (DKSHADOW)
354 WH GB
cd0b1709
VZ
355 WGGGGGGGGGGGGGGGGGB
356 BBBBBBBBBBBBBBBBBBB
357
358 When the button is selected, the button becomes like this (the total button
359 size doesn't change):
360
361 BBBBBBBBBBBBBBBBBBB
362 BWWWWWWWWWWWWWWWWBB
16162a64
GRG
363 BWHHHHHHHHHHHHHHGBB
364 BWH GBB
365 BWH GBB
cd0b1709
VZ
366 BWGGGGGGGGGGGGGGGBB
367 BBBBBBBBBBBBBBBBBBB
368 BBBBBBBBBBBBBBBBBBB
369
370 When the button is pushed (while selected) it is like:
371
372 BBBBBBBBBBBBBBBBBBB
373 BGGGGGGGGGGGGGGGGGB
374 BG GB
375 BG GB
376 BG GB
16162a64 377 BG GB
cd0b1709
VZ
378 BGGGGGGGGGGGGGGGGGB
379 BBBBBBBBBBBBBBBBBBB
380*/
381
382static void DrawButtonFrame(HDC hdc, const RECT& rectBtn,
383 bool selected, bool pushed)
384{
385 RECT r;
386 CopyRect(&r, &rectBtn);
387
16162a64
GRG
388 HPEN hpenBlack = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DDKSHADOW)),
389 hpenGrey = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW)),
390 hpenLightGr = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DLIGHT)),
391 hpenWhite = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT));
cd0b1709
VZ
392
393 HPEN hpenOld = (HPEN)SelectObject(hdc, hpenBlack);
394
395 r.right--;
396 r.bottom--;
397
398 if ( pushed )
399 {
400 DrawRect(hdc, r);
401
402 (void)SelectObject(hdc, hpenGrey);
403 InflateRect(&r, -1, -1);
404
405 DrawRect(hdc, r);
406 }
407 else // !pushed
408 {
409 if ( selected )
410 {
411 DrawRect(hdc, r);
412
413 InflateRect(&r, -1, -1);
414 }
415
416 MoveToEx(hdc, r.left, r.bottom, NULL);
417 LineTo(hdc, r.right, r.bottom);
418 LineTo(hdc, r.right, r.top - 1);
419
420 (void)SelectObject(hdc, hpenWhite);
421 MoveToEx(hdc, r.left, r.bottom - 1, NULL);
422 LineTo(hdc, r.left, r.top);
423 LineTo(hdc, r.right, r.top);
424
16162a64
GRG
425 (void)SelectObject(hdc, hpenLightGr);
426 MoveToEx(hdc, r.left + 1, r.bottom - 2, NULL);
427 LineTo(hdc, r.left + 1, r.top + 1);
428 LineTo(hdc, r.right - 1, r.top + 1);
429
cd0b1709
VZ
430 (void)SelectObject(hdc, hpenGrey);
431 MoveToEx(hdc, r.left + 1, r.bottom - 1, NULL);
432 LineTo(hdc, r.right - 1, r.bottom - 1);
433 LineTo(hdc, r.right - 1, r.top);
434 }
435
436 (void)SelectObject(hdc, hpenOld);
437 DeleteObject(hpenWhite);
16162a64 438 DeleteObject(hpenLightGr);
cd0b1709
VZ
439 DeleteObject(hpenGrey);
440 DeleteObject(hpenBlack);
441}
442
443bool wxButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis)
444{
445 LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)wxdis;
446
447 RECT rectBtn;
448 CopyRect(&rectBtn, &lpDIS->rcItem);
449
450 COLORREF colBg = wxColourToRGB(GetBackgroundColour()),
451 colFg = wxColourToRGB(GetForegroundColour());
452
453 HDC hdc = lpDIS->hDC;
454 UINT state = lpDIS->itemState;
455
456 // first, draw the background
457 HBRUSH hbrushBackground = ::CreateSolidBrush(colBg);
458
459 FillRect(hdc, &rectBtn, hbrushBackground);
460
461 // draw the border for the current state
462 bool selected = (state & ODS_SELECTED) != 0;
463 if ( !selected )
464 {
465 wxPanel *panel = wxDynamicCast(GetParent(), wxPanel);
466 if ( panel )
467 {
468 selected = panel->GetDefaultItem() == this;
469 }
470 }
471 bool pushed = (SendMessage(GetHwnd(), BM_GETSTATE, 0, 0) & BST_PUSHED) != 0;
472
473 DrawButtonFrame(hdc, rectBtn, selected, pushed);
474
475 // draw the focus rect if needed
476 if ( state & ODS_FOCUS )
477 {
478 RECT rectFocus;
479 CopyRect(&rectFocus, &rectBtn);
480
481 // I don't know where does this constant come from, but this is how
482 // Windows draws them
483 InflateRect(&rectFocus, -4, -4);
484
485 DrawFocusRect(hdc, &rectFocus);
486 }
487
9750fc42
VZ
488 if ( pushed )
489 {
490 // the label is shifted by 1 pixel to create "pushed" effect
491 OffsetRect(&rectBtn, 1, 1);
492 }
493
cd0b1709
VZ
494 DrawButtonText(hdc, &rectBtn, GetLabel(),
495 state & ODS_DISABLED ? GetSysColor(COLOR_GRAYTEXT)
496 : colFg);
497
498 ::DeleteObject(hbrushBackground);
499
cd0b1709
VZ
500 return TRUE;
501}
502
503#endif // __WIN32__
1e6feb95
VZ
504
505#endif // wxUSE_BUTTON
506