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