X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fabstractgroupitem.cpp;h=40923afafe1276fdad0850ebb838ab65e89cf706;hb=b691c7f8faddc1d7f2bf6acdba5cee879e810548;hp=dc5573d5f681fad58b4517c19031291882256cb9;hpb=d5e2d9e691b22dab741ed689df6d87478ba24c9f;p=kdenlive diff --git a/src/abstractgroupitem.cpp b/src/abstractgroupitem.cpp index dc5573d5..40923afa 100644 --- a/src/abstractgroupitem.cpp +++ b/src/abstractgroupitem.cpp @@ -18,50 +18,107 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ -#include -#include -#include -#include - -#include -#include - #include "abstractgroupitem.h" #include "abstractclipitem.h" #include "kdenlivesettings.h" #include "customtrackscene.h" +#include "customtrackview.h" + +#include + +#include +#include +#include +#include +#include -AbstractGroupItem::AbstractGroupItem(double fps): QGraphicsItemGroup(), m_fps(fps) { - setFlags(QGraphicsItem::ItemClipsToShape | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable); +AbstractGroupItem::AbstractGroupItem(double /* fps */) : + QObject(), + QGraphicsItemGroup() +{ + setZValue(1); + setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable); +#if QT_VERSION >= 0x040600 + setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); +#endif + setAcceptDrops(true); + m_resizeInfos = QList (); } -int AbstractGroupItem::type() const { +int AbstractGroupItem::type() const +{ return GROUPWIDGET; } -CustomTrackScene* AbstractGroupItem::projectScene() { - return static_cast (scene()); +int AbstractGroupItem::track() const +{ + return (int)(scenePos().y() / KdenliveSettings::trackheight()); } +void AbstractGroupItem::setItemLocked(bool locked) +{ + if (locked) + setSelected(false); -QPainterPath AbstractGroupItem::groupShape(QPointF offset) { - QList children = childItems(); + setFlag(QGraphicsItem::ItemIsMovable, !locked); + setFlag(QGraphicsItem::ItemIsSelectable, !locked); + + foreach (QGraphicsItem *child, childItems()) + ((AbstractClipItem *)child)->setItemLocked(locked); +} + +bool AbstractGroupItem::isItemLocked() const +{ + return !(flags() & (QGraphicsItem::ItemIsSelectable)); +} + +CustomTrackScene* AbstractGroupItem::projectScene() +{ + if (scene()) return static_cast (scene()); + return NULL; +} + +QPainterPath AbstractGroupItem::clipGroupShape(QPointF offset) const +{ + return groupShape(AVWIDGET, offset); +} + +QPainterPath AbstractGroupItem::transitionGroupShape(QPointF offset) const +{ + return groupShape(TRANSITIONWIDGET, offset); +} + +QPainterPath AbstractGroupItem::groupShape(GRAPHICSRECTITEM type, QPointF offset) const +{ QPainterPath path; + QList children = childItems(); for (int i = 0; i < children.count(); i++) { - QRectF r = children.at(i)->sceneBoundingRect(); - //kDebug()<<"// GROUP CHild: "<type() == (int)type) { + QRectF r(children.at(i)->sceneBoundingRect()); + r.translate(offset); + path.addRect(r); + } else if (children.at(i)->type() == GROUPWIDGET) { + QList subchildren = children.at(i)->childItems(); + for (int j = 0; j < subchildren.count(); j++) { + if (subchildren.at(j)->type() == (int)type) { + QRectF r(subchildren.at(j)->sceneBoundingRect()); + r.translate(offset); + path.addRect(r); + } + } + } } return path; } -void AbstractGroupItem::addItem(QGraphicsItem * item) { +void AbstractGroupItem::addItem(QGraphicsItem * item) +{ addToGroup(item); //fixItemRect(); } -void AbstractGroupItem::fixItemRect() { +void AbstractGroupItem::fixItemRect() +{ QPointF start = boundingRect().topLeft(); if (start != QPointF(0, 0)) { translate(0 - start.x(), 0 - start.y()); @@ -69,77 +126,345 @@ void AbstractGroupItem::fixItemRect() { } } +/*ItemInfo AbstractGroupItem::info() const { + ItemInfo itemInfo; + itemInfo.startPos = m_startPos; + itemInfo.track = m_track; + return itemInfo; +}*/ + // virtual -void AbstractGroupItem::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) { - p->fillRect(boundingRect(), QColor(200, 100, 100, 100)); +void AbstractGroupItem::paint(QPainter *p, const QStyleOptionGraphicsItem *option, QWidget *) +{ + const double scale = option->matrix.m11(); + QColor bgcolor(100, 100, 200, 100); + QRectF bound = option->exposedRect.adjusted(0, 0, 1, 1); + p->setClipRect(bound); + p->fillRect(option->exposedRect, bgcolor); + QPen pen = p->pen(); + pen.setColor(QColor(200, 90, 90)); + pen.setStyle(Qt::DashLine); + pen.setWidthF(0.0); + //pen.setCosmetic(true); + p->setPen(pen); + p->drawRect(boundingRect().adjusted(0, 0, - 1 / scale, 0)); } //virtual -QVariant AbstractGroupItem::itemChange(GraphicsItemChange change, const QVariant &value) { - if (change == ItemPositionChange && scene()) { +QVariant AbstractGroupItem::itemChange(GraphicsItemChange change, const QVariant &value) +{ + if (change == QGraphicsItem::ItemSelectedChange) { + if (value.toBool()) setZValue(10); + else setZValue(1); + } + if (change == ItemPositionChange && scene() && parentItem() == 0) { // calculate new position. - QPointF newPos = value.toPointF(); - QPainterPath sceneShape = groupShape(newPos); + const int trackHeight = KdenliveSettings::trackheight(); QPointF start = sceneBoundingRect().topLeft(); - QPointF sc = mapToScene(pos()); - int posx = start.x() + newPos.x(); //projectScene()->getSnapPointForPos(start.x() + sc.x(), KdenliveSettings::snaptopoints()); - //int startx = projectScene()->getSnapPointForPos(start.x(), false); - //int startx = projectScene()->getSnapPointForPos(start.x(), false); - kDebug() << "------------------------------------"; - kDebug() << "BRect: " << start.x() << "diff: " << newPos.x() << ",mapd: " << start.x() - sc.x(); - return newPos; - //kDebug()<<"BR: "<sceneBoundingRect(); - return pos(); - } - //else posx -= startx; - //posx = qMax(posx, 0); - newPos.setX(posx); - //kDebug()<<"Y POS: "<getSnapPointForPos((int)(start.x() + newPos.x() - pos().x()), KdenliveSettings::snaptopoints()); + xpos = qMax(xpos, 0); + //kDebug()<<"GRP XPOS:"<tracksCount() - (int)(boundingRect().height() + 5) / trackHeight); + correctedTrack = qMax(correctedTrack, 0); - QList items = scene()->items(sceneShape, Qt::IntersectsItemShape); + proposedTrack += (correctedTrack - realTrack); + // Check if top item is a clip or a transition + int offset = 0; + int topTrack = -1; QList children = childItems(); for (int i = 0; i < children.count(); i++) { - items.removeAll(children.at(i)); + int currentTrack = (int)(children.at(i)->scenePos().y() / trackHeight); + if (children.at(i)->type() == AVWIDGET) { + if (topTrack == -1 || currentTrack <= topTrack) { + offset = 0; + topTrack = currentTrack; + } + } else if (children.at(i)->type() == TRANSITIONWIDGET) { + if (topTrack == -1 || currentTrack < topTrack) { + offset = (int)(trackHeight / 3 * 2 - 1); + topTrack = currentTrack; + } + } else if (children.at(i)->type() == GROUPWIDGET) { + QList subchildren = children.at(i)->childItems(); + bool clipGroup = false; + for (int j = 0; j < subchildren.count(); j++) { + if (subchildren.at(j)->type() == AVWIDGET) { + clipGroup = true; + break; + } + } + if (clipGroup) { + if (topTrack == -1 || currentTrack <= topTrack) { + offset = 0; + topTrack = currentTrack; + } + } else { + if (topTrack == -1 || currentTrack < topTrack) { + offset = (int)(trackHeight / 3 * 2 - 1); + topTrack = currentTrack; + } + } + } } + newPos.setY((int)((proposedTrack) * trackHeight) + offset); + //if (newPos == start) return start; + /*if (newPos.x() < 0) { + // If group goes below 0, adjust position to 0 + return QPointF(pos().x() - start.x(), pos().y()); + }*/ + QList collidingItems; + QPainterPath shape; + if (projectScene()->editMode() == NORMALEDIT) { + shape = clipGroupShape(newPos - pos()); + collidingItems = scene()->items(shape, Qt::IntersectsItemShape); + collidingItems.removeAll(this); + for (int i = 0; i < children.count(); i++) { + if (children.at(i)->type() == GROUPWIDGET) { + QList subchildren = children.at(i)->childItems(); + for (int j = 0; j < subchildren.count(); j++) { + collidingItems.removeAll(subchildren.at(j)); + } + } + collidingItems.removeAll(children.at(i)); + } + } + if (!collidingItems.isEmpty()) { + bool forwardMove = xpos > start.x(); + int offset = 0; + for (int i = 0; i < collidingItems.count(); i++) { + QGraphicsItem *collision = collidingItems.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 + collidingItems = scene()->items(clipGroupShape(newPos - pos()), Qt::IntersectsItemShape); + collidingItems.removeAll(this); + for (int i = 0; i < children.count(); i++) { + if (children.at(i)->type() == GROUPWIDGET) { + QList subchildren = children.at(i)->childItems(); + for (int j = 0; j < subchildren.count(); j++) { + collidingItems.removeAll(subchildren.at(j)); + } + } + collidingItems.removeAll(children.at(i)); + } + for (int i = 0; i < collidingItems.count(); i++) + if (collidingItems.at(i)->type() == AVWIDGET) return pos(); + } + } - if (!items.isEmpty()) { - for (int i = 0; i < items.count(); i++) { - if (items.at(i)->type() == AVWIDGET) { - // Collision! - //kDebug()<<"/// COLLISION WITH ITEM: "<sceneBoundingRect(); - return pos(); - QPointF otherPos = items.at(i)->pos(); - if ((int) otherPos.y() != (int) pos().y()) return pos(); - if (pos().x() < otherPos.x()) { - // move clip just before colliding clip - int npos = (static_cast < AbstractClipItem* >(items.at(i))->startPos()).frames(m_fps) - sceneBoundingRect().width(); - newPos.setX(npos); + if (projectScene()->editMode() == NORMALEDIT) { + shape = transitionGroupShape(newPos - pos()); + collidingItems = scene()->items(shape, Qt::IntersectsItemShape); + collidingItems.removeAll(this); + for (int i = 0; i < children.count(); i++) { + if (children.at(i)->type() == GROUPWIDGET) { + QList subchildren = children.at(i)->childItems(); + for (int j = 0; j < subchildren.count(); j++) { + collidingItems.removeAll(subchildren.at(j)); + } + } + collidingItems.removeAll(children.at(i)); + } + } + if (collidingItems.isEmpty()) return newPos; + else { + bool forwardMove = xpos > start.x(); + int offset = 0; + for (int i = 0; i < collidingItems.count(); i++) { + QGraphicsItem *collision = collidingItems.at(i); + if (collision->type() == TRANSITIONWIDGET) { + // 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 { - // get pos just after colliding clip - int npos = static_cast < AbstractClipItem* >(items.at(i))->endPos().frames(m_fps); - newPos.setX(npos); + // 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)); } - return newPos; } } + 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 + collidingItems = scene()->items(transitionGroupShape(newPos - pos()), Qt::IntersectsItemShape); + 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) return pos(); + } } return newPos; } - return QGraphicsItem::itemChange(change, value); + return QGraphicsItemGroup::itemChange(change, value); } +//virtual +void AbstractGroupItem::dropEvent(QGraphicsSceneDragDropEvent * event) +{ + QString effects = QString(event->mimeData()->data("kdenlive/effectslist")); + QDomDocument doc; + doc.setContent(effects, true); + QDomElement e = doc.documentElement(); + CustomTrackView *view = (CustomTrackView *) scene()->views()[0]; + if (view) view->slotAddGroupEffect(e, this); +} + +//virtual +void AbstractGroupItem::dragEnterEvent(QGraphicsSceneDragDropEvent *event) +{ + event->setAccepted(event->mimeData()->hasFormat("kdenlive/effectslist")); +} + +void AbstractGroupItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *event) +{ + Q_UNUSED(event); +} + +// virtual +void AbstractGroupItem::mousePressEvent(QGraphicsSceneMouseEvent * event) +{ + if (event->modifiers() & Qt::ShiftModifier) { + // User want to do a rectangle selection, so ignore the event to pass it to the view + event->ignore(); + } else QGraphicsItem::mousePressEvent(event); +} + +void AbstractGroupItem::resizeStart(int diff) +{ + bool info = false; + if (m_resizeInfos.isEmpty()) + info = true; + int maximum = diff; + QList children = childItems(); + QList items; + int itemcount = 0; + for (int i = 0; i < children.count(); ++i) { + AbstractClipItem *item = static_cast (children.at(i)); + if (item && item->type() == AVWIDGET) { + items << item; + if (info) + m_resizeInfos << item->info(); + item->resizeStart((int)(m_resizeInfos.at(itemcount).startPos.frames(item->fps())) + diff); + int itemdiff = (int)(item->startPos() - m_resizeInfos.at(itemcount).startPos).frames(item->fps()); + if (qAbs(itemdiff) < qAbs(maximum)) + maximum = itemdiff; + ++itemcount; + } + } + + for (int i = 0; i < items.count(); ++i) + items.at(i)->resizeStart((int)(m_resizeInfos.at(i).startPos.frames(items.at(i)->fps())) + maximum); +} + +void AbstractGroupItem::resizeEnd(int diff) +{ + bool info = false; + if (m_resizeInfos.isEmpty()) + info = true; + int maximum = diff; + QList children = childItems(); + QList items; + int itemcount = 0; + for (int i = 0; i < children.count(); ++i) { + AbstractClipItem *item = static_cast (children.at(i)); + if (item && item->type() == AVWIDGET) { + items << item; + if (info) + m_resizeInfos << item->info(); + item->resizeEnd((int)(m_resizeInfos.at(itemcount).endPos.frames(item->fps())) + diff); + int itemdiff = (int)(item->endPos() - m_resizeInfos.at(itemcount).endPos).frames(item->fps()); + if (qAbs(itemdiff) < qAbs(maximum)) + maximum = itemdiff; + ++itemcount; + } + } + + for (int i = 0; i < items.count(); ++i) + items.at(i)->resizeEnd((int)(m_resizeInfos.at(i).endPos.frames(items.at(i)->fps())) + maximum); +} + +QList< ItemInfo > AbstractGroupItem::resizeInfos() +{ + return m_resizeInfos; +} + +void AbstractGroupItem::clearResizeInfos() +{ + // m_resizeInfos.clear() will crash in some cases for unknown reasons - ttill + m_resizeInfos = QList (); +} + +GenTime AbstractGroupItem::duration() +{ + QList children = childItems(); + GenTime start = GenTime(-1.0); + GenTime end = GenTime(); + for (int i = 0; i < children.count(); ++i) { + if (children.at(i)->type() != GROUPWIDGET) { + AbstractClipItem *item = static_cast (children.at(i)); + if (item) { + if (start < GenTime() || item->startPos() < start) + start = item->startPos(); + if (item->endPos() > end) + end = item->endPos(); + } + } else { + children << children.at(i)->childItems(); + } + } + return end - start; +}