1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/univ/anybutton.cpp
3 // Purpose:     wxAnyButton
4 // Author:      Vadim Zeitlin
5 // Created:     2014-03-26 (extracted from button.cpp and tglbtn.cpp)
6 // Copyright:   (c) 2014
7 // Licence:     wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9 
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12 
13 
14 
15 #ifndef WX_PRECOMP
16     #include "wx/dcclient.h"
17 #endif
18 
19 #include "wx/univ/renderer.h"
20 #include "wx/univ/theme.h"
21 #include "wx/univ/colschem.h"
22 
23 // ============================================================================
24 // implementation
25 // ============================================================================
26 
27 
Press()28 void wxAnyButton::Press()
29 {
30     if ( !m_isPressed )
31     {
32         m_isPressed = true;
33 
34         Refresh();
35     }
36 }
37 
Release()38 void wxAnyButton::Release()
39 {
40     if ( m_isPressed )
41     {
42         m_isPressed = false;
43 
44         Refresh();
45     }
46 }
47 
Toggle()48 void wxAnyButton::Toggle()
49 {
50     if ( m_isPressed )
51         Release();
52     else
53         Press();
54 
55     if ( !m_isPressed )
56     {
57         // releasing button after it had been pressed generates a click event
58         Click();
59     }
60     Refresh();
61 }
62 
PerformAction(const wxControlAction & action,long numArg,const wxString & strArg)63 bool wxAnyButton::PerformAction(const wxControlAction& action,
64                              long numArg,
65                              const wxString& strArg)
66 {
67     if ( action == wxACTION_BUTTON_TOGGLE )
68         Toggle();
69     else if ( action == wxACTION_BUTTON_CLICK )
70         Click();
71     else if ( action == wxACTION_BUTTON_PRESS )
72         Press();
73     else if ( action == wxACTION_BUTTON_RELEASE )
74         Release();
75     else
76         return wxControl::PerformAction(action, numArg, strArg);
77 
78     return true;
79 }
80 
81 /* static */
GetStdInputHandler(wxInputHandler * handlerDef)82 wxInputHandler *wxAnyButton::GetStdInputHandler(wxInputHandler *handlerDef)
83 {
84     static wxStdAnyButtonInputHandler s_handlerBtn(handlerDef);
85 
86     return &s_handlerBtn;
87 }
88 
89 // ----------------------------------------------------------------------------
90 // size management
91 // ----------------------------------------------------------------------------
92 
DoGetBestClientSize() const93 wxSize wxAnyButton::DoGetBestClientSize() const
94 {
95     wxClientDC dc(wxConstCast(this, wxAnyButton));
96     wxCoord width, height;
97     dc.GetMultiLineTextExtent(GetLabel(), &width, &height);
98 
99     if ( m_bitmap.IsOk() )
100     {
101         // allocate extra space for the bitmap
102         wxCoord heightBmp = m_bitmap.GetHeight() + 2*m_marginBmpY;
103         if ( height < heightBmp )
104             height = heightBmp;
105 
106         width += m_bitmap.GetWidth() + 2*m_marginBmpX;
107     }
108 
109     // The default size should not be adjusted, so the code is moved into the
110     // renderer. This is conceptual wrong but currently the only solution.
111     // (Otto Wyss, Patch 664399)
112 
113 /*
114     // for compatibility with other ports, the buttons default size is never
115     // less than the standard one, but not when display not PDAs.
116     if (wxSystemSettings::GetScreenType() > wxSYS_SCREEN_PDA)
117     {
118         if ( !(GetWindowStyle() & wxBU_EXACTFIT) )
119         {
120             wxSize szDef = GetDefaultSize();
121             if ( width < szDef.x )
122                 width = szDef.x;
123         }
124     }
125 */
126     return wxSize(width, height);
127 }
128 
129 // ----------------------------------------------------------------------------
130 // drawing
131 // ----------------------------------------------------------------------------
132 
DoDraw(wxControlRenderer * renderer)133 void wxAnyButton::DoDraw(wxControlRenderer *renderer)
134 {
135     if ( !(GetWindowStyle() & wxBORDER_NONE) )
136     {
137         renderer->DrawButtonBorder();
138     }
139 
140     renderer->DrawButtonLabel(m_bitmap, m_marginBmpX, m_marginBmpY);
141 }
142 
DoDrawBackground(wxDC & dc)143 bool wxAnyButton::DoDrawBackground(wxDC& dc)
144 {
145     wxRect rect;
146     wxSize size = GetSize();
147     rect.width = size.x;
148     rect.height = size.y;
149 
150     if ( GetBackgroundBitmap().IsOk() )
151     {
152         // get the bitmap and the flags
153         int alignment;
154         wxStretch stretch;
155         wxBitmap bmp = GetBackgroundBitmap(&alignment, &stretch);
156         wxControlRenderer::DrawBitmap(dc, bmp, rect, alignment, stretch);
157     }
158     else
159     {
160         m_renderer->DrawButtonSurface(dc, wxTHEME_BG_COLOUR(this),
161                                       rect, GetStateFlags());
162     }
163 
164     return true;
165 }
166 
167 // ============================================================================
168 // wxStdAnyButtonInputHandler
169 // ============================================================================
170 
wxStdAnyButtonInputHandler(wxInputHandler * handler)171 wxStdAnyButtonInputHandler::wxStdAnyButtonInputHandler(wxInputHandler *handler)
172                        : wxStdInputHandler(handler)
173 {
174     m_winCapture = NULL;
175     m_winHasMouse = false;
176 }
177 
HandleKey(wxInputConsumer * consumer,const wxKeyEvent & event,bool pressed)178 bool wxStdAnyButtonInputHandler::HandleKey(wxInputConsumer *consumer,
179                                         const wxKeyEvent& event,
180                                         bool pressed)
181 {
182     int keycode = event.GetKeyCode();
183     if ( keycode == WXK_SPACE || keycode == WXK_RETURN )
184     {
185         consumer->PerformAction(wxACTION_BUTTON_TOGGLE);
186 
187         return true;
188     }
189 
190     return wxStdInputHandler::HandleKey(consumer, event, pressed);
191 }
192 
HandleMouse(wxInputConsumer * consumer,const wxMouseEvent & event)193 bool wxStdAnyButtonInputHandler::HandleMouse(wxInputConsumer *consumer,
194                                           const wxMouseEvent& event)
195 {
196     // the button has 2 states: pressed and normal with the following
197     // transitions between them:
198     //
199     //      normal -> left down -> capture mouse and go to pressed state
200     //      pressed -> left up inside -> generate click -> go to normal
201     //                         outside ------------------>
202     //
203     // the other mouse buttons are ignored
204     if ( event.Button(1) )
205     {
206         if ( event.LeftDown() || event.LeftDClick() )
207         {
208             m_winCapture = consumer->GetInputWindow();
209             m_winCapture->CaptureMouse();
210             m_winHasMouse = true;
211 
212             consumer->PerformAction(wxACTION_BUTTON_PRESS);
213 
214             return true;
215         }
216         else if ( event.LeftUp() )
217         {
218             if ( m_winCapture )
219             {
220                 m_winCapture->ReleaseMouse();
221                 m_winCapture = NULL;
222             }
223 
224             if ( m_winHasMouse )
225             {
226                 // this will generate a click event
227                 consumer->PerformAction(wxACTION_BUTTON_TOGGLE);
228 
229                 return true;
230             }
231             //else: the mouse was released outside the window, this doesn't
232             //      count as a click
233         }
234         //else: don't do anything special about the double click
235     }
236 
237     return wxStdInputHandler::HandleMouse(consumer, event);
238 }
239 
HandleMouseMove(wxInputConsumer * consumer,const wxMouseEvent & event)240 bool wxStdAnyButtonInputHandler::HandleMouseMove(wxInputConsumer *consumer,
241                                               const wxMouseEvent& event)
242 {
243     // we only have to do something when the mouse leaves/enters the pressed
244     // button and don't care about the other ones
245     if ( event.GetEventObject() == m_winCapture )
246     {
247         // leaving the button should remove its pressed state
248         if ( event.Leaving() )
249         {
250             // remember that the mouse is now outside
251             m_winHasMouse = false;
252 
253             // we do have a pressed button, so release it
254             consumer->GetInputWindow()->SetCurrent(false);
255             consumer->PerformAction(wxACTION_BUTTON_RELEASE);
256 
257             return true;
258         }
259         // and entering it back should make it pressed again if it had been
260         // pressed
261         else if ( event.Entering() )
262         {
263             // the mouse is (back) inside the button
264             m_winHasMouse = true;
265 
266             // we did have a pressed button which we released when leaving the
267             // window, press it again
268             consumer->GetInputWindow()->SetCurrent(true);
269             consumer->PerformAction(wxACTION_BUTTON_PRESS);
270 
271             return true;
272         }
273     }
274 
275     return wxStdInputHandler::HandleMouseMove(consumer, event);
276 }
277 
HandleFocus(wxInputConsumer * WXUNUSED (consumer),const wxFocusEvent & WXUNUSED (event))278 bool wxStdAnyButtonInputHandler::HandleFocus(wxInputConsumer * WXUNUSED(consumer),
279                                           const wxFocusEvent& WXUNUSED(event))
280 {
281     // buttons change appearance when they get/lose focus, so return true to
282     // refresh
283     return true;
284 }
285 
HandleActivation(wxInputConsumer * consumer,bool WXUNUSED (activated))286 bool wxStdAnyButtonInputHandler::HandleActivation(wxInputConsumer *consumer,
287                                                bool WXUNUSED(activated))
288 {
289     // the default button changes appearance when the app is [de]activated, so
290     // return true to refresh
291     return wxStaticCast(consumer->GetInputWindow(), wxAnyButton)->IsDefault();
292 }
293