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