improved replace media and made it undoable
This commit is contained in:
@@ -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<Media*>(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;
|
||||
|
||||
@@ -6,6 +6,8 @@ extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
|
||||
#include "project/clip.h"
|
||||
|
||||
Media::Media() : ready(false) {}
|
||||
|
||||
Media::~Media() {
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@
|
||||
#include <QPixmap>
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
|
||||
+12
-4
@@ -18,7 +18,15 @@ extern "C" {
|
||||
#include <libswresample/swresample.h>
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+32
-65
@@ -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<Sequence*> all_sequences = list_all_project_sequences();
|
||||
for (int i=0;i<all_sequences.size();i++) {
|
||||
Sequence* s = all_sequences.at(i);
|
||||
for (int j=0;j<s->clip_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;i<all_sequences.size();i++) {
|
||||
Sequence* s = all_sequences.at(i);
|
||||
for (int j=0;j<s->clip_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<bool> 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;i<files.count();i++) {
|
||||
for (int i=0;i<files.size();i++) {
|
||||
if (QFileInfo(files.at(i)).isDir()) {
|
||||
QString folder_name = get_file_name_from_path(files.at(i));
|
||||
QTreeWidgetItem* folder = new_folder(folder_name);
|
||||
@@ -390,7 +352,7 @@ void Project::process_file_list(bool recursive, QStringList& files, QTreeWidgetI
|
||||
|
||||
process_file_list(true, subdir_filenames, folder, NULL);
|
||||
|
||||
if (!recursive) {
|
||||
if (create_undo_action) {
|
||||
ta->add_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<Sequence*> sequences = panel_project->list_all_project_sequences();
|
||||
for (int i=0;i<sequences.size();i++) {
|
||||
Sequence* s = sequences.at(i);
|
||||
for (int j=0;j<s->clip_count();j++) {
|
||||
Clip* c = s->get_clip(j);
|
||||
if (c != NULL) {
|
||||
for (int k=0;k<c->effects.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();
|
||||
|
||||
+3
-4
@@ -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<QTreeWidgetItem*> items, QList<QTreeWidgetItem*>& 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<Sequence*>* 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:
|
||||
|
||||
@@ -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);
|
||||
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;i<effects.size();i++) {
|
||||
effects.at(i)->init();
|
||||
|
||||
@@ -82,6 +82,7 @@ struct Clip
|
||||
bool reached_end;
|
||||
bool open;
|
||||
bool finished_opening;
|
||||
bool replaced;
|
||||
|
||||
// caching functions
|
||||
bool multithreaded;
|
||||
|
||||
@@ -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<Sequence*> all_sequences = panel_project->list_all_project_sequences();
|
||||
for (int i=0;i<all_sequences.size();i++) {
|
||||
Sequence* s = all_sequences.at(i);
|
||||
for (int j=0;j<s->clip_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;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ class QTreeWidgetItem;
|
||||
class QCheckBox;
|
||||
struct Clip;
|
||||
struct Sequence;
|
||||
struct Media;
|
||||
|
||||
#include <QUndoStack>
|
||||
#include <QUndoCommand>
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user