From ac49362cea32b0902fdbf26a7628335d6d4d300f Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Mardelle Date: Sun, 27 Sep 2009 12:07:50 +0000 Subject: [PATCH] Fix spacer timeline corruption, fix possible corruption when moving a group with transition svn path=/trunk/kdenlive/; revision=3948 --- src/abstractgroupitem.cpp | 75 ++++++++++++++++++++++++--- src/abstractgroupitem.h | 3 +- src/customtrackview.cpp | 105 +++++++++++++++++++++++++++++--------- src/customtrackview.h | 2 + 4 files changed, 154 insertions(+), 31 deletions(-) diff --git a/src/abstractgroupitem.cpp b/src/abstractgroupitem.cpp index 817db973..642f50f6 100644 --- a/src/abstractgroupitem.cpp +++ b/src/abstractgroupitem.cpp @@ -57,7 +57,7 @@ CustomTrackScene* AbstractGroupItem::projectScene() return NULL; } -QPainterPath AbstractGroupItem::groupShape(QPointF offset) +QPainterPath AbstractGroupItem::clipGroupShape(QPointF offset) const { QPainterPath path; QList children = childItems(); @@ -71,6 +71,20 @@ QPainterPath AbstractGroupItem::groupShape(QPointF offset) return path; } +QPainterPath AbstractGroupItem::transitionGroupShape(QPointF offset) const +{ + QPainterPath path; + QList children = childItems(); + for (int i = 0; i < children.count(); i++) { + if (children.at(i)->type() == TRANSITIONWIDGET) { + QRectF r(children.at(i)->sceneBoundingRect()); + r.translate(offset); + path.addRect(r); + } + } + return path; +} + void AbstractGroupItem::addItem(QGraphicsItem * item) { addToGroup(item); @@ -181,19 +195,68 @@ QVariant AbstractGroupItem::itemChange(GraphicsItemChange change, const QVariant return QPointF(pos().x() - start.x(), pos().y()); }*/ - QPainterPath shape = groupShape(newPos - pos()); + QPainterPath shape = clipGroupShape(newPos - pos()); QList collindingItems = scene()->items(shape, Qt::IntersectsItemShape); for (int i = 0; i < children.count(); i++) { collindingItems.removeAll(children.at(i)); } + + if (!collindingItems.isEmpty()) { + bool forwardMove = xpos > start.x(); + int offset = 0; + for (int i = 0; i < collindingItems.count(); i++) { + QGraphicsItem *collision = collindingItems.at(i); + if (collision->type() == AVWIDGET) { + // Collision + if (newPos.y() != pos().y()) { + // Track change results in collision, restore original position + return pos(); + } + AbstractClipItem *item = static_cast (collision); + if (forwardMove) { + // Moving forward, determine best pos + QPainterPath clipPath; + clipPath.addRect(item->sceneBoundingRect()); + QPainterPath res = shape.intersected(clipPath); + offset = qMax(offset, (int)(res.boundingRect().width() + 0.5)); + } else { + // Moving backward, determine best pos + QPainterPath clipPath; + clipPath.addRect(item->sceneBoundingRect()); + QPainterPath res = shape.intersected(clipPath); + offset = qMax(offset, (int)(res.boundingRect().width() + 0.5)); + } + } + } + if (offset > 0) { + if (forwardMove) { + newPos.setX(newPos.x() - offset); + } else { + newPos.setX(newPos.x() + offset); + } + // If there is still a collision after our position adjust, restore original pos + collindingItems = scene()->items(clipGroupShape(newPos - pos()), Qt::IntersectsItemShape); + for (int i = 0; i < children.count(); i++) { + collindingItems.removeAll(children.at(i)); + } + for (int i = 0; i < collindingItems.count(); i++) + if (collindingItems.at(i)->type() == AVWIDGET) return pos(); + } + } + + shape = transitionGroupShape(newPos - pos()); + collindingItems = scene()->items(shape, Qt::IntersectsItemShape); + for (int i = 0; i < children.count(); i++) { + collindingItems.removeAll(children.at(i)); + } if (collindingItems.isEmpty()) return newPos; else { bool forwardMove = xpos > start.x(); int offset = 0; for (int i = 0; i < collindingItems.count(); i++) { QGraphicsItem *collision = collindingItems.at(i); - if (collision->type() == AVWIDGET) { + if (collision->type() == TRANSITIONWIDGET) { // Collision if (newPos.y() != pos().y()) { // Track change results in collision, restore original position @@ -222,15 +285,15 @@ QVariant AbstractGroupItem::itemChange(GraphicsItemChange change, const QVariant newPos.setX(newPos.x() + offset); } // If there is still a collision after our position adjust, restore original pos - collindingItems = scene()->items(groupShape(newPos - pos()), Qt::IntersectsItemShape); + collindingItems = scene()->items(transitionGroupShape(newPos - pos()), Qt::IntersectsItemShape); for (int i = 0; i < children.count(); i++) { collindingItems.removeAll(children.at(i)); } for (int i = 0; i < collindingItems.count(); i++) - if (collindingItems.at(i)->type() == AVWIDGET) return pos(); + if (collindingItems.at(i)->type() == TRANSITIONWIDGET) return pos(); } - return newPos; } + return newPos; } return QGraphicsItemGroup::itemChange(change, value); } diff --git a/src/abstractgroupitem.h b/src/abstractgroupitem.h index bd54c9af..ba0b213b 100644 --- a/src/abstractgroupitem.h +++ b/src/abstractgroupitem.h @@ -39,6 +39,8 @@ public: CustomTrackScene* projectScene(); void addItem(QGraphicsItem * item); int track() const; + QPainterPath clipGroupShape(QPointF) const; + QPainterPath transitionGroupShape(QPointF) const; // ItemInfo info() const; protected: @@ -50,7 +52,6 @@ protected: virtual void mousePressEvent(QGraphicsSceneMouseEvent * event); private: - QPainterPath groupShape(QPointF); void fixItemRect(); }; diff --git a/src/customtrackview.cpp b/src/customtrackview.cpp index d9e0f6ad..8f2e3b33 100644 --- a/src/customtrackview.cpp +++ b/src/customtrackview.cpp @@ -324,6 +324,7 @@ void CustomTrackView::mouseMoveEvent(QMouseEvent * event) { int pos = event->x(); int mappedXPos = (int)(mapToScene(event->pos()).x() + 0.5); + double snappedPos = getSnapPointForPos(mappedXPos); emit mousePosition(mappedXPos); if (event->buttons() & Qt::MidButton) return; @@ -349,11 +350,9 @@ void CustomTrackView::mouseMoveEvent(QMouseEvent * event) } else if (m_scrollTimer.isActive()) m_scrollTimer.stop(); } else if (m_operationMode == RESIZESTART && move) { - double snappedPos = getSnapPointForPos(mappedXPos); m_document->renderer()->pause(); m_dragItem->resizeStart((int)(snappedPos)); } else if (m_operationMode == RESIZEEND && move) { - double snappedPos = getSnapPointForPos(mappedXPos); m_document->renderer()->pause(); m_dragItem->resizeEnd((int)(snappedPos)); } else if (m_operationMode == FADEIN && move) { @@ -387,7 +386,40 @@ void CustomTrackView::mouseMoveEvent(QMouseEvent * event) } else if (m_operationMode == SPACER && move && m_selectionGroup) { // spacer tool int mappedClick = (int)(mapToScene(m_clickEvent).x() + 0.5); - m_selectionGroup->translate(mappedXPos - m_selectionGroup->sceneBoundingRect().left(), 0); + + // Make sure there is no collision + QList children = m_selectionGroup->childItems(); + QPainterPath shape = m_selectionGroup->clipGroupShape(QPointF(snappedPos - m_selectionGroup->sceneBoundingRect().left(), 0)); + QList collidingItems = scene()->items(shape, Qt::IntersectsItemShape); + collidingItems.removeAll(m_selectionGroup); + for (int i = 0; i < children.count(); i++) { + collidingItems.removeAll(children.at(i)); + } + bool collision = false; + for (int i = 0; i < collidingItems.count(); i++) { + if (collidingItems.at(i)->type() == AVWIDGET) { + collision = true; + break; + } + } + if (!collision) { + // Check transitions + shape = m_selectionGroup->transitionGroupShape(QPointF(snappedPos - m_selectionGroup->sceneBoundingRect().left(), 0)); + collidingItems = scene()->items(shape, Qt::IntersectsItemShape); + collidingItems.removeAll(m_selectionGroup); + for (int i = 0; i < children.count(); i++) { + collidingItems.removeAll(children.at(i)); + } + for (int i = 0; i < collidingItems.count(); i++) { + if (collidingItems.at(i)->type() == TRANSITIONWIDGET) { + collision = true; + break; + } + } + } + + if (!collision) + m_selectionGroup->translate(snappedPos - m_selectionGroup->sceneBoundingRect().left(), 0); //m_selectionGroup->setPos(mappedXPos + (((int) m_selectionGroup->boundingRect().topLeft().x() + 0.5) - mappedClick) , m_selectionGroup->pos().y()); } } @@ -758,17 +790,14 @@ void CustomTrackView::mousePressEvent(QMouseEvent * event) if (event->modifiers() == Qt::ControlModifier) { // Ctrl + click, select all items on track after click position int track = (int)(mapToScene(m_clickEvent).y() / m_tracksHeight); - selection = items(m_clickEvent.x(), track * m_tracksHeight + m_tracksHeight / 2, mapFromScene(sceneRect().width(), 0).x() - m_clickEvent.x(), m_tracksHeight / 2 - 2); - int maxHeight = m_tracksHeight * 1.5; - for (int i = 0; i < selection.count(); i++) { - // Check that we don't try to move a group with clips on other tracks - if (selection.at(i)->type() == GROUPWIDGET && (selection.at(i)->boundingRect().height() >= maxHeight)) { - emit displayMessage(i18n("Cannot use spacer in a track with a group"), ErrorMessage); - return; - } else if (selection.at(i)->parentItem() && (selection.at(i)->parentItem()->boundingRect().height() >= maxHeight)) { - emit displayMessage(i18n("Cannot use spacer in a track with a group"), ErrorMessage); - return; - } + QRectF rect(mapToScene(m_clickEvent).x(), track * m_tracksHeight + m_tracksHeight / 2, sceneRect().width() - mapToScene(m_clickEvent).x(), m_tracksHeight / 2 - 2); + + bool isOk; + selection = checkForGroups(rect, isOk); + if (!isOk) { + // groups found on track, do not allow the move + emit displayMessage(i18n("Cannot use spacer in a track with a group"), ErrorMessage); + return; } kDebug() << "SPACER TOOL + CTRL, SELECTING ALL CLIPS ON TRACK " << track << " WITH SELECTION RECT " << m_clickEvent.x() << "/" << track * m_tracksHeight + 1 << "; " << mapFromScene(sceneRect().width(), 0).x() - m_clickEvent.x() << "/" << m_tracksHeight - 2; @@ -2196,6 +2225,24 @@ void CustomTrackView::slotSwitchTrackVideo(int ix) setDocumentModified(); } +QList CustomTrackView::checkForGroups(const QRectF &rect, bool &ok) +{ + // Check there is no group going over several tracks there, or that would result in timeline corruption + QList selection = scene()->items(rect); + int maxHeight = m_tracksHeight * 1.5; + for (int i = 0; i < selection.count(); i++) { + // Check that we don't try to move a group with clips on other tracks + if (selection.at(i)->type() == GROUPWIDGET && (selection.at(i)->boundingRect().height() >= maxHeight)) { + ok = false; + break; + } else if (selection.at(i)->parentItem() && (selection.at(i)->parentItem()->boundingRect().height() >= maxHeight)) { + ok = false; + break; + } + } + return selection; +} + void CustomTrackView::slotRemoveSpace() { GenTime pos; @@ -2209,6 +2256,7 @@ void CustomTrackView::slotRemoveSpace() pos = GenTime((int)(mapToScene(m_menuPosition).x()), m_document->fps()); track = (int)(mapToScene(m_menuPosition).y() / m_tracksHeight); } + ClipItem *item = getClipItemAt(pos, track); if (item) { emit displayMessage(i18n("You must be in an empty space to remove space (time: %1, track: %2)", m_document->timecode().getTimecodeFromFrames(mapToScene(m_menuPosition).x()), track), ErrorMessage); @@ -2221,8 +2269,16 @@ void CustomTrackView::slotRemoveSpace() return; } - QRectF r(pos.frames(m_document->fps()), track * m_tracksHeight + m_tracksHeight / 2, sceneRect().width() - pos.frames(m_document->fps()), m_tracksHeight / 2 - 1); - QList items = m_scene->items(r); + // Make sure there is no group in the way + QRectF rect(pos.frames(m_document->fps()), track * m_tracksHeight + m_tracksHeight / 2, sceneRect().width() - pos.frames(m_document->fps()), m_tracksHeight / 2 - 2); + + bool isOk; + QList items = checkForGroups(rect, isOk); + if (!isOk) { + // groups found on track, do not allow the move + emit displayMessage(i18n("Cannot remove space in a track with a group"), ErrorMessage); + return; + } QList clipsToMove; QList transitionsToMove; @@ -2257,19 +2313,20 @@ void CustomTrackView::slotInsertSpace() if (d.exec() != QDialog::Accepted) return; GenTime spaceDuration = d.selectedDuration(); track = d.selectedTrack(); + ClipItem *item = getClipItemAt(pos, track); if (item) pos = item->startPos(); - int minh = 0; - int maxh = sceneRect().height(); - if (track != -1) { - minh = track * m_tracksHeight + m_tracksHeight / 2; - maxh = m_tracksHeight / 2 - 1; + // Make sure there is no group in the way + QRectF rect(pos.frames(m_document->fps()), track * m_tracksHeight + m_tracksHeight / 2, sceneRect().width() - pos.frames(m_document->fps()), m_tracksHeight / 2 - 2); + bool isOk; + QList items = checkForGroups(rect, isOk); + if (!isOk) { + // groups found on track, do not allow the move + emit displayMessage(i18n("Cannot insert space in a track with a group"), ErrorMessage); + return; } - QRectF r(pos.frames(m_document->fps()), minh, sceneRect().width() - pos.frames(m_document->fps()), maxh); - QList items = m_scene->items(r); - QList clipsToMove; QList transitionsToMove; diff --git a/src/customtrackview.h b/src/customtrackview.h index db54f1d3..2370fc7b 100644 --- a/src/customtrackview.h +++ b/src/customtrackview.h @@ -252,6 +252,8 @@ private: void updateClipTypeActions(ClipItem *clip); /** Whether an item can be moved to a new position without colliding with similar items */ bool itemCollision(AbstractClipItem *item, ItemInfo newPos); + /** Selects all items in the scene rect, and sets ok to false if a group going over several tracks is found in it */ + QList checkForGroups(const QRectF &rect, bool &ok); private slots: void slotRefreshGuides(); -- 2.39.2