source: trunk/src/widgets/qslider.cpp

Last change on this file was 174, checked in by dmik, 18 years ago

Styles: Implemented the first version of the Warp4 style (contributed by Cornelis Bockemuehl).

  • Property svn:keywords set to Id
File size: 20.6 KB
Line 
1/****************************************************************************
2** $Id: qslider.cpp 174 2007-11-06 22:27:57Z dmik $
3**
4** Implementation of QSlider class
5**
6** Created : 961019
7**
8** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
9**
10** This file is part of the widgets module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "qslider.h"
39#ifndef QT_NO_SLIDER
40#include "qpainter.h"
41#include "qdrawutil.h"
42#include "qtimer.h"
43#include "qbitmap.h"
44#include "qapplication.h"
45#include "qstyle.h"
46#if defined(QT_ACCESSIBILITY_SUPPORT)
47#include "qaccessible.h"
48#endif
49
50static const int thresholdTime = 300;
51static const int repeatTime = 100;
52
53struct QSliderPrivate
54{
55 // ### move these to QSlider in Qt 4.0
56 int sliderStartVal;
57 QSliderPrivate() : sliderStartVal( 0 ) { }
58};
59
60
61/*!
62 \class QSlider
63 \brief The QSlider widget provides a vertical or horizontal slider.
64
65 \ingroup basic
66 \mainclass
67
68 The slider is the classic widget for controlling a bounded value.
69 It lets the user move a slider along a horizontal or vertical
70 groove and translates the slider's position into an integer value
71 within the legal range.
72
73 QSlider inherits QRangeControl, which provides the "integer" side
74 of the slider. setRange() and value() are likely to be used by
75 practically all slider users; see the \l QRangeControl
76 documentation for information about the many other functions that
77 class provides.
78
79 The main functions offered by the slider itself are tickmark and
80 orientation control; you can use setTickmarks() to indicate where
81 you want the tickmarks to be, setTickInterval() to indicate how
82 many of them you want and setOrientation() to indicate whether the
83 slider is to be horizontal or vertical.
84
85 A slider accepts focus on Tab and uses the mouse wheel and a
86 suitable keyboard interface.
87
88 <img src=qslider-m.png> <img src=qslider-w.png>
89
90 \important setRange
91
92 \sa QScrollBar QSpinBox
93 \link guibooks.html#fowler GUI Design Handbook: Slider\endlink
94*/
95
96
97/*!
98 \enum QSlider::TickSetting
99
100 This enum specifies where the tickmarks are to be drawn relative
101 to the slider's groove and the handle the user moves.
102
103 \value NoMarks do not draw any tickmarks.
104 \value Both draw tickmarks on both sides of the groove.
105 \value Above draw tickmarks above the (horizontal) slider
106 \value Below draw tickmarks below the (horizontal) slider
107 \value Left draw tickmarks to the left of the (vertical) slider
108 \value Right draw tickmarks to the right of the (vertical) slider
109*/
110
111
112/*!
113 Constructs a vertical slider.
114
115 The \a parent and \a name arguments are sent on to the QWidget
116 constructor.
117*/
118
119QSlider::QSlider( QWidget *parent, const char *name )
120 : QWidget( parent, name )
121{
122 orient = Vertical;
123 init();
124}
125
126/*!
127 Constructs a slider.
128
129 The \a orientation must be \l Qt::Vertical or \l Qt::Horizontal.
130
131 The \a parent and \a name arguments are sent on to the QWidget
132 constructor.
133*/
134
135QSlider::QSlider( Orientation orientation, QWidget *parent, const char *name )
136 : QWidget( parent, name )
137{
138 orient = orientation;
139 init();
140}
141
142/*!
143 Constructs a slider whose value can never be smaller than \a
144 minValue or greater than \a maxValue, whose page step size is \a
145 pageStep and whose value is initially \a value (which is
146 guaranteed to be in range using bound()).
147
148 If \a orientation is \c Qt::Vertical the slider is vertical and if it
149 is \c Qt::Horizontal the slider is horizontal.
150
151 The \a parent and \a name arguments are sent on to the QWidget
152 constructor.
153*/
154
155QSlider::QSlider( int minValue, int maxValue, int pageStep,
156 int value, Orientation orientation,
157 QWidget *parent, const char *name )
158 : QWidget( parent, name ),
159 QRangeControl( minValue, maxValue, 1, pageStep, value )
160{
161 orient = orientation;
162 init();
163 sliderVal = value;
164}
165
166/*!
167 Destructor.
168*/
169QSlider::~QSlider()
170{
171 delete d;
172}
173
174void QSlider::init()
175{
176 d = new QSliderPrivate;
177 timer = 0;
178 sliderPos = 0;
179 sliderVal = 0;
180 clickOffset = 0;
181 state = Idle;
182 track = TRUE;
183 ticks = NoMarks;
184 tickInt = 0;
185
186 if ( style().styleHint( QStyle::SH_GUIStyle ) == QStyle::PMStyle )
187 setFocusPolicy( StrongFocus );
188 else
189 setFocusPolicy( TabFocus );
190 initTicks();
191
192 QSizePolicy sp( QSizePolicy::Expanding, QSizePolicy::Fixed );
193 if ( orient == Vertical )
194 sp.transpose();
195 setSizePolicy( sp );
196 clearWState( WState_OwnSizePolicy );
197}
198
199
200/*
201 Does what's needed when someone changes the tickmark status.
202*/
203
204void QSlider::initTicks()
205{
206 tickOffset = style().pixelMetric( QStyle::PM_SliderTickmarkOffset, this );
207}
208
209
210/*!
211 \property QSlider::tracking
212 \brief whether slider tracking is enabled
213
214 If tracking is enabled (the default), the slider emits the
215 valueChanged() signal whenever the slider is being dragged. If
216 tracking is disabled, the slider emits the valueChanged() signal
217 when the user releases the mouse button (unless the value happens
218 to be the same as before).
219*/
220
221void QSlider::setTracking( bool enable )
222{
223 track = enable;
224}
225
226
227/*!
228 \fn void QSlider::valueChanged( int value )
229
230 This signal is emitted when the slider value is changed, with the
231 new slider \a value as its argument.
232*/
233
234/*!
235 \fn void QSlider::sliderPressed()
236
237 This signal is emitted when the user presses the slider with the
238 mouse.
239*/
240
241/*!
242 \fn void QSlider::sliderMoved( int value )
243
244 This signal is emitted when the slider is dragged, with the new
245 slider \a value as its argument.
246*/
247
248/*!
249 \fn void QSlider::sliderReleased()
250
251 This signal is emitted when the user releases the slider with the mouse.
252*/
253
254/*
255 Calculates slider position corresponding to value \a v.
256*/
257
258int QSlider::positionFromValue( int v ) const
259{
260 int a = available();
261 int x = QRangeControl::positionFromValue( v, a );
262 if ( orient == Horizontal && QApplication::reverseLayout() )
263 x = a - x;
264 return x;
265}
266
267/*
268 Returns the available space in which the slider can move.
269*/
270
271int QSlider::available() const
272{
273 return style().pixelMetric( QStyle::PM_SliderSpaceAvailable, this );
274}
275
276/*
277 Calculates a value corresponding to slider position \a p.
278*/
279
280int QSlider::valueFromPosition( int p ) const
281{
282 int a = available();
283 int x = QRangeControl::valueFromPosition( p, a );
284 if ( orient == Horizontal && QApplication::reverseLayout() )
285 x = maxValue() + minValue() - x;
286 return x;
287}
288
289/*!
290 Implements the virtual QRangeControl function.
291*/
292
293void QSlider::rangeChange()
294{
295 int newPos = positionFromValue( value() );
296 if ( newPos != sliderPos ) {
297 reallyMoveSlider( newPos );
298 }
299}
300
301/*!
302 Implements the virtual QRangeControl function.
303*/
304
305void QSlider::valueChange()
306{
307 if ( sliderVal != value() ) {
308 int newPos = positionFromValue( value() );
309 sliderVal = value();
310 reallyMoveSlider( newPos );
311 }
312 emit valueChanged(value());
313#if defined(QT_ACCESSIBILITY_SUPPORT)
314 QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged );
315#endif
316}
317
318
319/*!
320 \reimp
321*/
322void QSlider::resizeEvent( QResizeEvent * )
323{
324 rangeChange();
325 initTicks();
326}
327
328
329/*!
330 Reimplements the virtual function QWidget::setPalette().
331
332 Sets the background color to the mid color for Motif style sliders
333 using palette \a p.
334*/
335
336void QSlider::setPalette( const QPalette &p )
337{
338 QWidget::setPalette( p );
339}
340
341
342
343/*!
344 \property QSlider::orientation
345 \brief the slider's orientation
346
347 The orientation must be \l Qt::Vertical (the default) or \l
348 Qt::Horizontal.
349*/
350
351void QSlider::setOrientation( Orientation orientation )
352{
353 if ( orientation == orient )
354 return;
355
356 if ( !testWState( WState_OwnSizePolicy ) ) {
357 QSizePolicy sp = sizePolicy();
358 sp.transpose();
359 setSizePolicy( sp );
360 clearWState( WState_OwnSizePolicy );
361 }
362
363 orient = orientation;
364
365 rangeChange();
366 update();
367}
368
369/*!
370 \fn int QSlider::sliderStart() const
371
372 Returns the start position of the slider.
373*/
374
375
376/*!
377 Returns the slider handle rectangle. (This is the visual marker
378 that the user can move.)
379*/
380
381QRect QSlider::sliderRect() const
382{
383 return style().querySubControlMetrics( QStyle::CC_Slider, this,
384 QStyle::SC_SliderHandle );
385}
386
387/*
388 Performs the actual moving of the slider.
389*/
390
391void QSlider::reallyMoveSlider( int newPos )
392{
393 QRegion oldR(sliderRect());
394 sliderPos = newPos;
395 QRegion newR(sliderRect());
396
397 /* just the one repaint if no background */
398 if (backgroundMode() == NoBackground)
399 repaint(newR | oldR, FALSE);
400 else {
401 repaint(oldR.subtract(newR));
402 repaint(newR, FALSE);
403 }
404}
405
406
407/*!
408 \reimp
409*/
410void QSlider::paintEvent( QPaintEvent * )
411{
412 QPainter p( this );
413
414 QStyle::SFlags flags = QStyle::Style_Default;
415 if (isEnabled())
416 flags |= QStyle::Style_Enabled;
417 if (hasFocus())
418 flags |= QStyle::Style_HasFocus;
419
420 QStyle::SCFlags sub = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle;
421 if ( tickmarks() != NoMarks )
422 sub |= QStyle::SC_SliderTickmarks;
423
424 style().drawComplexControl( QStyle::CC_Slider, &p, this, rect(), colorGroup(),
425 flags, sub, state == Dragging ? QStyle::SC_SliderHandle : QStyle::SC_None );
426}
427
428
429/*!
430 \reimp
431*/
432void QSlider::mousePressEvent( QMouseEvent *e )
433{
434 int slideLength = style().pixelMetric( QStyle::PM_SliderLength, this );
435 resetState();
436 d->sliderStartVal = sliderVal;
437 QRect r = sliderRect();
438
439 if ( e->button() == RightButton )
440 return;
441
442 if ( r.contains( e->pos() ) ) {
443 state = Dragging;
444 clickOffset = (QCOORD)( goodPart( e->pos() ) - sliderPos );
445 emit sliderPressed();
446 } else if ( e->button() == MidButton ) {
447 int pos = goodPart( e->pos() );
448 moveSlider( pos - slideLength / 2 );
449 state = Dragging;
450 clickOffset = slideLength / 2;
451 } else if ( orient == Horizontal && e->pos().x() < r.left() //### goodPart
452 || orient == Vertical && e->pos().y() < r.top() ) {
453 if ( QApplication::reverseLayout() ) {
454 state = TimingUp;
455 addPage();
456 } else {
457 state = TimingDown;
458 subtractPage();
459 }
460 if ( !timer )
461 timer = new QTimer( this );
462 connect( timer, SIGNAL(timeout()), SLOT(repeatTimeout()) );
463 timer->start( thresholdTime, TRUE );
464 } else if ( orient == Horizontal && e->pos().x() > r.right() //### goodPart
465 || orient == Vertical && e->pos().y() > r.bottom() ) {
466 if ( QApplication::reverseLayout() ) {
467 state = TimingDown;
468 subtractPage();
469 } else {
470 state = TimingUp;
471 addPage();
472 }
473 if ( !timer )
474 timer = new QTimer( this );
475 connect( timer, SIGNAL(timeout()), SLOT(repeatTimeout()) );
476 timer->start( thresholdTime, TRUE );
477 }
478 update( sliderRect() );
479}
480
481/*!
482 \reimp
483*/
484void QSlider::mouseMoveEvent( QMouseEvent *e )
485{
486 if ( state != Dragging )
487 return;
488
489 QRect r = rect();
490 int m = style().pixelMetric( QStyle::PM_MaximumDragDistance,
491 this );
492 if ( m >= 0 ) {
493 if ( orientation() == Horizontal )
494 r.setRect( r.x() - m, r.y() - 2*m/3,
495 r.width() + 2*m, r.height() + 3*m );
496 else
497 r.setRect( r.x() - 2*m/3, r.y() - m,
498 r.width() + 3*m, r.height() + 2*m );
499 if ( !r.contains( e->pos() ) ) {
500 moveSlider( positionFromValue(d->sliderStartVal) );
501 return;
502 }
503 }
504
505 int pos = goodPart( e->pos() );
506 moveSlider( pos - clickOffset );
507}
508
509/*!
510 \reimp
511*/
512#ifndef QT_NO_WHEELEVENT
513void QSlider::wheelEvent( QWheelEvent * e )
514{
515 if ( e->orientation() != orientation() && !rect().contains(e->pos()) )
516 return;
517
518 static float offset = 0;
519 static QSlider* offset_owner = 0;
520 if (offset_owner != this){
521 offset_owner = this;
522 offset = 0;
523 }
524 offset += -e->delta()*QMAX(pageStep(),lineStep())/120;
525 if (QABS(offset)<1)
526 return;
527 setValue( value() + int(offset) );
528 offset -= int(offset);
529 e->accept();
530}
531#endif
532
533/*!
534 \reimp
535*/
536void QSlider::mouseReleaseEvent( QMouseEvent * )
537{
538 resetState();
539 update( sliderRect() );
540}
541
542/*!
543 \reimp
544*/
545void QSlider::focusInEvent( QFocusEvent * e)
546{
547 QWidget::focusInEvent( e );
548}
549
550/*!
551 \reimp
552*/
553void QSlider::focusOutEvent( QFocusEvent * e )
554{
555 QWidget::focusOutEvent( e );
556}
557
558/*!
559 Moves the left (or top) edge of the slider to position \a pos. The
560 slider is actually moved to the step position nearest the given \a
561 pos.
562*/
563
564void QSlider::moveSlider( int pos )
565{
566 int a = available();
567 int newPos = QMIN( a, QMAX( 0, pos ) );
568 int newVal = valueFromPosition( newPos );
569 if (style().styleHint(QStyle::SH_Slider_SnapToValue, this))
570 newPos = positionFromValue( newVal );
571 if ( sliderPos != newPos )
572 reallyMoveSlider( newPos );
573 if ( sliderVal != newVal ) {
574 sliderVal = newVal;
575 emit sliderMoved( sliderVal );
576 }
577 if ( tracking() && sliderVal != value() )
578 setValue( sliderVal );
579
580}
581
582
583/*
584 Resets all state information and stops the timer.
585*/
586
587void QSlider::resetState()
588{
589 if ( timer ) {
590 timer->stop();
591 timer->disconnect();
592 }
593 switch ( state ) {
594 case TimingUp:
595 case TimingDown:
596 break;
597 case Dragging: {
598 setValue( valueFromPosition( sliderPos ) );
599 emit sliderReleased();
600 break;
601 }
602 case Idle:
603 break;
604 default:
605 qWarning("QSlider: (%s) in wrong state", name( "unnamed" ) );
606 }
607 state = Idle;
608}
609
610
611/*!
612 \reimp
613*/
614void QSlider::keyPressEvent( QKeyEvent *e )
615{
616 bool sloppy = bool(style().styleHint(QStyle::SH_Slider_SloppyKeyEvents, this));
617 switch ( e->key() ) {
618 case Key_Left:
619 if ( sloppy || orient == Horizontal ) {
620 if (QApplication::reverseLayout())
621 addLine();
622 else
623 subtractLine();
624 }
625 break;
626 case Key_Right:
627 if ( sloppy || orient == Horizontal ) {
628 if (QApplication::reverseLayout())
629 subtractLine();
630 else
631 addLine();
632 }
633 break;
634 case Key_Up:
635 if ( sloppy || orient == Vertical )
636 subtractLine();
637 break;
638 case Key_Down:
639 if ( sloppy || orient == Vertical )
640 addLine();
641 break;
642 case Key_Prior:
643 subtractPage();
644 break;
645 case Key_Next:
646 addPage();
647 break;
648 case Key_Home:
649 setValue( minValue() );
650 break;
651 case Key_End:
652 setValue( maxValue() );
653 break;
654 default:
655 e->ignore();
656 return;
657 }
658}
659
660void QSlider::setValue( int value )
661{
662 QRangeControl::setValue( value );
663#if defined(QT_ACCESSIBILITY_SUPPORT)
664 QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged );
665#endif
666}
667
668
669/*! \reimp
670*/
671
672void QSlider::addLine()
673{
674 QRangeControl::addLine();
675}
676
677/*! \reimp
678*/
679
680void QSlider::subtractLine()
681{
682 QRangeControl::subtractLine();
683}
684
685/*!
686 Moves the slider one pageStep() up or right.
687*/
688
689void QSlider::addStep()
690{
691 addPage();
692}
693
694
695/*!
696 Moves the slider one pageStep() down or left.
697*/
698
699void QSlider::subtractStep()
700{
701 subtractPage();
702}
703
704
705/*
706 Waits for autorepeat.
707*/
708
709void QSlider::repeatTimeout()
710{
711 Q_ASSERT( timer );
712 timer->disconnect();
713 if ( state == TimingDown )
714 connect( timer, SIGNAL(timeout()), SLOT(subtractStep()) );
715 else if ( state == TimingUp )
716 connect( timer, SIGNAL(timeout()), SLOT(addStep()) );
717 timer->start( repeatTime, FALSE );
718}
719
720
721/*
722 Returns the relevant dimension of \a p.
723*/
724
725int QSlider::goodPart( const QPoint &p ) const
726{
727 return (orient == Horizontal) ? p.x() : p.y();
728}
729
730/*!
731 \reimp
732*/
733QSize QSlider::sizeHint() const
734{
735 constPolish();
736 const int length = 84, tickSpace = 5;
737 int thick = style().pixelMetric( QStyle::PM_SliderThickness, this );
738 if ( ticks & Above )
739 thick += tickSpace;
740 if ( ticks & Below )
741 thick += tickSpace;
742 int w = thick, h = length;
743 if ( orient == Horizontal ) {
744 w = length;
745 h = thick;
746 }
747 return (style().sizeFromContents(QStyle::CT_Slider, this,
748 QSize(w, h)).expandedTo(QApplication::globalStrut()));
749}
750
751
752
753/*!
754 \reimp
755*/
756
757QSize QSlider::minimumSizeHint() const
758{
759 QSize s = sizeHint();
760 int length = style().pixelMetric(QStyle::PM_SliderLength, this);
761 if ( orient == Horizontal )
762 s.setWidth( length );
763 else
764 s.setHeight( length );
765
766 return s;
767}
768
769/*! \fn void QSlider::setSizePolicy( QSizePolicy::SizeType, QSizePolicy::SizeType, bool )
770 \reimp
771*/
772
773/*! \reimp */
774void QSlider::setSizePolicy( QSizePolicy sp )
775{
776 // ## remove 4.0
777 QWidget::setSizePolicy( sp );
778}
779
780/*! \reimp */
781QSizePolicy QSlider::sizePolicy() const
782{
783 // ### 4.0 remove this reimplementation
784 return QWidget::sizePolicy();
785}
786
787/*!
788 \property QSlider::tickmarks
789 \brief the tickmark settings for this slider
790
791 The valid values are in \l{QSlider::TickSetting}. The default is
792 \c NoMarks.
793
794 \sa tickInterval
795*/
796
797void QSlider::setTickmarks( TickSetting s )
798{
799 ticks = s;
800 initTicks();
801 update();
802}
803
804
805/*!
806 \property QSlider::tickInterval
807 \brief the interval between tickmarks
808
809 This is a value interval, not a pixel interval. If it is 0, the
810 slider will choose between lineStep() and pageStep(). The initial
811 value of tickInterval is 0.
812
813 \sa QRangeControl::lineStep(), QRangeControl::pageStep()
814*/
815
816void QSlider::setTickInterval( int i )
817{
818 tickInt = QMAX( 0, i );
819 update();
820}
821
822
823/*!
824 \reimp
825*/
826void QSlider::styleChange( QStyle& old )
827{
828 QWidget::styleChange( old );
829}
830
831/*!
832 \property QSlider::minValue
833 \brief the current minimum value of the slider
834
835 When setting this property, the \l QSlider::maxValue is adjusted,
836 if necessary, to ensure that the range remains valid.
837
838 \sa setRange()
839*/
840int QSlider::minValue() const
841{
842 return QRangeControl::minValue();
843}
844
845/*!
846 \property QSlider::maxValue
847 \brief the current maximum value of the slider
848
849 When setting this property, the \l QSlider::minValue is adjusted,
850 if necessary, to ensure that the range remains valid.
851
852 \sa setRange()
853*/
854int QSlider::maxValue() const
855{
856 return QRangeControl::maxValue();
857}
858
859void QSlider::setMinValue( int minVal )
860{
861 QRangeControl::setMinValue( minVal );
862}
863
864void QSlider::setMaxValue( int maxVal )
865{
866 QRangeControl::setMaxValue( maxVal );
867}
868
869/*!
870 \property QSlider::lineStep
871 \brief the current line step
872
873 When setting lineStep, the virtual stepChange() function will be
874 called if the new line step is different from the previous
875 setting.
876
877 \sa setSteps() QRangeControl::pageStep() setRange()
878*/
879int QSlider::lineStep() const
880{
881 return QRangeControl::lineStep();
882}
883
884/*!
885 \property QSlider::pageStep
886 \brief the current page step
887
888 When setting pageStep, the virtual stepChange() function will be
889 called if the new page step is different from the previous
890 setting.
891
892 \sa QRangeControl::setSteps() setLineStep() setRange()
893*/
894
895int QSlider::pageStep() const
896{
897 return QRangeControl::pageStep();
898}
899
900void QSlider::setLineStep( int i )
901{
902 setSteps( i, pageStep() );
903}
904
905void QSlider::setPageStep( int i )
906{
907 setSteps( lineStep(), i );
908}
909
910/*!
911 \property QSlider::value
912 \brief the current slider value
913
914 \sa QRangeControl::value() prevValue()
915*/
916
917int QSlider::value() const
918{
919 return QRangeControl::value();
920}
921
922#endif
Note: See TracBrowser for help on using the repository browser.