diff --git a/.gitignore b/.gitignore
index c1b62a8a7..6df975821 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1 @@
-*.pro.user
-*.pro.user.*
+*.pro.user*
diff --git a/mainwindow.cpp b/mainwindow.cpp
index dfe428859..87530ebf2 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -11,6 +11,8 @@
#include "panels/viewer.h"
#include "panels/timeline.h"
+#include "project/undo.h"
+
#include "dialogs/aboutdialog.h"
#include "dialogs/newsequencedialog.h"
#include "dialogs/exportdialog.h"
@@ -91,6 +93,7 @@ MainWindow::MainWindow(QWidget *parent) :
connect(ui->menuWindow, SIGNAL(aboutToShow()), this, SLOT(windowMenu_About_To_Be_Shown()));
connect(ui->menu_View, SIGNAL(aboutToShow()), this, SLOT(viewMenu_About_To_Be_Shown()));
connect(ui->menu_Tools, SIGNAL(aboutToShow()), this, SLOT(toolMenu_About_To_Be_Shown()));
+ connect(ui->menuEdit, SIGNAL(aboutToShow()), this, SLOT(editMenu_About_To_Be_Shown()));
QString data_dir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
if (!data_dir.isEmpty()) {
@@ -148,7 +151,7 @@ void MainWindow::on_actionDelete_triggered()
if (panel_timeline->focused()) {
panel_timeline->delete_selection(false);
} else if (panel_effect_controls->is_focused()) {
- panel_effect_controls->delete_clips();
+ panel_effect_controls->delete_effects();
} else if (panel_project->is_focused()) {
panel_project->delete_selected_media();
}
@@ -223,18 +226,21 @@ void MainWindow::on_actionRipple_Delete_triggered()
panel_timeline->delete_selection(true);
}
+void MainWindow::editMenu_About_To_Be_Shown() {
+ ui->action_Undo->setEnabled(undo_stack.canUndo());
+ ui->action_Redo->setEnabled(undo_stack.canRedo());
+}
+
void MainWindow::on_action_Undo_triggered()
{
- if (panel_timeline->focused()) {
- panel_timeline->undo();
- }
+ undo_stack.undo();
+ panel_timeline->redraw_all_clips(true);
}
void MainWindow::on_action_Redo_triggered()
{
- if (panel_timeline->focused()) {
- panel_timeline->redo();
- }
+ undo_stack.redo();
+ panel_timeline->redraw_all_clips(true);
}
void MainWindow::on_actionSplit_at_Playhead_triggered()
diff --git a/mainwindow.h b/mainwindow.h
index 6fd473777..55d8ba275 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -144,6 +144,8 @@ private slots:
void on_actionFolder_triggered();
+ void editMenu_About_To_Be_Shown();
+
private:
Ui::MainWindow *ui;
void setup_layout();
diff --git a/olive.pro.user b/olive.pro.user
deleted file mode 100644
index 1e86905fd..000000000
--- a/olive.pro.user
+++ /dev/null
@@ -1,336 +0,0 @@
-
-
-
-
-
- EnvironmentId
- {be57d5b1-e5ae-4026-9f48-bb6e01f50d4e}
-
-
- ProjectExplorer.Project.ActiveTarget
- 0
-
-
- ProjectExplorer.Project.EditorSettings
-
- true
- false
- true
-
- Cpp
-
- CppGlobal
-
-
-
- QmlJS
-
- QmlJSGlobal
-
-
- 2
- UTF-8
- false
- 4
- false
- 80
- true
- true
- 1
- true
- false
- 2
- true
- true
- 1
- 8
- true
- 1
- true
- true
- true
- false
-
-
-
- ProjectExplorer.Project.PluginSettings
-
-
-
- ProjectExplorer.Project.Target.0
-
- Desktop Qt 5.5.1 GCC 64bit
- Desktop Qt 5.5.1 GCC 64bit
- qt.55.gcc_64_kit
- 0
- 0
- 0
-
- /home/matt/Documents/build-olive-Desktop_Qt_5_5_1_GCC_64bit-Debug
-
-
- true
- qmake
-
- QtProjectManager.QMakeBuildStep
- true
-
- false
- false
- false
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- -w
- -r
-
- false
-
-
-
- 2
- Build
-
- ProjectExplorer.BuildSteps.Build
-
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- -w
- -r
-
- true
- clean
-
-
- 1
- Clean
-
- ProjectExplorer.BuildSteps.Clean
-
- 2
- false
-
- Debug
- Debug
- Qt4ProjectManager.Qt4BuildConfiguration
- 2
- true
-
-
- /home/matt/build-olive-Desktop_Qt_5_5_1_GCC_64bit-Release
-
-
- true
- qmake
-
- QtProjectManager.QMakeBuildStep
- false
-
- false
- false
- false
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- -w
- -r
-
- false
-
-
-
- 2
- Build
-
- ProjectExplorer.BuildSteps.Build
-
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- -w
- -r
-
- true
- clean
-
-
- 1
- Clean
-
- ProjectExplorer.BuildSteps.Clean
-
- 2
- false
-
- Release
- Release
- Qt4ProjectManager.Qt4BuildConfiguration
- 0
- true
-
-
- /home/matt/build-olive-Desktop_Qt_5_5_1_GCC_64bit-Profile
-
-
- true
- qmake
-
- QtProjectManager.QMakeBuildStep
- true
-
- false
- true
- false
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- -w
- -r
-
- false
-
-
-
- 2
- Build
-
- ProjectExplorer.BuildSteps.Build
-
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- -w
- -r
-
- true
- clean
-
-
- 1
- Clean
-
- ProjectExplorer.BuildSteps.Clean
-
- 2
- false
-
- Profile
- Profile
- Qt4ProjectManager.Qt4BuildConfiguration
- 0
- true
-
- 3
-
-
- 0
- Deploy
-
- ProjectExplorer.BuildSteps.Deploy
-
- 1
- Deploy Configuration
-
- ProjectExplorer.DefaultDeployConfiguration
-
- 1
-
-
- false
- false
- 1000
-
- true
-
- false
- false
- false
- false
- true
- 0.01
- 10
- true
- 1
- 25
-
- 1
- true
- false
- true
- valgrind
-
- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
-
- 2
-
- olive
-
- Qt4ProjectManager.Qt4RunConfiguration:/home/matt/olive/olive.pro
- true
-
- olive.pro
- false
-
- /home/matt/Documents/build-olive-Desktop_Qt_5_5_1_GCC_64bit-Debug
- 3768
- false
- true
- false
- false
- true
-
- 1
-
-
-
- ProjectExplorer.Project.TargetCount
- 1
-
-
- ProjectExplorer.Project.Updater.FileVersion
- 18
-
-
- Version
- 18
-
-
diff --git a/olive.pro.user.3af0661 b/olive.pro.user.3af0661
deleted file mode 100644
index 24db8b91f..000000000
--- a/olive.pro.user.3af0661
+++ /dev/null
@@ -1,315 +0,0 @@
-
-
-
-
-
- EnvironmentId
- {3af06612-75ce-48d7-b444-7dc372f1566d}
-
-
- ProjectExplorer.Project.ActiveTarget
- 0
-
-
- ProjectExplorer.Project.EditorSettings
-
- true
- false
- true
-
- Cpp
-
- CppGlobal
-
-
-
- QmlJS
-
- QmlJSGlobal
-
-
- 2
- UTF-8
- false
- 4
- false
- 80
- true
- true
- 1
- true
- false
- 0
- true
- true
- 0
- 8
- true
- 1
- true
- true
- true
- false
-
-
-
- ProjectExplorer.Project.PluginSettings
-
-
-
- ProjectExplorer.Project.Target.0
-
- Desktop Qt 5.10.1 MSVC2017 64bit
- Desktop Qt 5.10.1 MSVC2017 64bit
- qt.qt5.5101.win64_msvc2017_64_kit
- 0
- 0
- 0
-
- C:/Users/Matt/Documents/temp
-
-
- true
- qmake
-
- QtProjectManager.QMakeBuildStep
- true
-
- false
- false
- false
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- false
-
-
-
- 2
- Build
-
- ProjectExplorer.BuildSteps.Build
-
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- true
- clean
-
-
- 1
- Clean
-
- ProjectExplorer.BuildSteps.Clean
-
- 2
- false
-
- Debug
- Debug
- Qt4ProjectManager.Qt4BuildConfiguration
- 2
- true
-
-
- //neptune/build-olive-qt-Desktop_Qt_5_10_1_MSVC2017_64bit-Release
-
-
- true
- qmake
-
- QtProjectManager.QMakeBuildStep
- false
-
- false
- false
- false
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- false
-
-
-
- 2
- Build
-
- ProjectExplorer.BuildSteps.Build
-
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- true
- clean
-
-
- 1
- Clean
-
- ProjectExplorer.BuildSteps.Clean
-
- 2
- false
-
- Release
- Release
- Qt4ProjectManager.Qt4BuildConfiguration
- 0
- true
-
-
- //neptune/build-olive-qt-Desktop_Qt_5_10_1_MSVC2017_64bit-Profile
-
-
- true
- qmake
-
- QtProjectManager.QMakeBuildStep
- true
-
- false
- true
- false
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- false
-
-
-
- 2
- Build
-
- ProjectExplorer.BuildSteps.Build
-
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- true
- clean
-
-
- 1
- Clean
-
- ProjectExplorer.BuildSteps.Clean
-
- 2
- false
-
- Profile
- Profile
- Qt4ProjectManager.Qt4BuildConfiguration
- 0
- true
-
- 3
-
-
- 0
- Deploy
-
- ProjectExplorer.BuildSteps.Deploy
-
- 1
- Deploy Configuration
-
- ProjectExplorer.DefaultDeployConfiguration
-
- 1
-
-
- false
- false
- 1000
-
- true
-
- false
- false
- false
- false
- true
- 0.01
- 10
- true
- 1
- 25
-
- 1
- true
- false
- true
- valgrind
-
- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
-
- 2
-
-
-
-
-
-
- ProjectExplorer.CustomExecutableRunConfiguration
- 3768
- false
- true
- false
- false
- true
-
- 1
-
-
-
- ProjectExplorer.Project.TargetCount
- 1
-
-
- ProjectExplorer.Project.Updater.FileVersion
- 18
-
-
- Version
- 18
-
-
diff --git a/olive.pro.user.89fae42 b/olive.pro.user.89fae42
deleted file mode 100644
index 72aef63ee..000000000
--- a/olive.pro.user.89fae42
+++ /dev/null
@@ -1,375 +0,0 @@
-
-
-
-
-
- EnvironmentId
- {89fae42a-e0bc-4377-8782-ec7014933bdd}
-
-
- ProjectExplorer.Project.ActiveTarget
- 0
-
-
- ProjectExplorer.Project.EditorSettings
-
- true
- false
- true
-
- Cpp
-
- CppGlobal
-
-
-
- QmlJS
-
- QmlJSGlobal
-
-
- 2
- UTF-8
- false
- 4
- false
- 80
- true
- true
- 1
- true
- false
- 0
- true
- true
- 0
- 8
- true
- 1
- true
- true
- true
- false
-
-
-
- ProjectExplorer.Project.PluginSettings
-
-
-
-
-
- ProjectExplorer.Project.Target.0
-
- Desktop Qt 5.10.1 MSVC2017 64bit
- Desktop Qt 5.10.1 MSVC2017 64bit
- qt.qt5.5101.win64_msvc2017_64_kit
- 0
- 0
- 0
-
- E:/olive/build-olive-qt-Desktop_Qt_5_10_1_MSVC2017_64bit-Debug
-
-
- true
- qmake
-
- QtProjectManager.QMakeBuildStep
- true
-
- false
- false
- false
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- false
-
-
-
- 2
- Build
-
- ProjectExplorer.BuildSteps.Build
-
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- true
- clean
-
-
- 1
- Clean
-
- ProjectExplorer.BuildSteps.Clean
-
- 2
- false
-
- Debug
- Debug
- Qt4ProjectManager.Qt4BuildConfiguration
- 2
- true
-
-
- E:/build-olive-qt-Desktop_Qt_5_10_1_MSVC2017_64bit-Release
-
-
- true
- qmake
-
- QtProjectManager.QMakeBuildStep
- false
-
- false
- false
- false
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- false
-
-
-
- 2
- Build
-
- ProjectExplorer.BuildSteps.Build
-
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- true
- clean
-
-
- 1
- Clean
-
- ProjectExplorer.BuildSteps.Clean
-
- 2
- false
-
- Release
- Release
- Qt4ProjectManager.Qt4BuildConfiguration
- 0
- true
-
-
- E:/build-olive-qt-Desktop_Qt_5_10_1_MSVC2017_64bit-Profile
-
-
- true
- qmake
-
- QtProjectManager.QMakeBuildStep
- true
-
- false
- true
- false
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- false
-
-
-
- 2
- Build
-
- ProjectExplorer.BuildSteps.Build
-
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- true
- clean
-
-
- 1
- Clean
-
- ProjectExplorer.BuildSteps.Clean
-
- 2
- false
-
- Profile
- Profile
- Qt4ProjectManager.Qt4BuildConfiguration
- 0
- true
-
- 3
-
-
- 0
- Deploy
-
- ProjectExplorer.BuildSteps.Deploy
-
- 1
- Deploy Configuration
-
- ProjectExplorer.DefaultDeployConfiguration
-
- 1
-
-
- false
- false
- 1000
-
- true
-
- false
- false
- false
- false
- true
- 0.01
- 10
- true
- 1
- 25
-
- 1
- true
- false
- true
- valgrind
-
- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
-
- 2
-
-
- E:/olive/build-olive-qt-Desktop_Qt_5_10_1_MSVC2017_64bit-Debug/debug/olive-qt.exe
- E:/olive/build-olive-qt-Desktop_Qt_5_10_1_MSVC2017_64bit-Debug/debug/
- Run E:\olive\build-olive-qt-Desktop_Qt_5_10_1_MSVC2017_64bit-Debug\debug\olive-qt.exe
- VC run
- ProjectExplorer.CustomExecutableRunConfiguration
- 3768
- false
- true
- false
- false
- true
-
-
- false
- false
- 1000
-
- true
-
- false
- false
- false
- false
- true
- 0.01
- 10
- true
- 1
- 25
-
- 1
- true
- false
- true
- valgrind
-
- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
-
- 2
-
- olive-qt
- olive-qt2
- Qt4ProjectManager.Qt4RunConfiguration:E:/olive/olive-qt/olive-qt.pro
- true
-
- olive-qt.pro
- false
-
- E:/olive/build-olive-qt-Desktop_Qt_5_10_1_MSVC2017_64bit-Debug
- 3768
- false
- true
- false
- false
- true
-
- 2
-
-
-
- ProjectExplorer.Project.TargetCount
- 1
-
-
- ProjectExplorer.Project.Updater.FileVersion
- 18
-
-
- Version
- 18
-
-
diff --git a/panels/effectcontrols.cpp b/panels/effectcontrols.cpp
index 1ffff3062..a7107f92f 100644
--- a/panels/effectcontrols.cpp
+++ b/panels/effectcontrols.cpp
@@ -115,23 +115,23 @@ void EffectControls::load_effects() {
}
}
-void EffectControls::delete_clips() {
+void EffectControls::delete_effects() {
// load in new clips
- QVector delete_effects;
+ QVector fx_to_del;
for (int i=0;iget_clip(selected_clips.at(i));
for (int j=0;jeffects.size();j++) {
Effect* effect = c->effects.at(j);
if (effect->container->selected) {
c->effects.removeAt(j);
- delete_effects.append(effect);
+ fx_to_del.append(effect);
}
}
}
- if (delete_effects.size() > 0) {
+ if (fx_to_del.size() > 0) {
reload_clips();
- for (int i=0;i& clips);
void clear_effects(bool clear_cache);
- void delete_clips();
+ void delete_effects();
bool is_focused();
private slots:
diff --git a/panels/project.cpp b/panels/project.cpp
index c34f9f820..5c9ca4e25 100644
--- a/panels/project.cpp
+++ b/panels/project.cpp
@@ -227,7 +227,7 @@ void Project::delete_selected_media() {
// remove all clips referencing this media
for (int j=0;jclip_count();j++) {
if (sequence->get_clip(j)->media == m) {
- sequence->delete_clip(j);
+ sequence->replace_clip(j, NULL);
}
}
remove = true;
@@ -371,6 +371,7 @@ QTreeWidgetItem* Project::find_loaded_folder_by_id(int id) {
return parent_item;
}
}
+ return NULL;
}
bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) {
@@ -627,12 +628,6 @@ void Project::load_project() {
// temp variables for loading
loaded_folders.clear();
loaded_media.clear();
- QVector temp_media_list;
- QString temp_name;
- QString temp_url;
- int temp_media_id;
- Sequence* temp_seq;
- Clip* temp_clip;
// find project file version
cont = load_worker(file, stream, LOAD_TYPE_VERSION);
diff --git a/panels/timeline.cpp b/panels/timeline.cpp
index bd86553c3..399afd2c0 100644
--- a/panels/timeline.cpp
+++ b/panels/timeline.cpp
@@ -14,6 +14,7 @@
#include "playback/playback.h"
#include "effects/transition.h"
#include "ui_viewer.h"
+#include "project/undo.h"
#include
#include
@@ -179,7 +180,7 @@ int Timeline::get_track_height_size(bool video) {
void Timeline::add_transition() {
for (int i=0;iclip_count();i++) {
Clip* c = sequence->get_clip(i);
- if (c != NULL && is_clip_selected(c)) {
+ if (c != NULL && is_clip_selected(c, true)) {
if (c->opening_transition == NULL) {
c->opening_transition = create_transition(0, c);
}
@@ -234,24 +235,6 @@ bool Timeline::focused() {
return (ui->headers->hasFocus() || ui->video_area->hasFocus() || ui->audio_area->hasFocus());
}
-void Timeline::undo() {
-// qDebug() << "[INFO] Undo/redo was so buggy, it's been disabled. Sorry for any inconvenience";
- if (sequence != NULL) {
- panel_effect_controls->clear_effects(true);
- sequence->undo();
- redraw_all_clips(false);
- }
-}
-
-void Timeline::redo() {
-// qDebug() << "[INFO] Undo/redo was so buggy, it's been disabled. Sorry for any inconvenience";
- if (sequence != NULL) {
- panel_effect_controls->clear_effects(true);
- sequence->redo();
- redraw_all_clips(false);
- }
-}
-
void Timeline::repaint_timeline() {
if (playing) {
playhead = round(playhead_start + ((QDateTime::currentMSecsSinceEpoch()-start_msecs) * 0.001 * sequence->frame_rate));
@@ -273,7 +256,6 @@ void Timeline::redraw_all_clips(bool changed) {
project_changed = true;
if (!playing) reset_all_audio();
panel_viewer->viewer_widget->update();
- sequence->undo_add_current();
}
ui->video_area->redraw_clips();
@@ -301,9 +283,12 @@ void Timeline::delete_selection(bool ripple_delete) {
if (selections.size() > 0) {
panel_effect_controls->clear_effects(true);
+ TimelineAction* ta = new TimelineAction();
+
long ripple_point = selections.at(0).in;
long ripple_length = selections.at(0).out - selections.at(0).in;
+ // retrieve ripple_point and ripple_length from current selection
for (int i=0;i s.out - s.in) ripple_length = s.out - s.in;
}
}
- delete_areas_and_relink(selections);
+ delete_areas_and_relink(ta, selections);
selections.clear();
if (ripple_delete) {
@@ -335,9 +320,11 @@ void Timeline::delete_selection(bool ripple_delete) {
}
}
}
- if (ripple_length > 0) ripple(ripple_point, -ripple_length);
+ if (ripple_length > 0) ripple(ta, ripple_point, -ripple_length);
}
+ undo_stack.push(ta);
+
redraw_all_clips(true);
}
}
@@ -362,13 +349,13 @@ void Timeline::set_zoom(bool in) {
redraw_all_clips(false);
}
-void Timeline::ripple(long ripple_point, long ripple_length) {
+void Timeline::ripple(TimelineAction* ta, long ripple_point, long ripple_length) {
// ripple all clips around the ripple_point
for (int i=0;iclip_count();i++) {
Clip* c = sequence->get_clip(i);
if (c != NULL && c->timeline_in >= ripple_point) {
- c->timeline_in += ripple_length;
- c->timeline_out += ripple_length;
+ ta->increase_timeline_in(i, ripple_length);
+ ta->increase_timeline_out(i, ripple_length);
}
}
@@ -389,6 +376,15 @@ void Timeline::decheck_tool_buttons(QObject* sender) {
}
}
+QVector Timeline::get_tracks_of_linked_clips(int i) {
+ QVector tracks;
+ Clip* clip = sequence->get_clip(i);
+ for (int j=0;jlinked.size();j++) {
+ tracks.append(sequence->get_clip(clip->linked.at(j))->track);
+ }
+ return tracks;
+}
+
void Timeline::on_pushButton_4_clicked()
{
set_zoom(true);
@@ -399,52 +395,106 @@ void Timeline::on_pushButton_5_clicked()
set_zoom(false);
}
-bool Timeline::is_clip_selected(Clip* clip) {
+bool Timeline::is_clip_selected(Clip* clip, bool containing) {
for (int i=0;itrack == s.track && clip->timeline_in >= s.in && clip->timeline_out <= s.out) {
+ if (clip->track == s.track && ((clip->timeline_in >= s.in && clip->timeline_out <= s.out && containing) ||
+ (!containing && !(clip->timeline_in < s.in && clip->timeline_out < s.in) && !(clip->timeline_in > s.in && clip->timeline_out > s.in)))) {
return true;
}
}
return false;
}
-void Timeline::on_snappingButton_toggled(bool checked)
-{
+void Timeline::on_snappingButton_toggled(bool checked) {
snapping = checked;
}
-void Timeline::split_clip_and_relink(int clip, long frame, bool relink) {
+Clip* Timeline::split_clip(TimelineAction* ta, int p, long frame) {
+ Clip* pre = sequence->get_clip(p);
+ if (pre != NULL && pre->timeline_in < frame && pre->timeline_out > frame) { // guard against attempts to split at in/out points
+ Clip* post = pre->copy();
+
+ ta->set_timeline_out(p, frame);
+ post->timeline_in = frame;
+ post->clip_in = pre->clip_in + (frame - pre->timeline_in);
+
+ long pre_length = pre->getLength();
+
+ if (pre->closing_transition != NULL) {
+ post->closing_transition = pre->closing_transition;
+ pre->closing_transition = NULL;
+ long post_length = post->getLength();
+ if (post->closing_transition->length > post_length) {
+ post->closing_transition->length = post_length;
+ }
+ }
+ if (pre->opening_transition != NULL && pre->opening_transition->length > pre_length) {
+ pre->opening_transition->length = pre_length;
+ }
+
+ return post;
+ }
+ return NULL;
+}
+
+bool Timeline::has_clip_been_split(int c) {
+ for (int i=0;iget_clip(clip);
if (c != NULL) {
QVector pre_clips;
- QVector post_clips;
+ QVector post_clips;
- int post = sequence->split_clip(clip, frame);
+ Clip* post = split_clip(ta, clip, frame);
// if alt is not down, split clips links too
- if (post > -1 && relink) {
- pre_clips.append(clip);
+ if (post == NULL) {
+ return false;
+ } else {
post_clips.append(post);
+ if (relink) {
+ pre_clips.append(clip);
- bool original_clip_is_selected = is_clip_selected(c);
+ bool original_clip_is_selected = is_clip_selected(c, true);
- // find linked clips of old clip
- for (int i=0;ilinked.size();i++) {
- int l = c->linked.at(i);
- Clip* link = sequence->get_clip(l);
- if (original_clip_is_selected != !is_clip_selected(link)) {
- int s = sequence->split_clip(l, frame);
- if (s > -1) {
- pre_clips.append(l);
- post_clips.append(s);
+ // find linked clips of old clip
+ for (int i=0;ilinked.size();i++) {
+ int l = c->linked.at(i);
+ if (!has_clip_been_split(l)) {
+ Clip* link = sequence->get_clip(l);
+ if ((original_clip_is_selected && is_clip_selected(link, true)) || !original_clip_is_selected) {
+ split_cache.append(l);
+ Clip* s = split_clip(ta, l, frame);
+ if (s != NULL) {
+ pre_clips.append(l);
+ post_clips.append(s);
+ }
+ }
}
}
- }
- relink_clips_using_ids(pre_clips, post_clips);
+ relink_clips_using_ids(pre_clips, post_clips);
+ }
+ ta->add_clips(post_clips);
+ return true;
}
}
+ return false;
}
void Timeline::clean_up_selections(QVector& areas) {
@@ -477,11 +527,11 @@ void Timeline::clean_up_selections(QVector& areas) {
}
}
-void Timeline::delete_areas_and_relink(QVector& areas) {
+void Timeline::delete_areas_and_relink(TimelineAction* ta, QVector& areas) {
clean_up_selections(areas);
QVector pre_clips;
- QVector post_clips;
+ QVector post_clips;
for (int i=0;i& areas) {
if (c != NULL && c->track == s.track && !c->undeletable) {
if (c->timeline_in >= s.in && c->timeline_out <= s.out) {
// clips falls entirely within deletion area
- sequence->delete_clip(j);
- j--;
+ ta->delete_clip(j);
} else if (c->timeline_in < s.in && c->timeline_out > s.out) {
// middle of clip is within deletion area
// duplicate clip
Clip* post = c->copy();
- c->timeline_out = s.in;
+ ta->set_timeline_out(j, s.in);
post->timeline_in = s.out;
- post->clip_in = c->clip_in + c->getLength() + (s.out - s.in);
+ post->clip_in = c->clip_in + (s.in - c->timeline_in) + (s.out - s.in);
pre_clips.append(j);
- post_clips.append(sequence->add_clip(post));
+ post_clips.append(post);
} else if (c->timeline_in < s.in && c->timeline_out > s.in) {
// only out point is in deletion area
- c->timeline_out = s.in;
+ ta->set_timeline_out(j, s.in);
} else if (c->timeline_in < s.out && c->timeline_out > s.out) {
// only in point is in deletion area
- c->clip_in += s.out - c->timeline_in;
- c->timeline_in = s.out;
+ ta->increase_clip_in(j, s.out - c->timeline_in);
+ ta->set_timeline_in(j, s.out);
}
}
}
}
relink_clips_using_ids(pre_clips, post_clips);
+ ta->add_clips(post_clips);
}
void Timeline::copy(bool del) {
@@ -529,7 +579,7 @@ void Timeline::copy(bool del) {
if (c != NULL) {
for (int j=0;jtrack && !((c->timeline_in < s.in && c->timeline_out < s.in) || (c->timeline_in > s.out && c->timeline_out > s.out))) {
+ if (s.track == c->track && !((c->timeline_in <= s.in && c->timeline_out <= s.in) || (c->timeline_in >= s.out && c->timeline_out >= s.out))) {
if (!cleared) {
clip_clipboard.clear();
cleared = true;
@@ -575,17 +625,15 @@ void Timeline::copy(bool del) {
}
}
-void Timeline::relink_clips_using_ids(QVector& old_clips, QVector& new_clips) {
+void Timeline::relink_clips_using_ids(QVector& old_clips, QVector& new_clips) {
// relink pasted clips
for (int i=0;iget_clip(old_clips.at(i));
- Clip* nc = sequence->get_clip(new_clips.at(i));
-
for (int j=0;jlinked.size();j++) {
for (int k=0;klinked.at(j)) {
- nc->linked.append(new_clips.at(k));
+ if (oc->linked.at(j) == old_clips.at(k)) {
+ new_clips.at(i)->linked.append(k);
}
}
}
@@ -594,10 +642,11 @@ void Timeline::relink_clips_using_ids(QVector& old_clips, QVector& new
void Timeline::paste() {
if (clip_clipboard.size() > 0) {
+ TimelineAction* ta = new TimelineAction();
+
// create copies and delete areas that we'll be pasting to
QVector delete_areas;
QVector pasted_clips;
- QVector pasted_clip_ids;
long paste_end = 0;
for (int i=0;itrack;
delete_areas.append(s);
}
- delete_areas_and_relink(delete_areas);
-
- // add copies to the sequence (added afterwards to avoid
- for (int i=0;iadd_clip(pasted_clips.at(i)));
- }
+ delete_areas_and_relink(ta, delete_areas);
// ADAPT
for (int i=0;ilinked.size();j++) {
for (int k=0;kload_id == oc->linked.at(j)) {
- pasted_clips.at(i)->linked.append(pasted_clip_ids.at(k));
+ pasted_clips.at(i)->linked.append(k);
}
}
}
}
// ADAPT
+ ta->add_clips(pasted_clips);
+
+ undo_stack.push(ta);
+
redraw_all_clips(true);
if (paste_seeks) {
@@ -647,12 +695,13 @@ void Timeline::paste() {
}
}
-bool Timeline::split_selection() {
+bool Timeline::split_selection(TimelineAction* ta) {
bool split = false;
// temporary relinking vectors
QVector pre_splits;
- QVector post_splits;
+ QVector post_splits;
+ QVector secondary_post_splits;
// find clips within selection and split
for (int j=0;jclip_count();j++) {
@@ -661,42 +710,70 @@ bool Timeline::split_selection() {
for (int i=0;itrack) {
- int post_a = sequence->split_clip(j, s.in);
- int post_b = sequence->split_clip(j, s.out);
-
- if (post_a != -1) {
+ if (clip->timeline_in < s.in && clip->timeline_out > s.out) {
+ Clip* split_A = clip->copy();
+ split_A->clip_in += (s.in - clip->timeline_in);
+ split_A->timeline_in = s.in;
+ split_A->timeline_out = s.out;
pre_splits.append(j);
- post_splits.append(post_a);
- }
- if (post_b != -1) {
- pre_splits.append(j);
- post_splits.append(post_b);
- }
+ post_splits.append(split_A);
- split = true;
+ Clip* split_B = clip->copy();
+ split_B->clip_in += (s.out - clip->timeline_in);
+ split_B->timeline_in = s.out;
+ secondary_post_splits.append(split_B);
+
+ ta->set_timeline_out(j, s.in);
+ split = true;
+ } else {
+ Clip* post_a = split_clip(ta, j, s.in);
+ Clip* post_b = split_clip(ta, j, s.out);
+ if (post_a != NULL) {
+ pre_splits.append(j);
+ post_splits.append(post_a);
+ split = true;
+ }
+ if (post_b != NULL) {
+ if (post_a != NULL) {
+ pre_splits.append(j);
+ post_splits.append(post_b);
+ } else {
+ secondary_post_splits.append(post_b);
+ }
+ split = true;
+ }
+ }
}
}
}
}
- // relink after splitting
- relink_clips_using_ids(pre_splits, post_splits);
+ if (split) {
+ // relink after splitting
+ relink_clips_using_ids(pre_splits, post_splits);
+ relink_clips_using_ids(pre_splits, secondary_post_splits);
+ ta->add_clips(post_splits);
+ ta->add_clips(secondary_post_splits);
- return split;
+ return true;
+ }
+ return false;
}
void Timeline::split_at_playhead() {
+ TimelineAction* ta = new TimelineAction();
bool split_selected = false;
+ split_cache.clear();
if (selections.size() > 0) {
// see if whole clips are selected
QVector pre_clips;
- QVector post_clips;
+ QVector post_clips;
for (int j=0;jclip_count();j++) {
Clip* clip = sequence->get_clip(j);
- if (clip != NULL && is_clip_selected(clip)) {
- int s = sequence->split_clip(j, playhead);
- if (s != -1) {
+ if (clip != NULL && is_clip_selected(clip, true)) {
+ Clip* s = split_clip(ta, j, playhead);
+ if (s != NULL) {
pre_clips.append(j);
post_clips.append(s);
split_selected = true;
@@ -707,9 +784,10 @@ void Timeline::split_at_playhead() {
if (split_selected) {
// relink clips if we split
relink_clips_using_ids(pre_clips, post_clips);
+ ta->add_clips(post_clips);
} else {
// split a selection if not
- split_selected = split_selection();
+ split_selected = split_selection(ta);
}
}
@@ -719,13 +797,19 @@ void Timeline::split_at_playhead() {
Clip* c = sequence->get_clip(j);
if (c != NULL) {
// always relinks
- split_clip_and_relink(j, playhead, true);
- split_selected = true;
+ if (split_clip_and_relink(ta, j, playhead, true)) {
+ split_selected = true;
+ }
}
}
}
- if (split_selected) redraw_all_clips(true);
+ if (split_selected) {
+ undo_stack.push(ta);
+ redraw_all_clips(true);
+ } else {
+ delete ta;
+ }
}
bool Timeline::snap_to_point(long point, long* l) {
diff --git a/panels/timeline.h b/panels/timeline.h
index 9fc8c69dc..55cea36e0 100644
--- a/panels/timeline.h
+++ b/panels/timeline.h
@@ -1,4 +1,4 @@
-#ifndef TIMELINE_H
+#ifndef TIMELINE_H
#define TIMELINE_H
#include "ui/timelinetools.h"
@@ -10,6 +10,7 @@
class QPushButton;
class SourceTable;
class ViewerWidget;
+class TimelineAction;
struct Sequence;
struct Clip;
struct Media;
@@ -64,21 +65,22 @@ public:
bool focused();
void set_zoom(bool in);
- void undo();
- void redo();
void copy(bool del);
void paste();
void deselect();
- bool split_selection();
+ Clip* split_clip(TimelineAction* ta, int p, long frame);
+ bool split_selection(TimelineAction* ta);
void split_at_playhead();
- void split_clip_and_relink(int clip, long frame, bool relink);
+ bool split_clip_and_relink(TimelineAction* ta, int clip, long frame, bool relink);
void clean_up_selections(QVector& areas);
- void delete_areas_and_relink(QVector& areas);
- void relink_clips_using_ids(QVector& old_clips, QVector& new_clips);
+ void delete_areas_and_relink(TimelineAction* ta, QVector& areas);
+ void relink_clips_using_ids(QVector& old_clips, QVector& new_clips);
void update_sequence();
void increase_track_height();
void decrease_track_height();
void add_transition();
+ QVector get_tracks_of_linked_clips(int i);
+ bool has_clip_been_split(int c);
int get_snap_range();
int getScreenPointFromFrame(long frame);
@@ -132,7 +134,7 @@ public:
bool selecting;
int selection_offset;
QVector selections;
- bool is_clip_selected(Clip* clip);
+ bool is_clip_selected(Clip* clip, bool containing);
void delete_selection(bool ripple);
void select_all();
bool rect_select_init;
@@ -153,12 +155,14 @@ public:
// splitting
bool splitting;
+ QVector split_tracks;
+ QVector split_cache;
// importing
bool importing;
// ripple
- void ripple(long ripple_point, long ripple_length);
+ void ripple(TimelineAction* ta, long ripple_point, long ripple_length);
Ui::Timeline *ui;
public slots:
diff --git a/project/sequence.cpp b/project/sequence.cpp
index a84652574..803f8f130 100644
--- a/project/sequence.cpp
+++ b/project/sequence.cpp
@@ -5,9 +5,7 @@
#include
-Sequence::Sequence() {
- reset_undo();
-}
+Sequence::Sequence() {}
Sequence::~Sequence() {
// dealloc all clips
@@ -48,23 +46,8 @@ Clip* Sequence::get_clip(int i) {
return clips.at(i);
}
-void Sequence::delete_clip(int i) {
- // remove any potential link references to clip
- for (int j=0;jlinked.size();k++) {
- if (c->linked[k] == i) {
- c->linked.removeAt(k);
- break;
- }
- }
- }
- }
-
- // finally remove from vector
- delete clips.at(i);
- clips[i] = NULL;
+void Sequence::replace_clip(int i, Clip* c) {
+ clips[i] = c;
}
long Sequence::getEndFrame() {
@@ -78,6 +61,11 @@ long Sequence::getEndFrame() {
return end;
}
+void Sequence::destroy_clip(int i, bool del) {
+ if (del) delete clips.at(i);
+ clips.removeAt(i);
+}
+
void Sequence::get_track_limits(int* video_tracks, int* audio_tracks) {
int vt = 0;
int at = 0;
@@ -95,105 +83,5 @@ void Sequence::get_track_limits(int* video_tracks, int* audio_tracks) {
if (audio_tracks != NULL) *audio_tracks = at;
}
-//void Sequence::delete_area(long in, long out, int track) {
-
-//}
-
-int Sequence::split_clip(int p, long frame) {
- Clip* pre = get_clip(p);
- if (pre != NULL && pre->timeline_in < frame && pre->timeline_out > frame) { // guard against attempts to split at in/out points
- Clip* post = pre->copy();
-
- pre->timeline_out = frame;
- post->timeline_in = frame;
- post->clip_in = pre->clip_in + pre->getLength();
-
- long pre_length = pre->getLength();
-
- if (pre->closing_transition != NULL) {
- post->closing_transition = pre->closing_transition;
- pre->closing_transition = NULL;
- long post_length = post->getLength();
- if (post->closing_transition->length > post_length) {
- post->closing_transition->length = post_length;
- }
- }
- if (pre->opening_transition != NULL && pre->opening_transition->length > pre_length) {
- pre->opening_transition->length = pre_length;
- }
-
- return add_clip(post);
- }
- return -1;
-}
-
-void Sequence::reset_undo() {
- while (undo_stack.size() > 0) {
- delete undo_stack.last();
- undo_stack.removeLast();
- }
- undo_pointer = -1;
- undo_add_current();
-}
-
-void Sequence::undo_add_current() {
- if (undo_stack.size() == UNDO_LIMIT) {
- delete undo_stack.at(0);
- undo_stack.removeFirst();
- } else {
- undo_pointer++;
- while (undo_stack.size() > undo_pointer+1) {
- delete undo_stack.last();
- undo_stack.removeLast();
- }
- }
-
- // copy clips
- QVector* copied_clips = new QVector();
- for (int i=0;icopy();
- copy->linked = original->linked;
- copied_clips->append(copy);
- }
- }
- undo_stack.append(copied_clips);
-}
-
-void Sequence::set_undo() {
- qDebug() << "[INFO] Setting undo pointer to" << undo_pointer;
-
- for (int i=0;i* copy_from_list = undo_stack.at(undo_pointer);
- for (int i=0;isize();i++) {
- Clip* original = copy_from_list->at(i);
- Clip* copy = original->copy();
- copy->linked = original->linked;
- add_clip(copy);
- }
-}
-
-void Sequence::undo() {
- if (undo_pointer > 0) {
- undo_pointer--;
- set_undo();
- } else {
- qDebug() << "[INFO] No more undos";
- }
-}
-void Sequence::redo() {
- if (undo_pointer == undo_stack.size() - 1) {
- qDebug() << "[INFO] No more redos";
- } else {
- undo_pointer++;
- set_undo();
- }
-}
-
// static variable for the currently active sequence
Sequence* sequence = NULL;
diff --git a/project/sequence.h b/project/sequence.h
index 1616bafef..566e234de 100644
--- a/project/sequence.h
+++ b/project/sequence.h
@@ -16,26 +16,17 @@ public:
int add_clip(Clip* c);
int clip_count();
Clip* get_clip(int i);
- void delete_clip(int i);
- int split_clip(int pre, long frame);
+ void replace_clip(int i, Clip* c);
void get_track_limits(int* video_tracks, int* audio_tracks);
+ void destroy_clip(int i, bool del);
long getEndFrame();
int width;
int height;
float frame_rate;
int audio_frequency;
- int audio_layout;
-
- void reset_undo();
- void undo_add_current();
- void undo();
- void redo();
+ int audio_layout;
private:
QVector clips;
-
- void set_undo();
- QVector*> undo_stack;
- int undo_pointer;
};
// static variable for the currently active sequence
diff --git a/project/undo.cpp b/project/undo.cpp
index 8ea9917d9..5137a49af 100644
--- a/project/undo.cpp
+++ b/project/undo.cpp
@@ -1,3 +1,231 @@
#include "undo.h"
+#include
+
QUndoStack undo_stack;
+
+#define TA_IN 0
+#define TA_OUT 1
+#define TA_CLIP_IN 2
+#define TA_TRACK 3
+#define TA_DELETE 4
+#define TA_ADD_IN 5
+#define TA_ADD_OUT 6
+#define TA_ADD_CLIP_IN 7
+#define TA_ADD_TRACK 8
+
+TimelineAction::TimelineAction() : done(false) {}
+
+TimelineAction::~TimelineAction() {
+ for (int i=0;i& clips, int offset) {
+ for (int i=0;ilinked.size();j++) {
+ c->linked[j] += offset;
+ }
+ }
+}
+
+void TimelineAction::add_clips(QVector& add) {
+ offset_links(add, clips_to_add.size());
+ clips_to_add.append(add);
+}
+
+void TimelineAction::new_action(int action, int clip, long old_val, long new_val) {
+ actions.append(action);
+ clips.append(clip);
+ old_values.append(old_val);
+ new_values.append(new_val);
+}
+
+void TimelineAction::set_timeline_in(int clip, long value) {
+ new_action(TA_IN, clip, sequence->get_clip(clip)->timeline_in, value);
+}
+
+void TimelineAction::set_timeline_out(int clip, long value) {
+ new_action(TA_OUT, clip, sequence->get_clip(clip)->timeline_out, value);
+}
+
+void TimelineAction::set_clip_in(int clip, long value) {
+ new_action(TA_CLIP_IN, clip, sequence->get_clip(clip)->clip_in, value);
+}
+
+void TimelineAction::set_track(int clip, int value) {
+ new_action(TA_TRACK, clip, sequence->get_clip(clip)->track, value);
+}
+
+void TimelineAction::increase_timeline_in(int clip, long value) {
+ new_action(TA_ADD_IN, clip, 0, value);
+}
+
+void TimelineAction::increase_timeline_out(int clip, long value) {
+ new_action(TA_ADD_OUT, clip, 0, value);
+}
+
+void TimelineAction::increase_clip_in(int clip, long value) {
+ new_action(TA_ADD_CLIP_IN, clip, 0, value);
+}
+
+void TimelineAction::increase_track(int clip, int value) {
+ new_action(TA_ADD_TRACK, clip, 0, value);
+}
+
+void TimelineAction::delete_clip(int clip) {
+ new_action(TA_DELETE, clip, 0, 0);
+}
+
+void TimelineAction::undo() {
+ for (int i=0;iget_clip(clips.at(i))->timeline_in = old_values.at(i);
+ break;
+ case TA_OUT:
+ sequence->get_clip(clips.at(i))->timeline_out = old_values.at(i);
+ break;
+ case TA_CLIP_IN:
+ sequence->get_clip(clips.at(i))->clip_in = old_values.at(i);
+ break;
+ case TA_TRACK:
+ sequence->get_clip(clips.at(i))->track = old_values.at(i);
+ break;
+ case TA_DELETE:
+ sequence->replace_clip(clips.at(i), deleted_clips.at(new_values.at(i)));
+ break;
+ case TA_ADD_IN:
+ sequence->get_clip(clips.at(i))->timeline_in -= new_values.at(i);
+ break;
+ case TA_ADD_OUT:
+ sequence->get_clip(clips.at(i))->timeline_out -= new_values.at(i);
+ break;
+ case TA_ADD_CLIP_IN:
+ sequence->get_clip(clips.at(i))->clip_in -= new_values.at(i);
+ break;
+ case TA_ADD_TRACK:
+ sequence->get_clip(clips.at(i))->track -= new_values.at(i);
+ break;
+ }
+ }
+
+
+
+ // restore link references to deleted clips
+ for (int i=0;iget_clip(removed_link_from.at(i))->linked.append(removed_link_to.at(i));
+ }
+
+ // delete added clips
+ if (clips_to_add.size() > 0) {
+ int delete_count = 0;
+ for (int i=0;idestroy_clip(added_indexes.at(i)-delete_count, true);
+ delete_count++;
+ }
+ }
+
+ done = false;
+}
+
+void TimelineAction::redo() {
+ removed_link_from.clear();
+ removed_link_to.clear();
+ deleted_clips.clear();
+ deleted_clips_indices.clear();
+ added_indexes.clear();
+
+ for (int i=0;iget_clip(clips.at(i))->timeline_in = new_values.at(i);
+ }
+ break;
+ case TA_OUT:
+ {
+ sequence->get_clip(clips.at(i))->timeline_out = new_values.at(i);
+ }
+ break;
+ case TA_CLIP_IN:
+ {
+ sequence->get_clip(clips.at(i))->clip_in = new_values.at(i);
+ }
+ break;
+ case TA_TRACK:
+ {
+ sequence->get_clip(clips.at(i))->track = new_values.at(i);
+ }
+ break;
+ case TA_DELETE:
+ {
+ new_values[i] = deleted_clips.size();
+ deleted_clips.append(sequence->get_clip(clips.at(i)));
+ deleted_clips_indices.append(clips.at(i));
+ sequence->replace_clip(clips.at(i), NULL);
+ }
+ break;
+ case TA_ADD_IN:
+ {
+ sequence->get_clip(clips.at(i))->timeline_in += new_values.at(i);
+ }
+ break;
+ case TA_ADD_OUT:
+ {
+ sequence->get_clip(clips.at(i))->timeline_out += new_values.at(i);
+ }
+ break;
+ case TA_ADD_CLIP_IN:
+ {
+ sequence->get_clip(clips.at(i))->clip_in += new_values.at(i);
+ }
+ break;
+ case TA_ADD_TRACK:
+ {
+ sequence->get_clip(clips.at(i))->track += new_values.at(i);
+ }
+ break;
+ }
+ }
+
+ // remove any potential link references from deleted clips
+ for (int i=0;iclip_count();j++) {
+ Clip* c = sequence->get_clip(j);
+ if (c != NULL) {
+ for (int k=0;klinked.size();k++) {
+ if (c->linked.at(k) == deleted_clips_indices.at(i)) {
+ removed_link_from.append(j);
+ removed_link_to.append(deleted_clips_indices.at(i));
+ c->linked.removeAt(k);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // add any new clips
+ if (clips_to_add.size() > 0) {
+ int link_offset = sequence->clip_count();
+ for (int i=0;icopy();
+ copy->linked.resize(original->linked.size());
+ for (int j=0;jlinked.size();j++) {
+ copy->linked[j] = original->linked.at(j) + link_offset;
+ }
+ added_indexes.append(sequence->add_clip(copy));
+ }
+ }
+
+ done = true;
+}
diff --git a/project/undo.h b/project/undo.h
index 559a2e113..fbf49c72b 100644
--- a/project/undo.h
+++ b/project/undo.h
@@ -10,4 +10,42 @@
extern QUndoStack undo_stack;
+class TimelineAction : public QUndoCommand {
+public:
+ TimelineAction();
+ ~TimelineAction();
+ void add_clips(QVector& add);
+ void set_timeline_in(int clip, long value);
+ void increase_timeline_in(int clip, long value);
+ void set_timeline_out(int clip, long value);
+ void increase_timeline_out(int clip, long value);
+ void set_clip_in(int clip, long value);
+ void increase_clip_in(int clip, long value);
+ void set_track(int clip, int value);
+ void increase_track(int clip, int value);
+ void delete_clip(int clip);
+ void undo();
+ void redo();
+private:
+ QVector actions;
+ QVector clips;
+ QVector old_values;
+ QVector new_values;
+
+ QVector deleted_clips;
+ QVector deleted_clips_indices;
+
+ QVector clips_to_add;
+ QVector added_indexes;
+
+ void new_action(int action, int clip, long old_val, long new_val);
+
+ void offset_links(QVector& clips, int offset);
+
+ QVector removed_link_from;
+ QVector removed_link_to;
+
+ bool done;
+};
+
#endif // UNDO_H
diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp
index 862edc953..d3573dcd8 100644
--- a/ui/timelinewidget.cpp
+++ b/ui/timelinewidget.cpp
@@ -9,6 +9,7 @@
#include "io/media.h"
#include "ui/sourcetable.h"
#include "panels/effectcontrols.h"
+#include "project/undo.h"
#include "effects/effects.h"
#include "effects/transition.h"
@@ -38,7 +39,7 @@ TimelineWidget::TimelineWidget(QWidget *parent) : QWidget(parent) {
void TimelineWidget::show_context_menu(const QPoint& pos) {
QMenu menu(this);
- menu.addAction("heck!");
+ menu.addAction("Context menu coming soon...");
menu.exec(mapToGlobal(pos));
}
@@ -118,7 +119,9 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
if (panel_timeline->importing) {
event->accept();
- QVector added_clips;
+ TimelineAction* ta = new TimelineAction();
+
+ QVector added_clips;
// delete areas before adding
QVector delete_areas;
@@ -130,7 +133,7 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
s.track = g.track;
delete_areas.append(s);
}
- panel_timeline->delete_areas_and_relink(delete_areas);
+ panel_timeline->delete_areas_and_relink(ta, delete_areas);
// add clips
for (int i=0;ighosts.size();i++) {
@@ -171,24 +174,28 @@ void TimelineWidget::dropEvent(QDropEvent* event) {
c->effects.append(create_effect(AUDIO_PAN_EFFECT, c));
}
- added_clips.append(sequence->add_clip(c));
+ added_clips.append(c);
}
// link clips from the same media
for (int i=0;iget_clip(added_clips.at(i));
+ Clip* c = added_clips.at(i);
for (int j=0;jget_clip(added_clips.at(j));
+ Clip* cc = added_clips.at(j);
if (c != cc && c->media == cc->media) {
- c->linked.append(added_clips.at(j));
+ c->linked.append(j);
}
}
}
+ ta->add_clips(added_clips);
+
panel_timeline->ghosts.clear();
panel_timeline->importing = false;
panel_timeline->snapped = false;
+ undo_stack.push(ta);
+
setFocus();
panel_timeline->redraw_all_clips(true);
@@ -247,7 +254,7 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) {
if (clip_index >= 0) {
Clip* clip = sequence->get_clip(clip_index);
if (clip != NULL) {
- if (panel_timeline->is_clip_selected(clip)) {
+ if (panel_timeline->is_clip_selected(clip, true)) {
if (shift) {
// TODO if shift is down, deselect it
}
@@ -271,7 +278,7 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) {
if (!(event->modifiers() & Qt::AltModifier)) {
for (int i=0;ilinked.size();i++) {
Clip* link = sequence->get_clip(clip->linked.at(i));
- if (!panel_timeline->is_clip_selected(link)) {
+ if (!panel_timeline->is_clip_selected(link, true)) {
Selection ss;
ss.in = link->timeline_in;
ss.out = link->timeline_out;
@@ -302,11 +309,9 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) {
break;
case TIMELINE_TOOL_RAZOR:
{
- if (clip_index >= 0) {
- panel_timeline->split_clip_and_relink(clip_index, panel_timeline->drag_frame_start, !(event->modifiers() & Qt::AltModifier));
- }
panel_timeline->splitting = true;
- panel_timeline->redraw_all_clips(true);
+ panel_timeline->split_tracks.append(panel_timeline->drag_track_start);
+ panel_timeline->repaint_timeline();
}
break;
}
@@ -314,10 +319,14 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) {
}
void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) {
+ bool alt = (event->modifiers() & Qt::AltModifier);
+
if (event->button() == Qt::LeftButton) {
bool repaint = false;
if (panel_timeline->moving_proc) {
+ TimelineAction* ta = new TimelineAction();
+
// if we were RIPPLING, move all the clips
if (panel_timeline->tool == TIMELINE_TOOL_RIPPLE) {
long ripple_length, ripple_point;
@@ -344,18 +353,18 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) {
if (ripple_point > comp_point) ripple_point = comp_point;
}
if (!panel_timeline->trim_in_point) ripple_length = -ripple_length;
- panel_timeline->ripple(ripple_point, ripple_length);
+ panel_timeline->ripple(ta, ripple_point, ripple_length);
}
if (panel_timeline->tool == TIMELINE_TOOL_POINTER && (event->modifiers() & Qt::AltModifier)) { // if holding alt, duplicate rather than move
// duplicate clips
- QVector copy_clips;
QVector old_clips;
- QVector new_clips;
+ QVector new_clips;
QVector delete_areas;
for (int i=0;ighosts.size();i++) {
const Ghost& g = panel_timeline->ghosts.at(i);
if (g.old_in != g.in || g.old_out != g.out || g.track != g.old_track || g.clip_in != g.old_clip_in) {
+ // create copy of clip
Clip* c = sequence->get_clip(g.clip)->copy();
c->timeline_in = g.in;
@@ -369,61 +378,73 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) {
delete_areas.append(s);
old_clips.append(g.clip);
- copy_clips.append(c);
+ new_clips.append(c);
}
}
- if (copy_clips.size() > 0) {
- panel_timeline->delete_areas_and_relink(delete_areas);
- for (int i=0;iadd_clip(copy_clips.at(i)));
- }
+ if (new_clips.size() > 0) {
+ panel_timeline->delete_areas_and_relink(ta, delete_areas);
+
// relink duplicated clips
panel_timeline->relink_clips_using_ids(old_clips, new_clips);
+
+ for (int i=0;iadd_clips(new_clips);
+ }
}
} else {
// move clips
// TODO can we do this better than 3 consecutive for loops?
if (panel_timeline->tool == TIMELINE_TOOL_POINTER) {
- for (int i=0;ighosts.size();i++) {
- // step 1 - set clips that are moving to "undeletable" (to avoid step 2 deleting any part of them)
- sequence->get_clip(panel_timeline->ghosts[i].clip)->undeletable = true;
- }
- // step 2 - delete areas
QVector delete_areas;
for (int i=0;ighosts.size();i++) {
- // step 2 - delete anything that exists in area that clip is moving to
- // note: ripples are non-destructive so this is pointer-tool exclusive
+ // step 1 - set clips that are moving to "undeletable" (to avoid step 2 deleting any part of them)
const Ghost& g = panel_timeline->ghosts.at(i);
+
+ sequence->get_clip(g.clip)->undeletable = true;
+
Selection s;
s.in = g.in;
s.out = g.out;
s.track = g.track;
delete_areas.append(s);
}
- panel_timeline->delete_areas_and_relink(delete_areas);
+ panel_timeline->delete_areas_and_relink(ta, delete_areas);
+ for (int i=0;ighosts.size();i++) {
+ sequence->get_clip(panel_timeline->ghosts[i].clip)->undeletable = false;
+ }
}
for (int i=0;ighosts.size();i++) {
Ghost& g = panel_timeline->ghosts[i];
// step 3 - move clips
- Clip* c = sequence->get_clip(g.clip);
- c->timeline_in += (g.in - g.old_in);
- c->timeline_out += (g.out - g.old_out);
- c->track += (g.track - g.old_track);
- c->clip_in += (g.clip_in - g.old_clip_in);
- }
- if (panel_timeline->tool == TIMELINE_TOOL_POINTER) {
- for (int i=0;ighosts.size();i++) {
- // step 4 - set clips back to deletable
- sequence->get_clip(panel_timeline->ghosts[i].clip)->undeletable = false;
- }
+ ta->increase_timeline_in(g.clip, g.in - g.old_in);
+ ta->increase_timeline_out(g.clip, g.out - g.old_out);
+ ta->increase_track(g.clip, g.track - g.old_track);
+ ta->increase_clip_in(g.clip, g.clip_in - g.old_clip_in);
}
}
+ undo_stack.push(ta);
+
panel_timeline->redraw_all_clips(true);
} else if (panel_timeline->selecting || panel_timeline->rect_select_proc) {
repaint = true;
+ } else if (panel_timeline->splitting) {
+ TimelineAction* ta = new TimelineAction();
+ bool split = false;
+ for (int i=0;isplit_tracks.size();i++) {
+ int split_index = getClipIndexFromCoords(panel_timeline->drag_frame_start, panel_timeline->split_tracks.at(i));
+ if (split_index > -1 && panel_timeline->split_clip_and_relink(ta, split_index, panel_timeline->drag_frame_start, !alt)) {
+ split = true;
+ }
+ }
+ if (split) {
+ undo_stack.push(ta);
+ panel_timeline->redraw_all_clips(true);
+ } else {
+ delete ta;
+ }
+ panel_timeline->split_cache.clear();
}
// remove duplicate selections
@@ -432,6 +453,9 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) {
// destroy all ghosts
panel_timeline->ghosts.clear();
+ // clear split tracks
+ panel_timeline->split_tracks.clear();
+
panel_timeline->selecting = false;
panel_timeline->moving_proc = false;
panel_timeline->moving_init = false;
@@ -454,7 +478,7 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) {
int aclip = -1;
for (int i=0;iclip_count();i++) {
Clip* clip = sequence->get_clip(i);
- if (clip != NULL && panel_timeline->is_clip_selected(clip)) {
+ if (clip != NULL && panel_timeline->is_clip_selected(clip, true)) {
if (clip->track < 0) {
if (got_vclip) {
vclip = -1;
@@ -748,17 +772,18 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
if (panel_timeline->edit_tool_selects_links) {
for (int j=0;jclip_count();j++) {
Clip* c = sequence->get_clip(j);
- for (int i=0;iselections.size();i++) {
- const Selection& s = panel_timeline->selections.at(i);
- if (c->track == s.track &&
- !(c->timeline_in < s.in && c->timeline_out < s.in) &&
- !(c->timeline_in > s.out && c->timeline_out > s.out)) {
- for (int k=0;klinked.size();k++) {
- Clip* link = sequence->get_clip(c->linked.at(k));
+ for (int k=0;kselections.size();k++) {
+ const Selection& s = panel_timeline->selections.at(k);
+ if (!(c->timeline_in < s.in && c->timeline_out < s.in) &&
+ !(c->timeline_in > s.out && c->timeline_out > s.out) &&
+ c->track == s.track) {
+
+ QVector linked_tracks = panel_timeline->get_tracks_of_linked_clips(j);
+ for (int k=0;kselections.size();l++) {
const Selection& test_sel = panel_timeline->selections.at(l);
- if (test_sel.track == link->track &&
+ if (test_sel.track == linked_tracks.at(k) &&
test_sel.in == s.in &&
test_sel.out == s.out) {
found = true;
@@ -766,10 +791,12 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
}
}
if (!found) {
- Selection link_sel = {s.in, s.out, link->track};
+ Selection link_sel = {s.in, s.out, linked_tracks.at(k)};
panel_timeline->selections.append(link_sel);
}
}
+
+ break;
}
}
}
@@ -800,7 +827,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
// create ghosts
for (int i=0;iclip_count();i++) {
Clip* c = sequence->get_clip(i);
- if (c != NULL && panel_timeline->is_clip_selected(c)) {
+ if (c != NULL && panel_timeline->is_clip_selected(c, true)) {
Ghost g;
g.clip = i;
g.trimming = (panel_timeline->trim_target > -1);
@@ -917,7 +944,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
for (int i=0;iclip_count();i++) {
Clip* c = sequence->get_clip(i);
- if (c != NULL && !panel_timeline->is_clip_selected(c)) {
+ if (c != NULL && !panel_timeline->is_clip_selected(c, true)) {
for (int j=0;jtrim_in_point) ? axis->timeline_in : axis->timeline_out;
@@ -946,18 +973,30 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
}
panel_timeline->repaint_timeline();
} else if (panel_timeline->splitting) {
- int track = panel_timeline->cursor_track;
- bool repaint = false;
- for (int i=0;iclip_count();i++) {
- Clip* clip = sequence->get_clip(i);
- if (clip != NULL && clip->track == track) {
- panel_timeline->split_clip_and_relink(i, panel_timeline->drag_frame_start, !alt);
- repaint = true;
- }
- }
+ int track_start = qMin(panel_timeline->cursor_track, panel_timeline->drag_track_start);
+ int track_end = qMax(panel_timeline->cursor_track, panel_timeline->drag_track_start);
+ int track_size = 1 + track_end - track_start;
+ panel_timeline->split_tracks.resize(track_size);
+ for (int i=0;isplit_tracks[i] = track_start + i;
+ }
- // redraw clips since we changed them
- if (repaint) panel_timeline->redraw_all_clips(true);
+ if (!alt) {
+ for (int i=0;idrag_frame_start, panel_timeline->split_tracks[i]);
+ if (clip_index > -1) {
+ Clip* c = sequence->get_clip(clip_index);
+ QVector tracks = panel_timeline->get_tracks_of_linked_clips(clip_index);
+ for (int j=0;j track_end) {
+ panel_timeline->split_tracks.append(tracks.at(j));
+ }
+ }
+ }
+ }
+ }
+ panel_timeline->repaint_timeline();
} else if (panel_timeline->rect_select_init) {
if (panel_timeline->rect_select_proc) {
panel_timeline->rect_select_w = event->pos().x() - panel_timeline->rect_select_x;
@@ -1307,11 +1346,25 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
}
}
+ // Draw splitting cursor
+ if (panel_timeline->splitting) {
+ for (int i=0;isplit_tracks.size();i++) {
+ if (is_track_visible(panel_timeline->split_tracks.at(i))) {
+ int cursor_x = panel_timeline->getScreenPointFromFrame(panel_timeline->drag_frame_start);
+ int cursor_y = getScreenPointFromTrack(panel_timeline->split_tracks.at(i));
+
+ p.setPen(QColor(64, 64, 64));
+ p.drawLine(cursor_x, cursor_y, cursor_x, cursor_y + panel_timeline->calculate_track_height(panel_timeline->cursor_track, -1));
+ }
+ }
+ }
+
// Draw playhead
p.setPen(Qt::red);
int playhead_x = panel_timeline->getScreenPointFromFrame(panel_timeline->playhead);
p.drawLine(playhead_x, rect().top(), playhead_x, rect().bottom());
+ // draw border
p.setPen(QColor(0, 0, 0, 64));
int edge_y = (bottom_align) ? rect().height()-1 : 0;
p.drawLine(0, edge_y, rect().width(), edge_y);
@@ -1331,7 +1384,7 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
p.setPen(Qt::gray);
p.drawLine(cursor_x, cursor_y, cursor_x, cursor_y + panel_timeline->calculate_track_height(panel_timeline->cursor_track, -1));
- }
+ }
}
}
}
diff --git a/ui/timelinewidget.h b/ui/timelinewidget.h
index fe85f2dcc..8e49e7327 100644
--- a/ui/timelinewidget.h
+++ b/ui/timelinewidget.h
@@ -14,6 +14,7 @@
struct Sequence;
struct Clip;
class Timeline;
+class TimelineAction;
class TimelineWidget : public QWidget
{