]> git.saurik.com Git - wxWidgets.git/blame - src/univ/button.cpp
Fix horizontal mouse wheel scrolling in wxGTK.
[wxWidgets.git] / src / univ / button.cpp
CommitLineData
1e6feb95 1/////////////////////////////////////////////////////////////////////////////
80fdcdb9 2// Name: src/univ/button.cpp
1e6feb95
VZ
3// Purpose: wxButton
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 14.08.00
442b35b5 7// Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
65571936 8// Licence: wxWindows licence
1e6feb95
VZ
9/////////////////////////////////////////////////////////////////////////////
10
11// ============================================================================
12// declarations
13// ============================================================================
14
15// ----------------------------------------------------------------------------
16// headers
17// ----------------------------------------------------------------------------
18
1e6feb95
VZ
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"
c15521c6 32 #include "wx/settings.h"
1e6feb95
VZ
33#endif
34
35#include "wx/univ/renderer.h"
36#include "wx/univ/inphand.h"
37#include "wx/univ/theme.h"
193e19cf 38#include "wx/univ/colschem.h"
5f7bcb48 39#include "wx/stockitem.h"
1e6feb95 40
9467bdb7
VZ
41// ----------------------------------------------------------------------------
42// wxStdButtonInputHandler: translates SPACE and ENTER keys and the left mouse
43// click into button press/release actions
44// ----------------------------------------------------------------------------
45
46class WXDLLEXPORT wxStdButtonInputHandler : public wxStdInputHandler
47{
48public:
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
62private:
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
1e6feb95
VZ
69// ----------------------------------------------------------------------------
70// constants
71// ----------------------------------------------------------------------------
72
73// default margins around the image
ea1ad04b 74static const wxCoord DEFAULT_BTN_MARGIN_X = 0; // We should give space for the border, at least.
1e6feb95
VZ
75static const wxCoord DEFAULT_BTN_MARGIN_Y = 0;
76
77// ============================================================================
78// implementation
79// ============================================================================
80
1e6feb95
VZ
81// ----------------------------------------------------------------------------
82// creation
83// ----------------------------------------------------------------------------
84
85void wxButton::Init()
86{
87 m_isPressed =
a290fa5a 88 m_isDefault = false;
1e6feb95
VZ
89}
90
91bool wxButton::Create(wxWindow *parent,
92 wxWindowID id,
93 const wxBitmap& bitmap,
5f7bcb48 94 const wxString &lbl,
1e6feb95
VZ
95 const wxPoint &pos,
96 const wxSize &size,
97 long style,
98 const wxValidator& validator,
99 const wxString &name)
100{
5f7bcb48
VS
101 wxString label(lbl);
102 if (label.empty() && wxIsStockID(id))
103 label = wxGetStockLabel(id);
104
88d2e567 105 long ctrl_style = style & ~wxBU_ALIGN_MASK;
390f8114 106 ctrl_style = ctrl_style & ~wxALIGN_MASK;
041ae202
WS
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;
1e6feb95 114
041ae202
WS
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) )
a290fa5a 123 return false;
1e6feb95
VZ
124
125 SetLabel(label);
a2351b3f 126
a1b806b9 127 if (bitmap.IsOk())
2352862a 128 SetBitmap(bitmap); // SetInitialSize called by SetBitmap()
a2351b3f 129 else
170acdc9 130 SetInitialSize(size);
1e6feb95
VZ
131
132 CreateInputHandler(wxINP_HANDLER_BUTTON);
133
a290fa5a 134 return true;
1e6feb95
VZ
135}
136
137wxButton::~wxButton()
138{
139}
140
141// ----------------------------------------------------------------------------
142// size management
143// ----------------------------------------------------------------------------
144
145/* static */
146wxSize 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())
61a83c1c
JS
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;
1e6feb95
VZ
161 }
162
163 return s_sizeBtn;
164}
165
166wxSize wxButton::DoGetBestClientSize() const
167{
168 wxClientDC dc(wxConstCast(this, wxButton));
169 wxCoord width, height;
170 dc.GetMultiLineTextExtent(GetLabel(), &width, &height);
171
a1b806b9 172 if ( m_bitmap.IsOk() )
1e6feb95
VZ
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
041ae202 182 // The default size should not be adjusted, so the code is moved into the
61a83c1c
JS
183 // renderer. This is conceptual wrong but currently the only solution.
184 // (Otto Wyss, Patch 664399)
185
186/*
1e6feb95 187 // for compatibility with other ports, the buttons default size is never
c15521c6 188 // less than the standard one, but not when display not PDAs.
41fecb44 189 if (wxSystemSettings::GetScreenType() > wxSYS_SCREEN_PDA)
7ef8bfc4 190 {
c15521c6
RR
191 if ( !(GetWindowStyle() & wxBU_EXACTFIT) )
192 {
193 wxSize szDef = GetDefaultSize();
194 if ( width < szDef.x )
195 width = szDef.x;
196 }
7ef8bfc4 197 }
61a83c1c 198*/
1e6feb95
VZ
199 return wxSize(width, height);
200}
201
202// ----------------------------------------------------------------------------
203// drawing
204// ----------------------------------------------------------------------------
205
206void wxButton::DoDraw(wxControlRenderer *renderer)
207{
e4606ed9
VZ
208 if ( !(GetWindowStyle() & wxBORDER_NONE) )
209 {
210 renderer->DrawButtonBorder();
211 }
212
fdc468a0 213 renderer->DrawButtonLabel(m_bitmap, m_marginBmpX, m_marginBmpY);
1e6feb95
VZ
214}
215
193e19cf
RR
216bool wxButton::DoDrawBackground(wxDC& dc)
217{
218 wxRect rect;
219 wxSize size = GetSize();
220 rect.width = size.x;
221 rect.height = size.y;
041ae202 222
a1b806b9 223 if ( GetBackgroundBitmap().IsOk() )
193e19cf
RR
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
a290fa5a 237 return true;
193e19cf
RR
238}
239
1e6feb95
VZ
240// ----------------------------------------------------------------------------
241// input processing
242// ----------------------------------------------------------------------------
243
244void wxButton::Press()
245{
246 if ( !m_isPressed )
247 {
a290fa5a 248 m_isPressed = true;
1e6feb95
VZ
249
250 Refresh();
251 }
252}
253
254void wxButton::Release()
255{
256 if ( m_isPressed )
257 {
a290fa5a 258 m_isPressed = false;
1e6feb95
VZ
259
260 Refresh();
261 }
262}
263
264void 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
278void wxButton::Click()
279{
ce7fe42e 280 wxCommandEvent event(wxEVT_BUTTON, GetId());
1e6feb95
VZ
281 InitCommandEvent(event);
282 Command(event);
283}
284
285bool 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
a290fa5a 300 return true;
1e6feb95
VZ
301}
302
9467bdb7
VZ
303/* static */
304wxInputHandler *wxButton::GetStdInputHandler(wxInputHandler *handlerDef)
305{
306 static wxStdButtonInputHandler s_handlerBtn(handlerDef);
307
308 return &s_handlerBtn;
309}
310
1e6feb95
VZ
311// ----------------------------------------------------------------------------
312// misc
313// ----------------------------------------------------------------------------
314
2352862a 315void wxButton::DoSetBitmap(const wxBitmap& bitmap, State which)
1e6feb95 316{
2352862a
VZ
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;
1e6feb95 321
2352862a 322 SetBitmapMargins(DEFAULT_BTN_MARGIN_X, DEFAULT_BTN_MARGIN_Y);
1e6feb95
VZ
323}
324
2352862a 325void wxButton::DoSetBitmapMargins(wxCoord x, wxCoord y)
1e6feb95 326{
34d26f42
RR
327 m_marginBmpX = x + 2;
328 m_marginBmpY = y + 2;
041ae202 329
170acdc9 330 SetInitialSize(wxDefaultSize);
1e6feb95
VZ
331}
332
94aff5ff 333wxWindow *wxButton::SetDefault()
1e6feb95 334{
a290fa5a 335 m_isDefault = true;
94aff5ff
VZ
336
337 return wxButtonBase::SetDefault();
1e6feb95
VZ
338}
339
340// ============================================================================
341// wxStdButtonInputHandler
342// ============================================================================
343
344wxStdButtonInputHandler::wxStdButtonInputHandler(wxInputHandler *handler)
345 : wxStdInputHandler(handler)
346{
347 m_winCapture = NULL;
a290fa5a 348 m_winHasMouse = false;
1e6feb95
VZ
349}
350
23645bfa 351bool wxStdButtonInputHandler::HandleKey(wxInputConsumer *consumer,
1e6feb95
VZ
352 const wxKeyEvent& event,
353 bool pressed)
354{
355 int keycode = event.GetKeyCode();
356 if ( keycode == WXK_SPACE || keycode == WXK_RETURN )
357 {
23645bfa 358 consumer->PerformAction(wxACTION_BUTTON_TOGGLE);
1e6feb95 359
a290fa5a 360 return true;
1e6feb95
VZ
361 }
362
23645bfa 363 return wxStdInputHandler::HandleKey(consumer, event, pressed);
1e6feb95
VZ
364}
365
23645bfa 366bool wxStdButtonInputHandler::HandleMouse(wxInputConsumer *consumer,
1e6feb95
VZ
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 {
96e3c3d4 379 if ( event.LeftDown() || event.LeftDClick() )
1e6feb95 380 {
23645bfa 381 m_winCapture = consumer->GetInputWindow();
1e6feb95 382 m_winCapture->CaptureMouse();
a290fa5a 383 m_winHasMouse = true;
1e6feb95 384
23645bfa 385 consumer->PerformAction(wxACTION_BUTTON_PRESS);
1e6feb95 386
a290fa5a 387 return true;
1e6feb95 388 }
96e3c3d4 389 else if ( event.LeftUp() )
1e6feb95
VZ
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
23645bfa 400 consumer->PerformAction(wxACTION_BUTTON_TOGGLE);
1e6feb95 401
a290fa5a 402 return true;
1e6feb95
VZ
403 }
404 //else: the mouse was released outside the window, this doesn't
405 // count as a click
406 }
96e3c3d4 407 //else: don't do anything special about the double click
1e6feb95
VZ
408 }
409
23645bfa 410 return wxStdInputHandler::HandleMouse(consumer, event);
1e6feb95
VZ
411}
412
23645bfa 413bool wxStdButtonInputHandler::HandleMouseMove(wxInputConsumer *consumer,
1e6feb95
VZ
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
a290fa5a 424 m_winHasMouse = false;
1e6feb95
VZ
425
426 // we do have a pressed button, so release it
a290fa5a 427 consumer->GetInputWindow()->SetCurrent(false);
23645bfa 428 consumer->PerformAction(wxACTION_BUTTON_RELEASE);
1e6feb95 429
a290fa5a 430 return true;
1e6feb95
VZ
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
a290fa5a 437 m_winHasMouse = true;
1e6feb95
VZ
438
439 // we did have a pressed button which we released when leaving the
440 // window, press it again
a290fa5a 441 consumer->GetInputWindow()->SetCurrent(true);
23645bfa 442 consumer->PerformAction(wxACTION_BUTTON_PRESS);
1e6feb95 443
a290fa5a 444 return true;
1e6feb95
VZ
445 }
446 }
447
23645bfa 448 return wxStdInputHandler::HandleMouseMove(consumer, event);
1e6feb95
VZ
449}
450
61fef19b
VZ
451bool wxStdButtonInputHandler::HandleFocus(wxInputConsumer * WXUNUSED(consumer),
452 const wxFocusEvent& WXUNUSED(event))
1e6feb95 453{
a290fa5a 454 // buttons change appearance when they get/lose focus, so return true to
1e6feb95 455 // refresh
a290fa5a 456 return true;
1e6feb95
VZ
457}
458
23645bfa 459bool wxStdButtonInputHandler::HandleActivation(wxInputConsumer *consumer,
61fef19b 460 bool WXUNUSED(activated))
1e6feb95
VZ
461{
462 // the default button changes appearance when the app is [de]activated, so
a290fa5a 463 // return true to refresh
23645bfa 464 return wxStaticCast(consumer->GetInputWindow(), wxButton)->IsDefault();
1e6feb95
VZ
465}
466
467#endif // wxUSE_BUTTON
468