1 /************************************************************************
3 * Copyright 2010 Jakob Leben (jakob.leben@gmail.com)
5 * This file is part of SuperCollider Qt GUI.
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ************************************************************************/
22 #include "QcMultiSlider.h"
23 #include "../QcWidgetFactory.h"
25 #include <QApplication>
26 #include <QMouseEvent>
31 static QcWidgetFactory
<QcMultiSlider
> factory
;
33 QcMultiSlider::QcMultiSlider() :
40 thumbSize( QSizeF( 12.f
, 12.f
) ),
48 setSizePolicy( QSizePolicy::Expanding
, QSizePolicy::Expanding
);
49 connect( this, SIGNAL(modified()), this, SLOT(update()) );
50 connect( this, SIGNAL(interacted()), this, SLOT(doAction()) );
53 void QcMultiSlider::setSliderCount( int newSize
)
55 while( newSize
> _values
.size() )
56 _values
.append( 0.f
);
57 while( newSize
< _values
.size() )
63 VariantList
QcMultiSlider::values() const
66 Q_FOREACH( float val
, _values
)
67 list
.data
<< QVariant( val
);
71 void QcMultiSlider::setValues( const VariantList
& newValues
)
74 Q_FOREACH( QVariant var
, newValues
.data
) {
75 // FIXME Seems there is a crash in case _values are out of range and
77 float value
= var
.value
<float>();
79 value
= round( value
/ roundStep
) * roundStep
;
80 _values
.append( qMax( 0.f
, qMin( 1.f
, value
) ) );
85 void QcMultiSlider::setReference( const VariantList
&reference
)
88 Q_FOREACH( QVariant var
, reference
.data
) {
89 _ref
.append( qMax( 0.f
, qMin( 1.f
, var
.value
<float>() ) ) );
94 float QcMultiSlider::value() const
96 if( _currentIndex
>= 0 && _currentIndex
< _values
.count() )
97 return _values
[_currentIndex
];
102 void QcMultiSlider::setValue( float f
)
104 if( _currentIndex
>= 0 && _currentIndex
< _values
.count() )
105 setValue( _currentIndex
, f
);
110 void QcMultiSlider::setStepSize( float f
) {
111 if( roundStep
== f
) return;
113 if( f
== 0.f
) return;
114 int c
= _values
.count();
116 for( i
=0; i
<c
; ++i
) setValue( i
, _values
[i
] );
120 void QcMultiSlider::setIndex( int i
) {
121 if( i
>= 0 && i
< _values
.count() ) {
128 void QcMultiSlider::setSelectionSize( int i
) {
129 _selectionSize
= qMin(i
, _values
.count() - _currentIndex
);
133 inline float QcMultiSlider::valueFromPos( float pos
, float range
)
135 float thumbH
= thumbSize
.height();
136 if( thumbH
>= range
) return 0.f
;
138 pos
/= range
- thumbH
;
139 if( ort
== Qt::Horizontal
) pos
= 1.f
- pos
;
143 inline void QcMultiSlider::setValue( int index
, float value
)
145 if( roundStep
> 0.f
)
146 value
= round( value
/ roundStep
) * roundStep
;
147 _values
.replace( index
, qMax( 0.f
, qMin( 1.f
, value
) ) );
150 void QcMultiSlider::mousePressEvent( QMouseEvent
*e
)
152 moveOrigin
= e
->pos();
153 int c
= _values
.count();
160 if( ort
== Qt::Horizontal
) {
161 float step
= elastic
? (float) width() / c
: thumbSize
.width() + gap
;
162 i
= step
!= 0.f
? moveOrigin
.x() / step
: moveOrigin
.x();
163 val
= valueFromPos( moveOrigin
.y(), height() );
166 float step
= elastic
? (float) height() / c
: thumbSize
.width() + gap
;
167 i
= step
!= 0.f
? moveOrigin
.y() / step
: moveOrigin
.y();
168 val
= valueFromPos( moveOrigin
.x(), width() );
173 if( i
>= 0 && i
< c
) {
177 if( editable
) setValue( i
, val
);
178 if( editable
|| highlight
) Q_EMIT( modified() );
180 Q_EMIT( interacted() );
184 void QcMultiSlider::mouseMoveEvent( QMouseEvent
*e
)
186 int c
= _values
.count();
188 QPoint pos
= e
->pos();
195 float xOrig
, yOrig
, xDest
, yDest
, xStep
;
197 if( ort
== Qt::Horizontal
) {
198 xOrig
= moveOrigin
.x();
199 yOrig
= valueFromPos(moveOrigin
.y(), height());
201 yDest
= valueFromPos(pos
.y(), height());
202 xStep
= elastic
? (float) width() / c
: thumbSize
.width() + gap
;
205 xOrig
= moveOrigin
.y();
206 yOrig
= valueFromPos(moveOrigin
.x(), width());
208 yDest
= valueFromPos(pos
.x(), width());
209 xStep
= elastic
? (float) height() / c
: thumbSize
.width() + gap
;
212 int i
= floor( xOrig
/ xStep
);
213 float x
= (i
* xStep
) - xOrig
;
214 float xDif
= xDest
- xOrig
;
215 float k
= xDif
!= 0.f
? ( yDest
- yOrig
) / xDif
: 0.f
;
216 float yStep
= xStep
* k
;
223 float y
= x
* k
+ yOrig
;
224 while( i
< c
&& x
< xDif
) {
225 if( i
>= 0 && editable
) {
233 else if( xDif
< 0.f
)
235 float y
= x
* k
+ yOrig
;
236 while( i
>= 0 && x
> xDif
) {
237 if( i
< c
&& editable
) {
246 if( i
< c
&& i
>= 0 ) {
250 if( editable
) setValue( i
, yDest
);
251 if( editable
|| highlight
) Q_EMIT( modified() );
256 Q_EMIT( interacted() );
259 void QcMultiSlider::paintEvent( QPaintEvent
*e
)
264 QPalette pal
= palette();
265 p
.fillRect( rect(), pal
.color( QPalette::Base
) );
267 if( !_values
.count() ) return;
270 _fillColor
.isValid() ? _fillColor
: pal
.color( QPalette::Text
);
272 _strokeColor
.isValid() ? _strokeColor
: pal
.color( QPalette::Text
);
274 float iRange
, vRange
;
275 if( ort
== Qt::Horizontal
) {
276 iRange
= width(); vRange
= height();
278 iRange
= height(); vRange
= width();
281 float spacing
= elastic
?
282 iRange
/ _values
.count() :
283 thumbSize
.width() + gap
;
285 float iOffset
= - startIndex
* spacing
;
287 float vOffset
= thumbSize
.height() * 0.5f
;
288 if( ort
== Qt::Horizontal
) vOffset
= vRange
- vOffset
;
290 float vScale
= vRange
- thumbSize
.height();
291 if( ort
== Qt::Horizontal
) vScale
*= -1.f
;
298 float *x
, *y
, *w
, *h
;
299 if( ort
== Qt::Horizontal
) {
300 x
= &iPos
; y
= &vPos
; w
= &iSize
, h
= &vSize
;
303 x
= &vPos
; y
= &iPos
; w
= &vSize
, h
= &iSize
;
306 //highlight current index / selection
309 iSize
= _selectionSize
* spacing
;
311 iPos
= _currentIndex
* spacing
+ iOffset
;
314 hlRect
.setWidth( *w
);
315 hlRect
.setHeight( *h
);
316 hlRect
.translate( *x
, *y
);
318 QColor hlColor
= fillColor
;
319 hlColor
.setAlpha( 70 );
320 p
.fillRect( hlRect
, hlColor
);
323 p
.setPen( strokeColor
);
326 bool fill
= isFilled
& !drawRects
;
328 p
.setRenderHint( QPainter::Antialiasing
, true );
329 if( fill
) p
.setBrush( fillColor
);
333 // construct value line
334 iPos
= spacing
* 0.5 + iOffset
;
335 vPos
= ( _values
[0] * vScale
) + vOffset
;
336 path
.moveTo( *x
, *y
);
338 int vc
= _values
.count();
339 for( int i
= 1; i
< vc
; ++i
) {
341 vPos
= ( _values
[i
] * vScale
) + vOffset
;
342 path
.lineTo( *x
, *y
);
345 // construct reference line
346 int rc
= _ref
.count();
347 if( ( rc
|| fill
) && vc
) {
349 vPos
= ( (i
< rc
? _ref
[i
] : 0.f
) * vScale
) + vOffset
;
350 //NOTE re-use last iPos from value line;
352 if( fill
) path
.lineTo(*x
,*y
);
353 else path
.moveTo(*x
,*y
);
357 vPos
= ( (i
< rc
? _ref
[i
] : 0.f
) * vScale
) + vOffset
;
361 if( fill
) path
.closeSubpath();
368 p
.setRenderHint( QPainter::Antialiasing
, false );
369 p
.setBrush( fillColor
);
372 // 1 pixel smaller because of stroke on right and bottom edge
373 //FIXME erm, doesn't work right in direction of value
375 qMin( (qreal
)spacing
, thumbSize
.width() ) :
380 vSize
= thumbSize
.height() - 1;
381 r
.setSize( QSizeF( *w
, *h
) );
384 iPos
= spacing
* 0.5 + iOffset
;
387 int vc
= _values
.count();
388 int rc
= _ref
.count();
389 for( int i
= 0; i
< vc
; ++i
) {
391 float ref
= i
< rc
? _ref
[i
] : 0.f
;
392 float val
= _values
[i
];
393 vPos
= ((ref
+ val
) * 0.5 * vScale
) + vOffset
;
394 vSize
= ( val
- ref
);
395 vSize
*= vSize
> 0.f
? vScale
: -vScale
;
396 r
.setSize( QSizeF( *w
, *h
) );
397 pt
.setX(*x
); pt
.setY(*y
);
403 float ref
= i
< rc
? _ref
[i
] : 0.f
;
404 vPos
= ( ref
* vScale
) + vOffset
;
405 pt
.setX(*x
); pt
.setY(*y
);
410 vPos
= ( _values
[i
] * vScale
) + vOffset
;
411 pt
.setX(*x
); pt
.setY(*y
);
420 void QcMultiSlider::doAction()
422 Qt::KeyboardModifier ctrlMod
=
429 if( QApplication::keyboardModifiers() & ctrlMod
)
430 Q_EMIT( metaAction() );