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