Doc: Enabling Qt QML linking to Qt Quick.
[profile/ivi/qtdeclarative.git] / tools / qmleasing / splineeditor.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "splineeditor.h"
43 #include "segmentproperties.h"
44
45 #include <QPainter>
46 #include <QMouseEvent>
47 #include <QContextMenuEvent>
48 #include <QDebug>
49 #include <QApplication>
50
51 const int canvasWidth = 640;
52 const int canvasHeight = 320;
53
54 const int canvasMargin = 160;
55
56 SplineEditor::SplineEditor(QWidget *parent) :
57     QWidget(parent), m_pointListWidget(0), m_block(false)
58 {
59     setFixedSize(canvasWidth + canvasMargin * 2, canvasHeight + canvasMargin * 2);
60
61     m_controlPoints.append(QPointF(0.4, 0.075));
62     m_controlPoints.append(QPointF(0.45,0.24));
63     m_controlPoints.append(QPointF(0.5,0.5));
64
65     m_controlPoints.append(QPointF(0.55,0.76));
66     m_controlPoints.append(QPointF(0.7,0.9));
67     m_controlPoints.append(QPointF(1.0, 1.0));
68
69     m_numberOfSegments = 2;
70
71     m_activeControlPoint = -1;
72
73     m_mouseDrag = false;
74
75     m_pointContextMenu = new QMenu(this);
76     m_deleteAction = new QAction(tr("Delete point"), m_pointContextMenu);
77     m_smoothAction = new QAction(tr("Smooth point"), m_pointContextMenu);
78     m_cornerAction = new QAction(tr("Corner point"), m_pointContextMenu);
79
80     m_smoothAction->setCheckable(true);
81
82     m_pointContextMenu->addAction(m_deleteAction);
83     m_pointContextMenu->addAction(m_smoothAction);
84     m_pointContextMenu->addAction(m_cornerAction);
85
86     m_curveContextMenu = new QMenu(this);
87
88     m_addPoint = new QAction(tr("Add point"), m_pointContextMenu);
89
90     m_curveContextMenu->addAction(m_addPoint);
91
92     initPresets();
93
94     invalidateSmoothList();
95 }
96
97 static inline QPointF mapToCanvas(const QPointF &point)
98 {
99     return QPointF(point.x() * canvasWidth + canvasMargin,
100                    canvasHeight - point.y() * canvasHeight + canvasMargin);
101 }
102
103 static inline QPointF mapFromCanvas(const QPointF &point)
104 {
105     return QPointF((point.x() - canvasMargin) / canvasWidth ,
106                    1 - (point.y() - canvasMargin) / canvasHeight);
107 }
108
109 static inline void paintControlPoint(const QPointF &controlPoint, QPainter *painter, bool edit,
110                                      bool realPoint, bool active, bool smooth)
111 {
112     int pointSize = 4;
113
114     if (active)
115         painter->setBrush(QColor(140, 140, 240, 255));
116     else
117         painter->setBrush(QColor(120, 120, 220, 255));
118
119     if (realPoint) {
120         pointSize = 6;
121         painter->setBrush(QColor(80, 80, 210, 150));
122     }
123
124     painter->setPen(QColor(50, 50, 50, 140));
125
126     if (!edit)
127         painter->setBrush(QColor(160, 80, 80, 250));
128
129     if (smooth) {
130         painter->drawEllipse(QRectF(mapToCanvas(controlPoint).x() - pointSize + 0.5,
131                                     mapToCanvas(controlPoint).y() - pointSize + 0.5,
132                                     pointSize * 2, pointSize * 2));
133     } else {
134         painter->drawRect(QRectF(mapToCanvas(controlPoint).x() - pointSize + 0.5,
135                                  mapToCanvas(controlPoint).y() - pointSize + 0.5,
136                                  pointSize * 2, pointSize * 2));
137     }
138 }
139
140 static inline bool indexIsRealPoint(int i)
141 {
142     return  !((i + 1) % 3);
143 }
144
145 static inline int pointForControlPoint(int i)
146 {
147     if ((i % 3) == 0)
148         return i - 1;
149
150     if ((i % 3) == 1)
151         return i + 1;
152
153     return i;
154 }
155
156 void drawCleanLine(QPainter *painter, const QPoint p1, QPoint p2)
157 {
158     painter->drawLine(p1 + QPointF(0.5 , 0.5), p2 + QPointF(0.5, 0.5));
159 }
160
161 void SplineEditor::paintEvent(QPaintEvent *)
162 {
163     QPainter painter(this);
164
165     QPen pen(Qt::black);
166     pen.setWidth(1);
167     painter.fillRect(0,0,width() - 1, height() - 1, QBrush(Qt::white));
168     painter.drawRect(0,0,width() - 1, height() - 1);
169
170     painter.setRenderHint(QPainter::Antialiasing);
171
172     pen = QPen(Qt::gray);
173     pen.setWidth(1);
174     pen.setStyle(Qt::DashLine);
175     painter.setPen(pen);
176     drawCleanLine(&painter,mapToCanvas(QPoint(0, 0)).toPoint(), mapToCanvas(QPoint(1, 0)).toPoint());
177     drawCleanLine(&painter,mapToCanvas(QPoint(0, 1)).toPoint(), mapToCanvas(QPoint(1, 1)).toPoint());
178
179     for (int i = 0; i < m_numberOfSegments; i++) {
180         QPainterPath path;
181         QPointF p0;
182
183         if (i == 0)
184             p0 = mapToCanvas(QPointF(0.0, 0.0));
185         else
186             p0 = mapToCanvas(m_controlPoints.at(i * 3 - 1));
187
188         path.moveTo(p0);
189
190         QPointF p1 = mapToCanvas(m_controlPoints.at(i * 3));
191         QPointF p2 = mapToCanvas(m_controlPoints.at(i * 3 + 1));
192         QPointF p3 = mapToCanvas(m_controlPoints.at(i * 3 + 2));
193         path.cubicTo(p1, p2, p3);
194         painter.strokePath(path, QPen(QBrush(Qt::black), 2));
195
196         QPen pen(Qt::black);
197         pen.setWidth(1);
198         pen.setStyle(Qt::DashLine);
199         painter.setPen(pen);
200         painter.drawLine(p0, p1);
201         painter.drawLine(p3, p2);
202     }
203
204     paintControlPoint(QPointF(0.0, 0.0), &painter, false, true, false, false);
205     paintControlPoint(QPointF(1.0, 1.0), &painter, false, true, false, false);
206
207     for (int i = 0; i < m_controlPoints.count() - 1; ++i)
208         paintControlPoint(m_controlPoints.at(i),
209                           &painter,
210                           true,
211                           indexIsRealPoint(i),
212                           i == m_activeControlPoint,
213                           isControlPointSmooth(i));
214 }
215
216 void SplineEditor::mousePressEvent(QMouseEvent *e)
217 {
218     if (e->button() == Qt::LeftButton) {
219         m_activeControlPoint = findControlPoint(e->pos());
220
221         if (m_activeControlPoint != -1) {
222             mouseMoveEvent(e);
223         }
224         m_mousePress = e->pos();
225         e->accept();
226     }
227 }
228
229 void SplineEditor::mouseReleaseEvent(QMouseEvent *e)
230 {
231     if (e->button() == Qt::LeftButton) {
232         m_activeControlPoint = -1;
233
234         m_mouseDrag = false;
235         e->accept();
236     }
237 }
238
239 void SplineEditor::contextMenuEvent(QContextMenuEvent *e)
240 {
241     int index = findControlPoint(e->pos());
242
243     if (index > 0 && indexIsRealPoint(index)) {
244         m_smoothAction->setChecked(isControlPointSmooth(index));
245         QAction* action = m_pointContextMenu->exec(e->globalPos());
246         if (action == m_deleteAction)
247             deletePoint(index);
248         else if (action == m_smoothAction)
249             smoothPoint(index);
250         else if (action == m_cornerAction)
251             cornerPoint(index);
252     } else {
253         QAction* action = m_curveContextMenu->exec(e->globalPos());
254         if (action == m_addPoint)
255             addPoint(e->pos());
256     }
257 }
258
259 void SplineEditor::invalidate()
260 {
261     QEasingCurve easingCurve(QEasingCurve::BezierSpline);
262
263     for (int i = 0; i < m_numberOfSegments; ++i) {
264         easingCurve.addCubicBezierSegment(m_controlPoints.at(i * 3),
265                                           m_controlPoints.at(i * 3 + 1),
266                                           m_controlPoints.at(i * 3 + 2));
267     }
268     setEasingCurve(easingCurve);
269     invalidateSegmentProperties();
270 }
271
272 void SplineEditor::invalidateSmoothList()
273 {
274     m_smoothList.clear();
275
276     for (int i = 0; i < (m_numberOfSegments - 1); ++i)
277         m_smoothList.append(isSmooth(i * 3 + 2));
278
279 }
280
281 void SplineEditor::invalidateSegmentProperties()
282 {
283     for (int i = 0; i < m_numberOfSegments; ++i) {
284         SegmentProperties *segmentProperties = m_segmentProperties.at(i);
285         bool smooth = false;
286         if (i < (m_numberOfSegments - 1)) {
287             smooth = m_smoothList.at(i);
288         }
289         segmentProperties->setSegment(i, m_controlPoints.mid(i * 3, 3), smooth, i == (m_numberOfSegments - 1));
290     }
291 }
292
293 QHash<QString, QEasingCurve> SplineEditor::presets() const
294 {
295     return m_presets;
296 }
297
298 QString SplineEditor::generateCode()
299 {
300     QString s = QLatin1String("[");
301     foreach (const QPointF &point, m_controlPoints) {
302         s += QString::number(point.x(), 'g', 2) + "," + QString::number(point.y(), 'g', 3) + ",";
303     }
304     s.chop(1); //removing last ","
305     s += "]";
306
307     return s;
308 }
309
310 QStringList SplineEditor::presetNames() const
311 {
312     return m_presets.keys();
313 }
314
315 QWidget *SplineEditor::pointListWidget()
316 {
317     if (!m_pointListWidget) {
318         setupPointListWidget();
319     }
320
321     return m_pointListWidget;
322 }
323
324 int SplineEditor::findControlPoint(const QPoint &point)
325 {
326     int pointIndex = -1;
327     qreal distance = -1;
328     for (int i = 0; i<m_controlPoints.size() - 1; ++i) {
329         qreal d = QLineF(point, mapToCanvas(m_controlPoints.at(i))).length();
330         if ((distance < 0 && d < 10) || d < distance) {
331             distance = d;
332             pointIndex = i;
333         }
334     }
335     return pointIndex;
336 }
337
338 static inline bool veryFuzzyCompare(qreal r1, qreal r2)
339 {
340     if (qFuzzyCompare(r1, 2))
341         return true;
342
343     int r1i = qRound(r1 * 20);
344     int r2i = qRound(r2 * 20);
345
346     if (qFuzzyCompare(qreal(r1i) / 20, qreal(r2i) / 20))
347         return true;
348
349     return false;
350 }
351
352 bool SplineEditor::isSmooth(int i) const
353 {
354     if (i == 0)
355         return false;
356
357     QPointF p = m_controlPoints.at(i);
358     QPointF p_before = m_controlPoints.at(i - 1);
359     QPointF p_after = m_controlPoints.at(i + 1);
360
361     QPointF v1 = p_after - p;
362     v1 = v1 / v1.manhattanLength(); //normalize
363
364     QPointF v2 = p - p_before;
365     v2 = v2 / v2.manhattanLength(); //normalize
366
367     return veryFuzzyCompare(v1.x(), v2.x()) && veryFuzzyCompare(v1.y(), v2.y());
368 }
369
370 void SplineEditor::smoothPoint(int index)
371 {
372     if (m_smoothAction->isChecked()) {
373
374         QPointF before = QPointF(0,0);
375         if (index > 3)
376             before = m_controlPoints.at(index - 3);
377
378         QPointF after = QPointF(1.0, 1.0);
379         if ((index + 3) < m_controlPoints.count())
380             after = m_controlPoints.at(index + 3);
381
382         QPointF tangent = (after - before) / 6;
383
384         QPointF thisPoint =  m_controlPoints.at(index);
385
386         if (index > 0)
387             m_controlPoints[index - 1] = thisPoint - tangent;
388
389         if (index + 1  < m_controlPoints.count())
390             m_controlPoints[index + 1] = thisPoint + tangent;
391
392         m_smoothList[index / 3] = true;
393     } else {
394         m_smoothList[index / 3] = false;
395     }
396     invalidate();
397     update();
398 }
399
400 void SplineEditor::cornerPoint(int index)
401 {
402     QPointF before = QPointF(0,0);
403     if (index > 3)
404         before = m_controlPoints.at(index - 3);
405
406     QPointF after = QPointF(1.0, 1.0);
407     if ((index + 3) < m_controlPoints.count())
408         after = m_controlPoints.at(index + 3);
409
410     QPointF thisPoint =  m_controlPoints.at(index);
411
412     if (index > 0)
413         m_controlPoints[index - 1] = (before - thisPoint) / 3 + thisPoint;
414
415     if (index + 1  < m_controlPoints.count())
416         m_controlPoints[index + 1] = (after - thisPoint) / 3 + thisPoint;
417
418     m_smoothList[(index) / 3] = false;
419     invalidate();
420 }
421
422 void SplineEditor::deletePoint(int index)
423 {
424     m_controlPoints.remove(index - 1, 3);
425     m_numberOfSegments--;
426
427     invalidateSmoothList();
428     setupPointListWidget();
429     invalidate();
430 }
431
432 void SplineEditor::addPoint(const QPointF point)
433 {
434     QPointF newPos = mapFromCanvas(point);
435     int splitIndex = 0;
436     for (int i=0; i < m_controlPoints.size() - 1; ++i) {
437         if (indexIsRealPoint(i) && m_controlPoints.at(i).x() > newPos.x()) {
438             break;
439         } else if (indexIsRealPoint(i))
440             splitIndex = i;
441     }
442     QPointF before = QPointF(0,0);
443     if (splitIndex > 0)
444         before = m_controlPoints.at(splitIndex);
445
446     QPointF after = QPointF(1.0, 1.0);
447     if ((splitIndex + 3) < m_controlPoints.count())
448         after = m_controlPoints.at(splitIndex + 3);
449
450     if (splitIndex > 0) {
451         m_controlPoints.insert(splitIndex + 2, (newPos + after) / 2);
452         m_controlPoints.insert(splitIndex + 2, newPos);
453         m_controlPoints.insert(splitIndex + 2, (newPos + before) / 2);
454     } else {
455         m_controlPoints.insert(splitIndex + 1, (newPos + after) / 2);
456         m_controlPoints.insert(splitIndex + 1, newPos);
457         m_controlPoints.insert(splitIndex + 1, (newPos + before) / 2);
458     }
459     m_numberOfSegments++;
460
461     invalidateSmoothList();
462     setupPointListWidget();
463     invalidate();
464 }
465
466 void SplineEditor::initPresets()
467 {
468     const QPointF endPoint(1.0, 1.0);
469     {
470         QEasingCurve easingCurve(QEasingCurve::BezierSpline);
471         easingCurve.addCubicBezierSegment(QPointF(0.4, 0.075), QPointF(0.45, 0.24), QPointF(0.5, 0.5));
472         easingCurve.addCubicBezierSegment(QPointF(0.55, 0.76), QPointF(0.7, 0.9), endPoint);
473         m_presets.insert(tr("Standard Easing"), easingCurve);
474     }
475
476     {
477         QEasingCurve easingCurve(QEasingCurve::BezierSpline);
478         easingCurve.addCubicBezierSegment(QPointF(0.43, 0.0025), QPointF(0.65, 1), endPoint);
479         m_presets.insert(tr("Simple"), easingCurve);
480     }
481
482     {
483         QEasingCurve easingCurve(QEasingCurve::BezierSpline);
484         easingCurve.addCubicBezierSegment(QPointF(0.43, 0.0025), QPointF(0.38, 0.51), QPointF(0.57, 0.99));
485         easingCurve.addCubicBezierSegment(QPointF(0.8, 0.69), QPointF(0.65, 1), endPoint);
486         m_presets.insert(tr("Simple Bounce"), easingCurve);
487     }
488
489     {
490         QEasingCurve easingCurve(QEasingCurve::BezierSpline);
491         easingCurve.addCubicBezierSegment(QPointF(0.4, 0.075), QPointF(0.64, -0.0025), QPointF(0.74, 0.23));
492         easingCurve.addCubicBezierSegment(QPointF(0.84, 0.46), QPointF(0.91, 0.77), endPoint);
493         m_presets.insert(tr("Slow in fast out"), easingCurve);
494     }
495
496     {
497         QEasingCurve easingCurve(QEasingCurve::BezierSpline);
498         easingCurve.addCubicBezierSegment(QPointF(0.43, 0.0025), QPointF(0.47, 0.51), QPointF(0.59, 0.94));
499         easingCurve.addCubicBezierSegment(QPointF(0.84, 0.95), QPointF( 0.99, 0.94), endPoint);
500         m_presets.insert(tr("Snapping"), easingCurve);
501     }
502
503     {
504         QEasingCurve easingCurve(QEasingCurve::BezierSpline);
505         easingCurve.addCubicBezierSegment(QPointF( 0.38, 0.35),QPointF(0.38, 0.7), QPointF(0.45, 0.99));
506         easingCurve.addCubicBezierSegment(QPointF(0.48, 0.66), QPointF(0.62, 0.62), QPointF(0.66, 0.99));
507         easingCurve.addCubicBezierSegment(QPointF(0.69, 0.76), QPointF(0.77, 0.76), QPointF(0.79, 0.99));
508         easingCurve.addCubicBezierSegment(QPointF(0.83, 0.91), QPointF(0.87, 0.92), QPointF(0.91, 0.99));
509         easingCurve.addCubicBezierSegment(QPointF(0.95, 0.95), QPointF(0.97, 0.94), endPoint);
510         m_presets.insert(tr("Complex Bounce"), easingCurve);
511     }
512
513     {
514         QEasingCurve easingCurve4(QEasingCurve::BezierSpline);
515         easingCurve4.addCubicBezierSegment(QPointF(0.12, -0.12),QPointF(0.23, -0.19), QPointF( 0.35, -0.09));
516         easingCurve4.addCubicBezierSegment(QPointF(0.47, 0.005), QPointF(0.52, 1), QPointF(0.62, 1.1));
517         easingCurve4.addCubicBezierSegment(QPointF(0.73, 1.2), QPointF(0.91,1 ), endPoint);
518         m_presets.insert(tr("Overshoot"), easingCurve4);
519     }
520 }
521
522 void SplineEditor::setupPointListWidget()
523 {
524     if (!m_pointListWidget)
525         m_pointListWidget = new QScrollArea(this);
526
527     if (m_pointListWidget->widget())
528         delete m_pointListWidget->widget();
529
530     m_pointListWidget->setFrameStyle(QFrame::NoFrame);
531     m_pointListWidget->setWidgetResizable(true);
532     m_pointListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
533
534     m_pointListWidget->setWidget(new QWidget(m_pointListWidget));
535     QVBoxLayout *layout = new QVBoxLayout(m_pointListWidget->widget());
536     layout->setMargin(0);
537     layout->setSpacing(2);
538     m_pointListWidget->widget()->setLayout(layout);
539
540     m_segmentProperties.clear();
541
542     { //implicit 0,0
543         QWidget *widget = new QWidget(m_pointListWidget->widget());
544         Ui_Pane pane;
545         pane.setupUi(widget);
546         pane.p1_x->setValue(0);
547         pane.p1_y->setValue(0);
548         layout->addWidget(widget);
549         pane.label->setText("p0");
550         widget->setEnabled(false);
551     }
552
553     for (int i = 0; i < m_numberOfSegments; ++i) {
554         SegmentProperties *segmentProperties = new SegmentProperties(m_pointListWidget->widget());
555         layout->addWidget(segmentProperties);
556         bool smooth = false;
557         if (i < (m_numberOfSegments - 1)) {
558             smooth = m_smoothList.at(i);
559         }
560         segmentProperties->setSegment(i, m_controlPoints.mid(i * 3, 3), smooth, i == (m_numberOfSegments - 1));
561         segmentProperties->setSplineEditor(this);
562         m_segmentProperties << segmentProperties;
563     }
564     layout->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Expanding));
565
566     m_pointListWidget->viewport()->show();
567     m_pointListWidget->viewport()->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
568     m_pointListWidget->show();
569 }
570
571 bool SplineEditor::isControlPointSmooth(int i) const
572 {
573     if (i == 0)
574         return false;
575
576     if (i == m_controlPoints.count() - 1)
577         return false;
578
579     if (m_numberOfSegments == 1)
580         return false;
581
582     int index = pointForControlPoint(i);
583
584     if (index == 0)
585         return false;
586
587     if (index == m_controlPoints.count() - 1)
588         return false;
589
590     return m_smoothList.at(index / 3);
591 }
592
593 QPointF limitToCanvas(const QPointF point)
594 {
595     qreal left = -qreal( canvasMargin) / qreal(canvasWidth);
596     qreal width = 1.0 - 2.0 * left;
597
598     qreal top = -qreal( canvasMargin) / qreal(canvasHeight);
599     qreal height = 1.0 - 2.0 * top;
600
601     QPointF p = point;
602     QRectF r(left, top, width, height);
603
604     if (p.x() > r.right()) {
605         p.setX(r.right());
606     }
607     if (p.x() < r.left()) {
608         p.setX(r.left());
609     }
610     if (p.y() < r.top()) {
611         p.setY(r.top());
612     }
613     if (p.y() > r.bottom()) {
614         p.setY(r.bottom());
615     }
616     return p;
617 }
618
619 void SplineEditor::mouseMoveEvent(QMouseEvent *e)
620 {
621     // If we've moved more then 25 pixels, assume user is dragging
622     if (!m_mouseDrag && QPoint(m_mousePress - e->pos()).manhattanLength() > qApp->startDragDistance())
623         m_mouseDrag = true;
624
625     QPointF p = mapFromCanvas(e->pos());
626
627     if (m_mouseDrag && m_activeControlPoint >= 0 && m_activeControlPoint < m_controlPoints.size()) {
628         p = limitToCanvas(p);
629         if (indexIsRealPoint(m_activeControlPoint)) {
630             //move also the tangents
631             QPointF targetPoint = p;
632             QPointF distance = targetPoint - m_controlPoints[m_activeControlPoint];
633             m_controlPoints[m_activeControlPoint] = targetPoint;
634             m_controlPoints[m_activeControlPoint - 1] += distance;
635             m_controlPoints[m_activeControlPoint + 1] += distance;
636         } else {
637             if (!isControlPointSmooth(m_activeControlPoint)) {
638                 m_controlPoints[m_activeControlPoint] = p;
639             } else {
640                 QPointF targetPoint = p;
641                 QPointF distance = targetPoint - m_controlPoints[m_activeControlPoint];
642                 m_controlPoints[m_activeControlPoint] = p;
643
644                 if ((m_activeControlPoint > 1) && (m_activeControlPoint % 3) == 0) { //right control point
645                     m_controlPoints[m_activeControlPoint - 2] -= distance;
646                 } else if ((m_activeControlPoint < (m_controlPoints.count() - 2)) //left control point
647                            && (m_activeControlPoint % 3) == 1) {
648                     m_controlPoints[m_activeControlPoint + 2] -= distance;
649                 }
650             }
651         }
652         invalidate();
653     }
654 }
655
656 void SplineEditor::setEasingCurve(const QEasingCurve &easingCurve)
657 {
658     if (m_easingCurve == easingCurve)
659         return;
660     m_block = true;
661     m_easingCurve = easingCurve;
662     m_controlPoints = m_easingCurve.toCubicSpline();
663     m_numberOfSegments = m_controlPoints.count() / 3;
664     update();
665     emit easingCurveChanged();
666
667     const QString code = generateCode();
668     emit easingCurveCodeChanged(code);
669
670     m_block = false;
671 }
672
673 void SplineEditor::setPreset(const QString &name)
674 {
675     setEasingCurve(m_presets.value(name));
676     invalidateSmoothList();
677     setupPointListWidget();
678 }
679
680 void SplineEditor::setEasingCurve(const QString &code)
681 {
682     if (m_block)
683         return;
684     if (code.left(1) == QLatin1String("[") && code.right(1) == QLatin1String("]")) {
685         QString cleanCode = code;
686         cleanCode.remove(0, 1);
687         cleanCode.chop(1);
688         const QStringList stringList = cleanCode.split(QLatin1Char(','), QString::SkipEmptyParts);
689         if (stringList.count() >= 6 && (stringList.count() % 6 == 0)) {
690             QList<qreal> realList;
691             foreach (const QString &string, stringList) {
692                 bool ok;
693                 realList.append(string.toDouble(&ok));
694                 if (!ok)
695                     return;
696             }
697             QList<QPointF> points;
698             for (int i = 0; i < realList.count() / 2; ++i)
699                 points.append(QPointF(realList.at(i * 2), realList.at(i * 2 + 1)));
700             if (points.last() == QPointF(1.0, 1.0)) {
701                 QEasingCurve easingCurve(QEasingCurve::BezierSpline);
702
703                 for (int i = 0; i < points.count() / 3; ++i) {
704                     easingCurve.addCubicBezierSegment(points.at(i * 3),
705                                                       points.at(i * 3 + 1),
706                                                       points.at(i * 3 + 2));
707                 }
708                 setEasingCurve(easingCurve);
709                 invalidateSmoothList();
710                 setupPointListWidget();
711             }
712         }
713     }
714 }