From a614803b6c36c2731c180ce7df2514cad0fed18a Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 15 Jul 2018 12:31:10 +0100 Subject: [PATCH] many project window enhancements --- mainwindow.cpp | 13 ++- panels/project.cpp | 195 +++++++++++++++++++++++++++++++----------- panels/project.h | 6 +- panels/timeline.cpp | 30 +++---- project/effect.cpp | 4 +- project/undo.cpp | 177 +++++++++++++++++++++++++++----------- project/undo.h | 47 ++++++---- ui/labelslider.cpp | 4 +- ui/sourcetable.cpp | 80 +++++++++++++---- ui/sourcetable.h | 13 +++ ui/timelinewidget.cpp | 12 +-- 11 files changed, 417 insertions(+), 164 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 9fc14d4a9..ec9682469 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -353,6 +353,7 @@ void MainWindow::on_action_Open_Project_triggered() if (!fn.isEmpty() && can_close_project()) { project_url = fn; panel_project->load_project(); + undo_stack.clear(); } } @@ -362,6 +363,7 @@ void MainWindow::on_actionProject_triggered() project_url.clear(); project_changed = false; panel_project->new_project(); + undo_stack.clear(); } } @@ -595,8 +597,15 @@ void MainWindow::fileMenu_About_To_Be_Shown() { } void MainWindow::load_recent_project() { - if (can_close_project()) { - project_url = recent_projects.at(static_cast(sender())->data().toInt()); + int index = static_cast(sender())->data().toInt(); + QString recent_url = recent_projects.at(index); + if (!QFile::exists(recent_url)) { + if (QMessageBox::question(this, "Missing recent project", "The project '" + recent_url + "' no longer exists. Would you like to remove it from the recent projects list?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { + recent_projects.removeAt(index); + panel_project->save_recent_projects(); + } + } else if (can_close_project()) { + project_url = recent_url; panel_project->load_project(); } } diff --git a/panels/project.cpp b/panels/project.cpp index b98716f7a..f4349bda9 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -12,6 +12,7 @@ #include "project/effect.h" #include "effects/transition.h" #include "io/previewgenerator.h" +#include "project/undo.h" #include "mainwindow.h" #include @@ -203,46 +204,100 @@ QTreeWidgetItem* Project::new_folder() { return item; } +void Project::get_media_from_table(QList items, QList& list, int search_type) { + for (int i=0;i children; + for (int j=0;jchildCount();j++) { + children.append(item->child(j)); + } + get_media_from_table(children, list, search_type); + } else if (search_type == type) { + list.append(item); + } + } +} + void Project::delete_selected_media() { + TimelineAction* ta = new TimelineAction(); QList items = ui->treeWidget->selectedItems(); bool remove = true; bool redraw = false; // check if media is in use - for (int i=0;iclip_count();j++) { - Clip* c = sequence->get_clip(j); - if (c != NULL && c->media == m) { - remove = false; - break; - } - } - if (!remove) { - QMessageBox confirm(this); - confirm.setWindowTitle("Delete media in use?"); - confirm.setText("The media '" + m->name + "' is currently used in the sequence. Deleting it will remove all instances in the sequence. Are you sure you want to do this?"); - QAbstractButton* yes_button = confirm.addButton(QMessageBox::Yes); - QAbstractButton* skip_button = confirm.addButton("Skip", QMessageBox::NoRole); - QAbstractButton* abort_button = confirm.addButton(QMessageBox::Abort); - confirm.exec(); - if (confirm.clickedButton() == yes_button) { - // remove all clips referencing this media - for (int j=0;jclip_count();j++) { - if (sequence->get_clip(j)->media == m) { - sequence->replace_clip(j, NULL); + QVector parents; + QList sequence_items; + QList all_top_level_items; + for (int i=0;itreeWidget->topLevelItemCount();i++) { + all_top_level_items.append(ui->treeWidget->topLevelItem(i)); + } + get_media_from_table(all_top_level_items, sequence_items, MEDIA_TYPE_SEQUENCE); // find all sequences in project + if (sequence_items.size() > 0) { + QList media_items; + get_media_from_table(items, media_items, MEDIA_TYPE_FOOTAGE); + for (int i=0;iclip_count();k++) { + Clip* c = s->get_clip(k); + if (c != NULL && c->media == media) { + if (!confirm_delete) { + // we found a reference, so we know we'll need to ask if the user wants to delete it + QMessageBox confirm(this); + confirm.setWindowTitle("Delete media in use?"); + confirm.setText("The media '" + media->name + "' is currently used in '" + s->name + "'. Deleting it will remove all instances in the sequence. Are you sure you want to do this?"); + QAbstractButton* yes_button = confirm.addButton(QMessageBox::Yes); + QAbstractButton* skip_button = confirm.addButton("Skip", QMessageBox::NoRole); + QAbstractButton* abort_button = confirm.addButton(QMessageBox::Abort); + confirm.exec(); + if (confirm.clickedButton() == yes_button) { + // remove all clips referencing this media + confirm_delete = true; + redraw = true; + } else if (confirm.clickedButton() == skip_button) { + // remove media item and any folders containing it from the remove list + QTreeWidgetItem* parent = item; + while (parent != NULL) { + parents.append(parent); + + // re-add item's siblings + for (int m=0;mchildCount();m++) { + QTreeWidgetItem* child = parent->child(m); + bool found = false; + for (int n=0;nparent(); + } + + j = sequence_items.size(); + k = s->clip_count(); + } else if (confirm.clickedButton() == abort_button) { + // break out of loop + i = media_items.size(); + j = sequence_items.size(); + k = s->clip_count(); + + remove = false; + } + } + if (confirm_delete) { + ta->delete_clip(s, k); } } - remove = true; - redraw = true; - } else if (confirm.clickedButton() == skip_button) { - items.removeAt(i); - i--; - remove = true; - } else if (confirm.clickedButton() == abort_button) { - break; } } } @@ -250,20 +305,38 @@ void Project::delete_selected_media() { // remove if (remove) { - for (int i=0;iredraw_all_clips(true); + for (int i=0;idelete_media(items.at(i)); + } + undo_stack.push(ta); + + // redraw clips + if (redraw) { + panel_timeline->redraw_all_clips(true); + } + } else { + delete ta; } } void Project::process_file_list(QStringList& files) { + bool imported = false; + TimelineAction* ta = new TimelineAction(); for (int i=0;itreeWidget->addTopLevelItem(import_file(file)); + ta->add_media(import_file(file)); + imported = true; + } + if (imported) { + undo_stack.push(ta); + } else { + delete ta; } } @@ -428,6 +507,8 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) { folder->setData(0, Qt::UserRole + 3, attr.value().toInt()); } else if (attr.name() == "name") { folder->setText(0, attr.value().toString()); + } else if (attr.name() == "parent") { + folder->setData(0, Qt::UserRole + 4, attr.value().toInt()); } } loaded_folders.append(folder); @@ -644,6 +725,16 @@ void Project::load_project() { // load media if (cont) { + // since folders loaded correctly, organize them appropriately + for (int i=0;idata(0, Qt::UserRole + 4).toInt(); + if (parent > 0) { + ui->treeWidget->takeTopLevelItem(ui->treeWidget->indexOfTopLevelItem(folder)); + find_loaded_folder_by_id(parent)->addChild(folder); + } + } + cont = load_worker(file, stream, MEDIA_TYPE_FOOTAGE); } @@ -668,8 +759,6 @@ void Project::load_project() { } file.close(); - - add_recent_project(project_url); } void Project::save_folder(QXmlStreamWriter& stream, QTreeWidgetItem* parent, int type) { @@ -679,14 +768,20 @@ void Project::save_folder(QXmlStreamWriter& stream, QTreeWidgetItem* parent, int QTreeWidgetItem* item = root ? ui->treeWidget->topLevelItem(i) : parent->child(i); int item_type = get_type_from_tree(item); if (item_type == MEDIA_TYPE_FOLDER) { - if (type == MEDIA_TYPE_FOLDER) { + if (type == SAVE_SET_FOLDER_IDS) { + item->setData(0, Qt::UserRole + 3, folder_id); // saves a temporary ID for matching in the project file + folder_id++; + } else if (type == MEDIA_TYPE_FOLDER) { // if we're saving folders, save the folder stream.writeStartElement("folder"); stream.writeAttribute("name", item->text(0)); - stream.writeAttribute("id", QString::number(folder_id)); - item->setData(0, Qt::UserRole + 3, folder_id); // saves a temporary ID for matching in the project file - stream.writeEndElement(); - folder_id++; + stream.writeAttribute("id", QString::number(item->data(0, Qt::UserRole + 3).toInt())); + if (item->parent() == NULL) { + stream.writeAttribute("parent", "0"); + } else { + stream.writeAttribute("parent", QString::number(item->parent()->data(0, Qt::UserRole + 3).toInt())); + } + stream.writeEndElement(); } save_folder(stream, item, type); } else if (type == item_type) { @@ -777,6 +872,8 @@ void Project::save_project() { stream.writeTextElement("version", SAVE_VERSION); + save_folder(stream, NULL, SAVE_SET_FOLDER_IDS); + stream.writeStartElement("folders"); // folders save_folder(stream, NULL, MEDIA_TYPE_FOLDER); stream.writeEndElement(); // folders @@ -795,8 +892,6 @@ void Project::save_project() { file.close(); - add_recent_project(project_url); - project_changed = false; } diff --git a/panels/project.h b/panels/project.h index 0bc4c4cc4..773f4d23b 100644 --- a/panels/project.h +++ b/panels/project.h @@ -14,7 +14,7 @@ class QXmlStreamWriter; class QXmlStreamReader; class QFile; -#define SAVE_VERSION "180711" +#define SAVE_VERSION "180715" #define MEDIA_TYPE_FOOTAGE 0 #define MEDIA_TYPE_SEQUENCE 1 @@ -22,6 +22,7 @@ class QFile; #define MEDIA_TYPE_SOLID 3 #define LOAD_TYPE_VERSION 69 +#define SAVE_SET_FOLDER_IDS 70 namespace Ui { class Project; @@ -62,6 +63,7 @@ public: Sequence* get_sequence_from_tree(QTreeWidgetItem* item); void set_sequence_of_tree(QTreeWidgetItem* item, Sequence* sequence); void set_item_to_folder(QTreeWidgetItem* item); + void save_recent_projects(); SourceTable* source_table; private: @@ -76,7 +78,7 @@ private: QVector loaded_media; QTreeWidgetItem* find_loaded_folder_by_id(int id); void add_recent_project(QString url); - void save_recent_projects(); + void get_media_from_table(QList items, QList& list, int type); private slots: void rename_media(QTreeWidgetItem* item, int column); void clear_recent_projects(); diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 399afd2c0..7593cf5d6 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -354,8 +354,8 @@ void Timeline::ripple(TimelineAction* ta, long ripple_point, long ripple_length) for (int i=0;iclip_count();i++) { Clip* c = sequence->get_clip(i); if (c != NULL && c->timeline_in >= ripple_point) { - ta->increase_timeline_in(i, ripple_length); - ta->increase_timeline_out(i, ripple_length); + ta->increase_timeline_in(sequence, i, ripple_length); + ta->increase_timeline_out(sequence, i, ripple_length); } } @@ -415,7 +415,7 @@ Clip* Timeline::split_clip(TimelineAction* ta, int p, long frame) { 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); + ta->set_timeline_out(sequence, p, frame); post->timeline_in = frame; post->clip_in = pre->clip_in + (frame - pre->timeline_in); @@ -490,7 +490,7 @@ bool Timeline::split_clip_and_relink(TimelineAction* ta, int clip, long frame, b relink_clips_using_ids(pre_clips, post_clips); } - ta->add_clips(post_clips); + ta->add_clips(sequence, post_clips); return true; } } @@ -540,14 +540,14 @@ void Timeline::delete_areas_and_relink(TimelineAction* ta, QVector& a 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 - ta->delete_clip(j); + ta->delete_clip(sequence, 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(); - ta->set_timeline_out(j, s.in); + ta->set_timeline_out(sequence, j, s.in); post->timeline_in = s.out; post->clip_in = c->clip_in + (s.in - c->timeline_in) + (s.out - s.in); @@ -555,17 +555,17 @@ void Timeline::delete_areas_and_relink(TimelineAction* ta, QVector& a post_clips.append(post); } else if (c->timeline_in < s.in && c->timeline_out > s.in) { // only out point is in deletion area - ta->set_timeline_out(j, s.in); + ta->set_timeline_out(sequence, j, s.in); } else if (c->timeline_in < s.out && c->timeline_out > s.out) { // only in point is in deletion area - ta->increase_clip_in(j, s.out - c->timeline_in); - ta->set_timeline_in(j, s.out); + ta->increase_clip_in(sequence, j, s.out - c->timeline_in); + ta->set_timeline_in(sequence, j, s.out); } } } } relink_clips_using_ids(pre_clips, post_clips); - ta->add_clips(post_clips); + ta->add_clips(sequence, post_clips); } void Timeline::copy(bool del) { @@ -683,7 +683,7 @@ void Timeline::paste() { } // ADAPT - ta->add_clips(pasted_clips); + ta->add_clips(sequence, pasted_clips); undo_stack.push(ta); @@ -723,7 +723,7 @@ bool Timeline::split_selection(TimelineAction* ta) { split_B->timeline_in = s.out; secondary_post_splits.append(split_B); - ta->set_timeline_out(j, s.in); + ta->set_timeline_out(sequence, j, s.in); split = true; } else { Clip* post_a = split_clip(ta, j, s.in); @@ -752,8 +752,8 @@ bool Timeline::split_selection(TimelineAction* ta) { // 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); + ta->add_clips(sequence, post_splits); + ta->add_clips(sequence, secondary_post_splits); return true; } @@ -784,7 +784,7 @@ 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); + ta->add_clips(sequence, post_clips); } else { // split a selection if not split_selected = split_selection(ta); diff --git a/project/effect.cpp b/project/effect.cpp index cf5c22007..bec57d341 100644 --- a/project/effect.cpp +++ b/project/effect.cpp @@ -33,8 +33,8 @@ bool Effect::is_enabled() { } Effect* Effect::copy(Clip*) {return NULL;} -void Effect::load(QXmlStreamReader* stream) {} -void Effect::save(QXmlStreamWriter* stream) {} +void Effect::load(QXmlStreamReader*) {} +void Effect::save(QXmlStreamWriter*) {} /*void Effect::import_values(float* val, int count) { qDebug() << "[ERROR] import_values MUST be overridden"; } diff --git a/project/undo.cpp b/project/undo.cpp index 5137a49af..e2b8e2581 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -1,6 +1,14 @@ #include "undo.h" #include +#include + +#include "project/clip.h" +#include "project/sequence.h" +#include "panels/panels.h" +#include "panels/project.h" +#include "playback/playback.h" +#include "ui/sourcetable.h" QUndoStack undo_stack; @@ -24,6 +32,15 @@ TimelineAction::~TimelineAction() { for (int i=0;idelete_media(deleted_media.at(i)); + delete deleted_media.at(i); + } + } else { + /*for (int i=0;idelete_media(media_to_add.at(i)); + delete media_to_add.at(i); + }*/ } } @@ -36,103 +53,130 @@ void TimelineAction::offset_links(QVector& clips, int offset) { } } -void TimelineAction::add_clips(QVector& add) { +void TimelineAction::add_clips(Sequence* s, QVector& add) { offset_links(add, clips_to_add.size()); clips_to_add.append(add); + for (int i=0;iget_clip(clip)->timeline_in, value); +void TimelineAction::set_timeline_in(Sequence* s, int clip, long value) { + new_action(s, TA_IN, clip, s->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_timeline_out(Sequence* s, int clip, long value) { + new_action(s, TA_OUT, clip, s->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_clip_in(Sequence* s, int clip, long value) { + new_action(s, TA_CLIP_IN, clip, s->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::set_track(Sequence* s, int clip, int value) { + new_action(s, TA_TRACK, clip, s->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_in(Sequence* s, int clip, long value) { + new_action(s, 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_timeline_out(Sequence* s, int clip, long value) { + new_action(s, 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_clip_in(Sequence* s, int clip, long value) { + new_action(s, 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::increase_track(Sequence* s, int clip, int value) { + new_action(s, TA_ADD_TRACK, clip, 0, value); } -void TimelineAction::delete_clip(int clip) { - new_action(TA_DELETE, clip, 0, 0); +void TimelineAction::delete_clip(Sequence* s, int clip) { + new_action(s, TA_DELETE, clip, 0, 0); +} + +void TimelineAction::add_media(QTreeWidgetItem* item) { + media_to_add.append(item); +} + +void TimelineAction::delete_media(QTreeWidgetItem* item) { + deleted_media.append(item); } void TimelineAction::undo() { + for (int i=0;isource_table->addTopLevelItem(item); + } else { + parent->addChild(item); + } + } + for (int i=0;iget_clip(clips.at(i))->timeline_in = old_values.at(i); + sequences.at(i)->get_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); + sequences.at(i)->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); + sequences.at(i)->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); + sequences.at(i)->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))); + sequences.at(i)->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); + sequences.at(i)->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); + sequences.at(i)->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); + sequences.at(i)->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); + sequences.at(i)->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)); + removed_link_from_sequence.at(i)->get_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); + sequence_to_add_clips_to.at(i)->destroy_clip(added_indexes.at(i)-delete_count, true); delete_count++; } } + // remove any added media + if (media_to_add.size() > 0) { + for (int i=0;isource_table->takeTopLevelItem(panel_project->source_table->indexOfTopLevelItem(media_to_add.at(i))); + } + } + done = false; } @@ -141,56 +185,60 @@ void TimelineAction::redo() { removed_link_to.clear(); deleted_clips.clear(); deleted_clips_indices.clear(); + deleted_clip_sequences.clear(); added_indexes.clear(); + deleted_media_parents.clear(); + removed_link_from_sequence.clear(); for (int i=0;iget_clip(clips.at(i))->timeline_in = new_values.at(i); + sequences.at(i)->get_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); + sequences.at(i)->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); + sequences.at(i)->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); + sequences.at(i)->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.append(sequences.at(i)->get_clip(clips.at(i))); deleted_clips_indices.append(clips.at(i)); - sequence->replace_clip(clips.at(i), NULL); + deleted_clip_sequences.append(sequences.at(i)); + sequences.at(i)->replace_clip(clips.at(i), NULL); } break; case TA_ADD_IN: { - sequence->get_clip(clips.at(i))->timeline_in += new_values.at(i); + sequences.at(i)->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); + sequences.at(i)->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); + sequences.at(i)->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); + sequences.at(i)->get_clip(clips.at(i))->track += new_values.at(i); } break; } @@ -198,11 +246,13 @@ void TimelineAction::redo() { // remove any potential link references from deleted clips for (int i=0;iclip_count();j++) { - Clip* c = sequence->get_clip(j); + Sequence* s = deleted_clip_sequences.at(i); + for (int j=0;jclip_count();j++) { + Clip* c = s->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_sequence.append(s); removed_link_from.append(j); removed_link_to.append(deleted_clips_indices.at(i)); c->linked.removeAt(k); @@ -215,15 +265,46 @@ void TimelineAction::redo() { // add any new clips if (clips_to_add.size() > 0) { - int link_offset = sequence->clip_count(); + QVector copies; 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; + copy->linked[j] = original->linked.at(j) + sequence_to_add_clips_to.at(i)->clip_count(); } - added_indexes.append(sequence->add_clip(copy)); + copies.append(copy); + } + for (int i=0;iadd_clip(copies.at(i))); + } + } + + // delete media + if (deleted_media.size() > 0) { + for (int i=0;iparent()); + + // if we're deleting the open sequence, close it + if (panel_project->get_type_from_tree(item) == MEDIA_TYPE_SEQUENCE) { + if (panel_project->get_sequence_from_tree(item) == sequence) { + set_sequence(NULL); + } + } + + if (item->parent() == NULL) { + panel_project->source_table->takeTopLevelItem(panel_project->source_table->indexOfTopLevelItem(item)); + } else { + item->parent()->removeChild(item); + } + } + } + + // add any new media + if (media_to_add.size() > 0) { + for (int i=0;isource_table->addTopLevelItem(media_to_add.at(i)); } } diff --git a/project/undo.h b/project/undo.h index fbf49c72b..7e395ff10 100644 --- a/project/undo.h +++ b/project/undo.h @@ -1,32 +1,38 @@ #ifndef UNDO_H #define UNDO_H +class QTreeWidgetItem; +class Clip; +class Sequence; + #include #include #include -#include "project/clip.h" -#include "project/sequence.h" - 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 add_clips(Sequence* s, QVector& add); + void set_timeline_in(Sequence* s, int clip, long value); + void increase_timeline_in(Sequence* s, int clip, long value); + void set_timeline_out(Sequence* s, int clip, long value); + void increase_timeline_out(Sequence* s, int clip, long value); + void set_clip_in(Sequence* s, int clip, long value); + void increase_clip_in(Sequence* s, int clip, long value); + void set_track(Sequence* s, int clip, int value); + void increase_track(Sequence* s, int clip, int value); + void delete_clip(Sequence* s, int clip); + void add_media(QTreeWidgetItem* item); + void delete_media(QTreeWidgetItem* item); void undo(); void redo(); private: + bool done; + + QVector sequences; QVector actions; QVector clips; QVector old_values; @@ -34,18 +40,23 @@ private: QVector deleted_clips; QVector deleted_clips_indices; + QVector deleted_clip_sequences; + QVector sequence_to_add_clips_to; 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_sequence; QVector removed_link_from; QVector removed_link_to; - bool done; + QVector deleted_media; + QVector deleted_media_parents; + + QVector media_to_add; + + void new_action(Sequence* s, int action, int clip, long old_val, long new_val); + void offset_links(QVector& clips, int offset); }; #endif // UNDO_H diff --git a/ui/labelslider.cpp b/ui/labelslider.cpp index c4b637da2..8a7584d41 100644 --- a/ui/labelslider.cpp +++ b/ui/labelslider.cpp @@ -60,7 +60,7 @@ void LabelSlider::mousePressEvent(QMouseEvent *ev) { } } -void LabelSlider::mouseMoveEvent(QMouseEvent *ev) { +void LabelSlider::mouseMoveEvent(QMouseEvent*) { if (drag_start) { set_value(internal_value + (cursor().pos().x()-drag_start_x) + (drag_start_y-cursor().pos().y())); cursor().setPos(drag_start_x, drag_start_y); @@ -68,7 +68,7 @@ void LabelSlider::mouseMoveEvent(QMouseEvent *ev) { } } -void LabelSlider::mouseReleaseEvent(QMouseEvent *ev) { +void LabelSlider::mouseReleaseEvent(QMouseEvent*) { if (drag_start) { qApp->restoreOverrideCursor(); drag_start = false; diff --git a/ui/sourcetable.cpp b/ui/sourcetable.cpp index ba28406b8..6ab1f1c46 100644 --- a/ui/sourcetable.cpp +++ b/ui/sourcetable.cpp @@ -6,6 +6,7 @@ #include "panels/viewer.h" #include "panels/panels.h" #include "playback/playback.h" +#include "project/undo.h" #include #include @@ -71,6 +72,7 @@ void SourceTable::dragMoveEvent(QDragMoveEvent *event) { void SourceTable::dropEvent(QDropEvent* event) { const QMimeData* mimeData = event->mimeData(); if (mimeData->hasUrls()) { + // drag files in from outside QList urls = mimeData->urls(); if (!urls.isEmpty()) { QStringList paths; @@ -82,33 +84,73 @@ void SourceTable::dropEvent(QDropEvent* event) { } event->acceptProposedAction(); } else { - QTreeWidgetItem* item = itemAt(event->pos()); - if (item == NULL || (item != NULL && panel_project->get_type_from_tree(item) == MEDIA_TYPE_FOLDER)) { - QList items = selectedItems(); - for (int i=0;i move_items; + QTreeWidgetItem* drop_item = itemAt(event->pos()); + // if we dragged to the root OR dragged to a folder + if (drop_item == NULL || (drop_item != NULL && panel_project->get_type_from_tree(drop_item) == MEDIA_TYPE_FOLDER)) { + QList selected_items = selectedItems(); + for (int i=0;iparent() == NULL) { - takeTopLevelItem(indexOfTopLevelItem(s)); - } else { + if (s->parent() != NULL) { // if child belongs to a selected parent, assume the user is just moving the parent and ignore the child - for (int j=0;jparent() == items.at(j)) { - ignore = true; - break; + QTreeWidgetItem* par = s->parent(); + while (par != NULL) { + for (int j=0;jparent(); } - if (!ignore) s->parent()->takeChild(s->parent()->indexOfChild(s)); } if (!ignore) { - if (item == NULL) { - addTopLevelItem(s); - } else { - item->addChild(s); - } + move_items.append(s); } } - project_changed = true; + if (move_items.size() > 0) { + MediaMove* mm = new MediaMove(this); + mm->to = drop_item; + mm->items = move_items; + undo_stack.push(mm); + project_changed = true; + } + } + } +} + +MediaMove::MediaMove(SourceTable *s) : table(s) {} + +void MediaMove::undo() { + for (int i=0;itakeTopLevelItem(table->indexOfTopLevelItem(items.at(i))); + } else { + to->removeChild(items.at(i)); + } + if (froms.at(i) == NULL) { + table->addTopLevelItem(items.at(i)); + } else { + froms.at(i)->addChild(items.at(i)); + } + } +} + +void MediaMove::redo() { + for (int i=0;iparent(); + froms.append(parent); + if (parent == NULL) { + table->takeTopLevelItem(table->indexOfTopLevelItem(items.at(i))); + } else { + parent->removeChild(items.at(i)); + } + if (to == NULL) { + table->addTopLevelItem(items.at(i)); + } else { + to->addChild(items.at(i)); } } } diff --git a/ui/sourcetable.h b/ui/sourcetable.h index 50a926886..19f7bc22a 100644 --- a/ui/sourcetable.h +++ b/ui/sourcetable.h @@ -3,6 +3,7 @@ #include #include +#include class Project; @@ -26,4 +27,16 @@ private slots: void stop_rename_timer(); }; +class MediaMove : public QUndoCommand { +public: + MediaMove(SourceTable* s); + QVector items; + QTreeWidgetItem* to; + void undo(); + void redo(); +private: + QVector froms; + SourceTable* table; +}; + #endif // SOURCETABLE_H diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 630ff39ff..a9b667181 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -188,7 +188,7 @@ void TimelineWidget::dropEvent(QDropEvent* event) { } } - ta->add_clips(added_clips); + ta->add_clips(sequence, added_clips); panel_timeline->ghosts.clear(); panel_timeline->importing = false; @@ -388,7 +388,7 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { panel_timeline->relink_clips_using_ids(old_clips, new_clips); for (int i=0;iadd_clips(new_clips); + ta->add_clips(sequence, new_clips); } } } else { @@ -417,10 +417,10 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { Ghost& g = panel_timeline->ghosts[i]; // step 3 - move clips - 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); + ta->increase_timeline_in(sequence, g.clip, g.in - g.old_in); + ta->increase_timeline_out(sequence, g.clip, g.out - g.old_out); + ta->increase_track(sequence, g.clip, g.track - g.old_track); + ta->increase_clip_in(sequence, g.clip, g.clip_in - g.old_clip_in); } }