fix divide by 0 bug in UpdateScrollbars() (part of patch 1716763)
[wxWidgets.git] / src / univ / slider.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/univ/slider.cpp
3 // Purpose: implementation of the universal version of wxSlider
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 09.02.01
7 // RCS-ID: $Id$
8 // Copyright: (c) 2001 SciTech Software, Inc. (www.scitechsoft.com)
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 /*
13 There is some discrepancy in wxSL_LABELS style handling between wxMSW and
14 wxGTK: the latter handles it natively and shows only the current value of
15 the slider on the side corresponding to wxSL_TOP/BOTTOM/LEFT/RIGHT style
16 given (which can be combined with wxSL_HORIZONTAL/VERTICAL) while wxMSW
17 emulates this somehow and shows the min and max values near the ends of the
18 slider and the current value in a separate static box nearby.
19
20 We currently follow wxGTK except that wxSL_HORIZONTAL slider can only have
21 the label displayed on top or bottom of it and wxSL_VERTICAL - to the left
22 or right.
23
24 What we really need is probably a more fine grain control on labels, i.e. we
25 should be able to select if we show nothing at all, the current value only
26 or the value and the limits - the current approach is just that of the
27 lowest common denominator.
28
29 TODO:
30
31 +0. add ticks support
32 1. support for all orientations
33 2. draw the slider thumb highlighted when it is dragged
34 ?3. manual ticks support?
35 */
36
37 // ============================================================================
38 // declarations
39 // ============================================================================
40
41 // ----------------------------------------------------------------------------
42 // headers
43 // ----------------------------------------------------------------------------
44
45 #include "wx/wxprec.h"
46
47 #ifdef __BORLANDC__
48 #pragma hdrstop
49 #endif
50
51 #if wxUSE_SLIDER
52
53 #include "wx/slider.h"
54
55 #ifndef WX_PRECOMP
56 #include "wx/dc.h"
57 #endif
58
59 #include "wx/univ/renderer.h"
60 #include "wx/univ/inphand.h"
61 #include "wx/univ/theme.h"
62
63 // ----------------------------------------------------------------------------
64 // wxStdSliderInputHandler: default slider input handling
65 // ----------------------------------------------------------------------------
66
67 class WXDLLEXPORT wxStdSliderInputHandler : public wxStdInputHandler
68 {
69 public:
70 // default ctor
71 wxStdSliderInputHandler(wxInputHandler *inphand)
72 : wxStdInputHandler(inphand)
73 {
74 }
75
76 // base class methods
77 virtual bool HandleKey(wxInputConsumer *consumer,
78 const wxKeyEvent& event,
79 bool pressed);
80 virtual bool HandleMouse(wxInputConsumer *consumer,
81 const wxMouseEvent& event);
82 virtual bool HandleMouseMove(wxInputConsumer *consumer,
83 const wxMouseEvent& event);
84
85 virtual bool HandleFocus(wxInputConsumer *consumer, const wxFocusEvent& event);
86 };
87
88 // ----------------------------------------------------------------------------
89 // constants
90 // ----------------------------------------------------------------------------
91
92 // the margin between the slider and the label (FIXME: hardcoded)
93 static const wxCoord SLIDER_LABEL_MARGIN = 2;
94
95 // ============================================================================
96 // implementation of wxSlider
97 // ============================================================================
98
99 IMPLEMENT_DYNAMIC_CLASS(wxSlider, wxControl)
100
101 BEGIN_EVENT_TABLE(wxSlider, wxControl)
102 EVT_SIZE(wxSlider::OnSize)
103 END_EVENT_TABLE()
104
105 // ----------------------------------------------------------------------------
106 // wxSlider creation
107 // ----------------------------------------------------------------------------
108
109 #ifdef __VISUALC__
110 // warning C4355: 'this' : used in base member initializer list
111 #pragma warning(disable:4355)
112 #endif
113
114 wxSlider::wxSlider()
115 : m_thumb(this)
116 {
117 Init();
118 }
119
120 wxSlider::wxSlider(wxWindow *parent,
121 wxWindowID id,
122 int value, int minValue, int maxValue,
123 const wxPoint& pos,
124 const wxSize& size,
125 long style,
126 const wxValidator& validator,
127 const wxString& name)
128 : m_thumb(this)
129 {
130 Init();
131
132 (void)Create(parent, id, value, minValue, maxValue,
133 pos, size, style, validator, name);
134 }
135
136 #ifdef __VISUALC__
137 // warning C4355: 'this' : used in base member initializer list
138 #pragma warning(default:4355)
139 #endif
140
141 void wxSlider::Init()
142 {
143 m_min =
144 m_max =
145 m_value = 0;
146
147 m_tickFreq = 1;
148
149 m_lineSize =
150 m_pageSize = 0;
151
152 m_thumbSize = 0;
153 m_thumbFlags = 0;
154 }
155
156 bool wxSlider::Create(wxWindow *parent,
157 wxWindowID id,
158 int value, int minValue, int maxValue,
159 const wxPoint& pos,
160 const wxSize& size,
161 long style,
162 const wxValidator& validator,
163 const wxString& name)
164 {
165 if ( !wxSliderBase::Create(parent, id, pos, size, style,
166 validator, name) )
167 return false;
168
169 SetRange(minValue, maxValue);
170 SetValue(value);
171
172 // call this after setting the range as the best size depends (at least if
173 // we have wxSL_LABELS style) on the range
174 SetInitialSize(size);
175
176 CreateInputHandler(wxINP_HANDLER_SLIDER);
177
178 return true;
179 }
180
181 // ----------------------------------------------------------------------------
182 // wxSlider range and value
183 // ----------------------------------------------------------------------------
184
185 int wxSlider::GetValue() const
186 {
187 return m_value;
188 }
189
190 int wxSlider::NormalizeValue(int value) const
191 {
192 if ( value < m_min )
193 return m_min;
194 else if ( value > m_max )
195 return m_max;
196 else
197 return value;
198 }
199
200 bool wxSlider::ChangeValueBy(int inc)
201 {
202 return ChangeValueTo(NormalizeValue(m_value + inc));
203 }
204
205 bool wxSlider::ChangeValueTo(int value)
206 {
207 // check if the value is going to change at all
208 if (value == m_value)
209 return false;
210
211 // this method is protected and we should only call it with normalized
212 // value!
213 wxCHECK_MSG( IsInRange(value), false, _T("invalid slider value") );
214
215 m_value = value;
216
217 Refresh();
218
219 // generate the events: both a specific scroll event and a command event
220 wxScrollEvent eventScroll(wxEVT_SCROLL_CHANGED, GetId());
221 eventScroll.SetPosition(m_value);
222 eventScroll.SetEventObject( this );
223 (void)GetEventHandler()->ProcessEvent(eventScroll);
224
225 wxCommandEvent event(wxEVT_COMMAND_SLIDER_UPDATED, GetId());
226 event.SetInt(m_value);
227 event.SetEventObject(this);
228 (void)GetEventHandler()->ProcessEvent(event);
229
230 return true;
231 }
232
233 void wxSlider::SetValue(int value)
234 {
235 value = NormalizeValue(value);
236
237 if ( m_value != value )
238 {
239 m_value = value;
240
241 Refresh();
242 }
243 }
244
245 void wxSlider::SetRange(int minValue, int maxValue)
246 {
247 if ( minValue > maxValue )
248 {
249 // swap them, we always want min to be less than max
250 int tmp = minValue;
251 minValue = maxValue;
252 maxValue = tmp;
253 }
254
255 if ( m_min != minValue || m_max != maxValue )
256 {
257 m_min = minValue;
258 m_max = maxValue;
259
260 // reset the value to make sure it is in the new range
261 SetValue(m_value);
262
263 // the size of the label rect might have changed
264 if ( HasLabels() )
265 {
266 CalcGeometry();
267 }
268
269 Refresh();
270 }
271 //else: nothing changed
272 }
273
274 int wxSlider::GetMin() const
275 {
276 return m_min;
277 }
278
279 int wxSlider::GetMax() const
280 {
281 return m_max;
282 }
283
284 // ----------------------------------------------------------------------------
285 // wxSlider line/page/thumb size
286 // ----------------------------------------------------------------------------
287
288 void wxSlider::SetLineSize(int lineSize)
289 {
290 wxCHECK_RET( lineSize >= 0, _T("invalid slider line size") );
291
292 m_lineSize = lineSize;
293 }
294
295 void wxSlider::SetPageSize(int pageSize)
296 {
297 wxCHECK_RET( pageSize >= 0, _T("invalid slider page size") );
298
299 m_pageSize = pageSize;
300 }
301
302 int wxSlider::GetLineSize() const
303 {
304 if ( !m_lineSize )
305 {
306 // the default line increment is 1
307 wxConstCast(this, wxSlider)->m_lineSize = 1;
308 }
309
310 return m_lineSize;
311 }
312
313 int wxSlider::GetPageSize() const
314 {
315 if ( !m_pageSize )
316 {
317 // the default page increment is m_tickFreq
318 wxConstCast(this, wxSlider)->m_pageSize = m_tickFreq;
319 }
320
321 return m_pageSize;
322 }
323
324 void wxSlider::SetThumbLength(int lenPixels)
325 {
326 wxCHECK_RET( lenPixels >= 0, _T("invalid slider thumb size") );
327
328 // use m_thumbSize here directly and not GetThumbLength() to avoid setting
329 // it to the default value as we don't need it
330 if ( lenPixels != m_thumbSize )
331 {
332 m_thumbSize = lenPixels;
333
334 Refresh();
335 }
336 }
337
338 int wxSlider::GetThumbLength() const
339 {
340 wxSize sz = GetDefaultThumbSize();
341 int len = (IsVert() ? sz.x : sz.y);
342 if (m_thumbSize > len)
343 {
344 return m_thumbSize;
345 }
346 else
347 {
348 return len;
349 }
350
351 }
352
353 // ----------------------------------------------------------------------------
354 // wxSlider ticks
355 // ----------------------------------------------------------------------------
356
357 void wxSlider::SetTickFreq(int n, int WXUNUSED(dummy))
358 {
359 wxCHECK_RET (n > 0, _T("invalid slider tick frequency"));
360
361 if ( n != m_tickFreq )
362 {
363 m_tickFreq = n;
364
365 Refresh();
366 }
367 }
368
369 // ----------------------------------------------------------------------------
370 // wxSlider geometry
371 // ----------------------------------------------------------------------------
372
373 wxSize wxSlider::CalcLabelSize() const
374 {
375 wxSize size;
376
377 // there is no sense in trying to calc the labels size if we haven't got
378 // any, the caller must check for it
379 wxCHECK_MSG( HasLabels(), size, _T("shouldn't be called") );
380
381 wxCoord w1, h1, w2, h2;
382 GetTextExtent(FormatValue(m_min), &w1, &h1);
383 GetTextExtent(FormatValue(m_max), &w2, &h2);
384
385 size.x = wxMax(w1, w2);
386 size.y = wxMax(h1, h2);
387
388 return size;
389 }
390
391 wxSize wxSlider::DoGetBestClientSize() const
392 {
393 // this dimension is completely arbitrary
394 static const wxCoord SLIDER_WIDTH = 40;
395
396 long style = GetWindowStyle();
397
398 // first calculate the size of the slider itself: i.e. the shaft and the
399 // thumb
400 wxCoord height = GetRenderer()->GetSliderDim();
401
402 wxSize size;
403 if ( IsVert() )
404 {
405 size.x = height;
406 size.y = SLIDER_WIDTH;
407 }
408 else // horizontal
409 {
410 size.x = SLIDER_WIDTH;
411 size.y = height;
412 }
413
414 // add space for ticks
415 if ( HasTicks() )
416 {
417 wxCoord lenTick = GetRenderer()->GetSliderTickLen();
418 if (style & wxSL_BOTH)
419 {
420 lenTick = 2 * lenTick;
421 }
422
423 if ( IsVert() )
424 size.x += lenTick;
425 else
426 size.y += lenTick;
427 }
428
429 // if we have the label, reserve enough space for it
430 if ( HasLabels() )
431 {
432 wxSize sizeLabels = CalcLabelSize();
433
434 if (style & (wxSL_LEFT|wxSL_RIGHT))
435 {
436 size.x += sizeLabels.x + SLIDER_LABEL_MARGIN;
437 }
438 else if (style & (wxSL_TOP|wxSL_BOTTOM))
439 {
440 size.y += sizeLabels.y + SLIDER_LABEL_MARGIN;
441 }
442 }
443
444 return size;
445 }
446
447 void wxSlider::OnSize(wxSizeEvent& event)
448 {
449 CalcGeometry();
450
451 event.Skip();
452 }
453
454 const wxRect& wxSlider::GetSliderRect() const
455 {
456 if ( m_rectSlider.width < 0 )
457 {
458 wxConstCast(this, wxSlider)->CalcGeometry();
459 }
460
461 return m_rectSlider;
462 }
463
464 void wxSlider::CalcGeometry()
465 {
466 /*
467 recalc the label and slider positions, this looks like this for
468 wxSL_HORIZONTAL | wxSL_TOP slider:
469
470 LLL lll
471 -------------------------
472 | T | <-- this is the slider rect
473 | HHHHHHHHHHHHHHHTHHHHH |
474 | T |
475 | * * * * * * * *|
476 -------------------------
477
478 LLL is m_rectLabel as calculated here and lll is the real rect used for
479 label drawing in OnDraw() (TTT indicated the thumb position and *s are
480 the ticks)
481
482 in the wxSL_VERTICAL | wxSL_RIGHT case the picture is like this:
483
484 ------ LLL
485 | H |
486 | H *|
487 | H |
488 | H *|
489 | H |
490 | H *|
491 | H |
492 |TTT*| lll
493 | H |
494 | H *|
495 ------
496 */
497 long style = GetWindowStyle();
498
499 // initialize to the full client rect
500 wxRect rectTotal = GetClientRect();
501 m_rectSlider = rectTotal;
502 wxSize sizeThumb = GetThumbSize();
503
504 // Labels reduce the size of the slider rect
505 if ( HasLabels() )
506 {
507 wxSize sizeLabels = CalcLabelSize();
508
509 m_rectLabel = wxRect(rectTotal.GetPosition(), sizeLabels);
510
511 if (style & wxSL_TOP)
512 {
513 // shrink and offset the slider to the bottom
514 m_rectSlider.y += sizeLabels.y + SLIDER_LABEL_MARGIN;
515 m_rectSlider.height -= sizeLabels.y + SLIDER_LABEL_MARGIN;
516 }
517 else if (style & wxSL_BOTTOM)
518 {
519 // shrink the slider and move the label to the bottom
520 m_rectSlider.height -= sizeLabels.y + SLIDER_LABEL_MARGIN;
521 m_rectLabel.y += m_rectSlider.height + SLIDER_LABEL_MARGIN;
522 }
523 else if (style & wxSL_LEFT)
524 {
525 // shrink and offset the slider to the right
526 m_rectSlider.x += sizeLabels.x + SLIDER_LABEL_MARGIN;
527 m_rectSlider.width -= sizeLabels.x + SLIDER_LABEL_MARGIN;
528 }
529 else if (style & wxSL_RIGHT)
530 {
531 // shrink the slider and move the label to the right
532 m_rectSlider.width -= sizeLabels.x + SLIDER_LABEL_MARGIN;
533 m_rectLabel.x += m_rectSlider.width + SLIDER_LABEL_MARGIN;
534 }
535 }
536
537 // calculate ticks too
538 if ( HasTicks() )
539 {
540 wxCoord lenTick = GetRenderer()->GetSliderTickLen();
541
542 // it
543 m_rectTicks = GetShaftRect();
544
545 if ( IsVert() )
546 {
547 if (style & (wxSL_LEFT|wxSL_BOTH))
548 {
549 m_rectTicks.x = m_rectSlider.x;
550 }
551 else
552 { // wxSL_RIGHT
553 m_rectTicks.x = m_rectSlider.x + m_rectSlider.width - lenTick;
554 }
555 m_rectTicks.width = lenTick;
556 }
557 else // horizontal
558 {
559 if (style & (wxSL_TOP|wxSL_BOTH))
560 {
561 m_rectTicks.y = m_rectSlider.y;
562 }
563 else
564 { // wxSL_BOTTOM
565 m_rectTicks.y = m_rectSlider.y + m_rectSlider.height - lenTick;
566 }
567 m_rectTicks.height = lenTick;
568 }
569 }
570
571 // slider is never smaller than thumb size unless rectTotal
572 if ( IsVert() )
573 {
574 wxCoord width = wxMin ( rectTotal.width, sizeThumb.x );
575 m_rectSlider.width = wxMax ( m_rectSlider.width, width );
576 }
577 else
578 {
579 wxCoord height = wxMin ( rectTotal.height, sizeThumb.y );
580 m_rectSlider.height = wxMax ( m_rectSlider.height, height );
581 }
582 }
583
584 wxSize wxSlider::GetDefaultThumbSize() const
585 {
586 // Default size has no styles (arrows)
587 return GetRenderer()->GetSliderThumbSize(GetSliderRect(), 0, GetOrientation());
588 }
589
590 wxSize wxSlider::GetThumbSize() const
591 {
592 return GetRenderer()->GetSliderThumbSize(GetSliderRect(), m_thumbSize, GetOrientation());
593 }
594
595 // ----------------------------------------------------------------------------
596 // wxSlider thumb geometry
597 // ----------------------------------------------------------------------------
598
599 wxRect wxSlider::GetShaftRect() const
600 {
601 return GetRenderer()->GetSliderShaftRect(m_rectSlider, m_thumbSize, GetOrientation(), GetWindowStyle());
602 }
603
604 void wxSlider::CalcThumbRect(const wxRect *rectShaftIn,
605 wxRect *rectThumbOut,
606 wxRect *rectLabelOut,
607 int value) const
608 {
609 if ( value == INVALID_THUMB_VALUE )
610 {
611 // use the current if not specified
612 value = m_value;
613 }
614
615 bool isVertical = IsVert();
616
617 wxRect rectShaft;
618 if ( rectShaftIn )
619 {
620 rectShaft = *rectShaftIn;
621 }
622 else // no shaft rect provided, calc it
623 {
624 rectShaft = GetShaftRect();
625 }
626
627 wxCoord lenShaft,
628 lenThumb;
629 wxCoord *p;
630
631 wxRect rectThumb(rectShaft.GetPosition(), GetThumbSize());
632 if ( isVertical )
633 {
634 rectThumb.x += (rectShaft.width - rectThumb.width) / 2;
635
636 lenThumb = rectThumb.height;
637 lenShaft = rectShaft.height;
638 p = &rectThumb.y;
639 }
640 else // horz
641 {
642 rectThumb.y += (rectShaft.height - rectThumb.height) / 2;
643
644 lenThumb = rectThumb.width;
645 lenShaft = rectShaft.width;
646 p = &rectThumb.x;
647 }
648
649 // the thumb must always be entirely inside the shaft limits, so the max
650 // position is not at lenShaft but at lenShaft - thumbSize
651 if ( m_max != m_min )
652 {
653 if ( isVertical )
654 {
655 *p += ((lenShaft - lenThumb)*(m_max - value))/(m_max - m_min);
656 }
657 else
658 { // horz
659 *p += ((lenShaft - lenThumb)*(value - m_min))/(m_max - m_min);
660 }
661 }
662
663 // calc the label rect
664 if ( HasLabels() && rectLabelOut )
665 {
666 long style = GetWindowStyle();
667 wxRect rectLabel = m_rectLabel;
668
669 // centre the label relatively to the thumb position
670 if (style & (wxSL_TOP|wxSL_BOTTOM))
671 {
672 rectLabel.x = rectThumb.x + (rectThumb.width - m_rectLabel.width)/2;
673 }
674 else if (style & (wxSL_LEFT|wxSL_RIGHT))
675 {
676 rectLabel.y = rectThumb.y + (rectThumb.height - m_rectLabel.height)/2;
677 }
678
679 *rectLabelOut = rectLabel;
680 }
681
682 if ( rectThumbOut )
683
684 *rectThumbOut = rectThumb;
685 }
686
687 // ----------------------------------------------------------------------------
688 // wxSlider drawing
689 // ----------------------------------------------------------------------------
690
691 wxString wxSlider::FormatValue(int value) const
692 {
693 return wxString::Format(_T("%d"), value);
694 }
695
696 void wxSlider::DoDraw(wxControlRenderer *renderer)
697 {
698 wxRenderer *rend = GetRenderer();
699 wxDC& dc = renderer->GetDC();
700 wxRect rectUpdate = GetUpdateClientRect();
701
702 wxOrientation orient = GetOrientation();
703 int flags = GetStateFlags();
704 long style = GetWindowStyle();
705
706 wxSize sz = GetThumbSize();
707 int len = IsVert() ? sz.x : sz.y;
708
709 // first draw the shaft
710 wxRect rectShaft = rend->GetSliderShaftRect(m_rectSlider, len, orient, style);
711 if ( rectUpdate.Intersects(rectShaft) )
712 {
713 rend->DrawSliderShaft(dc, m_rectSlider, len, orient, flags, style);
714 }
715
716 // calculate the thumb position in pixels and draw it
717 wxRect rectThumb, rectLabel;
718 CalcThumbRect(&rectShaft, &rectThumb, &rectLabel);
719
720 // then draw the ticks
721 if ( HasTicks() && rectUpdate.Intersects(m_rectTicks) )
722 {
723 rend->DrawSliderTicks(dc, m_rectSlider, len, orient,
724 m_min, m_max, m_tickFreq, flags, style);
725 }
726
727 // then draw the thumb
728 if ( rectUpdate.Intersects(rectThumb) )
729 {
730 rend->DrawSliderThumb(dc, rectThumb, orient, flags | m_thumbFlags, style);
731 }
732
733 // finally, draw the label near the thumb
734 if ( HasLabels() && rectUpdate.Intersects(rectLabel) )
735 {
736 // align it to be close to the shaft
737 int align = 0;
738 if (style & wxSL_TOP)
739 {
740 align = wxALIGN_CENTRE_HORIZONTAL|wxALIGN_TOP;
741 }
742 else if (style & wxSL_BOTTOM)
743 {
744 align = wxALIGN_CENTRE_HORIZONTAL|wxALIGN_BOTTOM;
745 }
746 else if (style & wxSL_LEFT)
747 {
748 align = wxALIGN_CENTRE_VERTICAL|wxALIGN_LEFT;
749 }
750 else if (style & wxSL_RIGHT)
751 {
752 align = wxALIGN_CENTRE_VERTICAL|wxALIGN_RIGHT;
753 }
754
755 dc.SetFont(GetFont());
756 dc.SetTextForeground(GetForegroundColour());
757
758 // the slider label is never drawn focused
759 rend->DrawLabel(dc, FormatValue(m_value), rectLabel,
760 flags & ~wxCONTROL_FOCUSED, align);
761 }
762 }
763
764 // ----------------------------------------------------------------------------
765 // wxSlider input processing
766 // ----------------------------------------------------------------------------
767
768 bool wxSlider::PerformAction(const wxControlAction& action,
769 long numArg,
770 const wxString& strArg)
771 {
772 wxEventType scrollEvent = wxEVT_NULL;
773 int value;
774 bool valueChanged = true;
775
776 if ( action == wxACTION_SLIDER_START )
777 {
778 scrollEvent = wxEVT_SCROLL_TOP;
779 value = m_min;
780 }
781 else if ( action == wxACTION_SLIDER_END )
782 {
783 scrollEvent = wxEVT_SCROLL_BOTTOM;
784 value = m_max;
785 }
786 else if ( action == wxACTION_SLIDER_PAGE_CHANGE )
787 {
788 value = NormalizeValue(m_value + numArg * GetPageSize());
789 }
790 else if ( action == wxACTION_SLIDER_LINE_UP )
791 {
792 scrollEvent = wxEVT_SCROLL_LINEUP;
793 value = NormalizeValue(m_value + +GetLineSize());
794 }
795 else if ( action == wxACTION_SLIDER_LINE_DOWN )
796 {
797 scrollEvent = wxEVT_SCROLL_LINEDOWN;
798 value = NormalizeValue(m_value + -GetLineSize());
799 }
800 else if ( action == wxACTION_SLIDER_PAGE_UP )
801 {
802 scrollEvent = wxEVT_SCROLL_PAGEUP;
803 value = NormalizeValue(m_value + +GetPageSize());
804 }
805 else if ( action == wxACTION_SLIDER_PAGE_DOWN )
806 {
807 scrollEvent = wxEVT_SCROLL_PAGEDOWN;
808 value = NormalizeValue(m_value + -GetPageSize());
809 }
810 else if ( action == wxACTION_SLIDER_THUMB_DRAG ||
811 action == wxACTION_SLIDER_THUMB_MOVE )
812 {
813 scrollEvent = wxEVT_SCROLL_THUMBTRACK;
814
815 // we shouldn't generate a command event about this change but we still
816 // should update our value and the slider appearance
817 valueChanged = false;
818 m_value =
819 value = (int)numArg;
820 Refresh();
821 }
822 else if ( action == wxACTION_SLIDER_THUMB_RELEASE )
823 {
824 scrollEvent = wxEVT_SCROLL_THUMBRELEASE;
825 value = (int)numArg;
826 }
827 else
828 {
829 return wxControl::PerformAction(action, numArg, strArg);
830 }
831
832 // update wxSlider current value and generate wxCommandEvent, except while
833 // dragging the thumb
834 if ( valueChanged )
835 ChangeValueTo(value);
836
837 // also generate more precise wxScrollEvent if applicable
838 if ( scrollEvent != wxEVT_NULL )
839 {
840 wxScrollEvent event(scrollEvent, GetId());
841 event.SetPosition(value);
842 event.SetEventObject( this );
843 GetEventHandler()->ProcessEvent(event);
844 }
845
846 return true;
847 }
848
849 /* static */
850 wxInputHandler *wxSlider::GetStdInputHandler(wxInputHandler *handlerDef)
851 {
852 static wxStdSliderInputHandler s_handler(handlerDef);
853
854 return &s_handler;
855 }
856
857 // ----------------------------------------------------------------------------
858 // wxSlider implementation of wxControlWithThumb interface
859 // ----------------------------------------------------------------------------
860
861 wxScrollThumb::Shaft wxSlider::HitTest(const wxPoint& pt) const
862 {
863 wxRect rectShaft = GetShaftRect();
864 wxRect rectThumb;
865 CalcThumbRect(&rectShaft, &rectThumb, NULL);
866
867 // check for possible shaft or thumb hit
868 if (!rectShaft.Contains(pt) && !rectThumb.Contains(pt))
869 {
870 return wxScrollThumb::Shaft_None;
871 }
872
873 // the position to test and the start and end of the thumb
874 wxCoord x, x1, x2, x3, x4;
875 if (IsVert())
876 {
877 x = pt.y;
878 x1 = rectThumb.GetBottom();
879 x2 = rectShaft.GetBottom();
880 x3 = rectShaft.GetTop();
881 x4 = rectThumb.GetTop();
882 }
883 else
884 { // horz
885 x = pt.x;
886 x1 = rectShaft.GetLeft();
887 x2 = rectThumb.GetLeft();
888 x3 = rectThumb.GetRight();
889 x4 = rectShaft.GetRight();
890 }
891 if ((x1 <= x) && (x < x2))
892 {
893 // or to the left
894 return wxScrollThumb::Shaft_Above;
895 }
896
897 if ((x3 < x) && (x <= x4)) {
898 // or to the right
899 return wxScrollThumb::Shaft_Below;
900 }
901
902 // where else can it be?
903 return wxScrollThumb::Shaft_Thumb;
904 }
905
906 wxCoord wxSlider::ThumbPosToPixel() const
907 {
908 wxRect rectThumb;
909 CalcThumbRect(NULL, &rectThumb, NULL);
910
911 return IsVert() ? rectThumb.y : rectThumb.x;
912 }
913
914 int wxSlider::PixelToThumbPos(wxCoord x) const
915 {
916 wxRect rectShaft = GetShaftRect();
917 wxSize sizeThumb = GetThumbSize();
918
919 wxCoord x0, len;
920 if ( IsVert() )
921 {
922 x0 = rectShaft.y;
923 len = rectShaft.height - sizeThumb.y;
924 }
925 else // horz
926 {
927 x0 = rectShaft.x;
928 len = rectShaft.width - sizeThumb.x;
929 }
930
931 int pos = m_min;
932 if ( len > 0 )
933 {
934 if ( x > x0 )
935 {
936 pos += ((x - x0) * (m_max - m_min)) / len;
937 if ( pos > m_max )
938 pos = m_max;
939 }
940 //else: x <= x0, leave pos = min
941 }
942
943 return pos;
944 }
945
946 void wxSlider::SetShaftPartState(wxScrollThumb::Shaft shaftPart,
947 int flag,
948 bool set)
949 {
950 // for now we ignore the flags for the shaft as no renderer uses them
951 // anyhow
952 if ( shaftPart == wxScrollThumb::Shaft_Thumb )
953 {
954 if ( set )
955 m_thumbFlags |= flag;
956 else
957 m_thumbFlags &= ~flag;
958
959 Refresh();
960 }
961 }
962
963 void wxSlider::OnThumbDragStart(int pos)
964 {
965 if (IsVert())
966 {
967 PerformAction(wxACTION_SLIDER_THUMB_DRAG, m_max - pos);
968 }
969 else
970 {
971 PerformAction(wxACTION_SLIDER_THUMB_DRAG, pos);
972 }
973 }
974
975 void wxSlider::OnThumbDrag(int pos)
976 {
977 if (IsVert())
978 {
979 PerformAction(wxACTION_SLIDER_THUMB_MOVE, m_max - pos);
980 }
981 else
982 {
983 PerformAction(wxACTION_SLIDER_THUMB_MOVE, pos);
984 }
985 }
986
987 void wxSlider::OnThumbDragEnd(int pos)
988 {
989 if (IsVert())
990 {
991 PerformAction(wxACTION_SLIDER_THUMB_RELEASE, m_max - pos);
992 }
993 else
994 {
995 PerformAction(wxACTION_SLIDER_THUMB_RELEASE, pos);
996 }
997 }
998
999 void wxSlider::OnPageScrollStart()
1000 {
1001 // we do nothing here
1002 }
1003
1004 bool wxSlider::OnPageScroll(int pageInc)
1005 {
1006 int value = GetValue();
1007 PerformAction(wxACTION_SLIDER_PAGE_CHANGE, pageInc);
1008
1009 return GetValue() != value;
1010 }
1011
1012 // ----------------------------------------------------------------------------
1013 // wxStdSliderInputHandler
1014 // ----------------------------------------------------------------------------
1015
1016 bool wxStdSliderInputHandler::HandleKey(wxInputConsumer *consumer,
1017 const wxKeyEvent& event,
1018 bool pressed)
1019 {
1020 if ( pressed )
1021 {
1022 int keycode = event.GetKeyCode();
1023
1024 wxControlAction action;
1025 switch ( keycode )
1026 {
1027 case WXK_HOME:
1028 action = wxACTION_SLIDER_END;
1029 break;
1030
1031 case WXK_END:
1032 action = wxACTION_SLIDER_START;
1033 break;
1034
1035 case WXK_RIGHT:
1036 case WXK_UP:
1037 action = wxACTION_SLIDER_LINE_UP;
1038 break;
1039
1040 case WXK_LEFT:
1041 case WXK_DOWN:
1042 action = wxACTION_SLIDER_LINE_DOWN;
1043 break;
1044
1045 case WXK_PAGEUP:
1046 action = wxACTION_SLIDER_PAGE_UP;
1047 break;
1048
1049 case WXK_PAGEDOWN:
1050 action = wxACTION_SLIDER_PAGE_DOWN;
1051 break;
1052 }
1053
1054 if ( !action.IsEmpty() )
1055 {
1056 consumer->PerformAction(action);
1057
1058 return true;
1059 }
1060 }
1061
1062 return wxStdInputHandler::HandleKey(consumer, event, pressed);
1063 }
1064
1065 bool wxStdSliderInputHandler::HandleMouse(wxInputConsumer *consumer,
1066 const wxMouseEvent& event)
1067 {
1068 wxSlider *slider = wxStaticCast(consumer->GetInputWindow(), wxSlider);
1069
1070 if ( slider->GetThumb().HandleMouse(event) )
1071 {
1072 // processed by the thumb
1073 return false;
1074 }
1075
1076 return wxStdInputHandler::HandleMouse(consumer, event);
1077 }
1078
1079 bool wxStdSliderInputHandler::HandleMouseMove(wxInputConsumer *consumer,
1080 const wxMouseEvent& event)
1081 {
1082 wxSlider *slider = wxStaticCast(consumer->GetInputWindow(), wxSlider);
1083
1084 if ( slider->GetThumb().HandleMouseMove(event) )
1085 {
1086 // processed by the thumb
1087 return false;
1088 }
1089
1090 return wxStdInputHandler::HandleMouseMove(consumer, event);
1091 }
1092
1093 bool
1094 wxStdSliderInputHandler::HandleFocus(wxInputConsumer * WXUNUSED(consumer),
1095 const wxFocusEvent& WXUNUSED(event))
1096 {
1097 // slider appearance changes when it gets/loses focus
1098 return true;
1099 }
1100
1101 #endif // wxUSE_SLIDER