Buttons need to be small on PDAs.
[wxWidgets.git] / src / univ / button.cpp
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$
8 // Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
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
48 static const wxCoord DEFAULT_BTN_MARGIN_X = 0; // We should give space for the border, at least.
49 static const wxCoord DEFAULT_BTN_MARGIN_Y = 0;
50
51 // ============================================================================
52 // implementation
53 // ============================================================================
54
55 IMPLEMENT_DYNAMIC_CLASS(wxButton, wxControl)
56
57 // ----------------------------------------------------------------------------
58 // creation
59 // ----------------------------------------------------------------------------
60
61 void wxButton::Init()
62 {
63 m_isPressed =
64 m_isDefault = FALSE;
65 }
66
67 bool 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
95 wxButton::~wxButton()
96 {
97 }
98
99 // ----------------------------------------------------------------------------
100 // size management
101 // ----------------------------------------------------------------------------
102
103 /* static */
104 wxSize 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
121 wxSize 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 #ifndef __WXX11__
140 if ( !(GetWindowStyle() & wxBU_EXACTFIT) )
141 {
142 wxSize szDef = GetDefaultSize();
143 if ( width < szDef.x )
144 width = szDef.x;
145 }
146 #endif
147
148 return wxSize(width, height);
149 }
150
151 // ----------------------------------------------------------------------------
152 // drawing
153 // ----------------------------------------------------------------------------
154
155 void wxButton::DoDraw(wxControlRenderer *renderer)
156 {
157 if ( !(GetWindowStyle() & wxBORDER_NONE) )
158 {
159 renderer->DrawButtonBorder();
160 }
161
162 renderer->DrawLabel(m_bitmap, m_marginBmpX, m_marginBmpY);
163 }
164
165 // ----------------------------------------------------------------------------
166 // input processing
167 // ----------------------------------------------------------------------------
168
169 void wxButton::Press()
170 {
171 if ( !m_isPressed )
172 {
173 m_isPressed = TRUE;
174
175 Refresh();
176 }
177 }
178
179 void wxButton::Release()
180 {
181 if ( m_isPressed )
182 {
183 m_isPressed = FALSE;
184
185 Refresh();
186 }
187 }
188
189 void wxButton::Toggle()
190 {
191 if ( m_isPressed )
192 Release();
193 else
194 Press();
195
196 if ( !m_isPressed )
197 {
198 // releasing button after it had been pressed generates a click event
199 Click();
200 }
201 }
202
203 void wxButton::Click()
204 {
205 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
206 InitCommandEvent(event);
207 Command(event);
208 }
209
210 bool wxButton::PerformAction(const wxControlAction& action,
211 long numArg,
212 const wxString& strArg)
213 {
214 if ( action == wxACTION_BUTTON_TOGGLE )
215 Toggle();
216 else if ( action == wxACTION_BUTTON_CLICK )
217 Click();
218 else if ( action == wxACTION_BUTTON_PRESS )
219 Press();
220 else if ( action == wxACTION_BUTTON_RELEASE )
221 Release();
222 else
223 return wxControl::PerformAction(action, numArg, strArg);
224
225 return TRUE;
226 }
227
228 // ----------------------------------------------------------------------------
229 // misc
230 // ----------------------------------------------------------------------------
231
232 void wxButton::SetImageLabel(const wxBitmap& bitmap)
233 {
234 m_bitmap = bitmap;
235
236 SetImageMargins(DEFAULT_BTN_MARGIN_X, DEFAULT_BTN_MARGIN_Y);
237 }
238
239 void wxButton::SetImageMargins(wxCoord x, wxCoord y)
240 {
241 m_marginBmpX = x + 2;
242 m_marginBmpY = y + 2;
243
244 SetBestSize(wxDefaultSize);
245 }
246
247 void wxButton::SetDefault()
248 {
249 m_isDefault = TRUE;
250 }
251
252 // ============================================================================
253 // wxStdButtonInputHandler
254 // ============================================================================
255
256 wxStdButtonInputHandler::wxStdButtonInputHandler(wxInputHandler *handler)
257 : wxStdInputHandler(handler)
258 {
259 m_winCapture = NULL;
260 m_winHasMouse = FALSE;
261 }
262
263 bool wxStdButtonInputHandler::HandleKey(wxInputConsumer *consumer,
264 const wxKeyEvent& event,
265 bool pressed)
266 {
267 int keycode = event.GetKeyCode();
268 if ( keycode == WXK_SPACE || keycode == WXK_RETURN )
269 {
270 consumer->PerformAction(wxACTION_BUTTON_TOGGLE);
271
272 return TRUE;
273 }
274
275 return wxStdInputHandler::HandleKey(consumer, event, pressed);
276 }
277
278 bool wxStdButtonInputHandler::HandleMouse(wxInputConsumer *consumer,
279 const wxMouseEvent& event)
280 {
281 // the button has 2 states: pressed and normal with the following
282 // transitions between them:
283 //
284 // normal -> left down -> capture mouse and go to pressed state
285 // pressed -> left up inside -> generate click -> go to normal
286 // outside ------------------>
287 //
288 // the other mouse buttons are ignored
289 if ( event.Button(1) )
290 {
291 if ( event.LeftDown() || event.LeftDClick() )
292 {
293 m_winCapture = consumer->GetInputWindow();
294 m_winCapture->CaptureMouse();
295 m_winHasMouse = TRUE;
296
297 consumer->PerformAction(wxACTION_BUTTON_PRESS);
298
299 return TRUE;
300 }
301 else if ( event.LeftUp() )
302 {
303 if ( m_winCapture )
304 {
305 m_winCapture->ReleaseMouse();
306 m_winCapture = NULL;
307 }
308
309 if ( m_winHasMouse )
310 {
311 // this will generate a click event
312 consumer->PerformAction(wxACTION_BUTTON_TOGGLE);
313
314 return TRUE;
315 }
316 //else: the mouse was released outside the window, this doesn't
317 // count as a click
318 }
319 //else: don't do anything special about the double click
320 }
321
322 return wxStdInputHandler::HandleMouse(consumer, event);
323 }
324
325 bool wxStdButtonInputHandler::HandleMouseMove(wxInputConsumer *consumer,
326 const wxMouseEvent& event)
327 {
328 // we only have to do something when the mouse leaves/enters the pressed
329 // button and don't care about the other ones
330 if ( event.GetEventObject() == m_winCapture )
331 {
332 // leaving the button should remove its pressed state
333 if ( event.Leaving() )
334 {
335 // remember that the mouse is now outside
336 m_winHasMouse = FALSE;
337
338 // we do have a pressed button, so release it
339 consumer->GetInputWindow()->SetCurrent(FALSE);
340 consumer->PerformAction(wxACTION_BUTTON_RELEASE);
341
342 return TRUE;
343 }
344 // and entering it back should make it pressed again if it had been
345 // pressed
346 else if ( event.Entering() )
347 {
348 // the mouse is (back) inside the button
349 m_winHasMouse = TRUE;
350
351 // we did have a pressed button which we released when leaving the
352 // window, press it again
353 consumer->GetInputWindow()->SetCurrent(TRUE);
354 consumer->PerformAction(wxACTION_BUTTON_PRESS);
355
356 return TRUE;
357 }
358 }
359
360 return wxStdInputHandler::HandleMouseMove(consumer, event);
361 }
362
363 bool wxStdButtonInputHandler::HandleFocus(wxInputConsumer *consumer,
364 const wxFocusEvent& event)
365 {
366 // buttons change appearance when they get/lose focus, so return TRUE to
367 // refresh
368 return TRUE;
369 }
370
371 bool wxStdButtonInputHandler::HandleActivation(wxInputConsumer *consumer,
372 bool activated)
373 {
374 // the default button changes appearance when the app is [de]activated, so
375 // return TRUE to refresh
376 return wxStaticCast(consumer->GetInputWindow(), wxButton)->IsDefault();
377 }
378
379 #endif // wxUSE_BUTTON
380