timeline: implemented system of moving selections and making them undoable
This commit is contained in:
@@ -29,5 +29,7 @@ set(OLIVE_SOURCES
|
||||
widget/timelinewidget/timelinescaledobject.cpp
|
||||
widget/timelinewidget/timelinewidget.h
|
||||
widget/timelinewidget/timelinewidget.cpp
|
||||
widget/timelinewidget/timelinewidgetselections.h
|
||||
widget/timelinewidget/timelinewidgetselections.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -119,6 +119,7 @@ TimelineWidget::TimelineWidget(QWidget *parent) :
|
||||
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
||||
view->SetSnapService(this);
|
||||
view->SetSelectionList(&selections_);
|
||||
view->SetGhostList(&ghost_items_);
|
||||
|
||||
view_splitter_->addWidget(tview);
|
||||
|
||||
@@ -173,20 +174,17 @@ TimelineWidget::~TimelineWidget()
|
||||
|
||||
void TimelineWidget::Clear()
|
||||
{
|
||||
QList<Block*> deselected_blocks;
|
||||
|
||||
QMap<Block*, TimelineViewBlockItem*>::const_iterator iterator;
|
||||
for (iterator=block_items_.begin(); iterator!=block_items_.end(); iterator++) {
|
||||
if (IsItemSelected(iterator.value())) {
|
||||
deselected_blocks.append(iterator.key());
|
||||
}
|
||||
|
||||
// Delete all items
|
||||
for (auto iterator=block_items_.begin(); iterator!=block_items_.end(); iterator++) {
|
||||
delete iterator.value();
|
||||
}
|
||||
block_items_.clear();
|
||||
|
||||
emit BlocksDeselected(deselected_blocks);
|
||||
// Emit that we've deselected any selected blocks
|
||||
emit BlocksDeselected(selected_blocks_);
|
||||
selected_blocks_.clear();
|
||||
|
||||
// Set null timebase
|
||||
SetTimebase(0);
|
||||
}
|
||||
|
||||
@@ -236,10 +234,6 @@ void TimelineWidget::ScaleChangedEvent(const double &scale)
|
||||
}
|
||||
}
|
||||
|
||||
foreach (TimelineViewGhostItem* ghost, ghost_items_) {
|
||||
ghost->SetScale(scale);
|
||||
}
|
||||
|
||||
foreach (TimelineAndTrackView* view, views_) {
|
||||
view->view()->SetScale(scale);
|
||||
}
|
||||
@@ -375,34 +369,31 @@ rational TimelineWidget::GetToolTipTimebase() const
|
||||
|
||||
void TimelineWidget::SelectAll()
|
||||
{
|
||||
QList<Block*> blocks_selected;
|
||||
QList<Block*> newly_selected_blocks;
|
||||
|
||||
QMap<Block*, TimelineViewBlockItem*>::const_iterator i;
|
||||
|
||||
for (i=block_items_.constBegin(); i!=block_items_.end(); i++) {
|
||||
if (!IsItemSelected(i.value())) {
|
||||
AddSelection(i.value());
|
||||
blocks_selected.append(i.key());
|
||||
for (auto it=block_items_.cbegin(); it!=block_items_.cend(); it++) {
|
||||
if (!selected_blocks_.contains(it.key())) {
|
||||
newly_selected_blocks.append(it.key());
|
||||
AddSelection(it.key()->range(), it.value()->Track());
|
||||
}
|
||||
}
|
||||
|
||||
emit BlocksSelected(blocks_selected);
|
||||
SignalSelectedBlocks(newly_selected_blocks);
|
||||
}
|
||||
|
||||
void TimelineWidget::DeselectAll()
|
||||
{
|
||||
QList<Block*> blocks_deselected;
|
||||
// Clear selections
|
||||
selections_.clear();
|
||||
|
||||
QMap<Block*, TimelineViewBlockItem*>::const_iterator i;
|
||||
// Update all viewports
|
||||
UpdateViewports();
|
||||
|
||||
for (i=block_items_.constBegin(); i!=block_items_.end(); i++) {
|
||||
if (IsItemSelected(i.value())) {
|
||||
RemoveSelection(i.value());
|
||||
blocks_deselected.append(i.key());
|
||||
}
|
||||
}
|
||||
// Emit signal that any previously selected block is no longer selected
|
||||
emit BlocksDeselected(selected_blocks_);
|
||||
|
||||
emit BlocksDeselected(blocks_deselected);
|
||||
// Empty selected blocks list
|
||||
selected_blocks_.clear();
|
||||
}
|
||||
|
||||
void TimelineWidget::RippleToIn()
|
||||
@@ -544,6 +535,9 @@ void TimelineWidget::DeleteSelected(bool ripple)
|
||||
// Replace clips with gaps (effectively deleting them)
|
||||
ReplaceBlocksWithGaps(clips_to_delete, true, command);
|
||||
|
||||
// Remove all selections
|
||||
new TimelineSetSelectionsCommand(this, TimelineWidgetSelections(), GetSelections(), command);
|
||||
|
||||
// Insert ripple command now that it's all cleaned up gaps
|
||||
if (ripple) {
|
||||
TimeRangeList range_list;
|
||||
@@ -780,16 +774,8 @@ QList<TimelineViewBlockItem *> TimelineWidget::GetSelectedBlocks()
|
||||
{
|
||||
QList<TimelineViewBlockItem *> list;
|
||||
|
||||
QMapIterator<Block*, TimelineViewBlockItem*> iterator(block_items_);
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
iterator.next();
|
||||
|
||||
TimelineViewBlockItem* item = iterator.value();
|
||||
|
||||
if (item && IsItemSelected(item)) {
|
||||
list.append(item);
|
||||
}
|
||||
foreach (Block* b, selected_blocks_) {
|
||||
list.append(block_items_.value(b));
|
||||
}
|
||||
|
||||
return list;
|
||||
@@ -950,20 +936,30 @@ void TimelineWidget::RemoveBlock(const QList<Block *> &blocks)
|
||||
QList<Block*> deselect_blocks;
|
||||
|
||||
foreach (Block* b, blocks) {
|
||||
// Disconnect all signals
|
||||
disconnect(b, &Block::Refreshed, this, &TimelineWidget::BlockRefreshed);
|
||||
disconnect(b, &Block::LinksChanged, this, &TimelineWidget::BlockUpdated);
|
||||
disconnect(b, &Block::LabelChanged, this, &TimelineWidget::BlockUpdated);
|
||||
disconnect(b, &Block::EnabledChanged, this, &TimelineWidget::BlockUpdated);
|
||||
|
||||
// Take item from map
|
||||
TimelineViewBlockItem* item = block_items_.take(b);
|
||||
delete_items.append(item);
|
||||
|
||||
if (IsItemSelected(item)) {
|
||||
// If selected, deselect it
|
||||
int select_index = selected_blocks_.indexOf(b);
|
||||
if (select_index > -1) {
|
||||
selected_blocks_.removeAt(select_index);
|
||||
deselect_blocks.append(b);
|
||||
RemoveSelection(item);
|
||||
}
|
||||
|
||||
// Finally, delete item
|
||||
delete item;
|
||||
}
|
||||
|
||||
if (!deselect_blocks.isEmpty()) {
|
||||
// We already removed the blocks from selected_blocks_, so we can signal directly rather than
|
||||
// through
|
||||
emit BlocksDeselected(deselect_blocks);
|
||||
}
|
||||
|
||||
@@ -1171,9 +1167,9 @@ void TimelineWidget::ViewTimestampChanged(int64_t ts)
|
||||
|
||||
void TimelineWidget::AddGhost(TimelineViewGhostItem *ghost)
|
||||
{
|
||||
ghost->SetScale(GetScale());
|
||||
ghost_items_.append(ghost);
|
||||
views_.at(ghost->Track().type())->view()->scene()->addItem(ghost);
|
||||
|
||||
UpdateViewports(ghost->GetTrack().type());
|
||||
}
|
||||
|
||||
void TimelineWidget::UpdateViewTimebases()
|
||||
@@ -1231,6 +1227,22 @@ const QRect& TimelineWidget::GetRubberBandGeometry() const
|
||||
return rubberband_.geometry();
|
||||
}
|
||||
|
||||
void TimelineWidget::SignalSelectedBlocks(const QList<Block *> &selected_blocks)
|
||||
{
|
||||
selected_blocks_.append(selected_blocks);
|
||||
|
||||
emit BlocksSelected(selected_blocks);
|
||||
}
|
||||
|
||||
void TimelineWidget::SignalDeselectedBlocks(const QList<Block *> &deselected_blocks)
|
||||
{
|
||||
foreach (Block* b, deselected_blocks) {
|
||||
selected_blocks_.removeOne(b);
|
||||
}
|
||||
|
||||
emit BlocksDeselected(deselected_blocks);
|
||||
}
|
||||
|
||||
QVector<Timeline::EditToInfo> TimelineWidget::GetEditToInfo(const rational& playhead_time,
|
||||
Timeline::MovementMode mode)
|
||||
{
|
||||
@@ -1372,6 +1384,17 @@ void TimelineWidget::ShowSnap(const QList<rational> ×)
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::UpdateViewports(const Timeline::TrackType &type)
|
||||
{
|
||||
if (type == Timeline::kTrackTypeNone) {
|
||||
foreach (TimelineAndTrackView* tview, views_) {
|
||||
tview->view()->viewport()->update();
|
||||
}
|
||||
} else {
|
||||
views_.at(type)->view()->viewport()->update();
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::HideSnaps()
|
||||
{
|
||||
foreach (TimelineAndTrackView* tview, views_) {
|
||||
@@ -1396,10 +1419,8 @@ void TimelineWidget::StartRubberBandSelect(bool enable_selecting, bool select_li
|
||||
|
||||
// We don't touch any blocks that are already selected. If you want these to be deselected by
|
||||
// default, call DeselectAll() befoer calling StartRubberBandSelect()
|
||||
foreach (TimelineViewBlockItem* block, block_items_) {
|
||||
if (IsItemSelected(block)) {
|
||||
rubberband_already_selected_.append(block);
|
||||
}
|
||||
foreach (Block* b, selected_blocks_) {
|
||||
rubberband_already_selected_.append(block_items_.value(b));
|
||||
}
|
||||
|
||||
MoveRubberBandSelect(enable_selecting, select_links);
|
||||
@@ -1507,7 +1528,7 @@ void TimelineWidget::AddSelection(const TimeRange &time, const TrackReference &t
|
||||
{
|
||||
selections_[track].InsertTimeRange(time);
|
||||
|
||||
views_.at(track.type())->view()->viewport()->update();
|
||||
UpdateViewports(track.type());
|
||||
}
|
||||
|
||||
void TimelineWidget::AddSelection(TimelineViewBlockItem *item)
|
||||
@@ -1519,7 +1540,7 @@ void TimelineWidget::RemoveSelection(const TimeRange &time, const TrackReference
|
||||
{
|
||||
selections_[track].RemoveTimeRange(time);
|
||||
|
||||
views_.at(track.type())->view()->viewport()->update();
|
||||
UpdateViewports(track.type());
|
||||
}
|
||||
|
||||
void TimelineWidget::RemoveSelection(TimelineViewBlockItem *item)
|
||||
@@ -1527,22 +1548,11 @@ void TimelineWidget::RemoveSelection(TimelineViewBlockItem *item)
|
||||
RemoveSelection(item->block()->range(), item->Track());
|
||||
}
|
||||
|
||||
void TimelineWidget::ShiftSelections(const rational &diff)
|
||||
{
|
||||
for (auto it=selections_.begin(); it!=selections_.end(); it++) {
|
||||
for (auto it2=it.value().begin(); it2!=it.value().end(); it2++) {
|
||||
(*it2) += diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::SetSelections(const TimelineWidget::Selections &s)
|
||||
void TimelineWidget::SetSelections(const TimelineWidgetSelections &s)
|
||||
{
|
||||
selections_ = s;
|
||||
|
||||
foreach (TimelineAndTrackView* tview, views_) {
|
||||
tview->view()->viewport(),update();
|
||||
}
|
||||
UpdateViewports();
|
||||
}
|
||||
|
||||
TimelineViewBlockItem *TimelineWidget::GetItemAtScenePos(const TimelineCoordinate& coord)
|
||||
@@ -1561,11 +1571,6 @@ TimelineViewBlockItem *TimelineWidget::GetItemAtScenePos(const TimelineCoordinat
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool TimelineWidget::IsItemSelected(TimelineViewBlockItem *item) const
|
||||
{
|
||||
return selections_[item->Track()].ContainsTimeRange(item->block()->range());
|
||||
}
|
||||
|
||||
struct SnapData {
|
||||
rational time;
|
||||
rational movement;
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "widget/nodecopypaste/nodecopypaste.h"
|
||||
#include "widget/slider/timeslider.h"
|
||||
#include "widget/timebased/timebased.h"
|
||||
#include "widget/timelinewidget/timelinewidgetselections.h"
|
||||
#include "widget/timelinewidget/tool/import.h"
|
||||
#include "widget/timelinewidget/tool/tool.h"
|
||||
|
||||
@@ -113,22 +114,18 @@ public:
|
||||
return block_items_;
|
||||
}
|
||||
|
||||
using Selections = QHash<TrackReference, TimeRangeList>;
|
||||
|
||||
void AddSelection(const TimeRange& time, const TrackReference& track);
|
||||
void AddSelection(TimelineViewBlockItem* item);
|
||||
|
||||
void RemoveSelection(const TimeRange& time, const TrackReference& track);
|
||||
void RemoveSelection(TimelineViewBlockItem* item);
|
||||
|
||||
void ShiftSelections(const rational& diff);
|
||||
|
||||
const Selections& GetSelections() const
|
||||
const TimelineWidgetSelections& GetSelections() const
|
||||
{
|
||||
return selections_;
|
||||
}
|
||||
|
||||
void SetSelections(const Selections& s);
|
||||
void SetSelections(const TimelineWidgetSelections &s);
|
||||
|
||||
TrackOutput* GetTrackFromReference(const TrackReference& ref);
|
||||
|
||||
@@ -159,7 +156,10 @@ public:
|
||||
|
||||
rational GetToolTipTimebase() const;
|
||||
|
||||
bool IsItemSelected(TimelineViewBlockItem* item) const;
|
||||
bool IsBlockSelected(Block* b) const
|
||||
{
|
||||
return selected_blocks_.contains(b);
|
||||
}
|
||||
|
||||
void SetBlockLinksSelected(Block *block, bool selected);
|
||||
|
||||
@@ -169,6 +169,10 @@ public:
|
||||
|
||||
const QRect &GetRubberBandGeometry() const;
|
||||
|
||||
void SignalSelectedBlocks(const QList<Block*>& selected_blocks);
|
||||
|
||||
void SignalDeselectedBlocks(const QList<Block*>& deselected_blocks);
|
||||
|
||||
signals:
|
||||
void BlocksSelected(const QList<Block*>& selected_blocks);
|
||||
|
||||
@@ -203,13 +207,15 @@ private:
|
||||
|
||||
void ShowSnap(const QList<rational>& times);
|
||||
|
||||
void UpdateViewports(const Timeline::TrackType& type = Timeline::kTrackTypeNone);
|
||||
|
||||
QPoint drag_origin_;
|
||||
|
||||
QRubberBand rubberband_;
|
||||
QList<QGraphicsItem*> rubberband_already_selected_;
|
||||
QList<QGraphicsItem*> rubberband_now_selected_;
|
||||
|
||||
Selections selections_;
|
||||
TimelineWidgetSelections selections_;
|
||||
|
||||
TimelineTool* GetActiveTool();
|
||||
|
||||
@@ -227,6 +233,8 @@ private:
|
||||
|
||||
TimeSlider* timecode_label_;
|
||||
|
||||
QList<Block*> selected_blocks_;
|
||||
|
||||
int deferred_scroll_value_;
|
||||
|
||||
bool use_audio_time_units_;
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/***
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "timelinewidgetselections.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
void TimelineWidgetSelections::ShiftTime(const rational &diff)
|
||||
{
|
||||
for (auto it=this->begin(); it!=this->end(); it++) {
|
||||
for (auto it2=it.value().begin(); it2!=it.value().end(); it2++) {
|
||||
(*it2) += diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidgetSelections::ShiftTracks(Timeline::TrackType type, int diff)
|
||||
{
|
||||
TimelineWidgetSelections cached_selections;
|
||||
|
||||
{
|
||||
// Take all selections of this track type
|
||||
auto it = this->begin();
|
||||
while (it != this->end()) {
|
||||
if (it.key().type() == type) {
|
||||
cached_selections.insert(it.key(), it.value());
|
||||
it = this->erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then re-insert them with the diff applied
|
||||
for (auto it=cached_selections.cbegin(); it!=cached_selections.cend(); it++) {
|
||||
TrackReference ref(it.key().type(), it.key().index() + diff);
|
||||
|
||||
this->insert(ref, it.value());
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -0,0 +1,44 @@
|
||||
/***
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef TIMELINEWIDGETSELECTIONS_H
|
||||
#define TIMELINEWIDGETSELECTIONS_H
|
||||
|
||||
#include <QHash>
|
||||
|
||||
#include "common/timerange.h"
|
||||
#include "timeline/trackreference.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class TimelineWidgetSelections : public QHash<TrackReference, TimeRangeList>
|
||||
{
|
||||
public:
|
||||
TimelineWidgetSelections() = default;
|
||||
|
||||
void ShiftTime(const rational& diff);
|
||||
|
||||
void ShiftTracks(Timeline::TrackType type, int diff);
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // TIMELINEWIDGETSELECTIONS_H
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "node/block/transition/transition.h"
|
||||
#include "node/graph.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
#include "widget/timelinewidget/timelinewidget.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -1696,4 +1697,22 @@ void TransitionRemoveCommand::undo_internal()
|
||||
track_->InvalidateCache(TimeRange(block_->in(), block_->out()), track_->block_input(), track_->block_input());
|
||||
}
|
||||
|
||||
TimelineSetSelectionsCommand::TimelineSetSelectionsCommand(TimelineWidget *timeline, const TimelineWidgetSelections &now, const TimelineWidgetSelections &old, QUndoCommand *parent) :
|
||||
QUndoCommand(parent),
|
||||
timeline_(timeline),
|
||||
old_(old),
|
||||
now_(now)
|
||||
{
|
||||
}
|
||||
|
||||
void TimelineSetSelectionsCommand::redo()
|
||||
{
|
||||
timeline_->SetSelections(now_);
|
||||
}
|
||||
|
||||
void TimelineSetSelectionsCommand::undo()
|
||||
{
|
||||
timeline_->SetSelections(old_);
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "node/output/track/tracklist.h"
|
||||
#include "timeline/timelinepoints.h"
|
||||
#include "undo/undocommand.h"
|
||||
#include "widget/timelinewidget/timelinewidgetselections.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -613,6 +614,23 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class TimelineWidget;
|
||||
|
||||
class TimelineSetSelectionsCommand : public QUndoCommand {
|
||||
public:
|
||||
TimelineSetSelectionsCommand(TimelineWidget* timeline, const TimelineWidgetSelections& now, const TimelineWidgetSelections& old, QUndoCommand* parent = nullptr);
|
||||
|
||||
protected:
|
||||
virtual void redo() override;
|
||||
virtual void undo() override;
|
||||
|
||||
private:
|
||||
TimelineWidget* timeline_;
|
||||
TimelineWidgetSelections old_;
|
||||
TimelineWidgetSelections now_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // TIMELINEUNDOABLE_H
|
||||
|
||||
Reference in New Issue
Block a user