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