#include "undo.h" #include #include #include #include #include "project/clip.h" #include "project/sequence.h" #include "panels/panels.h" #include "panels/project.h" #include "panels/effectcontrols.h" #include "playback/playback.h" #include "ui/sourcetable.h" #include "effects/effect.h" #include "io/media.h" #include "playback/cacher.h" #include "effects/transition.h" #include "ui/labelslider.h" 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 #define TA_ADD_EFFECT 9 #define TA_ADD_TRANSITION 10 #define TA_MODIFY_TRANSITION 11 #define TA_DELETE_TRANSITION 12 extern "C" { #include "libavfilter/avfilter.h" } TimelineAction::TimelineAction() : done(false), change_seq(false), ripple_enabled(false), change_in_out(false), old_project_changed(project_changed) {} 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); } } } void TimelineAction::offset_links(QVector& clips, int offset) { for (int i=0;ilinked.size();j++) { c->linked[j] += offset; } } } void TimelineAction::change_sequence(Sequence* s) { new_seq = s; change_seq = true; } void TimelineAction::new_sequence(QTreeWidgetItem *s, QTreeWidgetItem* parent) { new_sequence_items.append(s); new_sequence_parents.append(parent); } 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_out(Sequence* s, int clip, long value) { new_action(s, TA_OUT, clip, TA_NO_TRANSITION, s->get_clip(clip)->timeline_out, value); } void TimelineAction::set_clip_in(Sequence* s, int clip, long value) { new_action(s, TA_CLIP_IN, clip, TA_NO_TRANSITION, s->get_clip(clip)->clip_in, value); } void TimelineAction::set_track(Sequence* s, int clip, int value) { new_action(s, TA_TRACK, clip, TA_NO_TRANSITION, s->get_clip(clip)->track, value); } void TimelineAction::increase_timeline_in(Sequence* s, int clip, long value) { new_action(s, TA_ADD_IN, clip, TA_NO_TRANSITION, 0, value); } void TimelineAction::increase_timeline_out(Sequence* s, int clip, long value) { new_action(s, TA_ADD_OUT, clip, TA_NO_TRANSITION, 0, value); } void TimelineAction::increase_clip_in(Sequence* s, int clip, long value) { new_action(s, TA_ADD_CLIP_IN, clip, TA_NO_TRANSITION, 0, value); } void TimelineAction::increase_track(Sequence* s, int clip, int value) { new_action(s, TA_ADD_TRACK, clip, TA_NO_TRANSITION, 0, value); } void TimelineAction::delete_clip(Sequence* s, int clip) { new_action(s, TA_DELETE, clip, TA_NO_TRANSITION, 0, 0); } void TimelineAction::add_media(QTreeWidgetItem* item, QTreeWidgetItem *parent) { media_to_add.append(item); media_to_add_parents.append(parent); } void TimelineAction::delete_media(QTreeWidgetItem* item) { deleted_media.append(item); } void TimelineAction::ripple(Sequence* s, long point, long length) { QVector i; ripple(s, point, length, i); } void TimelineAction::add_effect(Sequence* s, int clip, int effect) { new_action(s, TA_ADD_EFFECT, clip, TA_NO_TRANSITION, 0, effect); } void TimelineAction::add_transition(Sequence* s, int clip, int transition, int type) { new_action(s, TA_ADD_TRANSITION, clip, type, 0, transition); } void TimelineAction::modify_transition(Sequence* s, int clip, int type, long length) { new_action(s, TA_MODIFY_TRANSITION, clip, type, 0, length); } void TimelineAction::delete_transition(Sequence* s, int clip, int type) { new_action(s, TA_DELETE_TRANSITION, clip, type, 0, 0); } void TimelineAction::set_in_out(Sequence* s, bool enabled, long in, long out) { change_in_out = true; change_in_out_sequence = s; old_in_out_enabled = s->using_workarea; old_sequence_in = s->workarea_in; old_sequence_out = s->workarea_out; new_in_out_enabled = enabled; new_sequence_in = in; new_sequence_out = out; } void TimelineAction::ripple(Sequence* s, long point, long length, QVector& ignore) { ripple_enabled = true; ripple_point = point; ripple_length = length; ripple_sequence = s; ripple_ignores = ignore; } void TimelineAction::undo() { // re-add deleted media for (int i=0;isource_table->addTopLevelItem(item); } else { parent->addChild(item); } } // de-ripple clips if (ripple_enabled) { for (int i=0;iget_clip(rippled_clips.at(i)); c->timeline_in -= ripple_length; c->timeline_out -= ripple_length; } } for (int i=actions.size()-1;i>=0;i--) { switch (actions.at(i)) { case TA_IN: sequences.at(i)->get_clip(clips.at(i))->timeline_in = old_values.at(i); break; case TA_OUT: sequences.at(i)->get_clip(clips.at(i))->timeline_out = old_values.at(i); break; case TA_CLIP_IN: sequences.at(i)->get_clip(clips.at(i))->clip_in = old_values.at(i); break; case TA_TRACK: sequences.at(i)->get_clip(clips.at(i))->track = old_values.at(i); break; case TA_DELETE: sequences.at(i)->replace_clip(clips.at(i), deleted_clips.at(new_values.at(i))); break; case TA_ADD_IN: sequences.at(i)->get_clip(clips.at(i))->timeline_in -= new_values.at(i); break; case TA_ADD_OUT: sequences.at(i)->get_clip(clips.at(i))->timeline_out -= new_values.at(i); break; case TA_ADD_CLIP_IN: sequences.at(i)->get_clip(clips.at(i))->clip_in -= new_values.at(i); break; case TA_ADD_TRACK: sequences.at(i)->get_clip(clips.at(i))->track -= new_values.at(i); break; case TA_ADD_EFFECT: { Clip* c = sequences.at(i)->get_clip(clips.at(i)); Effect* effect = c->effects.at(old_values.at(i)); c->effects.removeAt(old_values.at(i)); delete effect; } break; case TA_ADD_TRANSITION: { Clip* c = sequences.at(i)->get_clip(clips.at(i)); if (transition_types.at(i) == TA_OPENING_TRANSITION) { delete c->opening_transition; c->opening_transition = NULL; } else { delete c->closing_transition; c->closing_transition = NULL; } } break; case TA_MODIFY_TRANSITION: { Clip* c = sequences.at(i)->get_clip(clips.at(i)); if (transition_types.at(i) == TA_OPENING_TRANSITION) { c->opening_transition->length = old_values.at(i); } else { c->closing_transition->length = old_values.at(i); } } break; case TA_DELETE_TRANSITION: { Clip* c = sequences.at(i)->get_clip(clips.at(i)); if (transition_types.at(i) == TA_OPENING_TRANSITION) { c->opening_transition = create_transition(new_values.at(i), c); c->opening_transition->length = old_values.at(i); } else { c->closing_transition = create_transition(new_values.at(i), c); c->closing_transition->length = old_values.at(i); } } break; } } // remove added sequences for (int i=0;isource_table->takeTopLevelItem(panel_project->source_table->indexOfTopLevelItem(new_sequence_items.at(i))); } else { new_sequence_parents.at(i)->removeChild(new_sequence_items.at(i)); } } // 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 int delete_count = 0; for (int i=0;idestroy_clip(added_indexes.at(i)-delete_count, true); delete_count++; } // remove any added media for (int i=0;isource_table->takeTopLevelItem(panel_project->source_table->indexOfTopLevelItem(media_to_add.at(i))); } else { media_to_add_parents.at(i)->removeChild(media_to_add.at(i)); } } // un-change workarea if (change_in_out) { change_in_out_sequence->using_workarea = old_in_out_enabled; change_in_out_sequence->workarea_in = old_sequence_in; change_in_out_sequence->workarea_out = old_sequence_out; } // un-change current active sequence if (change_seq) { set_sequence(old_seq); } project_changed = old_project_changed; done = false; } void TimelineAction::redo() { removed_link_from.clear(); 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(); rippled_clips.clear(); for (int i=0;isource_table->addTopLevelItem(new_sequence_items.at(i)); } else { new_sequence_parents.at(i)->addChild(new_sequence_items.at(i)); } } // add any new clips if (clips_to_add.size() > 0) { QVector copies; for (int i=0;icopy(sequence_to_add_clips_to.at(i)); copy->linked.resize(original->linked.size()); for (int j=0;jlinked.size();j++) { copy->linked[j] = original->linked.at(j) + sequence_to_add_clips_to.at(i)->clip_count(); } copies.append(copy); } for (int i=0;iadd_clip(copies.at(i))); } } for (int i=0;iget_clip(clips.at(i))->timeline_in = new_values.at(i); } break; case TA_OUT: { sequences.at(i)->get_clip(clips.at(i))->timeline_out = new_values.at(i); } break; case TA_CLIP_IN: { sequences.at(i)->get_clip(clips.at(i))->clip_in = new_values.at(i); } break; case TA_TRACK: { 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(sequences.at(i)->get_clip(clips.at(i))); deleted_clips_indices.append(clips.at(i)); deleted_clip_sequences.append(sequences.at(i)); sequences.at(i)->replace_clip(clips.at(i), NULL); } break; case TA_ADD_IN: { sequences.at(i)->get_clip(clips.at(i))->timeline_in += new_values.at(i); } break; case TA_ADD_OUT: { sequences.at(i)->get_clip(clips.at(i))->timeline_out += new_values.at(i); } break; case TA_ADD_CLIP_IN: { sequences.at(i)->get_clip(clips.at(i))->clip_in += new_values.at(i); } break; case TA_ADD_TRACK: { sequences.at(i)->get_clip(clips.at(i))->track += new_values.at(i); } break; case TA_ADD_EFFECT: { Clip* c = sequences.at(i)->get_clip(clips.at(i)); old_values[i] = c->effects.size(); Effect* e = create_effect(new_values.at(i), c); c->effects.append(e); } break; case TA_ADD_TRANSITION: { Clip* c = sequences.at(i)->get_clip(clips.at(i)); if (transition_types.at(i) == TA_OPENING_TRANSITION) { c->opening_transition = create_transition(new_values.at(i), c); } else { c->closing_transition = create_transition(new_values.at(i), c); } } break; case TA_MODIFY_TRANSITION: { Clip* c = sequences.at(i)->get_clip(clips.at(i)); if (transition_types.at(i) == TA_OPENING_TRANSITION) { old_values[i] = c->opening_transition->length; c->opening_transition->length = new_values.at(i); } else { old_values[i] = c->closing_transition->length; c->closing_transition->length = new_values.at(i); } } break; case TA_DELETE_TRANSITION: { Clip* c = sequences.at(i)->get_clip(clips.at(i)); if (transition_types.at(i) == TA_OPENING_TRANSITION) { new_values[i] = c->opening_transition->id; old_values[i] = c->opening_transition->length; delete c->opening_transition; c->opening_transition = NULL; } else { new_values[i] = c->closing_transition->id; old_values[i] = c->closing_transition->length; delete c->closing_transition; c->closing_transition = NULL; } } break; } } // remove any potential link references from deleted clips for (int i=0;iclip_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); break; } } } } } // ripple clips - moves all clips after a certain ripple_point by ripple_length if (ripple_enabled) { for (int j=0;jclip_count();j++) { bool found = false; for (int i=0;iget_clip(j); if (c != NULL && c->timeline_in >= ripple_point) { c->timeline_in += ripple_length; c->timeline_out += ripple_length; rippled_clips.append(j); } } } } // delete media for (int i=0;iparent()); // if we're deleting the open sequence, close it if (get_type_from_tree(item) == MEDIA_TYPE_SEQUENCE && get_sequence_from_tree(item) == sequence && !change_seq) { old_seq = sequence; new_seq = NULL; change_seq = true; } if (item->parent() == NULL) { panel_project->source_table->takeTopLevelItem(panel_project->source_table->indexOfTopLevelItem(item)); } else { item->parent()->removeChild(item); } } // add any new media for (int i=0;isource_table->addTopLevelItem(media_to_add.at(i)); } else { media_to_add_parents.at(i)->addChild(media_to_add.at(i)); } } // change sequence workarea if (change_in_out) { change_in_out_sequence->using_workarea = new_in_out_enabled; change_in_out_sequence->workarea_in = new_sequence_in; change_in_out_sequence->workarea_out = new_sequence_out; } // change current sequence if (change_seq) { old_seq = sequence; set_sequence(new_seq); } project_changed = true; done = true; } LinkCommand::LinkCommand() : link(true), old_project_changed(project_changed) {} void LinkCommand::undo() { for (int i=0;ilinked.clear(); } else { c->linked = old_links.at(i); } } project_changed = old_project_changed; } void LinkCommand::redo() { old_links.clear(); for (int i=0;ilinked.append(j); } } } else { old_links.append(c->linked); c->linked.clear(); } } project_changed = true; } CheckboxCommand::CheckboxCommand(QCheckBox* b) : box(b), checked(box->isChecked()), done(true), old_project_changed(project_changed) {} CheckboxCommand::~CheckboxCommand() {} void CheckboxCommand::undo() { box->setChecked(!checked); done = false; project_changed = old_project_changed; } void CheckboxCommand::redo() { if (!done) { box->setChecked(checked); } project_changed = true; } 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; } ReplaceClipMediaCommand::ReplaceClipMediaCommand(void *a, void *b, int c, int d, bool e) : old_media(a), new_media(b), old_type(c), new_type(d), preserve_clip_ins(e), old_project_changed(project_changed) {} void ReplaceClipMediaCommand::replace(bool undo) { if (!undo) { old_clip_ins.clear(); } for (int i=0;iopen) { close_clip(c); c->cacher->wait(); } if (undo) { if (!preserve_clip_ins) { c->clip_in = old_clip_ins.at(i); } c->media = old_media; c->media_type = old_type; } else { if (!preserve_clip_ins) { old_clip_ins.append(c->clip_in); c->clip_in = 0; } c->media = new_media; c->media_type = new_type; } c->replaced = true; c->refresh(); } } void ReplaceClipMediaCommand::undo() { replace(true); project_changed = old_project_changed; } void ReplaceClipMediaCommand::redo() { replace(false); project_changed = true; } EffectDeleteCommand::EffectDeleteCommand() : done(false), old_project_changed(project_changed) {} EffectDeleteCommand::~EffectDeleteCommand() { if (done) { for (int i=0;ieffects.insert(fx.at(i), deleted_objects.at(i)); } panel_effect_controls->reload_clips(); done = false; project_changed = old_project_changed; } void EffectDeleteCommand::redo() { deleted_objects.clear(); for (int i=0;ieffects.at(fx_id)); c->effects.removeAt(fx_id); } panel_effect_controls->reload_clips(); done = true; project_changed = true; } MediaMove::MediaMove(SourceTable *s) : table(s), old_project_changed(project_changed) {} 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)); } } project_changed = old_project_changed; } 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)); } } project_changed = true; } MediaRename::MediaRename() : done(true), old_project_changed(project_changed) {} void MediaRename::undo() { item->setText(0, from); done = false; project_changed = old_project_changed; } void MediaRename::redo() { if (!done) { item->setText(0, to); } project_changed = true; } ValueChangeCommand::ValueChangeCommand() : done(true), old_project_changed(project_changed) {} void ValueChangeCommand::undo() { source->set_value(old_val, false); done = false; project_changed = old_project_changed; } void ValueChangeCommand::redo() { if (!done) { source->set_value(new_val, false); } project_changed = true; }