]> git.saurik.com Git - wxWidgets.git/blob - src/univ/button.cpp
avoiding nesting dcs on the same window concurrently
[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 #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"
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
42 // ----------------------------------------------------------------------------
43 // wxStdButtonInputHandler: translates SPACE and ENTER keys and the left mouse
44 // click into button press/release actions
45 // ----------------------------------------------------------------------------
46
47 class WXDLLEXPORT wxStdButtonInputHandler : public wxStdInputHandler
48 {
49 public:
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
63 private:
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
70 // ----------------------------------------------------------------------------
71 // constants
72 // ----------------------------------------------------------------------------
73
74 // default margins around the image
75 static const wxCoord DEFAULT_BTN_MARGIN_X = 0; // We should give space for the border, at least.
76 static const wxCoord DEFAULT_BTN_MARGIN_Y = 0;
77
78 // ============================================================================
79 // implementation
80 // ============================================================================
81
82 IMPLEMENT_DYNAMIC_CLASS(wxButton, wxControl)
83
84 // ----------------------------------------------------------------------------
85 // creation
86 // ----------------------------------------------------------------------------
87
88 void wxButton::Init()
89 {
90 m_isPressed =
91 m_isDefault = false;
92 }
93
94 bool wxButton::Create(wxWindow *parent,
95 wxWindowID id,
96 const wxBitmap& bitmap,
97 const wxString &lbl,
98 const wxPoint &pos,
99 const wxSize &size,
100 long style,
101 const wxValidator& validator,
102 const wxString &name)
103 {
104 wxString label(lbl);
105 if (label.empty() && wxIsStockID(id))
106 label = wxGetStockLabel(id);
107
108 long ctrl_style = style & ~wxBU_ALIGN_MASK;
109 ctrl_style = ctrl_style & ~wxALIGN_MASK;
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;
117
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) )
126 return false;
127
128 SetLabel(label);
129 SetImageLabel(bitmap);
130 // SetBestSize(size); -- called by SetImageLabel()
131
132 CreateInputHandler(wxINP_HANDLER_BUTTON);
133
134 return true;
135 }
136
137 wxButton::~wxButton()
138 {
139 }
140
141 // ----------------------------------------------------------------------------
142 // size management
143 // ----------------------------------------------------------------------------
144
145 /* static */
146 wxSize 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())
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;
161 }
162
163 return s_sizeBtn;
164 }
165
166 wxSize 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
182 // The default size should not be adjusted, so the code is moved into the
183 // renderer. This is conceptual wrong but currently the only solution.
184 // (Otto Wyss, Patch 664399)
185
186 /*
187 // for compatibility with other ports, the buttons default size is never
188 // less than the standard one, but not when display not PDAs.
189 if (wxSystemSettings::GetScreenType() > wxSYS_SCREEN_PDA)
190 {
191 if ( !(GetWindowStyle() & wxBU_EXACTFIT) )
192 {
193 wxSize szDef = GetDefaultSize();
194 if ( width < szDef.x )
195 width = szDef.x;
196 }
197 }
198 */
199 return wxSize(width, height);
200 }
201
202 // ----------------------------------------------------------------------------
203 // drawing
204 // ----------------------------------------------------------------------------
205
206 void wxButton::DoDraw(wxControlRenderer *renderer)
207 {
208 if ( !(GetWindowStyle() & wxBORDER_NONE) )
209 {
210 renderer->DrawButtonBorder();
211 }
212
213 renderer->DrawLabel(m_bitmap, m_marginBmpX, m_marginBmpY);
214 }
215
216 bool wxButton::DoDrawBackground(wxDC& dc)
217 {
218 wxRect rect;
219 wxSize size = GetSize();
220 rect.width = size.x;
221 rect.height = size.y;
222
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
237 return true;
238 }
239
240 // ----------------------------------------------------------------------------
241 // input processing
242 // ----------------------------------------------------------------------------
243
244 void wxButton::Press()
245 {
246 if ( !m_isPressed )
247 {
248 m_isPressed = true;
249
250 Refresh();
251 }
252 }
253
254 void wxButton::Release()
255 {
256 if ( m_isPressed )
257 {
258 m_isPressed = false;
259
260 Refresh();
261 }
262 }
263
264 void 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
278 void wxButton::Click()
279 {
280 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
281 InitCommandEvent(event);
282 Command(event);
283 }
284
285 bool 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
300 return true;
301 }
302
303 /* static */
304 wxInputHandler *wxButton::GetStdInputHandler(wxInputHandler *handlerDef)
305 {
306 static wxStdButtonInputHandler s_handlerBtn(handlerDef);
307
308 return &s_handlerBtn;
309 }
310
311 // ----------------------------------------------------------------------------
312 // misc
313 // ----------------------------------------------------------------------------
314
315 void wxButton::SetImageLabel(const wxBitmap& bitmap)
316 {
317 m_bitmap = bitmap;
318
319 SetImageMargins(DEFAULT_BTN_MARGIN_X, DEFAULT_BTN_MARGIN_Y);
320 }
321
322 void wxButton::SetImageMargins(wxCoord x, wxCoord y)
323 {
324 m_marginBmpX = x + 2;
325 m_marginBmpY = y + 2;
326
327 SetBestSize(wxDefaultSize);
328 }
329
330 void wxButton::SetDefault()
331 {
332 m_isDefault = true;
333 }
334
335 // ============================================================================
336 // wxStdButtonInputHandler
337 // ============================================================================
338
339 wxStdButtonInputHandler::wxStdButtonInputHandler(wxInputHandler *handler)
340 : wxStdInputHandler(handler)
341 {
342 m_winCapture = NULL;
343 m_winHasMouse = false;
344 }
345
346 bool wxStdButtonInputHandler::HandleKey(wxInputConsumer *consumer,
347 const wxKeyEvent& event,
348 bool pressed)
349 {
350 int keycode = event.GetKeyCode();
351 if ( keycode == WXK_SPACE || keycode == WXK_RETURN )
352 {
353 consumer->PerformAction(wxACTION_BUTTON_TOGGLE);
354
355 return true;
356 }
357
358 return wxStdInputHandler::HandleKey(consumer, event, pressed);
359 }
360
361 bool wxStdButtonInputHandler::HandleMouse(wxInputConsumer *consumer,
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 {
374 if ( event.LeftDown() || event.LeftDClick() )
375 {
376 m_winCapture = consumer->GetInputWindow();
377 m_winCapture->CaptureMouse();
378 m_winHasMouse = true;
379
380 consumer->PerformAction(wxACTION_BUTTON_PRESS);
381
382 return true;
383 }
384 else if ( event.LeftUp() )
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
395 consumer->PerformAction(wxACTION_BUTTON_TOGGLE);
396
397 return true;
398 }
399 //else: the mouse was released outside the window, this doesn't
400 // count as a click
401 }
402 //else: don't do anything special about the double click
403 }
404
405 return wxStdInputHandler::HandleMouse(consumer, event);
406 }
407
408 bool wxStdButtonInputHandler::HandleMouseMove(wxInputConsumer *consumer,
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
419 m_winHasMouse = false;
420
421 // we do have a pressed button, so release it
422 consumer->GetInputWindow()->SetCurrent(false);
423 consumer->PerformAction(wxACTION_BUTTON_RELEASE);
424
425 return true;
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
432 m_winHasMouse = true;
433
434 // we did have a pressed button which we released when leaving the
435 // window, press it again
436 consumer->GetInputWindow()->SetCurrent(true);
437 consumer->PerformAction(wxACTION_BUTTON_PRESS);
438
439 return true;
440 }
441 }
442
443 return wxStdInputHandler::HandleMouseMove(consumer, event);
444 }
445
446 bool wxStdButtonInputHandler::HandleFocus(wxInputConsumer * WXUNUSED(consumer),
447 const wxFocusEvent& WXUNUSED(event))
448 {
449 // buttons change appearance when they get/lose focus, so return true to
450 // refresh
451 return true;
452 }
453
454 bool wxStdButtonInputHandler::HandleActivation(wxInputConsumer *consumer,
455 bool WXUNUSED(activated))
456 {
457 // the default button changes appearance when the app is [de]activated, so
458 // return true to refresh
459 return wxStaticCast(consumer->GetInputWindow(), wxButton)->IsDefault();
460 }
461
462 #endif // wxUSE_BUTTON
463