source: vendor/trolltech/current/src/widgets/qslider.cpp

Last change on this file was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

  • Property svn:keywords set to Id
File size: 20.5 KB
Line 
1/****************************************************************************
2** $Id: qslider.cpp 2 2005-11-16 15:49:26Z 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 setFocusPolicy( TabFocus );
186 initTicks();
187
188 QSizePolicy sp( QSizePolicy::Expanding, QSizePolicy::Fixed );
189 if ( orient == Vertical )
190 sp.transpose();
191 setSizePolicy( sp );
192 clearWState( WState_OwnSizePolicy );
193}
194
195
196/*
197 Does what's needed when someone changes the tickmark status.
198*/
199
200void QSlider::initTicks()
201{
202 tickOffset = style().pixelMetric( QStyle::PM_SliderTickmarkOffset, this );
203}
204
205
206/*!
207 \property QSlider::tracking
208 \brief whether slider tracking is enabled
209
210 If tracking is enabled (the default), the slider emits the
211 valueChanged() signal whenever the slider is being dragged. If
212 tracking is disabled, the slider emits the valueChanged() signal
213 when the user releases the mouse button (unless the value happens
214 to be the same as before).
215*/
216
217void QSlider::setTracking( bool enable )
218{
219 track = enable;
220}
221
222
223/*!
224 \fn void QSlider::valueChanged( int value )
225
226 This signal is emitted when the slider value is changed, with the
227 new slider \a value as its argument.
228*/
229
230/*!
231 \fn void QSlider::sliderPressed()
232
233 This signal is emitted when the user presses the slider with the
234 mouse.
235*/
236
237/*!
238 \fn void QSlider::sliderMoved( int value )
239
240 This signal is emitted when the slider is dragged, with the new
241 slider \a value as its argument.
242*/
243
244/*!
245 \fn void QSlider::sliderReleased()
246
247 This signal is emitted when the user releases the slider with the mouse.
248*/
249
250/*
251 Calculates slider position corresponding to value \a v.
252*/
253
254int QSlider::positionFromValue( int v ) const
255{
256 int a = available();
257 int x = QRangeControl::positionFromValue( v, a );
258 if ( orient == Horizontal && QApplication::reverseLayout() )
259 x = a - x;
260 return x;
261}
262
263/*
264 Returns the available space in which the slider can move.
265*/
266
267int QSlider::available() const
268{
269 return style().pixelMetric( QStyle::PM_SliderSpaceAvailable, this );
270}
271
272/*
273 Calculates a value corresponding to slider position \a p.
274*/
275
276int QSlider::valueFromPosition( int p ) const
277{
278 int a = available();
279 int x = QRangeControl::valueFromPosition( p, a );
280 if ( orient == Horizontal && QApplication::reverseLayout() )
281 x = maxValue() + minValue() - x;
282 return x;
283}
284
285/*!
286 Implements the virtual QRangeControl function.
287*/
288
289void QSlider::rangeChange()
290{
291 int newPos = positionFromValue( value() );
292 if ( newPos != sliderPos ) {
293 reallyMoveSlider( newPos );
294 }
295}
296
297/*!
298 Implements the virtual QRangeControl function.
299*/
300
301void QSlider::valueChange()
302{
303 if ( sliderVal != value() ) {
304 int newPos = positionFromValue( value() );
305 sliderVal = value();
306 reallyMoveSlider( newPos );
307 }
308 emit valueChanged(value());
309#if defined(QT_ACCESSIBILITY_SUPPORT)
310 QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged );
311#endif
312}
313
314
315/*!
316 \reimp
317*/
318void QSlider::resizeEvent( QResizeEvent * )
319{
320 rangeChange();
321 initTicks();
322}
323
324
325/*!
326 Reimplements the virtual function QWidget::setPalette().
327
328 Sets the background color to the mid color for Motif style sliders
329 using palette \a p.
330*/
331
332void QSlider::setPalette( const QPalette &p )
333{
334 QWidget::setPalette( p );
335}
336
337
338
339/*!
340 \property QSlider::orientation
341 \brief the slider's orientation
342
343 The orientation must be \l Qt::Vertical (the default) or \l
344 Qt::Horizontal.
345*/
346
347void QSlider::setOrientation( Orientation orientation )
348{
349 if ( orientation == orient )
350 return;
351
352 if ( !testWState( WState_OwnSizePolicy ) ) {
353 QSizePolicy sp = sizePolicy();
354 sp.transpose();
355 setSizePolicy( sp );
356 clearWState( WState_OwnSizePolicy );
357 }
358
359 orient = orientation;
360
361 rangeChange();
362 update();
363}
364
365/*!
366 \fn int QSlider::sliderStart() const
367
368 Returns the start position of the slider.
369*/
370
371
372/*!
373 Returns the slider handle rectangle. (This is the visual marker
374 that the user can move.)
375*/
376
377QRect QSlider::sliderRect() const
378{
379 return style().querySubControlMetrics( QStyle::CC_Slider, this,
380 QStyle::SC_SliderHandle );
381}
382
383/*
384 Performs the actual moving of the slider.
385*/
386
387void QSlider::reallyMoveSlider( int newPos )
388{
389 QRegion oldR(sliderRect());
390 sliderPos = newPos;
391 QRegion newR(sliderRect());
392
393 /* just the one repaint if no background */
394 if (backgroundMode() == NoBackground)
395 repaint(newR | oldR, FALSE);
396 else {
397 repaint(oldR.subtract(newR));
398 repaint(newR, FALSE);
399 }
400}
401
402
403/*!
404 \reimp
405*/
406void QSlider::paintEvent( QPaintEvent * )
407{
408 QPainter p( this );
409
410 QStyle::SFlags flags = QStyle::Style_Default;
411 if (isEnabled())
412 flags |= QStyle::Style_Enabled;
413 if (hasFocus())
414 flags |= QStyle::Style_HasFocus;
415
416 QStyle::SCFlags sub = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle;
417 if ( tickmarks() != NoMarks )
418 sub |= QStyle::SC_SliderTickmarks;
419
420 style().drawComplexControl( QStyle::CC_Slider, &p, this, rect(), colorGroup(),
421 flags, sub, state == Dragging ? QStyle::SC_SliderHandle : QStyle::SC_None );
422}
423
424
425/*!
426 \reimp
427*/
428void QSlider::mousePressEvent( QMouseEvent *e )
429{
430 int slideLength = style().pixelMetric( QStyle::PM_SliderLength, this );
431 resetState();
432 d->sliderStartVal = sliderVal;
433 QRect r = sliderRect();
434
435 if ( e->button() == RightButton )
436 return;
437
438 if ( r.contains( e->pos() ) ) {
439 state = Dragging;
440 clickOffset = (QCOORD)( goodPart( e->pos() ) - sliderPos );
441 emit sliderPressed();
442 } else if ( e->button() == MidButton ) {
443 int pos = goodPart( e->pos() );
444 moveSlider( pos - slideLength / 2 );
445 state = Dragging;
446 clickOffset = slideLength / 2;
447 } else if ( orient == Horizontal && e->pos().x() < r.left() //### goodPart
448 || orient == Vertical && e->pos().y() < r.top() ) {
449 if ( QApplication::reverseLayout() ) {
450 state = TimingUp;
451 addPage();
452 } else {
453 state = TimingDown;
454 subtractPage();
455 }
456 if ( !timer )
457 timer = new QTimer( this );
458 connect( timer, SIGNAL(timeout()), SLOT(repeatTimeout()) );
459 timer->start( thresholdTime, TRUE );
460 } else if ( orient == Horizontal && e->pos().x() > r.right() //### goodPart
461 || orient == Vertical && e->pos().y() > r.bottom() ) {
462 if ( QApplication::reverseLayout() ) {
463 state = TimingDown;
464 subtractPage();
465 } else {
466 state = TimingUp;
467 addPage();
468 }
469 if ( !timer )
470 timer = new QTimer( this );
471 connect( timer, SIGNAL(timeout()), SLOT(repeatTimeout()) );
472 timer->start( thresholdTime, TRUE );
473 }
474 update( sliderRect() );
475}
476
477/*!
478 \reimp
479*/
480void QSlider::mouseMoveEvent( QMouseEvent *e )
481{
482 if ( state != Dragging )
483 return;
484
485 QRect r = rect();
486 int m = style().pixelMetric( QStyle::PM_MaximumDragDistance,
487 this );
488 if ( m >= 0 ) {
489 if ( orientation() == Horizontal )
490 r.setRect( r.x() - m, r.y() - 2*m/3,
491 r.width() + 2*m, r.height() + 3*m );
492 else
493 r.setRect( r.x() - 2*m/3, r.y() - m,
494 r.width() + 3*m, r.height() + 2*m );
495 if ( !r.contains( e->pos() ) ) {
496 moveSlider( positionFromValue(d->sliderStartVal) );
497 return;
498 }
499 }
500
501 int pos = goodPart( e->pos() );
502 moveSlider( pos - clickOffset );
503}
504
505/*!
506 \reimp
507*/
508#ifndef QT_NO_WHEELEVENT
509void QSlider::wheelEvent( QWheelEvent * e )
510{
511 if ( e->orientation() != orientation() && !rect().contains(e->pos()) )
512 return;
513
514 static float offset = 0;
515 static QSlider* offset_owner = 0;
516 if (offset_owner != this){
517 offset_owner = this;
518 offset = 0;
519 }
520 offset += -e->delta()*QMAX(pageStep(),lineStep())/120;
521 if (QABS(offset)<1)
522 return;
523 setValue( value() + int(offset) );
524 offset -= int(offset);
525 e->accept();
526}
527#endif
528
529/*!
530 \reimp
531*/
532void QSlider::mouseReleaseEvent( QMouseEvent * )
533{
534 resetState();
535 update( sliderRect() );
536}
537
538/*!
539 \reimp
540*/
541void QSlider::focusInEvent( QFocusEvent * e)
542{
543 QWidget::focusInEvent( e );
544}
545
546/*!
547 \reimp
548*/
549void QSlider::focusOutEvent( QFocusEvent * e )
550{
551 QWidget::focusOutEvent( e );
552}
553
554/*!
555 Moves the left (or top) edge of the slider to position \a pos. The
556 slider is actually moved to the step position nearest the given \a
557 pos.
558*/
559
560void QSlider::moveSlider( int pos )
561{
562 int a = available();
563 int newPos = QMIN( a, QMAX( 0, pos ) );
564 int newVal = valueFromPosition( newPos );
565 if (style().styleHint(QStyle::SH_Slider_SnapToValue, this))
566 newPos = positionFromValue( newVal );
567 if ( sliderPos != newPos )
568 reallyMoveSlider( newPos );
569 if ( sliderVal != newVal ) {
570 sliderVal = newVal;
571 emit sliderMoved( sliderVal );
572 }
573 if ( tracking() && sliderVal != value() )
574 setValue( sliderVal );
575
576}
577
578
579/*
580 Resets all state information and stops the timer.
581*/
582
583void QSlider::resetState()
584{
585 if ( timer ) {
586 timer->stop();
587 timer->disconnect();
588 }
589 switch ( state ) {
590 case TimingUp:
591 case TimingDown:
592 break;
593 case Dragging: {
594 setValue( valueFromPosition( sliderPos ) );
595 emit sliderReleased();
596 break;
597 }
598 case Idle:
599 break;
600 default:
601 qWarning("QSlider: (%s) in wrong state", name( "unnamed" ) );
602 }
603 state = Idle;
604}
605
606
607/*!
608 \reimp
609*/
610void QSlider::keyPressEvent( QKeyEvent *e )
611{
612 bool sloppy = bool(style().styleHint(QStyle::SH_Slider_SloppyKeyEvents, this));
613 switch ( e->key() ) {
614 case Key_Left:
615 if ( sloppy || orient == Horizontal ) {
616 if (QApplication::reverseLayout())
617 addLine();
618 else
619 subtractLine();
620 }
621 break;
622 case Key_Right:
623 if ( sloppy || orient == Horizontal ) {
624 if (QApplication::reverseLayout())
625 subtractLine();
626 else
627 addLine();
628 }
629 break;
630 case Key_Up:
631 if ( sloppy || orient == Vertical )
632 subtractLine();
633 break;
634 case Key_Down:
635 if ( sloppy || orient == Vertical )
636 addLine();
637 break;
638 case Key_Prior:
639 subtractPage();
640 break;
641 case Key_Next:
642 addPage();
643 break;
644 case Key_Home:
645 setValue( minValue() );
646 break;
647 case Key_End:
648 setValue( maxValue() );
649 break;
650 default:
651 e->ignore();
652 return;
653 }
654}
655
656void QSlider::setValue( int value )
657{
658 QRangeControl::setValue( value );
659#if defined(QT_ACCESSIBILITY_SUPPORT)
660 QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged );
661#endif
662}
663
664
665/*! \reimp
666*/
667
668void QSlider::addLine()
669{
670 QRangeControl::addLine();
671}
672
673/*! \reimp
674*/
675
676void QSlider::subtractLine()
677{
678 QRangeControl::subtractLine();
679}
680
681/*!
682 Moves the slider one pageStep() up or right.
683*/
684
685void QSlider::addStep()
686{
687 addPage();
688}
689
690
691/*!
692 Moves the slider one pageStep() down or left.
693*/
694
695void QSlider::subtractStep()
696{
697 subtractPage();
698}
699
700
701/*
702 Waits for autorepeat.
703*/
704
705void QSlider::repeatTimeout()
706{
707 Q_ASSERT( timer );
708 timer->disconnect();
709 if ( state == TimingDown )
710 connect( timer, SIGNAL(timeout()), SLOT(subtractStep()) );
711 else if ( state == TimingUp )
712 connect( timer, SIGNAL(timeout()), SLOT(addStep()) );
713 timer->start( repeatTime, FALSE );
714}
715
716
717/*
718 Returns the relevant dimension of \a p.
719*/
720
721int QSlider::goodPart( const QPoint &p ) const
722{
723 return (orient == Horizontal) ? p.x() : p.y();
724}
725
726/*!
727 \reimp
728*/
729QSize QSlider::sizeHint() const
730{
731 constPolish();
732 const int length = 84, tickSpace = 5;
733 int thick = style().pixelMetric( QStyle::PM_SliderThickness, this );
734 if ( ticks & Above )
735 thick += tickSpace;
736 if ( ticks & Below )
737 thick += tickSpace;
738 int w = thick, h = length;
739 if ( orient == Horizontal ) {
740 w = length;
741 h = thick;
742 }
743 return (style().sizeFromContents(QStyle::CT_Slider, this,
744 QSize(w, h)).expandedTo(QApplication::globalStrut()));
745}
746
747
748
749/*!
750 \reimp
751*/
752
753QSize QSlider::minimumSizeHint() const
754{
755 QSize s = sizeHint();
756 int length = style().pixelMetric(QStyle::PM_SliderLength, this);
757 if ( orient == Horizontal )
758 s.setWidth( length );
759 else
760 s.setHeight( length );
761
762 return s;
763}
764
765/*! \fn void QSlider::setSizePolicy( QSizePolicy::SizeType, QSizePolicy::SizeType, bool )
766 \reimp
767*/
768
769/*! \reimp */
770void QSlider::setSizePolicy( QSizePolicy sp )
771{
772 // ## remove 4.0
773 QWidget::setSizePolicy( sp );
774}
775
776/*! \reimp */
777QSizePolicy QSlider::sizePolicy() const
778{
779 // ### 4.0 remove this reimplementation
780 return QWidget::sizePolicy();
781}
782
783/*!
784 \property QSlider::tickmarks
785 \brief the tickmark settings for this slider
786
787 The valid values are in \l{QSlider::TickSetting}. The default is
788 \c NoMarks.
789
790 \sa tickInterval
791*/
792
793void QSlider::setTickmarks( TickSetting s )
794{
795 ticks = s;
796 initTicks();
797 update();
798}
799
800
801/*!
802 \property QSlider::tickInterval
803 \brief the interval between tickmarks
804
805 This is a value interval, not a pixel interval. If it is 0, the
806 slider will choose between lineStep() and pageStep(). The initial
807 value of tickInterval is 0.
808
809 \sa QRangeControl::lineStep(), QRangeControl::pageStep()
810*/
811
812void QSlider::setTickInterval( int i )
813{
814 tickInt = QMAX( 0, i );
815 update();
816}
817
818
819/*!
820 \reimp
821*/
822void QSlider::styleChange( QStyle& old )
823{
824 QWidget::styleChange( old );
825}
826
827/*!
828 \property QSlider::minValue
829 \brief the current minimum value of the slider
830
831 When setting this property, the \l QSlider::maxValue is adjusted,
832 if necessary, to ensure that the range remains valid.
833
834 \sa setRange()
835*/
836int QSlider::minValue() const
837{
838 return QRangeControl::minValue();
839}
840
841/*!
842 \property QSlider::maxValue
843 \brief the current maximum value of the slider
844
845 When setting this property, the \l QSlider::minValue is adjusted,
846 if necessary, to ensure that the range remains valid.
847
848 \sa setRange()
849*/
850int QSlider::maxValue() const
851{
852 return QRangeControl::maxValue();
853}
854
855void QSlider::setMinValue( int minVal )
856{
857 QRangeControl::setMinValue( minVal );
858}
859
860void QSlider::setMaxValue( int maxVal )
861{
862 QRangeControl::setMaxValue( maxVal );
863}
864
865/*!
866 \property QSlider::lineStep
867 \brief the current line step
868
869 When setting lineStep, the virtual stepChange() function will be
870 called if the new line step is different from the previous
871 setting.
872
873 \sa setSteps() QRangeControl::pageStep() setRange()
874*/
875int QSlider::lineStep() const
876{
877 return QRangeControl::lineStep();
878}
879
880/*!
881 \property QSlider::pageStep
882 \brief the current page step
883
884 When setting pageStep, the virtual stepChange() function will be
885 called if the new page step is different from the previous
886 setting.
887
888 \sa QRangeControl::setSteps() setLineStep() setRange()
889*/
890
891int QSlider::pageStep() const
892{
893 return QRangeControl::pageStep();
894}
895
896void QSlider::setLineStep( int i )
897{
898 setSteps( i, pageStep() );
899}
900
901void QSlider::setPageStep( int i )
902{
903 setSteps( lineStep(), i );
904}
905
906/*!
907 \property QSlider::value
908 \brief the current slider value
909
910 \sa QRangeControl::value() prevValue()
911*/
912
913int QSlider::value() const
914{
915 return QRangeControl::value();
916}
917
918#endif
Note: See TracBrowser for help on using the repository browser.