fix String:wrapExtend to fill out to the actual requested size
[supercollider.git] / QtCollider / widgets / QcMultiSlider.cpp
blob4461ab5d12906e79599a7142e8b7cd848019d0e7
1 /************************************************************************
3 * Copyright 2010-2012 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"
24 #include "../style/routines.hpp"
26 #include <QApplication>
27 #include <QMouseEvent>
28 #include <QPainter>
30 #include <cmath>
32 QC_DECLARE_QWIDGET_FACTORY(QcMultiSlider);
34 QcMultiSlider::QcMultiSlider() :
35 QtCollider::Style::Client(this),
36 _currentIndex(0),
37 _selectionSize(1),
38 roundStep( 0.f ),
39 editable( true ),
40 ort( Qt::Vertical),
41 elastic( false ),
42 thumbSize( QSizeF( 12.f, 12.f ) ),
43 gap( 1 ),
44 drawRects( true ),
45 drawLines( false ),
46 isFilled( false ),
47 highlight( false ),
48 startIndex( 0 )
50 setFocusPolicy( Qt::StrongFocus );
51 setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
52 connect( this, SIGNAL(modified()), this, SLOT(update()) );
53 connect( this, SIGNAL(interacted()), this, SLOT(doAction()) );
56 void QcMultiSlider::setSliderCount( int newSize )
58 while( newSize > _values.size() )
59 _values.append( 0.f );
60 while( newSize < _values.size() )
61 _values.removeLast();
62 update();
66 VariantList QcMultiSlider::values() const
68 VariantList list;
69 Q_FOREACH( float val, _values )
70 list.data << QVariant( val );
71 return list;
74 void QcMultiSlider::setValues( const VariantList & newValues )
76 _values.clear();
77 Q_FOREACH( QVariant var, newValues.data ) {
78 // FIXME Seems there is a crash in case _values are out of range and
79 // not clipped
80 float value = var.value<float>();
81 if( roundStep > 0.f )
82 value = round( value / roundStep ) * roundStep;
83 _values.append( qMax( 0.f, qMin( 1.f, value ) ) );
85 update();
88 void QcMultiSlider::setReference( const VariantList &reference )
90 _ref.clear();
91 Q_FOREACH( QVariant var, reference.data ) {
92 _ref.append( qMax( 0.f, qMin( 1.f, var.value<float>() ) ) );
94 update();
97 float QcMultiSlider::value() const
99 if( _currentIndex >= 0 && _currentIndex < _values.count() )
100 return _values[_currentIndex];
101 else
102 return 0.f;
105 void QcMultiSlider::setValue( float f )
107 if( _currentIndex >= 0 && _currentIndex < _values.count() )
108 setValue( _currentIndex, f );
110 update();
113 void QcMultiSlider::setStepSize( float f ) {
114 if( roundStep == f ) return;
115 roundStep = f;
116 if( f == 0.f ) return;
117 int c = _values.count();
118 int i;
119 for( i=0; i<c; ++i ) setValue( i, _values[i] );
120 update();
123 void QcMultiSlider::setIndex( int i ) {
124 if( i >= 0 && i < _values.count() ) {
125 _currentIndex = i;
126 _selectionSize = 1;
127 update();
131 void QcMultiSlider::setSelectionSize( int i ) {
132 _selectionSize = qMin(i, _values.count() - _currentIndex);
133 update();
136 inline void QcMultiSlider::setValue( int index, float value )
138 if( roundStep > 0.f )
139 value = round( value / roundStep ) * roundStep;
140 _values.replace( index, qMax( 0.f, qMin( 1.f, value ) ) );
143 QRect QcMultiSlider::contentsRect()
145 return QtCollider::Style::sunkenContentsRect(rect()).adjusted(2, 2,-2,-2);
148 QRect QcMultiSlider::valueRect( int count, qreal & spacing )
150 QRect r( contentsRect() );
152 bool horiz = ort == Qt::Horizontal;
154 spacing = elastic ? (qreal) (horiz ? r.height() : r.width()) / count : thumbSize.width() + gap;
156 if( !isFilled ) {
157 int hnd = thumbSize.height();
158 if( horiz ) {
159 r.setWidth( r.width() - hnd );
160 r.moveLeft( r.left() + hnd * 0.5 );
162 else {
163 r.setHeight( r.height() - hnd );
164 r.moveTop( r.top() + hnd * 0.5 );
168 return r;
171 void QcMultiSlider::mousePressEvent( QMouseEvent *e )
173 using namespace QtCollider::Style;
175 moveOrigin = e->pos();
176 int c = _values.count();
178 if( !c ) return;
180 bool horiz = ort == Qt::Horizontal;
181 double spacing;
183 QRect r( valueRect( c - startIndex, spacing ) );
185 int i;
186 float val;
188 if( horiz ) {
189 i = spacing > 0.f ? floor((float)(moveOrigin.y() - r.y()) / spacing) : 0;
190 val = xValue((qreal)moveOrigin.x(), r);
192 else {
193 i = spacing > 0.f ? floor((float)(moveOrigin.x() - r.x()) / spacing) : 0;
194 val = yValue((qreal)moveOrigin.y(), r);
197 i += startIndex;
199 if( i >= startIndex && i < c ) {
200 _currentIndex = i;
201 _selectionSize = 1;
203 if( editable ) setValue( i, val );
204 if( editable || highlight ) Q_EMIT( modified() );
206 Q_EMIT( interacted() );
210 void QcMultiSlider::mouseMoveEvent( QMouseEvent *e )
212 using namespace QtCollider::Style;
214 if( !e->buttons() ) return;
216 int c = _values.count();
218 QPoint pos = e->pos();
220 if( !c ) {
221 moveOrigin = pos;
222 return;
225 double xStep;
227 QRect r( valueRect( c - startIndex, xStep ) );
229 float xOrig, yOrig, xDest, yDest;
230 int xOffset, xRange;
231 int iOrig, iDest, iMax;
233 if( ort == Qt::Vertical ) {
234 xOffset = r.left(); xRange = r.width();
235 xOrig = moveOrigin.x();
236 yOrig = yValue(moveOrigin.y(), r);
237 xDest = pos.x();
238 yDest = yValue(pos.y(), r);
240 else {
241 xOffset = r.top(); xRange = r.height();
242 xOrig = moveOrigin.y();
243 yOrig = xValue(moveOrigin.x(), r);
244 xDest = pos.y();
245 yDest = xValue(pos.x(), r);
248 if( xStep > 0.0 ) {
249 iOrig = floor((xOrig - xOffset) / xStep); iOrig += startIndex;
250 iDest = floor((xDest - xOffset) / xStep); iDest += startIndex;
251 iMax = elastic ? c : qMin( c, int( (float)xRange / xStep) + startIndex + 1);
253 else
255 iOrig = iDest = iMax = startIndex;
258 if( editable && (iOrig != iDest) ) {
259 float k = (yDest - yOrig) / (xDest - xOrig);
260 float n = yOrig - (xOrig - xOffset) * k;
261 int i = iOrig - startIndex;
263 while( iOrig < iDest ) {
264 ++i;
265 if( iOrig >= startIndex && iOrig < iMax ) {
266 float y = (i * xStep) * k + n;
267 setValue( iOrig, y );
269 ++iOrig;
271 while( iOrig > iDest ) {
272 if( iOrig >= startIndex && iOrig < iMax ) {
273 float y = (i * xStep) * k + n;
274 setValue( iOrig, y );
276 --iOrig; --i;
280 if( iDest >= startIndex && iDest < iMax ) {
281 if( editable ) setValue( iDest, yDest );
283 _currentIndex = iDest;
284 _selectionSize = 1;
287 moveOrigin = pos;
289 Q_EMIT( modified() );
290 Q_EMIT( interacted() );
293 void QcMultiSlider::paintEvent( QPaintEvent *e )
295 using namespace QtCollider::Style;
297 Q_UNUSED(e);
298 QPainter p(this);
299 p.setRenderHint( QPainter::Antialiasing, true );
301 QPalette pal = palette();
303 RoundRect frame(rect(), 2);
304 drawSunken( &p, pal, frame, pal.color(QPalette::Base), hasFocus() ? focusColor() : QColor() );
306 if( !_values.count() ) return;
308 p.setRenderHint( QPainter::Antialiasing, false );
310 bool horiz = ort == Qt::Horizontal;
312 QColor fillColor =
313 _fillColor.isValid() ? _fillColor : pal.color( QPalette::Text );
314 QColor strokeColor =
315 _strokeColor.isValid() ? _strokeColor : pal.color( QPalette::Text );
317 QRect bounds( contentsRect() );
319 p.setClipRect( bounds );
321 if( horiz ) {
322 p.translate( bounds.topLeft() );
323 p.rotate(90);
324 p.scale(1.0, -1.0);
325 bounds.setSize( QSize( bounds.height(), bounds.width() ) );
327 else {
328 p.translate( bounds.left(), bounds.top() + bounds.height() );
329 p.scale(1.0, -1.0);
332 int count = _values.count() - startIndex;
333 double spacing, width, yscale;
335 spacing = elastic ? (double) bounds.width() / count : thumbSize.width() + gap;
336 width = elastic ? qMin( spacing, (double) thumbSize.width() ) : thumbSize.width();
337 yscale = bounds.height();
338 if( !isFilled ) yscale -= thumbSize.height();
340 // selection
342 if( highlight ) {
343 int i = _currentIndex - startIndex;
344 int c = qMin( count - i, _selectionSize );
345 if(c) {
346 QRect r;
347 r.setHeight( bounds.height() );
348 r.setWidth( c * spacing );
349 r.moveLeft( i * spacing );
351 QColor hlColor = fillColor;
352 hlColor.setAlpha( 70 );
353 p.fillRect( r, hlColor );
357 p.setPen( strokeColor );
359 // lines
361 if( drawLines ) {
362 bool fill = isFilled & !drawRects;
364 p.save();
366 p.setRenderHint( QPainter::Antialiasing, true );
367 p.translate( spacing * 0.5, isFilled ? 0.0 : thumbSize.height() * 0.5 );
368 p.scale( 1.0, (qreal) yscale );
369 if( fill ) p.setBrush( fillColor );
371 QPainterPath path;
373 // value line
375 path.moveTo( 0, _values[startIndex] );
376 for( int i = 1; i < count; ++i )
377 path.lineTo( (qreal) i * spacing, _values[i + startIndex] );
379 // reference line
381 int refcount = _ref.count() - startIndex;
382 if( refcount > 0 || fill ) {
383 qreal x, y;
384 int i = count - 1;
386 x = i * spacing;
387 y = i < refcount ? _ref[i + startIndex] : 0.f;
388 if( fill ) path.lineTo(x, y);
389 else path.moveTo(x, y);
391 while( --i >= 0 ) {
392 x = i * spacing;
393 y = i < refcount ? _ref[i + startIndex] : 0.f;
394 path.lineTo(x, y);
397 if( fill ) path.closeSubpath();
400 p.drawPath( path );
402 p.restore();
405 // rects
407 if( drawRects ) {
408 p.setRenderHint( QPainter::Antialiasing, false );
409 p.translate( (spacing - width) * 0.5, 0 );
410 p.setBrush( fillColor );
412 QRectF r;
413 r.setWidth( width );
415 if( isFilled ) {
416 int refcount = _ref.count() - startIndex;
417 for( int i = 0; i < count; ++i ) {
418 int ref = (i < refcount ? _ref[i + startIndex] : 0.f) * yscale;
419 int val = _values[i + startIndex] * yscale;
420 r.moveLeft( i * spacing );
421 r.moveTop( ref );
422 r.setHeight( val - ref );
423 if(horiz) p.drawRect(r.normalized().adjusted(0,0,-1,-1));
424 else p.drawRect(r.normalized().adjusted(0,1,-1,0));
427 else {
428 r.setHeight( thumbSize.height() );
429 for( int i = 0; i < count; ++i ) {
430 r.moveLeft( i * spacing );
431 r.moveTop( _values[i + startIndex] * yscale );
432 if(horiz) p.drawRect(r.adjusted(0,0,-1,-1));
433 else p.drawRect(r.adjusted(0,1,-1,0));
439 void QcMultiSlider::doAction()
441 Qt::KeyboardModifier ctrlMod =
442 #ifdef Q_OS_MAC
443 Qt::MetaModifier;
444 #else
445 Qt::ControlModifier;
446 #endif
448 if( QApplication::keyboardModifiers() & ctrlMod )
449 Q_EMIT( metaAction() );
450 else
451 Q_EMIT( action() );