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