diff --git a/app/common/rational.cpp b/app/common/rational.cpp index 28f88d2ba..31cd56d4f 100644 --- a/app/common/rational.cpp +++ b/app/common/rational.cpp @@ -84,6 +84,11 @@ rational rational::flipped() const return rational(denom, numer); } +bool rational::isNull() const +{ + return denominator() == 0; +} + //Function: get active instances int rational::getActiveInstances() diff --git a/app/common/rational.h b/app/common/rational.h index 22298b108..951cc7305 100644 --- a/app/common/rational.h +++ b/app/common/rational.h @@ -96,6 +96,9 @@ public: // Produce "flipped" version rational flipped() const; + + // Returns whether the rational is null or not + bool isNull() const; //Function: print number to cout void print(ostream &out = cout) const; diff --git a/app/node/output/timeline/timeline.cpp b/app/node/output/timeline/timeline.cpp index 358deb8e2..6a1140721 100644 --- a/app/node/output/timeline/timeline.cpp +++ b/app/node/output/timeline/timeline.cpp @@ -63,6 +63,7 @@ void TimelineOutput::AttachTimeline(TimelinePanel *timeline) //disconnect(view, SIGNAL(RequestInsertBlockAtIndex(Block*, int)), this, SLOT(InsertBlockAtIndex(Block*, int))); disconnect(view, SIGNAL(RequestPlaceBlock(Block*, rational, int)), this, SLOT(PlaceBlock(Block*, rational, int))); + disconnect(view, SIGNAL(RequestReplaceBlock(Block*, Block*, int)), this, SLOT(ReplaceBlock(Block*, Block*, int))); // Remove existing UI objects from TimelinePanel attached_timeline_->Clear(); @@ -84,6 +85,7 @@ void TimelineOutput::AttachTimeline(TimelinePanel *timeline) //connect(view, SIGNAL(RequestInsertBlockAtIndex(Block*, int)), this, SLOT(InsertBlockAtIndex(Block*, int))); connect(view, SIGNAL(RequestPlaceBlock(Block*, rational, int)), this, SLOT(PlaceBlock(Block*, rational, int))); + connect(view, SIGNAL(RequestReplaceBlock(Block*, Block*, int)), this, SLOT(ReplaceBlock(Block*, Block*, int))); } } @@ -240,9 +242,16 @@ void TimelineOutput::TrackEdgeRemoved(NodeEdgePtr edge) void TimelineOutput::PlaceBlock(Block *block, rational start, int track) { + Q_ASSERT(track >= 0); + while (track >= track_cache_.size()) { AddTrack(); } track_cache_.at(track)->PlaceBlock(block, start); } + +void TimelineOutput::ReplaceBlock(Block *old, Block *replace, int track) +{ + track_cache_.at(track)->ReplaceBlock(old, replace); +} diff --git a/app/node/output/timeline/timeline.h b/app/node/output/timeline/timeline.h index b6fca7db1..0b8c76d41 100644 --- a/app/node/output/timeline/timeline.h +++ b/app/node/output/timeline/timeline.h @@ -101,9 +101,17 @@ private slots: /** * @brief Forwards a PlaceBlock signal to the requested track + * + * If the track index doesn't exist, tracks are automatically created until a track at that index does exist + * (provided the track index is positive). A negative track index fails immediately. */ void PlaceBlock(Block* block, rational start, int track); + /** + * @brief Forwards a ReplaceBlock signal to the appropriate track + */ + void ReplaceBlock(Block* old, Block* replace, int track); + }; #endif // TIMELINEOUTPUT_H diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index 48eb0864f..4b426c002 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -245,12 +245,12 @@ void TrackOutput::AddBlockToGraph(Block *block) void TrackOutput::PlaceBlock(Block *block, rational start) { - AddBlockToGraph(block); - - if (block->in() == start) { + if (block_cache_.contains(block) && block->in() == start) { return; } + AddBlockToGraph(block); + // Check if the placement location is past the end of the timeline if (start >= in()) { if (start > in()) { @@ -408,3 +408,22 @@ void TrackOutput::RippleRemoveArea(rational in, rational out, Block *insert) } } } + +void TrackOutput::ReplaceBlock(Block *old, Block *replace) +{ + Block* previous = old->previous(); + Block* next = old->next(); + + AddBlockToGraph(replace); + + // Disconnect old block from its surroundings + if (previous != nullptr) { + Block::DisconnectBlocks(previous, old); + Block::ConnectBlocks(previous, replace); + } + + if (next != nullptr) { + Block::DisconnectBlocks(old, next); + Block::ConnectBlocks(replace, next); + } +} diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 5abedbb92..48b77bfee 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -125,6 +125,13 @@ public: */ void RippleRemoveArea(rational in, rational out, Block* insert = nullptr); + /** + * @brief Replaces Block `old` with Block `replace` + * + * Makes no modification to the lengths of either Blocks + */ + void ReplaceBlock(Block* old, Block* replace); + signals: /** * @brief Signal emitted when a Block is added to this Track diff --git a/app/widget/timelineview/timelineview.cpp b/app/widget/timelineview/timelineview.cpp index 3decc7b1f..455b8d44f 100644 --- a/app/widget/timelineview/timelineview.cpp +++ b/app/widget/timelineview/timelineview.cpp @@ -43,9 +43,8 @@ TimelineView::TimelineView(QWidget *parent) : connect(&scene_, SIGNAL(changed(const QList&)), this, SLOT(UpdateSceneRect())); - // Create playhead line and ensure it's always on top + // Create playhead line playhead_line_ = new TimelineViewPlayheadItem(); - playhead_line_->setZValue(100); scene_.addItem(playhead_line_); @@ -267,6 +266,7 @@ void TimelineView::UpdateSceneRect() bounding_rect.setTopLeft(QPointF(0, 0)); // Ensure the scene height is always AT LEAST the height of the view + // The scrollbar appears to have a 1px margin on the top and bottom, hence the -2 int minimum_height = height() - horizontalScrollBar()->height() - 2; if (bounding_rect.height() < minimum_height) { bounding_rect.setHeight(minimum_height); @@ -283,39 +283,3 @@ void TimelineView::UpdateSceneRect() scene_.setSceneRect(bounding_rect); } -TimelineView::Tool::Tool(TimelineView *parent) : - dragging_(false), - parent_(parent) -{ -} - -TimelineView::Tool::~Tool(){} - -void TimelineView::Tool::MousePress(QMouseEvent *){} - -void TimelineView::Tool::MouseMove(QMouseEvent *){} - -void TimelineView::Tool::MouseRelease(QMouseEvent *){} - -void TimelineView::Tool::DragEnter(QDragEnterEvent *){} - -void TimelineView::Tool::DragMove(QDragMoveEvent *){} - -void TimelineView::Tool::DragLeave(QDragLeaveEvent *){} - -void TimelineView::Tool::DragDrop(QDropEvent *){} - -TimelineView *TimelineView::Tool::parent() -{ - return parent_; -} - -QPointF TimelineView::Tool::GetScenePos(const QPoint &screen_pos) -{ - return parent()->mapToScene(screen_pos); -} - -QGraphicsItem *TimelineView::Tool::GetItemAtScenePos(const QPointF &scene_pos) -{ - return parent()->scene_.itemAt(scene_pos, parent()->transform()); -} diff --git a/app/widget/timelineview/timelineview.h b/app/widget/timelineview/timelineview.h index a1ccf5d7d..5f4d24319 100644 --- a/app/widget/timelineview/timelineview.h +++ b/app/widget/timelineview/timelineview.h @@ -60,6 +60,7 @@ public slots: signals: void RequestPlaceBlock(Block* block, rational start, int track); + void RequestReplaceBlock(Block* old, Block* replace, int track); protected: virtual void mousePressEvent(QMouseEvent *event) override; @@ -97,6 +98,8 @@ private: QGraphicsItem* GetItemAtScenePos(const QPointF& scene_pos); + rational ValidateMovement(rational movement, const QVector ghosts); + bool dragging_; QPointF drag_start_; diff --git a/app/widget/timelineview/timelineviewplayheaditem.cpp b/app/widget/timelineview/timelineviewplayheaditem.cpp index ec4c81b89..d48774d0b 100644 --- a/app/widget/timelineview/timelineviewplayheaditem.cpp +++ b/app/widget/timelineview/timelineviewplayheaditem.cpp @@ -29,7 +29,8 @@ TimelineViewPlayheadItem::TimelineViewPlayheadItem(QGraphicsItem *parent) : TimelineViewRect(parent), playhead_(0) { - + // Ensure this item is always on top + setZValue(100); } void TimelineViewPlayheadItem::SetPlayhead(const int64_t &playhead) diff --git a/app/widget/timelineview/tool/CMakeLists.txt b/app/widget/timelineview/tool/CMakeLists.txt index 9eadd9514..9c974d975 100644 --- a/app/widget/timelineview/tool/CMakeLists.txt +++ b/app/widget/timelineview/tool/CMakeLists.txt @@ -18,5 +18,6 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} widget/timelineview/tool/import.cpp widget/timelineview/tool/pointer.cpp + widget/timelineview/tool/tool.cpp PARENT_SCOPE ) diff --git a/app/widget/timelineview/tool/import.cpp b/app/widget/timelineview/tool/import.cpp index b4929b861..3b5a3b31d 100644 --- a/app/widget/timelineview/tool/import.cpp +++ b/app/widget/timelineview/tool/import.cpp @@ -51,7 +51,8 @@ void TimelineView::ImportTool::DragEnter(QDragEnterEvent *event) drag_start_ = GetScenePos(event->pos()); // Set ghosts to start where the cursor entered - rational ghost_start = parent()->SceneToTime(drag_start_.x()); + // FIXME: 100 = magic number so that imported clips are not right on the cursor when dragged in + rational ghost_start = parent()->SceneToTime(drag_start_.x() - 100); while (!stream.atEnd()) { stream >> r >> item_ptr; @@ -92,22 +93,24 @@ void TimelineView::ImportTool::DragEnter(QDragEnterEvent *event) void TimelineView::ImportTool::DragMove(QDragMoveEvent *event) { if (parent()->HasGhosts()) { + QPointF pos = GetScenePos(event->pos()); + QPointF movement = pos - drag_start_; + + rational time_movement = parent()->SceneToTime(movement.x()); + + int ghost_track = parent()->SceneToTrack(pos.y()); + int ghost_y = parent()->GetTrackY(ghost_track); + int ghost_height = parent()->GetTrackHeight(ghost_track); + + time_movement = ValidateMovement(time_movement, parent()->ghost_items_); + // Move ghosts to the mouse cursor foreach (TimelineViewGhostItem* ghost, parent()->ghost_items_) { - QPointF pos = GetScenePos(event->pos()); - QPointF movement = pos - drag_start_; - - rational time_movement = parent()->SceneToTime(movement.x()); - ghost->SetInAdjustment(time_movement); ghost->SetOutAdjustment(time_movement); - int ghost_track = parent()->SceneToTrack(pos.y()); ghost->SetTrack(ghost_track); - int ghost_y = parent()->GetTrackY(ghost_track); - int ghost_height = parent()->GetTrackHeight(ghost_track); - ghost->SetY(ghost_y); ghost->SetHeight(ghost_height); } diff --git a/app/widget/timelineview/tool/pointer.cpp b/app/widget/timelineview/tool/pointer.cpp index 04ca82013..42c80c4ba 100644 --- a/app/widget/timelineview/tool/pointer.cpp +++ b/app/widget/timelineview/tool/pointer.cpp @@ -20,6 +20,8 @@ #include "widget/timelineview/timelineview.h" +#include "node/block/gap/gap.h" + TimelineView::PointerTool::PointerTool(TimelineView *parent) : Tool(parent) { @@ -71,6 +73,9 @@ void TimelineView::PointerTool::MouseMove(QMouseEvent *event) ghost->SetScale(parent()->scale_); ghost->SetData(Node::PtrToValue(clip)); + // FIXME: Very bad. Change immediately. + ghost->SetTrack(parent()->SceneToTrack(drag_start_.y())); + ghost->setPos(clip_item->pos()); parent()->ghost_items_.append(ghost); @@ -89,12 +94,7 @@ void TimelineView::PointerTool::MouseMove(QMouseEvent *event) rational time_movement = parent()->SceneToTime(movement.x()); // Validate movement - foreach (TimelineViewGhostItem* ghost, parent()->ghost_items_) { - rational validator = ghost->In() + time_movement; - if (validator < 0) { - time_movement = rational(0) - ghost->In(); - } - } + time_movement = ValidateMovement(time_movement, parent()->ghost_items_); // Perform movement foreach (TimelineViewGhostItem* ghost, parent()->ghost_items_) { @@ -110,13 +110,24 @@ void TimelineView::PointerTool::MouseRelease(QMouseEvent *event) parent()->QGraphicsView::mouseReleaseEvent(event); - /* + QObject block_memory_manager; + foreach (TimelineViewGhostItem* ghost, parent()->ghost_items_) { Block* b = Node::ValueToPtr(ghost->data()); - emit parent()->RequestPlaceBlock(b, ghost->In()); + // Replace old Block with a new Gap + GapBlock* gap = new GapBlock(); + gap->setParent(&block_memory_manager); + gap->set_length(b->length()); + + emit parent()->RequestReplaceBlock(b, gap, ghost->Track()); + } + + foreach (TimelineViewGhostItem* ghost, parent()->ghost_items_) { + Block* b = Node::ValueToPtr(ghost->data()); + + emit parent()->RequestPlaceBlock(b, ghost->GetAdjustedIn(), ghost->Track()); } - */ parent()->ClearGhosts(); diff --git a/app/widget/timelineview/tool/tool.cpp b/app/widget/timelineview/tool/tool.cpp new file mode 100644 index 000000000..f90c445b6 --- /dev/null +++ b/app/widget/timelineview/tool/tool.cpp @@ -0,0 +1,70 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "widget/timelineview/timelineview.h" + +TimelineView::Tool::Tool(TimelineView *parent) : + dragging_(false), + parent_(parent) +{ +} + +TimelineView::Tool::~Tool(){} + +void TimelineView::Tool::MousePress(QMouseEvent *){} + +void TimelineView::Tool::MouseMove(QMouseEvent *){} + +void TimelineView::Tool::MouseRelease(QMouseEvent *){} + +void TimelineView::Tool::DragEnter(QDragEnterEvent *){} + +void TimelineView::Tool::DragMove(QDragMoveEvent *){} + +void TimelineView::Tool::DragLeave(QDragLeaveEvent *){} + +void TimelineView::Tool::DragDrop(QDropEvent *){} + +TimelineView *TimelineView::Tool::parent() +{ + return parent_; +} + +QPointF TimelineView::Tool::GetScenePos(const QPoint &screen_pos) +{ + return parent()->mapToScene(screen_pos); +} + +QGraphicsItem *TimelineView::Tool::GetItemAtScenePos(const QPointF &scene_pos) +{ + return parent()->scene_.itemAt(scene_pos, parent()->transform()); +} + +rational TimelineView::Tool::ValidateMovement(rational movement, const QVector ghosts) +{ + foreach (TimelineViewGhostItem* ghost, ghosts) { + rational validator = ghost->In() + movement; + if (validator < 0) { + movement = rational(0) - ghost->In(); + } + } + + return movement; +} diff --git a/app/widget/timeruler/timeruler.cpp b/app/widget/timeruler/timeruler.cpp index 36f3a8e63..b2b5a359e 100644 --- a/app/widget/timeruler/timeruler.cpp +++ b/app/widget/timeruler/timeruler.cpp @@ -109,7 +109,7 @@ void TimeRuler::SetScroll(int s) void TimeRuler::paintEvent(QPaintEvent *) { // Nothing to paint if the timebase is invalid - if (timebase_.denominator() == 0) { + if (timebase_.isNull()) { return; }