]> git.sesse.net Git - kdenlive/blobdiff - src/onmonitoritems/onmonitorcornersitem.cpp
Integrate with the required MLT hooks for getting Movit to work.
[kdenlive] / src / onmonitoritems / onmonitorcornersitem.cpp
index 7c0ee55274cd17a2dd435e037d020cc1afeb8f4d..0b6d1351e4a5fb5da453c2ecf6b6132ea10d4861 100644 (file)
 #include "onmonitorcornersitem.h"
 #include "kdenlivesettings.h"
 
+#include <algorithm>
+
 #include <QGraphicsSceneMouseEvent>
 #include <QPainter>
 #include <QStyleOptionGraphicsItem>
 #include <QCursor>
+#include <QGraphicsView>
 
-OnMonitorCornersItem::OnMonitorCornersItem(MonitorScene* scene, QGraphicsItem* parent) :
-        AbstractOnMonitorItem(scene),
-        QGraphicsPolygonItem(parent)
+OnMonitorCornersItem::OnMonitorCornersItem(QGraphicsItem* parent) :
+    QGraphicsPolygonItem(parent)
+  , m_mode(NoAction)
+  , m_selectedCorner(-1)
+  , m_modified(false)
+  , m_view(NULL)
 {
     setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
 
@@ -35,101 +41,235 @@ OnMonitorCornersItem::OnMonitorCornersItem(MonitorScene* scene, QGraphicsItem* p
     framepen.setColor(Qt::yellow);
     setPen(framepen);
     setBrush(Qt::NoBrush);
+    setAcceptHoverEvents(true);
 }
 
-cornersActions OnMonitorCornersItem::getMode(QPoint pos)
+OnMonitorCornersItem::cornersActions OnMonitorCornersItem::getMode(const QPointF &pos, int *corner)
 {
-    QPainterPath mouseArea;
-    pos = mapFromScene(pos).toPoint();
-    mouseArea.addRect(pos.x() - 6, pos.y() - 6, 12, 12);
-    if (mouseArea.contains(polygon().at(0)))
-        return Corner1;
-    else if (mouseArea.contains(polygon().at(1)))
-        return Corner2;
-    else if (mouseArea.contains(polygon().at(2)))
-        return Corner3;
-    else if (mouseArea.contains(polygon().at(3)))
-        return Corner4;
-    else
+    *corner = -1;
+    if (polygon().count() != 4)
         return NoAction;
-}
 
-void OnMonitorCornersItem::slotMousePressed(QGraphicsSceneMouseEvent* event)
-{
-    event->accept();
+    QPainterPath mouseArea;
+    qreal size = 12;
+    if (getView())
+        size /= m_view->matrix().m11();
+    mouseArea.addRect(pos.x() - size / 2, pos.y() - size / 2, size, size);
+    for (int i = 0; i < 4; ++i) {
+        if (mouseArea.contains(polygon().at(i))) {
+            *corner = i;
+            return Corner;
+        }
+    }
+    if (KdenliveSettings::onmonitoreffects_cornersshowcontrols()) {
+        if (mouseArea.contains(getCentroid()))
+            return Move;
 
-    if (!isEnabled())
-        return;
+        int j;
+        for (int i = 0; i < 4; ++i) {
+            j = (i + 1) % 4;
+            if (mouseArea.contains(QLineF(polygon().at(i), polygon().at(j)).pointAt(.5))) {
+                *corner = i;
+                return MoveSide;
+            }
+        }
+    }
 
-    m_mode = getMode(event->scenePos().toPoint());
+    return NoAction;
 }
 
-void OnMonitorCornersItem::slotMouseMoved(QGraphicsSceneMouseEvent* event)
+void OnMonitorCornersItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
 {
-    event->accept();
+    m_mode = getMode(event->pos(), &m_selectedCorner);
+    m_lastPoint = event->scenePos();
 
-    if (!isEnabled()) {
-        emit requestCursor(QCursor(Qt::ArrowCursor));
-        return;
-    }
+    if (m_mode == NoAction)
+        event->ignore();
+}
 
+void OnMonitorCornersItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
+{
     /*if (event->buttons() != Qt::NoButton && (event->screenPos() - m_screenClickPoint).manhattanLength() < QApplication::startDragDistance()) {
      *   event->accept();
      *   return;
     }*/
 
     if (event->buttons() & Qt::LeftButton) {
-        QPoint mousePos = mapFromScene(event->scenePos()).toPoint();
-        QPolygon p = polygon().toPolygon();
+        QPointF mousePos = mapFromScene(event->scenePos());
+        QPolygonF p = polygon();
         switch (m_mode) {
-        case Corner1:
-            p.replace(0, mousePos);
+        case Corner:
+            p.replace(m_selectedCorner, mousePos);
             m_modified = true;
             break;
-        case Corner2:
-            p.replace(1, mousePos);
+        case Move:
+            p.translate(mousePos - m_lastPoint);
             m_modified = true;
             break;
-        case Corner3:
-            p.replace(2, mousePos);
-            m_modified = true;
-            break;
-        case Corner4:
-            p.replace(3, mousePos);
+        case MoveSide:
+            p[m_selectedCorner] += mousePos - m_lastPoint;
+            p[(m_selectedCorner + 1) % 4] += mousePos - m_lastPoint;
             m_modified = true;
             break;
         default:
             break;
         }
+        m_lastPoint = mousePos;
         setPolygon(p);
-    } else {
-        switch (getMode(event->scenePos().toPoint())) {
-        case NoAction:
-            emit requestCursor(QCursor(Qt::ArrowCursor));
-            break;
-        default:
-            emit requestCursor(QCursor(Qt::OpenHandCursor));
-            break;
+    }
+
+    if (m_modified) {
+        event->accept();
+        if (KdenliveSettings::monitorscene_directupdate()) {
+            emit changed();
+            m_modified = false;
         }
+    } else {
+        event->ignore();
     }
-    if (m_modified && KdenliveSettings::monitorscene_directupdate()) {
-        emit actionFinished();
+}
+
+void OnMonitorCornersItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
+{
+    if (m_modified) {
         m_modified = false;
+        emit changed();
+    }
+    event->accept();
+}
+
+void OnMonitorCornersItem::hoverMoveEvent(QGraphicsSceneHoverEvent* event)
+{
+    int corner;
+    switch (getMode(event->pos(), &corner)) {
+    case NoAction:
+        unsetCursor();
+        break;
+    case Move:
+        setCursor(QCursor(Qt::SizeAllCursor));
+        break;
+    default:
+        setCursor(QCursor(Qt::OpenHandCursor));
+        break;
     }
 }
 
 void OnMonitorCornersItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
 {
+    painter->setPen(QPen(Qt::yellow, 1, Qt::SolidLine));
+
     if (KdenliveSettings::onmonitoreffects_cornersshowlines())
         QGraphicsPolygonItem::paint(painter, option, widget);
 
+    if (polygon().count() != 4)
+        return;
+
+    double baseSize = 1 / painter->worldTransform().m11();
     painter->setRenderHint(QPainter::Antialiasing);
-    painter->setBrush(QBrush(Qt::yellow));
-    double handleSize = 4 / painter->matrix().m11();
-    painter->drawEllipse(polygon().at(0), handleSize, handleSize);
-    painter->drawEllipse(polygon().at(1), handleSize, handleSize);
-    painter->drawEllipse(polygon().at(2), handleSize, handleSize);
-    painter->drawEllipse(polygon().at(3), handleSize, handleSize);
+    painter->setBrush(QBrush(isEnabled() ? Qt::yellow : Qt::red));
+    double handleSize = 4  * baseSize;
+    for (int i = 0; i < 4; ++i)
+        painter->drawEllipse(polygon().at(i), handleSize, handleSize);
+
+    if (KdenliveSettings::onmonitoreffects_cornersshowcontrols() && isEnabled()) {
+        painter->setPen(QPen(Qt::red, 2, Qt::SolidLine));
+        double toolSize = 6 * baseSize;
+        // move tool
+        QPointF c = getCentroid();
+        painter->drawLine(QLineF(c - QPointF(toolSize, toolSize), c + QPointF(toolSize, toolSize)));
+        painter->drawLine(QLineF(c - QPointF(-toolSize, toolSize), c + QPointF(-toolSize, toolSize)));
+
+        // move side tools (2 corners at once)
+        int j;
+        for (int i = 0; i < 4; ++i) {
+            j = (i + 1) % 4;
+            QPointF m = QLineF(polygon().at(i), polygon().at(j)).pointAt(.5);
+            painter->drawRect(QRectF(-toolSize / 2., -toolSize / 2., toolSize, toolSize).translated(m));
+        }
+    }
+}
+
+QPointF OnMonitorCornersItem::getCentroid()
+{
+    QList <QPointF> p = sortedClockwise();
+
+    /*
+     * See: http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
+     */
+
+    double A = 0;
+    int i, j;
+    for (i = 0; i < 4; ++i) {
+        j = (i + 1) % 4;
+        A += p[j].x() * p[i].y() - p[i].x() * p[j].y();
+    }
+    A /= 2.;
+    A = qAbs(A);
+
+    double x = 0, y = 0, f;
+    for (i = 0; i < 4; ++i) {
+        j = (i + 1) % 4;
+        f = (p[i].x() * p[j].y() - p[j].x() * p[i].y());
+        x += f * (p[i].x() + p[j].x());
+        y += f * (p[i].y() + p[j].y());
+    }
+    x /= 6*A;
+    y /= 6*A;
+    return QPointF(x, y);
+}
+
+QList <QPointF> OnMonitorCornersItem::sortedClockwise()
+{
+    QList <QPointF> points = polygon().toList();
+    QPointF& a = points[0];
+    QPointF& b = points[1];
+    QPointF& c = points[2];
+    QPointF& d = points[3];
+
+    /*
+     * http://stackoverflow.com/questions/242404/sort-four-points-in-clockwise-order
+     */
+
+    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();
+    if (abc > 0) {
+        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();
+        if (acd > 0) {
+            ;
+        } else {
+            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();
+            if (abd > 0) {
+                std::swap(d, c);
+            } else{
+                std::swap(a, d);
+            }
+        }
+    } else {
+        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();
+        if (acd > 0) {
+            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();
+            if (abd > 0) {
+                std::swap(b, c);
+            } else {
+                std::swap(a, b);
+            }
+        } else {
+            std::swap(a, c);
+        }
+    }
+    return points;
+}
+
+bool OnMonitorCornersItem::getView()
+{
+    if (m_view)
+        return true;
+
+    if (scene() && !scene()->views().isEmpty()) {
+        m_view = scene()->views().first();
+        return true;
+    } else {
+        return false;
+    }
 }
 
 #include "onmonitorcornersitem.moc"