class library: PriorityQueue - implement removeValue, hide array
[supercollider.git] / QtCollider / widgets / QcMultiSlider.cpp
blobddbcdfc68ae4a88a8a86acf22d38f5e517096838
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>
27 #include <QPainter>
29 #include <cmath>
31 static QcWidgetFactory<QcMultiSlider> factory;
33 QcMultiSlider::QcMultiSlider() :
34 _currentIndex(0),
35 _selectionSize(1),
36 roundStep( 0.f ),
37 editable( true ),
38 ort( Qt::Horizontal),
39 elastic( false ),
40 thumbSize( QSizeF( 12.f, 12.f ) ),
41 gap( 1 ),
42 drawRects( true ),
43 drawLines( false ),
44 isFilled( false ),
45 highlight( false ),
46 startIndex( 0 )
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() )
58 _values.removeLast();
59 update();
63 VariantList QcMultiSlider::values() const
65 VariantList list;
66 Q_FOREACH( float val, _values )
67 list.data << QVariant( val );
68 return list;
71 void QcMultiSlider::setValues( const VariantList & newValues )
73 _values.clear();
74 Q_FOREACH( QVariant var, newValues.data ) {
75 // FIXME Seems there is a crash in case _values are out of range and
76 // not clipped
77 float value = var.value<float>();
78 if( roundStep > 0.f )
79 value = round( value / roundStep ) * roundStep;
80 _values.append( qMax( 0.f, qMin( 1.f, value ) ) );
82 update();
85 void QcMultiSlider::setReference( const VariantList &reference )
87 _ref.clear();
88 Q_FOREACH( QVariant var, reference.data ) {
89 _ref.append( qMax( 0.f, qMin( 1.f, var.value<float>() ) ) );
91 update();
94 float QcMultiSlider::value() const
96 if( _currentIndex >= 0 && _currentIndex < _values.count() )
97 return _values[_currentIndex];
98 else
99 return 0.f;
102 void QcMultiSlider::setValue( float f )
104 if( _currentIndex >= 0 && _currentIndex < _values.count() )
105 setValue( _currentIndex, f );
107 update();
110 void QcMultiSlider::setStepSize( float f ) {
111 if( roundStep == f ) return;
112 roundStep = f;
113 if( f == 0.f ) return;
114 int c = _values.count();
115 int i;
116 for( i=0; i<c; ++i ) setValue( i, _values[i] );
117 update();
120 void QcMultiSlider::setIndex( int i ) {
121 if( i >= 0 && i < _values.count() ) {
122 _currentIndex = i;
123 _selectionSize = 1;
124 update();
128 void QcMultiSlider::setSelectionSize( int i ) {
129 _selectionSize = qMin(i, _values.count() - _currentIndex);
130 update();
133 inline float QcMultiSlider::valueFromPos( float pos, float range )
135 float thumbH = thumbSize.height();
136 if( thumbH >= range ) return 0.f;
137 pos -= thumbH * 0.5;
138 pos /= range - thumbH;
139 if( ort == Qt::Horizontal ) pos = 1.f - pos ;
140 return 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();
155 if( !c ) return;
157 int i;
158 float val;
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() );
165 else {
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() );
171 i += startIndex;
173 if( i >= 0 && i < c ) {
174 _currentIndex = i;
175 _selectionSize = 1;
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();
190 if( !c ) {
191 moveOrigin = pos;
192 return;
195 float xOrig, yOrig, xDest, yDest, xStep;
197 if( ort == Qt::Horizontal ) {
198 xOrig = moveOrigin.x();
199 yOrig = valueFromPos(moveOrigin.y(), height());
200 xDest = pos.x();
201 yDest = valueFromPos(pos.y(), height());
202 xStep = elastic ? (float) width() / c : thumbSize.width() + gap;
204 else {
205 xOrig = moveOrigin.y();
206 yOrig = valueFromPos(moveOrigin.x(), width());
207 xDest = pos.y();
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;
218 i += startIndex;
220 if( xDif > 0.f )
222 x+=xStep;
223 float y = x * k + yOrig;
224 while( i < c && x < xDif ) {
225 if( i >= 0 && editable ) {
226 setValue( i, y );
228 y += yStep;
229 x+=xStep;
230 i++;
233 else if( xDif < 0.f )
235 float y = x * k + yOrig;
236 while( i >= 0 && x > xDif ) {
237 if( i < c && editable ) {
238 setValue( i, y );
240 y -= yStep;
241 x-=xStep;
242 i--;
246 if( i < c && i >= 0 ) {
247 _currentIndex = i;
248 _selectionSize = 1;
250 if( editable ) setValue( i, yDest );
251 if( editable || highlight ) Q_EMIT( modified() );
254 moveOrigin = pos;
256 Q_EMIT( interacted() );
259 void QcMultiSlider::paintEvent( QPaintEvent *e )
261 Q_UNUSED(e);
262 QPainter p(this);
264 QPalette pal = palette();
265 p.fillRect( rect(), pal.color( QPalette::Base ) );
267 if( !_values.count() ) return;
269 QColor fillColor =
270 _fillColor.isValid() ? _fillColor : pal.color( QPalette::Text );
271 QColor strokeColor =
272 _strokeColor.isValid() ? _strokeColor : pal.color( QPalette::Text );
274 float iRange, vRange;
275 if( ort == Qt::Horizontal ) {
276 iRange = width(); vRange = height();
277 } else {
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;
293 float iPos;
294 float vPos;
295 float iSize;
296 float vSize;
298 float *x, *y, *w, *h;
299 if( ort == Qt::Horizontal ) {
300 x = &iPos; y = &vPos; w = &iSize, h = &vSize;
302 else {
303 x = &vPos; y = &iPos; w = &vSize, h = &iSize;
306 //highlight current index / selection
308 if( highlight ) {
309 iSize = _selectionSize * spacing;
310 vSize = vRange;
311 iPos = _currentIndex * spacing + iOffset;
312 vPos = 0.f;
313 QRectF hlRect;
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 );
325 if( drawLines ) {
326 bool fill = isFilled & !drawRects;
328 p.setRenderHint( QPainter::Antialiasing, true );
329 if( fill ) p.setBrush( fillColor );
331 QPainterPath path;
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 ) {
340 iPos += spacing;
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) {
348 int i = vc - 1;
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);
355 while( --i >= 0 ) {
356 iPos -= spacing;
357 vPos = ( (i < rc ? _ref[i] : 0.f) * vScale ) + vOffset;
358 path.lineTo(*x,*y);
361 if( fill ) path.closeSubpath();
364 p.drawPath( path );
367 if( drawRects ) {
368 p.setRenderHint( QPainter::Antialiasing, false );
369 p.setBrush( fillColor );
371 QRectF r;
372 // 1 pixel smaller because of stroke on right and bottom edge
373 //FIXME erm, doesn't work right in direction of value
374 iSize = elastic ?
375 qMin( (qreal)spacing, thumbSize.width() ) :
376 thumbSize.width();
377 iSize -= 1;
379 if( !isFilled ) {
380 vSize = thumbSize.height() - 1;
381 r.setSize( QSizeF( *w, *h ) );
384 iPos = spacing * 0.5 + iOffset;
386 QPointF pt;
387 int vc = _values.count();
388 int rc = _ref.count();
389 for( int i = 0; i < vc; ++i ) {
390 if( isFilled ) {
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);
398 r.moveCenter( pt );
399 p.drawRect( r );
401 else {
402 if( rc ) {
403 float ref = i < rc ? _ref[i] : 0.f;
404 vPos = ( ref * vScale ) + vOffset;
405 pt.setX(*x); pt.setY(*y);
406 r.moveCenter( pt );
407 p.drawRect( r );
410 vPos = ( _values[i] * vScale ) + vOffset;
411 pt.setX(*x); pt.setY(*y);
412 r.moveCenter( pt );
413 p.drawRect( r );
415 iPos += spacing;
420 void QcMultiSlider::doAction()
422 Qt::KeyboardModifier ctrlMod =
423 #ifdef Q_OS_MAC
424 Qt::MetaModifier;
425 #else
426 Qt::ControlModifier;
427 #endif
429 if( QApplication::keyboardModifiers() & ctrlMod )
430 Q_EMIT( metaAction() );
431 else
432 Q_EMIT( action() );