]> git.saurik.com Git - wxWidgets.git/blame - src/univ/button.cpp
copying as much values from the dc as possible
[wxWidgets.git] / src / univ / button.cpp
CommitLineData
1e6feb95
VZ
1/////////////////////////////////////////////////////////////////////////////
2// Name: univ/button.cpp
3// Purpose: wxButton
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 14.08.00
7// RCS-ID: $Id$
442b35b5 8// Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
65571936 9// Licence: wxWindows licence
1e6feb95
VZ
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
1e6feb95
VZ
20#include "wx/wxprec.h"
21
22#ifdef __BORLANDC__
23 #pragma hdrstop
24#endif
25
26#if wxUSE_BUTTON
27
28#ifndef WX_PRECOMP
29 #include "wx/dcclient.h"
30 #include "wx/dcscreen.h"
31 #include "wx/button.h"
32 #include "wx/validate.h"
c15521c6 33 #include "wx/settings.h"
1e6feb95
VZ
34#endif
35
36#include "wx/univ/renderer.h"
37#include "wx/univ/inphand.h"
38#include "wx/univ/theme.h"
193e19cf 39#include "wx/univ/colschem.h"
5f7bcb48 40#include "wx/stockitem.h"
1e6feb95 41
9467bdb7
VZ
42// ----------------------------------------------------------------------------
43// wxStdButtonInputHandler: translates SPACE and ENTER keys and the left mouse
44// click into button press/release actions
45// ----------------------------------------------------------------------------
46
47class WXDLLEXPORT wxStdButtonInputHandler : public wxStdInputHandler
48{
49public:
50 wxStdButtonInputHandler(wxInputHandler *inphand);
51
52 virtual bool HandleKey(wxInputConsumer *consumer,
53 const wxKeyEvent& event,
54 bool pressed);
55 virtual bool HandleMouse(wxInputConsumer *consumer,
56 const wxMouseEvent& event);
57 virtual bool HandleMouseMove(wxInputConsumer *consumer,
58 const wxMouseEvent& event);
59 virtual bool HandleFocus(wxInputConsumer *consumer,
60 const wxFocusEvent& event);
61 virtual bool HandleActivation(wxInputConsumer *consumer, bool activated);
62
63private:
64 // the window (button) which has capture or NULL and the flag telling if
65 // the mouse is inside the button which captured it or not
66 wxWindow *m_winCapture;
67 bool m_winHasMouse;
68};
69
1e6feb95
VZ
70// ----------------------------------------------------------------------------
71// constants
72// ----------------------------------------------------------------------------
73
74// default margins around the image
ea1ad04b 75static const wxCoord DEFAULT_BTN_MARGIN_X = 0; // We should give space for the border, at least.
1e6feb95
VZ
76static const wxCoord DEFAULT_BTN_MARGIN_Y = 0;
77
78// ============================================================================
79// implementation
80// ============================================================================
81
82IMPLEMENT_DYNAMIC_CLASS(wxButton, wxControl)
83
84// ----------------------------------------------------------------------------
85// creation
86// ----------------------------------------------------------------------------
87
88void wxButton::Init()
89{
90 m_isPressed =
a290fa5a 91 m_isDefault = false;
1e6feb95
VZ
92}
93
94bool wxButton::Create(wxWindow *parent,
95 wxWindowID id,
96 const wxBitmap& bitmap,
5f7bcb48 97 const wxString &lbl,
1e6feb95
VZ
98 const wxPoint &pos,
99 const wxSize &size,
100 long style,
101 const wxValidator& validator,
102 const wxString &name)
103{
5f7bcb48
VS
104 wxString label(lbl);
105 if (label.empty() && wxIsStockID(id))
106 label = wxGetStockLabel(id);
107
88d2e567 108 long ctrl_style = style & ~wxBU_ALIGN_MASK;
390f8114 109 ctrl_style = ctrl_style & ~wxALIGN_MASK;
041ae202
WS
110
111 if((style & wxBU_RIGHT) == wxBU_RIGHT)
112 ctrl_style |= wxALIGN_RIGHT;
113 else if((style & wxBU_LEFT) == wxBU_LEFT)
114 ctrl_style |= wxALIGN_LEFT;
115 else
116 ctrl_style |= wxALIGN_CENTRE_HORIZONTAL;
1e6feb95 117
041ae202
WS
118 if((style & wxBU_TOP) == wxBU_TOP)
119 ctrl_style |= wxALIGN_TOP;
120 else if((style & wxBU_BOTTOM) == wxBU_BOTTOM)
121 ctrl_style |= wxALIGN_BOTTOM;
122 else
123 ctrl_style |= wxALIGN_CENTRE_VERTICAL;
124
125 if ( !wxControl::Create(parent, id, pos, size, ctrl_style, validator, name) )
a290fa5a 126 return false;
1e6feb95
VZ
127
128 SetLabel(label);
129 SetImageLabel(bitmap);
130 // SetBestSize(size); -- called by SetImageLabel()
131
132 CreateInputHandler(wxINP_HANDLER_BUTTON);
133
a290fa5a 134 return true;
1e6feb95
VZ
135}
136
137wxButton::~wxButton()
138{
139}
140
141// ----------------------------------------------------------------------------
142// size management
143// ----------------------------------------------------------------------------
144
145/* static */
146wxSize wxButtonBase::GetDefaultSize()
147{
148 static wxSize s_sizeBtn;
149
150 if ( s_sizeBtn.x == 0 )
151 {
152 wxScreenDC dc;
153
154 // this corresponds more or less to wxMSW standard in Win32 theme (see
155 // wxWin32Renderer::AdjustSize())
61a83c1c
JS
156// s_sizeBtn.x = 8*dc.GetCharWidth();
157// s_sizeBtn.y = (11*dc.GetCharHeight())/10 + 2;
158 // Otto Wyss, Patch 664399
159 s_sizeBtn.x = dc.GetCharWidth()*10 + 2;
160 s_sizeBtn.y = dc.GetCharHeight()*11/10 + 2;
1e6feb95
VZ
161 }
162
163 return s_sizeBtn;
164}
165
166wxSize wxButton::DoGetBestClientSize() const
167{
168 wxClientDC dc(wxConstCast(this, wxButton));
169 wxCoord width, height;
170 dc.GetMultiLineTextExtent(GetLabel(), &width, &height);
171
172 if ( m_bitmap.Ok() )
173 {
174 // allocate extra space for the bitmap
175 wxCoord heightBmp = m_bitmap.GetHeight() + 2*m_marginBmpY;
176 if ( height < heightBmp )
177 height = heightBmp;
178
179 width += m_bitmap.GetWidth() + 2*m_marginBmpX;
180 }
181
041ae202 182 // The default size should not be adjusted, so the code is moved into the
61a83c1c
JS
183 // renderer. This is conceptual wrong but currently the only solution.
184 // (Otto Wyss, Patch 664399)
185
186/*
1e6feb95 187 // for compatibility with other ports, the buttons default size is never
c15521c6 188 // less than the standard one, but not when display not PDAs.
41fecb44 189 if (wxSystemSettings::GetScreenType() > wxSYS_SCREEN_PDA)
7ef8bfc4 190 {
c15521c6
RR
191 if ( !(GetWindowStyle() & wxBU_EXACTFIT) )
192 {
193 wxSize szDef = GetDefaultSize();
194 if ( width < szDef.x )
195 width = szDef.x;
196 }
7ef8bfc4 197 }
61a83c1c 198*/
1e6feb95
VZ
199 return wxSize(width, height);
200}
201
202// ----------------------------------------------------------------------------
203// drawing
204// ----------------------------------------------------------------------------
205
206void wxButton::DoDraw(wxControlRenderer *renderer)
207{
e4606ed9
VZ
208 if ( !(GetWindowStyle() & wxBORDER_NONE) )
209 {
210 renderer->DrawButtonBorder();
211 }
212
1e6feb95
VZ
213 renderer->DrawLabel(m_bitmap, m_marginBmpX, m_marginBmpY);
214}
215
193e19cf
RR
216bool wxButton::DoDrawBackground(wxDC& dc)
217{
218 wxRect rect;
219 wxSize size = GetSize();
220 rect.width = size.x;
221 rect.height = size.y;
041ae202 222
193e19cf
RR
223 if ( GetBackgroundBitmap().Ok() )
224 {
225 // get the bitmap and the flags
226 int alignment;
227 wxStretch stretch;
228 wxBitmap bmp = GetBackgroundBitmap(&alignment, &stretch);
229 wxControlRenderer::DrawBitmap(dc, bmp, rect, alignment, stretch);
230 }
231 else
232 {
233 m_renderer->DrawButtonSurface(dc, wxTHEME_BG_COLOUR(this),
234 rect, GetStateFlags());
235 }
236
a290fa5a 237 return true;
193e19cf
RR
238}
239
1e6feb95
VZ
240// ----------------------------------------------------------------------------
241// input processing
242// ----------------------------------------------------------------------------
243
244void wxButton::Press()
245{
246 if ( !m_isPressed )
247 {
a290fa5a 248 m_isPressed = true;
1e6feb95
VZ
249
250 Refresh();
251 }
252}
253
254void wxButton::Release()
255{
256 if ( m_isPressed )
257 {
a290fa5a 258 m_isPressed = false;
1e6feb95
VZ
259
260 Refresh();
261 }
262}
263
264void wxButton::Toggle()
265{
266 if ( m_isPressed )
267 Release();
268 else
269 Press();
270
271 if ( !m_isPressed )
272 {
273 // releasing button after it had been pressed generates a click event
274 Click();
275 }
276}
277
278void wxButton::Click()
279{
280 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
281 InitCommandEvent(event);
282 Command(event);
283}
284
285bool wxButton::PerformAction(const wxControlAction& action,
286 long numArg,
287 const wxString& strArg)
288{
289 if ( action == wxACTION_BUTTON_TOGGLE )
290 Toggle();
291 else if ( action == wxACTION_BUTTON_CLICK )
292 Click();
293 else if ( action == wxACTION_BUTTON_PRESS )
294 Press();
295 else if ( action == wxACTION_BUTTON_RELEASE )
296 Release();
297 else
298 return wxControl::PerformAction(action, numArg, strArg);
299
a290fa5a 300 return true;
1e6feb95
VZ
301}
302
9467bdb7
VZ
303/* static */
304wxInputHandler *wxButton::GetStdInputHandler(wxInputHandler *handlerDef)
305{
306 static wxStdButtonInputHandler s_handlerBtn(handlerDef);
307
308 return &s_handlerBtn;
309}
310
1e6feb95
VZ
311// ----------------------------------------------------------------------------
312// misc
313// ----------------------------------------------------------------------------
314
315void wxButton::SetImageLabel(const wxBitmap& bitmap)
316{
317 m_bitmap = bitmap;
318
319 SetImageMargins(DEFAULT_BTN_MARGIN_X, DEFAULT_BTN_MARGIN_Y);
320}
321
322void wxButton::SetImageMargins(wxCoord x, wxCoord y)
323{
34d26f42
RR
324 m_marginBmpX = x + 2;
325 m_marginBmpY = y + 2;
041ae202 326
1e6feb95
VZ
327 SetBestSize(wxDefaultSize);
328}
329
330void wxButton::SetDefault()
331{
a290fa5a 332 m_isDefault = true;
1e6feb95
VZ
333}
334
335// ============================================================================
336// wxStdButtonInputHandler
337// ============================================================================
338
339wxStdButtonInputHandler::wxStdButtonInputHandler(wxInputHandler *handler)
340 : wxStdInputHandler(handler)
341{
342 m_winCapture = NULL;
a290fa5a 343 m_winHasMouse = false;
1e6feb95
VZ
344}
345
23645bfa 346bool wxStdButtonInputHandler::HandleKey(wxInputConsumer *consumer,
1e6feb95
VZ
347 const wxKeyEvent& event,
348 bool pressed)
349{
350 int keycode = event.GetKeyCode();
351 if ( keycode == WXK_SPACE || keycode == WXK_RETURN )
352 {
23645bfa 353 consumer->PerformAction(wxACTION_BUTTON_TOGGLE);
1e6feb95 354
a290fa5a 355 return true;
1e6feb95
VZ
356 }
357
23645bfa 358 return wxStdInputHandler::HandleKey(consumer, event, pressed);
1e6feb95
VZ
359}
360
23645bfa 361bool wxStdButtonInputHandler::HandleMouse(wxInputConsumer *consumer,
1e6feb95
VZ
362 const wxMouseEvent& event)
363{
364 // the button has 2 states: pressed and normal with the following
365 // transitions between them:
366 //
367 // normal -> left down -> capture mouse and go to pressed state
368 // pressed -> left up inside -> generate click -> go to normal
369 // outside ------------------>
370 //
371 // the other mouse buttons are ignored
372 if ( event.Button(1) )
373 {
96e3c3d4 374 if ( event.LeftDown() || event.LeftDClick() )
1e6feb95 375 {
23645bfa 376 m_winCapture = consumer->GetInputWindow();
1e6feb95 377 m_winCapture->CaptureMouse();
a290fa5a 378 m_winHasMouse = true;
1e6feb95 379
23645bfa 380 consumer->PerformAction(wxACTION_BUTTON_PRESS);
1e6feb95 381
a290fa5a 382 return true;
1e6feb95 383 }
96e3c3d4 384 else if ( event.LeftUp() )
1e6feb95
VZ
385 {
386 if ( m_winCapture )
387 {
388 m_winCapture->ReleaseMouse();
389 m_winCapture = NULL;
390 }
391
392 if ( m_winHasMouse )
393 {
394 // this will generate a click event
23645bfa 395 consumer->PerformAction(wxACTION_BUTTON_TOGGLE);
1e6feb95 396
a290fa5a 397 return true;
1e6feb95
VZ
398 }
399 //else: the mouse was released outside the window, this doesn't
400 // count as a click
401 }
96e3c3d4 402 //else: don't do anything special about the double click
1e6feb95
VZ
403 }
404
23645bfa 405 return wxStdInputHandler::HandleMouse(consumer, event);
1e6feb95
VZ
406}
407
23645bfa 408bool wxStdButtonInputHandler::HandleMouseMove(wxInputConsumer *consumer,
1e6feb95
VZ
409 const wxMouseEvent& event)
410{
411 // we only have to do something when the mouse leaves/enters the pressed
412 // button and don't care about the other ones
413 if ( event.GetEventObject() == m_winCapture )
414 {
415 // leaving the button should remove its pressed state
416 if ( event.Leaving() )
417 {
418 // remember that the mouse is now outside
a290fa5a 419 m_winHasMouse = false;
1e6feb95
VZ
420
421 // we do have a pressed button, so release it
a290fa5a 422 consumer->GetInputWindow()->SetCurrent(false);
23645bfa 423 consumer->PerformAction(wxACTION_BUTTON_RELEASE);
1e6feb95 424
a290fa5a 425 return true;
1e6feb95
VZ
426 }
427 // and entering it back should make it pressed again if it had been
428 // pressed
429 else if ( event.Entering() )
430 {
431 // the mouse is (back) inside the button
a290fa5a 432 m_winHasMouse = true;
1e6feb95
VZ
433
434 // we did have a pressed button which we released when leaving the
435 // window, press it again
a290fa5a 436 consumer->GetInputWindow()->SetCurrent(true);
23645bfa 437 consumer->PerformAction(wxACTION_BUTTON_PRESS);
1e6feb95 438
a290fa5a 439 return true;
1e6feb95
VZ
440 }
441 }
442
23645bfa 443 return wxStdInputHandler::HandleMouseMove(consumer, event);
1e6feb95
VZ
444}
445
61fef19b
VZ
446bool wxStdButtonInputHandler::HandleFocus(wxInputConsumer * WXUNUSED(consumer),
447 const wxFocusEvent& WXUNUSED(event))
1e6feb95 448{
a290fa5a 449 // buttons change appearance when they get/lose focus, so return true to
1e6feb95 450 // refresh
a290fa5a 451 return true;
1e6feb95
VZ
452}
453
23645bfa 454bool wxStdButtonInputHandler::HandleActivation(wxInputConsumer *consumer,
61fef19b 455 bool WXUNUSED(activated))
1e6feb95
VZ
456{
457 // the default button changes appearance when the app is [de]activated, so
a290fa5a 458 // return true to refresh
23645bfa 459 return wxStaticCast(consumer->GetInputWindow(), wxButton)->IsDefault();
1e6feb95
VZ
460}
461
462#endif // wxUSE_BUTTON
463