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