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 {