
With the aim of removing `seqbasep` to remove the complicated logic for repairing pointers within the `Strip` struct when loading files and undo steps, this commit just moves access of the variable behind a function. In the future the function will retrieve the list from a Strip pointer, for now it just returns the existing pointer. Overall motivation is that blend file pointer manipulation is incompatible with the changes required for #127706. Pull Request: https://projects.blender.org/blender/blender/pulls/144624
541 lines
20 KiB
C++
541 lines
20 KiB
C++
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
|
* SPDX-FileCopyrightText: 2003-2009 Blender Authors
|
|
* SPDX-FileCopyrightText: 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup bke
|
|
*/
|
|
|
|
#include <algorithm>
|
|
#include <cstring>
|
|
|
|
#include "BLO_readfile.hh"
|
|
#include "BLO_writefile.hh"
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "ED_outliner.hh"
|
|
#include "ED_sequencer.hh"
|
|
|
|
#include "DNA_anim_types.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_sequence_types.h"
|
|
#include "DNA_space_types.h"
|
|
#include "DNA_windowmanager_types.h"
|
|
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_path_utils.hh"
|
|
|
|
#include "BKE_anim_data.hh"
|
|
#include "BKE_appdir.hh"
|
|
#include "BKE_blender_copybuffer.hh"
|
|
#include "BKE_blendfile.hh"
|
|
#include "BKE_context.hh"
|
|
#include "BKE_fcurve.hh"
|
|
#include "BKE_lib_id.hh"
|
|
#include "BKE_lib_query.hh"
|
|
#include "BKE_lib_remap.hh"
|
|
#include "BKE_main.hh"
|
|
#include "BKE_report.hh"
|
|
#include "BKE_scene.hh"
|
|
|
|
#include "SEQ_animation.hh"
|
|
#include "SEQ_iterator.hh"
|
|
#include "SEQ_select.hh"
|
|
#include "SEQ_sequencer.hh"
|
|
#include "SEQ_time.hh"
|
|
#include "SEQ_transform.hh"
|
|
#include "SEQ_utils.hh"
|
|
|
|
#include "DEG_depsgraph.hh"
|
|
#include "DEG_depsgraph_build.hh"
|
|
|
|
#include "ANIM_action.hh"
|
|
#include "ANIM_action_legacy.hh"
|
|
#include "ANIM_animdata.hh"
|
|
|
|
#include "WM_api.hh"
|
|
#include "WM_types.hh"
|
|
|
|
#ifdef WITH_AUDASPACE
|
|
# include <AUD_Special.h>
|
|
#endif
|
|
|
|
/* Own include. */
|
|
#include "sequencer_intern.hh"
|
|
|
|
namespace blender::ed::vse {
|
|
|
|
using namespace bke::blendfile;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Copy Operator Helper functions
|
|
*/
|
|
|
|
static void sequencer_copy_animation_listbase(Scene *scene_src,
|
|
Strip *strip_dst,
|
|
ListBase *clipboard_dst,
|
|
ListBase *fcurve_base_src)
|
|
{
|
|
/* Add curves for strips inside meta strip. */
|
|
if (strip_dst->type == STRIP_TYPE_META) {
|
|
LISTBASE_FOREACH (Strip *, meta_child, &strip_dst->seqbase) {
|
|
sequencer_copy_animation_listbase(scene_src, meta_child, clipboard_dst, fcurve_base_src);
|
|
}
|
|
}
|
|
|
|
Vector<FCurve *> fcurves_src = animrig::fcurves_in_listbase_filtered(
|
|
*fcurve_base_src,
|
|
[&](const FCurve &fcurve) { return seq::fcurve_matches(*strip_dst, fcurve); });
|
|
|
|
for (FCurve *fcu_src : fcurves_src) {
|
|
BLI_addtail(clipboard_dst, BKE_fcurve_copy(fcu_src));
|
|
}
|
|
}
|
|
|
|
/* This is effectively just a copy of `sequencer_copy_animation_listbase()`
|
|
* above, except that it copies from an action's animation to a vector rather
|
|
* than between two listbases. */
|
|
static void sequencer_copy_animation_to_vector(Scene *scene_src,
|
|
Strip *strip_dst,
|
|
Vector<FCurve *> &clipboard_dst,
|
|
bAction &fcurves_src_action,
|
|
animrig::slot_handle_t fcurves_src_slot_handle)
|
|
{
|
|
/* Add curves for strips inside meta strip. */
|
|
if (strip_dst->type == STRIP_TYPE_META) {
|
|
LISTBASE_FOREACH (Strip *, meta_child, &strip_dst->seqbase) {
|
|
sequencer_copy_animation_to_vector(
|
|
scene_src, meta_child, clipboard_dst, fcurves_src_action, fcurves_src_slot_handle);
|
|
}
|
|
}
|
|
|
|
Vector<FCurve *> fcurves_src = animrig::fcurves_in_action_slot_filtered(
|
|
&fcurves_src_action, fcurves_src_slot_handle, [&](const FCurve &fcurve) {
|
|
return seq::fcurve_matches(*strip_dst, fcurve);
|
|
});
|
|
|
|
for (FCurve *fcu_src : fcurves_src) {
|
|
FCurve *fcu_copy = BKE_fcurve_copy(fcu_src);
|
|
|
|
/* Handling groups properly requires more work, so for now just ignore them. */
|
|
fcu_copy->grp = nullptr;
|
|
|
|
clipboard_dst.append(fcu_copy);
|
|
}
|
|
}
|
|
|
|
static void sequencer_copy_animation(Scene *scene_src,
|
|
Vector<FCurve *> &fcurves_dst,
|
|
ListBase *drivers_dst,
|
|
Strip *strip_dst)
|
|
{
|
|
if (seq::animation_keyframes_exist(scene_src)) {
|
|
sequencer_copy_animation_to_vector(
|
|
scene_src, strip_dst, fcurves_dst, *scene_src->adt->action, scene_src->adt->slot_handle);
|
|
}
|
|
if (seq::animation_drivers_exist(scene_src)) {
|
|
sequencer_copy_animation_listbase(scene_src, strip_dst, drivers_dst, &scene_src->adt->drivers);
|
|
}
|
|
}
|
|
|
|
static void sequencer_copybuffer_filepath_get(char filepath[FILE_MAX], size_t filepath_maxncpy)
|
|
{
|
|
BLI_path_join(filepath, filepath_maxncpy, BKE_tempdir_base(), "copybuffer_vse.blend");
|
|
}
|
|
|
|
static bool sequencer_write_copy_paste_file(Main *bmain_src,
|
|
Scene *scene_src,
|
|
const char *filepath,
|
|
ReportList &reports)
|
|
|
|
{
|
|
/* NOTE: Setting the same current file path as G_MAIN is necessary for now to get correct
|
|
* external filepaths when writing the partial write context on disk. otherwise, filepaths from
|
|
* the scene's sequencer strips (e.g. image ones) would also need to be remapped in this code. */
|
|
PartialWriteContext copy_buffer{bmain_src->filepath};
|
|
const char *scene_name = "copybuffer_vse_scene";
|
|
|
|
/* Add a dummy empty scene to the temporary Main copy buffer. */
|
|
Scene *scene_dst = reinterpret_cast<Scene *>(
|
|
copy_buffer.id_create(ID_SCE,
|
|
scene_name,
|
|
nullptr,
|
|
{(PartialWriteContext::IDAddOperations::SET_FAKE_USER |
|
|
PartialWriteContext::IDAddOperations::SET_CLIPBOARD_MARK)}));
|
|
|
|
/* Create an empty sequence editor data to store all copied strips. */
|
|
scene_dst->ed = MEM_callocN<Editing>(__func__);
|
|
scene_dst->ed->seqbasep = &scene_dst->ed->seqbase;
|
|
seq::seqbase_duplicate_recursive(bmain_src,
|
|
scene_src,
|
|
scene_dst,
|
|
&scene_dst->ed->seqbase,
|
|
scene_src->ed->current_strips(),
|
|
seq::StripDuplicate::Selected,
|
|
0);
|
|
|
|
BLI_duplicatelist(&scene_dst->ed->channels, &scene_src->ed->channels);
|
|
scene_dst->ed->displayed_channels = &scene_dst->ed->channels;
|
|
|
|
/* Save current frame and active strip. */
|
|
scene_dst->r.cfra = scene_src->r.cfra;
|
|
Strip *active_seq_src = seq::select_active_get(scene_src);
|
|
if (active_seq_src) {
|
|
Strip *strip_dst = static_cast<Strip *>(
|
|
BLI_findstring(&scene_dst->ed->seqbase, active_seq_src->name, offsetof(Strip, name)));
|
|
if (strip_dst) {
|
|
seq::select_active_set(scene_dst, strip_dst);
|
|
}
|
|
}
|
|
|
|
Vector<FCurve *> fcurves_dst = {};
|
|
ListBase drivers_dst = {nullptr, nullptr};
|
|
LISTBASE_FOREACH (Strip *, strip_dst, &scene_dst->ed->seqbase) {
|
|
/* Copy any fcurves/drivers from `scene_src` that are relevant to `strip_dst`. */
|
|
sequencer_copy_animation(scene_src, fcurves_dst, &drivers_dst, strip_dst);
|
|
}
|
|
|
|
BLI_assert(scene_dst->adt == nullptr);
|
|
|
|
/* Copy over the fcurves. */
|
|
if (!fcurves_dst.is_empty()) {
|
|
scene_dst->adt = BKE_animdata_ensure_id(&scene_dst->id);
|
|
animrig::Action &action_dst =
|
|
reinterpret_cast<bAction *>(
|
|
copy_buffer.id_create(
|
|
ID_AC, scene_name, nullptr, {PartialWriteContext::IDAddOperations::SET_FAKE_USER}))
|
|
->wrap();
|
|
|
|
/* Assign the `dst_action` as either legacy or layered, depending on what
|
|
* the source action we're copying from is. */
|
|
if (animrig::legacy::action_treat_as_legacy(*scene_src->adt->action)) {
|
|
const bool success = animrig::assign_action(&action_dst, scene_dst->id);
|
|
if (!success) {
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
/* If we're copying from a layered action, also ensure a connected slot. */
|
|
animrig::Slot *slot = animrig::assign_action_ensure_slot_for_keying(action_dst,
|
|
scene_dst->id);
|
|
if (slot == nullptr) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for (FCurve *fcurve : fcurves_dst) {
|
|
animrig::action_fcurve_attach(action_dst,
|
|
scene_dst->adt->slot_handle,
|
|
*fcurve,
|
|
fcurve->grp ? std::optional(fcurve->grp->name) : std::nullopt);
|
|
}
|
|
}
|
|
|
|
/* Copy over the drivers. */
|
|
if (!BLI_listbase_is_empty(&drivers_dst)) {
|
|
scene_dst->adt = BKE_animdata_ensure_id(&scene_dst->id);
|
|
BLI_movelisttolist(&scene_dst->adt->drivers, &drivers_dst);
|
|
}
|
|
|
|
/* Only add to the paste buffer some dependency ID types. For example, scenes are ignored/cleared
|
|
* (how to copy and paste scene strips is not clear currently).
|
|
*/
|
|
/* NOTE: since a special Scene root ID needs to be forged for the VSE copy/paste (instead of
|
|
* directly using the current scene and adding it to the paste buffer), the first level of
|
|
* dependencies (IDs directly used by the scene) need to be processed manually here.
|
|
*
|
|
* All other indirect dependencies will then be handled automatically by the partial write
|
|
* context code.
|
|
*/
|
|
#define VSE_COPYBUFFER_IDTYPES ID_SO, ID_MC, ID_IM, ID_TXT, ID_VF, ID_AC
|
|
auto add_scene_ids_dependencies_cb = [©_buffer,
|
|
scene_dst](LibraryIDLinkCallbackData *cb_data) -> int {
|
|
ID *id_src = *cb_data->id_pointer;
|
|
|
|
/* Embedded or null IDs usages can be ignored here. */
|
|
if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_EMBEDDED_NOT_OWNING)) {
|
|
return IDWALK_RET_NOP;
|
|
}
|
|
if (!id_src) {
|
|
return IDWALK_RET_NOP;
|
|
}
|
|
|
|
/* The Action ID of the destination scene has already been added (created actually) in the copy
|
|
* buffer. This is necessary to ensure that only the relevant sequencer-related animation data
|
|
* is copied into the destination paste buffer, and not the whole scene's animation. See the
|
|
* code around the call to #sequencer_copy_animation above.
|
|
*
|
|
* So trying to add it again here would lead to serious issues. */
|
|
if (scene_dst->adt && scene_dst->adt->action == reinterpret_cast<bAction *>(id_src)) {
|
|
BLI_assert(GS(id_src->name) == ID_AC);
|
|
return IDWALK_RET_NOP;
|
|
}
|
|
|
|
ID *id_dst = nullptr;
|
|
const ID_Type id_type = GS((id_src)->name);
|
|
/* Only add (and follow) IDs which usage is marked as 'never null', or are from following
|
|
* types: #bSound, #MovieClip, #Image, #Text, #VFont, #bAction. */
|
|
if (ELEM(id_type, VSE_COPYBUFFER_IDTYPES) || (cb_data->cb_flag & IDWALK_CB_NEVER_NULL)) {
|
|
/* The partial write context handle dependencies of ID added to it. This callback will tell
|
|
* it whether a given dependency ID should be skipped/cleared, or also added in the context.
|
|
*/
|
|
auto partial_write_dependencies_filter_cb = [](LibraryIDLinkCallbackData *cb_deps_data,
|
|
PartialWriteContext::IDAddOptions /*options*/)
|
|
-> PartialWriteContext::IDAddOperations {
|
|
ID *id_deps_src = *cb_deps_data->id_pointer;
|
|
const ID_Type id_type = GS((id_deps_src)->name);
|
|
if (ELEM(id_type, VSE_COPYBUFFER_IDTYPES) ||
|
|
(cb_deps_data->cb_flag & IDWALK_CB_NEVER_NULL))
|
|
{
|
|
return PartialWriteContext::IDAddOperations::ADD_DEPENDENCIES;
|
|
}
|
|
return PartialWriteContext::IDAddOperations::CLEAR_DEPENDENCIES;
|
|
};
|
|
id_dst = copy_buffer.id_add(id_src,
|
|
{PartialWriteContext::IDAddOperations::NOP},
|
|
partial_write_dependencies_filter_cb);
|
|
}
|
|
*cb_data->id_pointer = id_dst;
|
|
return IDWALK_RET_NOP;
|
|
};
|
|
BKE_library_foreach_ID_link(
|
|
nullptr, &scene_dst->id, add_scene_ids_dependencies_cb, nullptr, IDWALK_NOP);
|
|
#undef VSE_COPYBUFFER_IDTYPES
|
|
|
|
BLI_assert(copy_buffer.is_valid());
|
|
|
|
const bool retval = copy_buffer.write(filepath, reports);
|
|
|
|
return retval;
|
|
}
|
|
|
|
wmOperatorStatus sequencer_clipboard_copy_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Main *bmain = CTX_data_main(C);
|
|
Scene *scene = CTX_data_sequencer_scene(C);
|
|
Editing *ed = seq::editing_get(scene);
|
|
|
|
blender::VectorSet<Strip *> selected = seq::query_selected_strips(ed->current_strips());
|
|
|
|
if (selected.is_empty()) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
blender::VectorSet<Strip *> effect_chain;
|
|
effect_chain.add_multiple(selected);
|
|
seq::iterator_set_expand(
|
|
scene, ed->current_strips(), effect_chain, seq::query_strip_effect_chain);
|
|
|
|
blender::VectorSet<Strip *> expanded;
|
|
for (Strip *strip : effect_chain) {
|
|
if (!(strip->flag & SELECT)) {
|
|
strip->flag |= SELECT;
|
|
expanded.add(strip);
|
|
}
|
|
}
|
|
|
|
char filepath[FILE_MAX];
|
|
sequencer_copybuffer_filepath_get(filepath, sizeof(filepath));
|
|
bool success = sequencer_write_copy_paste_file(bmain, scene, filepath, *op->reports);
|
|
if (!success) {
|
|
BKE_report(op->reports, RPT_ERROR, "Could not create the copy paste file!");
|
|
for (Strip *strip : expanded) {
|
|
strip->flag &= ~SELECT;
|
|
}
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
/* We are all done! */
|
|
if (effect_chain.size() > selected.size()) {
|
|
BKE_report(op->reports,
|
|
RPT_INFO,
|
|
"Copied the selected Video Sequencer strips and associated effect chain to "
|
|
"internal clipboard");
|
|
}
|
|
else {
|
|
BKE_report(
|
|
op->reports, RPT_INFO, "Copied the selected Video Sequencer strips to internal clipboard");
|
|
}
|
|
ED_outliner_select_sync_from_sequence_tag(C);
|
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Paste Operator Helper functions
|
|
*/
|
|
|
|
static bool sequencer_paste_animation(Main *bmain_dst, Scene *scene_dst, Scene *scene_src)
|
|
{
|
|
if (!seq::animation_keyframes_exist(scene_src) && !seq::animation_drivers_exist(scene_src)) {
|
|
return false;
|
|
}
|
|
|
|
bAction *act_dst = animrig::id_action_ensure(bmain_dst, &scene_dst->id);
|
|
|
|
/* For layered actions ensure we have an attached slot. */
|
|
if (!animrig::legacy::action_treat_as_legacy(*act_dst)) {
|
|
const animrig::Slot *slot = animrig::assign_action_ensure_slot_for_keying(act_dst->wrap(),
|
|
scene_dst->id);
|
|
BLI_assert(slot != nullptr);
|
|
if (slot == nullptr) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for (FCurve *fcu : animrig::legacy::fcurves_for_assigned_action(scene_src->adt)) {
|
|
animrig::action_fcurve_attach(act_dst->wrap(),
|
|
scene_dst->adt->slot_handle,
|
|
*BKE_fcurve_copy(fcu),
|
|
fcu->grp ? std::optional(fcu->grp->name) : std::nullopt);
|
|
}
|
|
LISTBASE_FOREACH (FCurve *, fcu, &scene_src->adt->drivers) {
|
|
BLI_addtail(&scene_dst->adt->drivers, BKE_fcurve_copy(fcu));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
wmOperatorStatus sequencer_clipboard_paste_exec(bContext *C, wmOperator *op)
|
|
{
|
|
char filepath[FILE_MAX];
|
|
sequencer_copybuffer_filepath_get(filepath, sizeof(filepath));
|
|
const BlendFileReadParams params{};
|
|
BlendFileReadReport bf_reports{};
|
|
BlendFileData *bfd = BKE_blendfile_read(filepath, ¶ms, &bf_reports);
|
|
|
|
if (bfd == nullptr) {
|
|
BKE_report(op->reports, RPT_INFO, "No data to paste");
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
Main *bmain_src = bfd->main;
|
|
bfd->main = nullptr;
|
|
BLO_blendfiledata_free(bfd);
|
|
|
|
Scene *scene_src = nullptr;
|
|
/* Find the scene we pasted that contains the strips. It should be tagged. */
|
|
LISTBASE_FOREACH (Scene *, scene_iter, &bmain_src->scenes) {
|
|
if (scene_iter->id.flag & ID_FLAG_CLIPBOARD_MARK) {
|
|
scene_src = scene_iter;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!scene_src || !scene_src->ed) {
|
|
BKE_report(op->reports, RPT_ERROR, "No clipboard scene to paste Video Sequencer data from");
|
|
BKE_main_free(bmain_src);
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
const int num_strips_to_paste = BLI_listbase_count(&scene_src->ed->seqbase);
|
|
if (num_strips_to_paste == 0) {
|
|
BKE_report(op->reports, RPT_INFO, "No strips to paste");
|
|
BKE_main_free(bmain_src);
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
Scene *scene_dst = CTX_data_sequencer_scene(C);
|
|
Editing *ed_dst = seq::editing_ensure(scene_dst); /* Creates "ed" if it's missing. */
|
|
int ofs;
|
|
|
|
deselect_all_strips(scene_dst);
|
|
if (RNA_boolean_get(op->ptr, "keep_offset")) {
|
|
ofs = scene_dst->r.cfra - scene_src->r.cfra;
|
|
}
|
|
else {
|
|
int min_seq_startdisp = INT_MAX;
|
|
LISTBASE_FOREACH (Strip *, strip, &scene_src->ed->seqbase) {
|
|
min_seq_startdisp = std::min(seq::time_left_handle_frame_get(scene_src, strip),
|
|
min_seq_startdisp);
|
|
}
|
|
/* Paste strips relative to the current-frame. */
|
|
ofs = scene_dst->r.cfra - min_seq_startdisp;
|
|
}
|
|
|
|
Strip *prev_active_seq = seq::select_active_get(scene_src);
|
|
std::string active_seq_name;
|
|
if (prev_active_seq) {
|
|
active_seq_name.assign(prev_active_seq->name);
|
|
}
|
|
|
|
/* Make sure we have all data IDs we need in bmain_dst. Remap the IDs if we already have them.
|
|
* This has to happen BEFORE we move the strip over to scene_dst. their ID mapping will not be
|
|
* correct otherwise. */
|
|
Main *bmain_dst = CTX_data_main(C);
|
|
MainMergeReport merge_reports = {};
|
|
/* NOTE: BKE_main_merge will free bmain_src! */
|
|
BKE_main_merge(bmain_dst, &bmain_src, merge_reports);
|
|
|
|
/* Paste animation.
|
|
* NOTE: Only fcurves and drivers are copied. NLA action strips are not copied.
|
|
* First backup original curves from scene and move curves from clipboard into scene. This way,
|
|
* when pasted strips are renamed, pasted fcurves are renamed with them. Finally restore original
|
|
* curves from backup.
|
|
*/
|
|
seq::AnimationBackup animation_backup = {{nullptr}};
|
|
seq::animation_backup_original(scene_dst, &animation_backup);
|
|
bool has_animation = sequencer_paste_animation(bmain_dst, scene_dst, scene_src);
|
|
|
|
ListBase nseqbase = {nullptr, nullptr};
|
|
/* NOTE: seq::seqbase_duplicate_recursive() takes care of generating
|
|
* new UIDs for sequences in the new list. */
|
|
seq::seqbase_duplicate_recursive(bmain_dst,
|
|
scene_src,
|
|
scene_dst,
|
|
&nseqbase,
|
|
&scene_src->ed->seqbase,
|
|
seq::StripDuplicate::Selected,
|
|
0);
|
|
|
|
/* BKE_main_merge will copy the scene_src and its action into bmain_dst. Remove them as
|
|
* we merge the data from these manually.
|
|
*/
|
|
if (has_animation) {
|
|
BKE_id_delete(bmain_dst, scene_src->adt->action);
|
|
}
|
|
BKE_id_delete(bmain_dst, scene_src);
|
|
|
|
Strip *iseq_first = static_cast<Strip *>(nseqbase.first);
|
|
BLI_movelisttolist(ed_dst->seqbasep, &nseqbase);
|
|
/* Restore "first" pointer as BLI_movelisttolist sets it to nullptr */
|
|
nseqbase.first = iseq_first;
|
|
|
|
LISTBASE_FOREACH (Strip *, istrip, &nseqbase) {
|
|
if (istrip->name == active_seq_name) {
|
|
seq::select_active_set(scene_dst, istrip);
|
|
}
|
|
/* Make sure, that pasted strips have unique names. This has to be done after
|
|
* adding strips to seqbase, for lookup cache to work correctly. */
|
|
seq::ensure_unique_name(istrip, scene_dst);
|
|
}
|
|
|
|
LISTBASE_FOREACH (Strip *, istrip, &nseqbase) {
|
|
/* Translate after name has been changed, otherwise this will affect animdata of original
|
|
* strip. */
|
|
seq::transform_translate_strip(scene_dst, istrip, ofs);
|
|
/* Ensure, that pasted strips don't overlap. */
|
|
if (seq::transform_test_overlap(scene_dst, ed_dst->seqbasep, istrip)) {
|
|
seq::transform_seqbase_shuffle(ed_dst->seqbasep, istrip, scene_dst);
|
|
}
|
|
}
|
|
|
|
seq::animation_restore_original(scene_dst, &animation_backup);
|
|
|
|
DEG_id_tag_update(&scene_dst->id, ID_RECALC_SEQUENCER_STRIPS);
|
|
if (scene_dst->adt && scene_dst->adt->action) {
|
|
DEG_id_tag_update(&scene_dst->adt->action->id, ID_RECALC_ANIMATION_NO_FLUSH);
|
|
}
|
|
DEG_relations_tag_update(bmain_dst);
|
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene_dst);
|
|
WM_event_add_notifier(C, NC_SCENE | ND_ANIMCHAN, scene_dst);
|
|
ED_outliner_select_sync_from_sequence_tag(C);
|
|
|
|
BKE_reportf(op->reports, RPT_INFO, "%d strips pasted", num_strips_to_paste);
|
|
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
} // namespace blender::ed::vse
|