diff --git a/dialogs/replaceclipmediadialog.cpp b/dialogs/replaceclipmediadialog.cpp index 4bc74182e..9a7bbb11c 100644 --- a/dialogs/replaceclipmediadialog.cpp +++ b/dialogs/replaceclipmediadialog.cpp @@ -89,15 +89,9 @@ void ReplaceClipMediaDialog::replace() { c->clip_in = 0; } - int new_type = get_type_from_tree(new_item); c->media = new_media; - c->media_type = new_type; - - if (new_type == MEDIA_TYPE_FOOTAGE) { - // TODO: the media streams may be invalid, here we have a BASIC heuristic for getting the right ones that COULD BE BETTER - Media* casted_new_media = static_cast(new_media); - c->media_stream = (c->track < 0) ? casted_new_media->video_tracks.at(0)->file_index : casted_new_media->audio_tracks.at(0)->file_index; - } + c->media_type = get_type_from_tree(new_item); + c->replaced = true; c->refresh(); changed = true; diff --git a/io/media.cpp b/io/media.cpp index 8d5ce4399..b9c0e9e95 100644 --- a/io/media.cpp +++ b/io/media.cpp @@ -6,6 +6,8 @@ extern "C" { #include } +#include "project/clip.h" + Media::Media() : ready(false) {} Media::~Media() { diff --git a/io/media.h b/io/media.h index 105afa0af..42272a675 100644 --- a/io/media.h +++ b/io/media.h @@ -9,6 +9,7 @@ #include struct Sequence; +struct Clip; struct MediaStream { int file_index; @@ -39,7 +40,7 @@ struct Media { bool ready; long get_length_in_frames(double frame_rate); - MediaStream* get_stream_from_file_index(int index); + MediaStream* get_stream_from_file_index(int index); void reset(); }; diff --git a/io/previewgenerator.cpp b/io/previewgenerator.cpp index 7b26aab03..a81d2cec0 100644 --- a/io/previewgenerator.cpp +++ b/io/previewgenerator.cpp @@ -18,7 +18,15 @@ extern "C" { #include } -PreviewGenerator::PreviewGenerator(QTreeWidgetItem* i, Media* m) : QThread(0), fmt_ctx(NULL), item(i), media(m), retrieve_duration(false), contains_still_image(false) { +PreviewGenerator::PreviewGenerator(QTreeWidgetItem* i, Media* m, bool r) : + QThread(0), + fmt_ctx(NULL), + item(i), + media(m), + retrieve_duration(false), + contains_still_image(false), + replace(r) +{ connect(this, SIGNAL(finished()), this, SLOT(deleteLater())); } @@ -72,9 +80,9 @@ void PreviewGenerator::finalize_media() { media->ready = true; if (media->video_tracks.size() == 0) { - emit set_icon(ICON_TYPE_AUDIO); + emit set_icon(ICON_TYPE_AUDIO, replace); } else { - emit set_icon(ICON_TYPE_VIDEO); + emit set_icon(ICON_TYPE_VIDEO, replace); } if (!contains_still_image || media->audio_tracks.size() > 0) { @@ -290,7 +298,7 @@ void PreviewGenerator::run() { avformat_close_input(&fmt_ctx); } if (error) { - emit set_icon(ICON_TYPE_ERROR); + emit set_icon(ICON_TYPE_ERROR, replace); } else { item->setToolTip(0, QString()); } diff --git a/io/previewgenerator.h b/io/previewgenerator.h index a08839a3b..15b1e805f 100644 --- a/io/previewgenerator.h +++ b/io/previewgenerator.h @@ -16,10 +16,10 @@ class PreviewGenerator : public QThread { Q_OBJECT public: - PreviewGenerator(QTreeWidgetItem*, Media*); + PreviewGenerator(QTreeWidgetItem*, Media*, bool); void run(); signals: - void set_icon(int); + void set_icon(int, bool); private: void parse_media(); void generate_waveform(); @@ -29,6 +29,7 @@ private: AVFormatContext* fmt_ctx; bool retrieve_duration; bool contains_still_image; + bool replace; }; #endif // PREVIEWGENERATOR_H diff --git a/panels/project.cpp b/panels/project.cpp index 19d19baab..04c15d5e9 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -117,43 +117,12 @@ void Project::replace_selected_file() { } void Project::replace_media(QTreeWidgetItem* item, QString filename) { - Media* m = get_footage_from_tree(item); - - QStringList files; if (filename.isEmpty()) { - QString browse_file = QFileDialog::getOpenFileName(this, "Replace '" + item->text(0) + "'", "", "All Files (*)"); - if (!browse_file.isEmpty()) files.append(browse_file); - } else { - files.append(filename); + filename = QFileDialog::getOpenFileName(this, "Replace '" + item->text(0) + "'", "", "All Files (*)"); } - - if (files.size() > 0) { - // close any clips currently using this media - QVector all_sequences = list_all_project_sequences(); - for (int i=0;iclip_count();j++) { - Clip* c = s->get_clip(j); - if (c != NULL && c->media == m && c->open) { - close_clip(c); - c->cacher->wait(); - } - } - } - - process_file_list(NULL, files, NULL, item); - - for (int i=0;iclip_count();j++) { - Clip* c = s->get_clip(j); - if (c != NULL && c->media == m) { - c->refresh(); - } - } - } - - panel_timeline->redraw_all_clips(true); + if (!filename.isEmpty()) { + ReplaceMediaCommand* rmc = new ReplaceMediaCommand(item, filename); + undo_stack.push(rmc); } } @@ -191,12 +160,12 @@ void Project::new_sequence(TimelineAction *ta, Sequence *s, bool open, QTreeWidg project_changed = true; } -void Project::start_preview_generator(QTreeWidgetItem* item, Media* media) { +void Project::start_preview_generator(QTreeWidgetItem* item, Media* media, bool replacing) { // set up throbber animation MediaThrobber* throbber = new MediaThrobber(item); - PreviewGenerator* pg = new PreviewGenerator(item, media); - connect(pg, SIGNAL(set_icon(int)), throbber, SLOT(stop(int))); + PreviewGenerator* pg = new PreviewGenerator(item, media, replacing); + connect(pg, SIGNAL(set_icon(int, bool)), throbber, SLOT(stop(int, bool))); connect(pg, SIGNAL(finished()), pg, SLOT(deleteLater())); pg->start(QThread::LowPriority); } @@ -205,14 +174,6 @@ QString Project::get_file_name_from_path(const QString& path) { return path.mid(path.lastIndexOf('/')+1); } -void Project::import_file(QTreeWidgetItem* item, Media* m, QString file, QString imported_filename) { - m->url = file; - m->name = get_file_name_from_path(imported_filename); - - // generate waveform/thumbnail in another thread - start_preview_generator(item, m); -} - QTreeWidgetItem* Project::new_item() { QTreeWidgetItem* item = new QTreeWidgetItem(); item->setFlags(item->flags() | Qt::ItemIsEditable); @@ -370,10 +331,11 @@ void Project::process_file_list(bool recursive, QStringList& files, QTreeWidgetI QVector image_sequence_importassequence; QStringList image_sequence_formats = config.img_seq_formats.split("|"); + bool create_undo_action = (!recursive && replace == NULL); TimelineAction* ta; - if (!recursive) ta = new TimelineAction(); + if (create_undo_action) ta = new TimelineAction(); - for (int i=0;iadd_media(folder, parent); } else { parent->addChild(folder); @@ -483,20 +445,27 @@ void Project::process_file_list(bool recursive, QStringList& files, QTreeWidgetI m = new Media(); } - import_file(item, m, file, files.at(i)); + m->url = file; + m->name = get_file_name_from_path(files.at(i)); + + // generate waveform/thumbnail in another thread + start_preview_generator(item, m, replace != NULL); + set_footage_of_tree(item, m); - if (recursive) { - parent->addChild(item); - } else { - ta->add_media(item, parent); + if (replace == NULL) { + if (create_undo_action) { + ta->add_media(item, parent); + } else { + parent->addChild(item); + } } imported = true; } } } - if (!recursive) { + if (create_undo_action) { if (imported) { undo_stack.push(ta); } else { @@ -520,7 +489,7 @@ void Project::import_dialog() { if (fd.exec()) { QStringList files = fd.selectedFiles(); - process_file_list(NULL, files, get_selected_folder(), NULL); + process_file_list(false, files, get_selected_folder(), NULL); } } @@ -753,7 +722,7 @@ bool Project::load_worker(QFile& f, QXmlStreamReader& stream, int type) { } // analyze media to see if it's the same - start_preview_generator(item, m); + start_preview_generator(item, m, true); loaded_media.append(m); } @@ -1285,7 +1254,7 @@ void MediaThrobber::animation_update() { animation++; } -void MediaThrobber::stop(int icon_type) { +void MediaThrobber::stop(int icon_type, bool replace) { animator.stop(); switch (icon_type) { @@ -1294,22 +1263,20 @@ void MediaThrobber::stop(int icon_type) { case ICON_TYPE_ERROR: item->setIcon(0, QIcon::fromTheme("dialog-error")); break; } - // re-init all effects on all clips + // refresh all clips QVector sequences = panel_project->list_all_project_sequences(); for (int i=0;iclip_count();j++) { Clip* c = s->get_clip(j); - if (c != NULL) { - for (int k=0;keffects.size();k++) { - c->effects.at(k)->init(); - } + if (c != NULL) { + c->refresh(); } } - } + } // redraw clips - panel_timeline->redraw_all_clips(false); + panel_timeline->redraw_all_clips(replace); panel_project->source_table->viewport()->update(); deleteLater(); diff --git a/panels/project.h b/panels/project.h index f10514fd3..f21daaa61 100644 --- a/panels/project.h +++ b/panels/project.h @@ -51,8 +51,7 @@ public: explicit Project(QWidget *parent = 0); ~Project(); bool is_focused(); - void clear(); - void import_file(QTreeWidgetItem* item, Media* m, QString url, QString imported_filename); + void clear(); void new_sequence(TimelineAction* ta, Sequence* s, bool open, QTreeWidgetItem* parent); QString get_next_sequence_name(); void delete_media(QTreeWidgetItem* item); @@ -96,7 +95,7 @@ private: QTreeWidgetItem* find_loaded_folder_by_id(int id); void add_recent_project(QString url); void get_all_media_from_table(QList items, QList& list, int type); - void start_preview_generator(QTreeWidgetItem* item, Media* media); + void start_preview_generator(QTreeWidgetItem* item, Media* media, bool replacing); void list_all_sequences_worker(QVector* list, QTreeWidgetItem* parent); QString get_file_name_from_path(const QString &path); private slots: @@ -109,7 +108,7 @@ class MediaThrobber : public QObject { public: MediaThrobber(QTreeWidgetItem*); public slots: - void stop(int); + void stop(int, bool replace); private slots: void animation_update(); private: diff --git a/project/clip.cpp b/project/clip.cpp index 65bea6ae2..facf772c8 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -26,6 +26,7 @@ Clip::Clip(Sequence* s) : opening_transition(NULL), closing_transition(NULL), pkt(new AVPacket()), + replaced(false), texture(NULL) { reset(); @@ -106,6 +107,13 @@ void Clip::reset_audio() { } void Clip::refresh() { + // validates media if it was replaced + if (replaced && media_type == MEDIA_TYPE_FOOTAGE) { + Media* m = static_cast(media); + media_stream = (track < 0) ? m->video_tracks.at(0)->file_index : m->audio_tracks.at(0)->file_index; + } + replaced = false; + // reinitializes all effects... just in case for (int i=0;iinit(); diff --git a/project/clip.h b/project/clip.h index 9c451f9c1..5932d7d19 100644 --- a/project/clip.h +++ b/project/clip.h @@ -82,6 +82,7 @@ struct Clip bool reached_end; bool open; bool finished_opening; + bool replaced; // caching functions bool multithreaded; diff --git a/project/undo.cpp b/project/undo.cpp index 9953572b5..f2e2cee17 100644 --- a/project/undo.cpp +++ b/project/undo.cpp @@ -13,6 +13,8 @@ #include "playback/playback.h" #include "ui/sourcetable.h" #include "effects/effects.h" +#include "io/media.h" +#include "playback/cacher.h" QUndoStack undo_stack; @@ -506,3 +508,45 @@ void CheckboxCommand::redo() { box->setChecked(checked); } } + +ReplaceMediaCommand::ReplaceMediaCommand(QTreeWidgetItem* i, QString s) : + item(i), + new_filename(s), + old_project_changed(project_changed) +{ + media = get_footage_from_tree(item); + old_filename = media->url; +} + +void ReplaceMediaCommand::replace(QString& filename) { + // close any clips currently using this media + QVector all_sequences = panel_project->list_all_project_sequences(); + for (int i=0;iclip_count();j++) { + Clip* c = s->get_clip(j); + if (c != NULL && c->media == media && c->open) { + close_clip(c); + c->cacher->wait(); + c->replaced = true; + } + } + } + + // replace media + QStringList files; + files.append(filename); + panel_project->process_file_list(false, files, NULL, item); +} + +void ReplaceMediaCommand::undo() { + replace(old_filename); + + project_changed = old_project_changed; +} + +void ReplaceMediaCommand::redo() { + replace(new_filename); + + project_changed = true; +} diff --git a/project/undo.h b/project/undo.h index 12deb4131..98f868144 100644 --- a/project/undo.h +++ b/project/undo.h @@ -5,6 +5,7 @@ class QTreeWidgetItem; class QCheckBox; struct Clip; struct Sequence; +struct Media; #include #include @@ -115,4 +116,18 @@ private: bool done; }; +class ReplaceMediaCommand : public QUndoCommand { +public: + ReplaceMediaCommand(QTreeWidgetItem*, QString); + void undo(); + void redo(); +private: + QTreeWidgetItem *item; + QString old_filename; + QString new_filename; + bool old_project_changed; + Media* media; + void replace(QString& filename); +}; + #endif // UNDO_H diff --git a/ui/sourcetable.cpp b/ui/sourcetable.cpp index 7b71e7f61..56adfe77c 100644 --- a/ui/sourcetable.cpp +++ b/ui/sourcetable.cpp @@ -169,7 +169,7 @@ void SourceTable::dropEvent(QDropEvent* event) { if (drop_item != NULL && get_type_from_tree(drop_item) == MEDIA_TYPE_FOLDER) { parent = drop_item; } - panel_project->process_file_list(NULL, paths, parent, NULL); + panel_project->process_file_list(false, paths, parent, NULL); } } event->acceptProposedAction();