Don't complain under MicroWindows if a wxDC's HDC is NULL - it happens
[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)
1e6feb95
VZ
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20#ifdef __GNUG__
21 #pragma implementation "univbutton.h"
22#endif
23
24#include "wx/wxprec.h"
25
26#ifdef __BORLANDC__
27 #pragma hdrstop
28#endif
29
30#if wxUSE_BUTTON
31
32#ifndef WX_PRECOMP
33 #include "wx/dcclient.h"
34 #include "wx/dcscreen.h"
35 #include "wx/button.h"
36 #include "wx/validate.h"
37#endif
38
39#include "wx/univ/renderer.h"
40#include "wx/univ/inphand.h"
41#include "wx/univ/theme.h"
42
43// ----------------------------------------------------------------------------
44// constants
45// ----------------------------------------------------------------------------
46
47// default margins around the image
48static const wxCoord DEFAULT_BTN_MARGIN_X = 0;
49static const wxCoord DEFAULT_BTN_MARGIN_Y = 0;
50
51// ============================================================================
52// implementation
53// ============================================================================
54
55IMPLEMENT_DYNAMIC_CLASS(wxButton, wxControl)
56
57// ----------------------------------------------------------------------------
58// creation
59// ----------------------------------------------------------------------------
60
61void wxButton::Init()
62{
63 m_isPressed =
64 m_isDefault = FALSE;
65}
66
67bool wxButton::Create(wxWindow *parent,
68 wxWindowID id,
69 const wxBitmap& bitmap,
70 const wxString &label,
71 const wxPoint &pos,
72 const wxSize &size,
73 long style,
74 const wxValidator& validator,
75 const wxString &name)
76{
77 // center label by default
78 if ( !(style & wxALIGN_MASK) )
79 {
80 style |= wxALIGN_CENTRE_HORIZONTAL | wxALIGN_CENTRE_VERTICAL;
81 }
82
83 if ( !wxControl::Create(parent, id, pos, size, style, wxDefaultValidator, name) )
84 return FALSE;
85
86 SetLabel(label);
87 SetImageLabel(bitmap);
88 // SetBestSize(size); -- called by SetImageLabel()
89
90 CreateInputHandler(wxINP_HANDLER_BUTTON);
91
92 return TRUE;
93}
94
95wxButton::~wxButton()
96{
97}
98
99// ----------------------------------------------------------------------------
100// size management
101// ----------------------------------------------------------------------------
102
103/* static */
104wxSize wxButtonBase::GetDefaultSize()
105{
106 static wxSize s_sizeBtn;
107
108 if ( s_sizeBtn.x == 0 )
109 {
110 wxScreenDC dc;
111
112 // this corresponds more or less to wxMSW standard in Win32 theme (see
113 // wxWin32Renderer::AdjustSize())
114 s_sizeBtn.x = 8*dc.GetCharWidth();
115 s_sizeBtn.y = (11*dc.GetCharHeight())/10 + 2;
116 }
117
118 return s_sizeBtn;
119}
120
121wxSize wxButton::DoGetBestClientSize() const
122{
123 wxClientDC dc(wxConstCast(this, wxButton));
124 wxCoord width, height;
125 dc.GetMultiLineTextExtent(GetLabel(), &width, &height);
126
127 if ( m_bitmap.Ok() )
128 {
129 // allocate extra space for the bitmap
130 wxCoord heightBmp = m_bitmap.GetHeight() + 2*m_marginBmpY;
131 if ( height < heightBmp )
132 height = heightBmp;
133
134 width += m_bitmap.GetWidth() + 2*m_marginBmpX;
135 }
136
137 // for compatibility with other ports, the buttons default size is never
138 // less than the standard one
139 wxSize szDef = GetDefaultSize();
140 if ( width < szDef.x )
141 width = szDef.x;
142
143 return wxSize(width, height);
144}
145
146// ----------------------------------------------------------------------------
147// drawing
148// ----------------------------------------------------------------------------
149
150void wxButton::DoDraw(wxControlRenderer *renderer)
151{
152 renderer->DrawButtonBorder();
153 renderer->DrawLabel(m_bitmap, m_marginBmpX, m_marginBmpY);
154}
155
156// ----------------------------------------------------------------------------
157// input processing
158// ----------------------------------------------------------------------------
159
160void wxButton::Press()
161{
162 if ( !m_isPressed )
163 {
164 m_isPressed = TRUE;
165
166 Refresh();
167 }
168}
169
170void wxButton::Release()
171{
172 if ( m_isPressed )
173 {
174 m_isPressed = FALSE;
175
176 Refresh();
177 }
178}
179
180void wxButton::Toggle()
181{
182 if ( m_isPressed )
183 Release();
184 else
185 Press();
186
187 if ( !m_isPressed )
188 {
189 // releasing button after it had been pressed generates a click event
190 Click();
191 }
192}
193
194void wxButton::Click()
195{
196 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
197 InitCommandEvent(event);
198 Command(event);
199}
200
201bool wxButton::PerformAction(const wxControlAction& action,
202 long numArg,
203 const wxString& strArg)
204{
205 if ( action == wxACTION_BUTTON_TOGGLE )
206 Toggle();
207 else if ( action == wxACTION_BUTTON_CLICK )
208 Click();
209 else if ( action == wxACTION_BUTTON_PRESS )
210 Press();
211 else if ( action == wxACTION_BUTTON_RELEASE )
212 Release();
213 else
214 return wxControl::PerformAction(action, numArg, strArg);
215
216 return TRUE;
217}
218
219// ----------------------------------------------------------------------------
220// misc
221// ----------------------------------------------------------------------------
222
223void wxButton::SetImageLabel(const wxBitmap& bitmap)
224{
225 m_bitmap = bitmap;
226
227 SetImageMargins(DEFAULT_BTN_MARGIN_X, DEFAULT_BTN_MARGIN_Y);
228}
229
230void wxButton::SetImageMargins(wxCoord x, wxCoord y)
231{
232 m_marginBmpX = x;
233 m_marginBmpY = y;
234
235 SetBestSize(wxDefaultSize);
236}
237
238void wxButton::SetDefault()
239{
240 m_isDefault = TRUE;
241}
242
243// ============================================================================
244// wxStdButtonInputHandler
245// ============================================================================
246
247wxStdButtonInputHandler::wxStdButtonInputHandler(wxInputHandler *handler)
248 : wxStdInputHandler(handler)
249{
250 m_winCapture = NULL;
251 m_winHasMouse = FALSE;
252}
253
254bool wxStdButtonInputHandler::HandleKey(wxControl *control,
255 const wxKeyEvent& event,
256 bool pressed)
257{
258 int keycode = event.GetKeyCode();
259 if ( keycode == WXK_SPACE || keycode == WXK_RETURN )
260 {
261 control->PerformAction(wxACTION_BUTTON_TOGGLE);
262
263 return TRUE;
264 }
265
266 return wxStdInputHandler::HandleKey(control, event, pressed);
267}
268
269bool wxStdButtonInputHandler::HandleMouse(wxControl *control,
270 const wxMouseEvent& event)
271{
272 // the button has 2 states: pressed and normal with the following
273 // transitions between them:
274 //
275 // normal -> left down -> capture mouse and go to pressed state
276 // pressed -> left up inside -> generate click -> go to normal
277 // outside ------------------>
278 //
279 // the other mouse buttons are ignored
280 if ( event.Button(1) )
281 {
282 if ( event.ButtonDown(1) )
283 {
284 m_winCapture = control;
285 m_winCapture->CaptureMouse();
286 m_winHasMouse = TRUE;
287
288 control->PerformAction(wxACTION_BUTTON_PRESS);
289
290 return TRUE;
291 }
292 else // up
293 {
294 if ( m_winCapture )
295 {
296 m_winCapture->ReleaseMouse();
297 m_winCapture = NULL;
298 }
299
300 if ( m_winHasMouse )
301 {
302 // this will generate a click event
303 control->PerformAction(wxACTION_BUTTON_TOGGLE);
304
305 return TRUE;
306 }
307 //else: the mouse was released outside the window, this doesn't
308 // count as a click
309 }
310 }
311
312 return wxStdInputHandler::HandleMouse(control, event);
313}
314
315bool wxStdButtonInputHandler::HandleMouseMove(wxControl *control,
316 const wxMouseEvent& event)
317{
318 // we only have to do something when the mouse leaves/enters the pressed
319 // button and don't care about the other ones
320 if ( event.GetEventObject() == m_winCapture )
321 {
322 // leaving the button should remove its pressed state
323 if ( event.Leaving() )
324 {
325 // remember that the mouse is now outside
326 m_winHasMouse = FALSE;
327
328 // we do have a pressed button, so release it
329 control->SetCurrent(FALSE);
330 control->PerformAction(wxACTION_BUTTON_RELEASE);
331
332 return TRUE;
333 }
334 // and entering it back should make it pressed again if it had been
335 // pressed
336 else if ( event.Entering() )
337 {
338 // the mouse is (back) inside the button
339 m_winHasMouse = TRUE;
340
341 // we did have a pressed button which we released when leaving the
342 // window, press it again
343 control->SetCurrent(TRUE);
344 control->PerformAction(wxACTION_BUTTON_PRESS);
345
346 return TRUE;
347 }
348 }
349
350 return wxStdInputHandler::HandleMouseMove(control, event);
351}
352
353bool wxStdButtonInputHandler::HandleFocus(wxControl *control,
354 const wxFocusEvent& event)
355{
356 // buttons change appearance when they get/lose focus, so return TRUE to
357 // refresh
358 return TRUE;
359}
360
361bool wxStdButtonInputHandler::HandleActivation(wxControl *control,
362 bool activated)
363{
364 // the default button changes appearance when the app is [de]activated, so
365 // return TRUE to refresh
366 return wxStaticCast(control, wxButton)->IsDefault();
367}
368
369#endif // wxUSE_BUTTON
370