]> git.sesse.net Git - kdenlive/blob - src/onmonitoritems/onmonitorcornersitem.cpp
Fix timeline drawing pixel offset and remove deprecated use of matrix in QPainter
[kdenlive] / src / onmonitoritems / onmonitorcornersitem.cpp
1 /***************************************************************************
2  *   Copyright (C) 2010 by Till Theato (root@ttill.de)                     *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
18  ***************************************************************************/
19
20 #include "onmonitorcornersitem.h"
21 #include "kdenlivesettings.h"
22
23 #include <algorithm>
24
25 #include <QGraphicsSceneMouseEvent>
26 #include <QPainter>
27 #include <QStyleOptionGraphicsItem>
28 #include <QCursor>
29 #include <QGraphicsView>
30
31 OnMonitorCornersItem::OnMonitorCornersItem(QGraphicsItem* parent) :
32         QGraphicsPolygonItem(parent),
33         m_selectedCorner(-1),
34         m_modified(false),
35         m_view(NULL)
36 {
37     setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
38
39     QPen framepen(Qt::SolidLine);
40     framepen.setColor(Qt::yellow);
41     setPen(framepen);
42     setBrush(Qt::NoBrush);
43     setAcceptHoverEvents(true);
44 }
45
46 OnMonitorCornersItem::cornersActions OnMonitorCornersItem::getMode(QPointF pos, int *corner)
47 {
48     *corner = -1;
49     if (polygon().count() != 4)
50         return NoAction;
51
52     QPainterPath mouseArea;
53     qreal size = 12;
54     if (getView())
55         size /= m_view->matrix().m11();
56     mouseArea.addRect(pos.x() - size / 2, pos.y() - size / 2, size, size);
57     for (int i = 0; i < 4; ++i) {
58         if (mouseArea.contains(polygon().at(i))) {
59             *corner = i;
60             return Corner;
61         }
62     }
63     if (KdenliveSettings::onmonitoreffects_cornersshowcontrols()) {
64         if (mouseArea.contains(getCentroid()))
65             return Move;
66
67         int j;
68         for (int i = 0; i < 4; ++i) {
69             j = (i + 1) % 4;
70             if (mouseArea.contains(QLineF(polygon().at(i), polygon().at(j)).pointAt(.5))) {
71                 *corner = i;
72                 return MoveSide;
73             }
74         }
75     }
76
77     return NoAction;
78 }
79
80 void OnMonitorCornersItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
81 {
82     m_mode = getMode(event->pos(), &m_selectedCorner);
83     m_lastPoint = event->scenePos();
84
85     if (m_mode == NoAction)
86         event->ignore();
87 }
88
89 void OnMonitorCornersItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
90 {
91     /*if (event->buttons() != Qt::NoButton && (event->screenPos() - m_screenClickPoint).manhattanLength() < QApplication::startDragDistance()) {
92      *   event->accept();
93      *   return;
94     }*/
95
96     if (event->buttons() & Qt::LeftButton) {
97         QPointF mousePos = mapFromScene(event->scenePos());
98         QPolygonF p = polygon();
99         switch (m_mode) {
100         case Corner:
101             p.replace(m_selectedCorner, mousePos);
102             m_modified = true;
103             break;
104         case Move:
105             p.translate(mousePos - m_lastPoint);
106             m_modified = true;
107             break;
108         case MoveSide:
109             p[m_selectedCorner] += mousePos - m_lastPoint;
110             p[(m_selectedCorner + 1) % 4] += mousePos - m_lastPoint;
111             m_modified = true;
112             break;
113         default:
114             break;
115         }
116         m_lastPoint = mousePos;
117         setPolygon(p);
118     }
119
120     if (m_modified) {
121         event->accept();
122         if (KdenliveSettings::monitorscene_directupdate()) {
123             emit changed();
124             m_modified = false;
125         }
126     } else {
127         event->ignore();
128     }
129 }
130
131 void OnMonitorCornersItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
132 {
133     if (m_modified) {
134         m_modified = false;
135         emit changed();
136     }
137     event->accept();
138 }
139
140 void OnMonitorCornersItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
141 {
142     int corner;
143     switch (getMode(event->pos(), &corner)) {
144     case NoAction:
145         unsetCursor();
146         break;
147     case Move:
148         setCursor(QCursor(Qt::SizeAllCursor));
149         break;
150     default:
151         setCursor(QCursor(Qt::OpenHandCursor));
152         break;
153     }
154 }
155
156 void OnMonitorCornersItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
157 {
158     painter->setPen(QPen(Qt::yellow, 1, Qt::SolidLine));
159
160     if (KdenliveSettings::onmonitoreffects_cornersshowlines())
161         QGraphicsPolygonItem::paint(painter, option, widget);
162
163     if (polygon().count() != 4)
164         return;
165
166     double baseSize = 1 / painter->worldTransform().m11();
167     painter->setRenderHint(QPainter::Antialiasing);
168     painter->setBrush(QBrush(isEnabled() ? Qt::yellow : Qt::red));
169     double handleSize = 4  * baseSize;
170     for (int i = 0; i < 4; ++i)
171         painter->drawEllipse(polygon().at(i), handleSize, handleSize);
172
173     if (KdenliveSettings::onmonitoreffects_cornersshowcontrols() && isEnabled()) {
174         painter->setPen(QPen(Qt::red, 2, Qt::SolidLine));
175         double toolSize = 6 * baseSize;
176         // move tool
177         QPointF c = getCentroid();
178         painter->drawLine(QLineF(c - QPointF(toolSize, toolSize), c + QPointF(toolSize, toolSize)));
179         painter->drawLine(QLineF(c - QPointF(-toolSize, toolSize), c + QPointF(-toolSize, toolSize)));
180
181         // move side tools (2 corners at once)
182         int j;
183         for (int i = 0; i < 4; ++i) {
184             j = (i + 1) % 4;
185             QPointF m = QLineF(polygon().at(i), polygon().at(j)).pointAt(.5);
186             painter->drawRect(QRectF(-toolSize / 2., -toolSize / 2., toolSize, toolSize).translated(m));
187         }
188     }
189 }
190
191 QPointF OnMonitorCornersItem::getCentroid()
192 {
193     QList <QPointF> p = sortedClockwise();
194
195     /*
196      * See: http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
197      */
198
199     double A = 0;
200     int i, j;
201     for (i = 0; i < 4; ++i) {
202         j = (i + 1) % 4;
203         A += p[j].x() * p[i].y() - p[i].x() * p[j].y();
204     }
205     A /= 2.;
206     A = qAbs(A);
207
208     double x = 0, y = 0, f;
209     for (i = 0; i < 4; ++i) {
210         j = (i + 1) % 4;
211         f = (p[i].x() * p[j].y() - p[j].x() * p[i].y());
212         x += f * (p[i].x() + p[j].x());
213         y += f * (p[i].y() + p[j].y());
214     }
215     x /= 6*A;
216     y /= 6*A;
217     return QPointF(x, y);
218 }
219
220 QList <QPointF> OnMonitorCornersItem::sortedClockwise()
221 {
222     QList <QPointF> points = polygon().toList();
223     QPointF& a = points[0];
224     QPointF& b = points[1];
225     QPointF& c = points[2];
226     QPointF& d = points[3];
227
228     /*
229      * http://stackoverflow.com/questions/242404/sort-four-points-in-clockwise-order
230      */
231
232     double abc = a.x() * b.y() - a.y() * b.x() + b.x() * c.y() - b.y() * c.x() + c.x() * a.y() - c.y() * a.x();
233     if (abc > 0) {
234         double acd = a.x() * c.y() - a.y() * c.x() + c.x() * d.y() - c.y() * d.x() + d.x() * a.y() - d.y() * a.x();
235         if (acd > 0) {
236             ;
237         } else {
238             double abd = a.x() * b.y() - a.y() * b.x() + b.x() * d.y() - b.y() * d.x() + d.x() * a.y() - d.y() * a.x();
239             if (abd > 0) {
240                 std::swap(d, c);
241             } else{
242                 std::swap(a, d);
243             }
244         }
245     } else {
246         double acd = a.x() * c.y() - a.y() * c.x() + c.x() * d.y() - c.y() * d.x() + d.x() * a.y() - d.y() * a.x();
247         if (acd > 0) {
248             double abd = a.x() * b.y() - a.y() * b.x() + b.x() * d.y() - b.y() * d.x() + d.x() * a.y() - d.y() * a.x();
249             if (abd > 0) {
250                 std::swap(b, c);
251             } else {
252                 std::swap(a, b);
253             }
254         } else {
255             std::swap(a, c);
256         }
257     }
258     return points;
259 }
260
261 bool OnMonitorCornersItem::getView()
262 {
263     if (m_view)
264         return true;
265
266     if (scene() && scene()->views().count()) {
267         m_view = scene()->views()[0];
268         return true;
269     } else {
270         return false;
271     }
272 }
273
274 #include "onmonitorcornersitem.moc"