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 ************************************************************************/
23 #include "../QcWidgetFactory.h"
24 #include "../style/routines.hpp"
27 #include <QMouseEvent>
28 #include <QApplication>
32 QC_DECLARE_QWIDGET_FACTORY(QcGraph
);
34 void QcGraphModel::append( QcGraphElement
* e
) {
35 if( _elems
.count() ) {
36 QcGraphElement
*prev
= _elems
.last();
41 Q_EMIT( appended(e
) );
44 void QcGraphModel::removeAt( int i
) {
45 QcGraphElement
*e
= _elems
[i
];
46 int ci
= _conns
.count();
48 Connection c
= _conns
[ci
];
49 if( c
.a
== e
|| c
.b
== e
) _conns
.removeAt(ci
);
51 if( e
->_prev
) e
->_prev
->_next
= e
->_next
;
52 if( e
->_next
) e
->_next
->_prev
= e
->_prev
;
61 QtCollider::Style::Client(this),
62 _defaultThumbSize( QSize(18,18) ),
68 _selectionForm( ElasticSelection
),
71 _geometryDirty( false ),
74 QPalette
plt( palette() );
76 setFocusPolicy( Qt::StrongFocus
);
77 setSizePolicy( QSizePolicy::Expanding
, QSizePolicy::Expanding
);
79 connect( &_model
, SIGNAL(removed(QcGraphElement
*)), this, SLOT(onElementRemoved(QcGraphElement
*)) );
82 VariantList
QcGraph::value() const
86 QList
<QcGraphElement
*> elems
= _model
.elements();
87 Q_FOREACH( QcGraphElement
* e
, elems
) {
88 QPointF val
= e
->value
;
89 x
.data
.append( val
.x() );
90 y
.data
.append( val
.y() );
94 values
.data
.append( QVariant::fromValue
<VariantList
>(x
) );
95 values
.data
.append( QVariant::fromValue
<VariantList
>(y
) );
100 QcGraphElement
*QcGraph::currentElement() const
102 return _selection
.count() ? _selection
.elems
.first().elem
: 0;
105 int QcGraph::index() const
107 QcGraphElement
*e
= currentElement();
108 return e
? _model
.elements().indexOf(e
) : -1;
111 float QcGraph::currentX() const
113 QcGraphElement
*e
= currentElement();
114 return e
? e
->value
.x() : 0.f
;
117 float QcGraph::currentY() const
119 QcGraphElement
*e
= currentElement();
120 return e
? e
->value
.y() : 0.f
;
123 void QcGraph::setValue( const VariantList
&list
)
125 if( list
.data
.count() != 2 ) return;
126 VariantList xList
= list
.data
[0].value
<VariantList
>();
127 VariantList yList
= list
.data
[1].value
<VariantList
>();
129 int newc
= qMin( xList
.data
.count(), yList
.data
.count() );
132 int c
= _model
.elementCount();
136 _model
.removeAt( c
);
140 for( i
= 0; i
< newc
; ++i
)
142 QPointF
val( xList
.data
[i
].value
<float>(),
143 yList
.data
[i
].value
<float>() );
145 QcGraphElement
*e
= _model
.elementAt(i
);
149 QcGraphElement
*e
= new QcGraphElement(_defaultThumbSize
);
155 if( newc
) ensureOrder();
157 _geometryDirty
= true;
162 void QcGraph::setStrings( const VariantList
&list
)
164 int strc
= list
.data
.count();
165 int c
= _model
.elementCount();
167 for( i
= 0; i
< c
&& i
< strc
; ++i
) {
168 QcGraphElement
*e
= _model
.elementAt(i
);
169 e
->text
= list
.data
[i
].toString();
174 void QcGraph::setCurves( const VariantList
& curves
)
176 for( int i
= 0; i
< curves
.data
.size() && i
< _model
.elementCount(); ++i
) {
177 QVariant data
= curves
.data
[i
];
178 QcGraphElement::CurveType type
;
180 if( data
.type() == QVariant::Int
) {
181 type
= (QcGraphElement::CurveType
) data
.toInt();
185 type
= QcGraphElement::Curvature
;
186 curvature
= data
.value
<double>();
188 _model
.elementAt(i
)->setCurveType( type
, curvature
);
193 void QcGraph::setCurves( double curvature
)
195 Q_FOREACH( QcGraphElement
* e
, _model
.elements() )
196 e
->setCurveType( QcGraphElement::Curvature
, curvature
);
200 void QcGraph::setCurves( int typeId
)
202 QcGraphElement::CurveType type
= (QcGraphElement::CurveType
)typeId
;
203 Q_FOREACH( QcGraphElement
* e
, _model
.elements() )
204 e
->setCurveType( type
);
208 void QcGraph::setStringAt( int i
, const QString
& str
)
210 int c
= _model
.elementCount();
211 if( i
>= 0 && i
< c
) {
212 QcGraphElement
*e
= _model
.elementAt(i
);
218 void QcGraph::connectElements( int src
, VariantList targets
)
220 int c
= _model
.elementCount();
221 if( src
< 0 || src
>= c
) return;
223 Q_FOREACH( QVariant var
, targets
.data
) {
224 int trg
= var
.toInt();
225 if( trg
< 0 || trg
>= c
) continue;
226 _model
.connect( src
, trg
);
232 void QcGraph::setIndex( int i
) {
236 void QcGraph::select( int i
, bool exclusive
) {
237 if( i
>= 0 && i
< _model
.elementCount() ) {
238 if( exclusive
) setAllDeselected();
239 setIndexSelected( i
, true );
244 void QcGraph::deselect( int i
) {
245 if( i
>= 0 && i
< _model
.elementCount() ) {
246 setIndexSelected( i
, false );
251 void QcGraph::deselectAll() {
255 void QcGraph::setCurrentX( float f
)
257 QcGraphElement
*e
= currentElement();
260 QPointF val
= e
->value
;
262 if( _xOrder
!= NoOrder
) orderRestrictValue(e
,val
,true);
263 else restrictValue(val
);
269 void QcGraph::setCurrentY( float f
)
271 QcGraphElement
*e
= currentElement();
274 QPointF val
= e
->value
;
276 if( _xOrder
!= NoOrder
) orderRestrictValue(e
,val
,true);
277 else restrictValue(val
);
283 void QcGraph::setThumbSize( int s
)
287 _defaultThumbSize
= size
;
289 int c
= _model
.elementCount();
290 for( int i
=0; i
<c
; ++i
) {
291 QcGraphElement
*e
= _model
.elementAt(i
);
295 _largestThumbSize
= size
;
296 _geometryDirty
= false;
301 void QcGraph::setThumbWidth( int w
)
303 _defaultThumbSize
.setWidth(w
);
305 int c
= _model
.elementCount();
306 for( int i
=0; i
<c
; ++i
) {
307 QcGraphElement
*e
= _model
.elementAt(i
);
311 // For backward compatibility, switch to style that supports
312 // different thumb width and height:
313 _style
= RectElements
;
314 _largestThumbSize
.setWidth(w
);
319 void QcGraph::setThumbHeight( int h
)
321 _defaultThumbSize
.setHeight(h
);
323 int c
= _model
.elementCount();
324 for( int i
=0; i
<c
; ++i
) {
325 QcGraphElement
*e
= _model
.elementAt(i
);
326 e
->size
.setHeight(h
);
329 // For backward compatibility, switch to style that supports
330 // different thumb width and height:
331 _style
= RectElements
;
332 _largestThumbSize
.setHeight(h
);
337 void QcGraph::setThumbSizeAt( int i
, int s
)
339 if( i
< 0 || i
>= _model
.elementCount() ) return;
340 _model
.elementAt(i
)->size
= QSize(s
,s
);
341 _geometryDirty
= true;
345 void QcGraph::setThumbWidthAt( int i
, int w
)
347 if( i
< 0 || i
>= _model
.elementCount() ) return;
348 _model
.elementAt(i
)->size
.setWidth(w
);
349 // For backward compatibility, switch to style that supports
350 // different thumb width and height:
351 _style
= RectElements
;
352 _geometryDirty
= true;
356 void QcGraph::setThumbHeightAt( int i
, int h
)
358 if( i
< 0 || i
>= _model
.elementCount() ) return;
359 _model
.elementAt(i
)->size
.setHeight(h
);
360 // For backward compatibility, switch to style that supports
361 // different thumb width and height:
362 _style
= RectElements
;
363 _geometryDirty
= true;
367 void QcGraph::setFillColor( const QColor
& color
)
369 int c
= _model
.elementCount();
370 for( int i
=0; i
<c
; ++i
) {
371 QcGraphElement
*e
= _model
.elementAt(i
);
372 e
->fillColor
= color
;
377 void QcGraph::setFillColorAt( int i
, const QColor
& color
)
379 int c
= _model
.elementCount();
380 if( i
>= 0 && i
< c
) {
381 QcGraphElement
*e
= _model
.elementAt(i
);
382 e
->fillColor
= color
;
387 void QcGraph::setEditableAt( int i
, bool b
)
389 int c
= _model
.elementCount();
390 if( i
>= 0 && i
< c
) {
391 QcGraphElement
*e
= _model
.elementAt(i
);
396 void QcGraph::setStep( double step
)
398 _step
= qMax( 0.0, step
);
400 if( _model
.elementCount() ) {
406 void QcGraph::setHorizontalOrder( int i
) {
408 if( _xOrder
!= NoOrder
) {
414 void QcGraph::onElementRemoved( QcGraphElement
*e
)
416 _selection
.elems
.removeAll( SelectedElement(e
) );
419 void QcGraph::setAllDeselected()
421 int c
= _model
.elementCount();
422 for( int i
= 0; i
< c
; ++i
) {
423 QcGraphElement
*e
= _model
.elementAt(i
);
426 _selection
.elems
.clear();
429 void QcGraph::setIndexSelected( int index
, bool select
)
431 Q_ASSERT( index
>= 0 && index
< _model
.elementCount() );
433 QcGraphElement
*e
= _model
.elementAt( index
);
434 if( e
->selected
== select
) return;
438 int c
= _model
.elementCount();
442 if( _model
.elementAt(i
)->selected
) ++si
;
445 _selection
.elems
.insert( si
, SelectedElement(e
) );
450 _selection
.elems
.removeAll( SelectedElement(e
) );
456 inline static void qc_graph_round( double &val
, double &step
, bool &grid
)
462 double ratio
= ( val
+ (step
*0.5) > 1.0 ) ? floor(1.0/step
) : round(val
/step
);
465 else if ( val
> 1.0 ) {
470 inline void QcGraph::restrictValue( QPointF
& val
)
474 bool grid
= _step
> 0.0;
475 qc_graph_round(x
,_step
,grid
);
476 qc_graph_round(y
,_step
,grid
);
481 void QcGraph::orderRestrictValue( QcGraphElement
*e
, QPointF
& val
, bool selected
)
485 double x0
= e
->value
.x();
492 // new x is smaller, check if not too small;
493 QcGraphElement
*prev
= e
->prev();
494 if( prev
&& (selected
|| !prev
->selected
) && x
< prev
->value
.x() )
495 val
.setX( prev
->value
.x() );
498 // new x is larger, check if not too large;
499 QcGraphElement
*next
= e
->next();
500 if( next
&& (selected
|| !next
->selected
) && x
> next
->value
.x() )
501 val
.setX( next
->value
.x() );
505 inline void QcGraph::setValue( QcGraphElement
* e
, const QPointF
& pt
)
508 restrictValue( val
);
512 void QcGraph::ensureOrder()
514 int c
= _model
.elementCount();
516 for( int i
= 0; i
< c
; ++i
) {
517 QcGraphElement
*e
= _model
.elementAt(i
);
518 QPointF val
= e
->value
;
519 if( _xOrder
!= NoOrder
&& val
.x() < x_min
) val
.setX(x_min
);
521 x_min
= e
->value
.x();
525 void QcGraph::moveFree( QcGraphElement
*e
, const QPointF
& val
)
527 if( !e
->editable
) return;
531 void QcGraph::moveOrderRestricted( QcGraphElement
*e
, const QPointF
& val
)
533 if( !e
->editable
) return;
535 orderRestrictValue( e
, v
, true );
539 void QcGraph::moveSelected( const QPointF
& dif
, SelectionForm form
, bool cached
)
541 int c
= _selection
.count();
545 case ElasticSelection
: {
551 for( int i
= 0; i
< c
; ++i
) {
552 SelectedElement
& se
= _selection
.elems
[i
];
553 moveFree( se
.elem
, (cached
? se
.moveOrigin
: se
.elem
->value
) + dif
);
561 for( int i
= 0; i
< c
; ++i
) {
562 SelectedElement
& se
= _selection
.elems
[i
];
563 moveOrderRestricted( se
.elem
, (cached
? se
.moveOrigin
: se
.elem
->value
) + dif
);
567 for( int i
= _selection
.count() - 1; i
>= 0; --i
) {
568 SelectedElement
& se
= _selection
.elems
[i
];
569 moveOrderRestricted( se
.elem
, (cached
? se
.moveOrigin
: se
.elem
->value
) + dif
);
579 case RigidSelection
: {
581 // reduce dif until acceptable by all nodes
583 for( int i
= 0; i
< c
; ++i
) {
584 SelectedElement
& se
= _selection
.elems
[i
];
585 // if any node in selection is not editable, abort, since
586 // we want to keep the selection form!
587 if( !se
.elem
->editable
) return;
588 QPointF val0
= (cached
? se
.moveOrigin
: se
.elem
->value
);
589 QPointF val
= val0
+ d
;
590 if( _xOrder
== NoOrder
) {
591 restrictValue( val
);
594 orderRestrictValue( se
.elem
, val
, false );
599 // if no dif left, do not bother moving
600 if( d
.isNull() ) return;
602 // move all with the new dif
603 for( int i
= 0; i
< c
; ++i
) {
604 SelectedElement
& se
= _selection
.elems
[i
];
605 if( !se
.elem
->editable
) continue;
606 se
.elem
->value
= (cached
? se
.moveOrigin
: se
.elem
->value
) + d
;
616 void QcGraph::addCurve( QPainterPath
&path
, QcGraphElement
*e1
, QcGraphElement
*e2
)
618 QcGraphElement::CurveType type
= e1
->curveType
;
620 const QPointF
&pt1
= e1
->value
;
621 const QPointF
&pt2
= e2
->value
;
623 // coefficients for control points of cubic curve
624 // approximating first quarter of sinusoid
625 // technically: y = sin(pi*x/2) over x = [0,1]
626 static const float ax
= 1.0/3.0;
627 static const float ay
= 0.52359877f
; // pi/6
628 static const float bx
= 2.0/3.0;
629 static const float by
= 1.0;
632 case QcGraphElement::Step
:
634 path
.lineTo( pt1
.x(), pt2
.y() );
637 case QcGraphElement::Linear
:
641 case QcGraphElement::Sine
: {
642 // half of difference between end points
643 float dx
= (pt2
.x() - pt1
.x()) * 0.5f
;
644 float dy
= (pt2
.y() - pt1
.y()) * 0.5f
;
647 QPointF mid
= pt1
+ QPointF( dx
, dy
);
650 path
.cubicTo( pt1
+ QPointF( dx
*(1-bx
), dy
*(1-by
) ), pt1
+ QPointF( dx
*(1-ax
), dy
*(1-ay
) ), mid
);
651 path
.cubicTo( mid
+ QPointF( dx
*ax
, dy
*ay
), mid
+ QPointF( dx
*bx
, dy
*by
), pt2
);
655 case QcGraphElement::Welch
: {
656 // difference between points
657 float dx
= (pt2
.x() - pt1
.x());
658 float dy
= (pt2
.y() - pt1
.y());
662 path
.cubicTo( pt1
+ QPointF( dx
*ax
, dy
*ay
), pt1
+ QPointF( dx
*bx
, dy
*by
), pt2
);
664 path
.cubicTo( pt1
+ QPointF( dx
*(1-bx
), dy
*(1-by
) ), pt1
+ QPointF( dx
*(1-ax
), dy
*(1-ay
) ), pt2
);
668 case QcGraphElement::Exponential
: {
670 // FIXME: find a Bezier curve approximation
674 float dx
= (pt2
.x() - pt1
.x());
675 float dy
= (pt2
.y() - pt1
.y());
677 // prevent NaN, optimize
678 if( pt1
.y() <= 0.f
|| pt2
.y() <= 0.f
) {
679 path
.lineTo( dy
< 0 ? QPointF(pt1
.x(),pt2
.y()) : QPointF(pt2
.x(), pt1
.y()) );
683 const float n
= 100.f
;
684 const float yratio
= pt2
.y() / pt1
.y();
685 for( float ph
=1/n
; ph
<=(1-1/n
); ph
+=1/n
) {
686 qreal y
= pt1
.y() * pow( yratio
, ph
);
687 path
.lineTo( pt1
.x() + (dx
* ph
), y
);
694 case QcGraphElement::Curvature
:
696 // FIXME: find a Bezier curve approximation
701 double curve
= qBound( -100.0, e1
->curvature
, 100.0 );
703 if( std::abs( curve
) < 0.0001 ) {
707 float dx
= (pt2
.x() - pt1
.x());
708 float dy
= (pt2
.y() - pt1
.y());
709 double denom
= 1.0 - exp( curve
);
710 const float n
= 100.f
;
711 for( float ph
=1/n
; ph
<=(1-1/n
); ph
+=1/n
) {
712 double numer
= 1.0 - exp( ph
* curve
);
713 qreal y
= pt1
.y() + dy
* (numer
/ denom
);
714 path
.lineTo( pt1
.x() + (dx
* ph
), y
);
722 QSize
QcGraph::drawnElementSize( QcGraphElement
*e
)
724 if (_style
== DotElements
) {
725 int s
= qMin(e
->size
.width(), e
->size
.height());
732 QRect
QcGraph::valueRect()
734 using namespace QtCollider::Style
;
740 int c
= _model
.elementCount();
741 if (_style
== RectElements
)
743 for (int i
= 0; i
< c
; ++i
) {
744 QSize s
= _model
.elementAt(i
)->size
;
745 w
= qMax(w
, s
.width());
746 h
= qMax(h
, s
.height());
751 for (int i
= 0; i
< c
; ++i
) {
752 QSize s
= _model
.elementAt(i
)->size
;
753 w
= qMax(w
, qMin(s
.width(), s
.height()));
757 _largestThumbSize
= QSize(w
,h
);
758 _geometryDirty
= false;
761 return marginsRect( sunkenContentsRect( rect() ), _largestThumbSize
).adjusted(1,1,-1,-1);
764 QRectF
QcGraph::labelRect( QcGraphElement
*e
, const QPointF
&pt
, const QRect
&bounds
, const QFontMetrics
&fm
)
766 QRectF
textRect( fm
.boundingRect(e
->text
) );
767 QSize
sz( drawnElementSize(e
) );
768 qreal hnd_w_2
= sz
.width() * 0.5;
769 qreal hnd_h_2
= sz
.height() * 0.5;
770 textRect
.moveBottomLeft( pt
+ QPointF(hnd_w_2
, -hnd_h_2
) );
771 if( textRect
.y() < bounds
.y() )
772 textRect
.moveTop( pt
.y() + hnd_h_2
);
773 if( textRect
.right() > bounds
.right() )
774 textRect
.moveRight( pt
.x() - hnd_w_2
);
778 void QcGraph::paintEvent( QPaintEvent
* )
780 using namespace QtCollider::Style
;
781 using QtCollider::Style::Ellipse
;
782 using QtCollider::Style::RoundRect
;
785 const QPalette
& plt
= palette();
787 p
.setRenderHint( QPainter::Antialiasing
, true );
788 RoundRect
frame(rect(), 2);
789 drawSunken( &p
, plt
, frame
, background(), hasFocus() ? focusColor() : QColor() );
790 p
.setRenderHint( QPainter::Antialiasing
, false );
792 QRect
contentsRect( valueRect() );
796 p
.setPen( gridColor() );
797 p
.setBrush( Qt::NoBrush
);
798 p
.drawRect( contentsRect
);
801 float dx
= _gridMetrics
.x();
802 float dy
= _gridMetrics
.y();
803 float cl
= contentsRect
.left();
804 float cr
= contentsRect
.right();
805 float ct
= contentsRect
.top();
806 float cb
= contentsRect
.bottom();
808 if( contentsRect
.width() > 0.f
&& dx
> 0.f
&& dx
< 1.f
) {
809 dx
*= contentsRect
.width();
812 while( (x
= i
* dx
+ cl
) < cr
) {
813 p
.drawLine( x
, ct
, x
, cb
);
818 if( contentsRect
.height() > 0.f
&& dy
> 0.f
&& dy
< 1.f
) {
819 dy
*= contentsRect
.height();
822 while( (y
= cb
- i
* dy
) > ct
) {
823 p
.drawLine( cl
, y
, cr
, y
);
829 QList
<QcGraphElement
*> elems
= _model
.elements();
831 int c
= elems
.count();
834 const QColor
& strokeClr
= strokeColor();
836 p
.setPen( strokeClr
);
842 QList
<QcGraphModel::Connection
> conns
= _model
.connections();
844 if( conns
.count() ) {
846 Q_FOREACH( QcGraphModel::Connection c
, conns
) {
847 addCurve( lines
, c
.a
, c
.b
);
853 QcGraphElement
*e1
= elems
[0];
855 for( i
= 1; i
< c
; ++i
) {
856 QcGraphElement
*e2
= elems
[i
];
857 addCurve( lines
, e1
, e2
);
864 p
.setRenderHint( QPainter::Antialiasing
, true );
865 p
.setBrush( Qt::NoBrush
);
866 p
.translate( contentsRect
.x(), contentsRect
.y() + contentsRect
.height() );
867 p
.scale( contentsRect
.width(), -contentsRect
.height() );
872 // draw rects and strings
875 p
.setRenderHint( QPainter::Antialiasing
, true );
877 QFontMetrics
fm( font() );
878 QColor rectColor
= plt
.color(QPalette::Button
).lighter(105);
879 QColor circleColor
= rectColor
; circleColor
.setAlpha(70);
880 QColor dotColor
= plt
.color(QPalette::Text
);
881 const QColor
& selectClr
= selectionColor();
886 for( i
= 0; i
< c
; ++i
) {
888 QcGraphElement
*e
= elems
[i
];
890 rect
.setSize( drawnElementSize(e
) );
892 pt
= Style::pos( e
->value
, contentsRect
);
893 rect
.moveCenter( pt
.toPoint() );
895 if( _style
== DotElements
)
896 drawDotElement(e
, rect
, contentsRect
,
897 dotColor
, circleColor
, strokeClr
, selectClr
,
900 drawRectElement(e
, rect
, rectColor
, strokeClr
, selectClr
, plt
, &p
);
907 void QcGraph::drawDotElement
908 ( QcGraphElement
*e
, const QRectF
&rect
, const QRect
& bounds
,
909 const QColor
& dotColor
, const QColor
& circleColor
,
910 const QColor
& textColor
, const QColor
& selectColor
,
911 const QPalette
&plt
, const QFontMetrics
&fm
, QPainter
*p
)
913 using namespace QtCollider::Style
;
914 using QtCollider::Style::Ellipse
;
918 drawRaised( p
, plt
, thumb
, circleColor
);
921 QRectF
r( thumb
._rect
);
922 qreal wdif
= r
.width() * 0.3;
923 qreal hdif
= r
.height() * 0.3;
924 p
->setPen( Qt::NoPen
);
925 p
->setBrush( e
->fillColor
.isValid() ? e
->fillColor
: dotColor
);
926 p
->drawEllipse( r
.adjusted( wdif
, hdif
, -wdif
, -hdif
) );
928 // selection indicator
930 p
->setBrush(Qt::NoBrush
);
931 p
->setPen( selectColor
);
932 p
->drawEllipse( thumb
._rect
.adjusted(1,1,-1,-1) );
936 p
->setPen( textColor
);
937 QString text
= e
->text
;
938 if( !text
.isEmpty() ) {
939 QRectF
lblRect( labelRect( e
, rect
.center(), bounds
, fm
) );
940 p
->drawText( lblRect
, 0, text
);
944 void QcGraph::drawRectElement
946 QcGraphElement
*e
, const QRectF
&rect
,
947 const QColor
& fillColor
,
948 const QColor
& textColor
,
949 const QColor
& selectColor
,
950 const QPalette
&plt
, QPainter
*p
)
952 using namespace QtCollider::Style
;
953 using QtCollider::Style::RoundRect
;
956 RoundRect
base(rect
, 5);
957 drawRaised( p
, plt
, base
, e
->fillColor
.isValid() ? e
->fillColor
: fillColor
);
959 // selection indicator
961 p
->setBrush(Qt::NoBrush
);
962 p
->setPen( selectColor
);
963 p
->drawRoundedRect( base
._rect
, 5, 5 );
967 p
->setPen( textColor
);
968 QString text
= e
->text
;
969 if( !text
.isEmpty() ) {
970 p
->drawText( rect
, Qt::AlignCenter
, text
);
974 void QcGraph::mousePressEvent( QMouseEvent
*ev
)
976 using namespace QtCollider::Style
;
978 QList
<QcGraphElement
*> elems
= _model
.elements();
979 int c
= elems
.count();
982 QPointF mpos
= ev
->pos();
983 QRectF
valueRect( this->valueRect() );
988 for( i
= 0; i
< c
; ++i
) {
989 QcGraphElement
*e
= elems
[i
];
990 r
.setSize( drawnElementSize(e
) );
991 QPointF
pt( Style::pos( e
->value
, valueRect
) );
993 if( r
.contains( mpos
) ) {
995 if( ev
->modifiers() & Qt::ShiftModifier
) {
996 setIndexSelected( i
, !e
->selected
);
1001 setIndexSelected( i
, true );
1005 _selection
.cached
= false;
1007 // if the element that was hit ended up selected
1008 // prepare for moving
1009 _selection
.shallMove
= true;
1010 _selection
.moveOrigin
= Style::value(mpos
, valueRect
);
1013 _selection
.shallMove
= false;
1021 _selection
.shallMove
= false;
1023 if( !(ev
->modifiers() & Qt::ShiftModifier
) ) {
1030 void QcGraph::mouseMoveEvent( QMouseEvent
*ev
)
1032 using namespace QtCollider::Style
;
1034 if( !ev
->buttons() ) return;
1036 if( !_editable
|| !_selection
.shallMove
|| !_selection
.size() ) return;
1038 if( !_selection
.cached
) {
1039 int c
= _selection
.count();
1040 for( int i
= 0; i
< c
; ++i
) {
1041 SelectedElement
&se
= _selection
.elems
[i
];
1042 se
.moveOrigin
= se
.elem
->value
;
1044 _selection
.cached
= true;
1047 QRectF
valueRect( this->valueRect() );
1048 QPointF
dValue( Style::value( ev
->pos(), valueRect
) );
1049 dValue
= dValue
- _selection
.moveOrigin
;
1051 moveSelected( dValue
, _selectionForm
, true );
1054 doAction( ev
->modifiers() );
1057 void QcGraph::keyPressEvent( QKeyEvent
*event
)
1059 int mods
= event
->modifiers();
1060 if( mods
& Qt::AltModifier
|| mods
& Qt::ShiftModifier
)
1064 int c
= _model
.elementCount();
1067 switch( event
->key() ) {
1070 // select the index after the last selected one (wrapping)
1071 // or extend selection to the right
1074 if(_selection
.count()) {
1075 for (i
= c
- 1; i
>= 0; --i
) {
1076 if(_model
.elementAt(i
)->selected
)
1081 if( event
->modifiers() & Qt::ShiftModifier
) {
1082 i
= qMin(i
+ 1, c
- 1);
1083 setIndexSelected( i
, true );
1089 setIndexSelected( i
, true );
1096 // select the index before the first selected one (wrapping)
1097 // or extend selection to the left
1099 if(_selection
.count()) {
1100 for(i
= 0; i
< c
; ++i
) {
1101 if(_model
.elementAt(i
)->selected
)
1106 if( event
->modifiers() & Qt::ShiftModifier
) {
1108 setIndexSelected( i
, true );
1112 if (i
< 0) i
= c
- 1;
1114 setIndexSelected( i
, true );
1126 if( !_editable
|| !_selection
.size() ) return;
1130 switch( event
->key() ) {
1132 dValue
.setY( _step
);
1135 dValue
.setY( - _step
);
1138 dValue
.setX( _step
);
1141 dValue
.setX( - _step
);
1147 moveSelected( dValue
, _selectionForm
, false );
1150 doAction( event
->modifiers() );
1154 void QcGraph::doAction( Qt::KeyboardModifiers mods
)
1156 Qt::KeyboardModifier ctrlMod
=
1160 Qt::ControlModifier
;
1163 if( mods
& ctrlMod
)
1164 Q_EMIT( metaAction() );