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