]> git.saurik.com Git - wxWidgets.git/blob - src/univ/button.cpp
guard with compile-time checks the new GDK_* event type symbols; explain why runtime...
[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
130     if (bitmap.Ok())
131         SetImageLabel(bitmap); // SetInitialSize called by SetImageLabel()
132     else
133         SetInitialSize(size);
134
135     CreateInputHandler(wxINP_HANDLER_BUTTON);
136
137     return true;
138 }
139
140 wxButton::~wxButton()
141 {
142 }
143
144 // ----------------------------------------------------------------------------
145 // size management
146 // ----------------------------------------------------------------------------
147
148 /* static */
149 wxSize wxButtonBase::GetDefaultSize()
150 {
151     static wxSize s_sizeBtn;
152
153     if ( s_sizeBtn.x == 0 )
154     {
155         wxScreenDC dc;
156
157         // this corresponds more or less to wxMSW standard in Win32 theme (see
158         // wxWin32Renderer::AdjustSize())
159 //        s_sizeBtn.x = 8*dc.GetCharWidth();
160 //        s_sizeBtn.y = (11*dc.GetCharHeight())/10 + 2;
161         // Otto Wyss, Patch 664399
162         s_sizeBtn.x = dc.GetCharWidth()*10 + 2;
163         s_sizeBtn.y = dc.GetCharHeight()*11/10 + 2;
164     }
165
166     return s_sizeBtn;
167 }
168
169 wxSize wxButton::DoGetBestClientSize() const
170 {
171     wxClientDC dc(wxConstCast(this, wxButton));
172     wxCoord width, height;
173     dc.GetMultiLineTextExtent(GetLabel(), &width, &height);
174
175     if ( m_bitmap.Ok() )
176     {
177         // allocate extra space for the bitmap
178         wxCoord heightBmp = m_bitmap.GetHeight() + 2*m_marginBmpY;
179         if ( height < heightBmp )
180             height = heightBmp;
181
182         width += m_bitmap.GetWidth() + 2*m_marginBmpX;
183     }
184
185     // The default size should not be adjusted, so the code is moved into the
186     // renderer. This is conceptual wrong but currently the only solution.
187     // (Otto Wyss, Patch 664399)
188
189 /*
190     // for compatibility with other ports, the buttons default size is never
191     // less than the standard one, but not when display not PDAs.
192     if (wxSystemSettings::GetScreenType() > wxSYS_SCREEN_PDA)
193     {
194         if ( !(GetWindowStyle() & wxBU_EXACTFIT) )
195         {
196             wxSize szDef = GetDefaultSize();
197             if ( width < szDef.x )
198                 width = szDef.x;
199         }
200     }
201 */
202     return wxSize(width, height);
203 }
204
205 // ----------------------------------------------------------------------------
206 // drawing
207 // ----------------------------------------------------------------------------
208
209 void wxButton::DoDraw(wxControlRenderer *renderer)
210 {
211     if ( !(GetWindowStyle() & wxBORDER_NONE) )
212     {
213         renderer->DrawButtonBorder();
214     }
215
216     renderer->DrawLabel(m_bitmap, m_marginBmpX, m_marginBmpY);
217 }
218
219 bool wxButton::DoDrawBackground(wxDC& dc)
220 {
221     wxRect rect;
222     wxSize size = GetSize();
223     rect.width = size.x;
224     rect.height = size.y;
225
226     if ( GetBackgroundBitmap().Ok() )
227     {
228         // get the bitmap and the flags
229         int alignment;
230         wxStretch stretch;
231         wxBitmap bmp = GetBackgroundBitmap(&alignment, &stretch);
232         wxControlRenderer::DrawBitmap(dc, bmp, rect, alignment, stretch);
233     }
234     else
235     {
236         m_renderer->DrawButtonSurface(dc, wxTHEME_BG_COLOUR(this),
237                                       rect, GetStateFlags());
238     }
239
240     return true;
241 }
242
243 // ----------------------------------------------------------------------------
244 // input processing
245 // ----------------------------------------------------------------------------
246
247 void wxButton::Press()
248 {
249     if ( !m_isPressed )
250     {
251         m_isPressed = true;
252
253         Refresh();
254     }
255 }
256
257 void wxButton::Release()
258 {
259     if ( m_isPressed )
260     {
261         m_isPressed = false;
262
263         Refresh();
264     }
265 }
266
267 void wxButton::Toggle()
268 {
269     if ( m_isPressed )
270         Release();
271     else
272         Press();
273
274     if ( !m_isPressed )
275     {
276         // releasing button after it had been pressed generates a click event
277         Click();
278     }
279 }
280
281 void wxButton::Click()
282 {
283     wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetId());
284     InitCommandEvent(event);
285     Command(event);
286 }
287
288 bool wxButton::PerformAction(const wxControlAction& action,
289                              long numArg,
290                              const wxString& strArg)
291 {
292     if ( action == wxACTION_BUTTON_TOGGLE )
293         Toggle();
294     else if ( action == wxACTION_BUTTON_CLICK )
295         Click();
296     else if ( action == wxACTION_BUTTON_PRESS )
297         Press();
298     else if ( action == wxACTION_BUTTON_RELEASE )
299         Release();
300     else
301         return wxControl::PerformAction(action, numArg, strArg);
302
303     return true;
304 }
305
306 /* static */
307 wxInputHandler *wxButton::GetStdInputHandler(wxInputHandler *handlerDef)
308 {
309     static wxStdButtonInputHandler s_handlerBtn(handlerDef);
310
311     return &s_handlerBtn;
312 }
313
314 // ----------------------------------------------------------------------------
315 // misc
316 // ----------------------------------------------------------------------------
317
318 void wxButton::SetImageLabel(const wxBitmap& bitmap)
319 {
320     m_bitmap = bitmap;
321
322     SetImageMargins(DEFAULT_BTN_MARGIN_X, DEFAULT_BTN_MARGIN_Y);
323 }
324
325 void wxButton::SetImageMargins(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