]> git.saurik.com Git - wxWidgets.git/blame - src/univ/spinbutt.cpp
avoiding nesting dcs on the same window concurrently
[wxWidgets.git] / src / univ / spinbutt.cpp
CommitLineData
1e6feb95
VZ
1///////////////////////////////////////////////////////////////////////////////
2// Name: univ/spinbutt.cpp
3// Purpose: implementation of the universal version of wxSpinButton
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 21.01.01
7// RCS-ID: $Id$
442b35b5 8// Copyright: (c) 2001 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#ifndef WX_PRECOMP
27#endif
28
29#include "wx/spinbutt.h"
30
31#if wxUSE_SPINBTN
32
33#include "wx/univ/renderer.h"
34#include "wx/univ/inphand.h"
35#include "wx/univ/theme.h"
36
37// ============================================================================
38// implementation of wxSpinButton
39// ============================================================================
40
41IMPLEMENT_DYNAMIC_CLASS(wxSpinEvent, wxNotifyEvent)
42IMPLEMENT_DYNAMIC_CLASS(wxSpinButton, wxControl)
43
44// ----------------------------------------------------------------------------
45// creation
46// ----------------------------------------------------------------------------
47
48#ifdef __VISUALC__
49 // warning C4355: 'this' : used in base member initializer list
50 #pragma warning(disable:4355) // so what? disable it...
51#endif
52
53wxSpinButton::wxSpinButton()
54 : m_arrows(this)
55{
56 Init();
57}
58
59wxSpinButton::wxSpinButton(wxWindow *parent,
60 wxWindowID id,
61 const wxPoint& pos,
62 const wxSize& size,
63 long style,
64 const wxString& name)
65 : m_arrows(this)
66{
67 Init();
68
69 (void)Create(parent, id, pos, size, style, name);
70}
71
72#ifdef __VISUALC__
73 // warning C4355: 'this' : used in base member initializer list
74 #pragma warning(default:4355)
75#endif
76
77void wxSpinButton::Init()
78{
79 for ( size_t n = 0; n < WXSIZEOF(m_arrowsState); n++ )
80 {
81 m_arrowsState[n] = 0;
82 }
83
84 m_value = 0;
85}
86
87bool wxSpinButton::Create(wxWindow *parent,
88 wxWindowID id,
89 const wxPoint& pos,
90 const wxSize& size,
91 long style,
92 const wxString& name)
93{
94 // the spin buttons never have the border
95 style &= ~wxBORDER_MASK;
96
97 if ( !wxSpinButtonBase::Create(parent, id, pos, size, style,
98 wxDefaultValidator, name) )
a290fa5a 99 return false;
1e6feb95
VZ
100
101 SetBestSize(size);
102
103 CreateInputHandler(wxINP_HANDLER_SPINBTN);
104
a290fa5a 105 return true;
1e6feb95
VZ
106}
107
108// ----------------------------------------------------------------------------
109// value access
110// ----------------------------------------------------------------------------
111
112void wxSpinButton::SetRange(int minVal, int maxVal)
113{
114 wxSpinButtonBase::SetRange(minVal, maxVal);
115
116 // because the arrows disabled state might have changed - we don't check if
117 // it really changed or not because SetRange() is called rarely enough and
118 // son an extre refresh here doesn't really hurt
119 Refresh();
120}
121
122int wxSpinButton::GetValue() const
123{
124 return m_value;
125}
126
127void wxSpinButton::SetValue(int val)
128{
129 if ( val != m_value )
130 {
131 m_value = val;
132
133 Refresh();
134 }
135}
136
137int wxSpinButton::NormalizeValue(int value) const
138{
139 if ( value > m_max )
140 {
141 if ( GetWindowStyleFlag() & wxSP_WRAP )
5f133dc1 142 value = m_min + (value - m_max - 1) % (m_max - m_min + 1);
1e6feb95
VZ
143 else
144 value = m_max;
145 }
146 else if ( value < m_min )
147 {
148 if ( GetWindowStyleFlag() & wxSP_WRAP )
5f133dc1 149 value = m_max - (m_min - value - 1) % (m_max - m_min + 1);
1e6feb95
VZ
150 else
151 value = m_min;
152 }
153
154 return value;
155}
156
157bool wxSpinButton::ChangeValue(int inc)
158{
159 int valueNew = NormalizeValue(m_value + inc);
160
161 if ( valueNew == m_value )
162 {
163 // nothing changed - most likely because we are already at min/max
164 // value
a290fa5a 165 return false;
1e6feb95
VZ
166 }
167
168 wxSpinEvent event(inc > 0 ? wxEVT_SCROLL_LINEUP : wxEVT_SCROLL_LINEDOWN,
169 GetId());
170 event.SetPosition(valueNew);
171 event.SetEventObject(this);
172
173 if ( GetEventHandler()->ProcessEvent(event) && !event.IsAllowed() )
174 {
175 // programm has vetoed the event
a290fa5a 176 return false;
1e6feb95
VZ
177 }
178
179 m_value = valueNew;
180
181 // send wxEVT_SCROLL_THUMBTRACK as well
182 event.SetEventType(wxEVT_SCROLL_THUMBTRACK);
183 (void)GetEventHandler()->ProcessEvent(event);
184
a290fa5a 185 return true;
1e6feb95
VZ
186}
187
188// ----------------------------------------------------------------------------
189// size calculations
190// ----------------------------------------------------------------------------
191
192wxSize wxSpinButton::DoGetBestClientSize() const
193{
194 // a spin button has by default the same size as two scrollbar arrows put
195 // together
196 wxSize size = m_renderer->GetScrollbarArrowSize();
197 if ( IsVertical() )
198 {
199 size.y *= 2;
200 }
201 else
202 {
203 size.x *= 2;
204 }
205
206 return size;
207}
208
209// ----------------------------------------------------------------------------
210// wxControlWithArrows methods
211// ----------------------------------------------------------------------------
212
213int wxSpinButton::GetArrowState(wxScrollArrows::Arrow arrow) const
214{
215 int state = m_arrowsState[arrow];
216
217 // the arrow may also be disabled: either because the control is completely
218 // disabled
219 bool disabled = !IsEnabled();
220
221 if ( !disabled && !(GetWindowStyleFlag() & wxSP_WRAP) )
222 {
223 // ... or because we can't go any further - note that this never
224 // happens if we just wrap
225 if ( IsVertical() )
226 {
227 if ( arrow == wxScrollArrows::Arrow_First )
228 disabled = m_value == m_max;
229 else
230 disabled = m_value == m_min;
231 }
232 else // horizontal
233 {
234 if ( arrow == wxScrollArrows::Arrow_First )
235 disabled = m_value == m_min;
236 else
237 disabled = m_value == m_max;
238 }
239 }
240
241 if ( disabled )
242 {
243 state |= wxCONTROL_DISABLED;
244 }
245
246 return state;
247}
248
249void wxSpinButton::SetArrowFlag(wxScrollArrows::Arrow arrow, int flag, bool set)
250{
251 int state = m_arrowsState[arrow];
252 if ( set )
253 state |= flag;
254 else
255 state &= ~flag;
256
257 if ( state != m_arrowsState[arrow] )
258 {
259 m_arrowsState[arrow] = state;
260 Refresh();
261 }
262}
263
264bool wxSpinButton::OnArrow(wxScrollArrows::Arrow arrow)
265{
266 int valueOld = GetValue();
267
268 wxControlAction action;
269 if ( arrow == wxScrollArrows::Arrow_First )
270 action = IsVertical() ? wxACTION_SPIN_INC : wxACTION_SPIN_DEC;
271 else
272 action = IsVertical() ? wxACTION_SPIN_DEC : wxACTION_SPIN_INC;
273
274 PerformAction(action);
275
276 // did we scroll to the end?
277 return GetValue() != valueOld;
278}
279
280// ----------------------------------------------------------------------------
281// drawing
282// ----------------------------------------------------------------------------
283
284void wxSpinButton::DoDraw(wxControlRenderer *renderer)
285{
286 wxRect rectArrow1, rectArrow2;
287 CalcArrowRects(&rectArrow1, &rectArrow2);
288
289 wxDC& dc = renderer->GetDC();
290 m_arrows.DrawArrow(wxScrollArrows::Arrow_First, dc, rectArrow1);
291 m_arrows.DrawArrow(wxScrollArrows::Arrow_Second, dc, rectArrow2);
292}
293
294// ----------------------------------------------------------------------------
295// geometry
296// ----------------------------------------------------------------------------
297
298void wxSpinButton::CalcArrowRects(wxRect *rect1, wxRect *rect2) const
299{
300 // calculate the rectangles for both arrows: note that normally the 2
301 // arrows are adjacent to each other but if the total control width/height
302 // is odd, we can have 1 pixel between them
303 wxRect rectTotal = GetClientRect();
304
305 *rect1 =
306 *rect2 = rectTotal;
307 if ( IsVertical() )
308 {
309 rect1->height /= 2;
310 rect2->height /= 2;
311
312 rect2->y += rect1->height;
313 if ( rectTotal.height % 2 )
314 rect2->y++;
315 }
316 else // horizontal
317 {
318 rect1->width /= 2;
319 rect2->width /= 2;
320
321 rect2->x += rect1->width;
322 if ( rectTotal.width % 2 )
323 rect2->x++;
324 }
325}
326
327wxScrollArrows::Arrow wxSpinButton::HitTest(const wxPoint& pt) const
328{
329 wxRect rectArrow1, rectArrow2;
330 CalcArrowRects(&rectArrow1, &rectArrow2);
331
22a35096 332 if ( rectArrow1.Contains(pt) )
1e6feb95 333 return wxScrollArrows::Arrow_First;
22a35096 334 else if ( rectArrow2.Contains(pt) )
1e6feb95
VZ
335 return wxScrollArrows::Arrow_Second;
336 else
337 return wxScrollArrows::Arrow_None;
338}
339
340// ----------------------------------------------------------------------------
341// input processing
342// ----------------------------------------------------------------------------
343
344bool wxSpinButton::PerformAction(const wxControlAction& action,
345 long numArg,
346 const wxString& strArg)
347{
348 if ( action == wxACTION_SPIN_INC )
349 ChangeValue(+1);
350 else if ( action == wxACTION_SPIN_DEC )
351 ChangeValue(-1);
352 else
353 return wxControl::PerformAction(action, numArg, strArg);
354
a290fa5a 355 return true;
1e6feb95
VZ
356}
357
9467bdb7
VZ
358/* static */
359wxInputHandler *wxSpinButton::GetStdInputHandler(wxInputHandler *handlerDef)
360{
361 static wxStdSpinButtonInputHandler s_handler(handlerDef);
362
363 return &s_handler;
364}
365
1e6feb95
VZ
366// ----------------------------------------------------------------------------
367// wxStdSpinButtonInputHandler
368// ----------------------------------------------------------------------------
369
370wxStdSpinButtonInputHandler::
371wxStdSpinButtonInputHandler(wxInputHandler *inphand)
372 : wxStdInputHandler(inphand)
373{
374}
375
23645bfa 376bool wxStdSpinButtonInputHandler::HandleKey(wxInputConsumer *consumer,
1e6feb95
VZ
377 const wxKeyEvent& event,
378 bool pressed)
379{
380 if ( pressed )
381 {
382 wxControlAction action;
383 switch ( event.GetKeyCode() )
384 {
385 case WXK_DOWN:
386 case WXK_RIGHT:
387 action = wxACTION_SPIN_DEC;
388 break;
389
390 case WXK_UP:
391 case WXK_LEFT:
392 action = wxACTION_SPIN_INC;
393 break;
394 }
395
a290fa5a 396 if ( !action.IsEmpty() )
1e6feb95 397 {
23645bfa 398 consumer->PerformAction(action);
1e6feb95 399
a290fa5a 400 return true;
1e6feb95
VZ
401 }
402 }
403
23645bfa 404 return wxStdInputHandler::HandleKey(consumer, event, pressed);
1e6feb95
VZ
405}
406
23645bfa 407bool wxStdSpinButtonInputHandler::HandleMouse(wxInputConsumer *consumer,
1e6feb95
VZ
408 const wxMouseEvent& event)
409{
23645bfa 410 wxSpinButton *spinbtn = wxStaticCast(consumer->GetInputWindow(), wxSpinButton);
1e6feb95
VZ
411
412 if ( spinbtn->GetArrows().HandleMouse(event) )
413 {
414 // don't refresh, everything is already done
a290fa5a 415 return false;
1e6feb95
VZ
416 }
417
23645bfa 418 return wxStdInputHandler::HandleMouse(consumer, event);
1e6feb95
VZ
419}
420
23645bfa 421bool wxStdSpinButtonInputHandler::HandleMouseMove(wxInputConsumer *consumer,
1e6feb95
VZ
422 const wxMouseEvent& event)
423{
23645bfa 424 wxSpinButton *spinbtn = wxStaticCast(consumer->GetInputWindow(), wxSpinButton);
1e6feb95
VZ
425
426 if ( spinbtn->GetArrows().HandleMouseMove(event) )
427 {
428 // processed by the arrows
a290fa5a 429 return false;
1e6feb95
VZ
430 }
431
23645bfa 432 return wxStdInputHandler::HandleMouseMove(consumer, event);
1e6feb95
VZ
433}
434
435
436#endif // wxUSE_SPINBTN