`UUID` generally stands for "universally unique identifier". The session identifier that we use is neither universally unique, nor does it follow the standard. Therefor, the term "session uuid" is confusing and should be replaced. In #116888 we briefly talked about a better name and ended up with "session uid". The reason for "uid" instead of "id" is that the latter is a very overloaded term in Blender already. This patch changes all uses of "uuid" to "uid" where it's used in the context of a "session uid". It's not always trivial to see whether a specific mention of "uuid" refers to an actual uuid or something else. Therefore, I might have missed some renames. I can't think of an automated way to differentiate the case. BMesh also uses the term "uuid" sometimes in a the wrong context (e.g. `UUIDFaceStepItem`) but there it also does not mean "session uid", so it's *not* changed by this patch. Pull Request: https://projects.blender.org/blender/blender/pulls/117350
400 lines
13 KiB
C++
400 lines
13 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 <cstring>
|
|
|
|
#include "BLO_readfile.h"
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "ED_keyframing.hh"
|
|
#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_ghash.h"
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_path_util.h"
|
|
|
|
#include "BKE_appdir.hh"
|
|
#include "BKE_blender_copybuffer.hh"
|
|
#include "BKE_blendfile.hh"
|
|
#include "BKE_context.hh"
|
|
#include "BKE_fcurve.h"
|
|
#include "BKE_lib_id.hh"
|
|
#include "BKE_lib_query.hh"
|
|
#include "BKE_lib_remap.hh"
|
|
#include "BKE_main.hh"
|
|
#include "BKE_report.h"
|
|
#include "BKE_scene.h"
|
|
|
|
#include "RNA_access.hh"
|
|
|
|
#include "SEQ_animation.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_animdata.hh"
|
|
|
|
#include "WM_api.hh"
|
|
#include "WM_types.hh"
|
|
|
|
#ifdef WITH_AUDASPACE
|
|
# include <AUD_Special.h>
|
|
#endif
|
|
|
|
/* Own include. */
|
|
#include "sequencer_intern.hh"
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Copy Operator Helper functions
|
|
*/
|
|
|
|
static int gather_strip_data_ids_to_null(LibraryIDLinkCallbackData *cb_data)
|
|
{
|
|
IDRemapper *id_remapper = static_cast<IDRemapper *>(cb_data->user_data);
|
|
ID *id = *cb_data->id_pointer;
|
|
|
|
/* We don't care about embedded, loop-back, or internal IDs. */
|
|
if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_EMBEDDED_NOT_OWNING)) {
|
|
return IDWALK_RET_NOP;
|
|
}
|
|
if (cb_data->cb_flag & (IDWALK_CB_LOOPBACK | IDWALK_CB_INTERNAL)) {
|
|
return IDWALK_RET_STOP_RECURSION;
|
|
}
|
|
|
|
if (id) {
|
|
ID_Type id_type = GS((id)->name);
|
|
/* Nullify everything that is not:
|
|
* #bSound, #MovieClip, #Image, #Text, #VFont, #bAction, or #Collection IDs. */
|
|
if (!ELEM(id_type, ID_SO, ID_MC, ID_IM, ID_TXT, ID_VF, ID_AC)) {
|
|
BKE_id_remapper_add(id_remapper, id, nullptr);
|
|
return IDWALK_RET_STOP_RECURSION;
|
|
}
|
|
}
|
|
return IDWALK_RET_NOP;
|
|
}
|
|
|
|
static void sequencer_copy_animation_listbase(Scene *scene_src,
|
|
Sequence *seq_dst,
|
|
ListBase *clipboard_dst,
|
|
ListBase *fcurve_base_src)
|
|
{
|
|
/* Add curves for strips inside meta strip. */
|
|
if (seq_dst->type == SEQ_TYPE_META) {
|
|
LISTBASE_FOREACH (Sequence *, meta_child, &seq_dst->seqbase) {
|
|
sequencer_copy_animation_listbase(scene_src, meta_child, clipboard_dst, fcurve_base_src);
|
|
}
|
|
}
|
|
|
|
GSet *fcurves_src = SEQ_fcurves_by_strip_get(seq_dst, fcurve_base_src);
|
|
if (fcurves_src == nullptr) {
|
|
return;
|
|
}
|
|
|
|
GSET_FOREACH_BEGIN (FCurve *, fcu_src, fcurves_src) {
|
|
BLI_addtail(clipboard_dst, BKE_fcurve_copy(fcu_src));
|
|
}
|
|
GSET_FOREACH_END();
|
|
|
|
BLI_gset_free(fcurves_src, nullptr);
|
|
}
|
|
|
|
static void sequencer_copy_animation(Scene *scene_src,
|
|
ListBase *fcurves_dst,
|
|
ListBase *drivers_dst,
|
|
Sequence *seq_dst)
|
|
{
|
|
if (SEQ_animation_curves_exist(scene_src)) {
|
|
sequencer_copy_animation_listbase(
|
|
scene_src, seq_dst, fcurves_dst, &scene_src->adt->action->curves);
|
|
}
|
|
if (SEQ_animation_drivers_exist(scene_src)) {
|
|
sequencer_copy_animation_listbase(scene_src, seq_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)
|
|
|
|
{
|
|
/* Ideally, scene should not be added to the global Main. There currently is no good
|
|
* solution to avoid it if we want to properly pull in all strip dependencies. */
|
|
Scene *scene_dst = BKE_scene_add(bmain_src, "copybuffer_vse_scene");
|
|
|
|
/* Create a temporary scene that we will copy from.
|
|
* This is needed as it is the scene that contains all the VSE strip data.
|
|
*/
|
|
scene_dst->ed = MEM_cnew<Editing>(__func__);
|
|
scene_dst->ed->seqbasep = &scene_dst->ed->seqbase;
|
|
SEQ_sequence_base_dupli_recursive(
|
|
scene_src, scene_dst, &scene_dst->ed->seqbase, &scene_src->ed->seqbase, 0, 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;
|
|
Sequence *active_seq_src = SEQ_select_active_get(scene_src);
|
|
if (active_seq_src) {
|
|
Sequence *seq_dst = static_cast<Sequence *>(
|
|
BLI_findstring(&scene_dst->ed->seqbase, active_seq_src->name, offsetof(Sequence, name)));
|
|
if (seq_dst) {
|
|
SEQ_select_active_set(scene_dst, seq_dst);
|
|
}
|
|
}
|
|
|
|
ListBase fcurves_dst = {nullptr, nullptr};
|
|
ListBase drivers_dst = {nullptr, nullptr};
|
|
LISTBASE_FOREACH (Sequence *, seq_dst, &scene_dst->ed->seqbase) {
|
|
/* Copy animation curves from seq_dst (if any). */
|
|
sequencer_copy_animation(scene_src, &fcurves_dst, &drivers_dst, seq_dst);
|
|
}
|
|
|
|
if (!BLI_listbase_is_empty(&fcurves_dst) || !BLI_listbase_is_empty(&drivers_dst)) {
|
|
BLI_assert(scene_dst->adt == nullptr);
|
|
bAction *act_dst = blender::animrig::id_action_ensure(bmain_src, &scene_dst->id);
|
|
BLI_movelisttolist(&act_dst->curves, &fcurves_dst);
|
|
BLI_movelisttolist(&scene_dst->adt->drivers, &drivers_dst);
|
|
}
|
|
|
|
/* Nullify all ID pointers that we don't want to copy. For example, we don't want
|
|
* to copy whole scenes. We have to come up with a proper idea of how to copy and
|
|
* paste scene strips.
|
|
*/
|
|
IDRemapper *id_remapper = BKE_id_remapper_create();
|
|
BKE_library_foreach_ID_link(
|
|
bmain_src, &scene_dst->id, gather_strip_data_ids_to_null, id_remapper, IDWALK_RECURSE);
|
|
|
|
BKE_libblock_remap_multiple(bmain_src, id_remapper, 0);
|
|
BKE_id_remapper_free(id_remapper);
|
|
|
|
/* Ensure that there are no old copy tags around */
|
|
BKE_blendfile_write_partial_begin(bmain_src);
|
|
/* Tag the scene copy so we can pull in all scrip dependencies. */
|
|
BKE_copybuffer_copy_tag_ID(&scene_dst->id);
|
|
/* Create the copy/paste temp file */
|
|
bool retval = BKE_copybuffer_copy_end(bmain_src, filepath, reports);
|
|
|
|
/* Clean up the action ID if we created any. */
|
|
if (scene_dst->adt != nullptr && scene_dst->adt->action != nullptr) {
|
|
BKE_id_delete(bmain_src, scene_dst->adt->action);
|
|
}
|
|
|
|
/* Cleanup the dummy scene file */
|
|
BKE_id_delete(bmain_src, scene_dst);
|
|
|
|
return retval;
|
|
}
|
|
|
|
int sequencer_clipboard_copy_exec(bContext *C, wmOperator *op)
|
|
{
|
|
Main *bmain = CTX_data_main(C);
|
|
Scene *scene = CTX_data_scene(C);
|
|
Editing *ed = SEQ_editing_get(scene);
|
|
|
|
if (SEQ_transform_seqbase_isolated_sel_check(ed->seqbasep) == false) {
|
|
BKE_report(op->reports, RPT_ERROR, "Please select all related strips");
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
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!");
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
/* We are all done! */
|
|
BKE_report(
|
|
op->reports, RPT_INFO, "Copied the selected Video Sequencer strips to internal clipboard");
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Paste Operator Helper functions
|
|
*/
|
|
|
|
static bool sequencer_paste_animation(Main *bmain_dst, Scene *scene_dst, Scene *scene_src)
|
|
{
|
|
if (!SEQ_animation_curves_exist(scene_src) && !SEQ_animation_drivers_exist(scene_src)) {
|
|
return false;
|
|
}
|
|
|
|
bAction *act_dst;
|
|
|
|
if (scene_dst->adt != nullptr && scene_dst->adt->action != nullptr) {
|
|
act_dst = scene_dst->adt->action;
|
|
}
|
|
else {
|
|
/* get action to add F-Curve+keyframe to */
|
|
act_dst = blender::animrig::id_action_ensure(bmain_dst, &scene_dst->id);
|
|
}
|
|
|
|
LISTBASE_FOREACH (FCurve *, fcu, &scene_src->adt->action->curves) {
|
|
BLI_addtail(&act_dst->curves, BKE_fcurve_copy(fcu));
|
|
}
|
|
LISTBASE_FOREACH (FCurve *, fcu, &scene_src->adt->drivers) {
|
|
BLI_addtail(&scene_dst->adt->drivers, BKE_fcurve_copy(fcu));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int 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 & LIB_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_scene(C);
|
|
Editing *ed_dst = SEQ_editing_ensure(scene_dst); /* Creates "ed" if it's missing. */
|
|
int ofs;
|
|
|
|
ED_sequencer_deselect_all(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 (Sequence *, seq, &scene_src->ed->seqbase) {
|
|
if (SEQ_time_left_handle_frame_get(scene_src, seq) < min_seq_startdisp) {
|
|
min_seq_startdisp = SEQ_time_left_handle_frame_get(scene_src, seq);
|
|
}
|
|
}
|
|
/* Paste strips relative to the current-frame. */
|
|
ofs = scene_dst->r.cfra - min_seq_startdisp;
|
|
}
|
|
|
|
Sequence *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.
|
|
*/
|
|
SeqAnimationBackup 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_sequence_base_dupli_recursive() takes care of generating
|
|
* new UIDs for sequences in the new list. */
|
|
SEQ_sequence_base_dupli_recursive(
|
|
scene_src, scene_dst, &nseqbase, &scene_src->ed->seqbase, 0, 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);
|
|
|
|
Sequence *iseq_first = static_cast<Sequence *>(nseqbase.first);
|
|
BLI_movelisttolist(ed_dst->seqbasep, &nseqbase);
|
|
/* Restore "first" pointer as BLI_movelisttolist sets it to nullptr */
|
|
nseqbase.first = iseq_first;
|
|
|
|
LISTBASE_FOREACH (Sequence *, iseq, &nseqbase) {
|
|
if (STREQ(iseq->name, active_seq_name.c_str())) {
|
|
SEQ_select_active_set(scene_dst, iseq);
|
|
}
|
|
/* 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(iseq, scene_dst);
|
|
}
|
|
|
|
LISTBASE_FOREACH (Sequence *, iseq, &nseqbase) {
|
|
/* Translate after name has been changed, otherwise this will affect animdata of original
|
|
* strip. */
|
|
SEQ_transform_translate_sequence(scene_dst, iseq, ofs);
|
|
/* Ensure, that pasted strips don't overlap. */
|
|
if (SEQ_transform_test_overlap(scene_dst, ed_dst->seqbasep, iseq)) {
|
|
SEQ_transform_seqbase_shuffle(ed_dst->seqbasep, iseq, scene_dst);
|
|
}
|
|
}
|
|
|
|
SEQ_animation_restore_original(scene_dst, &animation_backup);
|
|
|
|
DEG_id_tag_update(&scene_dst->id, ID_RECALC_SEQUENCER_STRIPS);
|
|
DEG_relations_tag_update(bmain_dst);
|
|
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, 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;
|
|
}
|