]> git.saurik.com Git - wxWidgets.git/blob - src/univ/button.cpp
follow up parent chain to properly support modal dialog parents, see #15383
[wxWidgets.git] / src / univ / button.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/univ/button.cpp
3 // Purpose: wxButton
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 14.08.00
7 // Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 #include "wx/wxprec.h"
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 #if wxUSE_BUTTON
26
27 #ifndef WX_PRECOMP
28 #include "wx/dcclient.h"
29 #include "wx/dcscreen.h"
30 #include "wx/button.h"
31 #include "wx/validate.h"
32 #include "wx/settings.h"
33 #endif
34
35 #include "wx/univ/renderer.h"
36 #include "wx/univ/inphand.h"
37 #include "wx/univ/theme.h"
38 #include "wx/univ/colschem.h"
39 #include "wx/stockitem.h"
40
41 // ----------------------------------------------------------------------------
42 // wxStdButtonInputHandler: translates SPACE and ENTER keys and the left mouse
43 // click into button press/release actions
44 // ----------------------------------------------------------------------------
45
46 class WXDLLEXPORT wxStdButtonInputHandler : public wxStdInputHandler
47 {
48 public:
49 wxStdButtonInputHandler(wxInputHandler *inphand);
50
51 virtual bool HandleKey(wxInputConsumer *consumer,
52 const wxKeyEvent& event,
53 bool pressed);
54 virtual bool HandleMouse(wxInputConsumer *consumer,
55 const wxMouseEvent& event);
56 virtual bool HandleMouseMove(wxInputConsumer *consumer,
57 const wxMouseEvent& event);
58 virtual bool HandleFocus(wxInputConsumer *consumer,
59 const wxFocusEvent& event);
60 virtual bool HandleActivation(wxInputConsumer *consumer, bool activated);
61
62 private:
63 // the window (button) which has capture or NULL and the flag telling if
64 // the mouse is inside the button which captured it or not
65 wxWindow *m_winCapture;
66 bool m_winHasMouse;
67 };
68
69 // ----------------------------------------------------------------------------
70 // constants
71 // ----------------------------------------------------------------------------
72
73 // default margins around the image
74 static const wxCoord DEFAULT_BTN_MARGIN_X = 0; // We should give space for the border, at least.
75 static const wxCoord DEFAULT_BTN_MARGIN_Y = 0;
76
77 // ============================================================================
78 // implementation
79 // ============================================================================
80
81 // ----------------------------------------------------------------------------
82 // creation
83 // ----------------------------------------------------------------------------
84
85 void wxButton::Init()
86 {
87 m_isPressed =
88 m_isDefault = false;
89 }
90
91 bool wxButton::Create(wxWindow *parent,
92 wxWindowID id,
93 const wxBitmap& bitmap,
94 const wxString &lbl,
95 const wxPoint &pos,
96 const wxSize &size,
97 long style,
98 const wxValidator& validator,
99 const wxString &name)
100 {
101 wxString label(lbl);
102 if (label.empty() && wxIsStockID(id))
103 label = wxGetStockLabel(id);
104
105 long ctrl_style = style & ~wxBU_ALIGN_MASK;
106 ctrl_style = ctrl_style & ~wxALIGN_MASK;
107
108 if((style & wxBU_RIGHT) == wxBU_RIGHT)
109 ctrl_style |= wxALIGN_RIGHT;
110 else if((style & wxBU_LEFT) == wxBU_LEFT)
111 ctrl_style |= wxALIGN_LEFT;
112 else
113 ctrl_style |= wxALIGN_CENTRE_HORIZONTAL;
114
115 if((style & wxBU_TOP) == wxBU_TOP)
116 ctrl_style |= wxALIGN_TOP;
117 else if((style & wxBU_BOTTOM) == wxBU_BOTTOM)
118 ctrl_style |= wxALIGN_BOTTOM;
119 else
120 ctrl_style |= wxALIGN_CENTRE_VERTICAL;
121
122 if ( !wxControl::Create(parent, id, pos, size, ctrl_style, validator, name) )
123 return false;
124
125 SetLabel(label);
126
127 if (bitmap.IsOk())
128 SetBitmap(bitmap); // SetInitialSize called by SetBitmap()
129 else
130 SetInitialSize(size);
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.IsOk() )
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->DrawButtonLabel(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().IsOk() )
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_BUTTON, 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::DoSetBitmap(const wxBitmap& bitmap, State which)
316 {
317 // we support only one bitmap right now, although this wouldn't be
318 // difficult to change
319 if ( which == State_Normal )
320 m_bitmap = bitmap;
321
322 SetBitmapMargins(DEFAULT_BTN_MARGIN_X, DEFAULT_BTN_MARGIN_Y);
323 }
324
325 void wxButton::DoSetBitmapMargins(wxCoord x, wxCoord y)
326 {
327 m_marginBmpX = x + 2;
328 m_marginBmpY = y + 2;
329
330 SetInitialSize(wxDefaultSize);
331 }
332
333 wxWindow *wxButton::SetDefault()
334 {
335 m_isDefault = true;
336
337 return wxButtonBase::SetDefault();
338 }
339
340 // ============================================================================
341 // wxStdButtonInputHandler
342 // ============================================================================
343
344 wxStdButtonInputHandler::wxStdButtonInputHandler(wxInputHandler *handler)
345 : wxStdInputHandler(handler)
346 {
347 m_winCapture = NULL;
348 m_winHasMouse = false;
349 }
350
351 bool wxStdButtonInputHandler::HandleKey(wxInputConsumer *consumer,
352 const wxKeyEvent& event,
353 bool pressed)
354 {
355 int keycode = event.GetKeyCode();
356 if ( keycode == WXK_SPACE || keycode == WXK_RETURN )
357 {
358 consumer->PerformAction(wxACTION_BUTTON_TOGGLE);
359
360 return true;
361 }
362
363 return wxStdInputHandler::HandleKey(consumer, event, pressed);
364 }
365
366 bool wxStdButtonInputHandler::HandleMouse(wxInputConsumer *consumer,
367 const wxMouseEvent& event)
368 {
369 // the button has 2 states: pressed and normal with the following
370 // transitions between them:
371 //
372 // normal -> left down -> capture mouse and go to pressed state
373 // pressed -> left up inside -> generate click -> go to normal
374 // outside ------------------>
375 //
376 // the other mouse buttons are ignored
377 if ( event.Button(1) )
378 {
379 if ( event.LeftDown() || event.LeftDClick() )
380 {
381 m_winCapture = consumer->GetInputWindow();
382 m_winCapture->CaptureMouse();
383 m_winHasMouse = true;
384
385 consumer->PerformAction(wxACTION_BUTTON_PRESS);
386
387 return true;
388 }
389 else if ( event.LeftUp() )
390 {
391 if ( m_winCapture )
392 {
393 m_winCapture->ReleaseMouse();
394 m_winCapture = NULL;
395 }
396
397 if ( m_winHasMouse )
398 {
399 // this will generate a click event
400 consumer->PerformAction(wxACTION_BUTTON_TOGGLE);
401
402 return true;
403 }
404 //else: the mouse was released outside the window, this doesn't
405 // count as a click
406 }
407 //else: don't do anything special about the double click
408 }
409
410 return wxStdInputHandler::HandleMouse(consumer, event);
411 }
412
413 bool wxStdButtonInputHandler::HandleMouseMove(wxInputConsumer *consumer,
414 const wxMouseEvent& event)
415 {
416 // we only have to do something when the mouse leaves/enters the pressed
417 // button and don't care about the other ones
418 if ( event.GetEventObject() == m_winCapture )
419 {
420 // leaving the button should remove its pressed state
421 if ( event.Leaving() )
422 {
423 // remember that the mouse is now outside
424 m_winHasMouse = false;
425
426 // we do have a pressed button, so release it
427 consumer->GetInputWindow()->SetCurrent(false);
428 consumer->PerformAction(wxACTION_BUTTON_RELEASE);
429
430 return true;
431 }
432 // and entering it back should make it pressed again if it had been
433 // pressed
434 else if ( event.Entering() )
435 {
436 // the mouse is (back) inside the button
437 m_winHasMouse = true;
438
439 // we did have a pressed button which we released when leaving the
440 // window, press it again
441 consumer->GetInputWindow()->SetCurrent(true);
442 consumer->PerformAction(wxACTION_BUTTON_PRESS);
443
444 return true;
445 }
446 }
447
448 return wxStdInputHandler::HandleMouseMove(consumer, event);
449 }
450
451 bool wxStdButtonInputHandler::HandleFocus(wxInputConsumer * WXUNUSED(consumer),
452 const wxFocusEvent& WXUNUSED(event))
453 {
454 // buttons change appearance when they get/lose focus, so return true to
455 // refresh
456 return true;
457 }
458
459 bool wxStdButtonInputHandler::HandleActivation(wxInputConsumer *consumer,
460 bool WXUNUSED(activated))
461 {
462 // the default button changes appearance when the app is [de]activated, so
463 // return true to refresh
464 return wxStaticCast(consumer->GetInputWindow(), wxButton)->IsDefault();
465 }
466
467 #endif // wxUSE_BUTTON
468