Update copyright headers
[qt:qt.git] / demos / pathstroke / pathstroke.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the demonstration 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 The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/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 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "pathstroke.h"
43 #include "arthurstyle.h"
44 #include "arthurwidgets.h"
45
46 #include <stdio.h>
47
48 extern void draw_round_rect(QPainter *p, const QRect &bounds, int radius);
49
50
51 PathStrokeControls::PathStrokeControls(QWidget* parent, PathStrokeRenderer* renderer, bool smallScreen)
52       : QWidget(parent)
53 {
54     m_renderer = renderer;
55
56     if (smallScreen)
57         layoutForSmallScreens();
58     else
59         layoutForDesktop();
60 }
61
62 void PathStrokeControls::createCommonControls(QWidget* parent)
63 {
64     m_capGroup = new QGroupBox(parent);
65     m_capGroup->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
66     QRadioButton *flatCap = new QRadioButton(m_capGroup);
67     QRadioButton *squareCap = new QRadioButton(m_capGroup);
68     QRadioButton *roundCap = new QRadioButton(m_capGroup);
69     m_capGroup->setTitle(tr("Cap Style"));
70     flatCap->setText(tr("Flat"));
71     squareCap->setText(tr("Square"));
72     roundCap->setText(tr("Round"));
73     flatCap->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
74     squareCap->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
75     roundCap->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
76
77     m_joinGroup = new QGroupBox(parent);
78     m_joinGroup->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
79     QRadioButton *bevelJoin = new QRadioButton(m_joinGroup);
80     QRadioButton *miterJoin = new QRadioButton(m_joinGroup);
81     QRadioButton *roundJoin = new QRadioButton(m_joinGroup);
82     m_joinGroup->setTitle(tr("Join Style"));
83     bevelJoin->setText(tr("Bevel"));
84     miterJoin->setText(tr("Miter"));
85     roundJoin->setText(tr("Round"));
86
87     m_styleGroup = new QGroupBox(parent);
88     m_styleGroup->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
89     QRadioButton *solidLine = new QRadioButton(m_styleGroup);
90     QRadioButton *dashLine = new QRadioButton(m_styleGroup);
91     QRadioButton *dotLine = new QRadioButton(m_styleGroup);
92     QRadioButton *dashDotLine = new QRadioButton(m_styleGroup);
93     QRadioButton *dashDotDotLine = new QRadioButton(m_styleGroup);
94     QRadioButton *customDashLine = new QRadioButton(m_styleGroup);
95     m_styleGroup->setTitle(tr("Pen Style"));
96
97     QPixmap line_solid(":res/images/line_solid.png");
98     solidLine->setIcon(line_solid);
99     solidLine->setIconSize(line_solid.size());
100     QPixmap line_dashed(":res/images/line_dashed.png");
101     dashLine->setIcon(line_dashed);
102     dashLine->setIconSize(line_dashed.size());
103     QPixmap line_dotted(":res/images/line_dotted.png");
104     dotLine->setIcon(line_dotted);
105     dotLine->setIconSize(line_dotted.size());
106     QPixmap line_dash_dot(":res/images/line_dash_dot.png");
107     dashDotLine->setIcon(line_dash_dot);
108     dashDotLine->setIconSize(line_dash_dot.size());
109     QPixmap line_dash_dot_dot(":res/images/line_dash_dot_dot.png");
110     dashDotDotLine->setIcon(line_dash_dot_dot);
111     dashDotDotLine->setIconSize(line_dash_dot_dot.size());
112     customDashLine->setText(tr("Custom"));
113
114     int fixedHeight = bevelJoin->sizeHint().height();
115     solidLine->setFixedHeight(fixedHeight);
116     dashLine->setFixedHeight(fixedHeight);
117     dotLine->setFixedHeight(fixedHeight);
118     dashDotLine->setFixedHeight(fixedHeight);
119     dashDotDotLine->setFixedHeight(fixedHeight);
120
121     m_pathModeGroup = new QGroupBox(parent);
122     m_pathModeGroup->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
123     QRadioButton *curveMode = new QRadioButton(m_pathModeGroup);
124     QRadioButton *lineMode = new QRadioButton(m_pathModeGroup);
125     m_pathModeGroup->setTitle(tr("Line Style"));
126     curveMode->setText(tr("Curves"));
127     lineMode->setText(tr("Lines"));
128
129
130     // Layouts
131     QVBoxLayout *capGroupLayout = new QVBoxLayout(m_capGroup);
132     capGroupLayout->addWidget(flatCap);
133     capGroupLayout->addWidget(squareCap);
134     capGroupLayout->addWidget(roundCap);
135
136     QVBoxLayout *joinGroupLayout = new QVBoxLayout(m_joinGroup);
137     joinGroupLayout->addWidget(bevelJoin);
138     joinGroupLayout->addWidget(miterJoin);
139     joinGroupLayout->addWidget(roundJoin);
140
141     QVBoxLayout *styleGroupLayout = new QVBoxLayout(m_styleGroup);
142     styleGroupLayout->addWidget(solidLine);
143     styleGroupLayout->addWidget(dashLine);
144     styleGroupLayout->addWidget(dotLine);
145     styleGroupLayout->addWidget(dashDotLine);
146     styleGroupLayout->addWidget(dashDotDotLine);
147     styleGroupLayout->addWidget(customDashLine);
148
149     QVBoxLayout *pathModeGroupLayout = new QVBoxLayout(m_pathModeGroup);
150     pathModeGroupLayout->addWidget(curveMode);
151     pathModeGroupLayout->addWidget(lineMode);
152
153
154     // Connections
155     connect(flatCap, SIGNAL(clicked()), m_renderer, SLOT(setFlatCap()));
156     connect(squareCap, SIGNAL(clicked()), m_renderer, SLOT(setSquareCap()));
157     connect(roundCap, SIGNAL(clicked()), m_renderer, SLOT(setRoundCap()));
158
159     connect(bevelJoin, SIGNAL(clicked()), m_renderer, SLOT(setBevelJoin()));
160     connect(miterJoin, SIGNAL(clicked()), m_renderer, SLOT(setMiterJoin()));
161     connect(roundJoin, SIGNAL(clicked()), m_renderer, SLOT(setRoundJoin()));
162
163     connect(curveMode, SIGNAL(clicked()), m_renderer, SLOT(setCurveMode()));
164     connect(lineMode, SIGNAL(clicked()), m_renderer, SLOT(setLineMode()));
165
166     connect(solidLine, SIGNAL(clicked()), m_renderer, SLOT(setSolidLine()));
167     connect(dashLine, SIGNAL(clicked()), m_renderer, SLOT(setDashLine()));
168     connect(dotLine, SIGNAL(clicked()), m_renderer, SLOT(setDotLine()));
169     connect(dashDotLine, SIGNAL(clicked()), m_renderer, SLOT(setDashDotLine()));
170     connect(dashDotDotLine, SIGNAL(clicked()), m_renderer, SLOT(setDashDotDotLine()));
171     connect(customDashLine, SIGNAL(clicked()), m_renderer, SLOT(setCustomDashLine()));
172
173     // Set the defaults:
174     flatCap->setChecked(true);
175     bevelJoin->setChecked(true);
176     curveMode->setChecked(true);
177     solidLine->setChecked(true);
178 }
179
180
181 void PathStrokeControls::layoutForDesktop()
182 {
183     QGroupBox *mainGroup = new QGroupBox(this);
184     mainGroup->setFixedWidth(180);
185     mainGroup->setTitle(tr("Path Stroking"));
186
187     createCommonControls(mainGroup);
188
189     QGroupBox* penWidthGroup = new QGroupBox(mainGroup);
190     QSlider *penWidth = new QSlider(Qt::Horizontal, penWidthGroup);
191     penWidth->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
192     penWidthGroup->setTitle(tr("Pen Width"));
193     penWidth->setRange(0, 500);
194
195     QPushButton *animated = new QPushButton(mainGroup);
196     animated->setText(tr("Animate"));
197     animated->setCheckable(true);
198
199     QPushButton *showSourceButton = new QPushButton(mainGroup);
200     showSourceButton->setText(tr("Show Source"));
201 #ifdef QT_OPENGL_SUPPORT
202     QPushButton *enableOpenGLButton = new QPushButton(mainGroup);
203     enableOpenGLButton->setText(tr("Use OpenGL"));
204     enableOpenGLButton->setCheckable(true);
205     enableOpenGLButton->setChecked(m_renderer->usesOpenGL());
206     if (!QGLFormat::hasOpenGL())
207         enableOpenGLButton->hide();
208 #endif
209     QPushButton *whatsThisButton = new QPushButton(mainGroup);
210     whatsThisButton->setText(tr("What's This?"));
211     whatsThisButton->setCheckable(true);
212
213
214     // Layouts:
215     QVBoxLayout *penWidthLayout = new QVBoxLayout(penWidthGroup);
216     penWidthLayout->addWidget(penWidth);
217
218     QVBoxLayout * mainLayout = new QVBoxLayout(this);
219     mainLayout->setMargin(0);
220     mainLayout->addWidget(mainGroup);
221
222     QVBoxLayout *mainGroupLayout = new QVBoxLayout(mainGroup);
223     mainGroupLayout->setMargin(3);
224     mainGroupLayout->addWidget(m_capGroup);
225     mainGroupLayout->addWidget(m_joinGroup);
226     mainGroupLayout->addWidget(m_styleGroup);
227     mainGroupLayout->addWidget(penWidthGroup);
228     mainGroupLayout->addWidget(m_pathModeGroup);
229     mainGroupLayout->addWidget(animated);
230     mainGroupLayout->addStretch(1);
231     mainGroupLayout->addWidget(showSourceButton);
232 #ifdef QT_OPENGL_SUPPORT
233     mainGroupLayout->addWidget(enableOpenGLButton);
234 #endif
235     mainGroupLayout->addWidget(whatsThisButton);
236
237
238     // Set up connections
239     connect(animated, SIGNAL(toggled(bool)),
240             m_renderer, SLOT(setAnimation(bool)));
241
242     connect(penWidth, SIGNAL(valueChanged(int)),
243             m_renderer, SLOT(setPenWidth(int)));
244
245     connect(showSourceButton, SIGNAL(clicked()), m_renderer, SLOT(showSource()));
246 #ifdef QT_OPENGL_SUPPORT
247     connect(enableOpenGLButton, SIGNAL(clicked(bool)), m_renderer, SLOT(enableOpenGL(bool)));
248 #endif
249     connect(whatsThisButton, SIGNAL(clicked(bool)), m_renderer, SLOT(setDescriptionEnabled(bool)));
250     connect(m_renderer, SIGNAL(descriptionEnabledChanged(bool)),
251             whatsThisButton, SLOT(setChecked(bool)));
252
253
254     // Set the defaults
255     animated->setChecked(true);
256     penWidth->setValue(50);
257
258 }
259
260 void PathStrokeControls::layoutForSmallScreens()
261 {
262     createCommonControls(this);
263
264     m_capGroup->layout()->setMargin(0);
265     m_joinGroup->layout()->setMargin(0);
266     m_styleGroup->layout()->setMargin(0);
267     m_pathModeGroup->layout()->setMargin(0);
268
269     QPushButton* okBtn = new QPushButton(tr("OK"), this);
270     okBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
271     okBtn->setMinimumSize(100,okBtn->minimumSize().height());
272
273     QPushButton* quitBtn = new QPushButton(tr("Quit"), this);
274     quitBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
275     quitBtn->setMinimumSize(100, okBtn->minimumSize().height());
276
277     QLabel *penWidthLabel = new QLabel(tr(" Width:"));
278     QSlider *penWidth = new QSlider(Qt::Horizontal, this);
279     penWidth->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
280     penWidth->setRange(0, 500);
281
282 #ifdef QT_OPENGL_SUPPORT
283     QPushButton *enableOpenGLButton = new QPushButton(this);
284     enableOpenGLButton->setText(tr("Use OpenGL"));
285     enableOpenGLButton->setCheckable(true);
286     enableOpenGLButton->setChecked(m_renderer->usesOpenGL());
287     if (!QGLFormat::hasOpenGL())
288         enableOpenGLButton->hide();
289 #endif
290
291     // Layouts:
292     QHBoxLayout *penWidthLayout = new QHBoxLayout(0);
293     penWidthLayout->addWidget(penWidthLabel, 0, Qt::AlignRight);
294     penWidthLayout->addWidget(penWidth);
295
296     QVBoxLayout *leftLayout = new QVBoxLayout(0);
297     leftLayout->addWidget(m_capGroup);
298     leftLayout->addWidget(m_joinGroup);
299 #ifdef QT_OPENGL_SUPPORT
300     leftLayout->addWidget(enableOpenGLButton);
301 #endif
302     leftLayout->addLayout(penWidthLayout);
303
304     QVBoxLayout *rightLayout = new QVBoxLayout(0);
305     rightLayout->addWidget(m_styleGroup);
306     rightLayout->addWidget(m_pathModeGroup);
307
308     QGridLayout *mainLayout = new QGridLayout(this);
309     mainLayout->setMargin(0);
310
311     // Add spacers around the form items so we don't look stupid at higher resolutions
312     mainLayout->addItem(new QSpacerItem(0,0), 0, 0, 1, 4);
313     mainLayout->addItem(new QSpacerItem(0,0), 1, 0, 2, 1);
314     mainLayout->addItem(new QSpacerItem(0,0), 1, 3, 2, 1);
315     mainLayout->addItem(new QSpacerItem(0,0), 3, 0, 1, 4);
316
317     mainLayout->addLayout(leftLayout, 1, 1);
318     mainLayout->addLayout(rightLayout, 1, 2);
319     mainLayout->addWidget(quitBtn, 2, 1, Qt::AlignHCenter | Qt::AlignTop);
320     mainLayout->addWidget(okBtn, 2, 2, Qt::AlignHCenter | Qt::AlignTop);
321
322 #ifdef QT_OPENGL_SUPPORT
323     connect(enableOpenGLButton, SIGNAL(clicked(bool)), m_renderer, SLOT(enableOpenGL(bool)));
324 #endif
325
326     connect(penWidth, SIGNAL(valueChanged(int)), m_renderer, SLOT(setPenWidth(int)));
327     connect(quitBtn, SIGNAL(clicked()), this, SLOT(emitQuitSignal()));
328     connect(okBtn, SIGNAL(clicked()), this, SLOT(emitOkSignal()));
329
330     m_renderer->setAnimation(true);
331     penWidth->setValue(50);
332 }
333
334 void PathStrokeControls::emitQuitSignal()
335 {   emit quitPressed();  }
336
337 void PathStrokeControls::emitOkSignal()
338 {   emit okPressed();   }
339
340
341 PathStrokeWidget::PathStrokeWidget(bool smallScreen)
342 {
343     setWindowTitle(tr("Path Stroking"));
344
345     // Widget construction and property setting
346     m_renderer = new PathStrokeRenderer(this, smallScreen);
347
348     m_controls = new PathStrokeControls(0, m_renderer, smallScreen);
349
350     // Layouting
351     QHBoxLayout *viewLayout = new QHBoxLayout(this);
352     viewLayout->addWidget(m_renderer);
353
354     if (!smallScreen)
355         viewLayout->addWidget(m_controls);
356
357     m_renderer->loadSourceFile(":res/pathstroke/pathstroke.cpp");
358     m_renderer->loadDescription(":res/pathstroke/pathstroke.html");
359
360     connect(m_renderer, SIGNAL(clicked()), this, SLOT(showControls()));
361     connect(m_controls, SIGNAL(okPressed()), this, SLOT(hideControls()));
362     connect(m_controls, SIGNAL(quitPressed()), QApplication::instance(), SLOT(quit()));
363 }
364
365
366 void PathStrokeWidget::showControls()
367 {
368     m_controls->showFullScreen();
369 }
370
371
372 void PathStrokeWidget::hideControls()
373 {
374     m_controls->hide();
375 }
376
377
378 void PathStrokeWidget::setStyle( QStyle * style )
379 {
380     QWidget::setStyle(style);
381     if (m_controls != 0)
382     {
383         m_controls->setStyle(style);
384         
385         QList<QWidget *> widgets = m_controls->findChildren<QWidget *>();
386         foreach (QWidget *w, widgets)
387             w->setStyle(style);
388     }
389 }
390
391
392 PathStrokeRenderer::PathStrokeRenderer(QWidget *parent, bool smallScreen)
393     : ArthurFrame(parent)
394 {
395     m_smallScreen = smallScreen;
396     m_pointSize = 10;
397     m_activePoint = -1;
398     m_capStyle = Qt::FlatCap;
399     m_joinStyle = Qt::BevelJoin;
400     m_pathMode = CurveMode;
401     m_penWidth = 1;
402     m_penStyle = Qt::SolidLine;
403     m_wasAnimated = true;
404     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
405     setAttribute(Qt::WA_AcceptTouchEvents);
406 }
407
408 void PathStrokeRenderer::paint(QPainter *painter)
409 {
410     if (m_points.isEmpty())
411         initializePoints();
412
413     painter->setRenderHint(QPainter::Antialiasing);
414
415     QPalette pal = palette();
416     painter->setPen(Qt::NoPen);
417
418     // Construct the path
419     QPainterPath path;
420     path.moveTo(m_points.at(0));
421
422     if (m_pathMode == LineMode) {
423         for (int i=1; i<m_points.size(); ++i) {
424             path.lineTo(m_points.at(i));
425         }
426     } else {
427         int i=1;
428         while (i + 2 < m_points.size()) {
429             path.cubicTo(m_points.at(i), m_points.at(i+1), m_points.at(i+2));
430             i += 3;
431         }
432         while (i < m_points.size()) {
433             path.lineTo(m_points.at(i));
434             ++i;
435         }
436     }
437
438     // Draw the path
439     {
440         QColor lg = Qt::red;
441
442         // The "custom" pen
443         if (m_penStyle == Qt::NoPen) {
444             QPainterPathStroker stroker;
445             stroker.setWidth(m_penWidth);
446             stroker.setJoinStyle(m_joinStyle);
447             stroker.setCapStyle(m_capStyle);
448
449             QVector<qreal> dashes;
450             qreal space = 4;
451             dashes << 1 << space
452                    << 3 << space
453                    << 9 << space
454                    << 27 << space
455                    << 9 << space
456                    << 3 << space;
457             stroker.setDashPattern(dashes);
458             QPainterPath stroke = stroker.createStroke(path);
459             painter->fillPath(stroke, lg);
460
461         } else {
462             QPen pen(lg, m_penWidth, m_penStyle, m_capStyle, m_joinStyle);
463             painter->strokePath(path, pen);
464         }
465     }
466
467     if (1) {
468         // Draw the control points
469         painter->setPen(QColor(50, 100, 120, 200));
470         painter->setBrush(QColor(200, 200, 210, 120));
471         for (int i=0; i<m_points.size(); ++i) {
472             QPointF pos = m_points.at(i);
473             painter->drawEllipse(QRectF(pos.x() - m_pointSize,
474                                        pos.y() - m_pointSize,
475                                        m_pointSize*2, m_pointSize*2));
476         }
477         painter->setPen(QPen(Qt::lightGray, 0, Qt::SolidLine));
478         painter->setBrush(Qt::NoBrush);
479         painter->drawPolyline(m_points);
480     }
481
482 }
483
484 void PathStrokeRenderer::initializePoints()
485 {
486     const int count = 7;
487     m_points.clear();
488     m_vectors.clear();
489
490     QMatrix m;
491     qreal rot = 360 / count;
492     QPointF center(width() / 2, height() / 2);
493     QMatrix vm;
494     vm.shear(2, -1);
495     vm.scale(3, 3);
496
497     for (int i=0; i<count; ++i) {
498         m_vectors << QPointF(.1f, .25f) * (m * vm);
499         m_points << QPointF(0, 100) * m + center;
500         m.rotate(rot);
501     }
502 }
503
504 void PathStrokeRenderer::updatePoints()
505 {
506     qreal pad = 10;
507     qreal left = pad;
508     qreal right = width() - pad;
509     qreal top = pad;
510     qreal bottom = height() - pad;
511
512     Q_ASSERT(m_points.size() == m_vectors.size());
513     for (int i=0; i<m_points.size(); ++i) {
514         QPointF pos = m_points.at(i);
515         QPointF vec = m_vectors.at(i);
516         pos += vec;
517         if (pos.x() < left || pos.x() > right) {
518             vec.setX(-vec.x());
519             pos.setX(pos.x() < left ? left : right);
520         } if (pos.y() < top || pos.y() > bottom) {
521             vec.setY(-vec.y());
522             pos.setY(pos.y() < top ? top : bottom);
523         }
524         m_points[i] = pos;
525         m_vectors[i] = vec;
526     }
527     update();
528 }
529
530 void PathStrokeRenderer::mousePressEvent(QMouseEvent *e)
531 {
532     if (!m_fingerPointMapping.isEmpty())
533         return;
534     setDescriptionEnabled(false);
535     m_activePoint = -1;
536     qreal distance = -1;
537     for (int i=0; i<m_points.size(); ++i) {
538         qreal d = QLineF(e->pos(), m_points.at(i)).length();
539         if ((distance < 0 && d < 8 * m_pointSize) || d < distance) {
540             distance = d;
541             m_activePoint = i;
542         }
543     }
544
545     if (m_activePoint != -1) {
546         m_wasAnimated = m_timer.isActive();
547         setAnimation(false);
548         mouseMoveEvent(e);
549     }
550     
551     // If we're not running in small screen mode, always assume we're dragging
552     m_mouseDrag = !m_smallScreen;
553     m_mousePress = e->pos();
554 }
555
556 void PathStrokeRenderer::mouseMoveEvent(QMouseEvent *e)
557 {
558     if (!m_fingerPointMapping.isEmpty())
559         return;
560     // If we've moved more then 25 pixels, assume user is dragging
561     if (!m_mouseDrag && QPoint(m_mousePress - e->pos()).manhattanLength() > 25)
562         m_mouseDrag = true;
563
564     if (m_mouseDrag && m_activePoint >= 0 && m_activePoint < m_points.size()) {
565         m_points[m_activePoint] = e->pos();
566         update();
567     }
568 }
569
570 void PathStrokeRenderer::mouseReleaseEvent(QMouseEvent *)
571 {
572     if (!m_fingerPointMapping.isEmpty())
573         return;
574     m_activePoint = -1;
575     setAnimation(m_wasAnimated);
576
577     if (!m_mouseDrag && m_smallScreen)
578         emit clicked();
579 }
580
581 void PathStrokeRenderer::timerEvent(QTimerEvent *e)
582 {
583     if (e->timerId() == m_timer.timerId()) {
584         updatePoints();
585         QApplication::syncX();
586     } // else if (e->timerId() == m_fpsTimer.timerId()) {
587 //         emit frameRate(m_frameCount);
588 //         m_frameCount = 0;
589 //     }
590 }
591
592 bool PathStrokeRenderer::event(QEvent *e)
593 {
594     bool touchBegin = false;
595     switch (e->type()) {
596     case QEvent::TouchBegin:
597         touchBegin = true;
598     case QEvent::TouchUpdate:
599         {
600             const QTouchEvent *const event = static_cast<const QTouchEvent*>(e);
601             const QList<QTouchEvent::TouchPoint> points = event->touchPoints();
602             foreach (const QTouchEvent::TouchPoint &touchPoint, points) {
603                 const int id = touchPoint.id();
604                 switch (touchPoint.state()) {
605                 case Qt::TouchPointPressed:
606                     {
607                         // find the point, move it
608                         QSet<int> activePoints = QSet<int>::fromList(m_fingerPointMapping.values());
609                         int activePoint = -1;
610                         qreal distance = -1;
611                         const int pointsCount = m_points.size();
612                         for (int i=0; i<pointsCount; ++i) {
613                             if (activePoints.contains(i))
614                                 continue;
615
616                             qreal d = QLineF(touchPoint.pos(), m_points.at(i)).length();
617                             if ((distance < 0 && d < 12 * m_pointSize) || d < distance) {
618                                 distance = d;
619                                 activePoint = i;
620                             }
621                         }
622                         if (activePoint != -1) {
623                             m_fingerPointMapping.insert(touchPoint.id(), activePoint);
624                             m_points[activePoint] = touchPoint.pos();
625                         }
626                     }
627                     break;
628                 case Qt::TouchPointReleased:
629                     {
630                         // move the point and release
631                         QHash<int,int>::iterator it = m_fingerPointMapping.find(id);
632                         m_points[it.value()] = touchPoint.pos();
633                         m_fingerPointMapping.erase(it);
634                     }
635                     break;
636                 case Qt::TouchPointMoved:
637                     {
638                         // move the point
639                         const int pointIdx = m_fingerPointMapping.value(id, -1);
640                         if (pointIdx >= 0)
641                             m_points[pointIdx] = touchPoint.pos();
642                     }
643                     break;
644                 default:
645                     break;
646                 }
647             }
648             if (m_fingerPointMapping.isEmpty()) {
649                 e->ignore();
650                 return false;
651             } else {
652                 if (touchBegin) {
653                     m_wasAnimated = m_timer.isActive();
654                     setAnimation(false);
655                 }
656                 update();
657                 return true;
658             }
659         }
660         break;
661     case QEvent::TouchEnd:
662         if (m_fingerPointMapping.isEmpty()) {
663             e->ignore();
664             return false;
665         }
666         m_fingerPointMapping.clear();
667         setAnimation(m_wasAnimated);
668         return true;
669         break;
670     default:
671         break;
672     }
673     return QWidget::event(e);
674 }
675
676 void PathStrokeRenderer::setAnimation(bool animation)
677 {
678     m_timer.stop();
679 //     m_fpsTimer.stop();
680
681     if (animation) {
682         m_timer.start(25, this);
683 //         m_fpsTimer.start(1000, this);
684 //         m_frameCount = 0;
685     }
686 }