From 56116bbdf434b57e4d74b1112b4b8111a7c8fa9a Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 10 Feb 2020 12:58:59 +0100 Subject: Cleanup/refactor: Rename `BKE_library` files to `BKE_lib`. Note that `BKE_library.h`/`library.c` were renamed to `BKE_lib_id.h`/`lib_id.c` to avoid having a too generic name here. Part of T72604. --- source/blender/blenkernel/intern/DerivedMesh.c | 2 +- source/blender/blenkernel/intern/action.c | 4 +- source/blender/blenkernel/intern/anim_sys.c | 6 +- source/blender/blenkernel/intern/armature.c | 4 +- .../blender/blenkernel/intern/blender_copybuffer.c | 2 +- source/blender/blenkernel/intern/blendfile.c | 2 +- source/blender/blenkernel/intern/bpath.c | 2 +- source/blender/blenkernel/intern/brush.c | 8 +- source/blender/blenkernel/intern/cachefile.c | 4 +- source/blender/blenkernel/intern/camera.c | 4 +- source/blender/blenkernel/intern/collection.c | 6 +- source/blender/blenkernel/intern/constraint.c | 2 +- source/blender/blenkernel/intern/crazyspace.c | 2 +- source/blender/blenkernel/intern/curve.c | 4 +- source/blender/blenkernel/intern/displist.c | 2 +- source/blender/blenkernel/intern/dynamicpaint.c | 2 +- source/blender/blenkernel/intern/editmesh.c | 2 +- source/blender/blenkernel/intern/fluid.c | 2 +- source/blender/blenkernel/intern/font.c | 2 +- source/blender/blenkernel/intern/freestyle.c | 2 +- source/blender/blenkernel/intern/gpencil.c | 4 +- .../blender/blenkernel/intern/gpencil_modifier.c | 4 +- source/blender/blenkernel/intern/idprop.c | 2 +- source/blender/blenkernel/intern/image.c | 4 +- source/blender/blenkernel/intern/ipo.c | 2 +- source/blender/blenkernel/intern/key.c | 4 +- source/blender/blenkernel/intern/lattice.c | 4 +- source/blender/blenkernel/intern/layer.c | 4 +- source/blender/blenkernel/intern/lib_id.c | 2646 ++++++++++++++++++++ source/blender/blenkernel/intern/lib_override.c | 954 +++++++ source/blender/blenkernel/intern/lib_query.c | 1477 +++++++++++ source/blender/blenkernel/intern/lib_remap.c | 1184 +++++++++ source/blender/blenkernel/intern/library.c | 2646 -------------------- .../blender/blenkernel/intern/library_override.c | 954 ------- source/blender/blenkernel/intern/library_query.c | 1477 ----------- source/blender/blenkernel/intern/library_remap.c | 1184 --------- source/blender/blenkernel/intern/light.c | 4 +- source/blender/blenkernel/intern/lightprobe.c | 4 +- source/blender/blenkernel/intern/linestyle.c | 4 +- source/blender/blenkernel/intern/main.c | 4 +- source/blender/blenkernel/intern/mask.c | 4 +- source/blender/blenkernel/intern/material.c | 4 +- source/blender/blenkernel/intern/mball.c | 4 +- source/blender/blenkernel/intern/mesh.c | 4 +- source/blender/blenkernel/intern/mesh_convert.c | 4 +- source/blender/blenkernel/intern/mesh_merge.c | 2 +- source/blender/blenkernel/intern/mesh_mirror.c | 4 +- .../blender/blenkernel/intern/mesh_remesh_voxel.c | 2 +- source/blender/blenkernel/intern/mesh_runtime.c | 2 +- source/blender/blenkernel/intern/modifier.c | 4 +- source/blender/blenkernel/intern/movieclip.c | 4 +- .../blender/blenkernel/intern/multires_reshape.c | 2 +- source/blender/blenkernel/intern/nla.c | 8 +- source/blender/blenkernel/intern/node.c | 4 +- source/blender/blenkernel/intern/object.c | 8 +- source/blender/blenkernel/intern/paint.c | 6 +- source/blender/blenkernel/intern/paint_toolslots.c | 2 +- source/blender/blenkernel/intern/particle.c | 4 +- .../blenkernel/intern/particle_distribute.c | 2 +- source/blender/blenkernel/intern/particle_system.c | 4 +- source/blender/blenkernel/intern/pointcache.c | 2 +- source/blender/blenkernel/intern/rigidbody.c | 4 +- source/blender/blenkernel/intern/scene.c | 8 +- source/blender/blenkernel/intern/seqeffects.c | 2 +- source/blender/blenkernel/intern/seqprefetch.c | 2 +- source/blender/blenkernel/intern/sequencer.c | 2 +- source/blender/blenkernel/intern/shader_fx.c | 4 +- source/blender/blenkernel/intern/shrinkwrap.c | 2 +- source/blender/blenkernel/intern/sound.c | 4 +- source/blender/blenkernel/intern/speaker.c | 4 +- source/blender/blenkernel/intern/text.c | 4 +- source/blender/blenkernel/intern/texture.c | 4 +- source/blender/blenkernel/intern/tracking.c | 2 +- source/blender/blenkernel/intern/undo_system.c | 2 +- source/blender/blenkernel/intern/workspace.c | 2 +- source/blender/blenkernel/intern/world.c | 4 +- source/blender/blenkernel/intern/writeffmpeg.c | 2 +- 77 files changed, 6381 insertions(+), 6381 deletions(-) create mode 100644 source/blender/blenkernel/intern/lib_id.c create mode 100644 source/blender/blenkernel/intern/lib_override.c create mode 100644 source/blender/blenkernel/intern/lib_query.c create mode 100644 source/blender/blenkernel/intern/lib_remap.c delete mode 100644 source/blender/blenkernel/intern/library.c delete mode 100644 source/blender/blenkernel/intern/library_override.c delete mode 100644 source/blender/blenkernel/intern/library_query.c delete mode 100644 source/blender/blenkernel/intern/library_remap.c (limited to 'source/blender/blenkernel/intern') diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index f9e7627a8dd..c04079bed22 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -48,7 +48,7 @@ #include "BKE_editmesh.h" #include "BKE_key.h" #include "BKE_layer.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_material.h" #include "BKE_modifier.h" #include "BKE_mesh.h" diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index b474e3f5ec5..97b6a7b2f40 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -50,7 +50,7 @@ #include "BKE_deform.h" #include "BKE_fcurve.h" #include "BKE_idprop.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_object.h" @@ -123,7 +123,7 @@ void BKE_action_free(bAction *act) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_action_copy_data(Main *UNUSED(bmain), bAction *act_dst, diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index be6622e5d42..2f4d58a1992 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -55,7 +55,7 @@ #include "BKE_context.h" #include "BKE_fcurve.h" #include "BKE_global.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_nla.h" @@ -307,7 +307,7 @@ bool BKE_animdata_id_is_animated(const struct ID *id) /** * Make a copy of the given AnimData - to be used when copying data-blocks. * \param flag: Control ID pointers management, - * see LIB_ID_CREATE_.../LIB_ID_COPY_... flags in BKE_library.h + * see LIB_ID_CREATE_.../LIB_ID_COPY_... flags in BKE_lib_id.h * \return The copied animdata. */ AnimData *BKE_animdata_copy(Main *bmain, AnimData *adt, const int flag) @@ -351,7 +351,7 @@ AnimData *BKE_animdata_copy(Main *bmain, AnimData *adt, const int flag) /** * \param flag: Control ID pointers management, - * see LIB_ID_CREATE_.../LIB_ID_COPY_... flags in BKE_library.h + * see LIB_ID_CREATE_.../LIB_ID_COPY_... flags in BKE_lib_id.h * \return true is successfully copied. */ bool BKE_animdata_copy_id(Main *bmain, ID *id_to, ID *id_from, const int flag) diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index e4da10797ff..ed752986ddd 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -58,7 +58,7 @@ #include "BKE_deform.h" #include "BKE_displist.h" #include "BKE_idprop.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_lattice.h" #include "BKE_main.h" #include "BKE_object.h" @@ -194,7 +194,7 @@ static void copy_bonechildren_custom_handles(Bone *bone_dst, bArmature *arm_dst) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_armature_copy_data(Main *UNUSED(bmain), bArmature *arm_dst, diff --git a/source/blender/blenkernel/intern/blender_copybuffer.c b/source/blender/blenkernel/intern/blender_copybuffer.c index 32c6f74dc2d..f78eefa0a27 100644 --- a/source/blender/blenkernel/intern/blender_copybuffer.c +++ b/source/blender/blenkernel/intern/blender_copybuffer.c @@ -41,7 +41,7 @@ #include "BKE_context.h" #include "BKE_global.h" #include "BKE_layer.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_scene.h" diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 62173393256..d1188bdb220 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -50,7 +50,7 @@ #include "BKE_ipo.h" #include "BKE_keyconfig.h" #include "BKE_layer.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_report.h" #include "BKE_scene.h" diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index 90b26f8c288..835fd645e25 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -66,7 +66,7 @@ #include "BLI_utildefines.h" #include "BKE_font.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" #include "BKE_report.h" diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 4f98290874e..230fe831184 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -33,9 +33,9 @@ #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_context.h" -#include "BKE_library.h" -#include "BKE_library_query.h" -#include "BKE_library_remap.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" +#include "BKE_lib_remap.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_paint.h" @@ -704,7 +704,7 @@ struct Brush *BKE_brush_first_search(struct Main *bmain, const eObjectMode ob_mo * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_brush_copy_data(Main *UNUSED(bmain), Brush *brush_dst, diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index 3b0f4d9c2aa..cfef2a7e8e7 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -39,7 +39,7 @@ #include "BKE_animsys.h" #include "BKE_cachefile.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_scene.h" @@ -192,7 +192,7 @@ void BKE_cachefile_free(CacheFile *cache_file) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_cachefile_copy_data(Main *UNUSED(bmain), CacheFile *cache_file_dst, diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index f70c5bb99d6..77a7c9581ac 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -42,7 +42,7 @@ #include "BKE_camera.h" #include "BKE_object.h" #include "BKE_layer.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_scene.h" #include "BKE_screen.h" @@ -79,7 +79,7 @@ void *BKE_camera_add(Main *bmain, const char *name) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_camera_copy_data(Main *UNUSED(bmain), Camera *cam_dst, diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index c1c3cc62f11..ba0f019e700 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -32,8 +32,8 @@ #include "BKE_icons.h" #include "BKE_idprop.h" #include "BKE_layer.h" -#include "BKE_library.h" -#include "BKE_library_remap.h" +#include "BKE_lib_id.h" +#include "BKE_lib_remap.h" #include "BKE_main.h" #include "BKE_object.h" #include "BKE_rigidbody.h" @@ -196,7 +196,7 @@ bool BKE_collection_delete(Main *bmain, Collection *collection, bool hierarchy) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_collection_copy_data(Main *bmain, Collection *collection_dst, diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 229578a38b7..76acaf5c91c 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -65,7 +65,7 @@ #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_idprop.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_mesh_runtime.h" #include "BKE_movieclip.h" #include "BKE_object.h" diff --git a/source/blender/blenkernel/intern/crazyspace.c b/source/blender/blenkernel/intern/crazyspace.c index 85b58da61de..e0abe836bf8 100644 --- a/source/blender/blenkernel/intern/crazyspace.c +++ b/source/blender/blenkernel/intern/crazyspace.c @@ -39,7 +39,7 @@ #include "BKE_multires.h" #include "BKE_mesh.h" #include "BKE_editmesh.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "DEG_depsgraph_query.h" diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 8246c3d9ff4..fab212f0944 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -49,7 +49,7 @@ #include "BKE_displist.h" #include "BKE_font.h" #include "BKE_key.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_object.h" #include "BKE_material.h" @@ -189,7 +189,7 @@ Curve *BKE_curve_add(Main *bmain, const char *name, int type) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_curve_copy_data(Main *bmain, Curve *cu_dst, const Curve *cu_src, const int flag) { diff --git a/source/blender/blenkernel/intern/displist.c b/source/blender/blenkernel/intern/displist.c index 46f6a604eaa..fc310ee720a 100644 --- a/source/blender/blenkernel/intern/displist.c +++ b/source/blender/blenkernel/intern/displist.c @@ -43,7 +43,7 @@ #include "BKE_displist.h" #include "BKE_cdderivedmesh.h" #include "BKE_object.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_mball.h" #include "BKE_mball_tessellate.h" #include "BKE_mesh.h" diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index a70e5b67a15..48c5aff721d 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -58,7 +58,7 @@ #include "BKE_dynamicpaint.h" #include "BKE_effect.h" #include "BKE_image.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_mesh.h" diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index 9b67a4fb925..4c076256d9f 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -31,7 +31,7 @@ #include "BKE_editmesh.h" #include "BKE_cdderivedmesh.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_iterators.h" #include "BKE_object.h" diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 2f13210ca44..c3db083810c 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -38,7 +38,7 @@ #include "BKE_effect.h" #include "BKE_fluid.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_modifier.h" #include "BKE_pointcache.h" diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index 47b6fbfb4f8..3d840c6232c 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -47,7 +47,7 @@ #include "DNA_object_types.h" #include "BKE_packedFile.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_font.h" #include "BKE_global.h" #include "BKE_main.h" diff --git a/source/blender/blenkernel/intern/freestyle.c b/source/blender/blenkernel/intern/freestyle.c index 19c5012dc54..aa3b4f1ef5e 100644 --- a/source/blender/blenkernel/intern/freestyle.c +++ b/source/blender/blenkernel/intern/freestyle.c @@ -31,7 +31,7 @@ #include "BLI_string_utils.h" #include "BKE_freestyle.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_linestyle.h" // function declarations diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index db137d26005..e3d6e73e8c9 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -55,7 +55,7 @@ #include "BKE_deform.h" #include "BKE_gpencil.h" #include "BKE_icons.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_object.h" @@ -647,7 +647,7 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_gpencil_copy_data(bGPdata *gpd_dst, const bGPdata *gpd_src, const int UNUSED(flag)) { diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index 181cb2d38a9..ebb927a7d60 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -39,8 +39,8 @@ #include "DNA_gpencil_types.h" #include "DNA_gpencil_modifier_types.h" -#include "BKE_library.h" -#include "BKE_library_query.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" #include "BKE_gpencil.h" #include "BKE_lattice.h" #include "BKE_material.h" diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index e3b27236616..e5b710b3619 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -32,7 +32,7 @@ #include "BLI_math.h" #include "BKE_idprop.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "CLG_log.h" diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index be354b04157..ebe90072f63 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -71,7 +71,7 @@ #include "BKE_global.h" #include "BKE_icons.h" #include "BKE_image.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_packedFile.h" #include "BKE_report.h" @@ -417,7 +417,7 @@ static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_image_copy_data(Main *UNUSED(bmain), Image *ima_dst, const Image *ima_src, const int flag) { diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index adf5d55efaa..f84d0681dad 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -63,7 +63,7 @@ #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_key.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_nla.h" #include "BKE_sequencer.h" diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 6e8677b5422..6bc494ae3ee 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -48,7 +48,7 @@ #include "BKE_deform.h" #include "BKE_key.h" #include "BKE_lattice.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_editmesh.h" @@ -158,7 +158,7 @@ Key *BKE_key_add(Main *bmain, ID *id) /* common function */ * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_key_copy_data(Main *UNUSED(bmain), Key *key_dst, diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index a09e97d4fed..ebfffc8279d 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -50,7 +50,7 @@ #include "BKE_displist.h" #include "BKE_key.h" #include "BKE_lattice.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_object.h" @@ -274,7 +274,7 @@ Lattice *BKE_lattice_add(Main *bmain, const char *name) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_lattice_copy_data(Main *bmain, Lattice *lt_dst, const Lattice *lt_src, const int flag) { diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 34c1c5ecab4..f9f15918d67 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -32,7 +32,7 @@ #include "BKE_freestyle.h" #include "BKE_idprop.h" #include "BKE_layer.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" #include "BKE_object.h" @@ -388,7 +388,7 @@ static void layer_collections_copy_data(ViewLayer *view_layer_dst, /** * Only copy internal data of ViewLayer from source to already allocated/initialized destination. * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_view_layer_copy_data(Scene *scene_dst, const Scene *UNUSED(scene_src), diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c new file mode 100644 index 00000000000..470de08b5f4 --- /dev/null +++ b/source/blender/blenkernel/intern/lib_id.c @@ -0,0 +1,2646 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup bke + * + * Contains management of ID's and libraries + * allocate and free of all library data + */ + +#include +#include +#include +#include +#include +#include + +#include "CLG_log.h" + +#include "MEM_guardedalloc.h" + +/* all types are needed here, in order to do memory operations */ +#include "DNA_anim_types.h" +#include "DNA_armature_types.h" +#include "DNA_brush_types.h" +#include "DNA_cachefile_types.h" +#include "DNA_camera_types.h" +#include "DNA_collection_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_ipo_types.h" +#include "DNA_key_types.h" +#include "DNA_light_types.h" +#include "DNA_lattice_types.h" +#include "DNA_linestyle_types.h" +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meta_types.h" +#include "DNA_modifier_types.h" +#include "DNA_movieclip_types.h" +#include "DNA_mask_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_lightprobe_types.h" +#include "DNA_rigidbody_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_speaker_types.h" +#include "DNA_sound_types.h" +#include "DNA_text_types.h" +#include "DNA_vfont_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_world_types.h" +#include "DNA_workspace_types.h" + +#include "BLI_utildefines.h" + +#include "BLI_bitmap.h" +#include "BLI_blenlib.h" +#include "BLI_ghash.h" +#include "BLI_linklist.h" +#include "BLI_memarena.h" +#include "BLI_string_utils.h" + +#include "BLT_translation.h" + +#include "BKE_action.h" +#include "BKE_animsys.h" +#include "BKE_armature.h" +#include "BKE_bpath.h" +#include "BKE_brush.h" +#include "BKE_camera.h" +#include "BKE_cachefile.h" +#include "BKE_collection.h" +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_font.h" +#include "BKE_global.h" +#include "BKE_gpencil.h" +#include "BKE_idcode.h" +#include "BKE_idprop.h" +#include "BKE_image.h" +#include "BKE_key.h" +#include "BKE_light.h" +#include "BKE_lattice.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" +#include "BKE_lib_remap.h" +#include "BKE_linestyle.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_material.h" +#include "BKE_main.h" +#include "BKE_mball.h" +#include "BKE_mask.h" +#include "BKE_movieclip.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_particle.h" +#include "BKE_packedFile.h" +#include "BKE_lightprobe.h" +#include "BKE_rigidbody.h" +#include "BKE_sound.h" +#include "BKE_speaker.h" +#include "BKE_scene.h" +#include "BKE_text.h" +#include "BKE_texture.h" +#include "BKE_world.h" + +#include "DEG_depsgraph.h" + +#include "RNA_access.h" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "atomic_ops.h" + +//#define DEBUG_TIME + +#ifdef DEBUG_TIME +# include "PIL_time_utildefines.h" +#endif + +static CLG_LogRef LOG = {"bke.library"}; + +/* GS reads the memory pointed at in a specific ordering. + * only use this definition, makes little and big endian systems + * work fine, in conjunction with MAKE_ID */ + +/* ************* general ************************ */ + +/* this has to be called from each make_local_* func, we could call + * from id_make_local() but then the make local functions would not be self + * contained. + * also note that the id _must_ have a library - campbell */ +void BKE_id_lib_local_paths(Main *bmain, Library *lib, ID *id) +{ + const char *bpath_user_data[2] = {BKE_main_blendfile_path(bmain), lib->filepath}; + + BKE_bpath_traverse_id(bmain, + id, + BKE_bpath_relocate_visitor, + BKE_BPATH_TRAVERSE_SKIP_MULTIFILE, + (void *)bpath_user_data); +} + +void id_lib_extern(ID *id) +{ + if (id && ID_IS_LINKED(id)) { + BLI_assert(BKE_idcode_is_linkable(GS(id->name))); + if (id->tag & LIB_TAG_INDIRECT) { + id->tag &= ~LIB_TAG_INDIRECT; + id->flag &= ~LIB_INDIRECT_WEAK_LINK; + id->tag |= LIB_TAG_EXTERN; + id->lib->parent = NULL; + } + } +} + +void id_lib_indirect_weak_link(ID *id) +{ + if (id && ID_IS_LINKED(id)) { + BLI_assert(BKE_idcode_is_linkable(GS(id->name))); + if (id->tag & LIB_TAG_INDIRECT) { + id->flag |= LIB_INDIRECT_WEAK_LINK; + } + } +} + +/** + * Ensure we have a real user + * + * \note Now that we have flags, we could get rid of the 'fake_user' special case, + * flags are enough to ensure we always have a real user. + * However, #ID_REAL_USERS is used in several places outside of core lib.c, + * so think we can wait later to make this change. + */ +void id_us_ensure_real(ID *id) +{ + if (id) { + const int limit = ID_FAKE_USERS(id); + id->tag |= LIB_TAG_EXTRAUSER; + if (id->us <= limit) { + if (id->us < limit || ((id->us == limit) && (id->tag & LIB_TAG_EXTRAUSER_SET))) { + CLOG_ERROR(&LOG, + "ID user count error: %s (from '%s')", + id->name, + id->lib ? id->lib->filepath : "[Main]"); + BLI_assert(0); + } + id->us = limit + 1; + id->tag |= LIB_TAG_EXTRAUSER_SET; + } + } +} + +void id_us_clear_real(ID *id) +{ + if (id && (id->tag & LIB_TAG_EXTRAUSER)) { + if (id->tag & LIB_TAG_EXTRAUSER_SET) { + id->us--; + BLI_assert(id->us >= ID_FAKE_USERS(id)); + } + id->tag &= ~(LIB_TAG_EXTRAUSER | LIB_TAG_EXTRAUSER_SET); + } +} + +/** + * Same as \a id_us_plus, but does not handle lib indirect -> extern. + * Only used by readfile.c so far, but simpler/safer to keep it here nonetheless. + */ +void id_us_plus_no_lib(ID *id) +{ + if (id) { + if ((id->tag & LIB_TAG_EXTRAUSER) && (id->tag & LIB_TAG_EXTRAUSER_SET)) { + BLI_assert(id->us >= 1); + /* No need to increase count, just tag extra user as no more set. + * Avoids annoying & inconsistent +1 in user count. */ + id->tag &= ~LIB_TAG_EXTRAUSER_SET; + } + else { + BLI_assert(id->us >= 0); + id->us++; + } + } +} + +void id_us_plus(ID *id) +{ + if (id) { + id_us_plus_no_lib(id); + id_lib_extern(id); + } +} + +/* decrements the user count for *id. */ +void id_us_min(ID *id) +{ + if (id) { + const int limit = ID_FAKE_USERS(id); + + if (id->us <= limit) { + CLOG_ERROR(&LOG, + "ID user decrement error: %s (from '%s'): %d <= %d", + id->name, + id->lib ? id->lib->filepath : "[Main]", + id->us, + limit); + BLI_assert(0); + id->us = limit; + } + else { + id->us--; + } + + if ((id->us == limit) && (id->tag & LIB_TAG_EXTRAUSER)) { + /* We need an extra user here, but never actually incremented user count for it so far, + * do it now. */ + id_us_ensure_real(id); + } + } +} + +void id_fake_user_set(ID *id) +{ + if (id && !(id->flag & LIB_FAKEUSER)) { + id->flag |= LIB_FAKEUSER; + id_us_plus(id); + } +} + +void id_fake_user_clear(ID *id) +{ + if (id && (id->flag & LIB_FAKEUSER)) { + id->flag &= ~LIB_FAKEUSER; + id_us_min(id); + } +} + +void BKE_id_clear_newpoin(ID *id) +{ + if (id->newid) { + id->newid->tag &= ~LIB_TAG_NEW; + } + id->newid = NULL; +} + +static int id_expand_local_callback(void *UNUSED(user_data), + struct ID *id_self, + struct ID **id_pointer, + int cb_flag) +{ + if (cb_flag & IDWALK_CB_PRIVATE) { + return IDWALK_RET_NOP; + } + + /* Can happen that we get un-linkable ID here, e.g. with shape-key referring to itself + * (through drivers)... + * Just skip it, shape key can only be either indirectly linked, or fully local, period. + * And let's curse one more time that stupid useless shapekey ID type! */ + if (*id_pointer && *id_pointer != id_self && BKE_idcode_is_linkable(GS((*id_pointer)->name))) { + id_lib_extern(*id_pointer); + } + + return IDWALK_RET_NOP; +} + +/** + * Expand ID usages of given id as 'extern' (and no more indirect) linked data. + * Used by ID copy/make_local functions. + */ +void BKE_id_expand_local(Main *bmain, ID *id) +{ + BKE_library_foreach_ID_link(bmain, id, id_expand_local_callback, NULL, IDWALK_READONLY); +} + +/** + * Ensure new (copied) ID is fully made local. + */ +void BKE_id_copy_ensure_local(Main *bmain, const ID *old_id, ID *new_id) +{ + if (ID_IS_LINKED(old_id)) { + BKE_id_expand_local(bmain, new_id); + BKE_id_lib_local_paths(bmain, old_id->lib, new_id); + } +} + +/** + * Generic 'make local' function, works for most of data-block types... + */ +void BKE_id_make_local_generic(Main *bmain, + ID *id, + const bool id_in_mainlist, + const bool lib_local) +{ + bool is_local = false, is_lib = false; + + /* - only lib users: do nothing (unless force_local is set) + * - only local users: set flag + * - mixed: make copy + * In case we make a whole lib's content local, + * we always want to localize, and we skip remapping (done later). + */ + + if (!ID_IS_LINKED(id)) { + return; + } + + BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); + + if (lib_local || is_local) { + if (!is_lib) { + id_clear_lib_data_ex(bmain, id, id_in_mainlist); + BKE_id_expand_local(bmain, id); + } + else { + ID *id_new; + + /* Should not fail in expected use cases, + * but a few ID types cannot be copied (LIB, WM, SCR...). */ + if (BKE_id_copy(bmain, id, &id_new)) { + id_new->us = 0; + + /* setting newid is mandatory for complex make_lib_local logic... */ + ID_NEW_SET(id, id_new); + Key *key = BKE_key_from_id(id), *key_new = BKE_key_from_id(id); + if (key && key_new) { + ID_NEW_SET(key, key_new); + } + bNodeTree *ntree = ntreeFromID(id), *ntree_new = ntreeFromID(id_new); + if (ntree && ntree_new) { + ID_NEW_SET(ntree, ntree_new); + } + + if (!lib_local) { + BKE_libblock_remap(bmain, id, id_new, ID_REMAP_SKIP_INDIRECT_USAGE); + } + } + } + } +} + +/** + * Calls the appropriate make_local method for the block, unless test is set. + * + * \note Always set ID->newid pointer in case it gets duplicated... + * + * \param lib_local: Special flag used when making a whole library's content local, + * it needs specific handling. + * + * \return true if the block can be made local. + */ +bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local) +{ + /* We don't care whether ID is directly or indirectly linked + * in case we are making a whole lib local... */ + if (!lib_local && (id->tag & LIB_TAG_INDIRECT)) { + return false; + } + + switch ((ID_Type)GS(id->name)) { + case ID_SCE: + if (!test) { + BKE_scene_make_local(bmain, (Scene *)id, lib_local); + } + return true; + case ID_OB: + if (!test) { + BKE_object_make_local(bmain, (Object *)id, lib_local); + } + return true; + case ID_ME: + if (!test) { + BKE_mesh_make_local(bmain, (Mesh *)id, lib_local); + } + return true; + case ID_CU: + if (!test) { + BKE_curve_make_local(bmain, (Curve *)id, lib_local); + } + return true; + case ID_MB: + if (!test) { + BKE_mball_make_local(bmain, (MetaBall *)id, lib_local); + } + return true; + case ID_MA: + if (!test) { + BKE_material_make_local(bmain, (Material *)id, lib_local); + } + return true; + case ID_TE: + if (!test) { + BKE_texture_make_local(bmain, (Tex *)id, lib_local); + } + return true; + case ID_IM: + if (!test) { + BKE_image_make_local(bmain, (Image *)id, lib_local); + } + return true; + case ID_LT: + if (!test) { + BKE_lattice_make_local(bmain, (Lattice *)id, lib_local); + } + return true; + case ID_LA: + if (!test) { + BKE_light_make_local(bmain, (Light *)id, lib_local); + } + return true; + case ID_CA: + if (!test) { + BKE_camera_make_local(bmain, (Camera *)id, lib_local); + } + return true; + case ID_SPK: + if (!test) { + BKE_speaker_make_local(bmain, (Speaker *)id, lib_local); + } + return true; + case ID_LP: + if (!test) { + BKE_lightprobe_make_local(bmain, (LightProbe *)id, lib_local); + } + return true; + case ID_WO: + if (!test) { + BKE_world_make_local(bmain, (World *)id, lib_local); + } + return true; + case ID_VF: + if (!test) { + BKE_vfont_make_local(bmain, (VFont *)id, lib_local); + } + return true; + case ID_TXT: + if (!test) { + BKE_text_make_local(bmain, (Text *)id, lib_local); + } + return true; + case ID_SO: + if (!test) { + BKE_sound_make_local(bmain, (bSound *)id, lib_local); + } + return true; + case ID_GR: + if (!test) { + BKE_collection_make_local(bmain, (Collection *)id, lib_local); + } + return true; + case ID_AR: + if (!test) { + BKE_armature_make_local(bmain, (bArmature *)id, lib_local); + } + return true; + case ID_AC: + if (!test) { + BKE_action_make_local(bmain, (bAction *)id, lib_local); + } + return true; + case ID_NT: + if (!test) { + ntreeMakeLocal(bmain, (bNodeTree *)id, true, lib_local); + } + return true; + case ID_BR: + if (!test) { + BKE_brush_make_local(bmain, (Brush *)id, lib_local); + } + return true; + case ID_PA: + if (!test) { + BKE_particlesettings_make_local(bmain, (ParticleSettings *)id, lib_local); + } + return true; + case ID_GD: + if (!test) { + BKE_gpencil_make_local(bmain, (bGPdata *)id, lib_local); + } + return true; + case ID_MC: + if (!test) { + BKE_movieclip_make_local(bmain, (MovieClip *)id, lib_local); + } + return true; + case ID_MSK: + if (!test) { + BKE_mask_make_local(bmain, (Mask *)id, lib_local); + } + return true; + case ID_LS: + if (!test) { + BKE_linestyle_make_local(bmain, (FreestyleLineStyle *)id, lib_local); + } + return true; + case ID_PAL: + if (!test) { + BKE_palette_make_local(bmain, (Palette *)id, lib_local); + } + return true; + case ID_PC: + if (!test) { + BKE_paint_curve_make_local(bmain, (PaintCurve *)id, lib_local); + } + return true; + case ID_CF: + if (!test) { + BKE_cachefile_make_local(bmain, (CacheFile *)id, lib_local); + } + return true; + case ID_WS: + case ID_SCR: + /* A bit special: can be appended but not linked. Return false + * since supporting make-local doesn't make much sense. */ + return false; + case ID_LI: + case ID_KE: + case ID_WM: + return false; /* can't be linked */ + case ID_IP: + return false; /* deprecated */ + } + + return false; +} + +struct IDCopyLibManagementData { + const ID *id_src; + ID *id_dst; + int flag; +}; + +/* Increases usercount as required, and remap self ID pointers. */ +static int id_copy_libmanagement_cb(void *user_data, + ID *UNUSED(id_self), + ID **id_pointer, + int cb_flag) +{ + struct IDCopyLibManagementData *data = user_data; + ID *id = *id_pointer; + + /* Remap self-references to new copied ID. */ + if (id == data->id_src) { + /* We cannot use id_self here, it is not *always* id_dst (thanks to $£!+@#&/? nodetrees). */ + id = *id_pointer = data->id_dst; + } + + /* Increase used IDs refcount if needed and required. */ + if ((data->flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0 && (cb_flag & IDWALK_CB_USER)) { + id_us_plus(id); + } + + return IDWALK_RET_NOP; +} + +bool BKE_id_copy_is_allowed(const ID *id) +{ +#define LIB_ID_TYPES_NOCOPY \ + ID_LI, ID_SCR, ID_WM, ID_WS, /* Not supported */ \ + ID_IP /* Deprecated */ + + return !ELEM(GS(id->name), LIB_ID_TYPES_NOCOPY); + +#undef LIB_ID_TYPES_NOCOPY +} + +/** + * Generic entry point for copying a data-block (new API). + * + * \note Copy is only affecting given data-block + * (no ID used by copied one will be affected, besides usercount). + * There is only one exception, if #LIB_ID_COPY_ACTIONS is defined, + * actions used by animdata will be duplicated. + * + * \note Usercount of new copy is always set to 1. + * + * \param bmain: Main database, may be NULL only if LIB_ID_CREATE_NO_MAIN is specified. + * \param id: Source data-block. + * \param r_newid: Pointer to new (copied) ID pointer. + * \param flag: Set of copy options, see DNA_ID.h enum for details + * (leave to zero for default, full copy). + * \return False when copying that ID type is not supported, true otherwise. + */ +bool BKE_id_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int flag) +{ + BLI_assert(r_newid != NULL); + /* Make sure destination pointer is all good. */ + if ((flag & LIB_ID_CREATE_NO_ALLOCATE) == 0) { + *r_newid = NULL; + } + else { + if (*r_newid != NULL) { + /* Allow some garbage non-initialized memory to go in, and clean it up here. */ + const size_t size = BKE_libblock_get_alloc_info(GS(id->name), NULL); + memset(*r_newid, 0, size); + } + } + + /* Early output is source is NULL. */ + if (id == NULL) { + return false; + } + if (!BKE_id_copy_is_allowed(id)) { + return false; + } + + BKE_libblock_copy_ex(bmain, id, r_newid, flag); + + switch ((ID_Type)GS(id->name)) { + case ID_SCE: + BKE_scene_copy_data(bmain, (Scene *)*r_newid, (Scene *)id, flag); + break; + case ID_OB: + BKE_object_copy_data(bmain, (Object *)*r_newid, (Object *)id, flag); + break; + case ID_ME: + BKE_mesh_copy_data(bmain, (Mesh *)*r_newid, (Mesh *)id, flag); + break; + case ID_CU: + BKE_curve_copy_data(bmain, (Curve *)*r_newid, (Curve *)id, flag); + break; + case ID_MB: + BKE_mball_copy_data(bmain, (MetaBall *)*r_newid, (MetaBall *)id, flag); + break; + case ID_MA: + BKE_material_copy_data(bmain, (Material *)*r_newid, (Material *)id, flag); + break; + case ID_TE: + BKE_texture_copy_data(bmain, (Tex *)*r_newid, (Tex *)id, flag); + break; + case ID_IM: + BKE_image_copy_data(bmain, (Image *)*r_newid, (Image *)id, flag); + break; + case ID_LT: + BKE_lattice_copy_data(bmain, (Lattice *)*r_newid, (Lattice *)id, flag); + break; + case ID_LA: + BKE_light_copy_data(bmain, (Light *)*r_newid, (Light *)id, flag); + break; + case ID_SPK: + BKE_speaker_copy_data(bmain, (Speaker *)*r_newid, (Speaker *)id, flag); + break; + case ID_LP: + BKE_lightprobe_copy_data(bmain, (LightProbe *)*r_newid, (LightProbe *)id, flag); + break; + case ID_CA: + BKE_camera_copy_data(bmain, (Camera *)*r_newid, (Camera *)id, flag); + break; + case ID_KE: + BKE_key_copy_data(bmain, (Key *)*r_newid, (Key *)id, flag); + break; + case ID_WO: + BKE_world_copy_data(bmain, (World *)*r_newid, (World *)id, flag); + break; + case ID_TXT: + BKE_text_copy_data(bmain, (Text *)*r_newid, (Text *)id, flag); + break; + case ID_GR: + BKE_collection_copy_data(bmain, (Collection *)*r_newid, (Collection *)id, flag); + break; + case ID_AR: + BKE_armature_copy_data(bmain, (bArmature *)*r_newid, (bArmature *)id, flag); + break; + case ID_AC: + BKE_action_copy_data(bmain, (bAction *)*r_newid, (bAction *)id, flag); + break; + case ID_NT: + BKE_node_tree_copy_data(bmain, (bNodeTree *)*r_newid, (bNodeTree *)id, flag); + break; + case ID_BR: + BKE_brush_copy_data(bmain, (Brush *)*r_newid, (Brush *)id, flag); + break; + case ID_PA: + BKE_particlesettings_copy_data( + bmain, (ParticleSettings *)*r_newid, (ParticleSettings *)id, flag); + break; + case ID_GD: + BKE_gpencil_copy_data((bGPdata *)*r_newid, (bGPdata *)id, flag); + break; + case ID_MC: + BKE_movieclip_copy_data(bmain, (MovieClip *)*r_newid, (MovieClip *)id, flag); + break; + case ID_MSK: + BKE_mask_copy_data(bmain, (Mask *)*r_newid, (Mask *)id, flag); + break; + case ID_LS: + BKE_linestyle_copy_data( + bmain, (FreestyleLineStyle *)*r_newid, (FreestyleLineStyle *)id, flag); + break; + case ID_PAL: + BKE_palette_copy_data(bmain, (Palette *)*r_newid, (Palette *)id, flag); + break; + case ID_PC: + BKE_paint_curve_copy_data(bmain, (PaintCurve *)*r_newid, (PaintCurve *)id, flag); + break; + case ID_CF: + BKE_cachefile_copy_data(bmain, (CacheFile *)*r_newid, (CacheFile *)id, flag); + break; + case ID_SO: + BKE_sound_copy_data(bmain, (bSound *)*r_newid, (bSound *)id, flag); + break; + case ID_VF: + BKE_vfont_copy_data(bmain, (VFont *)*r_newid, (VFont *)id, flag); + break; + case ID_LI: + case ID_SCR: + case ID_WM: + case ID_WS: + case ID_IP: + BLI_assert(0); /* Should have been rejected at start of function! */ + break; + } + + /* Update ID refcount, remap pointers to self in new ID. */ + struct IDCopyLibManagementData data = { + .id_src = id, + .id_dst = *r_newid, + .flag = flag, + }; + BKE_library_foreach_ID_link(bmain, *r_newid, id_copy_libmanagement_cb, &data, IDWALK_NOP); + + /* Do not make new copy local in case we are copying outside of main... + * XXX TODO: is this behavior OK, or should we need own flag to control that? */ + if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) { + BLI_assert((flag & LIB_ID_COPY_KEEP_LIB) == 0); + BKE_id_copy_ensure_local(bmain, id, *r_newid); + } + else { + (*r_newid)->lib = id->lib; + } + + return true; +} + +/** + * Invokes the appropriate copy method for the block and returns the result in + * newid, unless test. Returns true if the block can be copied. + */ +bool BKE_id_copy(Main *bmain, const ID *id, ID **newid) +{ + return BKE_id_copy_ex(bmain, id, newid, LIB_ID_COPY_DEFAULT); +} + +/** + * Does a mere memory swap over the whole IDs data (including type-specific memory). + * \note Most internal ID data itself is not swapped (only IDProperties are). + */ +void BKE_id_swap(Main *bmain, ID *id_a, ID *id_b) +{ + BLI_assert(GS(id_a->name) == GS(id_b->name)); + + const ID id_a_back = *id_a; + const ID id_b_back = *id_b; + +#define CASE_SWAP(_gs, _type) \ + case _gs: \ + SWAP(_type, *(_type *)id_a, *(_type *)id_b); \ + break + + switch ((ID_Type)GS(id_a->name)) { + CASE_SWAP(ID_SCE, Scene); + CASE_SWAP(ID_LI, Library); + CASE_SWAP(ID_OB, Object); + CASE_SWAP(ID_ME, Mesh); + CASE_SWAP(ID_CU, Curve); + CASE_SWAP(ID_MB, MetaBall); + CASE_SWAP(ID_MA, Material); + CASE_SWAP(ID_TE, Tex); + CASE_SWAP(ID_IM, Image); + CASE_SWAP(ID_LT, Lattice); + CASE_SWAP(ID_LA, Light); + CASE_SWAP(ID_LP, LightProbe); + CASE_SWAP(ID_CA, Camera); + CASE_SWAP(ID_KE, Key); + CASE_SWAP(ID_WO, World); + CASE_SWAP(ID_SCR, bScreen); + CASE_SWAP(ID_VF, VFont); + CASE_SWAP(ID_TXT, Text); + CASE_SWAP(ID_SPK, Speaker); + CASE_SWAP(ID_SO, bSound); + CASE_SWAP(ID_GR, Collection); + CASE_SWAP(ID_AR, bArmature); + CASE_SWAP(ID_AC, bAction); + CASE_SWAP(ID_NT, bNodeTree); + CASE_SWAP(ID_BR, Brush); + CASE_SWAP(ID_PA, ParticleSettings); + CASE_SWAP(ID_WM, wmWindowManager); + CASE_SWAP(ID_WS, WorkSpace); + CASE_SWAP(ID_GD, bGPdata); + CASE_SWAP(ID_MC, MovieClip); + CASE_SWAP(ID_MSK, Mask); + CASE_SWAP(ID_LS, FreestyleLineStyle); + CASE_SWAP(ID_PAL, Palette); + CASE_SWAP(ID_PC, PaintCurve); + CASE_SWAP(ID_CF, CacheFile); + case ID_IP: + break; /* Deprecated. */ + } + +#undef CASE_SWAP + + /* Restore original ID's internal data. */ + *id_a = id_a_back; + *id_b = id_b_back; + + /* Exception: IDProperties. */ + id_a->properties = id_b_back.properties; + id_b->properties = id_a_back.properties; + + /* Swap will have broken internal references to itself, restore them. */ + BKE_libblock_relink_ex(bmain, id_a, id_b, id_a, ID_REMAP_SKIP_NEVER_NULL_USAGE); + BKE_libblock_relink_ex(bmain, id_b, id_a, id_b, ID_REMAP_SKIP_NEVER_NULL_USAGE); +} + +/** Does *not* set ID->newid pointer. */ +bool id_single_user(bContext *C, ID *id, PointerRNA *ptr, PropertyRNA *prop) +{ + ID *newid = NULL; + PointerRNA idptr; + + if (id) { + /* If property isn't editable, + * we're going to have an extra block hanging around until we save. */ + if (RNA_property_editable(ptr, prop)) { + Main *bmain = CTX_data_main(C); + /* copy animation actions too */ + if (BKE_id_copy_ex(bmain, id, &newid, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS) && newid) { + /* us is 1 by convention with new IDs, but RNA_property_pointer_set + * will also increment it, decrement it here. */ + id_us_min(newid); + + /* assign copy */ + RNA_id_pointer_create(newid, &idptr); + RNA_property_pointer_set(ptr, prop, idptr, NULL); + RNA_property_update(C, ptr, prop); + + /* tag grease pencil data-block and disable onion */ + if (GS(id->name) == ID_GD) { + DEG_id_tag_update(id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + DEG_id_tag_update(newid, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + bGPdata *gpd = (bGPdata *)newid; + gpd->flag &= ~GP_DATA_SHOW_ONIONSKINS; + } + + return true; + } + } + } + + return false; +} + +static int libblock_management_us_plus(void *UNUSED(user_data), + ID *UNUSED(id_self), + ID **id_pointer, + int cb_flag) +{ + if (cb_flag & IDWALK_CB_USER) { + id_us_plus(*id_pointer); + } + if (cb_flag & IDWALK_CB_USER_ONE) { + id_us_ensure_real(*id_pointer); + } + + return IDWALK_RET_NOP; +} + +static int libblock_management_us_min(void *UNUSED(user_data), + ID *UNUSED(id_self), + ID **id_pointer, + int cb_flag) +{ + if (cb_flag & IDWALK_CB_USER) { + id_us_min(*id_pointer); + } + /* We can do nothing in IDWALK_CB_USER_ONE case! */ + + return IDWALK_RET_NOP; +} + +/** Add a 'NO_MAIN' data-block to given main (also sets usercounts of its IDs if needed). */ +void BKE_libblock_management_main_add(Main *bmain, void *idv) +{ + ID *id = idv; + + BLI_assert(bmain != NULL); + if ((id->tag & LIB_TAG_NO_MAIN) == 0) { + return; + } + + if ((id->tag & LIB_TAG_NOT_ALLOCATED) != 0) { + /* We cannot add non-allocated ID to Main! */ + return; + } + + /* We cannot allow non-userrefcounting IDs in Main database! */ + if ((id->tag & LIB_TAG_NO_USER_REFCOUNT) != 0) { + BKE_library_foreach_ID_link(bmain, id, libblock_management_us_plus, NULL, IDWALK_NOP); + } + + ListBase *lb = which_libbase(bmain, GS(id->name)); + BKE_main_lock(bmain); + BLI_addtail(lb, id); + BKE_id_new_name_validate(lb, id, NULL); + /* alphabetic insertion: is in new_id */ + id->tag &= ~(LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT); + bmain->is_memfile_undo_written = false; + BKE_main_unlock(bmain); +} + +/** Remove a data-block from given main (set it to 'NO_MAIN' status). */ +void BKE_libblock_management_main_remove(Main *bmain, void *idv) +{ + ID *id = idv; + + BLI_assert(bmain != NULL); + if ((id->tag & LIB_TAG_NO_MAIN) != 0) { + return; + } + + /* For now, allow userrefcounting IDs to get out of Main - can be handy in some cases... */ + + ListBase *lb = which_libbase(bmain, GS(id->name)); + BKE_main_lock(bmain); + BLI_remlink(lb, id); + id->tag |= LIB_TAG_NO_MAIN; + bmain->is_memfile_undo_written = false; + BKE_main_unlock(bmain); +} + +void BKE_libblock_management_usercounts_set(Main *bmain, void *idv) +{ + ID *id = idv; + + if ((id->tag & LIB_TAG_NO_USER_REFCOUNT) == 0) { + return; + } + + BKE_library_foreach_ID_link(bmain, id, libblock_management_us_plus, NULL, IDWALK_NOP); + id->tag &= ~LIB_TAG_NO_USER_REFCOUNT; +} + +void BKE_libblock_management_usercounts_clear(Main *bmain, void *idv) +{ + ID *id = idv; + + /* We do not allow IDs in Main database to not be userrefcounting. */ + if ((id->tag & LIB_TAG_NO_USER_REFCOUNT) != 0 || (id->tag & LIB_TAG_NO_MAIN) != 0) { + return; + } + + BKE_library_foreach_ID_link(bmain, id, libblock_management_us_min, NULL, IDWALK_NOP); + id->tag |= LIB_TAG_NO_USER_REFCOUNT; +} + +/** + * Clear or set given tags for all ids in listbase (runtime tags). + */ +void BKE_main_id_tag_listbase(ListBase *lb, const int tag, const bool value) +{ + ID *id; + if (value) { + for (id = lb->first; id; id = id->next) { + id->tag |= tag; + } + } + else { + const int ntag = ~tag; + for (id = lb->first; id; id = id->next) { + id->tag &= ntag; + } + } +} + +/** + * Clear or set given tags for all ids of given type in bmain (runtime tags). + */ +void BKE_main_id_tag_idcode(struct Main *mainvar, + const short type, + const int tag, + const bool value) +{ + ListBase *lb = which_libbase(mainvar, type); + + BKE_main_id_tag_listbase(lb, tag, value); +} + +/** + * Clear or set given tags for all ids in bmain (runtime tags). + */ +void BKE_main_id_tag_all(struct Main *mainvar, const int tag, const bool value) +{ + ListBase *lbarray[MAX_LIBARRAY]; + int a; + + a = set_listbasepointers(mainvar, lbarray); + while (a--) { + BKE_main_id_tag_listbase(lbarray[a], tag, value); + } +} + +/** + * Clear or set given flags for all ids in listbase (persistent flags). + */ +void BKE_main_id_flag_listbase(ListBase *lb, const int flag, const bool value) +{ + ID *id; + if (value) { + for (id = lb->first; id; id = id->next) { + id->tag |= flag; + } + } + else { + const int nflag = ~flag; + for (id = lb->first; id; id = id->next) { + id->tag &= nflag; + } + } +} + +/** + * Clear or set given flags for all ids in bmain (persistent flags). + */ +void BKE_main_id_flag_all(Main *bmain, const int flag, const bool value) +{ + ListBase *lbarray[MAX_LIBARRAY]; + int a; + a = set_listbasepointers(bmain, lbarray); + while (a--) { + BKE_main_id_flag_listbase(lbarray[a], flag, value); + } +} + +void BKE_main_id_repair_duplicate_names_listbase(ListBase *lb) +{ + int lb_len = 0; + for (ID *id = lb->first; id; id = id->next) { + if (id->lib == NULL) { + lb_len += 1; + } + } + if (lb_len <= 1) { + return; + } + + /* Fill an array because renaming sorts. */ + ID **id_array = MEM_mallocN(sizeof(*id_array) * lb_len, __func__); + GSet *gset = BLI_gset_str_new_ex(__func__, lb_len); + int i = 0; + for (ID *id = lb->first; id; id = id->next) { + if (id->lib == NULL) { + id_array[i] = id; + i++; + } + } + for (i = 0; i < lb_len; i++) { + if (!BLI_gset_add(gset, id_array[i]->name + 2)) { + BKE_id_new_name_validate(lb, id_array[i], NULL); + } + } + BLI_gset_free(gset, NULL); + MEM_freeN(id_array); +} + +void BKE_main_lib_objects_recalc_all(Main *bmain) +{ + Object *ob; + + /* flag for full recalc */ + for (ob = bmain->objects.first; ob; ob = ob->id.next) { + if (ID_IS_LINKED(ob)) { + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); + } + } + + DEG_id_type_tag(bmain, ID_OB); +} + +/* *********** ALLOC AND FREE ***************** + * + * BKE_libblock_free(ListBase *lb, ID *id ) + * provide a list-basis and data-block, but only ID is read + * + * void *BKE_libblock_alloc(ListBase *lb, type, name) + * inserts in list and returns a new ID + * + * **************************** */ + +/** + * Get allocation size of a given data-block type and optionally allocation name. + */ +size_t BKE_libblock_get_alloc_info(short type, const char **name) +{ +#define CASE_RETURN(id_code, type) \ + case id_code: \ + do { \ + if (name != NULL) { \ + *name = #type; \ + } \ + return sizeof(type); \ + } while (0) + + switch ((ID_Type)type) { + CASE_RETURN(ID_SCE, Scene); + CASE_RETURN(ID_LI, Library); + CASE_RETURN(ID_OB, Object); + CASE_RETURN(ID_ME, Mesh); + CASE_RETURN(ID_CU, Curve); + CASE_RETURN(ID_MB, MetaBall); + CASE_RETURN(ID_MA, Material); + CASE_RETURN(ID_TE, Tex); + CASE_RETURN(ID_IM, Image); + CASE_RETURN(ID_LT, Lattice); + CASE_RETURN(ID_LA, Light); + CASE_RETURN(ID_CA, Camera); + CASE_RETURN(ID_IP, Ipo); + CASE_RETURN(ID_KE, Key); + CASE_RETURN(ID_WO, World); + CASE_RETURN(ID_SCR, bScreen); + CASE_RETURN(ID_VF, VFont); + CASE_RETURN(ID_TXT, Text); + CASE_RETURN(ID_SPK, Speaker); + CASE_RETURN(ID_LP, LightProbe); + CASE_RETURN(ID_SO, bSound); + CASE_RETURN(ID_GR, Collection); + CASE_RETURN(ID_AR, bArmature); + CASE_RETURN(ID_AC, bAction); + CASE_RETURN(ID_NT, bNodeTree); + CASE_RETURN(ID_BR, Brush); + CASE_RETURN(ID_PA, ParticleSettings); + CASE_RETURN(ID_WM, wmWindowManager); + CASE_RETURN(ID_GD, bGPdata); + CASE_RETURN(ID_MC, MovieClip); + CASE_RETURN(ID_MSK, Mask); + CASE_RETURN(ID_LS, FreestyleLineStyle); + CASE_RETURN(ID_PAL, Palette); + CASE_RETURN(ID_PC, PaintCurve); + CASE_RETURN(ID_CF, CacheFile); + CASE_RETURN(ID_WS, WorkSpace); + } + return 0; +#undef CASE_RETURN +} + +/** + * Allocates and returns memory of the right size for the specified block type, + * initialized to zero. + */ +void *BKE_libblock_alloc_notest(short type) +{ + const char *name; + size_t size = BKE_libblock_get_alloc_info(type, &name); + if (size != 0) { + return MEM_callocN(size, name); + } + BLI_assert(!"Request to allocate unknown data type"); + return NULL; +} + +/** + * Allocates and returns a block of the specified type, with the specified name + * (adjusted as necessary to ensure uniqueness), and appended to the specified list. + * The user count is set to 1, all other content (apart from name and links) being + * initialized to zero. + */ +void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int flag) +{ + BLI_assert((flag & LIB_ID_CREATE_NO_ALLOCATE) == 0); + + ID *id = BKE_libblock_alloc_notest(type); + + if (id) { + if ((flag & LIB_ID_CREATE_NO_MAIN) != 0) { + id->tag |= LIB_TAG_NO_MAIN; + } + if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) != 0) { + id->tag |= LIB_TAG_NO_USER_REFCOUNT; + } + + id->icon_id = 0; + *((short *)id->name) = type; + if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { + id->us = 1; + } + if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) { + ListBase *lb = which_libbase(bmain, type); + + BKE_main_lock(bmain); + BLI_addtail(lb, id); + BKE_id_new_name_validate(lb, id, name); + bmain->is_memfile_undo_written = false; + /* alphabetic insertion: is in new_id */ + BKE_main_unlock(bmain); + + /* TODO to be removed from here! */ + if ((flag & LIB_ID_CREATE_NO_DEG_TAG) == 0) { + DEG_id_type_tag(bmain, type); + } + } + else { + BLI_strncpy(id->name + 2, name, sizeof(id->name) - 2); + } + } + + return id; +} + +/** + * Initialize an ID of given type, such that it has valid 'empty' data. + * ID is assumed to be just calloc'ed. + */ +void BKE_libblock_init_empty(ID *id) +{ + /* Note that only ID types that are not valid when filled of zero should have a callback here. */ + switch ((ID_Type)GS(id->name)) { + case ID_SCE: + BKE_scene_init((Scene *)id); + break; + case ID_LI: + /* Nothing to do. */ + break; + case ID_OB: { + Object *ob = (Object *)id; + BKE_object_init(ob, OB_EMPTY); + break; + } + case ID_ME: + BKE_mesh_init((Mesh *)id); + break; + case ID_CU: + BKE_curve_init((Curve *)id, 0); + break; + case ID_MB: + BKE_mball_init((MetaBall *)id); + break; + case ID_MA: + BKE_material_init((Material *)id); + break; + case ID_TE: + BKE_texture_default((Tex *)id); + break; + case ID_IM: + BKE_image_init((Image *)id); + break; + case ID_LT: + BKE_lattice_init((Lattice *)id); + break; + case ID_LA: + BKE_light_init((Light *)id); + break; + case ID_SPK: + BKE_speaker_init((Speaker *)id); + break; + case ID_LP: + BKE_lightprobe_init((LightProbe *)id); + break; + case ID_CA: + BKE_camera_init((Camera *)id); + break; + case ID_WO: + BKE_world_init((World *)id); + break; + case ID_SCR: + /* Nothing to do. */ + break; + case ID_VF: + BKE_vfont_init((VFont *)id); + break; + case ID_TXT: + BKE_text_init((Text *)id); + break; + case ID_SO: + /* Another fuzzy case, think NULLified content is OK here... */ + break; + case ID_GR: + /* Nothing to do. */ + break; + case ID_AR: + /* Nothing to do. */ + break; + case ID_AC: + /* Nothing to do. */ + break; + case ID_NT: + ntreeInitDefault((bNodeTree *)id); + break; + case ID_BR: + BKE_brush_init((Brush *)id); + break; + case ID_PA: + /* Nothing to do. */ + break; + case ID_PC: + /* Nothing to do. */ + break; + case ID_GD: + /* Nothing to do. */ + break; + case ID_MSK: + /* Nothing to do. */ + break; + case ID_LS: + BKE_linestyle_init((FreestyleLineStyle *)id); + break; + case ID_CF: + BKE_cachefile_init((CacheFile *)id); + break; + case ID_KE: + /* Shapekeys are a complex topic too - they depend on their 'user' data type... + * They are not linkable, though, so it should never reach here anyway. */ + BLI_assert(0); + break; + case ID_WM: + /* We should never reach this. */ + BLI_assert(0); + break; + case ID_IP: + /* Should not be needed - animation from lib pre-2.5 is broken anyway. */ + BLI_assert(0); + break; + case ID_PAL: + BKE_palette_init((Palette *)id); + break; + default: + BLI_assert(0); /* Should never reach this point... */ + } +} + +/** + * Generic helper to create a new empty data-block of given type in given \a bmain database. + * + * \param name: can be NULL, in which case we get default name for this ID type. + */ +void *BKE_id_new(Main *bmain, const short type, const char *name) +{ + BLI_assert(bmain != NULL); + + if (name == NULL) { + name = DATA_(BKE_idcode_to_name(type)); + } + + ID *id = BKE_libblock_alloc(bmain, type, name, 0); + BKE_libblock_init_empty(id); + + return id; +} + +/** + * Generic helper to create a new temporary empty data-block of given type, + * *outside* of any Main database. + * + * \param name: can be NULL, in which case we get default name for this ID type. */ +void *BKE_id_new_nomain(const short type, const char *name) +{ + if (name == NULL) { + name = DATA_(BKE_idcode_to_name(type)); + } + + ID *id = BKE_libblock_alloc(NULL, + type, + name, + LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT | + LIB_ID_CREATE_NO_DEG_TAG); + BKE_libblock_init_empty(id); + + return id; +} + +void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int orig_flag) +{ + ID *new_id = *r_newid; + int flag = orig_flag; + + const bool is_private_id_data = (id->flag & LIB_PRIVATE_DATA) != 0; + + BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || bmain != NULL); + BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || (flag & LIB_ID_CREATE_NO_ALLOCATE) == 0); + if (!is_private_id_data) { + /* When we are handling private ID data, we might still want to manage usercounts, even though + * that ID data-block is actually outside of Main... */ + BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 || + (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) != 0); + } + /* Never implicitly copy shapekeys when generating temp data outside of Main database. */ + BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 || (flag & LIB_ID_COPY_SHAPEKEY) == 0); + + /* 'Private ID' data handling. */ + if ((bmain != NULL) && is_private_id_data) { + flag |= LIB_ID_CREATE_NO_MAIN; + } + + /* The id->flag bits to copy over. */ + const int copy_idflag_mask = LIB_PRIVATE_DATA; + + if ((flag & LIB_ID_CREATE_NO_ALLOCATE) != 0) { + /* r_newid already contains pointer to allocated memory. */ + /* TODO do we want to memset(0) whole mem before filling it? */ + BLI_strncpy(new_id->name, id->name, sizeof(new_id->name)); + new_id->us = 0; + new_id->tag |= LIB_TAG_NOT_ALLOCATED | LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT; + /* TODO Do we want/need to copy more from ID struct itself? */ + } + else { + new_id = BKE_libblock_alloc(bmain, GS(id->name), id->name + 2, flag); + } + BLI_assert(new_id != NULL); + + const size_t id_len = BKE_libblock_get_alloc_info(GS(new_id->name), NULL); + const size_t id_offset = sizeof(ID); + if ((int)id_len - (int)id_offset > 0) { /* signed to allow neg result */ /* XXX ????? */ + const char *cp = (const char *)id; + char *cpn = (char *)new_id; + + memcpy(cpn + id_offset, cp + id_offset, id_len - id_offset); + } + + new_id->flag = (new_id->flag & ~copy_idflag_mask) | (id->flag & copy_idflag_mask); + + /* We do not want any handling of usercount in code duplicating the data here, we do that all + * at once in id_copy_libmanagement_cb() at the end. */ + const int copy_data_flag = orig_flag | LIB_ID_CREATE_NO_USER_REFCOUNT; + + if (id->properties) { + new_id->properties = IDP_CopyProperty_ex(id->properties, copy_data_flag); + } + + /* XXX Again... We need a way to control what we copy in a much more refined way. + * We cannot always copy this, some internal copying will die on it! */ + /* For now, upper level code will have to do that itself when required. */ +#if 0 + if (id->override != NULL) { + BKE_override_copy(new_id, id); + } +#endif + + if (id_can_have_animdata(new_id)) { + IdAdtTemplate *iat = (IdAdtTemplate *)new_id; + + /* the duplicate should get a copy of the animdata */ + if ((flag & LIB_ID_COPY_NO_ANIMDATA) == 0) { + /* Note that even though horrors like root nodetrees are not in bmain, the actions they use + * in their anim data *are* in bmain... super-mega-hooray. */ + BLI_assert((copy_data_flag & LIB_ID_COPY_ACTIONS) == 0 || + (copy_data_flag & LIB_ID_CREATE_NO_MAIN) == 0); + iat->adt = BKE_animdata_copy(bmain, iat->adt, copy_data_flag); + } + else { + iat->adt = NULL; + } + } + + if ((flag & LIB_ID_CREATE_NO_DEG_TAG) == 0 && (flag & LIB_ID_CREATE_NO_MAIN) == 0) { + DEG_id_type_tag(bmain, GS(new_id->name)); + } + + *r_newid = new_id; +} + +/* used everywhere in blenkernel */ +void *BKE_libblock_copy(Main *bmain, const ID *id) +{ + ID *idn; + + BKE_libblock_copy_ex(bmain, id, &idn, 0); + + return idn; +} + +/* XXX TODO: get rid of this useless wrapper at some point... */ +void *BKE_libblock_copy_for_localize(const ID *id) +{ + ID *idn; + BKE_libblock_copy_ex(NULL, id, &idn, LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA); + return idn; +} + +void BKE_library_free(Library *lib) +{ + if (lib->packedfile) { + BKE_packedfile_free(lib->packedfile); + } +} + +/* ***************** ID ************************ */ +ID *BKE_libblock_find_name(struct Main *bmain, const short type, const char *name) +{ + ListBase *lb = which_libbase(bmain, type); + BLI_assert(lb != NULL); + return BLI_findstring(lb, name, offsetof(ID, name) + 2); +} + +/** + * Sort given \a id into given \a lb list, using case-insensitive comparison of the id names. + * + * \note All other IDs beside given one are assumed already properly sorted in the list. + * + * \param id_sorting_hint Ignored if NULL. Otherwise, used to check if we can insert \a id + * immediately before or after that pointer. It must always be into given \a lb list. + */ +void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint) +{ +#define ID_SORT_STEP_SIZE 512 + + ID *idtest; + + /* insert alphabetically */ + if (lb->first == lb->last) { + return; + } + + BLI_remlink(lb, id); + + /* Check if we can actually insert id before or after id_sorting_hint, if given. */ + if (id_sorting_hint != NULL && id_sorting_hint != id) { + BLI_assert(BLI_findindex(lb, id_sorting_hint) >= 0); + + ID *id_sorting_hint_next = id_sorting_hint->next; + if (BLI_strcasecmp(id_sorting_hint->name, id->name) < 0 && + (id_sorting_hint_next == NULL || + BLI_strcasecmp(id_sorting_hint_next->name, id->name) > 0)) { + BLI_insertlinkafter(lb, id_sorting_hint, id); + return; + } + + ID *id_sorting_hint_prev = id_sorting_hint->prev; + if (BLI_strcasecmp(id_sorting_hint->name, id->name) > 0 && + (id_sorting_hint_prev == NULL || + BLI_strcasecmp(id_sorting_hint_prev->name, id->name) < 0)) { + BLI_insertlinkbefore(lb, id_sorting_hint, id); + return; + } + } + + void *item_array[ID_SORT_STEP_SIZE]; + int item_array_index; + + /* Step one: We go backward over a whole chunk of items at once, until we find a limit item + * that is lower than, or equal (should never happen!) to the one we want to insert. */ + /* Note: We start from the end, because in typical 'heavy' case (insertion of lots of IDs at + * once using the same base name), newly inserted items will generally be towards the end + * (higher extension numbers). */ + for (idtest = lb->last, item_array_index = ID_SORT_STEP_SIZE - 1; idtest != NULL; + idtest = idtest->prev, item_array_index--) { + item_array[item_array_index] = idtest; + if (item_array_index == 0) { + if ((idtest->lib == NULL && id->lib != NULL) || + BLI_strcasecmp(idtest->name, id->name) <= 0) { + break; + } + item_array_index = ID_SORT_STEP_SIZE; + } + } + + /* Step two: we go forward in the selected chunk of items and check all of them, as we know + * that our target is in there. */ + + /* If we reached start of the list, current item_array_index is off-by-one. + * Otherwise, we already know that it points to an item lower-or-equal-than the one we want to + * insert, no need to redo the check for that one. + * So we can increment that index in any case. */ + for (item_array_index++; item_array_index < ID_SORT_STEP_SIZE; item_array_index++) { + idtest = item_array[item_array_index]; + if ((idtest->lib != NULL && id->lib == NULL) || BLI_strcasecmp(idtest->name, id->name) > 0) { + BLI_insertlinkbefore(lb, idtest, id); + break; + } + } + if (item_array_index == ID_SORT_STEP_SIZE) { + if (idtest == NULL) { + /* If idtest is NULL here, it means that in the first loop, the last comparison was + * performed exactly on the first item of the list, and that it also failed. In other + * words, all items in the list are greater than inserted one, so we can put it at the + * start of the list. */ + /* Note that BLI_insertlinkafter() would have same behavior in that case, but better be + * explicit here. */ + BLI_addhead(lb, id); + } + else { + BLI_insertlinkafter(lb, idtest, id); + } + } + +#undef ID_SORT_STEP_SIZE +} + +/* Note: this code assumes and ensures that the suffix number can never go beyond 1 billion. */ +#define MAX_NUMBER 1000000000 +/* We do not want to get "name.000", so minimal number is 1. */ +#define MIN_NUMBER 1 +/* The maximum value up to which we search for the actual smallest unused number. Beyond that + * value, we will only use the first biggest unused number, without trying to 'fill the gaps' + * in-between already used numbers... */ +#define MAX_NUMBERS_IN_USE 1024 + +/** + * Helper building final ID name from given base_name and number. + * + * If everything goes well and we do generate a valid final ID name in given name, we return true. + * In case the final name would overflow the allowed ID name length, or given number is bigger than + * maximum allowed value, we truncate further the base_name (and given name, which is assumed to + * have the same 'base_name' part), and return false. + */ +static bool id_name_final_build(char *name, char *base_name, size_t base_name_len, int number) +{ + char number_str[11]; /* Dot + nine digits + NULL terminator. */ + size_t number_str_len = BLI_snprintf_rlen(number_str, ARRAY_SIZE(number_str), ".%.3d", number); + + /* If the number would lead to an overflow of the maximum ID name length, we need to truncate + * the base name part and do all the number checks again. */ + if (base_name_len + number_str_len >= MAX_ID_NAME - 2 || number >= MAX_NUMBER) { + if (base_name_len + number_str_len >= MAX_ID_NAME - 2) { + base_name_len = MAX_ID_NAME - 2 - number_str_len - 1; + } + else { + base_name_len--; + } + base_name[base_name_len] = '\0'; + + /* Code above may have generated invalid utf-8 string, due to raw truncation. + * Ensure we get a valid one now. */ + base_name_len -= (size_t)BLI_utf8_invalid_strip(base_name, base_name_len); + + /* Also truncate orig name, and start the whole check again. */ + name[base_name_len] = '\0'; + return false; + } + + /* We have our final number, we can put it in name and exit the function. */ + BLI_strncpy(name + base_name_len, number_str, number_str_len + 1); + return true; +} + +/** + * Check to see if an ID name is already used, and find a new one if so. + * Return true if a new name was created (returned in name). + * + * Normally the ID that's being checked is already in the ListBase, so ID *id points at the new + * entry. The Python Library module needs to know what the name of a data-block will be before it + * is appended, in this case ID *id is NULL. + */ +static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_hint) +{ + BLI_assert(strlen(name) < MAX_ID_NAME - 2); + + *r_id_sorting_hint = NULL; + + ID *id_test = lb->first; + bool is_name_changed = false; + + if (id_test == NULL) { + return is_name_changed; + } + + const short id_type = (short)GS(id_test->name); + + /* Static storage of previous handled ID/name info, used to perform a quicker test and optimize + * creation of huge number of IDs using the same given base name. */ + static char prev_orig_base_name[MAX_ID_NAME - 2] = {0}; + static char prev_final_base_name[MAX_ID_NAME - 2] = {0}; + static short prev_id_type = ID_LINK_PLACEHOLDER; /* Should never exist in actual ID list. */ + static int prev_number = MIN_NUMBER - 1; + + /* Initial test to check whether we can 'shortcut' the more complex loop of the main code below. + * Note that we do not do that for low numbers, as that would prevent using actual smallest + * available number in some cases, and benefits of this special case handling mostly show up with + * high numbers anyway. */ + if (id_type == prev_id_type && prev_number >= MAX_NUMBERS_IN_USE && + prev_number < MAX_NUMBER - 1 && name[0] == prev_final_base_name[0]) { + + /* Get the name and number parts ("name.number"). */ + char base_name[MAX_ID_NAME - 2]; + int number = MIN_NUMBER; + size_t base_name_len = BLI_split_name_num(base_name, &number, name, '.'); + size_t prev_final_base_name_len = strlen(prev_final_base_name); + size_t prev_orig_base_name_len = strlen(prev_orig_base_name); + + if (base_name_len == prev_orig_base_name_len && + STREQLEN(base_name, prev_orig_base_name, prev_orig_base_name_len)) { + /* Once we have ensured given base_name and original previous one are the same, we can check + * that previously used number is actually used, and that next one is free. */ + /* Note that from now on, we only used previous final base name, as it might have been + * truncated from original one due to number suffix length. */ + char final_name[MAX_ID_NAME - 2]; + char prev_final_name[MAX_ID_NAME - 2]; + BLI_strncpy(final_name, prev_final_base_name, prev_final_base_name_len + 1); + BLI_strncpy(prev_final_name, prev_final_base_name, prev_final_base_name_len + 1); + + if (id_name_final_build(final_name, base_name, prev_final_base_name_len, prev_number + 1) && + id_name_final_build(prev_final_name, base_name, prev_final_base_name_len, prev_number)) { + /* We successfully built valid final names of previous and current iterations, + * now we have to ensure that previous final name is indeed used in current ID list, + * and that current one is not. */ + bool is_valid = false; + for (id_test = lb->first; id_test; id_test = id_test->next) { + if (id != id_test && !ID_IS_LINKED(id_test)) { + if (id_test->name[2] == final_name[0] && STREQ(final_name, id_test->name + 2)) { + /* We expect final_name to not be already used, so this is a failure. */ + is_valid = false; + break; + } + /* Previous final name should only be found once in the list, so if it was found + * already, no need to do a string comparison again. */ + if (!is_valid && id_test->name[2] == prev_final_name[0] && + STREQ(prev_final_name, id_test->name + 2)) { + is_valid = true; + *r_id_sorting_hint = id_test; + } + } + } + + if (is_valid) { + /* Only the number changed, prev_orig_base_name, prev_final_base_name and prev_id_type + * remain the same. */ + prev_number++; + + strcpy(name, final_name); + return true; + } + } + } + } + + /* To speed up finding smallest unused number within [0 .. MAX_NUMBERS_IN_USE - 1]. + * We do not bother beyond that point. */ + ID *ids_in_use[MAX_NUMBERS_IN_USE] = {NULL}; + + bool is_first_run = true; + while (true) { + /* Get the name and number parts ("name.number"). */ + char base_name[MAX_ID_NAME - 2]; + int number = MIN_NUMBER; + size_t base_name_len = BLI_split_name_num(base_name, &number, name, '.'); + + /* Store previous original given base name now, as we might alter it later in code below. */ + if (is_first_run) { + strcpy(prev_orig_base_name, base_name); + is_first_run = false; + } + + /* In case we get an insane initial number suffix in given name. */ + /* Note: BLI_split_name_num() cannot return negative numbers, so we do not have to check for + * that here. */ + if (number >= MAX_NUMBER || number < MIN_NUMBER) { + number = MIN_NUMBER; + } + + bool is_orig_name_used = false; + for (id_test = lb->first; id_test; id_test = id_test->next) { + char base_name_test[MAX_ID_NAME - 2]; + int number_test; + if ((id != id_test) && !ID_IS_LINKED(id_test) && (name[0] == id_test->name[2]) && + (id_test->name[base_name_len + 2] == '.' || id_test->name[base_name_len + 2] == '\0') && + STREQLEN(name, id_test->name + 2, base_name_len) && + (BLI_split_name_num(base_name_test, &number_test, id_test->name + 2, '.') == + base_name_len)) { + /* If we did not yet encounter exact same name as the given one, check the remaining parts + * of the strings. */ + if (!is_orig_name_used) { + is_orig_name_used = STREQ(name + base_name_len, id_test->name + 2 + base_name_len); + } + /* Mark number of current id_test name as used, if possible. */ + if (number_test < MAX_NUMBERS_IN_USE) { + ids_in_use[number_test] = id_test; + } + /* Keep track of first largest unused number. */ + if (number <= number_test) { + *r_id_sorting_hint = id_test; + number = number_test + 1; + } + } + } + + /* If there is no double, we are done. + * Note however that name might have been changed (truncated) in a previous iteration already. + */ + if (!is_orig_name_used) { + /* Don't bother updating prev_ static variables here, this case is not supposed to happen + * that often, and is not straight-forward here, so just ignore and reset them to default. */ + prev_id_type = ID_LINK_PLACEHOLDER; + prev_final_base_name[0] = '\0'; + prev_number = MIN_NUMBER - 1; + + /* Value set previously is meaningless in that case. */ + *r_id_sorting_hint = NULL; + + return is_name_changed; + } + + /* Decide which value of number to use, either the smallest unused one if possible, or default + * to the first largest unused one we got from previous loop. */ + for (int i = MIN_NUMBER; i < MAX_NUMBERS_IN_USE; i++) { + if (ids_in_use[i] == NULL) { + number = i; + if (i > 0) { + *r_id_sorting_hint = ids_in_use[i - 1]; + } + break; + } + } + /* At this point, number is either the lowest unused number within + * [MIN_NUMBER .. MAX_NUMBERS_IN_USE - 1], or 1 greater than the largest used number if all + * those low ones are taken. + * We can't be bothered to look for the lowest unused number beyond + * (MAX_NUMBERS_IN_USE - 1). + */ + /* We know for wure that name will be changed. */ + is_name_changed = true; + + /* If id_name_final_build helper returns false, it had to truncate further given name, hence we + * have to go over the whole check again. */ + if (!id_name_final_build(name, base_name, base_name_len, number)) { + /* We have to clear our list of small used numbers before we do the whole check again. */ + memset(ids_in_use, 0, sizeof(ids_in_use)); + + continue; + } + + /* Update prev_ static variables, in case next call is for the same type of IDs and with the + * same initial base name, we can skip a lot of above process. */ + prev_id_type = id_type; + strcpy(prev_final_base_name, base_name); + prev_number = number; + + return is_name_changed; + } + +#undef MAX_NUMBERS_IN_USE +} + +#undef MIN_NUMBER +#undef MAX_NUMBER + +/** + * Ensures given ID has a unique name in given listbase. + * + * Only for local IDs (linked ones already have a unique ID in their library). + * + * \return true if a new name had to be created. + */ +bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname) +{ + bool result; + char name[MAX_ID_NAME - 2]; + + /* if library, don't rename */ + if (ID_IS_LINKED(id)) { + return false; + } + + /* if no name given, use name of current ID + * else make a copy (tname args can be const) */ + if (tname == NULL) { + tname = id->name + 2; + } + + BLI_strncpy(name, tname, sizeof(name)); + + if (name[0] == '\0') { + /* Disallow empty names. */ + BLI_strncpy(name, DATA_(BKE_idcode_to_name(GS(id->name))), sizeof(name)); + } + else { + /* disallow non utf8 chars, + * the interface checks for this but new ID's based on file names don't */ + BLI_utf8_invalid_strip(name, strlen(name)); + } + + ID *id_sorting_hint = NULL; + result = check_for_dupid(lb, id, name, &id_sorting_hint); + strcpy(id->name + 2, name); + + /* This was in 2.43 and previous releases + * however all data in blender should be sorted, not just duplicate names + * sorting should not hurt, but noting just in case it alters the way other + * functions work, so sort every time. */ +#if 0 + if (result) { + id_sort_by_name(lb, id, id_sorting_hint); + } +#endif + + id_sort_by_name(lb, id, id_sorting_hint); + + return result; +} + +/** + * Pull an ID out of a library (make it local). Only call this for IDs that + * don't have other library users. + */ +void id_clear_lib_data_ex(Main *bmain, ID *id, const bool id_in_mainlist) +{ + bNodeTree *ntree = NULL; + Key *key = NULL; + + BKE_id_lib_local_paths(bmain, id->lib, id); + + id_fake_user_clear(id); + + id->lib = NULL; + id->tag &= ~(LIB_TAG_INDIRECT | LIB_TAG_EXTERN); + id->flag &= ~LIB_INDIRECT_WEAK_LINK; + if (id_in_mainlist) { + if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL)) { + bmain->is_memfile_undo_written = false; + } + } + + /* Internal bNodeTree blocks inside data-blocks also stores id->lib, + * make sure this stays in sync. */ + if ((ntree = ntreeFromID(id))) { + id_clear_lib_data_ex(bmain, &ntree->id, false); /* Datablocks' nodetree is never in Main. */ + } + + /* Same goes for shapekeys. */ + if ((key = BKE_key_from_id(id))) { + id_clear_lib_data_ex(bmain, &key->id, id_in_mainlist); /* sigh, why are keys in Main? */ + } +} + +void id_clear_lib_data(Main *bmain, ID *id) +{ + id_clear_lib_data_ex(bmain, id, true); +} + +/* next to indirect usage in read/writefile also in editobject.c scene.c */ +void BKE_main_id_clear_newpoins(Main *bmain) +{ + ID *id; + + FOREACH_MAIN_ID_BEGIN (bmain, id) { + id->newid = NULL; + id->tag &= ~LIB_TAG_NEW; + } + FOREACH_MAIN_ID_END; +} + +static int id_refcount_recompute_callback(void *user_data, + struct ID *UNUSED(id_self), + struct ID **id_pointer, + int cb_flag) +{ + const bool do_linked_only = (bool)POINTER_AS_INT(user_data); + + if (*id_pointer == NULL) { + return IDWALK_RET_NOP; + } + if (do_linked_only && !ID_IS_LINKED(*id_pointer)) { + return IDWALK_RET_NOP; + } + + if (cb_flag & IDWALK_CB_USER) { + /* Do not touch to direct/indirect linked status here... */ + id_us_plus_no_lib(*id_pointer); + } + if (cb_flag & IDWALK_CB_USER_ONE) { + id_us_ensure_real(*id_pointer); + } + + return IDWALK_RET_NOP; +} + +void BKE_main_id_refcount_recompute(struct Main *bmain, const bool do_linked_only) +{ + ID *id; + + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (!ID_IS_LINKED(id) && do_linked_only) { + continue; + } + id->us = ID_FAKE_USERS(id); + /* Note that we keep EXTRAUSER tag here, since some UI users may define it too... */ + if (id->tag & LIB_TAG_EXTRAUSER) { + id->tag &= ~(LIB_TAG_EXTRAUSER | LIB_TAG_EXTRAUSER_SET); + id_us_ensure_real(id); + } + } + FOREACH_MAIN_ID_END; + + /* Go over whole Main database to re-generate proper usercounts... */ + FOREACH_MAIN_ID_BEGIN (bmain, id) { + BKE_library_foreach_ID_link(bmain, + id, + id_refcount_recompute_callback, + POINTER_FROM_INT((int)do_linked_only), + IDWALK_READONLY); + } + FOREACH_MAIN_ID_END; +} + +static void library_make_local_copying_check(ID *id, + GSet *loop_tags, + MainIDRelations *id_relations, + GSet *done_ids) +{ + if (BLI_gset_haskey(done_ids, id)) { + return; /* Already checked, nothing else to do. */ + } + + MainIDRelationsEntry *entry = BLI_ghash_lookup(id_relations->id_used_to_user, id); + BLI_gset_insert(loop_tags, id); + for (; entry != NULL; entry = entry->next) { + + /* Used_to_user stores ID pointer, not pointer to ID pointer. */ + ID *par_id = (ID *)entry->id_pointer; + + /* Our oh-so-beloved 'from' pointers... */ + if (entry->usage_flag & IDWALK_CB_LOOPBACK) { + /* We totally disregard Object->proxy_from 'usage' here, + * this one would only generate fake positives. */ + if (GS(par_id->name) == ID_OB) { + BLI_assert(((Object *)par_id)->proxy_from == (Object *)id); + continue; + } + + /* Shapekeys are considered 'private' to their owner ID here, and never tagged + * (since they cannot be linked), * so we have to switch effective parent to their owner. + */ + if (GS(par_id->name) == ID_KE) { + par_id = ((Key *)par_id)->from; + } + } + + if (par_id->lib == NULL) { + /* Local user, early out to avoid some gset querying... */ + continue; + } + if (!BLI_gset_haskey(done_ids, par_id)) { + if (BLI_gset_haskey(loop_tags, par_id)) { + /* We are in a 'dependency loop' of IDs, this does not say us anything, skip it. + * Note that this is the situation that can lead to archipelagoes of linked data-blocks + * (since all of them have non-local users, they would all be duplicated, + * leading to a loop of unused linked data-blocks that cannot be freed since they all use + * each other...). */ + continue; + } + /* Else, recursively check that user ID. */ + library_make_local_copying_check(par_id, loop_tags, id_relations, done_ids); + } + + if (par_id->tag & LIB_TAG_DOIT) { + /* This user will be fully local in future, so far so good, + * nothing to do here but check next user. */ + } + else { + /* This user won't be fully local in future, so current ID won't be either. + * And we are done checking it. */ + id->tag &= ~LIB_TAG_DOIT; + break; + } + } + BLI_gset_add(done_ids, id); + BLI_gset_remove(loop_tags, id, NULL); +} + +/** + * Make linked data-blocks local. + * + * \param bmain: Almost certainly global main. + * \param lib: If not NULL, only make local data-blocks from this library. + * \param untagged_only: If true, only make local data-blocks not tagged with + * LIB_TAG_PRE_EXISTING. + * \param set_fake: If true, set fake user on all localized data-blocks + * (except group and objects ones). + */ +/* Note: Old (2.77) version was simply making (tagging) data-blocks as local, + * without actually making any check whether they were also indirectly used or not... + * + * Current version uses regular id_make_local callback, with advanced pre-processing step to + * detect all cases of IDs currently indirectly used, but which will be used by local data only + * once this function is finished. This allows to avoid any unneeded duplication of IDs, and + * hence all time lost afterwards to remove orphaned linked data-blocks... + */ +void BKE_library_make_local(Main *bmain, + const Library *lib, + GHash *old_to_new_ids, + const bool untagged_only, + const bool set_fake) +{ + ListBase *lbarray[MAX_LIBARRAY]; + + LinkNode *todo_ids = NULL; + LinkNode *copied_ids = NULL; + MemArena *linklist_mem = BLI_memarena_new(512 * sizeof(*todo_ids), __func__); + + GSet *done_ids = BLI_gset_ptr_new(__func__); + +#ifdef DEBUG_TIME + TIMEIT_START(make_local); +#endif + + BKE_main_relations_create(bmain); + +#ifdef DEBUG_TIME + printf("Pre-compute current ID relations: Done.\n"); + TIMEIT_VALUE_PRINT(make_local); +#endif + + /* Step 1: Detect data-blocks to make local. */ + for (int a = set_listbasepointers(bmain, lbarray); a--;) { + ID *id = lbarray[a]->first; + + /* Do not explicitly make local non-linkable IDs (shapekeys, in fact), + * they are assumed to be handled by real data-blocks responsible of them. */ + const bool do_skip = (id && !BKE_idcode_is_linkable(GS(id->name))); + + for (; id; id = id->next) { + ID *ntree = (ID *)ntreeFromID(id); + + id->tag &= ~LIB_TAG_DOIT; + if (ntree != NULL) { + ntree->tag &= ~LIB_TAG_DOIT; + } + + if (id->lib == NULL) { + id->tag &= ~(LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW); + id->flag &= ~LIB_INDIRECT_WEAK_LINK; + } + /* The check on the fourth line (LIB_TAG_PRE_EXISTING) is done so it's possible to tag data + * you don't want to be made local, used for appending data, + * so any libdata already linked wont become local (very nasty + * to discover all your links are lost after appending). + * Also, never ever make proxified objects local, would not make any sense. */ + /* Some more notes: + * - Shapekeys are never tagged here (since they are not linkable). + * - Nodetrees used in materials etc. have to be tagged manually, + * since they do not exist in Main (!). + * This is ok-ish on 'make local' side of things + * (since those are handled by their 'owner' IDs), + * but complicates slightly the pre-processing of relations between IDs at step 2... */ + else if (!do_skip && id->tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW) && + ELEM(lib, NULL, id->lib) && + !(GS(id->name) == ID_OB && ((Object *)id)->proxy_from != NULL) && + ((untagged_only == false) || !(id->tag & LIB_TAG_PRE_EXISTING))) { + BLI_linklist_prepend_arena(&todo_ids, id, linklist_mem); + id->tag |= LIB_TAG_DOIT; + + /* Tag those nasty non-ID nodetrees, + * but do not add them to todo list, making them local is handled by 'owner' ID. + * This is needed for library_make_local_copying_check() to work OK at step 2. */ + if (ntree != NULL) { + ntree->tag |= LIB_TAG_DOIT; + } + } + else { + /* Linked ID that we won't be making local (needed info for step 2, see below). */ + BLI_gset_add(done_ids, id); + } + } + } + +#ifdef DEBUG_TIME + printf("Step 1: Detect data-blocks to make local: Done.\n"); + TIMEIT_VALUE_PRINT(make_local); +#endif + + /* Step 2: Check which data-blocks we can directly make local + * (because they are only used by already, or future, local data), + * others will need to be duplicated. */ + GSet *loop_tags = BLI_gset_ptr_new(__func__); + for (LinkNode *it = todo_ids; it; it = it->next) { + library_make_local_copying_check(it->link, loop_tags, bmain->relations, done_ids); + BLI_assert(BLI_gset_len(loop_tags) == 0); + } + BLI_gset_free(loop_tags, NULL); + BLI_gset_free(done_ids, NULL); + + /* Next step will most likely add new IDs, better to get rid of this mapping now. */ + BKE_main_relations_free(bmain); + +#ifdef DEBUG_TIME + printf("Step 2: Check which data-blocks we can directly make local: Done.\n"); + TIMEIT_VALUE_PRINT(make_local); +#endif + + /* Step 3: Make IDs local, either directly (quick and simple), or using generic process, + * which involves more complex checks and might instead + * create a local copy of original linked ID. */ + for (LinkNode *it = todo_ids, *it_next; it; it = it_next) { + it_next = it->next; + ID *id = it->link; + + if (id->tag & LIB_TAG_DOIT) { + /* We know all users of this object are local or will be made fully local, even if + * currently there are some indirect usages. So instead of making a copy that we'll likely + * get rid of later, directly make that data block local. + * Saves a tremendous amount of time with complex scenes... */ + id_clear_lib_data_ex(bmain, id, true); + BKE_id_expand_local(bmain, id); + id->tag &= ~LIB_TAG_DOIT; + + if (GS(id->name) == ID_OB) { + BKE_rigidbody_ensure_local_object(bmain, (Object *)id); + } + } + else { + /* In this specific case, we do want to make ID local even if it has no local usage yet... + */ + if (GS(id->name) == ID_OB) { + /* Special case for objects because we don't want proxy pointers to be + * cleared yet. This will happen down the road in this function. + */ + BKE_object_make_local_ex(bmain, (Object *)id, true, false); + } + else { + id_make_local(bmain, id, false, true); + } + + if (id->newid) { + if (GS(id->newid->name) == ID_OB) { + BKE_rigidbody_ensure_local_object(bmain, (Object *)id->newid); + } + + /* Reuse already allocated LinkNode (transferring it from todo_ids to copied_ids). */ + BLI_linklist_prepend_nlink(&copied_ids, id, it); + } + } + + if (set_fake) { + if (!ELEM(GS(id->name), ID_OB, ID_GR)) { + /* do not set fake user on objects, groups (instancing) */ + id_fake_user_set(id); + } + } + } + +#ifdef DEBUG_TIME + printf("Step 3: Make IDs local: Done.\n"); + TIMEIT_VALUE_PRINT(make_local); +#endif + + /* At this point, we are done with directly made local IDs. + * Now we have to handle duplicated ones, since their + * remaining linked original counterpart may not be needed anymore... */ + todo_ids = NULL; + + /* Step 4: We have to remap local usages of old (linked) ID to new (local) + * ID in a separated loop, + * as lbarray ordering is not enough to ensure us we did catch all dependencies + * (e.g. if making local a parent object before its child...). See T48907. */ + /* TODO This is now the biggest step by far (in term of processing time). + * We may be able to gain here by using again main->relations mapping, but... + * this implies BKE_libblock_remap & co to be able to update main->relations on the fly. + * Have to think about it a bit more, and see whether new code is OK first, anyway. */ + for (LinkNode *it = copied_ids; it; it = it->next) { + ID *id = it->link; + + BLI_assert(id->newid != NULL); + BLI_assert(id->lib != NULL); + + BKE_libblock_remap(bmain, id, id->newid, ID_REMAP_SKIP_INDIRECT_USAGE); + if (old_to_new_ids) { + BLI_ghash_insert(old_to_new_ids, id, id->newid); + } + + /* Special hack for groups... Thing is, since we can't instantiate them here, we need to + * ensure they remain 'alive' (only instantiation is a real group 'user'... *sigh* See + * T49722. */ + if (GS(id->name) == ID_GR && (id->tag & LIB_TAG_INDIRECT) != 0) { + id_us_ensure_real(id->newid); + } + } + +#ifdef DEBUG_TIME + printf("Step 4: Remap local usages of old (linked) ID to new (local) ID: Done.\n"); + TIMEIT_VALUE_PRINT(make_local); +#endif + + /* Step 5: proxy 'remapping' hack. */ + for (LinkNode *it = copied_ids; it; it = it->next) { + ID *id = it->link; + + /* Attempt to re-link copied proxy objects. This allows appending of an entire scene + * from another blend file into this one, even when that blend file contains proxified + * armatures that have local references. Since the proxified object needs to be linked + * (not local), this will only work when the "Localize all" checkbox is disabled. + * TL;DR: this is a dirty hack on top of an already weak feature (proxies). */ + if (GS(id->name) == ID_OB && ((Object *)id)->proxy != NULL) { + Object *ob = (Object *)id; + Object *ob_new = (Object *)id->newid; + bool is_local = false, is_lib = false; + + /* Proxies only work when the proxified object is linked-in from a library. */ + if (ob->proxy->id.lib == NULL) { + CLOG_WARN(&LOG, + "proxy object %s will loose its link to %s, because the " + "proxified object is local.", + id->newid->name, + ob->proxy->id.name); + continue; + } + + BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); + + /* We can only switch the proxy'ing to a made-local proxy if it is no longer + * referred to from a library. Not checking for local use; if new local proxy + * was not used locally would be a nasty bug! */ + if (is_local || is_lib) { + CLOG_WARN(&LOG, + "made-local proxy object %s will loose its link to %s, " + "because the linked-in proxy is referenced (is_local=%i, is_lib=%i).", + id->newid->name, + ob->proxy->id.name, + is_local, + is_lib); + } + else { + /* we can switch the proxy'ing from the linked-in to the made-local proxy. + * BKE_object_make_proxy() shouldn't be used here, as it allocates memory that + * was already allocated by BKE_object_make_local_ex() (which called BKE_object_copy). */ + ob_new->proxy = ob->proxy; + ob_new->proxy_group = ob->proxy_group; + ob_new->proxy_from = ob->proxy_from; + ob_new->proxy->proxy_from = ob_new; + ob->proxy = ob->proxy_from = ob->proxy_group = NULL; + } + } + } + +#ifdef DEBUG_TIME + printf("Step 5: Proxy 'remapping' hack: Done.\n"); + TIMEIT_VALUE_PRINT(make_local); +#endif + + /* This is probably more of a hack than something we should do here, but... + * Issue is, the whole copying + remapping done in complex cases above may leave pose-channels + * of armatures in complete invalid state (more precisely, the bone pointers of the + * pose-channels - very crappy cross-data-blocks relationship), se we tag it to be fully + * recomputed, but this does not seems to be enough in some cases, and evaluation code ends up + * trying to evaluate a not-yet-updated armature object's deformations. + * Try "make all local" in 04_01_H.lighting.blend from Agent327 without this, e.g. */ + for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) { + if (ob->data != NULL && ob->type == OB_ARMATURE && ob->pose != NULL && + ob->pose->flag & POSE_RECALC) { + BKE_pose_rebuild(bmain, ob, ob->data, true); + } + } + +#ifdef DEBUG_TIME + printf("Hack: Forcefully rebuild armature object poses: Done.\n"); + TIMEIT_VALUE_PRINT(make_local); +#endif + + BKE_main_id_clear_newpoins(bmain); + BLI_memarena_free(linklist_mem); + +#ifdef DEBUG_TIME + printf("Cleanup and finish: Done.\n"); + TIMEIT_END(make_local); +#endif +} + +/** + * Use after setting the ID's name + * When name exists: call 'new_id' + */ +void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) +{ + ListBase *lb; + ID *idtest; + + lb = which_libbase(bmain, GS(name)); + if (lb == NULL) { + return; + } + + /* search for id */ + idtest = BLI_findstring(lb, name + 2, offsetof(ID, name) + 2); + if (idtest != NULL) { + /* BKE_id_new_name_validate also takes care of sorting. */ + BKE_id_new_name_validate(lb, idtest, NULL); + bmain->is_memfile_undo_written = false; + } +} + +/** + * Sets the name of a block to name, suitably adjusted for uniqueness. + */ +void BKE_libblock_rename(Main *bmain, ID *id, const char *name) +{ + ListBase *lb = which_libbase(bmain, GS(id->name)); + if (BKE_id_new_name_validate(lb, id, name)) { + bmain->is_memfile_undo_written = false; + } +} + +/** + * Generate full name of the data-block (without ID code, but with library if any). + * + * \note Result is unique to a given ID type in a given Main database. + * + * \param name: An allocated string of minimal length #MAX_ID_FULL_NAME, + * will be filled with generated string. + */ +void BKE_id_full_name_get(char name[MAX_ID_FULL_NAME], const ID *id) +{ + strcpy(name, id->name + 2); + + if (id->lib != NULL) { + const size_t idname_len = strlen(id->name + 2); + const size_t libname_len = strlen(id->lib->id.name + 2); + + name[idname_len] = ' '; + name[idname_len + 1] = '['; + strcpy(name + idname_len + 2, id->lib->id.name + 2); + name[idname_len + 2 + libname_len] = ']'; + name[idname_len + 2 + libname_len + 1] = '\0'; + } +} + +/** + * Generate full name of the data-block (without ID code, but with library if any), + * with a 3-character prefix prepended indicating whether it comes from a library, + * is overriding, has a fake or no user, etc. + * + * \note Result is unique to a given ID type in a given Main database. + * + * \param name: An allocated string of minimal length #MAX_ID_FULL_NAME_UI, + * will be filled with generated string. + */ +void BKE_id_full_name_ui_prefix_get(char name[MAX_ID_FULL_NAME_UI], const ID *id) +{ + name[0] = id->lib ? (ID_MISSING(id) ? 'M' : 'L') : ID_IS_OVERRIDE_LIBRARY(id) ? 'O' : ' '; + name[1] = (id->flag & LIB_FAKEUSER) ? 'F' : ((id->us == 0) ? '0' : ' '); + name[2] = ' '; + + BKE_id_full_name_get(name + 3, id); +} + +/** + * Generate a concatenation of ID name (including two-chars type code) and its lib name, if any. + * + * \return A unique allocated string key for any ID in the whole Main database. + */ +char *BKE_id_to_unique_string_key(const struct ID *id) +{ + if (id->lib == NULL) { + return BLI_strdup(id->name); + } + else { + /* Prefix with an ascii character in the range of 32..96 (visible) + * this ensures we can't have a library ID pair that collide. + * Where 'LIfooOBbarOBbaz' could be ('LIfoo, OBbarOBbaz') or ('LIfooOBbar', 'OBbaz'). */ + const char ascii_len = strlen(id->lib->id.name + 2) + 32; + return BLI_sprintfN("%c%s%s", ascii_len, id->lib->id.name, id->name); + } +} + +void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath) +{ + /* in some cases this is used to update the absolute path from the + * relative */ + if (lib->name != filepath) { + BLI_strncpy(lib->name, filepath, sizeof(lib->name)); + } + + BLI_strncpy(lib->filepath, filepath, sizeof(lib->filepath)); + + /* not essential but set filepath is an absolute copy of value which + * is more useful if its kept in sync */ + if (BLI_path_is_rel(lib->filepath)) { + /* note that the file may be unsaved, in this case, setting the + * filepath on an indirectly linked path is not allowed from the + * outliner, and its not really supported but allow from here for now + * since making local could cause this to be directly linked - campbell + */ + /* Never make paths relative to parent lib - reading code (blenloader) always set *all* + * lib->name relative to current main, not to their parent for indirectly linked ones. */ + const char *basepath = BKE_main_blendfile_path(bmain); + BLI_path_abs(lib->filepath, basepath); + } +} + +void BKE_id_tag_set_atomic(ID *id, int tag) +{ + atomic_fetch_and_or_int32(&id->tag, tag); +} + +void BKE_id_tag_clear_atomic(ID *id, int tag) +{ + atomic_fetch_and_and_int32(&id->tag, ~tag); +} + +/** + * Check that given ID pointer actually is in G_MAIN. + * Main intended use is for debug asserts in places we cannot easily get rid of G_Main... + */ +bool BKE_id_is_in_global_main(ID *id) +{ + /* We do not want to fail when id is NULL here, even though this is a bit strange behavior... + */ + return (id == NULL || BLI_findindex(which_libbase(G_MAIN, GS(id->name)), id) != -1); +} + +/************************* Datablock order in UI **************************/ + +static int *id_order_get(ID *id) +{ + /* Only for workspace tabs currently. */ + switch (GS(id->name)) { + case ID_WS: + return &((WorkSpace *)id)->order; + default: + return NULL; + } +} + +static int id_order_compare(const void *a, const void *b) +{ + ID *id_a = ((LinkData *)a)->data; + ID *id_b = ((LinkData *)b)->data; + + int *order_a = id_order_get(id_a); + int *order_b = id_order_get(id_b); + + if (order_a && order_b) { + if (*order_a < *order_b) { + return -1; + } + else if (*order_a > *order_b) { + return 1; + } + } + + return strcmp(id_a->name, id_b->name); +} + +/** + * Returns ordered list of data-blocks for display in the UI. + * Result is list of LinkData of IDs that must be freed. + */ +void BKE_id_ordered_list(ListBase *ordered_lb, const ListBase *lb) +{ + BLI_listbase_clear(ordered_lb); + + for (ID *id = lb->first; id; id = id->next) { + BLI_addtail(ordered_lb, BLI_genericNodeN(id)); + } + + BLI_listbase_sort(ordered_lb, id_order_compare); + + int num = 0; + for (LinkData *link = ordered_lb->first; link; link = link->next) { + int *order = id_order_get(link->data); + if (order) { + *order = num++; + } + } +} + +/** + * Reorder ID in the list, before or after the "relative" ID. + */ +void BKE_id_reorder(const ListBase *lb, ID *id, ID *relative, bool after) +{ + int *id_order = id_order_get(id); + int relative_order; + + if (relative) { + relative_order = *id_order_get(relative); + } + else { + relative_order = (after) ? BLI_listbase_count(lb) : 0; + } + + if (after) { + /* Insert after. */ + for (ID *other = lb->first; other; other = other->next) { + int *order = id_order_get(other); + if (*order > relative_order) { + (*order)++; + } + } + + *id_order = relative_order + 1; + } + else { + /* Insert before. */ + for (ID *other = lb->first; other; other = other->next) { + int *order = id_order_get(other); + if (*order < relative_order) { + (*order)--; + } + } + + *id_order = relative_order - 1; + } +} diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c new file mode 100644 index 00000000000..4985dd1eb51 --- /dev/null +++ b/source/blender/blenkernel/intern/lib_override.c @@ -0,0 +1,954 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup bke + */ + +#include +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_ID.h" +#include "DNA_object_types.h" + +#include "DEG_depsgraph.h" + +#include "BKE_armature.h" +#include "BKE_lib_id.h" +#include "BKE_lib_override.h" +#include "BKE_lib_remap.h" +#include "BKE_main.h" + +#include "BLI_utildefines.h" +#include "BLI_ghash.h" +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "RNA_access.h" +#include "RNA_types.h" + +#include "PIL_time.h" +#include "PIL_time_utildefines.h" + +#define OVERRIDE_AUTO_CHECK_DELAY 0.2 /* 200ms between auto-override checks. */ + +static void bke_override_property_copy(IDOverrideLibraryProperty *op_dst, + IDOverrideLibraryProperty *op_src); +static void bke_override_property_operation_copy(IDOverrideLibraryPropertyOperation *opop_dst, + IDOverrideLibraryPropertyOperation *opop_src); + +static void bke_override_property_clear(IDOverrideLibraryProperty *op); +static void bke_override_property_operation_clear(IDOverrideLibraryPropertyOperation *opop); + +/* Temp, for until library override is ready and tested enough to go 'public', + * we hide it by default in UI and such. */ +static bool _override_library_enabled = true; + +void BKE_override_library_enable(const bool do_enable) +{ + _override_library_enabled = do_enable; +} + +bool BKE_override_library_is_enabled() +{ + return _override_library_enabled; +} + +/** Initialize empty overriding of \a reference_id by \a local_id. */ +IDOverrideLibrary *BKE_override_library_init(ID *local_id, ID *reference_id) +{ + /* If reference_id is NULL, we are creating an override template for purely local data. + * Else, reference *must* be linked data. */ + BLI_assert(reference_id == NULL || reference_id->lib != NULL); + BLI_assert(local_id->override_library == NULL); + + ID *ancestor_id; + for (ancestor_id = reference_id; ancestor_id != NULL && ancestor_id->override_library != NULL && + ancestor_id->override_library->reference != NULL; + ancestor_id = ancestor_id->override_library->reference) { + /* pass */ + } + + if (ancestor_id != NULL && ancestor_id->override_library != NULL) { + /* Original ID has a template, use it! */ + BKE_override_library_copy(local_id, ancestor_id); + if (local_id->override_library->reference != reference_id) { + id_us_min(local_id->override_library->reference); + local_id->override_library->reference = reference_id; + id_us_plus(local_id->override_library->reference); + } + return local_id->override_library; + } + + /* Else, generate new empty override. */ + local_id->override_library = MEM_callocN(sizeof(*local_id->override_library), __func__); + local_id->override_library->reference = reference_id; + id_us_plus(local_id->override_library->reference); + local_id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK; + /* TODO do we want to add tag or flag to referee to mark it as such? */ + return local_id->override_library; +} + +/** Deep copy of a whole override from \a src_id to \a dst_id. */ +void BKE_override_library_copy(ID *dst_id, const ID *src_id) +{ + BLI_assert(src_id->override_library != NULL); + + if (dst_id->override_library != NULL) { + if (src_id->override_library == NULL) { + BKE_override_library_free(&dst_id->override_library, true); + return; + } + else { + BKE_override_library_clear(dst_id->override_library, true); + } + } + else if (src_id->override_library == NULL) { + return; + } + else { + BKE_override_library_init(dst_id, NULL); + } + + /* Source is already overriding data, we copy it but reuse its reference for dest ID. + * otherwise, source is only an override template, it then becomes reference of dest ID. */ + dst_id->override_library->reference = src_id->override_library->reference ? + src_id->override_library->reference : + (ID *)src_id; + id_us_plus(dst_id->override_library->reference); + + BLI_duplicatelist(&dst_id->override_library->properties, &src_id->override_library->properties); + for (IDOverrideLibraryProperty *op_dst = dst_id->override_library->properties.first, + *op_src = src_id->override_library->properties.first; + op_dst; + op_dst = op_dst->next, op_src = op_src->next) { + bke_override_property_copy(op_dst, op_src); + } + + dst_id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK; +} + +/** Clear any overriding data from given \a override. */ +void BKE_override_library_clear(IDOverrideLibrary *override, const bool do_id_user) +{ + BLI_assert(override != NULL); + + if (override->runtime != NULL) { + BLI_ghash_clear(override->runtime, NULL, NULL); + } + + for (IDOverrideLibraryProperty *op = override->properties.first; op; op = op->next) { + bke_override_property_clear(op); + } + BLI_freelistN(&override->properties); + + if (do_id_user) { + id_us_min(override->reference); + /* override->storage should never be refcounted... */ + } +} + +/** Free given \a override. */ +void BKE_override_library_free(struct IDOverrideLibrary **override, const bool do_id_user) +{ + BLI_assert(*override != NULL); + + if ((*override)->runtime != NULL) { + BLI_ghash_free((*override)->runtime, NULL, NULL); + (*override)->runtime = NULL; + } + + BKE_override_library_clear(*override, do_id_user); + MEM_freeN(*override); + *override = NULL; +} + +static ID *override_library_create_from(Main *bmain, ID *reference_id) +{ + ID *local_id; + + if (!BKE_id_copy(bmain, reference_id, (ID **)&local_id)) { + return NULL; + } + id_us_min(local_id); + + BKE_override_library_init(local_id, reference_id); + local_id->override_library->flag |= OVERRIDE_LIBRARY_AUTO; + + return local_id; +} + +/** Create an overridden local copy of linked reference. */ +ID *BKE_override_library_create_from_id(Main *bmain, ID *reference_id, const bool do_tagged_remap) +{ + BLI_assert(reference_id != NULL); + BLI_assert(reference_id->lib != NULL); + + ID *local_id = override_library_create_from(bmain, reference_id); + + if (do_tagged_remap) { + ID *other_id; + FOREACH_MAIN_ID_BEGIN (bmain, other_id) { + if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib == NULL) { + /* Note that using ID_REMAP_SKIP_INDIRECT_USAGE below is superfluous, as we only remap + * local IDs usages anyway... */ + BKE_libblock_relink_ex(bmain, + other_id, + reference_id, + local_id, + ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY); + } + } + FOREACH_MAIN_ID_END; + } + + return local_id; +} + +/** + * Create overridden local copies of all tagged data-blocks in given Main. + * + * \note Set id->newid of overridden libs with newly created overrides, + * caller is responsible to clean those pointers before/after usage as needed. + * + * \note By default, it will only remap newly created local overriding data-blocks between + * themselves, to avoid 'enforcing' those overrides into all other usages of the linked data in + * main. You can add more local IDs to be remapped to use new overriding ones by setting their + * LIB_TAG_DOIT tag. + * + * \return \a true on success, \a false otherwise. + */ +bool BKE_override_library_create_from_tag(Main *bmain) +{ + ID *reference_id; + bool ret = true; + + ListBase todo_ids = {NULL}; + LinkData *todo_id_iter; + + /* Get all IDs we want to override. */ + FOREACH_MAIN_ID_BEGIN (bmain, reference_id) { + if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib != NULL) { + todo_id_iter = MEM_callocN(sizeof(*todo_id_iter), __func__); + todo_id_iter->data = reference_id; + BLI_addtail(&todo_ids, todo_id_iter); + } + } + FOREACH_MAIN_ID_END; + + /* Override the IDs. */ + for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) { + reference_id = todo_id_iter->data; + if ((reference_id->newid = override_library_create_from(bmain, reference_id)) == NULL) { + ret = false; + } + else { + /* We also tag the new IDs so that in next step we can remap their pointers too. */ + reference_id->newid->tag |= LIB_TAG_DOIT; + } + } + + /* Only remap new local ID's pointers, we don't want to force our new overrides onto our whole + * existing linked IDs usages. */ + for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) { + ID *other_id; + reference_id = todo_id_iter->data; + + if (reference_id->newid == NULL) { + continue; + } + + /* Still checking the whole Main, that way we can tag other local IDs as needing to be remapped + * to use newly created overriding IDs, if needed. */ + FOREACH_MAIN_ID_BEGIN (bmain, other_id) { + if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib == NULL) { + ID *local_id = reference_id->newid; + /* Note that using ID_REMAP_SKIP_INDIRECT_USAGE below is superfluous, as we only remap + * local IDs usages anyway... */ + BKE_libblock_relink_ex(bmain, + other_id, + reference_id, + local_id, + ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY); + } + } + FOREACH_MAIN_ID_END; + } + + BLI_freelistN(&todo_ids); + + return ret; +} + +/* We only build override GHash on request. */ +BLI_INLINE IDOverrideLibraryRuntime *override_library_rna_path_mapping_ensure( + IDOverrideLibrary *override) +{ + if (override->runtime == NULL) { + override->runtime = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__); + for (IDOverrideLibraryProperty *op = override->properties.first; op != NULL; op = op->next) { + BLI_ghash_insert(override->runtime, op->rna_path, op); + } + } + + return override->runtime; +} + +/** + * Find override property from given RNA path, if it exists. + */ +IDOverrideLibraryProperty *BKE_override_library_property_find(IDOverrideLibrary *override, + const char *rna_path) +{ + IDOverrideLibraryRuntime *override_runtime = override_library_rna_path_mapping_ensure(override); + return BLI_ghash_lookup(override_runtime, rna_path); +} + +/** + * Find override property from given RNA path, or create it if it does not exist. + */ +IDOverrideLibraryProperty *BKE_override_library_property_get(IDOverrideLibrary *override, + const char *rna_path, + bool *r_created) +{ + IDOverrideLibraryProperty *op = BKE_override_library_property_find(override, rna_path); + + if (op == NULL) { + op = MEM_callocN(sizeof(IDOverrideLibraryProperty), __func__); + op->rna_path = BLI_strdup(rna_path); + BLI_addtail(&override->properties, op); + + IDOverrideLibraryRuntime *override_runtime = override_library_rna_path_mapping_ensure( + override); + BLI_ghash_insert(override_runtime, op->rna_path, op); + + if (r_created) { + *r_created = true; + } + } + else if (r_created) { + *r_created = false; + } + + return op; +} + +void bke_override_property_copy(IDOverrideLibraryProperty *op_dst, + IDOverrideLibraryProperty *op_src) +{ + op_dst->rna_path = BLI_strdup(op_src->rna_path); + BLI_duplicatelist(&op_dst->operations, &op_src->operations); + + for (IDOverrideLibraryPropertyOperation *opop_dst = op_dst->operations.first, + *opop_src = op_src->operations.first; + opop_dst; + opop_dst = opop_dst->next, opop_src = opop_src->next) { + bke_override_property_operation_copy(opop_dst, opop_src); + } +} + +void bke_override_property_clear(IDOverrideLibraryProperty *op) +{ + BLI_assert(op->rna_path != NULL); + + MEM_freeN(op->rna_path); + + for (IDOverrideLibraryPropertyOperation *opop = op->operations.first; opop; opop = opop->next) { + bke_override_property_operation_clear(opop); + } + BLI_freelistN(&op->operations); +} + +/** + * Remove and free given \a override_property from given ID \a override. + */ +void BKE_override_library_property_delete(IDOverrideLibrary *override, + IDOverrideLibraryProperty *override_property) +{ + bke_override_property_clear(override_property); + if (override->runtime != NULL) { + BLI_ghash_remove(override->runtime, override_property->rna_path, NULL, NULL); + } + BLI_freelinkN(&override->properties, override_property); +} + +/** + * Find override property operation from given sub-item(s), if it exists. + */ +IDOverrideLibraryPropertyOperation *BKE_override_library_property_operation_find( + IDOverrideLibraryProperty *override_property, + const char *subitem_refname, + const char *subitem_locname, + const int subitem_refindex, + const int subitem_locindex, + const bool strict, + bool *r_strict) +{ + IDOverrideLibraryPropertyOperation *opop; + const int subitem_defindex = -1; + + if (r_strict) { + *r_strict = true; + } + + if (subitem_locname != NULL) { + opop = BLI_findstring_ptr(&override_property->operations, + subitem_locname, + offsetof(IDOverrideLibraryPropertyOperation, subitem_local_name)); + + if (opop == NULL) { + return NULL; + } + + if (subitem_refname == NULL || opop->subitem_reference_name == NULL) { + return subitem_refname == opop->subitem_reference_name ? opop : NULL; + } + return (subitem_refname != NULL && opop->subitem_reference_name != NULL && + STREQ(subitem_refname, opop->subitem_reference_name)) ? + opop : + NULL; + } + + if (subitem_refname != NULL) { + opop = BLI_findstring_ptr( + &override_property->operations, + subitem_refname, + offsetof(IDOverrideLibraryPropertyOperation, subitem_reference_name)); + + if (opop == NULL) { + return NULL; + } + + if (subitem_locname == NULL || opop->subitem_local_name == NULL) { + return subitem_locname == opop->subitem_local_name ? opop : NULL; + } + return (subitem_locname != NULL && opop->subitem_local_name != NULL && + STREQ(subitem_locname, opop->subitem_local_name)) ? + opop : + NULL; + } + + if ((opop = BLI_listbase_bytes_find( + &override_property->operations, + &subitem_locindex, + sizeof(subitem_locindex), + offsetof(IDOverrideLibraryPropertyOperation, subitem_local_index)))) { + return ELEM(subitem_refindex, -1, opop->subitem_reference_index) ? opop : NULL; + } + + if ((opop = BLI_listbase_bytes_find( + &override_property->operations, + &subitem_refindex, + sizeof(subitem_refindex), + offsetof(IDOverrideLibraryPropertyOperation, subitem_reference_index)))) { + return ELEM(subitem_locindex, -1, opop->subitem_local_index) ? opop : NULL; + } + + /* index == -1 means all indices, that is valid fallback in case we requested specific index. */ + if (!strict && (subitem_locindex != subitem_defindex) && + (opop = BLI_listbase_bytes_find( + &override_property->operations, + &subitem_defindex, + sizeof(subitem_defindex), + offsetof(IDOverrideLibraryPropertyOperation, subitem_local_index)))) { + if (r_strict) { + *r_strict = false; + } + return opop; + } + + return NULL; +} + +/** + * Find override property operation from given sub-item(s), or create it if it does not exist. + */ +IDOverrideLibraryPropertyOperation *BKE_override_library_property_operation_get( + IDOverrideLibraryProperty *override_property, + const short operation, + const char *subitem_refname, + const char *subitem_locname, + const int subitem_refindex, + const int subitem_locindex, + const bool strict, + bool *r_strict, + bool *r_created) +{ + IDOverrideLibraryPropertyOperation *opop = BKE_override_library_property_operation_find( + override_property, + subitem_refname, + subitem_locname, + subitem_refindex, + subitem_locindex, + strict, + r_strict); + + if (opop == NULL) { + opop = MEM_callocN(sizeof(IDOverrideLibraryPropertyOperation), __func__); + opop->operation = operation; + if (subitem_locname) { + opop->subitem_local_name = BLI_strdup(subitem_locname); + } + if (subitem_refname) { + opop->subitem_reference_name = BLI_strdup(subitem_refname); + } + opop->subitem_local_index = subitem_locindex; + opop->subitem_reference_index = subitem_refindex; + + BLI_addtail(&override_property->operations, opop); + + if (r_created) { + *r_created = true; + } + } + else if (r_created) { + *r_created = false; + } + + return opop; +} + +void bke_override_property_operation_copy(IDOverrideLibraryPropertyOperation *opop_dst, + IDOverrideLibraryPropertyOperation *opop_src) +{ + if (opop_src->subitem_reference_name) { + opop_dst->subitem_reference_name = BLI_strdup(opop_src->subitem_reference_name); + } + if (opop_src->subitem_local_name) { + opop_dst->subitem_local_name = BLI_strdup(opop_src->subitem_local_name); + } +} + +void bke_override_property_operation_clear(IDOverrideLibraryPropertyOperation *opop) +{ + if (opop->subitem_reference_name) { + MEM_freeN(opop->subitem_reference_name); + } + if (opop->subitem_local_name) { + MEM_freeN(opop->subitem_local_name); + } +} + +/** + * Remove and free given \a override_property_operation from given ID \a override_property. + */ +void BKE_override_library_property_operation_delete( + IDOverrideLibraryProperty *override_property, + IDOverrideLibraryPropertyOperation *override_property_operation) +{ + bke_override_property_operation_clear(override_property_operation); + BLI_freelinkN(&override_property->operations, override_property_operation); +} + +/** + * Check that status of local data-block is still valid against current reference one. + * + * It means that all overridable, but not overridden, properties' local values must be equal to + * reference ones. Clears #LIB_TAG_OVERRIDE_OK if they do not. + * + * This is typically used to detect whether some property has been changed in local and a new + * #IDOverrideProperty (of #IDOverridePropertyOperation) has to be added. + * + * \return true if status is OK, false otherwise. */ +bool BKE_override_library_status_check_local(Main *bmain, ID *local) +{ + BLI_assert(local->override_library != NULL); + + ID *reference = local->override_library->reference; + + if (reference == NULL) { + /* This is an override template, local status is always OK! */ + return true; + } + + BLI_assert(GS(local->name) == GS(reference->name)); + + if (GS(local->name) == ID_OB) { + /* Our beloved pose's bone cross-data pointers... Usually, depsgraph evaluation would ensure + * this is valid, but in some cases (like hidden collections etc.) this won't be the case, so + * we need to take care of this ourselves. */ + Object *ob_local = (Object *)local; + if (ob_local->data != NULL && ob_local->type == OB_ARMATURE && ob_local->pose != NULL && + ob_local->pose->flag & POSE_RECALC) { + BKE_pose_rebuild(bmain, ob_local, ob_local->data, true); + } + } + + /* Note that reference is assumed always valid, caller has to ensure that itself. */ + + PointerRNA rnaptr_local, rnaptr_reference; + RNA_id_pointer_create(local, &rnaptr_local); + RNA_id_pointer_create(reference, &rnaptr_reference); + + if (!RNA_struct_override_matches(bmain, + &rnaptr_local, + &rnaptr_reference, + NULL, + local->override_library, + RNA_OVERRIDE_COMPARE_IGNORE_NON_OVERRIDABLE | + RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN, + NULL)) { + local->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK; + return false; + } + + return true; +} + +/** + * Check that status of reference data-block is still valid against current local one. + * + * It means that all non-overridden properties' local values must be equal to reference ones. + * Clears LIB_TAG_OVERRIDE_OK if they do not. + * + * This is typically used to detect whether some reference has changed and local + * needs to be updated against it. + * + * \return true if status is OK, false otherwise. */ +bool BKE_override_library_status_check_reference(Main *bmain, ID *local) +{ + BLI_assert(local->override_library != NULL); + + ID *reference = local->override_library->reference; + + if (reference == NULL) { + /* This is an override template, reference is virtual, so its status is always OK! */ + return true; + } + + BLI_assert(GS(local->name) == GS(reference->name)); + + if (reference->override_library && (reference->tag & LIB_TAG_OVERRIDE_LIBRARY_REFOK) == 0) { + if (!BKE_override_library_status_check_reference(bmain, reference)) { + /* If reference is also override of another data-block, and its status is not OK, + * then this override is not OK either. + * Note that this should only happen when reloading libraries... */ + local->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK; + return false; + } + } + + if (GS(local->name) == ID_OB) { + /* Our beloved pose's bone cross-data pointers... Usually, depsgraph evaluation would ensure + * this is valid, but in some cases (like hidden collections etc.) this won't be the case, so + * we need to take care of this ourselves. */ + Object *ob_local = (Object *)local; + if (ob_local->data != NULL && ob_local->type == OB_ARMATURE && ob_local->pose != NULL && + ob_local->pose->flag & POSE_RECALC) { + BKE_pose_rebuild(bmain, ob_local, ob_local->data, true); + } + } + + PointerRNA rnaptr_local, rnaptr_reference; + RNA_id_pointer_create(local, &rnaptr_local); + RNA_id_pointer_create(reference, &rnaptr_reference); + + if (!RNA_struct_override_matches(bmain, + &rnaptr_local, + &rnaptr_reference, + NULL, + local->override_library, + RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN, + NULL)) { + local->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK; + return false; + } + + return true; +} + +/** + * Compares local and reference data-blocks and create new override operations as needed, + * or reset to reference values if overriding is not allowed. + * + * \note Defining override operations is only mandatory before saving a `.blend` file on disk + * (not for undo!). + * Knowing that info at runtime is only useful for UI/UX feedback. + * + * \note This is by far the biggest operation (the more time-consuming) of the three so far, + * since it has to go over all properties in depth (all overridable ones at least). + * Generating diff values and applying overrides are much cheaper. + * + * \return true if new overriding op was created, or some local data was reset. */ +bool BKE_override_library_operations_create(Main *bmain, ID *local, const bool force_auto) +{ + BLI_assert(local->override_library != NULL); + const bool is_template = (local->override_library->reference == NULL); + bool ret = false; + + if (!is_template && (force_auto || local->override_library->flag & OVERRIDE_LIBRARY_AUTO)) { + /* Do not attempt to generate overriding rules from an empty place-holder generated by link + * code when it cannot find to actual library/ID. Much better to keep the local datablock as + * is in the file in that case, until broken lib is fixed. */ + if (ID_MISSING(local->override_library->reference)) { + return ret; + } + + if (GS(local->name) == ID_OB) { + /* Our beloved pose's bone cross-data pointers... Usually, depsgraph evaluation would ensure + * this is valid, but in some cases (like hidden collections etc.) this won't be the case, so + * we need to take care of this ourselves. */ + Object *ob_local = (Object *)local; + if (ob_local->data != NULL && ob_local->type == OB_ARMATURE && ob_local->pose != NULL && + ob_local->pose->flag & POSE_RECALC) { + BKE_pose_rebuild(bmain, ob_local, ob_local->data, true); + } + } + + PointerRNA rnaptr_local, rnaptr_reference; + RNA_id_pointer_create(local, &rnaptr_local); + RNA_id_pointer_create(local->override_library->reference, &rnaptr_reference); + + eRNAOverrideMatchResult report_flags = 0; + RNA_struct_override_matches(bmain, + &rnaptr_local, + &rnaptr_reference, + NULL, + local->override_library, + RNA_OVERRIDE_COMPARE_CREATE | RNA_OVERRIDE_COMPARE_RESTORE, + &report_flags); + if (report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) { + ret = true; + } +#ifndef NDEBUG + if (report_flags & RNA_OVERRIDE_MATCH_RESULT_RESTORED) { + printf("We did restore some properties of %s from its reference.\n", local->name); + } + if (ret) { + printf("We did generate library override rules for %s\n", local->name); + } + else { + printf("No new library override rules for %s\n", local->name); + } +#endif + } + return ret; +} + +/** Check all overrides from given \a bmain and create/update overriding operations as needed. */ +void BKE_main_override_library_operations_create(Main *bmain, const bool force_auto) +{ + ID *id; + + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if ((ID_IS_OVERRIDE_LIBRARY(id) && force_auto) || + (ID_IS_OVERRIDE_LIBRARY_AUTO(id) && (id->tag & LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH))) { + BKE_override_library_operations_create(bmain, id, force_auto); + id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH; + } + } + FOREACH_MAIN_ID_END; +} + +/** Update given override from its reference (re-applying overridden properties). */ +void BKE_override_library_update(Main *bmain, ID *local) +{ + if (local->override_library == NULL || local->override_library->reference == NULL) { + return; + } + + /* Do not attempt to apply overriding rules over an empty place-holder generated by link code + * when it cannot find to actual library/ID. Much better to keep the local datablock as loaded + * from the file in that case, until broken lib is fixed. */ + if (ID_MISSING(local->override_library->reference)) { + return; + } + + /* Recursively do 'ancestors' overrides first, if any. */ + if (local->override_library->reference->override_library && + (local->override_library->reference->tag & LIB_TAG_OVERRIDE_LIBRARY_REFOK) == 0) { + BKE_override_library_update(bmain, local->override_library->reference); + } + + /* We want to avoid having to remap here, however creating up-to-date override is much simpler + * if based on reference than on current override. + * So we work on temp copy of reference, and 'swap' its content with local. */ + + /* XXX We need a way to get off-Main copies of IDs (similar to localized mats/texts/ etc.)! + * However, this is whole bunch of code work in itself, so for now plain stupid ID copy will + * do, as inn-efficient as it is. :/ + * Actually, maybe not! Since we are swapping with original ID's local content, we want to + * keep user-count in correct state when freeing tmp_id + * (and that user-counts of IDs used by 'new' local data also remain correct). */ + /* This would imply change in handling of user-count all over RNA + * (and possibly all over Blender code). + * Not impossible to do, but would rather see first if extra useless usual user handling + * is actually a (performances) issue here. */ + + ID *tmp_id; + BKE_id_copy(bmain, local->override_library->reference, &tmp_id); + + if (tmp_id == NULL) { + return; + } + + PointerRNA rnaptr_src, rnaptr_dst, rnaptr_storage_stack, *rnaptr_storage = NULL; + RNA_id_pointer_create(local, &rnaptr_src); + RNA_id_pointer_create(tmp_id, &rnaptr_dst); + if (local->override_library->storage) { + rnaptr_storage = &rnaptr_storage_stack; + RNA_id_pointer_create(local->override_library->storage, rnaptr_storage); + } + + RNA_struct_override_apply( + bmain, &rnaptr_dst, &rnaptr_src, rnaptr_storage, local->override_library); + + /* This also transfers all pointers (memory) owned by local to tmp_id, and vice-versa. + * So when we'll free tmp_id, we'll actually free old, outdated data from local. */ + BKE_id_swap(bmain, local, tmp_id); + + /* Again, horribly inn-efficient in our case, we need something off-Main + * (aka more generic nolib copy/free stuff)! */ + /* XXX And crashing in complex cases (e.g. because depsgraph uses same data...). */ + BKE_id_free_ex(bmain, tmp_id, LIB_ID_FREE_NO_UI_USER, true); + + if (local->override_library->storage) { + /* We know this datablock is not used anywhere besides local->override->storage. */ + /* XXX For until we get fully shadow copies, we still need to ensure storage releases + * its usage of any ID pointers it may have. */ + BKE_id_free_ex(bmain, local->override_library->storage, LIB_ID_FREE_NO_UI_USER, true); + local->override_library->storage = NULL; + } + + local->tag |= LIB_TAG_OVERRIDE_LIBRARY_REFOK; + + /* Full rebuild of Depsgraph! */ + + /* XXX Is this actual valid replacement for old DAG_relations_tag_update(bmain) ? */ + DEG_on_visible_update(bmain, true); +} + +/** Update all overrides from given \a bmain. */ +void BKE_main_override_library_update(Main *bmain) +{ + ID *id; + + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (id->override_library != NULL && id->lib == NULL) { + BKE_override_library_update(bmain, id); + } + } + FOREACH_MAIN_ID_END; +} + +/** + * Storage (how to store overriding data into `.blend` files). + * + * Basically: + * 1) Only 'differential' storage needs special handling here. All others (replacing values or + * inserting/removing items from a collection) can be handled with simply storing current + * content of local data-block. + * 2) We store the differential value into a second 'ghost' data-block, + * which is an empty ID of same type as local one, + * where we only define values that need differential data. + * + * This avoids us having to modify 'real' data-block at write time (and restoring it afterwards), + * which is inefficient, and potentially dangerous (in case of concurrent access...), while not + * using much extra memory in typical cases. It also ensures stored data-block always contains + * exact same data as "desired" ones (kind of "baked" data-blocks). + */ + +/** Initialize an override storage. */ +OverrideLibraryStorage *BKE_override_library_operations_store_initialize(void) +{ + return BKE_main_new(); +} + +/** + * Generate suitable 'write' data (this only affects differential override operations). + * + * Note that \a local ID is no more modified by this call, + * all extra data are stored in its temp \a storage_id copy. */ +ID *BKE_override_library_operations_store_start(Main *bmain, + OverrideLibraryStorage *override_storage, + ID *local) +{ + BLI_assert(local->override_library != NULL); + BLI_assert(override_storage != NULL); + const bool is_template = (local->override_library->reference == NULL); + + if (is_template) { + /* This is actually purely local data with an override template, nothing to do here! */ + return NULL; + } + + /* Forcefully ensure we know about all needed override operations. */ + BKE_override_library_operations_create(bmain, local, false); + + ID *storage_id; +#ifdef DEBUG_OVERRIDE_TIMEIT + TIMEIT_START_AVERAGED(BKE_override_operations_store_start); +#endif + + /* XXX TODO We may also want a specialized handling of things here too, to avoid copying heavy + * never-overridable data (like Mesh geometry etc.)? And also maybe avoid lib reference-counting + * completely (shallow copy...). */ + /* This would imply change in handling of user-count all over RNA + * (and possibly all over Blender code). + * Not impossible to do, but would rather see first is extra useless usual user handling is + * actually a (performances) issue here, before doing it. */ + BKE_id_copy((Main *)override_storage, local, &storage_id); + + if (storage_id != NULL) { + PointerRNA rnaptr_reference, rnaptr_final, rnaptr_storage; + RNA_id_pointer_create(local->override_library->reference, &rnaptr_reference); + RNA_id_pointer_create(local, &rnaptr_final); + RNA_id_pointer_create(storage_id, &rnaptr_storage); + + if (!RNA_struct_override_store( + bmain, &rnaptr_final, &rnaptr_reference, &rnaptr_storage, local->override_library)) { + BKE_id_free_ex(override_storage, storage_id, LIB_ID_FREE_NO_UI_USER, true); + storage_id = NULL; + } + } + + local->override_library->storage = storage_id; + +#ifdef DEBUG_OVERRIDE_TIMEIT + TIMEIT_END_AVERAGED(BKE_override_operations_store_start); +#endif + return storage_id; +} + +/** Restore given ID modified by \a BKE_override_operations_store_start, to its original state. */ +void BKE_override_library_operations_store_end(OverrideLibraryStorage *UNUSED(override_storage), + ID *local) +{ + BLI_assert(local->override_library != NULL); + + /* Nothing else to do here really, we need to keep all temp override storage data-blocks in + * memory until whole file is written anyway (otherwise we'd get mem pointers overlap...). */ + local->override_library->storage = NULL; +} + +void BKE_override_library_operations_store_finalize(OverrideLibraryStorage *override_storage) +{ + /* We cannot just call BKE_main_free(override_storage), not until we have option to make 'ghost' + * copies of IDs without increasing usercount of used data-blocks. */ + ID *id; + + FOREACH_MAIN_ID_BEGIN (override_storage, id) { + BKE_id_free_ex(override_storage, id, LIB_ID_FREE_NO_UI_USER, true); + } + FOREACH_MAIN_ID_END; + + BKE_main_free(override_storage); +} diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c new file mode 100644 index 00000000000..09e7be591cd --- /dev/null +++ b/source/blender/blenkernel/intern/lib_query.c @@ -0,0 +1,1477 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2014 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup bke + */ + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_anim_types.h" +#include "DNA_armature_types.h" +#include "DNA_brush_types.h" +#include "DNA_camera_types.h" +#include "DNA_collection_types.h" +#include "DNA_constraint_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_key_types.h" +#include "DNA_light_types.h" +#include "DNA_lattice_types.h" +#include "DNA_linestyle_types.h" +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_meta_types.h" +#include "DNA_movieclip_types.h" +#include "DNA_mask_types.h" +#include "DNA_node_types.h" +#include "DNA_object_force_types.h" +#include "DNA_lightprobe_types.h" +#include "DNA_rigidbody_types.h" +#include "DNA_scene_types.h" +#include "DNA_sequence_types.h" +#include "DNA_screen_types.h" +#include "DNA_speaker_types.h" +#include "DNA_sound_types.h" +#include "DNA_text_types.h" +#include "DNA_vfont_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" +#include "DNA_world_types.h" + +#include "BLI_utildefines.h" +#include "BLI_ghash.h" +#include "BLI_linklist_stack.h" + +#include "BKE_animsys.h" +#include "BKE_collection.h" +#include "BKE_constraint.h" +#include "BKE_fcurve.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_idprop.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" +#include "BKE_main.h" +#include "BKE_modifier.h" +#include "BKE_node.h" +#include "BKE_particle.h" +#include "BKE_rigidbody.h" +#include "BKE_sequencer.h" +#include "BKE_shader_fx.h" +#include "BKE_tracking.h" +#include "BKE_workspace.h" + +#define FOREACH_FINALIZE _finalize +#define FOREACH_FINALIZE_VOID \ + if (0) { \ + goto FOREACH_FINALIZE; \ + } \ + FOREACH_FINALIZE: \ + ((void)0) + +#define FOREACH_CALLBACK_INVOKE_ID_PP(_data, id_pp, _cb_flag) \ + CHECK_TYPE(id_pp, ID **); \ + if (!((_data)->status & IDWALK_STOP)) { \ + const int _flag = (_data)->flag; \ + ID *old_id = *(id_pp); \ + const int callback_return = (_data)->callback((_data)->user_data, \ + (_data)->self_id, \ + id_pp, \ + (_cb_flag | (_data)->cb_flag) & \ + ~(_data)->cb_flag_clear); \ + if (_flag & IDWALK_READONLY) { \ + BLI_assert(*(id_pp) == old_id); \ + } \ + if (old_id && (_flag & IDWALK_RECURSE)) { \ + if (BLI_gset_add((_data)->ids_handled, old_id)) { \ + if (!(callback_return & IDWALK_RET_STOP_RECURSION)) { \ + BLI_LINKSTACK_PUSH((_data)->ids_todo, old_id); \ + } \ + } \ + } \ + if (callback_return & IDWALK_RET_STOP_ITER) { \ + (_data)->status |= IDWALK_STOP; \ + goto FOREACH_FINALIZE; \ + } \ + } \ + else { \ + goto FOREACH_FINALIZE; \ + } \ + ((void)0) + +#define FOREACH_CALLBACK_INVOKE_ID(_data, id, cb_flag) \ + { \ + CHECK_TYPE_ANY(id, ID *, void *); \ + FOREACH_CALLBACK_INVOKE_ID_PP(_data, (ID **)&(id), cb_flag); \ + } \ + ((void)0) + +#define FOREACH_CALLBACK_INVOKE(_data, id_super, cb_flag) \ + { \ + CHECK_TYPE(&((id_super)->id), ID *); \ + FOREACH_CALLBACK_INVOKE_ID_PP(_data, (ID **)&(id_super), cb_flag); \ + } \ + ((void)0) + +/* status */ +enum { + IDWALK_STOP = 1 << 0, +}; + +typedef struct LibraryForeachIDData { + ID *self_id; + int flag; + int cb_flag; + int cb_flag_clear; + LibraryIDLinkCallback callback; + void *user_data; + int status; + + /* To handle recursion. */ + GSet *ids_handled; /* All IDs that are either already done, or still in ids_todo stack. */ + BLI_LINKSTACK_DECLARE(ids_todo, ID *); +} LibraryForeachIDData; + +static void library_foreach_ID_link(Main *bmain, + ID *id, + LibraryIDLinkCallback callback, + void *user_data, + int flag, + LibraryForeachIDData *inherit_data); + +static void library_foreach_idproperty_ID_link(LibraryForeachIDData *data, + IDProperty *prop, + int flag) +{ + if (!prop) { + return; + } + + switch (prop->type) { + case IDP_GROUP: { + for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) { + library_foreach_idproperty_ID_link(data, loop, flag); + } + break; + } + case IDP_IDPARRAY: { + IDProperty *loop = IDP_Array(prop); + for (int i = 0; i < prop->len; i++) { + library_foreach_idproperty_ID_link(data, &loop[i], flag); + } + break; + } + case IDP_ID: + FOREACH_CALLBACK_INVOKE_ID(data, prop->data.pointer, flag); + break; + default: + break; /* Nothing to do here with other types of IDProperties... */ + } + + FOREACH_FINALIZE_VOID; +} + +static void library_foreach_rigidbodyworldSceneLooper(struct RigidBodyWorld *UNUSED(rbw), + ID **id_pointer, + void *user_data, + int cb_flag) +{ + LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; + FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); + + FOREACH_FINALIZE_VOID; +} + +static void library_foreach_modifiersForeachIDLink(void *user_data, + Object *UNUSED(object), + ID **id_pointer, + int cb_flag) +{ + LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; + FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); + + FOREACH_FINALIZE_VOID; +} + +static void library_foreach_gpencil_modifiersForeachIDLink(void *user_data, + Object *UNUSED(object), + ID **id_pointer, + int cb_flag) +{ + LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; + FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); + + FOREACH_FINALIZE_VOID; +} + +static void library_foreach_shaderfxForeachIDLink(void *user_data, + Object *UNUSED(object), + ID **id_pointer, + int cb_flag) +{ + LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; + FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); + + FOREACH_FINALIZE_VOID; +} + +static void library_foreach_constraintObjectLooper(bConstraint *UNUSED(con), + ID **id_pointer, + bool is_reference, + void *user_data) +{ + LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; + const int cb_flag = is_reference ? IDWALK_CB_USER : IDWALK_CB_NOP; + FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); + + FOREACH_FINALIZE_VOID; +} + +static void library_foreach_particlesystemsObjectLooper(ParticleSystem *UNUSED(psys), + ID **id_pointer, + void *user_data, + int cb_flag) +{ + LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; + FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); + + FOREACH_FINALIZE_VOID; +} + +static void library_foreach_nla_strip(LibraryForeachIDData *data, NlaStrip *strip) +{ + NlaStrip *substrip; + + FOREACH_CALLBACK_INVOKE(data, strip->act, IDWALK_CB_USER); + + for (substrip = strip->strips.first; substrip; substrip = substrip->next) { + library_foreach_nla_strip(data, substrip); + } + + FOREACH_FINALIZE_VOID; +} + +static void library_foreach_animationData(LibraryForeachIDData *data, AnimData *adt) +{ + FCurve *fcu; + NlaTrack *nla_track; + NlaStrip *nla_strip; + + for (fcu = adt->drivers.first; fcu; fcu = fcu->next) { + ChannelDriver *driver = fcu->driver; + DriverVar *dvar; + + for (dvar = driver->variables.first; dvar; dvar = dvar->next) { + /* only used targets */ + DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { + FOREACH_CALLBACK_INVOKE_ID(data, dtar->id, IDWALK_CB_NOP); + } + DRIVER_TARGETS_LOOPER_END; + } + } + + FOREACH_CALLBACK_INVOKE(data, adt->action, IDWALK_CB_USER); + FOREACH_CALLBACK_INVOKE(data, adt->tmpact, IDWALK_CB_USER); + + for (nla_track = adt->nla_tracks.first; nla_track; nla_track = nla_track->next) { + for (nla_strip = nla_track->strips.first; nla_strip; nla_strip = nla_strip->next) { + library_foreach_nla_strip(data, nla_strip); + } + } + + FOREACH_FINALIZE_VOID; +} + +static void library_foreach_mtex(LibraryForeachIDData *data, MTex *mtex) +{ + FOREACH_CALLBACK_INVOKE(data, mtex->object, IDWALK_CB_NOP); + FOREACH_CALLBACK_INVOKE(data, mtex->tex, IDWALK_CB_USER); + + FOREACH_FINALIZE_VOID; +} + +static void library_foreach_paint(LibraryForeachIDData *data, Paint *paint) +{ + FOREACH_CALLBACK_INVOKE(data, paint->brush, IDWALK_CB_USER); + for (int i = 0; i < paint->tool_slots_len; i++) { + FOREACH_CALLBACK_INVOKE(data, paint->tool_slots[i].brush, IDWALK_CB_USER); + } + FOREACH_CALLBACK_INVOKE(data, paint->palette, IDWALK_CB_USER); + + FOREACH_FINALIZE_VOID; +} + +static void library_foreach_bone(LibraryForeachIDData *data, Bone *bone) +{ + library_foreach_idproperty_ID_link(data, bone->prop, IDWALK_CB_USER); + + for (Bone *curbone = bone->childbase.first; curbone; curbone = curbone->next) { + library_foreach_bone(data, curbone); + } + + FOREACH_FINALIZE_VOID; +} + +static void library_foreach_layer_collection(LibraryForeachIDData *data, ListBase *lb) +{ + for (LayerCollection *lc = lb->first; lc; lc = lc->next) { + FOREACH_CALLBACK_INVOKE(data, lc->collection, IDWALK_CB_NOP); + library_foreach_layer_collection(data, &lc->layer_collections); + } + + FOREACH_FINALIZE_VOID; +} + +/* Used by both real Collection data-blocks, and the fake horror of master collection from Scene. + */ +static void library_foreach_collection(LibraryForeachIDData *data, Collection *collection) +{ + for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) { + FOREACH_CALLBACK_INVOKE(data, cob->ob, IDWALK_CB_USER); + } + for (CollectionChild *child = collection->children.first; child; child = child->next) { + FOREACH_CALLBACK_INVOKE(data, child->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_USER); + } + for (CollectionParent *parent = collection->parents.first; parent; parent = parent->next) { + FOREACH_CALLBACK_INVOKE(data, parent->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_LOOPBACK); + } + + FOREACH_FINALIZE_VOID; +} + +static void library_foreach_ID_as_subdata_link(ID **id_pp, + LibraryIDLinkCallback callback, + void *user_data, + int flag, + LibraryForeachIDData *data) +{ + /* Needed e.g. for callbacks handling relationships... This call shall be absolutely readonly. */ + ID *id = *id_pp; + FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pp, IDWALK_CB_PRIVATE); + BLI_assert(id == *id_pp); + + if (flag & IDWALK_RECURSE) { + /* Defer handling into main loop, recursively calling BKE_library_foreach_ID_link in + * IDWALK_RECURSE case is troublesome, see T49553. */ + if (BLI_gset_add(data->ids_handled, id)) { + BLI_LINKSTACK_PUSH(data->ids_todo, id); + } + } + else { + library_foreach_ID_link(NULL, id, callback, user_data, flag, data); + } + + FOREACH_FINALIZE_VOID; +} + +static void library_foreach_ID_link(Main *bmain, + ID *id, + LibraryIDLinkCallback callback, + void *user_data, + int flag, + LibraryForeachIDData *inherit_data) +{ + LibraryForeachIDData data; + int i; + + if (flag & IDWALK_RECURSE) { + /* For now, recursion implies read-only. */ + flag |= IDWALK_READONLY; + + data.ids_handled = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); + BLI_LINKSTACK_INIT(data.ids_todo); + + BLI_gset_add(data.ids_handled, id); + } + else { + data.ids_handled = NULL; + } + data.flag = flag; + data.status = 0; + data.callback = callback; + data.user_data = user_data; + +#define CALLBACK_INVOKE_ID(check_id, cb_flag) FOREACH_CALLBACK_INVOKE_ID(&data, check_id, cb_flag) + +#define CALLBACK_INVOKE(check_id_super, cb_flag) \ + FOREACH_CALLBACK_INVOKE(&data, check_id_super, cb_flag) + + for (; id != NULL; id = (flag & IDWALK_RECURSE) ? BLI_LINKSTACK_POP(data.ids_todo) : NULL) { + data.self_id = id; + + /* inherit_data is non-NULL when this function is called for some sub-data ID + * (like root nodetree of a material). + * In that case, we do not want to generate those 'generic flags' from our current sub-data ID + * (the node tree), but re-use those generated for the 'owner' ID (the material). */ + if (inherit_data == NULL) { + data.cb_flag = ID_IS_LINKED(id) ? IDWALK_CB_INDIRECT_USAGE : 0; + /* When an ID is not in Main database, it should never refcount IDs it is using. + * Exceptions: NodeTrees (yeeahhh!) directly used by Materials. */ + data.cb_flag_clear = (id->tag & LIB_TAG_NO_MAIN) ? IDWALK_CB_USER | IDWALK_CB_USER_ONE : 0; + } + else { + data.cb_flag = inherit_data->cb_flag; + data.cb_flag_clear = inherit_data->cb_flag_clear; + } + + if (bmain != NULL && bmain->relations != NULL && (flag & IDWALK_READONLY)) { + /* Note that this is minor optimization, even in worst cases (like id being an object with + * lots of drivers and constraints and modifiers, or material etc. with huge node tree), + * but we might as well use it (Main->relations is always assumed valid, + * it's responsibility of code creating it to free it, + * especially if/when it starts modifying Main database). */ + MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->id_user_to_used, id); + for (; entry != NULL; entry = entry->next) { + FOREACH_CALLBACK_INVOKE_ID_PP(&data, entry->id_pointer, entry->usage_flag); + } + continue; + } + + if (id->override_library != NULL) { + CALLBACK_INVOKE_ID(id->override_library->reference, + IDWALK_CB_USER | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE); + CALLBACK_INVOKE_ID(id->override_library->storage, + IDWALK_CB_USER | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE); + } + + library_foreach_idproperty_ID_link(&data, id->properties, IDWALK_CB_USER); + + AnimData *adt = BKE_animdata_from_id(id); + if (adt) { + library_foreach_animationData(&data, adt); + } + + switch ((ID_Type)GS(id->name)) { + case ID_LI: { + Library *lib = (Library *)id; + CALLBACK_INVOKE(lib->parent, IDWALK_CB_NEVER_SELF); + break; + } + case ID_SCE: { + Scene *scene = (Scene *)id; + ToolSettings *toolsett = scene->toolsettings; + + CALLBACK_INVOKE(scene->camera, IDWALK_CB_NOP); + CALLBACK_INVOKE(scene->world, IDWALK_CB_USER); + CALLBACK_INVOKE(scene->set, IDWALK_CB_NEVER_SELF); + CALLBACK_INVOKE(scene->clip, IDWALK_CB_USER); + CALLBACK_INVOKE(scene->gpd, IDWALK_CB_USER); + CALLBACK_INVOKE(scene->r.bake.cage_object, IDWALK_CB_NOP); + if (scene->nodetree) { + /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ + library_foreach_ID_as_subdata_link( + (ID **)&scene->nodetree, callback, user_data, flag, &data); + } + if (scene->ed) { + Sequence *seq; + SEQP_BEGIN (scene->ed, seq) { + CALLBACK_INVOKE(seq->scene, IDWALK_CB_NEVER_SELF); + CALLBACK_INVOKE(seq->scene_camera, IDWALK_CB_NOP); + CALLBACK_INVOKE(seq->clip, IDWALK_CB_USER); + CALLBACK_INVOKE(seq->mask, IDWALK_CB_USER); + CALLBACK_INVOKE(seq->sound, IDWALK_CB_USER); + library_foreach_idproperty_ID_link(&data, seq->prop, IDWALK_CB_USER); + for (SequenceModifierData *smd = seq->modifiers.first; smd; smd = smd->next) { + CALLBACK_INVOKE(smd->mask_id, IDWALK_CB_USER); + } + + if (seq->type == SEQ_TYPE_TEXT && seq->effectdata) { + TextVars *text_data = seq->effectdata; + CALLBACK_INVOKE(text_data->text_font, IDWALK_CB_USER); + } + } + SEQ_END; + } + + library_foreach_collection(&data, scene->master_collection); + + ViewLayer *view_layer; + for (view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) { + CALLBACK_INVOKE(view_layer->mat_override, IDWALK_CB_USER); + + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + CALLBACK_INVOKE(base->object, IDWALK_CB_NOP); + } + + library_foreach_layer_collection(&data, &view_layer->layer_collections); + + for (FreestyleModuleConfig *fmc = view_layer->freestyle_config.modules.first; fmc; + fmc = fmc->next) { + if (fmc->script) { + CALLBACK_INVOKE(fmc->script, IDWALK_CB_NOP); + } + } + + for (FreestyleLineSet *fls = view_layer->freestyle_config.linesets.first; fls; + fls = fls->next) { + if (fls->group) { + CALLBACK_INVOKE(fls->group, IDWALK_CB_USER); + } + + if (fls->linestyle) { + CALLBACK_INVOKE(fls->linestyle, IDWALK_CB_USER); + } + } + } + + for (TimeMarker *marker = scene->markers.first; marker; marker = marker->next) { + CALLBACK_INVOKE(marker->camera, IDWALK_CB_NOP); + } + + if (toolsett) { + CALLBACK_INVOKE(toolsett->particle.scene, IDWALK_CB_NOP); + CALLBACK_INVOKE(toolsett->particle.object, IDWALK_CB_NOP); + CALLBACK_INVOKE(toolsett->particle.shape_object, IDWALK_CB_NOP); + + library_foreach_paint(&data, &toolsett->imapaint.paint); + CALLBACK_INVOKE(toolsett->imapaint.stencil, IDWALK_CB_USER); + CALLBACK_INVOKE(toolsett->imapaint.clone, IDWALK_CB_USER); + CALLBACK_INVOKE(toolsett->imapaint.canvas, IDWALK_CB_USER); + + if (toolsett->vpaint) { + library_foreach_paint(&data, &toolsett->vpaint->paint); + } + if (toolsett->wpaint) { + library_foreach_paint(&data, &toolsett->wpaint->paint); + } + if (toolsett->sculpt) { + library_foreach_paint(&data, &toolsett->sculpt->paint); + CALLBACK_INVOKE(toolsett->sculpt->gravity_object, IDWALK_CB_NOP); + } + if (toolsett->uvsculpt) { + library_foreach_paint(&data, &toolsett->uvsculpt->paint); + } + if (toolsett->gp_paint) { + library_foreach_paint(&data, &toolsett->gp_paint->paint); + } + + CALLBACK_INVOKE(toolsett->gp_sculpt.guide.reference_object, IDWALK_CB_NOP); + } + + if (scene->rigidbody_world) { + BKE_rigidbody_world_id_loop( + scene->rigidbody_world, library_foreach_rigidbodyworldSceneLooper, &data); + } + + break; + } + + case ID_OB: { + Object *object = (Object *)id; + ParticleSystem *psys; + + /* Object is special, proxies make things hard... */ + const int data_cb_flag = data.cb_flag; + const int proxy_cb_flag = ((data.flag & IDWALK_NO_INDIRECT_PROXY_DATA_USAGE) == 0 && + (object->proxy || object->proxy_group)) ? + IDWALK_CB_INDIRECT_USAGE : + 0; + + /* object data special case */ + data.cb_flag |= proxy_cb_flag; + if (object->type == OB_EMPTY) { + /* empty can have NULL or Image */ + CALLBACK_INVOKE_ID(object->data, IDWALK_CB_USER); + } + else { + /* when set, this can't be NULL */ + if (object->data) { + CALLBACK_INVOKE_ID(object->data, IDWALK_CB_USER | IDWALK_CB_NEVER_NULL); + } + } + data.cb_flag = data_cb_flag; + + CALLBACK_INVOKE(object->parent, IDWALK_CB_NEVER_SELF); + CALLBACK_INVOKE(object->track, IDWALK_CB_NEVER_SELF); + /* object->proxy is refcounted, but not object->proxy_group... *sigh* */ + CALLBACK_INVOKE(object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF); + CALLBACK_INVOKE(object->proxy_group, IDWALK_CB_NOP); + + /* Special case! + * Since this field is set/owned by 'user' of this ID (and not ID itself), + * it is only indirect usage if proxy object is linked... Twisted. */ + if (object->proxy_from) { + data.cb_flag = ID_IS_LINKED(object->proxy_from) ? IDWALK_CB_INDIRECT_USAGE : 0; + } + CALLBACK_INVOKE(object->proxy_from, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF); + data.cb_flag = data_cb_flag; + + CALLBACK_INVOKE(object->poselib, IDWALK_CB_USER); + + data.cb_flag |= proxy_cb_flag; + for (i = 0; i < object->totcol; i++) { + CALLBACK_INVOKE(object->mat[i], IDWALK_CB_USER); + } + data.cb_flag = data_cb_flag; + + /* Note that ob->gpd is deprecated, so no need to handle it here. */ + CALLBACK_INVOKE(object->instance_collection, IDWALK_CB_USER); + + if (object->pd) { + CALLBACK_INVOKE(object->pd->tex, IDWALK_CB_USER); + CALLBACK_INVOKE(object->pd->f_source, IDWALK_CB_NOP); + } + /* Note that ob->effect is deprecated, so no need to handle it here. */ + + if (object->pose) { + bPoseChannel *pchan; + + data.cb_flag |= proxy_cb_flag; + for (pchan = object->pose->chanbase.first; pchan; pchan = pchan->next) { + library_foreach_idproperty_ID_link(&data, pchan->prop, IDWALK_CB_USER); + CALLBACK_INVOKE(pchan->custom, IDWALK_CB_USER); + BKE_constraints_id_loop( + &pchan->constraints, library_foreach_constraintObjectLooper, &data); + } + data.cb_flag = data_cb_flag; + } + + if (object->rigidbody_constraint) { + CALLBACK_INVOKE(object->rigidbody_constraint->ob1, IDWALK_CB_NEVER_SELF); + CALLBACK_INVOKE(object->rigidbody_constraint->ob2, IDWALK_CB_NEVER_SELF); + } + + if (object->lodlevels.first) { + LodLevel *level; + for (level = object->lodlevels.first; level; level = level->next) { + CALLBACK_INVOKE(level->source, IDWALK_CB_NEVER_SELF); + } + } + + modifiers_foreachIDLink(object, library_foreach_modifiersForeachIDLink, &data); + BKE_gpencil_modifiers_foreachIDLink( + object, library_foreach_gpencil_modifiersForeachIDLink, &data); + BKE_constraints_id_loop( + &object->constraints, library_foreach_constraintObjectLooper, &data); + BKE_shaderfx_foreachIDLink(object, library_foreach_shaderfxForeachIDLink, &data); + + for (psys = object->particlesystem.first; psys; psys = psys->next) { + BKE_particlesystem_id_loop(psys, library_foreach_particlesystemsObjectLooper, &data); + } + + if (object->soft) { + CALLBACK_INVOKE(object->soft->collision_group, IDWALK_CB_NOP); + + if (object->soft->effector_weights) { + CALLBACK_INVOKE(object->soft->effector_weights->group, IDWALK_CB_NOP); + } + } + break; + } + + case ID_AR: { + bArmature *arm = (bArmature *)id; + + for (Bone *bone = arm->bonebase.first; bone; bone = bone->next) { + library_foreach_bone(&data, bone); + } + break; + } + + case ID_ME: { + Mesh *mesh = (Mesh *)id; + CALLBACK_INVOKE(mesh->texcomesh, IDWALK_CB_NEVER_SELF); + CALLBACK_INVOKE(mesh->key, IDWALK_CB_USER); + for (i = 0; i < mesh->totcol; i++) { + CALLBACK_INVOKE(mesh->mat[i], IDWALK_CB_USER); + } + break; + } + + case ID_CU: { + Curve *curve = (Curve *)id; + CALLBACK_INVOKE(curve->bevobj, IDWALK_CB_NOP); + CALLBACK_INVOKE(curve->taperobj, IDWALK_CB_NOP); + CALLBACK_INVOKE(curve->textoncurve, IDWALK_CB_NOP); + CALLBACK_INVOKE(curve->key, IDWALK_CB_USER); + for (i = 0; i < curve->totcol; i++) { + CALLBACK_INVOKE(curve->mat[i], IDWALK_CB_USER); + } + CALLBACK_INVOKE(curve->vfont, IDWALK_CB_USER); + CALLBACK_INVOKE(curve->vfontb, IDWALK_CB_USER); + CALLBACK_INVOKE(curve->vfonti, IDWALK_CB_USER); + CALLBACK_INVOKE(curve->vfontbi, IDWALK_CB_USER); + break; + } + + case ID_MB: { + MetaBall *metaball = (MetaBall *)id; + for (i = 0; i < metaball->totcol; i++) { + CALLBACK_INVOKE(metaball->mat[i], IDWALK_CB_USER); + } + break; + } + + case ID_MA: { + Material *material = (Material *)id; + if (material->nodetree) { + /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ + library_foreach_ID_as_subdata_link( + (ID **)&material->nodetree, callback, user_data, flag, &data); + } + if (material->texpaintslot != NULL) { + CALLBACK_INVOKE(material->texpaintslot->ima, IDWALK_CB_NOP); + } + if (material->gp_style != NULL) { + CALLBACK_INVOKE(material->gp_style->sima, IDWALK_CB_USER); + CALLBACK_INVOKE(material->gp_style->ima, IDWALK_CB_USER); + } + break; + } + + case ID_TE: { + Tex *texture = (Tex *)id; + if (texture->nodetree) { + /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ + library_foreach_ID_as_subdata_link( + (ID **)&texture->nodetree, callback, user_data, flag, &data); + } + CALLBACK_INVOKE(texture->ima, IDWALK_CB_USER); + break; + } + + case ID_LT: { + Lattice *lattice = (Lattice *)id; + CALLBACK_INVOKE(lattice->key, IDWALK_CB_USER); + break; + } + + case ID_LA: { + Light *lamp = (Light *)id; + if (lamp->nodetree) { + /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ + library_foreach_ID_as_subdata_link( + (ID **)&lamp->nodetree, callback, user_data, flag, &data); + } + break; + } + + case ID_CA: { + Camera *camera = (Camera *)id; + CALLBACK_INVOKE(camera->dof.focus_object, IDWALK_CB_NOP); + for (CameraBGImage *bgpic = camera->bg_images.first; bgpic; bgpic = bgpic->next) { + if (bgpic->source == CAM_BGIMG_SOURCE_IMAGE) { + CALLBACK_INVOKE(bgpic->ima, IDWALK_CB_USER); + } + else if (bgpic->source == CAM_BGIMG_SOURCE_MOVIE) { + CALLBACK_INVOKE(bgpic->clip, IDWALK_CB_USER); + } + } + + break; + } + + case ID_KE: { + Key *key = (Key *)id; + CALLBACK_INVOKE_ID(key->from, IDWALK_CB_LOOPBACK); + break; + } + + case ID_WO: { + World *world = (World *)id; + if (world->nodetree) { + /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ + library_foreach_ID_as_subdata_link( + (ID **)&world->nodetree, callback, user_data, flag, &data); + } + break; + } + + case ID_SPK: { + Speaker *speaker = (Speaker *)id; + CALLBACK_INVOKE(speaker->sound, IDWALK_CB_USER); + break; + } + + case ID_LP: { + LightProbe *probe = (LightProbe *)id; + CALLBACK_INVOKE(probe->image, IDWALK_CB_USER); + CALLBACK_INVOKE(probe->visibility_grp, IDWALK_CB_NOP); + break; + } + + case ID_GR: { + Collection *collection = (Collection *)id; + library_foreach_collection(&data, collection); + break; + } + + case ID_NT: { + bNodeTree *ntree = (bNodeTree *)id; + bNode *node; + bNodeSocket *sock; + + CALLBACK_INVOKE(ntree->gpd, IDWALK_CB_USER); + + for (node = ntree->nodes.first; node; node = node->next) { + CALLBACK_INVOKE_ID(node->id, IDWALK_CB_USER); + + library_foreach_idproperty_ID_link(&data, node->prop, IDWALK_CB_USER); + for (sock = node->inputs.first; sock; sock = sock->next) { + library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER); + } + for (sock = node->outputs.first; sock; sock = sock->next) { + library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER); + } + } + + for (sock = ntree->inputs.first; sock; sock = sock->next) { + library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER); + } + for (sock = ntree->outputs.first; sock; sock = sock->next) { + library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER); + } + break; + } + + case ID_BR: { + Brush *brush = (Brush *)id; + CALLBACK_INVOKE(brush->toggle_brush, IDWALK_CB_NOP); + CALLBACK_INVOKE(brush->clone.image, IDWALK_CB_NOP); + CALLBACK_INVOKE(brush->paint_curve, IDWALK_CB_USER); + if (brush->gpencil_settings) { + CALLBACK_INVOKE(brush->gpencil_settings->material, IDWALK_CB_USER); + } + library_foreach_mtex(&data, &brush->mtex); + library_foreach_mtex(&data, &brush->mask_mtex); + break; + } + + case ID_PA: { + ParticleSettings *psett = (ParticleSettings *)id; + CALLBACK_INVOKE(psett->instance_collection, IDWALK_CB_USER); + CALLBACK_INVOKE(psett->instance_object, IDWALK_CB_NOP); + CALLBACK_INVOKE(psett->bb_ob, IDWALK_CB_NOP); + CALLBACK_INVOKE(psett->collision_group, IDWALK_CB_NOP); + + for (i = 0; i < MAX_MTEX; i++) { + if (psett->mtex[i]) { + library_foreach_mtex(&data, psett->mtex[i]); + } + } + + if (psett->effector_weights) { + CALLBACK_INVOKE(psett->effector_weights->group, IDWALK_CB_NOP); + } + + if (psett->pd) { + CALLBACK_INVOKE(psett->pd->tex, IDWALK_CB_USER); + CALLBACK_INVOKE(psett->pd->f_source, IDWALK_CB_NOP); + } + if (psett->pd2) { + CALLBACK_INVOKE(psett->pd2->tex, IDWALK_CB_USER); + CALLBACK_INVOKE(psett->pd2->f_source, IDWALK_CB_NOP); + } + + if (psett->boids) { + BoidState *state; + BoidRule *rule; + + for (state = psett->boids->states.first; state; state = state->next) { + for (rule = state->rules.first; rule; rule = rule->next) { + if (rule->type == eBoidRuleType_Avoid) { + BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid *)rule; + CALLBACK_INVOKE(gabr->ob, IDWALK_CB_NOP); + } + else if (rule->type == eBoidRuleType_FollowLeader) { + BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader *)rule; + CALLBACK_INVOKE(flbr->ob, IDWALK_CB_NOP); + } + } + } + } + + for (ParticleDupliWeight *dw = psett->instance_weights.first; dw; dw = dw->next) { + CALLBACK_INVOKE(dw->ob, IDWALK_CB_NOP); + } + break; + } + + case ID_MC: { + MovieClip *clip = (MovieClip *)id; + MovieTracking *tracking = &clip->tracking; + MovieTrackingObject *object; + MovieTrackingTrack *track; + MovieTrackingPlaneTrack *plane_track; + + CALLBACK_INVOKE(clip->gpd, IDWALK_CB_USER); + + for (track = tracking->tracks.first; track; track = track->next) { + CALLBACK_INVOKE(track->gpd, IDWALK_CB_USER); + } + for (object = tracking->objects.first; object; object = object->next) { + for (track = object->tracks.first; track; track = track->next) { + CALLBACK_INVOKE(track->gpd, IDWALK_CB_USER); + } + } + + for (plane_track = tracking->plane_tracks.first; plane_track; + plane_track = plane_track->next) { + CALLBACK_INVOKE(plane_track->image, IDWALK_CB_USER); + } + break; + } + + case ID_MSK: { + Mask *mask = (Mask *)id; + MaskLayer *mask_layer; + for (mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { + MaskSpline *mask_spline; + + for (mask_spline = mask_layer->splines.first; mask_spline; + mask_spline = mask_spline->next) { + for (i = 0; i < mask_spline->tot_point; i++) { + MaskSplinePoint *point = &mask_spline->points[i]; + CALLBACK_INVOKE_ID(point->parent.id, IDWALK_CB_USER); + } + } + } + break; + } + + case ID_LS: { + FreestyleLineStyle *linestyle = (FreestyleLineStyle *)id; + LineStyleModifier *lsm; + for (i = 0; i < MAX_MTEX; i++) { + if (linestyle->mtex[i]) { + library_foreach_mtex(&data, linestyle->mtex[i]); + } + } + if (linestyle->nodetree) { + /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ + library_foreach_ID_as_subdata_link( + (ID **)&linestyle->nodetree, callback, user_data, flag, &data); + } + + for (lsm = linestyle->color_modifiers.first; lsm; lsm = lsm->next) { + if (lsm->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { + LineStyleColorModifier_DistanceFromObject *p = + (LineStyleColorModifier_DistanceFromObject *)lsm; + if (p->target) { + CALLBACK_INVOKE(p->target, IDWALK_CB_NOP); + } + } + } + for (lsm = linestyle->alpha_modifiers.first; lsm; lsm = lsm->next) { + if (lsm->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { + LineStyleAlphaModifier_DistanceFromObject *p = + (LineStyleAlphaModifier_DistanceFromObject *)lsm; + if (p->target) { + CALLBACK_INVOKE(p->target, IDWALK_CB_NOP); + } + } + } + for (lsm = linestyle->thickness_modifiers.first; lsm; lsm = lsm->next) { + if (lsm->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { + LineStyleThicknessModifier_DistanceFromObject *p = + (LineStyleThicknessModifier_DistanceFromObject *)lsm; + if (p->target) { + CALLBACK_INVOKE(p->target, IDWALK_CB_NOP); + } + } + } + break; + } + case ID_AC: { + bAction *act = (bAction *)id; + + for (TimeMarker *marker = act->markers.first; marker; marker = marker->next) { + CALLBACK_INVOKE(marker->camera, IDWALK_CB_NOP); + } + break; + } + + case ID_WM: { + wmWindowManager *wm = (wmWindowManager *)id; + + for (wmWindow *win = wm->windows.first; win; win = win->next) { + ID *workspace = (ID *)BKE_workspace_active_get(win->workspace_hook); + + CALLBACK_INVOKE(win->scene, IDWALK_CB_USER_ONE); + + CALLBACK_INVOKE_ID(workspace, IDWALK_CB_NOP); + /* allow callback to set a different workspace */ + BKE_workspace_active_set(win->workspace_hook, (WorkSpace *)workspace); + } + break; + } + + case ID_WS: { + WorkSpace *workspace = (WorkSpace *)id; + ListBase *layouts = BKE_workspace_layouts_get(workspace); + + for (WorkSpaceLayout *layout = layouts->first; layout; layout = layout->next) { + bScreen *screen = BKE_workspace_layout_screen_get(layout); + + /* CALLBACK_INVOKE expects an actual pointer, not a variable holding the pointer. + * However we can't access layout->screen here + * since we are outside the workspace project. */ + CALLBACK_INVOKE(screen, IDWALK_CB_USER); + /* allow callback to set a different screen */ + BKE_workspace_layout_screen_set(layout, screen); + } + break; + } + case ID_GD: { + bGPdata *gpencil = (bGPdata *)id; + /* materials */ + for (i = 0; i < gpencil->totcol; i++) { + CALLBACK_INVOKE(gpencil->mat[i], IDWALK_CB_USER); + } + + for (bGPDlayer *gplayer = gpencil->layers.first; gplayer != NULL; + gplayer = gplayer->next) { + CALLBACK_INVOKE(gplayer->parent, IDWALK_CB_NOP); + } + + break; + } + + /* Nothing needed for those... */ + case ID_SCR: + case ID_IM: + case ID_VF: + case ID_TXT: + case ID_SO: + case ID_PAL: + case ID_PC: + case ID_CF: + break; + + /* Deprecated. */ + case ID_IP: + break; + } + } + +FOREACH_FINALIZE: + if (data.ids_handled) { + BLI_gset_free(data.ids_handled, NULL); + BLI_LINKSTACK_FREE(data.ids_todo); + } + +#undef CALLBACK_INVOKE_ID +#undef CALLBACK_INVOKE +} + +#undef FOREACH_CALLBACK_INVOKE_ID +#undef FOREACH_CALLBACK_INVOKE + +/** + * Loop over all of the ID's this data-block links to. + */ +void BKE_library_foreach_ID_link( + Main *bmain, ID *id, LibraryIDLinkCallback callback, void *user_data, int flag) +{ + library_foreach_ID_link(bmain, id, callback, user_data, flag, NULL); +} + +/** + * re-usable function, use when replacing ID's + */ +void BKE_library_update_ID_link_user(ID *id_dst, ID *id_src, const int cb_flag) +{ + if (cb_flag & IDWALK_CB_USER) { + id_us_min(id_src); + id_us_plus(id_dst); + } + else if (cb_flag & IDWALK_CB_USER_ONE) { + id_us_ensure_real(id_dst); + } +} + +/** + * Say whether given \a id_type_owner can use (in any way) a data-block of \a id_type_used. + * + * This is a 'simplified' abstract version of #BKE_library_foreach_ID_link() above, + * quite useful to reduce* useless iterations in some cases. + */ +/* XXX This has to be fully rethink, basing check on ID type is not really working anymore + * (and even worth once IDProps will support ID pointers), + * we'll have to do some quick checks on IDs themselves... */ +bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) +{ + /* any type of ID can be used in custom props. */ + if (id_owner->properties) { + return true; + } + + const short id_type_owner = GS(id_owner->name); + + /* IDProps of armature bones and nodes, and bNode->id can use virtually any type of ID. */ + if (ELEM(id_type_owner, ID_NT, ID_AR)) { + return true; + } + + if (ntreeFromID(id_owner)) { + return true; + } + + if (BKE_animdata_from_id(id_owner)) { + /* AnimationData can use virtually any kind of data-blocks, through drivers especially. */ + return true; + } + + switch ((ID_Type)id_type_owner) { + case ID_LI: + return ELEM(id_type_used, ID_LI); + case ID_SCE: + return (ELEM(id_type_used, + ID_OB, + ID_WO, + ID_SCE, + ID_MC, + ID_MA, + ID_GR, + ID_TXT, + ID_LS, + ID_MSK, + ID_SO, + ID_GD, + ID_BR, + ID_PAL, + ID_IM, + ID_NT)); + case ID_OB: + /* Could be more specific, but simpler to just always say 'yes' here. */ + return true; + case ID_ME: + return ELEM(id_type_used, ID_ME, ID_KE, ID_MA, ID_IM); + case ID_CU: + return ELEM(id_type_used, ID_OB, ID_KE, ID_MA, ID_VF); + case ID_MB: + return ELEM(id_type_used, ID_MA); + case ID_MA: + return (ELEM(id_type_used, ID_TE, ID_GR)); + case ID_TE: + return (ELEM(id_type_used, ID_IM, ID_OB)); + case ID_LT: + return ELEM(id_type_used, ID_KE); + case ID_LA: + return (ELEM(id_type_used, ID_TE)); + case ID_CA: + return ELEM(id_type_used, ID_OB); + case ID_KE: + /* Warning! key->from, could be more types in future? */ + return ELEM(id_type_used, ID_ME, ID_CU, ID_LT); + case ID_SCR: + return ELEM(id_type_used, ID_SCE); + case ID_WO: + return (ELEM(id_type_used, ID_TE)); + case ID_SPK: + return ELEM(id_type_used, ID_SO); + case ID_GR: + return ELEM(id_type_used, ID_OB, ID_GR); + case ID_NT: + /* Could be more specific, but node.id has no type restriction... */ + return true; + case ID_BR: + return ELEM(id_type_used, ID_BR, ID_IM, ID_PC, ID_TE, ID_MA); + case ID_PA: + return ELEM(id_type_used, ID_OB, ID_GR, ID_TE); + case ID_MC: + return ELEM(id_type_used, ID_GD, ID_IM); + case ID_MSK: + /* WARNING! mask->parent.id, not typed. */ + return ELEM(id_type_used, ID_MC); + case ID_LS: + return (ELEM(id_type_used, ID_TE, ID_OB)); + case ID_LP: + return ELEM(id_type_used, ID_IM); + case ID_GD: + return ELEM(id_type_used, ID_MA); + case ID_WS: + return ELEM(id_type_used, ID_SCR, ID_SCE); + case ID_IM: + case ID_VF: + case ID_TXT: + case ID_SO: + case ID_AR: + case ID_AC: + case ID_WM: + case ID_PAL: + case ID_PC: + case ID_CF: + /* Those types never use/reference other IDs... */ + return false; + case ID_IP: + /* Deprecated... */ + return false; + } + return false; +} + +/* ***** ID users iterator. ***** */ +typedef struct IDUsersIter { + ID *id; + + ListBase *lb_array[MAX_LIBARRAY]; + int lb_idx; + + ID *curr_id; + int count_direct, count_indirect; /* Set by callback. */ +} IDUsersIter; + +static int foreach_libblock_id_users_callback(void *user_data, + ID *UNUSED(self_id), + ID **id_p, + int cb_flag) +{ + IDUsersIter *iter = user_data; + + if (*id_p) { + /* 'Loopback' ID pointers (the ugly 'from' ones, Object->proxy_from and Key->from). + * Those are not actually ID usage, we can ignore them here. + */ + if (cb_flag & IDWALK_CB_LOOPBACK) { + return IDWALK_RET_NOP; + } + + if (*id_p == iter->id) { +#if 0 + printf( + "%s uses %s (refcounted: %d, userone: %d, used_one: %d, used_one_active: %d, " + "indirect_usage: %d)\n", + iter->curr_id->name, + iter->id->name, + (cb_flag & IDWALK_USER) ? 1 : 0, + (cb_flag & IDWALK_USER_ONE) ? 1 : 0, + (iter->id->tag & LIB_TAG_EXTRAUSER) ? 1 : 0, + (iter->id->tag & LIB_TAG_EXTRAUSER_SET) ? 1 : 0, + (cb_flag & IDWALK_INDIRECT_USAGE) ? 1 : 0); +#endif + if (cb_flag & IDWALK_CB_INDIRECT_USAGE) { + iter->count_indirect++; + } + else { + iter->count_direct++; + } + } + } + + return IDWALK_RET_NOP; +} + +/** + * Return the number of times given \a id_user uses/references \a id_used. + * + * \note This only checks for pointer references of an ID, shallow usages + * (like e.g. by RNA paths, as done for FCurves) are not detected at all. + * + * \param id_user: the ID which is supposed to use (reference) \a id_used. + * \param id_used: the ID which is supposed to be used (referenced) by \a id_user. + * \return the number of direct usages/references of \a id_used by \a id_user. + */ +int BKE_library_ID_use_ID(ID *id_user, ID *id_used) +{ + IDUsersIter iter; + + /* We do not care about iter.lb_array/lb_idx here... */ + iter.id = id_used; + iter.curr_id = id_user; + iter.count_direct = iter.count_indirect = 0; + + BKE_library_foreach_ID_link( + NULL, iter.curr_id, foreach_libblock_id_users_callback, (void *)&iter, IDWALK_READONLY); + + return iter.count_direct + iter.count_indirect; +} + +static bool library_ID_is_used(Main *bmain, void *idv, const bool check_linked) +{ + IDUsersIter iter; + ListBase *lb_array[MAX_LIBARRAY]; + ID *id = idv; + int i = set_listbasepointers(bmain, lb_array); + bool is_defined = false; + + iter.id = id; + iter.count_direct = iter.count_indirect = 0; + while (i-- && !is_defined) { + ID *id_curr = lb_array[i]->first; + + if (!id_curr || !BKE_library_id_can_use_idtype(id_curr, GS(id->name))) { + continue; + } + + for (; id_curr && !is_defined; id_curr = id_curr->next) { + if (id_curr == id) { + /* We are not interested in self-usages (mostly from drivers or bone constraints...). */ + continue; + } + iter.curr_id = id_curr; + BKE_library_foreach_ID_link( + bmain, id_curr, foreach_libblock_id_users_callback, &iter, IDWALK_READONLY); + + is_defined = ((check_linked ? iter.count_indirect : iter.count_direct) != 0); + } + } + + return is_defined; +} + +/** + * Check whether given ID is used locally (i.e. by another non-linked ID). + */ +bool BKE_library_ID_is_locally_used(Main *bmain, void *idv) +{ + return library_ID_is_used(bmain, idv, false); +} + +/** + * Check whether given ID is used indirectly (i.e. by another linked ID). + */ +bool BKE_library_ID_is_indirectly_used(Main *bmain, void *idv) +{ + return library_ID_is_used(bmain, idv, true); +} + +/** + * Combine #BKE_library_ID_is_locally_used() and #BKE_library_ID_is_indirectly_used() + * in a single call. + */ +void BKE_library_ID_test_usages(Main *bmain, void *idv, bool *is_used_local, bool *is_used_linked) +{ + IDUsersIter iter; + ListBase *lb_array[MAX_LIBARRAY]; + ID *id = idv; + int i = set_listbasepointers(bmain, lb_array); + bool is_defined = false; + + iter.id = id; + iter.count_direct = iter.count_indirect = 0; + while (i-- && !is_defined) { + ID *id_curr = lb_array[i]->first; + + if (!id_curr || !BKE_library_id_can_use_idtype(id_curr, GS(id->name))) { + continue; + } + + for (; id_curr && !is_defined; id_curr = id_curr->next) { + if (id_curr == id) { + /* We are not interested in self-usages (mostly from drivers or bone constraints...). */ + continue; + } + iter.curr_id = id_curr; + BKE_library_foreach_ID_link( + bmain, id_curr, foreach_libblock_id_users_callback, &iter, IDWALK_READONLY); + + is_defined = (iter.count_direct != 0 && iter.count_indirect != 0); + } + } + + *is_used_local = (iter.count_direct != 0); + *is_used_linked = (iter.count_indirect != 0); +} + +/* ***** IDs usages.checking/tagging. ***** */ +static int foreach_libblock_used_linked_data_tag_clear_cb(void *user_data, + ID *self_id, + ID **id_p, + int cb_flag) +{ + bool *is_changed = user_data; + + if (*id_p) { + /* The infamous 'from' pointers (Key.from, Object.proxy_from, ...). + * those are not actually ID usage, so we ignore them here. */ + if (cb_flag & IDWALK_CB_LOOPBACK) { + return IDWALK_RET_NOP; + } + + /* If checked id is used by an assumed used ID, + * then it is also used and not part of any linked archipelago. */ + if (!(self_id->tag & LIB_TAG_DOIT) && ((*id_p)->tag & LIB_TAG_DOIT)) { + (*id_p)->tag &= ~LIB_TAG_DOIT; + *is_changed = true; + } + } + + return IDWALK_RET_NOP; +} + +/** + * Detect orphaned linked data blocks (i.e. linked data not used (directly or indirectly) + * in any way by any local data), including complex cases like 'linked archipelagoes', i.e. + * linked data-blocks that use each other in loops, + * which prevents their deletion by 'basic' usage checks. + * + * \param do_init_tag: if \a true, all linked data are checked, if \a false, + * only linked data-blocks already tagged with #LIB_TAG_DOIT are checked. + */ +void BKE_library_unused_linked_data_set_tag(Main *bmain, const bool do_init_tag) +{ + ID *id; + + if (do_init_tag) { + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (id->lib && (id->tag & LIB_TAG_INDIRECT) != 0) { + id->tag |= LIB_TAG_DOIT; + } + else { + id->tag &= ~LIB_TAG_DOIT; + } + } + FOREACH_MAIN_ID_END; + } + + for (bool do_loop = true; do_loop;) { + do_loop = false; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + /* We only want to check that ID if it is currently known as used... */ + if ((id->tag & LIB_TAG_DOIT) == 0) { + BKE_library_foreach_ID_link( + bmain, id, foreach_libblock_used_linked_data_tag_clear_cb, &do_loop, IDWALK_READONLY); + } + } + FOREACH_MAIN_ID_END; + } +} + +/** + * Untag linked data blocks used by other untagged linked data-blocks. + * Used to detect data-blocks that we can forcefully make local + * (instead of copying them to later get rid of original): + * All data-blocks we want to make local are tagged by caller, + * after this function has ran caller knows data-blocks still tagged can directly be made local, + * since they are only used by other data-blocks that will also be made fully local. + */ +void BKE_library_indirectly_used_data_tag_clear(Main *bmain) +{ + ListBase *lb_array[MAX_LIBARRAY]; + + bool do_loop = true; + while (do_loop) { + int i = set_listbasepointers(bmain, lb_array); + do_loop = false; + + while (i--) { + for (ID *id = lb_array[i]->first; id; id = id->next) { + if (id->lib == NULL || id->tag & LIB_TAG_DOIT) { + /* Local or non-indirectly-used ID (so far), no need to check it further. */ + continue; + } + BKE_library_foreach_ID_link( + bmain, id, foreach_libblock_used_linked_data_tag_clear_cb, &do_loop, IDWALK_READONLY); + } + } + } +} diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c new file mode 100644 index 00000000000..11be165a772 --- /dev/null +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -0,0 +1,1184 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bke + * + * Contains management of ID's and libraries remap, unlink and free logic. + */ + +#include +#include +#include +#include +#include +#include + +#include "CLG_log.h" + +#include "MEM_guardedalloc.h" + +/* all types are needed here, in order to do memory operations */ +#include "DNA_anim_types.h" +#include "DNA_armature_types.h" +#include "DNA_brush_types.h" +#include "DNA_camera_types.h" +#include "DNA_cachefile_types.h" +#include "DNA_collection_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_ipo_types.h" +#include "DNA_key_types.h" +#include "DNA_light_types.h" +#include "DNA_lattice_types.h" +#include "DNA_linestyle_types.h" +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meta_types.h" +#include "DNA_movieclip_types.h" +#include "DNA_mask_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_lightprobe_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_speaker_types.h" +#include "DNA_sound_types.h" +#include "DNA_text_types.h" +#include "DNA_vfont_types.h" +#include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" +#include "DNA_world_types.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" + +#include "BKE_action.h" +#include "BKE_animsys.h" +#include "BKE_armature.h" +#include "BKE_brush.h" +#include "BKE_camera.h" +#include "BKE_cachefile.h" +#include "BKE_collection.h" +#include "BKE_curve.h" +#include "BKE_fcurve.h" +#include "BKE_font.h" +#include "BKE_gpencil.h" +#include "BKE_idprop.h" +#include "BKE_image.h" +#include "BKE_ipo.h" +#include "BKE_key.h" +#include "BKE_light.h" +#include "BKE_lattice.h" +#include "BKE_layer.h" +#include "BKE_lib_id.h" +#include "BKE_lib_override.h" +#include "BKE_lib_query.h" +#include "BKE_lib_remap.h" +#include "BKE_linestyle.h" +#include "BKE_mesh.h" +#include "BKE_material.h" +#include "BKE_main.h" +#include "BKE_mask.h" +#include "BKE_mball.h" +#include "BKE_modifier.h" +#include "BKE_movieclip.h" +#include "BKE_multires.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_particle.h" +#include "BKE_lightprobe.h" +#include "BKE_speaker.h" +#include "BKE_sound.h" +#include "BKE_screen.h" +#include "BKE_scene.h" +#include "BKE_text.h" +#include "BKE_texture.h" +#include "BKE_workspace.h" +#include "BKE_world.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +#ifdef WITH_PYTHON +# include "BPY_extern.h" +#endif + +static CLG_LogRef LOG = {"bke.library_remap"}; + +static BKE_library_free_window_manager_cb free_windowmanager_cb = NULL; + +void BKE_library_callback_free_window_manager_set(BKE_library_free_window_manager_cb func) +{ + free_windowmanager_cb = func; +} + +static BKE_library_free_notifier_reference_cb free_notifier_reference_cb = NULL; + +void BKE_library_callback_free_notifier_reference_set(BKE_library_free_notifier_reference_cb func) +{ + free_notifier_reference_cb = func; +} + +static BKE_library_remap_editor_id_reference_cb remap_editor_id_reference_cb = NULL; + +void BKE_library_callback_remap_editor_id_reference_set( + BKE_library_remap_editor_id_reference_cb func) +{ + remap_editor_id_reference_cb = func; +} + +typedef struct IDRemap { + Main *bmain; /* Only used to trigger depsgraph updates in the right bmain. */ + ID *old_id; + ID *new_id; + /** The ID in which we are replacing old_id by new_id usages. */ + ID *id; + short flag; + + /* 'Output' data. */ + short status; + /** Number of direct usecases that could not be remapped (e.g.: obdata when in edit mode). */ + int skipped_direct; + /** Number of indirect usecases that could not be remapped. */ + int skipped_indirect; + /** Number of skipped usecases that refcount the datablock. */ + int skipped_refcounted; +} IDRemap; + +/* IDRemap->flag enums defined in BKE_lib.h */ + +/* IDRemap->status */ +enum { + /* *** Set by callback. *** */ + ID_REMAP_IS_LINKED_DIRECT = 1 << 0, /* new_id is directly linked in current .blend. */ + ID_REMAP_IS_USER_ONE_SKIPPED = 1 << 1, /* There was some skipped 'user_one' usages of old_id. */ +}; + +static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id_p, int cb_flag) +{ + if (cb_flag & IDWALK_CB_PRIVATE) { + return IDWALK_RET_NOP; + } + + IDRemap *id_remap_data = user_data; + ID *old_id = id_remap_data->old_id; + ID *new_id = id_remap_data->new_id; + ID *id = id_remap_data->id; + + if (!old_id) { /* Used to cleanup all IDs used by a specific one. */ + BLI_assert(!new_id); + old_id = *id_p; + } + + if (*id_p && (*id_p == old_id)) { + /* Better remap to NULL than not remapping at all, + * then we can handle it as a regular remap-to-NULL case. */ + if ((cb_flag & IDWALK_CB_NEVER_SELF) && (new_id == id_self)) { + new_id = NULL; + } + + const bool is_reference = (cb_flag & IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE) != 0; + const bool is_indirect = (cb_flag & IDWALK_CB_INDIRECT_USAGE) != 0; + const bool skip_indirect = (id_remap_data->flag & ID_REMAP_SKIP_INDIRECT_USAGE) != 0; + /* Note: proxy usage implies LIB_TAG_EXTERN, so on this aspect it is direct, + * on the other hand since they get reset to lib data on file open/reload it is indirect too. + * Edit Mode is also a 'skip direct' case. */ + const bool is_obj = (GS(id->name) == ID_OB); + const bool is_obj_proxy = (is_obj && (((Object *)id)->proxy || ((Object *)id)->proxy_group)); + const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id)); + const bool is_never_null = ((cb_flag & IDWALK_CB_NEVER_NULL) && (new_id == NULL) && + (id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0); + const bool skip_reference = (id_remap_data->flag & ID_REMAP_SKIP_OVERRIDE_LIBRARY) != 0; + const bool skip_never_null = (id_remap_data->flag & ID_REMAP_SKIP_NEVER_NULL_USAGE) != 0; + +#ifdef DEBUG_PRINT + printf( + "In %s (lib %p): Remapping %s (%p) to %s (%p) " + "(is_indirect: %d, skip_indirect: %d, is_reference: %d, skip_reference: %d)\n", + id->name, + id->lib, + old_id->name, + old_id, + new_id ? new_id->name : "", + new_id, + is_indirect, + skip_indirect, + is_reference, + skip_reference); +#endif + + if ((id_remap_data->flag & ID_REMAP_FLAG_NEVER_NULL_USAGE) && + (cb_flag & IDWALK_CB_NEVER_NULL)) { + id->tag |= LIB_TAG_DOIT; + } + + /* Special hack in case it's Object->data and we are in edit mode, and new_id is not NULL + * (otherwise, we follow common NEVER_NULL flags). + * (skipped_indirect too). */ + if ((is_never_null && skip_never_null) || + (is_obj_editmode && (((Object *)id)->data == *id_p) && new_id != NULL) || + (skip_indirect && is_indirect) || (is_reference && skip_reference)) { + if (is_indirect) { + id_remap_data->skipped_indirect++; + if (is_obj) { + Object *ob = (Object *)id; + if (ob->data == *id_p && ob->proxy != NULL) { + /* And another 'Proudly brought to you by Proxy Hell' hack! + * This will allow us to avoid clearing 'LIB_EXTERN' flag of obdata of proxies... */ + id_remap_data->skipped_direct++; + } + } + } + else if (is_never_null || is_obj_editmode || is_reference) { + id_remap_data->skipped_direct++; + } + else { + BLI_assert(0); + } + if (cb_flag & IDWALK_CB_USER) { + id_remap_data->skipped_refcounted++; + } + else if (cb_flag & IDWALK_CB_USER_ONE) { + /* No need to count number of times this happens, just a flag is enough. */ + id_remap_data->status |= ID_REMAP_IS_USER_ONE_SKIPPED; + } + } + else { + if (!is_never_null) { + *id_p = new_id; + DEG_id_tag_update_ex(id_remap_data->bmain, + id_self, + ID_RECALC_COPY_ON_WRITE | ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + } + if (cb_flag & IDWALK_CB_USER) { + /* NOTE: We don't user-count IDs which are not in the main database. + * This is because in certain conditions we can have data-blocks in + * the main which are referencing data-blocks outside of it. + * For example, BKE_mesh_new_from_object() called on an evaluated + * object will cause such situation. + */ + if ((old_id->tag & LIB_TAG_NO_MAIN) == 0) { + id_us_min(old_id); + } + if (new_id != NULL && (new_id->tag & LIB_TAG_NO_MAIN) == 0) { + /* We do not want to handle LIB_TAG_INDIRECT/LIB_TAG_EXTERN here. */ + new_id->us++; + } + } + else if (cb_flag & IDWALK_CB_USER_ONE) { + id_us_ensure_real(new_id); + /* We cannot affect old_id->us directly, LIB_TAG_EXTRAUSER(_SET) + * are assumed to be set as needed, that extra user is processed in final handling. */ + } + if (!is_indirect || is_obj_proxy) { + id_remap_data->status |= ID_REMAP_IS_LINKED_DIRECT; + } + /* We need to remap proxy_from pointer of remapped proxy... sigh. */ + if (is_obj_proxy && new_id != NULL) { + Object *ob = (Object *)id; + if (ob->proxy == (Object *)new_id) { + ob->proxy->proxy_from = ob; + } + } + } + } + + return IDWALK_RET_NOP; +} + +static void libblock_remap_data_preprocess(IDRemap *r_id_remap_data) +{ + switch (GS(r_id_remap_data->id->name)) { + case ID_OB: { + ID *old_id = r_id_remap_data->old_id; + if (!old_id || GS(old_id->name) == ID_AR) { + Object *ob = (Object *)r_id_remap_data->id; + /* Object's pose holds reference to armature bones... sic */ + /* Note that in theory, we should have to bother about + * linked/non-linked/never-null/etc. flags/states. + * Fortunately, this is just a tag, so we can accept to 'over-tag' a bit for pose recalc, + * and avoid another complex and risky condition nightmare like the one we have in + * foreach_libblock_remap_callback()... */ + if (ob->pose && (!old_id || ob->data == old_id)) { + BLI_assert(ob->type == OB_ARMATURE); + ob->pose->flag |= POSE_RECALC; + /* We need to clear pose bone pointers immediately, things like undo writefile may be + * called before pose is actually recomputed, can lead to segfault... */ + BKE_pose_clear_pointers(ob->pose); + } + } + break; + } + default: + break; + } +} + +/** + * Can be called with both old_ob and new_ob being NULL, + * this means we have to check whole Main database then. + */ +static void libblock_remap_data_postprocess_object_update(Main *bmain, + Object *old_ob, + Object *new_ob) +{ + if (new_ob == NULL) { + /* In case we unlinked old_ob (new_ob is NULL), the object has already + * been removed from the scenes and their collections. We still have + * to remove the NULL children from collections not used in any scene. */ + BKE_collections_object_remove_nulls(bmain); + } + + BKE_main_collection_sync_remap(bmain); + + if (old_ob == NULL) { + for (Object *ob = bmain->objects.first; ob != NULL; ob = ob->id.next) { + if (ob->type == OB_MBALL && BKE_mball_is_basis(ob)) { + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } + } + } + else { + for (Object *ob = bmain->objects.first; ob != NULL; ob = ob->id.next) { + if (ob->type == OB_MBALL && BKE_mball_is_basis_for(ob, old_ob)) { + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + break; /* There is only one basis... */ + } + } + } +} + +/* Can be called with both old_collection and new_collection being NULL, + * this means we have to check whole Main database then. */ +static void libblock_remap_data_postprocess_collection_update(Main *bmain, + Collection *UNUSED(old_collection), + Collection *new_collection) +{ + if (new_collection == NULL) { + /* XXX Complex cases can lead to NULL pointers in other collections than old_collection, + * and BKE_main_collection_sync_remap() does not tolerate any of those, so for now always check + * whole existing collections for NULL pointers. + * I'd consider optimizing that whole collection remapping process a TODO for later. */ + BKE_collections_child_remove_nulls(bmain, NULL /*old_collection*/); + } + else { + /* Temp safe fix, but a "tad" brute force... We should probably be able to use parents from + * old_collection instead? */ + BKE_main_collections_parent_relations_rebuild(bmain); + } + + BKE_main_collection_sync_remap(bmain); +} + +static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *ob, ID *new_id) +{ + if (ob->data == new_id) { + switch (GS(new_id->name)) { + case ID_ME: + multires_force_sculpt_rebuild(ob); + break; + case ID_CU: + BKE_curve_type_test(ob); + break; + default: + break; + } + test_object_modifiers(ob); + BKE_object_materials_test(bmain, ob, new_id); + } +} + +static void libblock_remap_data_postprocess_nodetree_update(Main *bmain, ID *new_id) +{ + /* Update all group nodes using a node group. */ + ntreeUpdateAllUsers(bmain, new_id); +} + +/** + * Execute the 'data' part of the remapping (that is, all ID pointers from other ID data-blocks). + * + * Behavior differs depending on whether given \a id is NULL or not: + * - \a id NULL: \a old_id must be non-NULL, \a new_id may be NULL (unlinking \a old_id) or not + * (remapping \a old_id to \a new_id). + * The whole \a bmain database is checked, and all pointers to \a old_id + * are remapped to \a new_id. + * - \a id is non-NULL: + * + If \a old_id is NULL, \a new_id must also be NULL, + * and all ID pointers from \a id are cleared + * (i.e. \a id does not references any other data-block anymore). + * + If \a old_id is non-NULL, behavior is as with a NULL \a id, but only within given \a id. + * + * \param bmain: the Main data storage to operate on (must never be NULL). + * \param id: the data-block to operate on + * (can be NULL, in which case we operate over all IDs from given bmain). + * \param old_id: the data-block to dereference (may be NULL if \a id is non-NULL). + * \param new_id: the new data-block to replace \a old_id references with (may be NULL). + * \param r_id_remap_data: if non-NULL, the IDRemap struct to use + * (uselful to retrieve info about remapping process). + */ +ATTR_NONNULL(1) +static void libblock_remap_data( + Main *bmain, ID *id, ID *old_id, ID *new_id, const short remap_flags, IDRemap *r_id_remap_data) +{ + IDRemap id_remap_data; + const int foreach_id_flags = (remap_flags & ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE) != 0 ? + IDWALK_NO_INDIRECT_PROXY_DATA_USAGE : + IDWALK_NOP; + + if (r_id_remap_data == NULL) { + r_id_remap_data = &id_remap_data; + } + r_id_remap_data->bmain = bmain; + r_id_remap_data->old_id = old_id; + r_id_remap_data->new_id = new_id; + r_id_remap_data->id = NULL; + r_id_remap_data->flag = remap_flags; + r_id_remap_data->status = 0; + r_id_remap_data->skipped_direct = 0; + r_id_remap_data->skipped_indirect = 0; + r_id_remap_data->skipped_refcounted = 0; + + if (id) { +#ifdef DEBUG_PRINT + printf("\tchecking id %s (%p, %p)\n", id->name, id, id->lib); +#endif + r_id_remap_data->id = id; + libblock_remap_data_preprocess(r_id_remap_data); + BKE_library_foreach_ID_link( + NULL, id, foreach_libblock_remap_callback, (void *)r_id_remap_data, foreach_id_flags); + } + else { + /* Note that this is a very 'brute force' approach, + * maybe we could use some depsgraph to only process objects actually using given old_id... + * sounds rather unlikely currently, though, so this will do for now. */ + ID *id_curr; + + FOREACH_MAIN_ID_BEGIN (bmain, id_curr) { + if (BKE_library_id_can_use_idtype(id_curr, GS(old_id->name))) { + /* Note that we cannot skip indirect usages of old_id here (if requested), + * we still need to check it for the user count handling... + * XXX No more true (except for debug usage of those skipping counters). */ + r_id_remap_data->id = id_curr; + libblock_remap_data_preprocess(r_id_remap_data); + BKE_library_foreach_ID_link(NULL, + id_curr, + foreach_libblock_remap_callback, + (void *)r_id_remap_data, + foreach_id_flags); + } + } + FOREACH_MAIN_ID_END; + } + + /* XXX We may not want to always 'transfer' fake-user from old to new id... + * Think for now it's desired behavior though, + * we can always add an option (flag) to control this later if needed. */ + if (old_id && (old_id->flag & LIB_FAKEUSER)) { + id_fake_user_clear(old_id); + id_fake_user_set(new_id); + } + + id_us_clear_real(old_id); + + if (new_id && (new_id->tag & LIB_TAG_INDIRECT) && + (r_id_remap_data->status & ID_REMAP_IS_LINKED_DIRECT)) { + new_id->tag &= ~LIB_TAG_INDIRECT; + new_id->flag &= ~LIB_INDIRECT_WEAK_LINK; + new_id->tag |= LIB_TAG_EXTERN; + } + +#ifdef DEBUG_PRINT + printf("%s: %d occurrences skipped (%d direct and %d indirect ones)\n", + __func__, + r_id_remap_data->skipped_direct + r_id_remap_data->skipped_indirect, + r_id_remap_data->skipped_direct, + r_id_remap_data->skipped_indirect); +#endif +} + +/** + * Replace all references in given Main to \a old_id by \a new_id + * (if \a new_id is NULL, it unlinks \a old_id). + */ +void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const short remap_flags) +{ + IDRemap id_remap_data; + ID *old_id = old_idv; + ID *new_id = new_idv; + int skipped_direct, skipped_refcounted; + + BLI_assert(old_id != NULL); + BLI_assert((new_id == NULL) || GS(old_id->name) == GS(new_id->name)); + BLI_assert(old_id != new_id); + + libblock_remap_data(bmain, NULL, old_id, new_id, remap_flags, &id_remap_data); + + if (free_notifier_reference_cb) { + free_notifier_reference_cb(old_id); + } + + /* We assume editors do not hold references to their IDs... This is false in some cases + * (Image is especially tricky here), + * editors' code is to handle refcount (id->us) itself then. */ + if (remap_editor_id_reference_cb) { + remap_editor_id_reference_cb(old_id, new_id); + } + + skipped_direct = id_remap_data.skipped_direct; + skipped_refcounted = id_remap_data.skipped_refcounted; + + /* If old_id was used by some ugly 'user_one' stuff (like Image or Clip editors...), and user + * count has actually been incremented for that, we have to decrease once more its user count... + * unless we had to skip some 'user_one' cases. */ + if ((old_id->tag & LIB_TAG_EXTRAUSER_SET) && + !(id_remap_data.status & ID_REMAP_IS_USER_ONE_SKIPPED)) { + id_us_clear_real(old_id); + } + + if (old_id->us - skipped_refcounted < 0) { + CLOG_ERROR(&LOG, + "Error in remapping process from '%s' (%p) to '%s' (%p): " + "wrong user count in old ID after process (summing up to %d)", + old_id->name, + old_id, + new_id ? new_id->name : "", + new_id, + old_id->us - skipped_refcounted); + BLI_assert(0); + } + + if (skipped_direct == 0) { + /* old_id is assumed to not be used directly anymore... */ + if (old_id->lib && (old_id->tag & LIB_TAG_EXTERN)) { + old_id->tag &= ~LIB_TAG_EXTERN; + old_id->tag |= LIB_TAG_INDIRECT; + } + } + + /* Some after-process updates. + * This is a bit ugly, but cannot see a way to avoid it. + * Maybe we should do a per-ID callback for this instead? */ + switch (GS(old_id->name)) { + case ID_OB: + libblock_remap_data_postprocess_object_update(bmain, (Object *)old_id, (Object *)new_id); + break; + case ID_GR: + libblock_remap_data_postprocess_collection_update( + bmain, (Collection *)old_id, (Collection *)new_id); + break; + case ID_ME: + case ID_CU: + case ID_MB: + if (new_id) { /* Only affects us in case obdata was relinked (changed). */ + for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) { + libblock_remap_data_postprocess_obdata_relink(bmain, ob, new_id); + } + } + break; + default: + break; + } + + /* Node trees may virtually use any kind of data-block... */ + /* XXX Yuck!!!! nodetree update can do pretty much any thing when talking about py nodes, + * including creating new data-blocks (see T50385), so we need to unlock main here. :( + * Why can't we have re-entrent locks? */ + BKE_main_unlock(bmain); + libblock_remap_data_postprocess_nodetree_update(bmain, new_id); + BKE_main_lock(bmain); + + /* Full rebuild of DEG! */ + DEG_relations_tag_update(bmain); +} + +void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, const short remap_flags) +{ + BKE_main_lock(bmain); + + BKE_libblock_remap_locked(bmain, old_idv, new_idv, remap_flags); + + BKE_main_unlock(bmain); +} + +/** + * Unlink given \a id from given \a bmain + * (does not touch to indirect, i.e. library, usages of the ID). + * + * \param do_flag_never_null: If true, all IDs using \a idv in a 'non-NULL' way are flagged by + * #LIB_TAG_DOIT flag (quite obviously, 'non-NULL' usages can never be unlinked by this function). + */ +void BKE_libblock_unlink(Main *bmain, + void *idv, + const bool do_flag_never_null, + const bool do_skip_indirect) +{ + const short remap_flags = (do_skip_indirect ? ID_REMAP_SKIP_INDIRECT_USAGE : 0) | + (do_flag_never_null ? ID_REMAP_FLAG_NEVER_NULL_USAGE : 0); + + BKE_main_lock(bmain); + + BKE_libblock_remap_locked(bmain, idv, NULL, remap_flags); + + BKE_main_unlock(bmain); +} + +/** + * Similar to libblock_remap, but only affects IDs used by given \a idv ID. + * + * \param old_idv: Unlike BKE_libblock_remap, can be NULL, + * in which case all ID usages by given \a idv will be cleared. + * \param us_min_never_null: If \a true and new_id is NULL, + * 'NEVER_NULL' ID usages keep their old id, but this one still gets its user count decremented + * (needed when given \a idv is going to be deleted right after being unlinked). + */ +/* Should be able to replace all _relink() funcs (constraints, rigidbody, etc.) ? */ +/* XXX Arg! Naming... :( + * _relink? avoids confusion with _remap, but is confusing with _unlink + * _remap_used_ids? + * _remap_datablocks? + * BKE_id_remap maybe? + * ... sigh + */ +void BKE_libblock_relink_ex( + Main *bmain, void *idv, void *old_idv, void *new_idv, const short remap_flags) +{ + ID *id = idv; + ID *old_id = old_idv; + ID *new_id = new_idv; + + /* No need to lock here, we are only affecting given ID, not bmain database. */ + + BLI_assert(id); + if (old_id) { + BLI_assert((new_id == NULL) || GS(old_id->name) == GS(new_id->name)); + BLI_assert(old_id != new_id); + } + else { + BLI_assert(new_id == NULL); + } + + libblock_remap_data(bmain, id, old_id, new_id, remap_flags, NULL); + + /* Some after-process updates. + * This is a bit ugly, but cannot see a way to avoid it. + * Maybe we should do a per-ID callback for this instead? + */ + switch (GS(id->name)) { + case ID_SCE: + case ID_GR: { + if (old_id) { + switch (GS(old_id->name)) { + case ID_OB: + libblock_remap_data_postprocess_object_update( + bmain, (Object *)old_id, (Object *)new_id); + break; + case ID_GR: + libblock_remap_data_postprocess_collection_update( + bmain, (Collection *)old_id, (Collection *)new_id); + break; + default: + break; + } + } + else { + /* No choice but to check whole objects/collections. */ + libblock_remap_data_postprocess_collection_update(bmain, NULL, NULL); + libblock_remap_data_postprocess_object_update(bmain, NULL, NULL); + } + break; + } + case ID_OB: + if (new_id) { /* Only affects us in case obdata was relinked (changed). */ + libblock_remap_data_postprocess_obdata_relink(bmain, (Object *)id, new_id); + } + break; + default: + break; + } + + DEG_relations_tag_update(bmain); +} + +static int id_relink_to_newid_looper(void *UNUSED(user_data), + ID *UNUSED(self_id), + ID **id_pointer, + const int cb_flag) +{ + if (cb_flag & IDWALK_CB_PRIVATE) { + return IDWALK_RET_NOP; + } + + ID *id = *id_pointer; + if (id) { + /* See: NEW_ID macro */ + if (id->newid) { + BKE_library_update_ID_link_user(id->newid, id, cb_flag); + *id_pointer = id->newid; + } + else if (id->tag & LIB_TAG_NEW) { + id->tag &= ~LIB_TAG_NEW; + BKE_libblock_relink_to_newid(id); + } + } + return IDWALK_RET_NOP; +} + +/** + * Similar to #libblock_relink_ex, + * but is remapping IDs to their newid value if non-NULL, in given \a id. + * + * Very specific usage, not sure we'll keep it on the long run, + * currently only used in Object/Collection duplication code... + */ +void BKE_libblock_relink_to_newid(ID *id) +{ + if (ID_IS_LINKED(id)) { + return; + } + + BKE_library_foreach_ID_link(NULL, id, id_relink_to_newid_looper, NULL, 0); +} + +void BKE_libblock_free_data(ID *id, const bool do_id_user) +{ + if (id->properties) { + IDP_FreePropertyContent_ex(id->properties, do_id_user); + MEM_freeN(id->properties); + } + + if (id->override_library) { + BKE_override_library_free(&id->override_library, do_id_user); + } + + /* XXX TODO remove animdata handling from each type's freeing func, + * and do it here, like for copy! */ +} + +void BKE_libblock_free_datablock(ID *id, const int UNUSED(flag)) +{ + const short type = GS(id->name); + switch (type) { + case ID_SCE: + BKE_scene_free_ex((Scene *)id, false); + break; + case ID_LI: + BKE_library_free((Library *)id); + break; + case ID_OB: + BKE_object_free((Object *)id); + break; + case ID_ME: + BKE_mesh_free((Mesh *)id); + break; + case ID_CU: + BKE_curve_free((Curve *)id); + break; + case ID_MB: + BKE_mball_free((MetaBall *)id); + break; + case ID_MA: + BKE_material_free((Material *)id); + break; + case ID_TE: + BKE_texture_free((Tex *)id); + break; + case ID_IM: + BKE_image_free((Image *)id); + break; + case ID_LT: + BKE_lattice_free((Lattice *)id); + break; + case ID_LA: + BKE_light_free((Light *)id); + break; + case ID_CA: + BKE_camera_free((Camera *)id); + break; + case ID_IP: /* Deprecated. */ + BKE_ipo_free((Ipo *)id); + break; + case ID_KE: + BKE_key_free((Key *)id); + break; + case ID_WO: + BKE_world_free((World *)id); + break; + case ID_SCR: + BKE_screen_free((bScreen *)id); + break; + case ID_VF: + BKE_vfont_free((VFont *)id); + break; + case ID_TXT: + BKE_text_free((Text *)id); + break; + case ID_SPK: + BKE_speaker_free((Speaker *)id); + break; + case ID_LP: + BKE_lightprobe_free((LightProbe *)id); + break; + case ID_SO: + BKE_sound_free((bSound *)id); + break; + case ID_GR: + BKE_collection_free((Collection *)id); + break; + case ID_AR: + BKE_armature_free((bArmature *)id); + break; + case ID_AC: + BKE_action_free((bAction *)id); + break; + case ID_NT: + ntreeFreeTree((bNodeTree *)id); + break; + case ID_BR: + BKE_brush_free((Brush *)id); + break; + case ID_PA: + BKE_particlesettings_free((ParticleSettings *)id); + break; + case ID_WM: + if (free_windowmanager_cb) { + free_windowmanager_cb(NULL, (wmWindowManager *)id); + } + break; + case ID_GD: + BKE_gpencil_free((bGPdata *)id, true); + break; + case ID_MC: + BKE_movieclip_free((MovieClip *)id); + break; + case ID_MSK: + BKE_mask_free((Mask *)id); + break; + case ID_LS: + BKE_linestyle_free((FreestyleLineStyle *)id); + break; + case ID_PAL: + BKE_palette_free((Palette *)id); + break; + case ID_PC: + BKE_paint_curve_free((PaintCurve *)id); + break; + case ID_CF: + BKE_cachefile_free((CacheFile *)id); + break; + case ID_WS: + BKE_workspace_free((WorkSpace *)id); + break; + } +} + +/** + * Complete ID freeing, extended version for corner cases. + * Can override default (and safe!) freeing process, to gain some speed up. + * + * At that point, given id is assumed to not be used by any other data-block already + * (might not be actually true, in case e.g. several inter-related IDs get freed together...). + * However, they might still be using (referencing) other IDs, this code takes care of it if + * #LIB_TAG_NO_USER_REFCOUNT is not defined. + * + * \param bmain: #Main database containing the freed #ID, + * can be NULL in case it's a temp ID outside of any #Main. + * \param idv: Pointer to ID to be freed. + * \param flag: Set of \a LIB_ID_FREE_... flags controlling/overriding usual freeing process, + * 0 to get default safe behavior. + * \param use_flag_from_idtag: Still use freeing info flags from given #ID datablock, + * even if some overriding ones are passed in \a flag parameter. + */ +void BKE_id_free_ex(Main *bmain, void *idv, int flag, const bool use_flag_from_idtag) +{ + ID *id = idv; + + if (use_flag_from_idtag) { + if ((id->tag & LIB_TAG_NO_MAIN) != 0) { + flag |= LIB_ID_FREE_NO_MAIN | LIB_ID_FREE_NO_UI_USER | LIB_ID_FREE_NO_DEG_TAG; + } + else { + flag &= ~LIB_ID_FREE_NO_MAIN; + } + + if ((id->tag & LIB_TAG_NO_USER_REFCOUNT) != 0) { + flag |= LIB_ID_FREE_NO_USER_REFCOUNT; + } + else { + flag &= ~LIB_ID_FREE_NO_USER_REFCOUNT; + } + + if ((id->tag & LIB_TAG_NOT_ALLOCATED) != 0) { + flag |= LIB_ID_FREE_NOT_ALLOCATED; + } + else { + flag &= ~LIB_ID_FREE_NOT_ALLOCATED; + } + } + + BLI_assert((flag & LIB_ID_FREE_NO_MAIN) != 0 || bmain != NULL); + BLI_assert((flag & LIB_ID_FREE_NO_MAIN) != 0 || (flag & LIB_ID_FREE_NOT_ALLOCATED) == 0); + BLI_assert((flag & LIB_ID_FREE_NO_MAIN) != 0 || (flag & LIB_ID_FREE_NO_USER_REFCOUNT) == 0); + + const short type = GS(id->name); + + if (bmain && (flag & LIB_ID_FREE_NO_DEG_TAG) == 0) { + DEG_id_type_tag(bmain, type); + } + +#ifdef WITH_PYTHON +# ifdef WITH_PYTHON_SAFETY + BPY_id_release(id); +# endif + if (id->py_instance) { + BPY_DECREF_RNA_INVALIDATE(id->py_instance); + } +#endif + + if ((flag & LIB_ID_FREE_NO_USER_REFCOUNT) == 0) { + BKE_libblock_relink_ex(bmain, id, NULL, NULL, 0); + } + + BKE_libblock_free_datablock(id, flag); + + /* avoid notifying on removed data */ + if ((flag & LIB_ID_FREE_NO_MAIN) == 0) { + BKE_main_lock(bmain); + } + + if ((flag & LIB_ID_FREE_NO_UI_USER) == 0) { + if (free_notifier_reference_cb) { + free_notifier_reference_cb(id); + } + + if (remap_editor_id_reference_cb) { + remap_editor_id_reference_cb(id, NULL); + } + } + + if ((flag & LIB_ID_FREE_NO_MAIN) == 0) { + ListBase *lb = which_libbase(bmain, type); + BLI_remlink(lb, id); + } + + BKE_libblock_free_data(id, (flag & LIB_ID_FREE_NO_USER_REFCOUNT) == 0); + + if ((flag & LIB_ID_FREE_NO_MAIN) == 0) { + BKE_main_unlock(bmain); + } + + if ((flag & LIB_ID_FREE_NOT_ALLOCATED) == 0) { + MEM_freeN(id); + } +} + +/** + * Complete ID freeing, should be usable in most cases (even for out-of-Main IDs). + * + * See #BKE_id_free_ex description for full details. + * + * \param bmain: Main database containing the freed ID, + * can be NULL in case it's a temp ID outside of any Main. + * \param idv: Pointer to ID to be freed. + */ +void BKE_id_free(Main *bmain, void *idv) +{ + BKE_id_free_ex(bmain, idv, 0, true); +} + +/** + * Not really a freeing function by itself, + * it decrements usercount of given id, and only frees it if it reaches 0. + */ +void BKE_id_free_us(Main *bmain, void *idv) /* test users */ +{ + ID *id = idv; + + id_us_min(id); + + /* XXX This is a temp (2.77) hack so that we keep same behavior as in 2.76 regarding collections + * when deleting an object. Since only 'user_one' usage of objects is collections, + * and only 'real user' usage of objects is scenes, removing that 'user_one' tag when there + * is no more real (scene) users of an object ensures it gets fully unlinked. + * But only for local objects, not linked ones! + * Otherwise, there is no real way to get rid of an object anymore - + * better handling of this is TODO. + */ + if ((GS(id->name) == ID_OB) && (id->us == 1) && (id->lib == NULL)) { + id_us_clear_real(id); + } + + if (id->us == 0) { + BKE_libblock_unlink(bmain, id, false, false); + + BKE_id_free(bmain, id); + } +} + +static void id_delete(Main *bmain, const bool do_tagged_deletion) +{ + const int tag = LIB_TAG_DOIT; + ListBase *lbarray[MAX_LIBARRAY]; + Link dummy_link = {0}; + int base_count, i; + + /* Used by batch tagged deletion, when we call BKE_id_free then, id is no more in Main database, + * and has already properly unlinked its other IDs usages. + * UI users are always cleared in BKE_libblock_remap_locked() call, so we can always skip it. */ + const int free_flag = LIB_ID_FREE_NO_UI_USER | + (do_tagged_deletion ? LIB_ID_FREE_NO_MAIN | LIB_ID_FREE_NO_USER_REFCOUNT : + 0); + ListBase tagged_deleted_ids = {NULL}; + + base_count = set_listbasepointers(bmain, lbarray); + + BKE_main_lock(bmain); + if (do_tagged_deletion) { + /* Main idea of batch deletion is to remove all IDs to be deleted from Main database. + * This means that we won't have to loop over all deleted IDs to remove usages + * of other deleted IDs. + * This gives tremendous speed-up when deleting a large amount of IDs from a Main + * containing thousands of those. + * This also means that we have to be very careful here, as we by-pass many 'common' + * processing, hence risking to 'corrupt' at least user counts, if not IDs themselves. */ + bool keep_looping = true; + while (keep_looping) { + ID *id, *id_next; + ID *last_remapped_id = tagged_deleted_ids.last; + keep_looping = false; + + /* First tag and remove from Main all datablocks directly from target lib. + * Note that we go forward here, since we want to check dependencies before users + * (e.g. meshes before objects). Avoids to have to loop twice. */ + for (i = 0; i < base_count; i++) { + ListBase *lb = lbarray[i]; + + for (id = lb->first; id; id = id_next) { + id_next = id->next; + /* Note: in case we delete a library, we also delete all its datablocks! */ + if ((id->tag & tag) || (id->lib != NULL && (id->lib->id.tag & tag))) { + BLI_remlink(lb, id); + BLI_addtail(&tagged_deleted_ids, id); + /* Do not tag as no_main now, we want to unlink it first (lower-level ID management + * code has some specific handling of 'nom main' + * IDs that would be a problem in that case). */ + id->tag |= tag; + keep_looping = true; + } + } + } + if (last_remapped_id == NULL) { + dummy_link.next = tagged_deleted_ids.first; + last_remapped_id = (ID *)(&dummy_link); + } + for (id = last_remapped_id->next; id; id = id->next) { + /* Will tag 'never NULL' users of this ID too. + * Note that we cannot use BKE_libblock_unlink() here, + * since it would ignore indirect (and proxy!) + * links, this can lead to nasty crashing here in second, actual deleting loop. + * Also, this will also flag users of deleted data that cannot be unlinked + * (object using deleted obdata, etc.), so that they also get deleted. */ + BKE_libblock_remap_locked( + bmain, id, NULL, ID_REMAP_FLAG_NEVER_NULL_USAGE | ID_REMAP_FORCE_NEVER_NULL_USAGE); + /* Since we removed ID from Main, + * we also need to unlink its own other IDs usages ourself. */ + BKE_libblock_relink_ex(bmain, id, NULL, NULL, 0); + /* Now we can safely mark that ID as not being in Main database anymore. */ + id->tag |= LIB_TAG_NO_MAIN; + /* This is needed because we may not have remapped usages + * of that ID by other deleted ones. */ + // id->us = 0; /* Is it actually? */ + } + } + } + else { + /* First tag all datablocks directly from target lib. + * Note that we go forward here, since we want to check dependencies before users + * (e.g. meshes before objects). + * Avoids to have to loop twice. */ + for (i = 0; i < base_count; i++) { + ListBase *lb = lbarray[i]; + ID *id, *id_next; + + for (id = lb->first; id; id = id_next) { + id_next = id->next; + /* Note: in case we delete a library, we also delete all its datablocks! */ + if ((id->tag & tag) || (id->lib != NULL && (id->lib->id.tag & tag))) { + id->tag |= tag; + + /* Will tag 'never NULL' users of this ID too. + * Note that we cannot use BKE_libblock_unlink() here, since it would ignore indirect + * (and proxy!) links, this can lead to nasty crashing here in second, + * actual deleting loop. + * Also, this will also flag users of deleted data that cannot be unlinked + * (object using deleted obdata, etc.), so that they also get deleted. */ + BKE_libblock_remap_locked( + bmain, id, NULL, ID_REMAP_FLAG_NEVER_NULL_USAGE | ID_REMAP_FORCE_NEVER_NULL_USAGE); + } + } + } + } + BKE_main_unlock(bmain); + + /* In usual reversed order, such that all usage of a given ID, even 'never NULL' ones, + * have been already cleared when we reach it + * (e.g. Objects being processed before meshes, they'll have already released their 'reference' + * over meshes when we come to freeing obdata). */ + for (i = do_tagged_deletion ? 1 : base_count; i--;) { + ListBase *lb = lbarray[i]; + ID *id, *id_next; + + for (id = do_tagged_deletion ? tagged_deleted_ids.first : lb->first; id; id = id_next) { + id_next = id->next; + if (id->tag & tag) { + if (id->us != 0) { +#ifdef DEBUG_PRINT + printf("%s: deleting %s (%d)\n", __func__, id->name, id->us); +#endif + BLI_assert(id->us == 0); + } + BKE_id_free_ex(bmain, id, free_flag, !do_tagged_deletion); + } + } + } + + bmain->is_memfile_undo_written = false; +} + +/** + * Properly delete a single ID from given \a bmain database. + */ +void BKE_id_delete(Main *bmain, void *idv) +{ + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + ((ID *)idv)->tag |= LIB_TAG_DOIT; + + id_delete(bmain, false); +} + +/** + * Properly delete all IDs tagged with \a LIB_TAG_DOIT, in given \a bmain database. + * + * This is more efficient than calling #BKE_id_delete repetitively on a large set of IDs + * (several times faster when deleting most of the IDs at once)... + * + * \warning Considered experimental for now, seems to be working OK but this is + * risky code in a complicated area. + */ +void BKE_id_multi_tagged_delete(Main *bmain) +{ + id_delete(bmain, true); +} diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c deleted file mode 100644 index e051dc946cb..00000000000 --- a/source/blender/blenkernel/intern/library.c +++ /dev/null @@ -1,2646 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup bke - * - * Contains management of ID's and libraries - * allocate and free of all library data - */ - -#include -#include -#include -#include -#include -#include - -#include "CLG_log.h" - -#include "MEM_guardedalloc.h" - -/* all types are needed here, in order to do memory operations */ -#include "DNA_anim_types.h" -#include "DNA_armature_types.h" -#include "DNA_brush_types.h" -#include "DNA_cachefile_types.h" -#include "DNA_camera_types.h" -#include "DNA_collection_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_ipo_types.h" -#include "DNA_key_types.h" -#include "DNA_light_types.h" -#include "DNA_lattice_types.h" -#include "DNA_linestyle_types.h" -#include "DNA_material_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meta_types.h" -#include "DNA_modifier_types.h" -#include "DNA_movieclip_types.h" -#include "DNA_mask_types.h" -#include "DNA_node_types.h" -#include "DNA_object_types.h" -#include "DNA_lightprobe_types.h" -#include "DNA_rigidbody_types.h" -#include "DNA_scene_types.h" -#include "DNA_screen_types.h" -#include "DNA_speaker_types.h" -#include "DNA_sound_types.h" -#include "DNA_text_types.h" -#include "DNA_vfont_types.h" -#include "DNA_windowmanager_types.h" -#include "DNA_world_types.h" -#include "DNA_workspace_types.h" - -#include "BLI_utildefines.h" - -#include "BLI_bitmap.h" -#include "BLI_blenlib.h" -#include "BLI_ghash.h" -#include "BLI_linklist.h" -#include "BLI_memarena.h" -#include "BLI_string_utils.h" - -#include "BLT_translation.h" - -#include "BKE_action.h" -#include "BKE_animsys.h" -#include "BKE_armature.h" -#include "BKE_bpath.h" -#include "BKE_brush.h" -#include "BKE_camera.h" -#include "BKE_cachefile.h" -#include "BKE_collection.h" -#include "BKE_context.h" -#include "BKE_curve.h" -#include "BKE_font.h" -#include "BKE_global.h" -#include "BKE_gpencil.h" -#include "BKE_idcode.h" -#include "BKE_idprop.h" -#include "BKE_image.h" -#include "BKE_key.h" -#include "BKE_light.h" -#include "BKE_lattice.h" -#include "BKE_library.h" -#include "BKE_library_query.h" -#include "BKE_library_remap.h" -#include "BKE_linestyle.h" -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" -#include "BKE_material.h" -#include "BKE_main.h" -#include "BKE_mball.h" -#include "BKE_mask.h" -#include "BKE_movieclip.h" -#include "BKE_node.h" -#include "BKE_object.h" -#include "BKE_paint.h" -#include "BKE_particle.h" -#include "BKE_packedFile.h" -#include "BKE_lightprobe.h" -#include "BKE_rigidbody.h" -#include "BKE_sound.h" -#include "BKE_speaker.h" -#include "BKE_scene.h" -#include "BKE_text.h" -#include "BKE_texture.h" -#include "BKE_world.h" - -#include "DEG_depsgraph.h" - -#include "RNA_access.h" - -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" - -#include "atomic_ops.h" - -//#define DEBUG_TIME - -#ifdef DEBUG_TIME -# include "PIL_time_utildefines.h" -#endif - -static CLG_LogRef LOG = {"bke.library"}; - -/* GS reads the memory pointed at in a specific ordering. - * only use this definition, makes little and big endian systems - * work fine, in conjunction with MAKE_ID */ - -/* ************* general ************************ */ - -/* this has to be called from each make_local_* func, we could call - * from id_make_local() but then the make local functions would not be self - * contained. - * also note that the id _must_ have a library - campbell */ -void BKE_id_lib_local_paths(Main *bmain, Library *lib, ID *id) -{ - const char *bpath_user_data[2] = {BKE_main_blendfile_path(bmain), lib->filepath}; - - BKE_bpath_traverse_id(bmain, - id, - BKE_bpath_relocate_visitor, - BKE_BPATH_TRAVERSE_SKIP_MULTIFILE, - (void *)bpath_user_data); -} - -void id_lib_extern(ID *id) -{ - if (id && ID_IS_LINKED(id)) { - BLI_assert(BKE_idcode_is_linkable(GS(id->name))); - if (id->tag & LIB_TAG_INDIRECT) { - id->tag &= ~LIB_TAG_INDIRECT; - id->flag &= ~LIB_INDIRECT_WEAK_LINK; - id->tag |= LIB_TAG_EXTERN; - id->lib->parent = NULL; - } - } -} - -void id_lib_indirect_weak_link(ID *id) -{ - if (id && ID_IS_LINKED(id)) { - BLI_assert(BKE_idcode_is_linkable(GS(id->name))); - if (id->tag & LIB_TAG_INDIRECT) { - id->flag |= LIB_INDIRECT_WEAK_LINK; - } - } -} - -/** - * Ensure we have a real user - * - * \note Now that we have flags, we could get rid of the 'fake_user' special case, - * flags are enough to ensure we always have a real user. - * However, #ID_REAL_USERS is used in several places outside of core library.c, - * so think we can wait later to make this change. - */ -void id_us_ensure_real(ID *id) -{ - if (id) { - const int limit = ID_FAKE_USERS(id); - id->tag |= LIB_TAG_EXTRAUSER; - if (id->us <= limit) { - if (id->us < limit || ((id->us == limit) && (id->tag & LIB_TAG_EXTRAUSER_SET))) { - CLOG_ERROR(&LOG, - "ID user count error: %s (from '%s')", - id->name, - id->lib ? id->lib->filepath : "[Main]"); - BLI_assert(0); - } - id->us = limit + 1; - id->tag |= LIB_TAG_EXTRAUSER_SET; - } - } -} - -void id_us_clear_real(ID *id) -{ - if (id && (id->tag & LIB_TAG_EXTRAUSER)) { - if (id->tag & LIB_TAG_EXTRAUSER_SET) { - id->us--; - BLI_assert(id->us >= ID_FAKE_USERS(id)); - } - id->tag &= ~(LIB_TAG_EXTRAUSER | LIB_TAG_EXTRAUSER_SET); - } -} - -/** - * Same as \a id_us_plus, but does not handle lib indirect -> extern. - * Only used by readfile.c so far, but simpler/safer to keep it here nonetheless. - */ -void id_us_plus_no_lib(ID *id) -{ - if (id) { - if ((id->tag & LIB_TAG_EXTRAUSER) && (id->tag & LIB_TAG_EXTRAUSER_SET)) { - BLI_assert(id->us >= 1); - /* No need to increase count, just tag extra user as no more set. - * Avoids annoying & inconsistent +1 in user count. */ - id->tag &= ~LIB_TAG_EXTRAUSER_SET; - } - else { - BLI_assert(id->us >= 0); - id->us++; - } - } -} - -void id_us_plus(ID *id) -{ - if (id) { - id_us_plus_no_lib(id); - id_lib_extern(id); - } -} - -/* decrements the user count for *id. */ -void id_us_min(ID *id) -{ - if (id) { - const int limit = ID_FAKE_USERS(id); - - if (id->us <= limit) { - CLOG_ERROR(&LOG, - "ID user decrement error: %s (from '%s'): %d <= %d", - id->name, - id->lib ? id->lib->filepath : "[Main]", - id->us, - limit); - BLI_assert(0); - id->us = limit; - } - else { - id->us--; - } - - if ((id->us == limit) && (id->tag & LIB_TAG_EXTRAUSER)) { - /* We need an extra user here, but never actually incremented user count for it so far, - * do it now. */ - id_us_ensure_real(id); - } - } -} - -void id_fake_user_set(ID *id) -{ - if (id && !(id->flag & LIB_FAKEUSER)) { - id->flag |= LIB_FAKEUSER; - id_us_plus(id); - } -} - -void id_fake_user_clear(ID *id) -{ - if (id && (id->flag & LIB_FAKEUSER)) { - id->flag &= ~LIB_FAKEUSER; - id_us_min(id); - } -} - -void BKE_id_clear_newpoin(ID *id) -{ - if (id->newid) { - id->newid->tag &= ~LIB_TAG_NEW; - } - id->newid = NULL; -} - -static int id_expand_local_callback(void *UNUSED(user_data), - struct ID *id_self, - struct ID **id_pointer, - int cb_flag) -{ - if (cb_flag & IDWALK_CB_PRIVATE) { - return IDWALK_RET_NOP; - } - - /* Can happen that we get un-linkable ID here, e.g. with shape-key referring to itself - * (through drivers)... - * Just skip it, shape key can only be either indirectly linked, or fully local, period. - * And let's curse one more time that stupid useless shapekey ID type! */ - if (*id_pointer && *id_pointer != id_self && BKE_idcode_is_linkable(GS((*id_pointer)->name))) { - id_lib_extern(*id_pointer); - } - - return IDWALK_RET_NOP; -} - -/** - * Expand ID usages of given id as 'extern' (and no more indirect) linked data. - * Used by ID copy/make_local functions. - */ -void BKE_id_expand_local(Main *bmain, ID *id) -{ - BKE_library_foreach_ID_link(bmain, id, id_expand_local_callback, NULL, IDWALK_READONLY); -} - -/** - * Ensure new (copied) ID is fully made local. - */ -void BKE_id_copy_ensure_local(Main *bmain, const ID *old_id, ID *new_id) -{ - if (ID_IS_LINKED(old_id)) { - BKE_id_expand_local(bmain, new_id); - BKE_id_lib_local_paths(bmain, old_id->lib, new_id); - } -} - -/** - * Generic 'make local' function, works for most of data-block types... - */ -void BKE_id_make_local_generic(Main *bmain, - ID *id, - const bool id_in_mainlist, - const bool lib_local) -{ - bool is_local = false, is_lib = false; - - /* - only lib users: do nothing (unless force_local is set) - * - only local users: set flag - * - mixed: make copy - * In case we make a whole lib's content local, - * we always want to localize, and we skip remapping (done later). - */ - - if (!ID_IS_LINKED(id)) { - return; - } - - BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); - - if (lib_local || is_local) { - if (!is_lib) { - id_clear_lib_data_ex(bmain, id, id_in_mainlist); - BKE_id_expand_local(bmain, id); - } - else { - ID *id_new; - - /* Should not fail in expected use cases, - * but a few ID types cannot be copied (LIB, WM, SCR...). */ - if (BKE_id_copy(bmain, id, &id_new)) { - id_new->us = 0; - - /* setting newid is mandatory for complex make_lib_local logic... */ - ID_NEW_SET(id, id_new); - Key *key = BKE_key_from_id(id), *key_new = BKE_key_from_id(id); - if (key && key_new) { - ID_NEW_SET(key, key_new); - } - bNodeTree *ntree = ntreeFromID(id), *ntree_new = ntreeFromID(id_new); - if (ntree && ntree_new) { - ID_NEW_SET(ntree, ntree_new); - } - - if (!lib_local) { - BKE_libblock_remap(bmain, id, id_new, ID_REMAP_SKIP_INDIRECT_USAGE); - } - } - } - } -} - -/** - * Calls the appropriate make_local method for the block, unless test is set. - * - * \note Always set ID->newid pointer in case it gets duplicated... - * - * \param lib_local: Special flag used when making a whole library's content local, - * it needs specific handling. - * - * \return true if the block can be made local. - */ -bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local) -{ - /* We don't care whether ID is directly or indirectly linked - * in case we are making a whole lib local... */ - if (!lib_local && (id->tag & LIB_TAG_INDIRECT)) { - return false; - } - - switch ((ID_Type)GS(id->name)) { - case ID_SCE: - if (!test) { - BKE_scene_make_local(bmain, (Scene *)id, lib_local); - } - return true; - case ID_OB: - if (!test) { - BKE_object_make_local(bmain, (Object *)id, lib_local); - } - return true; - case ID_ME: - if (!test) { - BKE_mesh_make_local(bmain, (Mesh *)id, lib_local); - } - return true; - case ID_CU: - if (!test) { - BKE_curve_make_local(bmain, (Curve *)id, lib_local); - } - return true; - case ID_MB: - if (!test) { - BKE_mball_make_local(bmain, (MetaBall *)id, lib_local); - } - return true; - case ID_MA: - if (!test) { - BKE_material_make_local(bmain, (Material *)id, lib_local); - } - return true; - case ID_TE: - if (!test) { - BKE_texture_make_local(bmain, (Tex *)id, lib_local); - } - return true; - case ID_IM: - if (!test) { - BKE_image_make_local(bmain, (Image *)id, lib_local); - } - return true; - case ID_LT: - if (!test) { - BKE_lattice_make_local(bmain, (Lattice *)id, lib_local); - } - return true; - case ID_LA: - if (!test) { - BKE_light_make_local(bmain, (Light *)id, lib_local); - } - return true; - case ID_CA: - if (!test) { - BKE_camera_make_local(bmain, (Camera *)id, lib_local); - } - return true; - case ID_SPK: - if (!test) { - BKE_speaker_make_local(bmain, (Speaker *)id, lib_local); - } - return true; - case ID_LP: - if (!test) { - BKE_lightprobe_make_local(bmain, (LightProbe *)id, lib_local); - } - return true; - case ID_WO: - if (!test) { - BKE_world_make_local(bmain, (World *)id, lib_local); - } - return true; - case ID_VF: - if (!test) { - BKE_vfont_make_local(bmain, (VFont *)id, lib_local); - } - return true; - case ID_TXT: - if (!test) { - BKE_text_make_local(bmain, (Text *)id, lib_local); - } - return true; - case ID_SO: - if (!test) { - BKE_sound_make_local(bmain, (bSound *)id, lib_local); - } - return true; - case ID_GR: - if (!test) { - BKE_collection_make_local(bmain, (Collection *)id, lib_local); - } - return true; - case ID_AR: - if (!test) { - BKE_armature_make_local(bmain, (bArmature *)id, lib_local); - } - return true; - case ID_AC: - if (!test) { - BKE_action_make_local(bmain, (bAction *)id, lib_local); - } - return true; - case ID_NT: - if (!test) { - ntreeMakeLocal(bmain, (bNodeTree *)id, true, lib_local); - } - return true; - case ID_BR: - if (!test) { - BKE_brush_make_local(bmain, (Brush *)id, lib_local); - } - return true; - case ID_PA: - if (!test) { - BKE_particlesettings_make_local(bmain, (ParticleSettings *)id, lib_local); - } - return true; - case ID_GD: - if (!test) { - BKE_gpencil_make_local(bmain, (bGPdata *)id, lib_local); - } - return true; - case ID_MC: - if (!test) { - BKE_movieclip_make_local(bmain, (MovieClip *)id, lib_local); - } - return true; - case ID_MSK: - if (!test) { - BKE_mask_make_local(bmain, (Mask *)id, lib_local); - } - return true; - case ID_LS: - if (!test) { - BKE_linestyle_make_local(bmain, (FreestyleLineStyle *)id, lib_local); - } - return true; - case ID_PAL: - if (!test) { - BKE_palette_make_local(bmain, (Palette *)id, lib_local); - } - return true; - case ID_PC: - if (!test) { - BKE_paint_curve_make_local(bmain, (PaintCurve *)id, lib_local); - } - return true; - case ID_CF: - if (!test) { - BKE_cachefile_make_local(bmain, (CacheFile *)id, lib_local); - } - return true; - case ID_WS: - case ID_SCR: - /* A bit special: can be appended but not linked. Return false - * since supporting make-local doesn't make much sense. */ - return false; - case ID_LI: - case ID_KE: - case ID_WM: - return false; /* can't be linked */ - case ID_IP: - return false; /* deprecated */ - } - - return false; -} - -struct IDCopyLibManagementData { - const ID *id_src; - ID *id_dst; - int flag; -}; - -/* Increases usercount as required, and remap self ID pointers. */ -static int id_copy_libmanagement_cb(void *user_data, - ID *UNUSED(id_self), - ID **id_pointer, - int cb_flag) -{ - struct IDCopyLibManagementData *data = user_data; - ID *id = *id_pointer; - - /* Remap self-references to new copied ID. */ - if (id == data->id_src) { - /* We cannot use id_self here, it is not *always* id_dst (thanks to $£!+@#&/? nodetrees). */ - id = *id_pointer = data->id_dst; - } - - /* Increase used IDs refcount if needed and required. */ - if ((data->flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0 && (cb_flag & IDWALK_CB_USER)) { - id_us_plus(id); - } - - return IDWALK_RET_NOP; -} - -bool BKE_id_copy_is_allowed(const ID *id) -{ -#define LIB_ID_TYPES_NOCOPY \ - ID_LI, ID_SCR, ID_WM, ID_WS, /* Not supported */ \ - ID_IP /* Deprecated */ - - return !ELEM(GS(id->name), LIB_ID_TYPES_NOCOPY); - -#undef LIB_ID_TYPES_NOCOPY -} - -/** - * Generic entry point for copying a data-block (new API). - * - * \note Copy is only affecting given data-block - * (no ID used by copied one will be affected, besides usercount). - * There is only one exception, if #LIB_ID_COPY_ACTIONS is defined, - * actions used by animdata will be duplicated. - * - * \note Usercount of new copy is always set to 1. - * - * \param bmain: Main database, may be NULL only if LIB_ID_CREATE_NO_MAIN is specified. - * \param id: Source data-block. - * \param r_newid: Pointer to new (copied) ID pointer. - * \param flag: Set of copy options, see DNA_ID.h enum for details - * (leave to zero for default, full copy). - * \return False when copying that ID type is not supported, true otherwise. - */ -bool BKE_id_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int flag) -{ - BLI_assert(r_newid != NULL); - /* Make sure destination pointer is all good. */ - if ((flag & LIB_ID_CREATE_NO_ALLOCATE) == 0) { - *r_newid = NULL; - } - else { - if (*r_newid != NULL) { - /* Allow some garbage non-initialized memory to go in, and clean it up here. */ - const size_t size = BKE_libblock_get_alloc_info(GS(id->name), NULL); - memset(*r_newid, 0, size); - } - } - - /* Early output is source is NULL. */ - if (id == NULL) { - return false; - } - if (!BKE_id_copy_is_allowed(id)) { - return false; - } - - BKE_libblock_copy_ex(bmain, id, r_newid, flag); - - switch ((ID_Type)GS(id->name)) { - case ID_SCE: - BKE_scene_copy_data(bmain, (Scene *)*r_newid, (Scene *)id, flag); - break; - case ID_OB: - BKE_object_copy_data(bmain, (Object *)*r_newid, (Object *)id, flag); - break; - case ID_ME: - BKE_mesh_copy_data(bmain, (Mesh *)*r_newid, (Mesh *)id, flag); - break; - case ID_CU: - BKE_curve_copy_data(bmain, (Curve *)*r_newid, (Curve *)id, flag); - break; - case ID_MB: - BKE_mball_copy_data(bmain, (MetaBall *)*r_newid, (MetaBall *)id, flag); - break; - case ID_MA: - BKE_material_copy_data(bmain, (Material *)*r_newid, (Material *)id, flag); - break; - case ID_TE: - BKE_texture_copy_data(bmain, (Tex *)*r_newid, (Tex *)id, flag); - break; - case ID_IM: - BKE_image_copy_data(bmain, (Image *)*r_newid, (Image *)id, flag); - break; - case ID_LT: - BKE_lattice_copy_data(bmain, (Lattice *)*r_newid, (Lattice *)id, flag); - break; - case ID_LA: - BKE_light_copy_data(bmain, (Light *)*r_newid, (Light *)id, flag); - break; - case ID_SPK: - BKE_speaker_copy_data(bmain, (Speaker *)*r_newid, (Speaker *)id, flag); - break; - case ID_LP: - BKE_lightprobe_copy_data(bmain, (LightProbe *)*r_newid, (LightProbe *)id, flag); - break; - case ID_CA: - BKE_camera_copy_data(bmain, (Camera *)*r_newid, (Camera *)id, flag); - break; - case ID_KE: - BKE_key_copy_data(bmain, (Key *)*r_newid, (Key *)id, flag); - break; - case ID_WO: - BKE_world_copy_data(bmain, (World *)*r_newid, (World *)id, flag); - break; - case ID_TXT: - BKE_text_copy_data(bmain, (Text *)*r_newid, (Text *)id, flag); - break; - case ID_GR: - BKE_collection_copy_data(bmain, (Collection *)*r_newid, (Collection *)id, flag); - break; - case ID_AR: - BKE_armature_copy_data(bmain, (bArmature *)*r_newid, (bArmature *)id, flag); - break; - case ID_AC: - BKE_action_copy_data(bmain, (bAction *)*r_newid, (bAction *)id, flag); - break; - case ID_NT: - BKE_node_tree_copy_data(bmain, (bNodeTree *)*r_newid, (bNodeTree *)id, flag); - break; - case ID_BR: - BKE_brush_copy_data(bmain, (Brush *)*r_newid, (Brush *)id, flag); - break; - case ID_PA: - BKE_particlesettings_copy_data( - bmain, (ParticleSettings *)*r_newid, (ParticleSettings *)id, flag); - break; - case ID_GD: - BKE_gpencil_copy_data((bGPdata *)*r_newid, (bGPdata *)id, flag); - break; - case ID_MC: - BKE_movieclip_copy_data(bmain, (MovieClip *)*r_newid, (MovieClip *)id, flag); - break; - case ID_MSK: - BKE_mask_copy_data(bmain, (Mask *)*r_newid, (Mask *)id, flag); - break; - case ID_LS: - BKE_linestyle_copy_data( - bmain, (FreestyleLineStyle *)*r_newid, (FreestyleLineStyle *)id, flag); - break; - case ID_PAL: - BKE_palette_copy_data(bmain, (Palette *)*r_newid, (Palette *)id, flag); - break; - case ID_PC: - BKE_paint_curve_copy_data(bmain, (PaintCurve *)*r_newid, (PaintCurve *)id, flag); - break; - case ID_CF: - BKE_cachefile_copy_data(bmain, (CacheFile *)*r_newid, (CacheFile *)id, flag); - break; - case ID_SO: - BKE_sound_copy_data(bmain, (bSound *)*r_newid, (bSound *)id, flag); - break; - case ID_VF: - BKE_vfont_copy_data(bmain, (VFont *)*r_newid, (VFont *)id, flag); - break; - case ID_LI: - case ID_SCR: - case ID_WM: - case ID_WS: - case ID_IP: - BLI_assert(0); /* Should have been rejected at start of function! */ - break; - } - - /* Update ID refcount, remap pointers to self in new ID. */ - struct IDCopyLibManagementData data = { - .id_src = id, - .id_dst = *r_newid, - .flag = flag, - }; - BKE_library_foreach_ID_link(bmain, *r_newid, id_copy_libmanagement_cb, &data, IDWALK_NOP); - - /* Do not make new copy local in case we are copying outside of main... - * XXX TODO: is this behavior OK, or should we need own flag to control that? */ - if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) { - BLI_assert((flag & LIB_ID_COPY_KEEP_LIB) == 0); - BKE_id_copy_ensure_local(bmain, id, *r_newid); - } - else { - (*r_newid)->lib = id->lib; - } - - return true; -} - -/** - * Invokes the appropriate copy method for the block and returns the result in - * newid, unless test. Returns true if the block can be copied. - */ -bool BKE_id_copy(Main *bmain, const ID *id, ID **newid) -{ - return BKE_id_copy_ex(bmain, id, newid, LIB_ID_COPY_DEFAULT); -} - -/** - * Does a mere memory swap over the whole IDs data (including type-specific memory). - * \note Most internal ID data itself is not swapped (only IDProperties are). - */ -void BKE_id_swap(Main *bmain, ID *id_a, ID *id_b) -{ - BLI_assert(GS(id_a->name) == GS(id_b->name)); - - const ID id_a_back = *id_a; - const ID id_b_back = *id_b; - -#define CASE_SWAP(_gs, _type) \ - case _gs: \ - SWAP(_type, *(_type *)id_a, *(_type *)id_b); \ - break - - switch ((ID_Type)GS(id_a->name)) { - CASE_SWAP(ID_SCE, Scene); - CASE_SWAP(ID_LI, Library); - CASE_SWAP(ID_OB, Object); - CASE_SWAP(ID_ME, Mesh); - CASE_SWAP(ID_CU, Curve); - CASE_SWAP(ID_MB, MetaBall); - CASE_SWAP(ID_MA, Material); - CASE_SWAP(ID_TE, Tex); - CASE_SWAP(ID_IM, Image); - CASE_SWAP(ID_LT, Lattice); - CASE_SWAP(ID_LA, Light); - CASE_SWAP(ID_LP, LightProbe); - CASE_SWAP(ID_CA, Camera); - CASE_SWAP(ID_KE, Key); - CASE_SWAP(ID_WO, World); - CASE_SWAP(ID_SCR, bScreen); - CASE_SWAP(ID_VF, VFont); - CASE_SWAP(ID_TXT, Text); - CASE_SWAP(ID_SPK, Speaker); - CASE_SWAP(ID_SO, bSound); - CASE_SWAP(ID_GR, Collection); - CASE_SWAP(ID_AR, bArmature); - CASE_SWAP(ID_AC, bAction); - CASE_SWAP(ID_NT, bNodeTree); - CASE_SWAP(ID_BR, Brush); - CASE_SWAP(ID_PA, ParticleSettings); - CASE_SWAP(ID_WM, wmWindowManager); - CASE_SWAP(ID_WS, WorkSpace); - CASE_SWAP(ID_GD, bGPdata); - CASE_SWAP(ID_MC, MovieClip); - CASE_SWAP(ID_MSK, Mask); - CASE_SWAP(ID_LS, FreestyleLineStyle); - CASE_SWAP(ID_PAL, Palette); - CASE_SWAP(ID_PC, PaintCurve); - CASE_SWAP(ID_CF, CacheFile); - case ID_IP: - break; /* Deprecated. */ - } - -#undef CASE_SWAP - - /* Restore original ID's internal data. */ - *id_a = id_a_back; - *id_b = id_b_back; - - /* Exception: IDProperties. */ - id_a->properties = id_b_back.properties; - id_b->properties = id_a_back.properties; - - /* Swap will have broken internal references to itself, restore them. */ - BKE_libblock_relink_ex(bmain, id_a, id_b, id_a, ID_REMAP_SKIP_NEVER_NULL_USAGE); - BKE_libblock_relink_ex(bmain, id_b, id_a, id_b, ID_REMAP_SKIP_NEVER_NULL_USAGE); -} - -/** Does *not* set ID->newid pointer. */ -bool id_single_user(bContext *C, ID *id, PointerRNA *ptr, PropertyRNA *prop) -{ - ID *newid = NULL; - PointerRNA idptr; - - if (id) { - /* If property isn't editable, - * we're going to have an extra block hanging around until we save. */ - if (RNA_property_editable(ptr, prop)) { - Main *bmain = CTX_data_main(C); - /* copy animation actions too */ - if (BKE_id_copy_ex(bmain, id, &newid, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS) && newid) { - /* us is 1 by convention with new IDs, but RNA_property_pointer_set - * will also increment it, decrement it here. */ - id_us_min(newid); - - /* assign copy */ - RNA_id_pointer_create(newid, &idptr); - RNA_property_pointer_set(ptr, prop, idptr, NULL); - RNA_property_update(C, ptr, prop); - - /* tag grease pencil data-block and disable onion */ - if (GS(id->name) == ID_GD) { - DEG_id_tag_update(id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - DEG_id_tag_update(newid, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - bGPdata *gpd = (bGPdata *)newid; - gpd->flag &= ~GP_DATA_SHOW_ONIONSKINS; - } - - return true; - } - } - } - - return false; -} - -static int libblock_management_us_plus(void *UNUSED(user_data), - ID *UNUSED(id_self), - ID **id_pointer, - int cb_flag) -{ - if (cb_flag & IDWALK_CB_USER) { - id_us_plus(*id_pointer); - } - if (cb_flag & IDWALK_CB_USER_ONE) { - id_us_ensure_real(*id_pointer); - } - - return IDWALK_RET_NOP; -} - -static int libblock_management_us_min(void *UNUSED(user_data), - ID *UNUSED(id_self), - ID **id_pointer, - int cb_flag) -{ - if (cb_flag & IDWALK_CB_USER) { - id_us_min(*id_pointer); - } - /* We can do nothing in IDWALK_CB_USER_ONE case! */ - - return IDWALK_RET_NOP; -} - -/** Add a 'NO_MAIN' data-block to given main (also sets usercounts of its IDs if needed). */ -void BKE_libblock_management_main_add(Main *bmain, void *idv) -{ - ID *id = idv; - - BLI_assert(bmain != NULL); - if ((id->tag & LIB_TAG_NO_MAIN) == 0) { - return; - } - - if ((id->tag & LIB_TAG_NOT_ALLOCATED) != 0) { - /* We cannot add non-allocated ID to Main! */ - return; - } - - /* We cannot allow non-userrefcounting IDs in Main database! */ - if ((id->tag & LIB_TAG_NO_USER_REFCOUNT) != 0) { - BKE_library_foreach_ID_link(bmain, id, libblock_management_us_plus, NULL, IDWALK_NOP); - } - - ListBase *lb = which_libbase(bmain, GS(id->name)); - BKE_main_lock(bmain); - BLI_addtail(lb, id); - BKE_id_new_name_validate(lb, id, NULL); - /* alphabetic insertion: is in new_id */ - id->tag &= ~(LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT); - bmain->is_memfile_undo_written = false; - BKE_main_unlock(bmain); -} - -/** Remove a data-block from given main (set it to 'NO_MAIN' status). */ -void BKE_libblock_management_main_remove(Main *bmain, void *idv) -{ - ID *id = idv; - - BLI_assert(bmain != NULL); - if ((id->tag & LIB_TAG_NO_MAIN) != 0) { - return; - } - - /* For now, allow userrefcounting IDs to get out of Main - can be handy in some cases... */ - - ListBase *lb = which_libbase(bmain, GS(id->name)); - BKE_main_lock(bmain); - BLI_remlink(lb, id); - id->tag |= LIB_TAG_NO_MAIN; - bmain->is_memfile_undo_written = false; - BKE_main_unlock(bmain); -} - -void BKE_libblock_management_usercounts_set(Main *bmain, void *idv) -{ - ID *id = idv; - - if ((id->tag & LIB_TAG_NO_USER_REFCOUNT) == 0) { - return; - } - - BKE_library_foreach_ID_link(bmain, id, libblock_management_us_plus, NULL, IDWALK_NOP); - id->tag &= ~LIB_TAG_NO_USER_REFCOUNT; -} - -void BKE_libblock_management_usercounts_clear(Main *bmain, void *idv) -{ - ID *id = idv; - - /* We do not allow IDs in Main database to not be userrefcounting. */ - if ((id->tag & LIB_TAG_NO_USER_REFCOUNT) != 0 || (id->tag & LIB_TAG_NO_MAIN) != 0) { - return; - } - - BKE_library_foreach_ID_link(bmain, id, libblock_management_us_min, NULL, IDWALK_NOP); - id->tag |= LIB_TAG_NO_USER_REFCOUNT; -} - -/** - * Clear or set given tags for all ids in listbase (runtime tags). - */ -void BKE_main_id_tag_listbase(ListBase *lb, const int tag, const bool value) -{ - ID *id; - if (value) { - for (id = lb->first; id; id = id->next) { - id->tag |= tag; - } - } - else { - const int ntag = ~tag; - for (id = lb->first; id; id = id->next) { - id->tag &= ntag; - } - } -} - -/** - * Clear or set given tags for all ids of given type in bmain (runtime tags). - */ -void BKE_main_id_tag_idcode(struct Main *mainvar, - const short type, - const int tag, - const bool value) -{ - ListBase *lb = which_libbase(mainvar, type); - - BKE_main_id_tag_listbase(lb, tag, value); -} - -/** - * Clear or set given tags for all ids in bmain (runtime tags). - */ -void BKE_main_id_tag_all(struct Main *mainvar, const int tag, const bool value) -{ - ListBase *lbarray[MAX_LIBARRAY]; - int a; - - a = set_listbasepointers(mainvar, lbarray); - while (a--) { - BKE_main_id_tag_listbase(lbarray[a], tag, value); - } -} - -/** - * Clear or set given flags for all ids in listbase (persistent flags). - */ -void BKE_main_id_flag_listbase(ListBase *lb, const int flag, const bool value) -{ - ID *id; - if (value) { - for (id = lb->first; id; id = id->next) { - id->tag |= flag; - } - } - else { - const int nflag = ~flag; - for (id = lb->first; id; id = id->next) { - id->tag &= nflag; - } - } -} - -/** - * Clear or set given flags for all ids in bmain (persistent flags). - */ -void BKE_main_id_flag_all(Main *bmain, const int flag, const bool value) -{ - ListBase *lbarray[MAX_LIBARRAY]; - int a; - a = set_listbasepointers(bmain, lbarray); - while (a--) { - BKE_main_id_flag_listbase(lbarray[a], flag, value); - } -} - -void BKE_main_id_repair_duplicate_names_listbase(ListBase *lb) -{ - int lb_len = 0; - for (ID *id = lb->first; id; id = id->next) { - if (id->lib == NULL) { - lb_len += 1; - } - } - if (lb_len <= 1) { - return; - } - - /* Fill an array because renaming sorts. */ - ID **id_array = MEM_mallocN(sizeof(*id_array) * lb_len, __func__); - GSet *gset = BLI_gset_str_new_ex(__func__, lb_len); - int i = 0; - for (ID *id = lb->first; id; id = id->next) { - if (id->lib == NULL) { - id_array[i] = id; - i++; - } - } - for (i = 0; i < lb_len; i++) { - if (!BLI_gset_add(gset, id_array[i]->name + 2)) { - BKE_id_new_name_validate(lb, id_array[i], NULL); - } - } - BLI_gset_free(gset, NULL); - MEM_freeN(id_array); -} - -void BKE_main_lib_objects_recalc_all(Main *bmain) -{ - Object *ob; - - /* flag for full recalc */ - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - if (ID_IS_LINKED(ob)) { - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); - } - } - - DEG_id_type_tag(bmain, ID_OB); -} - -/* *********** ALLOC AND FREE ***************** - * - * BKE_libblock_free(ListBase *lb, ID *id ) - * provide a list-basis and data-block, but only ID is read - * - * void *BKE_libblock_alloc(ListBase *lb, type, name) - * inserts in list and returns a new ID - * - * **************************** */ - -/** - * Get allocation size of a given data-block type and optionally allocation name. - */ -size_t BKE_libblock_get_alloc_info(short type, const char **name) -{ -#define CASE_RETURN(id_code, type) \ - case id_code: \ - do { \ - if (name != NULL) { \ - *name = #type; \ - } \ - return sizeof(type); \ - } while (0) - - switch ((ID_Type)type) { - CASE_RETURN(ID_SCE, Scene); - CASE_RETURN(ID_LI, Library); - CASE_RETURN(ID_OB, Object); - CASE_RETURN(ID_ME, Mesh); - CASE_RETURN(ID_CU, Curve); - CASE_RETURN(ID_MB, MetaBall); - CASE_RETURN(ID_MA, Material); - CASE_RETURN(ID_TE, Tex); - CASE_RETURN(ID_IM, Image); - CASE_RETURN(ID_LT, Lattice); - CASE_RETURN(ID_LA, Light); - CASE_RETURN(ID_CA, Camera); - CASE_RETURN(ID_IP, Ipo); - CASE_RETURN(ID_KE, Key); - CASE_RETURN(ID_WO, World); - CASE_RETURN(ID_SCR, bScreen); - CASE_RETURN(ID_VF, VFont); - CASE_RETURN(ID_TXT, Text); - CASE_RETURN(ID_SPK, Speaker); - CASE_RETURN(ID_LP, LightProbe); - CASE_RETURN(ID_SO, bSound); - CASE_RETURN(ID_GR, Collection); - CASE_RETURN(ID_AR, bArmature); - CASE_RETURN(ID_AC, bAction); - CASE_RETURN(ID_NT, bNodeTree); - CASE_RETURN(ID_BR, Brush); - CASE_RETURN(ID_PA, ParticleSettings); - CASE_RETURN(ID_WM, wmWindowManager); - CASE_RETURN(ID_GD, bGPdata); - CASE_RETURN(ID_MC, MovieClip); - CASE_RETURN(ID_MSK, Mask); - CASE_RETURN(ID_LS, FreestyleLineStyle); - CASE_RETURN(ID_PAL, Palette); - CASE_RETURN(ID_PC, PaintCurve); - CASE_RETURN(ID_CF, CacheFile); - CASE_RETURN(ID_WS, WorkSpace); - } - return 0; -#undef CASE_RETURN -} - -/** - * Allocates and returns memory of the right size for the specified block type, - * initialized to zero. - */ -void *BKE_libblock_alloc_notest(short type) -{ - const char *name; - size_t size = BKE_libblock_get_alloc_info(type, &name); - if (size != 0) { - return MEM_callocN(size, name); - } - BLI_assert(!"Request to allocate unknown data type"); - return NULL; -} - -/** - * Allocates and returns a block of the specified type, with the specified name - * (adjusted as necessary to ensure uniqueness), and appended to the specified list. - * The user count is set to 1, all other content (apart from name and links) being - * initialized to zero. - */ -void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int flag) -{ - BLI_assert((flag & LIB_ID_CREATE_NO_ALLOCATE) == 0); - - ID *id = BKE_libblock_alloc_notest(type); - - if (id) { - if ((flag & LIB_ID_CREATE_NO_MAIN) != 0) { - id->tag |= LIB_TAG_NO_MAIN; - } - if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) != 0) { - id->tag |= LIB_TAG_NO_USER_REFCOUNT; - } - - id->icon_id = 0; - *((short *)id->name) = type; - if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { - id->us = 1; - } - if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) { - ListBase *lb = which_libbase(bmain, type); - - BKE_main_lock(bmain); - BLI_addtail(lb, id); - BKE_id_new_name_validate(lb, id, name); - bmain->is_memfile_undo_written = false; - /* alphabetic insertion: is in new_id */ - BKE_main_unlock(bmain); - - /* TODO to be removed from here! */ - if ((flag & LIB_ID_CREATE_NO_DEG_TAG) == 0) { - DEG_id_type_tag(bmain, type); - } - } - else { - BLI_strncpy(id->name + 2, name, sizeof(id->name) - 2); - } - } - - return id; -} - -/** - * Initialize an ID of given type, such that it has valid 'empty' data. - * ID is assumed to be just calloc'ed. - */ -void BKE_libblock_init_empty(ID *id) -{ - /* Note that only ID types that are not valid when filled of zero should have a callback here. */ - switch ((ID_Type)GS(id->name)) { - case ID_SCE: - BKE_scene_init((Scene *)id); - break; - case ID_LI: - /* Nothing to do. */ - break; - case ID_OB: { - Object *ob = (Object *)id; - BKE_object_init(ob, OB_EMPTY); - break; - } - case ID_ME: - BKE_mesh_init((Mesh *)id); - break; - case ID_CU: - BKE_curve_init((Curve *)id, 0); - break; - case ID_MB: - BKE_mball_init((MetaBall *)id); - break; - case ID_MA: - BKE_material_init((Material *)id); - break; - case ID_TE: - BKE_texture_default((Tex *)id); - break; - case ID_IM: - BKE_image_init((Image *)id); - break; - case ID_LT: - BKE_lattice_init((Lattice *)id); - break; - case ID_LA: - BKE_light_init((Light *)id); - break; - case ID_SPK: - BKE_speaker_init((Speaker *)id); - break; - case ID_LP: - BKE_lightprobe_init((LightProbe *)id); - break; - case ID_CA: - BKE_camera_init((Camera *)id); - break; - case ID_WO: - BKE_world_init((World *)id); - break; - case ID_SCR: - /* Nothing to do. */ - break; - case ID_VF: - BKE_vfont_init((VFont *)id); - break; - case ID_TXT: - BKE_text_init((Text *)id); - break; - case ID_SO: - /* Another fuzzy case, think NULLified content is OK here... */ - break; - case ID_GR: - /* Nothing to do. */ - break; - case ID_AR: - /* Nothing to do. */ - break; - case ID_AC: - /* Nothing to do. */ - break; - case ID_NT: - ntreeInitDefault((bNodeTree *)id); - break; - case ID_BR: - BKE_brush_init((Brush *)id); - break; - case ID_PA: - /* Nothing to do. */ - break; - case ID_PC: - /* Nothing to do. */ - break; - case ID_GD: - /* Nothing to do. */ - break; - case ID_MSK: - /* Nothing to do. */ - break; - case ID_LS: - BKE_linestyle_init((FreestyleLineStyle *)id); - break; - case ID_CF: - BKE_cachefile_init((CacheFile *)id); - break; - case ID_KE: - /* Shapekeys are a complex topic too - they depend on their 'user' data type... - * They are not linkable, though, so it should never reach here anyway. */ - BLI_assert(0); - break; - case ID_WM: - /* We should never reach this. */ - BLI_assert(0); - break; - case ID_IP: - /* Should not be needed - animation from lib pre-2.5 is broken anyway. */ - BLI_assert(0); - break; - case ID_PAL: - BKE_palette_init((Palette *)id); - break; - default: - BLI_assert(0); /* Should never reach this point... */ - } -} - -/** - * Generic helper to create a new empty data-block of given type in given \a bmain database. - * - * \param name: can be NULL, in which case we get default name for this ID type. - */ -void *BKE_id_new(Main *bmain, const short type, const char *name) -{ - BLI_assert(bmain != NULL); - - if (name == NULL) { - name = DATA_(BKE_idcode_to_name(type)); - } - - ID *id = BKE_libblock_alloc(bmain, type, name, 0); - BKE_libblock_init_empty(id); - - return id; -} - -/** - * Generic helper to create a new temporary empty data-block of given type, - * *outside* of any Main database. - * - * \param name: can be NULL, in which case we get default name for this ID type. */ -void *BKE_id_new_nomain(const short type, const char *name) -{ - if (name == NULL) { - name = DATA_(BKE_idcode_to_name(type)); - } - - ID *id = BKE_libblock_alloc(NULL, - type, - name, - LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT | - LIB_ID_CREATE_NO_DEG_TAG); - BKE_libblock_init_empty(id); - - return id; -} - -void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int orig_flag) -{ - ID *new_id = *r_newid; - int flag = orig_flag; - - const bool is_private_id_data = (id->flag & LIB_PRIVATE_DATA) != 0; - - BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || bmain != NULL); - BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || (flag & LIB_ID_CREATE_NO_ALLOCATE) == 0); - if (!is_private_id_data) { - /* When we are handling private ID data, we might still want to manage usercounts, even though - * that ID data-block is actually outside of Main... */ - BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 || - (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) != 0); - } - /* Never implicitly copy shapekeys when generating temp data outside of Main database. */ - BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 || (flag & LIB_ID_COPY_SHAPEKEY) == 0); - - /* 'Private ID' data handling. */ - if ((bmain != NULL) && is_private_id_data) { - flag |= LIB_ID_CREATE_NO_MAIN; - } - - /* The id->flag bits to copy over. */ - const int copy_idflag_mask = LIB_PRIVATE_DATA; - - if ((flag & LIB_ID_CREATE_NO_ALLOCATE) != 0) { - /* r_newid already contains pointer to allocated memory. */ - /* TODO do we want to memset(0) whole mem before filling it? */ - BLI_strncpy(new_id->name, id->name, sizeof(new_id->name)); - new_id->us = 0; - new_id->tag |= LIB_TAG_NOT_ALLOCATED | LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT; - /* TODO Do we want/need to copy more from ID struct itself? */ - } - else { - new_id = BKE_libblock_alloc(bmain, GS(id->name), id->name + 2, flag); - } - BLI_assert(new_id != NULL); - - const size_t id_len = BKE_libblock_get_alloc_info(GS(new_id->name), NULL); - const size_t id_offset = sizeof(ID); - if ((int)id_len - (int)id_offset > 0) { /* signed to allow neg result */ /* XXX ????? */ - const char *cp = (const char *)id; - char *cpn = (char *)new_id; - - memcpy(cpn + id_offset, cp + id_offset, id_len - id_offset); - } - - new_id->flag = (new_id->flag & ~copy_idflag_mask) | (id->flag & copy_idflag_mask); - - /* We do not want any handling of usercount in code duplicating the data here, we do that all - * at once in id_copy_libmanagement_cb() at the end. */ - const int copy_data_flag = orig_flag | LIB_ID_CREATE_NO_USER_REFCOUNT; - - if (id->properties) { - new_id->properties = IDP_CopyProperty_ex(id->properties, copy_data_flag); - } - - /* XXX Again... We need a way to control what we copy in a much more refined way. - * We cannot always copy this, some internal copying will die on it! */ - /* For now, upper level code will have to do that itself when required. */ -#if 0 - if (id->override != NULL) { - BKE_override_copy(new_id, id); - } -#endif - - if (id_can_have_animdata(new_id)) { - IdAdtTemplate *iat = (IdAdtTemplate *)new_id; - - /* the duplicate should get a copy of the animdata */ - if ((flag & LIB_ID_COPY_NO_ANIMDATA) == 0) { - /* Note that even though horrors like root nodetrees are not in bmain, the actions they use - * in their anim data *are* in bmain... super-mega-hooray. */ - BLI_assert((copy_data_flag & LIB_ID_COPY_ACTIONS) == 0 || - (copy_data_flag & LIB_ID_CREATE_NO_MAIN) == 0); - iat->adt = BKE_animdata_copy(bmain, iat->adt, copy_data_flag); - } - else { - iat->adt = NULL; - } - } - - if ((flag & LIB_ID_CREATE_NO_DEG_TAG) == 0 && (flag & LIB_ID_CREATE_NO_MAIN) == 0) { - DEG_id_type_tag(bmain, GS(new_id->name)); - } - - *r_newid = new_id; -} - -/* used everywhere in blenkernel */ -void *BKE_libblock_copy(Main *bmain, const ID *id) -{ - ID *idn; - - BKE_libblock_copy_ex(bmain, id, &idn, 0); - - return idn; -} - -/* XXX TODO: get rid of this useless wrapper at some point... */ -void *BKE_libblock_copy_for_localize(const ID *id) -{ - ID *idn; - BKE_libblock_copy_ex(NULL, id, &idn, LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA); - return idn; -} - -void BKE_library_free(Library *lib) -{ - if (lib->packedfile) { - BKE_packedfile_free(lib->packedfile); - } -} - -/* ***************** ID ************************ */ -ID *BKE_libblock_find_name(struct Main *bmain, const short type, const char *name) -{ - ListBase *lb = which_libbase(bmain, type); - BLI_assert(lb != NULL); - return BLI_findstring(lb, name, offsetof(ID, name) + 2); -} - -/** - * Sort given \a id into given \a lb list, using case-insensitive comparison of the id names. - * - * \note All other IDs beside given one are assumed already properly sorted in the list. - * - * \param id_sorting_hint Ignored if NULL. Otherwise, used to check if we can insert \a id - * immediately before or after that pointer. It must always be into given \a lb list. - */ -void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint) -{ -#define ID_SORT_STEP_SIZE 512 - - ID *idtest; - - /* insert alphabetically */ - if (lb->first == lb->last) { - return; - } - - BLI_remlink(lb, id); - - /* Check if we can actually insert id before or after id_sorting_hint, if given. */ - if (id_sorting_hint != NULL && id_sorting_hint != id) { - BLI_assert(BLI_findindex(lb, id_sorting_hint) >= 0); - - ID *id_sorting_hint_next = id_sorting_hint->next; - if (BLI_strcasecmp(id_sorting_hint->name, id->name) < 0 && - (id_sorting_hint_next == NULL || - BLI_strcasecmp(id_sorting_hint_next->name, id->name) > 0)) { - BLI_insertlinkafter(lb, id_sorting_hint, id); - return; - } - - ID *id_sorting_hint_prev = id_sorting_hint->prev; - if (BLI_strcasecmp(id_sorting_hint->name, id->name) > 0 && - (id_sorting_hint_prev == NULL || - BLI_strcasecmp(id_sorting_hint_prev->name, id->name) < 0)) { - BLI_insertlinkbefore(lb, id_sorting_hint, id); - return; - } - } - - void *item_array[ID_SORT_STEP_SIZE]; - int item_array_index; - - /* Step one: We go backward over a whole chunk of items at once, until we find a limit item - * that is lower than, or equal (should never happen!) to the one we want to insert. */ - /* Note: We start from the end, because in typical 'heavy' case (insertion of lots of IDs at - * once using the same base name), newly inserted items will generally be towards the end - * (higher extension numbers). */ - for (idtest = lb->last, item_array_index = ID_SORT_STEP_SIZE - 1; idtest != NULL; - idtest = idtest->prev, item_array_index--) { - item_array[item_array_index] = idtest; - if (item_array_index == 0) { - if ((idtest->lib == NULL && id->lib != NULL) || - BLI_strcasecmp(idtest->name, id->name) <= 0) { - break; - } - item_array_index = ID_SORT_STEP_SIZE; - } - } - - /* Step two: we go forward in the selected chunk of items and check all of them, as we know - * that our target is in there. */ - - /* If we reached start of the list, current item_array_index is off-by-one. - * Otherwise, we already know that it points to an item lower-or-equal-than the one we want to - * insert, no need to redo the check for that one. - * So we can increment that index in any case. */ - for (item_array_index++; item_array_index < ID_SORT_STEP_SIZE; item_array_index++) { - idtest = item_array[item_array_index]; - if ((idtest->lib != NULL && id->lib == NULL) || BLI_strcasecmp(idtest->name, id->name) > 0) { - BLI_insertlinkbefore(lb, idtest, id); - break; - } - } - if (item_array_index == ID_SORT_STEP_SIZE) { - if (idtest == NULL) { - /* If idtest is NULL here, it means that in the first loop, the last comparison was - * performed exactly on the first item of the list, and that it also failed. In other - * words, all items in the list are greater than inserted one, so we can put it at the - * start of the list. */ - /* Note that BLI_insertlinkafter() would have same behavior in that case, but better be - * explicit here. */ - BLI_addhead(lb, id); - } - else { - BLI_insertlinkafter(lb, idtest, id); - } - } - -#undef ID_SORT_STEP_SIZE -} - -/* Note: this code assumes and ensures that the suffix number can never go beyond 1 billion. */ -#define MAX_NUMBER 1000000000 -/* We do not want to get "name.000", so minimal number is 1. */ -#define MIN_NUMBER 1 -/* The maximum value up to which we search for the actual smallest unused number. Beyond that - * value, we will only use the first biggest unused number, without trying to 'fill the gaps' - * in-between already used numbers... */ -#define MAX_NUMBERS_IN_USE 1024 - -/** - * Helper building final ID name from given base_name and number. - * - * If everything goes well and we do generate a valid final ID name in given name, we return true. - * In case the final name would overflow the allowed ID name length, or given number is bigger than - * maximum allowed value, we truncate further the base_name (and given name, which is assumed to - * have the same 'base_name' part), and return false. - */ -static bool id_name_final_build(char *name, char *base_name, size_t base_name_len, int number) -{ - char number_str[11]; /* Dot + nine digits + NULL terminator. */ - size_t number_str_len = BLI_snprintf_rlen(number_str, ARRAY_SIZE(number_str), ".%.3d", number); - - /* If the number would lead to an overflow of the maximum ID name length, we need to truncate - * the base name part and do all the number checks again. */ - if (base_name_len + number_str_len >= MAX_ID_NAME - 2 || number >= MAX_NUMBER) { - if (base_name_len + number_str_len >= MAX_ID_NAME - 2) { - base_name_len = MAX_ID_NAME - 2 - number_str_len - 1; - } - else { - base_name_len--; - } - base_name[base_name_len] = '\0'; - - /* Code above may have generated invalid utf-8 string, due to raw truncation. - * Ensure we get a valid one now. */ - base_name_len -= (size_t)BLI_utf8_invalid_strip(base_name, base_name_len); - - /* Also truncate orig name, and start the whole check again. */ - name[base_name_len] = '\0'; - return false; - } - - /* We have our final number, we can put it in name and exit the function. */ - BLI_strncpy(name + base_name_len, number_str, number_str_len + 1); - return true; -} - -/** - * Check to see if an ID name is already used, and find a new one if so. - * Return true if a new name was created (returned in name). - * - * Normally the ID that's being checked is already in the ListBase, so ID *id points at the new - * entry. The Python Library module needs to know what the name of a data-block will be before it - * is appended, in this case ID *id is NULL. - */ -static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_hint) -{ - BLI_assert(strlen(name) < MAX_ID_NAME - 2); - - *r_id_sorting_hint = NULL; - - ID *id_test = lb->first; - bool is_name_changed = false; - - if (id_test == NULL) { - return is_name_changed; - } - - const short id_type = (short)GS(id_test->name); - - /* Static storage of previous handled ID/name info, used to perform a quicker test and optimize - * creation of huge number of IDs using the same given base name. */ - static char prev_orig_base_name[MAX_ID_NAME - 2] = {0}; - static char prev_final_base_name[MAX_ID_NAME - 2] = {0}; - static short prev_id_type = ID_LINK_PLACEHOLDER; /* Should never exist in actual ID list. */ - static int prev_number = MIN_NUMBER - 1; - - /* Initial test to check whether we can 'shortcut' the more complex loop of the main code below. - * Note that we do not do that for low numbers, as that would prevent using actual smallest - * available number in some cases, and benefits of this special case handling mostly show up with - * high numbers anyway. */ - if (id_type == prev_id_type && prev_number >= MAX_NUMBERS_IN_USE && - prev_number < MAX_NUMBER - 1 && name[0] == prev_final_base_name[0]) { - - /* Get the name and number parts ("name.number"). */ - char base_name[MAX_ID_NAME - 2]; - int number = MIN_NUMBER; - size_t base_name_len = BLI_split_name_num(base_name, &number, name, '.'); - size_t prev_final_base_name_len = strlen(prev_final_base_name); - size_t prev_orig_base_name_len = strlen(prev_orig_base_name); - - if (base_name_len == prev_orig_base_name_len && - STREQLEN(base_name, prev_orig_base_name, prev_orig_base_name_len)) { - /* Once we have ensured given base_name and original previous one are the same, we can check - * that previously used number is actually used, and that next one is free. */ - /* Note that from now on, we only used previous final base name, as it might have been - * truncated from original one due to number suffix length. */ - char final_name[MAX_ID_NAME - 2]; - char prev_final_name[MAX_ID_NAME - 2]; - BLI_strncpy(final_name, prev_final_base_name, prev_final_base_name_len + 1); - BLI_strncpy(prev_final_name, prev_final_base_name, prev_final_base_name_len + 1); - - if (id_name_final_build(final_name, base_name, prev_final_base_name_len, prev_number + 1) && - id_name_final_build(prev_final_name, base_name, prev_final_base_name_len, prev_number)) { - /* We successfully built valid final names of previous and current iterations, - * now we have to ensure that previous final name is indeed used in current ID list, - * and that current one is not. */ - bool is_valid = false; - for (id_test = lb->first; id_test; id_test = id_test->next) { - if (id != id_test && !ID_IS_LINKED(id_test)) { - if (id_test->name[2] == final_name[0] && STREQ(final_name, id_test->name + 2)) { - /* We expect final_name to not be already used, so this is a failure. */ - is_valid = false; - break; - } - /* Previous final name should only be found once in the list, so if it was found - * already, no need to do a string comparison again. */ - if (!is_valid && id_test->name[2] == prev_final_name[0] && - STREQ(prev_final_name, id_test->name + 2)) { - is_valid = true; - *r_id_sorting_hint = id_test; - } - } - } - - if (is_valid) { - /* Only the number changed, prev_orig_base_name, prev_final_base_name and prev_id_type - * remain the same. */ - prev_number++; - - strcpy(name, final_name); - return true; - } - } - } - } - - /* To speed up finding smallest unused number within [0 .. MAX_NUMBERS_IN_USE - 1]. - * We do not bother beyond that point. */ - ID *ids_in_use[MAX_NUMBERS_IN_USE] = {NULL}; - - bool is_first_run = true; - while (true) { - /* Get the name and number parts ("name.number"). */ - char base_name[MAX_ID_NAME - 2]; - int number = MIN_NUMBER; - size_t base_name_len = BLI_split_name_num(base_name, &number, name, '.'); - - /* Store previous original given base name now, as we might alter it later in code below. */ - if (is_first_run) { - strcpy(prev_orig_base_name, base_name); - is_first_run = false; - } - - /* In case we get an insane initial number suffix in given name. */ - /* Note: BLI_split_name_num() cannot return negative numbers, so we do not have to check for - * that here. */ - if (number >= MAX_NUMBER || number < MIN_NUMBER) { - number = MIN_NUMBER; - } - - bool is_orig_name_used = false; - for (id_test = lb->first; id_test; id_test = id_test->next) { - char base_name_test[MAX_ID_NAME - 2]; - int number_test; - if ((id != id_test) && !ID_IS_LINKED(id_test) && (name[0] == id_test->name[2]) && - (id_test->name[base_name_len + 2] == '.' || id_test->name[base_name_len + 2] == '\0') && - STREQLEN(name, id_test->name + 2, base_name_len) && - (BLI_split_name_num(base_name_test, &number_test, id_test->name + 2, '.') == - base_name_len)) { - /* If we did not yet encounter exact same name as the given one, check the remaining parts - * of the strings. */ - if (!is_orig_name_used) { - is_orig_name_used = STREQ(name + base_name_len, id_test->name + 2 + base_name_len); - } - /* Mark number of current id_test name as used, if possible. */ - if (number_test < MAX_NUMBERS_IN_USE) { - ids_in_use[number_test] = id_test; - } - /* Keep track of first largest unused number. */ - if (number <= number_test) { - *r_id_sorting_hint = id_test; - number = number_test + 1; - } - } - } - - /* If there is no double, we are done. - * Note however that name might have been changed (truncated) in a previous iteration already. - */ - if (!is_orig_name_used) { - /* Don't bother updating prev_ static variables here, this case is not supposed to happen - * that often, and is not straight-forward here, so just ignore and reset them to default. */ - prev_id_type = ID_LINK_PLACEHOLDER; - prev_final_base_name[0] = '\0'; - prev_number = MIN_NUMBER - 1; - - /* Value set previously is meaningless in that case. */ - *r_id_sorting_hint = NULL; - - return is_name_changed; - } - - /* Decide which value of number to use, either the smallest unused one if possible, or default - * to the first largest unused one we got from previous loop. */ - for (int i = MIN_NUMBER; i < MAX_NUMBERS_IN_USE; i++) { - if (ids_in_use[i] == NULL) { - number = i; - if (i > 0) { - *r_id_sorting_hint = ids_in_use[i - 1]; - } - break; - } - } - /* At this point, number is either the lowest unused number within - * [MIN_NUMBER .. MAX_NUMBERS_IN_USE - 1], or 1 greater than the largest used number if all - * those low ones are taken. - * We can't be bothered to look for the lowest unused number beyond - * (MAX_NUMBERS_IN_USE - 1). - */ - /* We know for wure that name will be changed. */ - is_name_changed = true; - - /* If id_name_final_build helper returns false, it had to truncate further given name, hence we - * have to go over the whole check again. */ - if (!id_name_final_build(name, base_name, base_name_len, number)) { - /* We have to clear our list of small used numbers before we do the whole check again. */ - memset(ids_in_use, 0, sizeof(ids_in_use)); - - continue; - } - - /* Update prev_ static variables, in case next call is for the same type of IDs and with the - * same initial base name, we can skip a lot of above process. */ - prev_id_type = id_type; - strcpy(prev_final_base_name, base_name); - prev_number = number; - - return is_name_changed; - } - -#undef MAX_NUMBERS_IN_USE -} - -#undef MIN_NUMBER -#undef MAX_NUMBER - -/** - * Ensures given ID has a unique name in given listbase. - * - * Only for local IDs (linked ones already have a unique ID in their library). - * - * \return true if a new name had to be created. - */ -bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname) -{ - bool result; - char name[MAX_ID_NAME - 2]; - - /* if library, don't rename */ - if (ID_IS_LINKED(id)) { - return false; - } - - /* if no name given, use name of current ID - * else make a copy (tname args can be const) */ - if (tname == NULL) { - tname = id->name + 2; - } - - BLI_strncpy(name, tname, sizeof(name)); - - if (name[0] == '\0') { - /* Disallow empty names. */ - BLI_strncpy(name, DATA_(BKE_idcode_to_name(GS(id->name))), sizeof(name)); - } - else { - /* disallow non utf8 chars, - * the interface checks for this but new ID's based on file names don't */ - BLI_utf8_invalid_strip(name, strlen(name)); - } - - ID *id_sorting_hint = NULL; - result = check_for_dupid(lb, id, name, &id_sorting_hint); - strcpy(id->name + 2, name); - - /* This was in 2.43 and previous releases - * however all data in blender should be sorted, not just duplicate names - * sorting should not hurt, but noting just in case it alters the way other - * functions work, so sort every time. */ -#if 0 - if (result) { - id_sort_by_name(lb, id, id_sorting_hint); - } -#endif - - id_sort_by_name(lb, id, id_sorting_hint); - - return result; -} - -/** - * Pull an ID out of a library (make it local). Only call this for IDs that - * don't have other library users. - */ -void id_clear_lib_data_ex(Main *bmain, ID *id, const bool id_in_mainlist) -{ - bNodeTree *ntree = NULL; - Key *key = NULL; - - BKE_id_lib_local_paths(bmain, id->lib, id); - - id_fake_user_clear(id); - - id->lib = NULL; - id->tag &= ~(LIB_TAG_INDIRECT | LIB_TAG_EXTERN); - id->flag &= ~LIB_INDIRECT_WEAK_LINK; - if (id_in_mainlist) { - if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL)) { - bmain->is_memfile_undo_written = false; - } - } - - /* Internal bNodeTree blocks inside data-blocks also stores id->lib, - * make sure this stays in sync. */ - if ((ntree = ntreeFromID(id))) { - id_clear_lib_data_ex(bmain, &ntree->id, false); /* Datablocks' nodetree is never in Main. */ - } - - /* Same goes for shapekeys. */ - if ((key = BKE_key_from_id(id))) { - id_clear_lib_data_ex(bmain, &key->id, id_in_mainlist); /* sigh, why are keys in Main? */ - } -} - -void id_clear_lib_data(Main *bmain, ID *id) -{ - id_clear_lib_data_ex(bmain, id, true); -} - -/* next to indirect usage in read/writefile also in editobject.c scene.c */ -void BKE_main_id_clear_newpoins(Main *bmain) -{ - ID *id; - - FOREACH_MAIN_ID_BEGIN (bmain, id) { - id->newid = NULL; - id->tag &= ~LIB_TAG_NEW; - } - FOREACH_MAIN_ID_END; -} - -static int id_refcount_recompute_callback(void *user_data, - struct ID *UNUSED(id_self), - struct ID **id_pointer, - int cb_flag) -{ - const bool do_linked_only = (bool)POINTER_AS_INT(user_data); - - if (*id_pointer == NULL) { - return IDWALK_RET_NOP; - } - if (do_linked_only && !ID_IS_LINKED(*id_pointer)) { - return IDWALK_RET_NOP; - } - - if (cb_flag & IDWALK_CB_USER) { - /* Do not touch to direct/indirect linked status here... */ - id_us_plus_no_lib(*id_pointer); - } - if (cb_flag & IDWALK_CB_USER_ONE) { - id_us_ensure_real(*id_pointer); - } - - return IDWALK_RET_NOP; -} - -void BKE_main_id_refcount_recompute(struct Main *bmain, const bool do_linked_only) -{ - ID *id; - - FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (!ID_IS_LINKED(id) && do_linked_only) { - continue; - } - id->us = ID_FAKE_USERS(id); - /* Note that we keep EXTRAUSER tag here, since some UI users may define it too... */ - if (id->tag & LIB_TAG_EXTRAUSER) { - id->tag &= ~(LIB_TAG_EXTRAUSER | LIB_TAG_EXTRAUSER_SET); - id_us_ensure_real(id); - } - } - FOREACH_MAIN_ID_END; - - /* Go over whole Main database to re-generate proper usercounts... */ - FOREACH_MAIN_ID_BEGIN (bmain, id) { - BKE_library_foreach_ID_link(bmain, - id, - id_refcount_recompute_callback, - POINTER_FROM_INT((int)do_linked_only), - IDWALK_READONLY); - } - FOREACH_MAIN_ID_END; -} - -static void library_make_local_copying_check(ID *id, - GSet *loop_tags, - MainIDRelations *id_relations, - GSet *done_ids) -{ - if (BLI_gset_haskey(done_ids, id)) { - return; /* Already checked, nothing else to do. */ - } - - MainIDRelationsEntry *entry = BLI_ghash_lookup(id_relations->id_used_to_user, id); - BLI_gset_insert(loop_tags, id); - for (; entry != NULL; entry = entry->next) { - - /* Used_to_user stores ID pointer, not pointer to ID pointer. */ - ID *par_id = (ID *)entry->id_pointer; - - /* Our oh-so-beloved 'from' pointers... */ - if (entry->usage_flag & IDWALK_CB_LOOPBACK) { - /* We totally disregard Object->proxy_from 'usage' here, - * this one would only generate fake positives. */ - if (GS(par_id->name) == ID_OB) { - BLI_assert(((Object *)par_id)->proxy_from == (Object *)id); - continue; - } - - /* Shapekeys are considered 'private' to their owner ID here, and never tagged - * (since they cannot be linked), * so we have to switch effective parent to their owner. - */ - if (GS(par_id->name) == ID_KE) { - par_id = ((Key *)par_id)->from; - } - } - - if (par_id->lib == NULL) { - /* Local user, early out to avoid some gset querying... */ - continue; - } - if (!BLI_gset_haskey(done_ids, par_id)) { - if (BLI_gset_haskey(loop_tags, par_id)) { - /* We are in a 'dependency loop' of IDs, this does not say us anything, skip it. - * Note that this is the situation that can lead to archipelagoes of linked data-blocks - * (since all of them have non-local users, they would all be duplicated, - * leading to a loop of unused linked data-blocks that cannot be freed since they all use - * each other...). */ - continue; - } - /* Else, recursively check that user ID. */ - library_make_local_copying_check(par_id, loop_tags, id_relations, done_ids); - } - - if (par_id->tag & LIB_TAG_DOIT) { - /* This user will be fully local in future, so far so good, - * nothing to do here but check next user. */ - } - else { - /* This user won't be fully local in future, so current ID won't be either. - * And we are done checking it. */ - id->tag &= ~LIB_TAG_DOIT; - break; - } - } - BLI_gset_add(done_ids, id); - BLI_gset_remove(loop_tags, id, NULL); -} - -/** - * Make linked data-blocks local. - * - * \param bmain: Almost certainly global main. - * \param lib: If not NULL, only make local data-blocks from this library. - * \param untagged_only: If true, only make local data-blocks not tagged with - * LIB_TAG_PRE_EXISTING. - * \param set_fake: If true, set fake user on all localized data-blocks - * (except group and objects ones). - */ -/* Note: Old (2.77) version was simply making (tagging) data-blocks as local, - * without actually making any check whether they were also indirectly used or not... - * - * Current version uses regular id_make_local callback, with advanced pre-processing step to - * detect all cases of IDs currently indirectly used, but which will be used by local data only - * once this function is finished. This allows to avoid any unneeded duplication of IDs, and - * hence all time lost afterwards to remove orphaned linked data-blocks... - */ -void BKE_library_make_local(Main *bmain, - const Library *lib, - GHash *old_to_new_ids, - const bool untagged_only, - const bool set_fake) -{ - ListBase *lbarray[MAX_LIBARRAY]; - - LinkNode *todo_ids = NULL; - LinkNode *copied_ids = NULL; - MemArena *linklist_mem = BLI_memarena_new(512 * sizeof(*todo_ids), __func__); - - GSet *done_ids = BLI_gset_ptr_new(__func__); - -#ifdef DEBUG_TIME - TIMEIT_START(make_local); -#endif - - BKE_main_relations_create(bmain); - -#ifdef DEBUG_TIME - printf("Pre-compute current ID relations: Done.\n"); - TIMEIT_VALUE_PRINT(make_local); -#endif - - /* Step 1: Detect data-blocks to make local. */ - for (int a = set_listbasepointers(bmain, lbarray); a--;) { - ID *id = lbarray[a]->first; - - /* Do not explicitly make local non-linkable IDs (shapekeys, in fact), - * they are assumed to be handled by real data-blocks responsible of them. */ - const bool do_skip = (id && !BKE_idcode_is_linkable(GS(id->name))); - - for (; id; id = id->next) { - ID *ntree = (ID *)ntreeFromID(id); - - id->tag &= ~LIB_TAG_DOIT; - if (ntree != NULL) { - ntree->tag &= ~LIB_TAG_DOIT; - } - - if (id->lib == NULL) { - id->tag &= ~(LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW); - id->flag &= ~LIB_INDIRECT_WEAK_LINK; - } - /* The check on the fourth line (LIB_TAG_PRE_EXISTING) is done so it's possible to tag data - * you don't want to be made local, used for appending data, - * so any libdata already linked wont become local (very nasty - * to discover all your links are lost after appending). - * Also, never ever make proxified objects local, would not make any sense. */ - /* Some more notes: - * - Shapekeys are never tagged here (since they are not linkable). - * - Nodetrees used in materials etc. have to be tagged manually, - * since they do not exist in Main (!). - * This is ok-ish on 'make local' side of things - * (since those are handled by their 'owner' IDs), - * but complicates slightly the pre-processing of relations between IDs at step 2... */ - else if (!do_skip && id->tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW) && - ELEM(lib, NULL, id->lib) && - !(GS(id->name) == ID_OB && ((Object *)id)->proxy_from != NULL) && - ((untagged_only == false) || !(id->tag & LIB_TAG_PRE_EXISTING))) { - BLI_linklist_prepend_arena(&todo_ids, id, linklist_mem); - id->tag |= LIB_TAG_DOIT; - - /* Tag those nasty non-ID nodetrees, - * but do not add them to todo list, making them local is handled by 'owner' ID. - * This is needed for library_make_local_copying_check() to work OK at step 2. */ - if (ntree != NULL) { - ntree->tag |= LIB_TAG_DOIT; - } - } - else { - /* Linked ID that we won't be making local (needed info for step 2, see below). */ - BLI_gset_add(done_ids, id); - } - } - } - -#ifdef DEBUG_TIME - printf("Step 1: Detect data-blocks to make local: Done.\n"); - TIMEIT_VALUE_PRINT(make_local); -#endif - - /* Step 2: Check which data-blocks we can directly make local - * (because they are only used by already, or future, local data), - * others will need to be duplicated. */ - GSet *loop_tags = BLI_gset_ptr_new(__func__); - for (LinkNode *it = todo_ids; it; it = it->next) { - library_make_local_copying_check(it->link, loop_tags, bmain->relations, done_ids); - BLI_assert(BLI_gset_len(loop_tags) == 0); - } - BLI_gset_free(loop_tags, NULL); - BLI_gset_free(done_ids, NULL); - - /* Next step will most likely add new IDs, better to get rid of this mapping now. */ - BKE_main_relations_free(bmain); - -#ifdef DEBUG_TIME - printf("Step 2: Check which data-blocks we can directly make local: Done.\n"); - TIMEIT_VALUE_PRINT(make_local); -#endif - - /* Step 3: Make IDs local, either directly (quick and simple), or using generic process, - * which involves more complex checks and might instead - * create a local copy of original linked ID. */ - for (LinkNode *it = todo_ids, *it_next; it; it = it_next) { - it_next = it->next; - ID *id = it->link; - - if (id->tag & LIB_TAG_DOIT) { - /* We know all users of this object are local or will be made fully local, even if - * currently there are some indirect usages. So instead of making a copy that we'll likely - * get rid of later, directly make that data block local. - * Saves a tremendous amount of time with complex scenes... */ - id_clear_lib_data_ex(bmain, id, true); - BKE_id_expand_local(bmain, id); - id->tag &= ~LIB_TAG_DOIT; - - if (GS(id->name) == ID_OB) { - BKE_rigidbody_ensure_local_object(bmain, (Object *)id); - } - } - else { - /* In this specific case, we do want to make ID local even if it has no local usage yet... - */ - if (GS(id->name) == ID_OB) { - /* Special case for objects because we don't want proxy pointers to be - * cleared yet. This will happen down the road in this function. - */ - BKE_object_make_local_ex(bmain, (Object *)id, true, false); - } - else { - id_make_local(bmain, id, false, true); - } - - if (id->newid) { - if (GS(id->newid->name) == ID_OB) { - BKE_rigidbody_ensure_local_object(bmain, (Object *)id->newid); - } - - /* Reuse already allocated LinkNode (transferring it from todo_ids to copied_ids). */ - BLI_linklist_prepend_nlink(&copied_ids, id, it); - } - } - - if (set_fake) { - if (!ELEM(GS(id->name), ID_OB, ID_GR)) { - /* do not set fake user on objects, groups (instancing) */ - id_fake_user_set(id); - } - } - } - -#ifdef DEBUG_TIME - printf("Step 3: Make IDs local: Done.\n"); - TIMEIT_VALUE_PRINT(make_local); -#endif - - /* At this point, we are done with directly made local IDs. - * Now we have to handle duplicated ones, since their - * remaining linked original counterpart may not be needed anymore... */ - todo_ids = NULL; - - /* Step 4: We have to remap local usages of old (linked) ID to new (local) - * ID in a separated loop, - * as lbarray ordering is not enough to ensure us we did catch all dependencies - * (e.g. if making local a parent object before its child...). See T48907. */ - /* TODO This is now the biggest step by far (in term of processing time). - * We may be able to gain here by using again main->relations mapping, but... - * this implies BKE_libblock_remap & co to be able to update main->relations on the fly. - * Have to think about it a bit more, and see whether new code is OK first, anyway. */ - for (LinkNode *it = copied_ids; it; it = it->next) { - ID *id = it->link; - - BLI_assert(id->newid != NULL); - BLI_assert(id->lib != NULL); - - BKE_libblock_remap(bmain, id, id->newid, ID_REMAP_SKIP_INDIRECT_USAGE); - if (old_to_new_ids) { - BLI_ghash_insert(old_to_new_ids, id, id->newid); - } - - /* Special hack for groups... Thing is, since we can't instantiate them here, we need to - * ensure they remain 'alive' (only instantiation is a real group 'user'... *sigh* See - * T49722. */ - if (GS(id->name) == ID_GR && (id->tag & LIB_TAG_INDIRECT) != 0) { - id_us_ensure_real(id->newid); - } - } - -#ifdef DEBUG_TIME - printf("Step 4: Remap local usages of old (linked) ID to new (local) ID: Done.\n"); - TIMEIT_VALUE_PRINT(make_local); -#endif - - /* Step 5: proxy 'remapping' hack. */ - for (LinkNode *it = copied_ids; it; it = it->next) { - ID *id = it->link; - - /* Attempt to re-link copied proxy objects. This allows appending of an entire scene - * from another blend file into this one, even when that blend file contains proxified - * armatures that have local references. Since the proxified object needs to be linked - * (not local), this will only work when the "Localize all" checkbox is disabled. - * TL;DR: this is a dirty hack on top of an already weak feature (proxies). */ - if (GS(id->name) == ID_OB && ((Object *)id)->proxy != NULL) { - Object *ob = (Object *)id; - Object *ob_new = (Object *)id->newid; - bool is_local = false, is_lib = false; - - /* Proxies only work when the proxified object is linked-in from a library. */ - if (ob->proxy->id.lib == NULL) { - CLOG_WARN(&LOG, - "proxy object %s will loose its link to %s, because the " - "proxified object is local.", - id->newid->name, - ob->proxy->id.name); - continue; - } - - BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); - - /* We can only switch the proxy'ing to a made-local proxy if it is no longer - * referred to from a library. Not checking for local use; if new local proxy - * was not used locally would be a nasty bug! */ - if (is_local || is_lib) { - CLOG_WARN(&LOG, - "made-local proxy object %s will loose its link to %s, " - "because the linked-in proxy is referenced (is_local=%i, is_lib=%i).", - id->newid->name, - ob->proxy->id.name, - is_local, - is_lib); - } - else { - /* we can switch the proxy'ing from the linked-in to the made-local proxy. - * BKE_object_make_proxy() shouldn't be used here, as it allocates memory that - * was already allocated by BKE_object_make_local_ex() (which called BKE_object_copy). */ - ob_new->proxy = ob->proxy; - ob_new->proxy_group = ob->proxy_group; - ob_new->proxy_from = ob->proxy_from; - ob_new->proxy->proxy_from = ob_new; - ob->proxy = ob->proxy_from = ob->proxy_group = NULL; - } - } - } - -#ifdef DEBUG_TIME - printf("Step 5: Proxy 'remapping' hack: Done.\n"); - TIMEIT_VALUE_PRINT(make_local); -#endif - - /* This is probably more of a hack than something we should do here, but... - * Issue is, the whole copying + remapping done in complex cases above may leave pose-channels - * of armatures in complete invalid state (more precisely, the bone pointers of the - * pose-channels - very crappy cross-data-blocks relationship), se we tag it to be fully - * recomputed, but this does not seems to be enough in some cases, and evaluation code ends up - * trying to evaluate a not-yet-updated armature object's deformations. - * Try "make all local" in 04_01_H.lighting.blend from Agent327 without this, e.g. */ - for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) { - if (ob->data != NULL && ob->type == OB_ARMATURE && ob->pose != NULL && - ob->pose->flag & POSE_RECALC) { - BKE_pose_rebuild(bmain, ob, ob->data, true); - } - } - -#ifdef DEBUG_TIME - printf("Hack: Forcefully rebuild armature object poses: Done.\n"); - TIMEIT_VALUE_PRINT(make_local); -#endif - - BKE_main_id_clear_newpoins(bmain); - BLI_memarena_free(linklist_mem); - -#ifdef DEBUG_TIME - printf("Cleanup and finish: Done.\n"); - TIMEIT_END(make_local); -#endif -} - -/** - * Use after setting the ID's name - * When name exists: call 'new_id' - */ -void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) -{ - ListBase *lb; - ID *idtest; - - lb = which_libbase(bmain, GS(name)); - if (lb == NULL) { - return; - } - - /* search for id */ - idtest = BLI_findstring(lb, name + 2, offsetof(ID, name) + 2); - if (idtest != NULL) { - /* BKE_id_new_name_validate also takes care of sorting. */ - BKE_id_new_name_validate(lb, idtest, NULL); - bmain->is_memfile_undo_written = false; - } -} - -/** - * Sets the name of a block to name, suitably adjusted for uniqueness. - */ -void BKE_libblock_rename(Main *bmain, ID *id, const char *name) -{ - ListBase *lb = which_libbase(bmain, GS(id->name)); - if (BKE_id_new_name_validate(lb, id, name)) { - bmain->is_memfile_undo_written = false; - } -} - -/** - * Generate full name of the data-block (without ID code, but with library if any). - * - * \note Result is unique to a given ID type in a given Main database. - * - * \param name: An allocated string of minimal length #MAX_ID_FULL_NAME, - * will be filled with generated string. - */ -void BKE_id_full_name_get(char name[MAX_ID_FULL_NAME], const ID *id) -{ - strcpy(name, id->name + 2); - - if (id->lib != NULL) { - const size_t idname_len = strlen(id->name + 2); - const size_t libname_len = strlen(id->lib->id.name + 2); - - name[idname_len] = ' '; - name[idname_len + 1] = '['; - strcpy(name + idname_len + 2, id->lib->id.name + 2); - name[idname_len + 2 + libname_len] = ']'; - name[idname_len + 2 + libname_len + 1] = '\0'; - } -} - -/** - * Generate full name of the data-block (without ID code, but with library if any), - * with a 3-character prefix prepended indicating whether it comes from a library, - * is overriding, has a fake or no user, etc. - * - * \note Result is unique to a given ID type in a given Main database. - * - * \param name: An allocated string of minimal length #MAX_ID_FULL_NAME_UI, - * will be filled with generated string. - */ -void BKE_id_full_name_ui_prefix_get(char name[MAX_ID_FULL_NAME_UI], const ID *id) -{ - name[0] = id->lib ? (ID_MISSING(id) ? 'M' : 'L') : ID_IS_OVERRIDE_LIBRARY(id) ? 'O' : ' '; - name[1] = (id->flag & LIB_FAKEUSER) ? 'F' : ((id->us == 0) ? '0' : ' '); - name[2] = ' '; - - BKE_id_full_name_get(name + 3, id); -} - -/** - * Generate a concatenation of ID name (including two-chars type code) and its lib name, if any. - * - * \return A unique allocated string key for any ID in the whole Main database. - */ -char *BKE_id_to_unique_string_key(const struct ID *id) -{ - if (id->lib == NULL) { - return BLI_strdup(id->name); - } - else { - /* Prefix with an ascii character in the range of 32..96 (visible) - * this ensures we can't have a library ID pair that collide. - * Where 'LIfooOBbarOBbaz' could be ('LIfoo, OBbarOBbaz') or ('LIfooOBbar', 'OBbaz'). */ - const char ascii_len = strlen(id->lib->id.name + 2) + 32; - return BLI_sprintfN("%c%s%s", ascii_len, id->lib->id.name, id->name); - } -} - -void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath) -{ - /* in some cases this is used to update the absolute path from the - * relative */ - if (lib->name != filepath) { - BLI_strncpy(lib->name, filepath, sizeof(lib->name)); - } - - BLI_strncpy(lib->filepath, filepath, sizeof(lib->filepath)); - - /* not essential but set filepath is an absolute copy of value which - * is more useful if its kept in sync */ - if (BLI_path_is_rel(lib->filepath)) { - /* note that the file may be unsaved, in this case, setting the - * filepath on an indirectly linked path is not allowed from the - * outliner, and its not really supported but allow from here for now - * since making local could cause this to be directly linked - campbell - */ - /* Never make paths relative to parent lib - reading code (blenloader) always set *all* - * lib->name relative to current main, not to their parent for indirectly linked ones. */ - const char *basepath = BKE_main_blendfile_path(bmain); - BLI_path_abs(lib->filepath, basepath); - } -} - -void BKE_id_tag_set_atomic(ID *id, int tag) -{ - atomic_fetch_and_or_int32(&id->tag, tag); -} - -void BKE_id_tag_clear_atomic(ID *id, int tag) -{ - atomic_fetch_and_and_int32(&id->tag, ~tag); -} - -/** - * Check that given ID pointer actually is in G_MAIN. - * Main intended use is for debug asserts in places we cannot easily get rid of G_Main... - */ -bool BKE_id_is_in_global_main(ID *id) -{ - /* We do not want to fail when id is NULL here, even though this is a bit strange behavior... - */ - return (id == NULL || BLI_findindex(which_libbase(G_MAIN, GS(id->name)), id) != -1); -} - -/************************* Datablock order in UI **************************/ - -static int *id_order_get(ID *id) -{ - /* Only for workspace tabs currently. */ - switch (GS(id->name)) { - case ID_WS: - return &((WorkSpace *)id)->order; - default: - return NULL; - } -} - -static int id_order_compare(const void *a, const void *b) -{ - ID *id_a = ((LinkData *)a)->data; - ID *id_b = ((LinkData *)b)->data; - - int *order_a = id_order_get(id_a); - int *order_b = id_order_get(id_b); - - if (order_a && order_b) { - if (*order_a < *order_b) { - return -1; - } - else if (*order_a > *order_b) { - return 1; - } - } - - return strcmp(id_a->name, id_b->name); -} - -/** - * Returns ordered list of data-blocks for display in the UI. - * Result is list of LinkData of IDs that must be freed. - */ -void BKE_id_ordered_list(ListBase *ordered_lb, const ListBase *lb) -{ - BLI_listbase_clear(ordered_lb); - - for (ID *id = lb->first; id; id = id->next) { - BLI_addtail(ordered_lb, BLI_genericNodeN(id)); - } - - BLI_listbase_sort(ordered_lb, id_order_compare); - - int num = 0; - for (LinkData *link = ordered_lb->first; link; link = link->next) { - int *order = id_order_get(link->data); - if (order) { - *order = num++; - } - } -} - -/** - * Reorder ID in the list, before or after the "relative" ID. - */ -void BKE_id_reorder(const ListBase *lb, ID *id, ID *relative, bool after) -{ - int *id_order = id_order_get(id); - int relative_order; - - if (relative) { - relative_order = *id_order_get(relative); - } - else { - relative_order = (after) ? BLI_listbase_count(lb) : 0; - } - - if (after) { - /* Insert after. */ - for (ID *other = lb->first; other; other = other->next) { - int *order = id_order_get(other); - if (*order > relative_order) { - (*order)++; - } - } - - *id_order = relative_order + 1; - } - else { - /* Insert before. */ - for (ID *other = lb->first; other; other = other->next) { - int *order = id_order_get(other); - if (*order < relative_order) { - (*order)--; - } - } - - *id_order = relative_order - 1; - } -} diff --git a/source/blender/blenkernel/intern/library_override.c b/source/blender/blenkernel/intern/library_override.c deleted file mode 100644 index a8cc37973f2..00000000000 --- a/source/blender/blenkernel/intern/library_override.c +++ /dev/null @@ -1,954 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 by Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup bke - */ - -#include -#include - -#include "MEM_guardedalloc.h" - -#include "DNA_ID.h" -#include "DNA_object_types.h" - -#include "DEG_depsgraph.h" - -#include "BKE_armature.h" -#include "BKE_library.h" -#include "BKE_library_override.h" -#include "BKE_library_remap.h" -#include "BKE_main.h" - -#include "BLI_utildefines.h" -#include "BLI_ghash.h" -#include "BLI_listbase.h" -#include "BLI_string.h" - -#include "RNA_access.h" -#include "RNA_types.h" - -#include "PIL_time.h" -#include "PIL_time_utildefines.h" - -#define OVERRIDE_AUTO_CHECK_DELAY 0.2 /* 200ms between auto-override checks. */ - -static void bke_override_property_copy(IDOverrideLibraryProperty *op_dst, - IDOverrideLibraryProperty *op_src); -static void bke_override_property_operation_copy(IDOverrideLibraryPropertyOperation *opop_dst, - IDOverrideLibraryPropertyOperation *opop_src); - -static void bke_override_property_clear(IDOverrideLibraryProperty *op); -static void bke_override_property_operation_clear(IDOverrideLibraryPropertyOperation *opop); - -/* Temp, for until library override is ready and tested enough to go 'public', - * we hide it by default in UI and such. */ -static bool _override_library_enabled = true; - -void BKE_override_library_enable(const bool do_enable) -{ - _override_library_enabled = do_enable; -} - -bool BKE_override_library_is_enabled() -{ - return _override_library_enabled; -} - -/** Initialize empty overriding of \a reference_id by \a local_id. */ -IDOverrideLibrary *BKE_override_library_init(ID *local_id, ID *reference_id) -{ - /* If reference_id is NULL, we are creating an override template for purely local data. - * Else, reference *must* be linked data. */ - BLI_assert(reference_id == NULL || reference_id->lib != NULL); - BLI_assert(local_id->override_library == NULL); - - ID *ancestor_id; - for (ancestor_id = reference_id; ancestor_id != NULL && ancestor_id->override_library != NULL && - ancestor_id->override_library->reference != NULL; - ancestor_id = ancestor_id->override_library->reference) { - /* pass */ - } - - if (ancestor_id != NULL && ancestor_id->override_library != NULL) { - /* Original ID has a template, use it! */ - BKE_override_library_copy(local_id, ancestor_id); - if (local_id->override_library->reference != reference_id) { - id_us_min(local_id->override_library->reference); - local_id->override_library->reference = reference_id; - id_us_plus(local_id->override_library->reference); - } - return local_id->override_library; - } - - /* Else, generate new empty override. */ - local_id->override_library = MEM_callocN(sizeof(*local_id->override_library), __func__); - local_id->override_library->reference = reference_id; - id_us_plus(local_id->override_library->reference); - local_id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK; - /* TODO do we want to add tag or flag to referee to mark it as such? */ - return local_id->override_library; -} - -/** Deep copy of a whole override from \a src_id to \a dst_id. */ -void BKE_override_library_copy(ID *dst_id, const ID *src_id) -{ - BLI_assert(src_id->override_library != NULL); - - if (dst_id->override_library != NULL) { - if (src_id->override_library == NULL) { - BKE_override_library_free(&dst_id->override_library, true); - return; - } - else { - BKE_override_library_clear(dst_id->override_library, true); - } - } - else if (src_id->override_library == NULL) { - return; - } - else { - BKE_override_library_init(dst_id, NULL); - } - - /* Source is already overriding data, we copy it but reuse its reference for dest ID. - * otherwise, source is only an override template, it then becomes reference of dest ID. */ - dst_id->override_library->reference = src_id->override_library->reference ? - src_id->override_library->reference : - (ID *)src_id; - id_us_plus(dst_id->override_library->reference); - - BLI_duplicatelist(&dst_id->override_library->properties, &src_id->override_library->properties); - for (IDOverrideLibraryProperty *op_dst = dst_id->override_library->properties.first, - *op_src = src_id->override_library->properties.first; - op_dst; - op_dst = op_dst->next, op_src = op_src->next) { - bke_override_property_copy(op_dst, op_src); - } - - dst_id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK; -} - -/** Clear any overriding data from given \a override. */ -void BKE_override_library_clear(IDOverrideLibrary *override, const bool do_id_user) -{ - BLI_assert(override != NULL); - - if (override->runtime != NULL) { - BLI_ghash_clear(override->runtime, NULL, NULL); - } - - for (IDOverrideLibraryProperty *op = override->properties.first; op; op = op->next) { - bke_override_property_clear(op); - } - BLI_freelistN(&override->properties); - - if (do_id_user) { - id_us_min(override->reference); - /* override->storage should never be refcounted... */ - } -} - -/** Free given \a override. */ -void BKE_override_library_free(struct IDOverrideLibrary **override, const bool do_id_user) -{ - BLI_assert(*override != NULL); - - if ((*override)->runtime != NULL) { - BLI_ghash_free((*override)->runtime, NULL, NULL); - (*override)->runtime = NULL; - } - - BKE_override_library_clear(*override, do_id_user); - MEM_freeN(*override); - *override = NULL; -} - -static ID *override_library_create_from(Main *bmain, ID *reference_id) -{ - ID *local_id; - - if (!BKE_id_copy(bmain, reference_id, (ID **)&local_id)) { - return NULL; - } - id_us_min(local_id); - - BKE_override_library_init(local_id, reference_id); - local_id->override_library->flag |= OVERRIDE_LIBRARY_AUTO; - - return local_id; -} - -/** Create an overridden local copy of linked reference. */ -ID *BKE_override_library_create_from_id(Main *bmain, ID *reference_id, const bool do_tagged_remap) -{ - BLI_assert(reference_id != NULL); - BLI_assert(reference_id->lib != NULL); - - ID *local_id = override_library_create_from(bmain, reference_id); - - if (do_tagged_remap) { - ID *other_id; - FOREACH_MAIN_ID_BEGIN (bmain, other_id) { - if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib == NULL) { - /* Note that using ID_REMAP_SKIP_INDIRECT_USAGE below is superfluous, as we only remap - * local IDs usages anyway... */ - BKE_libblock_relink_ex(bmain, - other_id, - reference_id, - local_id, - ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY); - } - } - FOREACH_MAIN_ID_END; - } - - return local_id; -} - -/** - * Create overridden local copies of all tagged data-blocks in given Main. - * - * \note Set id->newid of overridden libs with newly created overrides, - * caller is responsible to clean those pointers before/after usage as needed. - * - * \note By default, it will only remap newly created local overriding data-blocks between - * themselves, to avoid 'enforcing' those overrides into all other usages of the linked data in - * main. You can add more local IDs to be remapped to use new overriding ones by setting their - * LIB_TAG_DOIT tag. - * - * \return \a true on success, \a false otherwise. - */ -bool BKE_override_library_create_from_tag(Main *bmain) -{ - ID *reference_id; - bool ret = true; - - ListBase todo_ids = {NULL}; - LinkData *todo_id_iter; - - /* Get all IDs we want to override. */ - FOREACH_MAIN_ID_BEGIN (bmain, reference_id) { - if ((reference_id->tag & LIB_TAG_DOIT) != 0 && reference_id->lib != NULL) { - todo_id_iter = MEM_callocN(sizeof(*todo_id_iter), __func__); - todo_id_iter->data = reference_id; - BLI_addtail(&todo_ids, todo_id_iter); - } - } - FOREACH_MAIN_ID_END; - - /* Override the IDs. */ - for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) { - reference_id = todo_id_iter->data; - if ((reference_id->newid = override_library_create_from(bmain, reference_id)) == NULL) { - ret = false; - } - else { - /* We also tag the new IDs so that in next step we can remap their pointers too. */ - reference_id->newid->tag |= LIB_TAG_DOIT; - } - } - - /* Only remap new local ID's pointers, we don't want to force our new overrides onto our whole - * existing linked IDs usages. */ - for (todo_id_iter = todo_ids.first; todo_id_iter != NULL; todo_id_iter = todo_id_iter->next) { - ID *other_id; - reference_id = todo_id_iter->data; - - if (reference_id->newid == NULL) { - continue; - } - - /* Still checking the whole Main, that way we can tag other local IDs as needing to be remapped - * to use newly created overriding IDs, if needed. */ - FOREACH_MAIN_ID_BEGIN (bmain, other_id) { - if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib == NULL) { - ID *local_id = reference_id->newid; - /* Note that using ID_REMAP_SKIP_INDIRECT_USAGE below is superfluous, as we only remap - * local IDs usages anyway... */ - BKE_libblock_relink_ex(bmain, - other_id, - reference_id, - local_id, - ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_OVERRIDE_LIBRARY); - } - } - FOREACH_MAIN_ID_END; - } - - BLI_freelistN(&todo_ids); - - return ret; -} - -/* We only build override GHash on request. */ -BLI_INLINE IDOverrideLibraryRuntime *override_library_rna_path_mapping_ensure( - IDOverrideLibrary *override) -{ - if (override->runtime == NULL) { - override->runtime = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__); - for (IDOverrideLibraryProperty *op = override->properties.first; op != NULL; op = op->next) { - BLI_ghash_insert(override->runtime, op->rna_path, op); - } - } - - return override->runtime; -} - -/** - * Find override property from given RNA path, if it exists. - */ -IDOverrideLibraryProperty *BKE_override_library_property_find(IDOverrideLibrary *override, - const char *rna_path) -{ - IDOverrideLibraryRuntime *override_runtime = override_library_rna_path_mapping_ensure(override); - return BLI_ghash_lookup(override_runtime, rna_path); -} - -/** - * Find override property from given RNA path, or create it if it does not exist. - */ -IDOverrideLibraryProperty *BKE_override_library_property_get(IDOverrideLibrary *override, - const char *rna_path, - bool *r_created) -{ - IDOverrideLibraryProperty *op = BKE_override_library_property_find(override, rna_path); - - if (op == NULL) { - op = MEM_callocN(sizeof(IDOverrideLibraryProperty), __func__); - op->rna_path = BLI_strdup(rna_path); - BLI_addtail(&override->properties, op); - - IDOverrideLibraryRuntime *override_runtime = override_library_rna_path_mapping_ensure( - override); - BLI_ghash_insert(override_runtime, op->rna_path, op); - - if (r_created) { - *r_created = true; - } - } - else if (r_created) { - *r_created = false; - } - - return op; -} - -void bke_override_property_copy(IDOverrideLibraryProperty *op_dst, - IDOverrideLibraryProperty *op_src) -{ - op_dst->rna_path = BLI_strdup(op_src->rna_path); - BLI_duplicatelist(&op_dst->operations, &op_src->operations); - - for (IDOverrideLibraryPropertyOperation *opop_dst = op_dst->operations.first, - *opop_src = op_src->operations.first; - opop_dst; - opop_dst = opop_dst->next, opop_src = opop_src->next) { - bke_override_property_operation_copy(opop_dst, opop_src); - } -} - -void bke_override_property_clear(IDOverrideLibraryProperty *op) -{ - BLI_assert(op->rna_path != NULL); - - MEM_freeN(op->rna_path); - - for (IDOverrideLibraryPropertyOperation *opop = op->operations.first; opop; opop = opop->next) { - bke_override_property_operation_clear(opop); - } - BLI_freelistN(&op->operations); -} - -/** - * Remove and free given \a override_property from given ID \a override. - */ -void BKE_override_library_property_delete(IDOverrideLibrary *override, - IDOverrideLibraryProperty *override_property) -{ - bke_override_property_clear(override_property); - if (override->runtime != NULL) { - BLI_ghash_remove(override->runtime, override_property->rna_path, NULL, NULL); - } - BLI_freelinkN(&override->properties, override_property); -} - -/** - * Find override property operation from given sub-item(s), if it exists. - */ -IDOverrideLibraryPropertyOperation *BKE_override_library_property_operation_find( - IDOverrideLibraryProperty *override_property, - const char *subitem_refname, - const char *subitem_locname, - const int subitem_refindex, - const int subitem_locindex, - const bool strict, - bool *r_strict) -{ - IDOverrideLibraryPropertyOperation *opop; - const int subitem_defindex = -1; - - if (r_strict) { - *r_strict = true; - } - - if (subitem_locname != NULL) { - opop = BLI_findstring_ptr(&override_property->operations, - subitem_locname, - offsetof(IDOverrideLibraryPropertyOperation, subitem_local_name)); - - if (opop == NULL) { - return NULL; - } - - if (subitem_refname == NULL || opop->subitem_reference_name == NULL) { - return subitem_refname == opop->subitem_reference_name ? opop : NULL; - } - return (subitem_refname != NULL && opop->subitem_reference_name != NULL && - STREQ(subitem_refname, opop->subitem_reference_name)) ? - opop : - NULL; - } - - if (subitem_refname != NULL) { - opop = BLI_findstring_ptr( - &override_property->operations, - subitem_refname, - offsetof(IDOverrideLibraryPropertyOperation, subitem_reference_name)); - - if (opop == NULL) { - return NULL; - } - - if (subitem_locname == NULL || opop->subitem_local_name == NULL) { - return subitem_locname == opop->subitem_local_name ? opop : NULL; - } - return (subitem_locname != NULL && opop->subitem_local_name != NULL && - STREQ(subitem_locname, opop->subitem_local_name)) ? - opop : - NULL; - } - - if ((opop = BLI_listbase_bytes_find( - &override_property->operations, - &subitem_locindex, - sizeof(subitem_locindex), - offsetof(IDOverrideLibraryPropertyOperation, subitem_local_index)))) { - return ELEM(subitem_refindex, -1, opop->subitem_reference_index) ? opop : NULL; - } - - if ((opop = BLI_listbase_bytes_find( - &override_property->operations, - &subitem_refindex, - sizeof(subitem_refindex), - offsetof(IDOverrideLibraryPropertyOperation, subitem_reference_index)))) { - return ELEM(subitem_locindex, -1, opop->subitem_local_index) ? opop : NULL; - } - - /* index == -1 means all indices, that is valid fallback in case we requested specific index. */ - if (!strict && (subitem_locindex != subitem_defindex) && - (opop = BLI_listbase_bytes_find( - &override_property->operations, - &subitem_defindex, - sizeof(subitem_defindex), - offsetof(IDOverrideLibraryPropertyOperation, subitem_local_index)))) { - if (r_strict) { - *r_strict = false; - } - return opop; - } - - return NULL; -} - -/** - * Find override property operation from given sub-item(s), or create it if it does not exist. - */ -IDOverrideLibraryPropertyOperation *BKE_override_library_property_operation_get( - IDOverrideLibraryProperty *override_property, - const short operation, - const char *subitem_refname, - const char *subitem_locname, - const int subitem_refindex, - const int subitem_locindex, - const bool strict, - bool *r_strict, - bool *r_created) -{ - IDOverrideLibraryPropertyOperation *opop = BKE_override_library_property_operation_find( - override_property, - subitem_refname, - subitem_locname, - subitem_refindex, - subitem_locindex, - strict, - r_strict); - - if (opop == NULL) { - opop = MEM_callocN(sizeof(IDOverrideLibraryPropertyOperation), __func__); - opop->operation = operation; - if (subitem_locname) { - opop->subitem_local_name = BLI_strdup(subitem_locname); - } - if (subitem_refname) { - opop->subitem_reference_name = BLI_strdup(subitem_refname); - } - opop->subitem_local_index = subitem_locindex; - opop->subitem_reference_index = subitem_refindex; - - BLI_addtail(&override_property->operations, opop); - - if (r_created) { - *r_created = true; - } - } - else if (r_created) { - *r_created = false; - } - - return opop; -} - -void bke_override_property_operation_copy(IDOverrideLibraryPropertyOperation *opop_dst, - IDOverrideLibraryPropertyOperation *opop_src) -{ - if (opop_src->subitem_reference_name) { - opop_dst->subitem_reference_name = BLI_strdup(opop_src->subitem_reference_name); - } - if (opop_src->subitem_local_name) { - opop_dst->subitem_local_name = BLI_strdup(opop_src->subitem_local_name); - } -} - -void bke_override_property_operation_clear(IDOverrideLibraryPropertyOperation *opop) -{ - if (opop->subitem_reference_name) { - MEM_freeN(opop->subitem_reference_name); - } - if (opop->subitem_local_name) { - MEM_freeN(opop->subitem_local_name); - } -} - -/** - * Remove and free given \a override_property_operation from given ID \a override_property. - */ -void BKE_override_library_property_operation_delete( - IDOverrideLibraryProperty *override_property, - IDOverrideLibraryPropertyOperation *override_property_operation) -{ - bke_override_property_operation_clear(override_property_operation); - BLI_freelinkN(&override_property->operations, override_property_operation); -} - -/** - * Check that status of local data-block is still valid against current reference one. - * - * It means that all overridable, but not overridden, properties' local values must be equal to - * reference ones. Clears #LIB_TAG_OVERRIDE_OK if they do not. - * - * This is typically used to detect whether some property has been changed in local and a new - * #IDOverrideProperty (of #IDOverridePropertyOperation) has to be added. - * - * \return true if status is OK, false otherwise. */ -bool BKE_override_library_status_check_local(Main *bmain, ID *local) -{ - BLI_assert(local->override_library != NULL); - - ID *reference = local->override_library->reference; - - if (reference == NULL) { - /* This is an override template, local status is always OK! */ - return true; - } - - BLI_assert(GS(local->name) == GS(reference->name)); - - if (GS(local->name) == ID_OB) { - /* Our beloved pose's bone cross-data pointers... Usually, depsgraph evaluation would ensure - * this is valid, but in some cases (like hidden collections etc.) this won't be the case, so - * we need to take care of this ourselves. */ - Object *ob_local = (Object *)local; - if (ob_local->data != NULL && ob_local->type == OB_ARMATURE && ob_local->pose != NULL && - ob_local->pose->flag & POSE_RECALC) { - BKE_pose_rebuild(bmain, ob_local, ob_local->data, true); - } - } - - /* Note that reference is assumed always valid, caller has to ensure that itself. */ - - PointerRNA rnaptr_local, rnaptr_reference; - RNA_id_pointer_create(local, &rnaptr_local); - RNA_id_pointer_create(reference, &rnaptr_reference); - - if (!RNA_struct_override_matches(bmain, - &rnaptr_local, - &rnaptr_reference, - NULL, - local->override_library, - RNA_OVERRIDE_COMPARE_IGNORE_NON_OVERRIDABLE | - RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN, - NULL)) { - local->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK; - return false; - } - - return true; -} - -/** - * Check that status of reference data-block is still valid against current local one. - * - * It means that all non-overridden properties' local values must be equal to reference ones. - * Clears LIB_TAG_OVERRIDE_OK if they do not. - * - * This is typically used to detect whether some reference has changed and local - * needs to be updated against it. - * - * \return true if status is OK, false otherwise. */ -bool BKE_override_library_status_check_reference(Main *bmain, ID *local) -{ - BLI_assert(local->override_library != NULL); - - ID *reference = local->override_library->reference; - - if (reference == NULL) { - /* This is an override template, reference is virtual, so its status is always OK! */ - return true; - } - - BLI_assert(GS(local->name) == GS(reference->name)); - - if (reference->override_library && (reference->tag & LIB_TAG_OVERRIDE_LIBRARY_REFOK) == 0) { - if (!BKE_override_library_status_check_reference(bmain, reference)) { - /* If reference is also override of another data-block, and its status is not OK, - * then this override is not OK either. - * Note that this should only happen when reloading libraries... */ - local->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK; - return false; - } - } - - if (GS(local->name) == ID_OB) { - /* Our beloved pose's bone cross-data pointers... Usually, depsgraph evaluation would ensure - * this is valid, but in some cases (like hidden collections etc.) this won't be the case, so - * we need to take care of this ourselves. */ - Object *ob_local = (Object *)local; - if (ob_local->data != NULL && ob_local->type == OB_ARMATURE && ob_local->pose != NULL && - ob_local->pose->flag & POSE_RECALC) { - BKE_pose_rebuild(bmain, ob_local, ob_local->data, true); - } - } - - PointerRNA rnaptr_local, rnaptr_reference; - RNA_id_pointer_create(local, &rnaptr_local); - RNA_id_pointer_create(reference, &rnaptr_reference); - - if (!RNA_struct_override_matches(bmain, - &rnaptr_local, - &rnaptr_reference, - NULL, - local->override_library, - RNA_OVERRIDE_COMPARE_IGNORE_OVERRIDDEN, - NULL)) { - local->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK; - return false; - } - - return true; -} - -/** - * Compares local and reference data-blocks and create new override operations as needed, - * or reset to reference values if overriding is not allowed. - * - * \note Defining override operations is only mandatory before saving a `.blend` file on disk - * (not for undo!). - * Knowing that info at runtime is only useful for UI/UX feedback. - * - * \note This is by far the biggest operation (the more time-consuming) of the three so far, - * since it has to go over all properties in depth (all overridable ones at least). - * Generating diff values and applying overrides are much cheaper. - * - * \return true if new overriding op was created, or some local data was reset. */ -bool BKE_override_library_operations_create(Main *bmain, ID *local, const bool force_auto) -{ - BLI_assert(local->override_library != NULL); - const bool is_template = (local->override_library->reference == NULL); - bool ret = false; - - if (!is_template && (force_auto || local->override_library->flag & OVERRIDE_LIBRARY_AUTO)) { - /* Do not attempt to generate overriding rules from an empty place-holder generated by link - * code when it cannot find to actual library/ID. Much better to keep the local datablock as - * is in the file in that case, until broken lib is fixed. */ - if (ID_MISSING(local->override_library->reference)) { - return ret; - } - - if (GS(local->name) == ID_OB) { - /* Our beloved pose's bone cross-data pointers... Usually, depsgraph evaluation would ensure - * this is valid, but in some cases (like hidden collections etc.) this won't be the case, so - * we need to take care of this ourselves. */ - Object *ob_local = (Object *)local; - if (ob_local->data != NULL && ob_local->type == OB_ARMATURE && ob_local->pose != NULL && - ob_local->pose->flag & POSE_RECALC) { - BKE_pose_rebuild(bmain, ob_local, ob_local->data, true); - } - } - - PointerRNA rnaptr_local, rnaptr_reference; - RNA_id_pointer_create(local, &rnaptr_local); - RNA_id_pointer_create(local->override_library->reference, &rnaptr_reference); - - eRNAOverrideMatchResult report_flags = 0; - RNA_struct_override_matches(bmain, - &rnaptr_local, - &rnaptr_reference, - NULL, - local->override_library, - RNA_OVERRIDE_COMPARE_CREATE | RNA_OVERRIDE_COMPARE_RESTORE, - &report_flags); - if (report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) { - ret = true; - } -#ifndef NDEBUG - if (report_flags & RNA_OVERRIDE_MATCH_RESULT_RESTORED) { - printf("We did restore some properties of %s from its reference.\n", local->name); - } - if (ret) { - printf("We did generate library override rules for %s\n", local->name); - } - else { - printf("No new library override rules for %s\n", local->name); - } -#endif - } - return ret; -} - -/** Check all overrides from given \a bmain and create/update overriding operations as needed. */ -void BKE_main_override_library_operations_create(Main *bmain, const bool force_auto) -{ - ID *id; - - FOREACH_MAIN_ID_BEGIN (bmain, id) { - if ((ID_IS_OVERRIDE_LIBRARY(id) && force_auto) || - (ID_IS_OVERRIDE_LIBRARY_AUTO(id) && (id->tag & LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH))) { - BKE_override_library_operations_create(bmain, id, force_auto); - id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH; - } - } - FOREACH_MAIN_ID_END; -} - -/** Update given override from its reference (re-applying overridden properties). */ -void BKE_override_library_update(Main *bmain, ID *local) -{ - if (local->override_library == NULL || local->override_library->reference == NULL) { - return; - } - - /* Do not attempt to apply overriding rules over an empty place-holder generated by link code - * when it cannot find to actual library/ID. Much better to keep the local datablock as loaded - * from the file in that case, until broken lib is fixed. */ - if (ID_MISSING(local->override_library->reference)) { - return; - } - - /* Recursively do 'ancestors' overrides first, if any. */ - if (local->override_library->reference->override_library && - (local->override_library->reference->tag & LIB_TAG_OVERRIDE_LIBRARY_REFOK) == 0) { - BKE_override_library_update(bmain, local->override_library->reference); - } - - /* We want to avoid having to remap here, however creating up-to-date override is much simpler - * if based on reference than on current override. - * So we work on temp copy of reference, and 'swap' its content with local. */ - - /* XXX We need a way to get off-Main copies of IDs (similar to localized mats/texts/ etc.)! - * However, this is whole bunch of code work in itself, so for now plain stupid ID copy will - * do, as inn-efficient as it is. :/ - * Actually, maybe not! Since we are swapping with original ID's local content, we want to - * keep user-count in correct state when freeing tmp_id - * (and that user-counts of IDs used by 'new' local data also remain correct). */ - /* This would imply change in handling of user-count all over RNA - * (and possibly all over Blender code). - * Not impossible to do, but would rather see first if extra useless usual user handling - * is actually a (performances) issue here. */ - - ID *tmp_id; - BKE_id_copy(bmain, local->override_library->reference, &tmp_id); - - if (tmp_id == NULL) { - return; - } - - PointerRNA rnaptr_src, rnaptr_dst, rnaptr_storage_stack, *rnaptr_storage = NULL; - RNA_id_pointer_create(local, &rnaptr_src); - RNA_id_pointer_create(tmp_id, &rnaptr_dst); - if (local->override_library->storage) { - rnaptr_storage = &rnaptr_storage_stack; - RNA_id_pointer_create(local->override_library->storage, rnaptr_storage); - } - - RNA_struct_override_apply( - bmain, &rnaptr_dst, &rnaptr_src, rnaptr_storage, local->override_library); - - /* This also transfers all pointers (memory) owned by local to tmp_id, and vice-versa. - * So when we'll free tmp_id, we'll actually free old, outdated data from local. */ - BKE_id_swap(bmain, local, tmp_id); - - /* Again, horribly inn-efficient in our case, we need something off-Main - * (aka more generic nolib copy/free stuff)! */ - /* XXX And crashing in complex cases (e.g. because depsgraph uses same data...). */ - BKE_id_free_ex(bmain, tmp_id, LIB_ID_FREE_NO_UI_USER, true); - - if (local->override_library->storage) { - /* We know this datablock is not used anywhere besides local->override->storage. */ - /* XXX For until we get fully shadow copies, we still need to ensure storage releases - * its usage of any ID pointers it may have. */ - BKE_id_free_ex(bmain, local->override_library->storage, LIB_ID_FREE_NO_UI_USER, true); - local->override_library->storage = NULL; - } - - local->tag |= LIB_TAG_OVERRIDE_LIBRARY_REFOK; - - /* Full rebuild of Depsgraph! */ - - /* XXX Is this actual valid replacement for old DAG_relations_tag_update(bmain) ? */ - DEG_on_visible_update(bmain, true); -} - -/** Update all overrides from given \a bmain. */ -void BKE_main_override_library_update(Main *bmain) -{ - ID *id; - - FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (id->override_library != NULL && id->lib == NULL) { - BKE_override_library_update(bmain, id); - } - } - FOREACH_MAIN_ID_END; -} - -/** - * Storage (how to store overriding data into `.blend` files). - * - * Basically: - * 1) Only 'differential' storage needs special handling here. All others (replacing values or - * inserting/removing items from a collection) can be handled with simply storing current - * content of local data-block. - * 2) We store the differential value into a second 'ghost' data-block, - * which is an empty ID of same type as local one, - * where we only define values that need differential data. - * - * This avoids us having to modify 'real' data-block at write time (and restoring it afterwards), - * which is inefficient, and potentially dangerous (in case of concurrent access...), while not - * using much extra memory in typical cases. It also ensures stored data-block always contains - * exact same data as "desired" ones (kind of "baked" data-blocks). - */ - -/** Initialize an override storage. */ -OverrideLibraryStorage *BKE_override_library_operations_store_initialize(void) -{ - return BKE_main_new(); -} - -/** - * Generate suitable 'write' data (this only affects differential override operations). - * - * Note that \a local ID is no more modified by this call, - * all extra data are stored in its temp \a storage_id copy. */ -ID *BKE_override_library_operations_store_start(Main *bmain, - OverrideLibraryStorage *override_storage, - ID *local) -{ - BLI_assert(local->override_library != NULL); - BLI_assert(override_storage != NULL); - const bool is_template = (local->override_library->reference == NULL); - - if (is_template) { - /* This is actually purely local data with an override template, nothing to do here! */ - return NULL; - } - - /* Forcefully ensure we know about all needed override operations. */ - BKE_override_library_operations_create(bmain, local, false); - - ID *storage_id; -#ifdef DEBUG_OVERRIDE_TIMEIT - TIMEIT_START_AVERAGED(BKE_override_operations_store_start); -#endif - - /* XXX TODO We may also want a specialized handling of things here too, to avoid copying heavy - * never-overridable data (like Mesh geometry etc.)? And also maybe avoid lib reference-counting - * completely (shallow copy...). */ - /* This would imply change in handling of user-count all over RNA - * (and possibly all over Blender code). - * Not impossible to do, but would rather see first is extra useless usual user handling is - * actually a (performances) issue here, before doing it. */ - BKE_id_copy((Main *)override_storage, local, &storage_id); - - if (storage_id != NULL) { - PointerRNA rnaptr_reference, rnaptr_final, rnaptr_storage; - RNA_id_pointer_create(local->override_library->reference, &rnaptr_reference); - RNA_id_pointer_create(local, &rnaptr_final); - RNA_id_pointer_create(storage_id, &rnaptr_storage); - - if (!RNA_struct_override_store( - bmain, &rnaptr_final, &rnaptr_reference, &rnaptr_storage, local->override_library)) { - BKE_id_free_ex(override_storage, storage_id, LIB_ID_FREE_NO_UI_USER, true); - storage_id = NULL; - } - } - - local->override_library->storage = storage_id; - -#ifdef DEBUG_OVERRIDE_TIMEIT - TIMEIT_END_AVERAGED(BKE_override_operations_store_start); -#endif - return storage_id; -} - -/** Restore given ID modified by \a BKE_override_operations_store_start, to its original state. */ -void BKE_override_library_operations_store_end(OverrideLibraryStorage *UNUSED(override_storage), - ID *local) -{ - BLI_assert(local->override_library != NULL); - - /* Nothing else to do here really, we need to keep all temp override storage data-blocks in - * memory until whole file is written anyway (otherwise we'd get mem pointers overlap...). */ - local->override_library->storage = NULL; -} - -void BKE_override_library_operations_store_finalize(OverrideLibraryStorage *override_storage) -{ - /* We cannot just call BKE_main_free(override_storage), not until we have option to make 'ghost' - * copies of IDs without increasing usercount of used data-blocks. */ - ID *id; - - FOREACH_MAIN_ID_BEGIN (override_storage, id) { - BKE_id_free_ex(override_storage, id, LIB_ID_FREE_NO_UI_USER, true); - } - FOREACH_MAIN_ID_END; - - BKE_main_free(override_storage); -} diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c deleted file mode 100644 index 5fd852ff089..00000000000 --- a/source/blender/blenkernel/intern/library_query.c +++ /dev/null @@ -1,1477 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2014 by Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup bke - */ - -#include - -#include "MEM_guardedalloc.h" - -#include "DNA_anim_types.h" -#include "DNA_armature_types.h" -#include "DNA_brush_types.h" -#include "DNA_camera_types.h" -#include "DNA_collection_types.h" -#include "DNA_constraint_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_key_types.h" -#include "DNA_light_types.h" -#include "DNA_lattice_types.h" -#include "DNA_linestyle_types.h" -#include "DNA_material_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_meta_types.h" -#include "DNA_movieclip_types.h" -#include "DNA_mask_types.h" -#include "DNA_node_types.h" -#include "DNA_object_force_types.h" -#include "DNA_lightprobe_types.h" -#include "DNA_rigidbody_types.h" -#include "DNA_scene_types.h" -#include "DNA_sequence_types.h" -#include "DNA_screen_types.h" -#include "DNA_speaker_types.h" -#include "DNA_sound_types.h" -#include "DNA_text_types.h" -#include "DNA_vfont_types.h" -#include "DNA_windowmanager_types.h" -#include "DNA_workspace_types.h" -#include "DNA_world_types.h" - -#include "BLI_utildefines.h" -#include "BLI_ghash.h" -#include "BLI_linklist_stack.h" - -#include "BKE_animsys.h" -#include "BKE_collection.h" -#include "BKE_constraint.h" -#include "BKE_fcurve.h" -#include "BKE_gpencil_modifier.h" -#include "BKE_idprop.h" -#include "BKE_library.h" -#include "BKE_library_query.h" -#include "BKE_main.h" -#include "BKE_modifier.h" -#include "BKE_node.h" -#include "BKE_particle.h" -#include "BKE_rigidbody.h" -#include "BKE_sequencer.h" -#include "BKE_shader_fx.h" -#include "BKE_tracking.h" -#include "BKE_workspace.h" - -#define FOREACH_FINALIZE _finalize -#define FOREACH_FINALIZE_VOID \ - if (0) { \ - goto FOREACH_FINALIZE; \ - } \ - FOREACH_FINALIZE: \ - ((void)0) - -#define FOREACH_CALLBACK_INVOKE_ID_PP(_data, id_pp, _cb_flag) \ - CHECK_TYPE(id_pp, ID **); \ - if (!((_data)->status & IDWALK_STOP)) { \ - const int _flag = (_data)->flag; \ - ID *old_id = *(id_pp); \ - const int callback_return = (_data)->callback((_data)->user_data, \ - (_data)->self_id, \ - id_pp, \ - (_cb_flag | (_data)->cb_flag) & \ - ~(_data)->cb_flag_clear); \ - if (_flag & IDWALK_READONLY) { \ - BLI_assert(*(id_pp) == old_id); \ - } \ - if (old_id && (_flag & IDWALK_RECURSE)) { \ - if (BLI_gset_add((_data)->ids_handled, old_id)) { \ - if (!(callback_return & IDWALK_RET_STOP_RECURSION)) { \ - BLI_LINKSTACK_PUSH((_data)->ids_todo, old_id); \ - } \ - } \ - } \ - if (callback_return & IDWALK_RET_STOP_ITER) { \ - (_data)->status |= IDWALK_STOP; \ - goto FOREACH_FINALIZE; \ - } \ - } \ - else { \ - goto FOREACH_FINALIZE; \ - } \ - ((void)0) - -#define FOREACH_CALLBACK_INVOKE_ID(_data, id, cb_flag) \ - { \ - CHECK_TYPE_ANY(id, ID *, void *); \ - FOREACH_CALLBACK_INVOKE_ID_PP(_data, (ID **)&(id), cb_flag); \ - } \ - ((void)0) - -#define FOREACH_CALLBACK_INVOKE(_data, id_super, cb_flag) \ - { \ - CHECK_TYPE(&((id_super)->id), ID *); \ - FOREACH_CALLBACK_INVOKE_ID_PP(_data, (ID **)&(id_super), cb_flag); \ - } \ - ((void)0) - -/* status */ -enum { - IDWALK_STOP = 1 << 0, -}; - -typedef struct LibraryForeachIDData { - ID *self_id; - int flag; - int cb_flag; - int cb_flag_clear; - LibraryIDLinkCallback callback; - void *user_data; - int status; - - /* To handle recursion. */ - GSet *ids_handled; /* All IDs that are either already done, or still in ids_todo stack. */ - BLI_LINKSTACK_DECLARE(ids_todo, ID *); -} LibraryForeachIDData; - -static void library_foreach_ID_link(Main *bmain, - ID *id, - LibraryIDLinkCallback callback, - void *user_data, - int flag, - LibraryForeachIDData *inherit_data); - -static void library_foreach_idproperty_ID_link(LibraryForeachIDData *data, - IDProperty *prop, - int flag) -{ - if (!prop) { - return; - } - - switch (prop->type) { - case IDP_GROUP: { - for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) { - library_foreach_idproperty_ID_link(data, loop, flag); - } - break; - } - case IDP_IDPARRAY: { - IDProperty *loop = IDP_Array(prop); - for (int i = 0; i < prop->len; i++) { - library_foreach_idproperty_ID_link(data, &loop[i], flag); - } - break; - } - case IDP_ID: - FOREACH_CALLBACK_INVOKE_ID(data, prop->data.pointer, flag); - break; - default: - break; /* Nothing to do here with other types of IDProperties... */ - } - - FOREACH_FINALIZE_VOID; -} - -static void library_foreach_rigidbodyworldSceneLooper(struct RigidBodyWorld *UNUSED(rbw), - ID **id_pointer, - void *user_data, - int cb_flag) -{ - LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); - - FOREACH_FINALIZE_VOID; -} - -static void library_foreach_modifiersForeachIDLink(void *user_data, - Object *UNUSED(object), - ID **id_pointer, - int cb_flag) -{ - LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); - - FOREACH_FINALIZE_VOID; -} - -static void library_foreach_gpencil_modifiersForeachIDLink(void *user_data, - Object *UNUSED(object), - ID **id_pointer, - int cb_flag) -{ - LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); - - FOREACH_FINALIZE_VOID; -} - -static void library_foreach_shaderfxForeachIDLink(void *user_data, - Object *UNUSED(object), - ID **id_pointer, - int cb_flag) -{ - LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); - - FOREACH_FINALIZE_VOID; -} - -static void library_foreach_constraintObjectLooper(bConstraint *UNUSED(con), - ID **id_pointer, - bool is_reference, - void *user_data) -{ - LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - const int cb_flag = is_reference ? IDWALK_CB_USER : IDWALK_CB_NOP; - FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); - - FOREACH_FINALIZE_VOID; -} - -static void library_foreach_particlesystemsObjectLooper(ParticleSystem *UNUSED(psys), - ID **id_pointer, - void *user_data, - int cb_flag) -{ - LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); - - FOREACH_FINALIZE_VOID; -} - -static void library_foreach_nla_strip(LibraryForeachIDData *data, NlaStrip *strip) -{ - NlaStrip *substrip; - - FOREACH_CALLBACK_INVOKE(data, strip->act, IDWALK_CB_USER); - - for (substrip = strip->strips.first; substrip; substrip = substrip->next) { - library_foreach_nla_strip(data, substrip); - } - - FOREACH_FINALIZE_VOID; -} - -static void library_foreach_animationData(LibraryForeachIDData *data, AnimData *adt) -{ - FCurve *fcu; - NlaTrack *nla_track; - NlaStrip *nla_strip; - - for (fcu = adt->drivers.first; fcu; fcu = fcu->next) { - ChannelDriver *driver = fcu->driver; - DriverVar *dvar; - - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - /* only used targets */ - DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { - FOREACH_CALLBACK_INVOKE_ID(data, dtar->id, IDWALK_CB_NOP); - } - DRIVER_TARGETS_LOOPER_END; - } - } - - FOREACH_CALLBACK_INVOKE(data, adt->action, IDWALK_CB_USER); - FOREACH_CALLBACK_INVOKE(data, adt->tmpact, IDWALK_CB_USER); - - for (nla_track = adt->nla_tracks.first; nla_track; nla_track = nla_track->next) { - for (nla_strip = nla_track->strips.first; nla_strip; nla_strip = nla_strip->next) { - library_foreach_nla_strip(data, nla_strip); - } - } - - FOREACH_FINALIZE_VOID; -} - -static void library_foreach_mtex(LibraryForeachIDData *data, MTex *mtex) -{ - FOREACH_CALLBACK_INVOKE(data, mtex->object, IDWALK_CB_NOP); - FOREACH_CALLBACK_INVOKE(data, mtex->tex, IDWALK_CB_USER); - - FOREACH_FINALIZE_VOID; -} - -static void library_foreach_paint(LibraryForeachIDData *data, Paint *paint) -{ - FOREACH_CALLBACK_INVOKE(data, paint->brush, IDWALK_CB_USER); - for (int i = 0; i < paint->tool_slots_len; i++) { - FOREACH_CALLBACK_INVOKE(data, paint->tool_slots[i].brush, IDWALK_CB_USER); - } - FOREACH_CALLBACK_INVOKE(data, paint->palette, IDWALK_CB_USER); - - FOREACH_FINALIZE_VOID; -} - -static void library_foreach_bone(LibraryForeachIDData *data, Bone *bone) -{ - library_foreach_idproperty_ID_link(data, bone->prop, IDWALK_CB_USER); - - for (Bone *curbone = bone->childbase.first; curbone; curbone = curbone->next) { - library_foreach_bone(data, curbone); - } - - FOREACH_FINALIZE_VOID; -} - -static void library_foreach_layer_collection(LibraryForeachIDData *data, ListBase *lb) -{ - for (LayerCollection *lc = lb->first; lc; lc = lc->next) { - FOREACH_CALLBACK_INVOKE(data, lc->collection, IDWALK_CB_NOP); - library_foreach_layer_collection(data, &lc->layer_collections); - } - - FOREACH_FINALIZE_VOID; -} - -/* Used by both real Collection data-blocks, and the fake horror of master collection from Scene. - */ -static void library_foreach_collection(LibraryForeachIDData *data, Collection *collection) -{ - for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) { - FOREACH_CALLBACK_INVOKE(data, cob->ob, IDWALK_CB_USER); - } - for (CollectionChild *child = collection->children.first; child; child = child->next) { - FOREACH_CALLBACK_INVOKE(data, child->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_USER); - } - for (CollectionParent *parent = collection->parents.first; parent; parent = parent->next) { - FOREACH_CALLBACK_INVOKE(data, parent->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_LOOPBACK); - } - - FOREACH_FINALIZE_VOID; -} - -static void library_foreach_ID_as_subdata_link(ID **id_pp, - LibraryIDLinkCallback callback, - void *user_data, - int flag, - LibraryForeachIDData *data) -{ - /* Needed e.g. for callbacks handling relationships... This call shall be absolutely readonly. */ - ID *id = *id_pp; - FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pp, IDWALK_CB_PRIVATE); - BLI_assert(id == *id_pp); - - if (flag & IDWALK_RECURSE) { - /* Defer handling into main loop, recursively calling BKE_library_foreach_ID_link in - * IDWALK_RECURSE case is troublesome, see T49553. */ - if (BLI_gset_add(data->ids_handled, id)) { - BLI_LINKSTACK_PUSH(data->ids_todo, id); - } - } - else { - library_foreach_ID_link(NULL, id, callback, user_data, flag, data); - } - - FOREACH_FINALIZE_VOID; -} - -static void library_foreach_ID_link(Main *bmain, - ID *id, - LibraryIDLinkCallback callback, - void *user_data, - int flag, - LibraryForeachIDData *inherit_data) -{ - LibraryForeachIDData data; - int i; - - if (flag & IDWALK_RECURSE) { - /* For now, recursion implies read-only. */ - flag |= IDWALK_READONLY; - - data.ids_handled = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); - BLI_LINKSTACK_INIT(data.ids_todo); - - BLI_gset_add(data.ids_handled, id); - } - else { - data.ids_handled = NULL; - } - data.flag = flag; - data.status = 0; - data.callback = callback; - data.user_data = user_data; - -#define CALLBACK_INVOKE_ID(check_id, cb_flag) FOREACH_CALLBACK_INVOKE_ID(&data, check_id, cb_flag) - -#define CALLBACK_INVOKE(check_id_super, cb_flag) \ - FOREACH_CALLBACK_INVOKE(&data, check_id_super, cb_flag) - - for (; id != NULL; id = (flag & IDWALK_RECURSE) ? BLI_LINKSTACK_POP(data.ids_todo) : NULL) { - data.self_id = id; - - /* inherit_data is non-NULL when this function is called for some sub-data ID - * (like root nodetree of a material). - * In that case, we do not want to generate those 'generic flags' from our current sub-data ID - * (the node tree), but re-use those generated for the 'owner' ID (the material). */ - if (inherit_data == NULL) { - data.cb_flag = ID_IS_LINKED(id) ? IDWALK_CB_INDIRECT_USAGE : 0; - /* When an ID is not in Main database, it should never refcount IDs it is using. - * Exceptions: NodeTrees (yeeahhh!) directly used by Materials. */ - data.cb_flag_clear = (id->tag & LIB_TAG_NO_MAIN) ? IDWALK_CB_USER | IDWALK_CB_USER_ONE : 0; - } - else { - data.cb_flag = inherit_data->cb_flag; - data.cb_flag_clear = inherit_data->cb_flag_clear; - } - - if (bmain != NULL && bmain->relations != NULL && (flag & IDWALK_READONLY)) { - /* Note that this is minor optimization, even in worst cases (like id being an object with - * lots of drivers and constraints and modifiers, or material etc. with huge node tree), - * but we might as well use it (Main->relations is always assumed valid, - * it's responsibility of code creating it to free it, - * especially if/when it starts modifying Main database). */ - MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->id_user_to_used, id); - for (; entry != NULL; entry = entry->next) { - FOREACH_CALLBACK_INVOKE_ID_PP(&data, entry->id_pointer, entry->usage_flag); - } - continue; - } - - if (id->override_library != NULL) { - CALLBACK_INVOKE_ID(id->override_library->reference, - IDWALK_CB_USER | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE); - CALLBACK_INVOKE_ID(id->override_library->storage, - IDWALK_CB_USER | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE); - } - - library_foreach_idproperty_ID_link(&data, id->properties, IDWALK_CB_USER); - - AnimData *adt = BKE_animdata_from_id(id); - if (adt) { - library_foreach_animationData(&data, adt); - } - - switch ((ID_Type)GS(id->name)) { - case ID_LI: { - Library *lib = (Library *)id; - CALLBACK_INVOKE(lib->parent, IDWALK_CB_NEVER_SELF); - break; - } - case ID_SCE: { - Scene *scene = (Scene *)id; - ToolSettings *toolsett = scene->toolsettings; - - CALLBACK_INVOKE(scene->camera, IDWALK_CB_NOP); - CALLBACK_INVOKE(scene->world, IDWALK_CB_USER); - CALLBACK_INVOKE(scene->set, IDWALK_CB_NEVER_SELF); - CALLBACK_INVOKE(scene->clip, IDWALK_CB_USER); - CALLBACK_INVOKE(scene->gpd, IDWALK_CB_USER); - CALLBACK_INVOKE(scene->r.bake.cage_object, IDWALK_CB_NOP); - if (scene->nodetree) { - /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link( - (ID **)&scene->nodetree, callback, user_data, flag, &data); - } - if (scene->ed) { - Sequence *seq; - SEQP_BEGIN (scene->ed, seq) { - CALLBACK_INVOKE(seq->scene, IDWALK_CB_NEVER_SELF); - CALLBACK_INVOKE(seq->scene_camera, IDWALK_CB_NOP); - CALLBACK_INVOKE(seq->clip, IDWALK_CB_USER); - CALLBACK_INVOKE(seq->mask, IDWALK_CB_USER); - CALLBACK_INVOKE(seq->sound, IDWALK_CB_USER); - library_foreach_idproperty_ID_link(&data, seq->prop, IDWALK_CB_USER); - for (SequenceModifierData *smd = seq->modifiers.first; smd; smd = smd->next) { - CALLBACK_INVOKE(smd->mask_id, IDWALK_CB_USER); - } - - if (seq->type == SEQ_TYPE_TEXT && seq->effectdata) { - TextVars *text_data = seq->effectdata; - CALLBACK_INVOKE(text_data->text_font, IDWALK_CB_USER); - } - } - SEQ_END; - } - - library_foreach_collection(&data, scene->master_collection); - - ViewLayer *view_layer; - for (view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) { - CALLBACK_INVOKE(view_layer->mat_override, IDWALK_CB_USER); - - for (Base *base = view_layer->object_bases.first; base; base = base->next) { - CALLBACK_INVOKE(base->object, IDWALK_CB_NOP); - } - - library_foreach_layer_collection(&data, &view_layer->layer_collections); - - for (FreestyleModuleConfig *fmc = view_layer->freestyle_config.modules.first; fmc; - fmc = fmc->next) { - if (fmc->script) { - CALLBACK_INVOKE(fmc->script, IDWALK_CB_NOP); - } - } - - for (FreestyleLineSet *fls = view_layer->freestyle_config.linesets.first; fls; - fls = fls->next) { - if (fls->group) { - CALLBACK_INVOKE(fls->group, IDWALK_CB_USER); - } - - if (fls->linestyle) { - CALLBACK_INVOKE(fls->linestyle, IDWALK_CB_USER); - } - } - } - - for (TimeMarker *marker = scene->markers.first; marker; marker = marker->next) { - CALLBACK_INVOKE(marker->camera, IDWALK_CB_NOP); - } - - if (toolsett) { - CALLBACK_INVOKE(toolsett->particle.scene, IDWALK_CB_NOP); - CALLBACK_INVOKE(toolsett->particle.object, IDWALK_CB_NOP); - CALLBACK_INVOKE(toolsett->particle.shape_object, IDWALK_CB_NOP); - - library_foreach_paint(&data, &toolsett->imapaint.paint); - CALLBACK_INVOKE(toolsett->imapaint.stencil, IDWALK_CB_USER); - CALLBACK_INVOKE(toolsett->imapaint.clone, IDWALK_CB_USER); - CALLBACK_INVOKE(toolsett->imapaint.canvas, IDWALK_CB_USER); - - if (toolsett->vpaint) { - library_foreach_paint(&data, &toolsett->vpaint->paint); - } - if (toolsett->wpaint) { - library_foreach_paint(&data, &toolsett->wpaint->paint); - } - if (toolsett->sculpt) { - library_foreach_paint(&data, &toolsett->sculpt->paint); - CALLBACK_INVOKE(toolsett->sculpt->gravity_object, IDWALK_CB_NOP); - } - if (toolsett->uvsculpt) { - library_foreach_paint(&data, &toolsett->uvsculpt->paint); - } - if (toolsett->gp_paint) { - library_foreach_paint(&data, &toolsett->gp_paint->paint); - } - - CALLBACK_INVOKE(toolsett->gp_sculpt.guide.reference_object, IDWALK_CB_NOP); - } - - if (scene->rigidbody_world) { - BKE_rigidbody_world_id_loop( - scene->rigidbody_world, library_foreach_rigidbodyworldSceneLooper, &data); - } - - break; - } - - case ID_OB: { - Object *object = (Object *)id; - ParticleSystem *psys; - - /* Object is special, proxies make things hard... */ - const int data_cb_flag = data.cb_flag; - const int proxy_cb_flag = ((data.flag & IDWALK_NO_INDIRECT_PROXY_DATA_USAGE) == 0 && - (object->proxy || object->proxy_group)) ? - IDWALK_CB_INDIRECT_USAGE : - 0; - - /* object data special case */ - data.cb_flag |= proxy_cb_flag; - if (object->type == OB_EMPTY) { - /* empty can have NULL or Image */ - CALLBACK_INVOKE_ID(object->data, IDWALK_CB_USER); - } - else { - /* when set, this can't be NULL */ - if (object->data) { - CALLBACK_INVOKE_ID(object->data, IDWALK_CB_USER | IDWALK_CB_NEVER_NULL); - } - } - data.cb_flag = data_cb_flag; - - CALLBACK_INVOKE(object->parent, IDWALK_CB_NEVER_SELF); - CALLBACK_INVOKE(object->track, IDWALK_CB_NEVER_SELF); - /* object->proxy is refcounted, but not object->proxy_group... *sigh* */ - CALLBACK_INVOKE(object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF); - CALLBACK_INVOKE(object->proxy_group, IDWALK_CB_NOP); - - /* Special case! - * Since this field is set/owned by 'user' of this ID (and not ID itself), - * it is only indirect usage if proxy object is linked... Twisted. */ - if (object->proxy_from) { - data.cb_flag = ID_IS_LINKED(object->proxy_from) ? IDWALK_CB_INDIRECT_USAGE : 0; - } - CALLBACK_INVOKE(object->proxy_from, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF); - data.cb_flag = data_cb_flag; - - CALLBACK_INVOKE(object->poselib, IDWALK_CB_USER); - - data.cb_flag |= proxy_cb_flag; - for (i = 0; i < object->totcol; i++) { - CALLBACK_INVOKE(object->mat[i], IDWALK_CB_USER); - } - data.cb_flag = data_cb_flag; - - /* Note that ob->gpd is deprecated, so no need to handle it here. */ - CALLBACK_INVOKE(object->instance_collection, IDWALK_CB_USER); - - if (object->pd) { - CALLBACK_INVOKE(object->pd->tex, IDWALK_CB_USER); - CALLBACK_INVOKE(object->pd->f_source, IDWALK_CB_NOP); - } - /* Note that ob->effect is deprecated, so no need to handle it here. */ - - if (object->pose) { - bPoseChannel *pchan; - - data.cb_flag |= proxy_cb_flag; - for (pchan = object->pose->chanbase.first; pchan; pchan = pchan->next) { - library_foreach_idproperty_ID_link(&data, pchan->prop, IDWALK_CB_USER); - CALLBACK_INVOKE(pchan->custom, IDWALK_CB_USER); - BKE_constraints_id_loop( - &pchan->constraints, library_foreach_constraintObjectLooper, &data); - } - data.cb_flag = data_cb_flag; - } - - if (object->rigidbody_constraint) { - CALLBACK_INVOKE(object->rigidbody_constraint->ob1, IDWALK_CB_NEVER_SELF); - CALLBACK_INVOKE(object->rigidbody_constraint->ob2, IDWALK_CB_NEVER_SELF); - } - - if (object->lodlevels.first) { - LodLevel *level; - for (level = object->lodlevels.first; level; level = level->next) { - CALLBACK_INVOKE(level->source, IDWALK_CB_NEVER_SELF); - } - } - - modifiers_foreachIDLink(object, library_foreach_modifiersForeachIDLink, &data); - BKE_gpencil_modifiers_foreachIDLink( - object, library_foreach_gpencil_modifiersForeachIDLink, &data); - BKE_constraints_id_loop( - &object->constraints, library_foreach_constraintObjectLooper, &data); - BKE_shaderfx_foreachIDLink(object, library_foreach_shaderfxForeachIDLink, &data); - - for (psys = object->particlesystem.first; psys; psys = psys->next) { - BKE_particlesystem_id_loop(psys, library_foreach_particlesystemsObjectLooper, &data); - } - - if (object->soft) { - CALLBACK_INVOKE(object->soft->collision_group, IDWALK_CB_NOP); - - if (object->soft->effector_weights) { - CALLBACK_INVOKE(object->soft->effector_weights->group, IDWALK_CB_NOP); - } - } - break; - } - - case ID_AR: { - bArmature *arm = (bArmature *)id; - - for (Bone *bone = arm->bonebase.first; bone; bone = bone->next) { - library_foreach_bone(&data, bone); - } - break; - } - - case ID_ME: { - Mesh *mesh = (Mesh *)id; - CALLBACK_INVOKE(mesh->texcomesh, IDWALK_CB_NEVER_SELF); - CALLBACK_INVOKE(mesh->key, IDWALK_CB_USER); - for (i = 0; i < mesh->totcol; i++) { - CALLBACK_INVOKE(mesh->mat[i], IDWALK_CB_USER); - } - break; - } - - case ID_CU: { - Curve *curve = (Curve *)id; - CALLBACK_INVOKE(curve->bevobj, IDWALK_CB_NOP); - CALLBACK_INVOKE(curve->taperobj, IDWALK_CB_NOP); - CALLBACK_INVOKE(curve->textoncurve, IDWALK_CB_NOP); - CALLBACK_INVOKE(curve->key, IDWALK_CB_USER); - for (i = 0; i < curve->totcol; i++) { - CALLBACK_INVOKE(curve->mat[i], IDWALK_CB_USER); - } - CALLBACK_INVOKE(curve->vfont, IDWALK_CB_USER); - CALLBACK_INVOKE(curve->vfontb, IDWALK_CB_USER); - CALLBACK_INVOKE(curve->vfonti, IDWALK_CB_USER); - CALLBACK_INVOKE(curve->vfontbi, IDWALK_CB_USER); - break; - } - - case ID_MB: { - MetaBall *metaball = (MetaBall *)id; - for (i = 0; i < metaball->totcol; i++) { - CALLBACK_INVOKE(metaball->mat[i], IDWALK_CB_USER); - } - break; - } - - case ID_MA: { - Material *material = (Material *)id; - if (material->nodetree) { - /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link( - (ID **)&material->nodetree, callback, user_data, flag, &data); - } - if (material->texpaintslot != NULL) { - CALLBACK_INVOKE(material->texpaintslot->ima, IDWALK_CB_NOP); - } - if (material->gp_style != NULL) { - CALLBACK_INVOKE(material->gp_style->sima, IDWALK_CB_USER); - CALLBACK_INVOKE(material->gp_style->ima, IDWALK_CB_USER); - } - break; - } - - case ID_TE: { - Tex *texture = (Tex *)id; - if (texture->nodetree) { - /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link( - (ID **)&texture->nodetree, callback, user_data, flag, &data); - } - CALLBACK_INVOKE(texture->ima, IDWALK_CB_USER); - break; - } - - case ID_LT: { - Lattice *lattice = (Lattice *)id; - CALLBACK_INVOKE(lattice->key, IDWALK_CB_USER); - break; - } - - case ID_LA: { - Light *lamp = (Light *)id; - if (lamp->nodetree) { - /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link( - (ID **)&lamp->nodetree, callback, user_data, flag, &data); - } - break; - } - - case ID_CA: { - Camera *camera = (Camera *)id; - CALLBACK_INVOKE(camera->dof.focus_object, IDWALK_CB_NOP); - for (CameraBGImage *bgpic = camera->bg_images.first; bgpic; bgpic = bgpic->next) { - if (bgpic->source == CAM_BGIMG_SOURCE_IMAGE) { - CALLBACK_INVOKE(bgpic->ima, IDWALK_CB_USER); - } - else if (bgpic->source == CAM_BGIMG_SOURCE_MOVIE) { - CALLBACK_INVOKE(bgpic->clip, IDWALK_CB_USER); - } - } - - break; - } - - case ID_KE: { - Key *key = (Key *)id; - CALLBACK_INVOKE_ID(key->from, IDWALK_CB_LOOPBACK); - break; - } - - case ID_WO: { - World *world = (World *)id; - if (world->nodetree) { - /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link( - (ID **)&world->nodetree, callback, user_data, flag, &data); - } - break; - } - - case ID_SPK: { - Speaker *speaker = (Speaker *)id; - CALLBACK_INVOKE(speaker->sound, IDWALK_CB_USER); - break; - } - - case ID_LP: { - LightProbe *probe = (LightProbe *)id; - CALLBACK_INVOKE(probe->image, IDWALK_CB_USER); - CALLBACK_INVOKE(probe->visibility_grp, IDWALK_CB_NOP); - break; - } - - case ID_GR: { - Collection *collection = (Collection *)id; - library_foreach_collection(&data, collection); - break; - } - - case ID_NT: { - bNodeTree *ntree = (bNodeTree *)id; - bNode *node; - bNodeSocket *sock; - - CALLBACK_INVOKE(ntree->gpd, IDWALK_CB_USER); - - for (node = ntree->nodes.first; node; node = node->next) { - CALLBACK_INVOKE_ID(node->id, IDWALK_CB_USER); - - library_foreach_idproperty_ID_link(&data, node->prop, IDWALK_CB_USER); - for (sock = node->inputs.first; sock; sock = sock->next) { - library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER); - } - for (sock = node->outputs.first; sock; sock = sock->next) { - library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER); - } - } - - for (sock = ntree->inputs.first; sock; sock = sock->next) { - library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER); - } - for (sock = ntree->outputs.first; sock; sock = sock->next) { - library_foreach_idproperty_ID_link(&data, sock->prop, IDWALK_CB_USER); - } - break; - } - - case ID_BR: { - Brush *brush = (Brush *)id; - CALLBACK_INVOKE(brush->toggle_brush, IDWALK_CB_NOP); - CALLBACK_INVOKE(brush->clone.image, IDWALK_CB_NOP); - CALLBACK_INVOKE(brush->paint_curve, IDWALK_CB_USER); - if (brush->gpencil_settings) { - CALLBACK_INVOKE(brush->gpencil_settings->material, IDWALK_CB_USER); - } - library_foreach_mtex(&data, &brush->mtex); - library_foreach_mtex(&data, &brush->mask_mtex); - break; - } - - case ID_PA: { - ParticleSettings *psett = (ParticleSettings *)id; - CALLBACK_INVOKE(psett->instance_collection, IDWALK_CB_USER); - CALLBACK_INVOKE(psett->instance_object, IDWALK_CB_NOP); - CALLBACK_INVOKE(psett->bb_ob, IDWALK_CB_NOP); - CALLBACK_INVOKE(psett->collision_group, IDWALK_CB_NOP); - - for (i = 0; i < MAX_MTEX; i++) { - if (psett->mtex[i]) { - library_foreach_mtex(&data, psett->mtex[i]); - } - } - - if (psett->effector_weights) { - CALLBACK_INVOKE(psett->effector_weights->group, IDWALK_CB_NOP); - } - - if (psett->pd) { - CALLBACK_INVOKE(psett->pd->tex, IDWALK_CB_USER); - CALLBACK_INVOKE(psett->pd->f_source, IDWALK_CB_NOP); - } - if (psett->pd2) { - CALLBACK_INVOKE(psett->pd2->tex, IDWALK_CB_USER); - CALLBACK_INVOKE(psett->pd2->f_source, IDWALK_CB_NOP); - } - - if (psett->boids) { - BoidState *state; - BoidRule *rule; - - for (state = psett->boids->states.first; state; state = state->next) { - for (rule = state->rules.first; rule; rule = rule->next) { - if (rule->type == eBoidRuleType_Avoid) { - BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid *)rule; - CALLBACK_INVOKE(gabr->ob, IDWALK_CB_NOP); - } - else if (rule->type == eBoidRuleType_FollowLeader) { - BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader *)rule; - CALLBACK_INVOKE(flbr->ob, IDWALK_CB_NOP); - } - } - } - } - - for (ParticleDupliWeight *dw = psett->instance_weights.first; dw; dw = dw->next) { - CALLBACK_INVOKE(dw->ob, IDWALK_CB_NOP); - } - break; - } - - case ID_MC: { - MovieClip *clip = (MovieClip *)id; - MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *object; - MovieTrackingTrack *track; - MovieTrackingPlaneTrack *plane_track; - - CALLBACK_INVOKE(clip->gpd, IDWALK_CB_USER); - - for (track = tracking->tracks.first; track; track = track->next) { - CALLBACK_INVOKE(track->gpd, IDWALK_CB_USER); - } - for (object = tracking->objects.first; object; object = object->next) { - for (track = object->tracks.first; track; track = track->next) { - CALLBACK_INVOKE(track->gpd, IDWALK_CB_USER); - } - } - - for (plane_track = tracking->plane_tracks.first; plane_track; - plane_track = plane_track->next) { - CALLBACK_INVOKE(plane_track->image, IDWALK_CB_USER); - } - break; - } - - case ID_MSK: { - Mask *mask = (Mask *)id; - MaskLayer *mask_layer; - for (mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { - MaskSpline *mask_spline; - - for (mask_spline = mask_layer->splines.first; mask_spline; - mask_spline = mask_spline->next) { - for (i = 0; i < mask_spline->tot_point; i++) { - MaskSplinePoint *point = &mask_spline->points[i]; - CALLBACK_INVOKE_ID(point->parent.id, IDWALK_CB_USER); - } - } - } - break; - } - - case ID_LS: { - FreestyleLineStyle *linestyle = (FreestyleLineStyle *)id; - LineStyleModifier *lsm; - for (i = 0; i < MAX_MTEX; i++) { - if (linestyle->mtex[i]) { - library_foreach_mtex(&data, linestyle->mtex[i]); - } - } - if (linestyle->nodetree) { - /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link( - (ID **)&linestyle->nodetree, callback, user_data, flag, &data); - } - - for (lsm = linestyle->color_modifiers.first; lsm; lsm = lsm->next) { - if (lsm->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { - LineStyleColorModifier_DistanceFromObject *p = - (LineStyleColorModifier_DistanceFromObject *)lsm; - if (p->target) { - CALLBACK_INVOKE(p->target, IDWALK_CB_NOP); - } - } - } - for (lsm = linestyle->alpha_modifiers.first; lsm; lsm = lsm->next) { - if (lsm->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { - LineStyleAlphaModifier_DistanceFromObject *p = - (LineStyleAlphaModifier_DistanceFromObject *)lsm; - if (p->target) { - CALLBACK_INVOKE(p->target, IDWALK_CB_NOP); - } - } - } - for (lsm = linestyle->thickness_modifiers.first; lsm; lsm = lsm->next) { - if (lsm->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { - LineStyleThicknessModifier_DistanceFromObject *p = - (LineStyleThicknessModifier_DistanceFromObject *)lsm; - if (p->target) { - CALLBACK_INVOKE(p->target, IDWALK_CB_NOP); - } - } - } - break; - } - case ID_AC: { - bAction *act = (bAction *)id; - - for (TimeMarker *marker = act->markers.first; marker; marker = marker->next) { - CALLBACK_INVOKE(marker->camera, IDWALK_CB_NOP); - } - break; - } - - case ID_WM: { - wmWindowManager *wm = (wmWindowManager *)id; - - for (wmWindow *win = wm->windows.first; win; win = win->next) { - ID *workspace = (ID *)BKE_workspace_active_get(win->workspace_hook); - - CALLBACK_INVOKE(win->scene, IDWALK_CB_USER_ONE); - - CALLBACK_INVOKE_ID(workspace, IDWALK_CB_NOP); - /* allow callback to set a different workspace */ - BKE_workspace_active_set(win->workspace_hook, (WorkSpace *)workspace); - } - break; - } - - case ID_WS: { - WorkSpace *workspace = (WorkSpace *)id; - ListBase *layouts = BKE_workspace_layouts_get(workspace); - - for (WorkSpaceLayout *layout = layouts->first; layout; layout = layout->next) { - bScreen *screen = BKE_workspace_layout_screen_get(layout); - - /* CALLBACK_INVOKE expects an actual pointer, not a variable holding the pointer. - * However we can't access layout->screen here - * since we are outside the workspace project. */ - CALLBACK_INVOKE(screen, IDWALK_CB_USER); - /* allow callback to set a different screen */ - BKE_workspace_layout_screen_set(layout, screen); - } - break; - } - case ID_GD: { - bGPdata *gpencil = (bGPdata *)id; - /* materials */ - for (i = 0; i < gpencil->totcol; i++) { - CALLBACK_INVOKE(gpencil->mat[i], IDWALK_CB_USER); - } - - for (bGPDlayer *gplayer = gpencil->layers.first; gplayer != NULL; - gplayer = gplayer->next) { - CALLBACK_INVOKE(gplayer->parent, IDWALK_CB_NOP); - } - - break; - } - - /* Nothing needed for those... */ - case ID_SCR: - case ID_IM: - case ID_VF: - case ID_TXT: - case ID_SO: - case ID_PAL: - case ID_PC: - case ID_CF: - break; - - /* Deprecated. */ - case ID_IP: - break; - } - } - -FOREACH_FINALIZE: - if (data.ids_handled) { - BLI_gset_free(data.ids_handled, NULL); - BLI_LINKSTACK_FREE(data.ids_todo); - } - -#undef CALLBACK_INVOKE_ID -#undef CALLBACK_INVOKE -} - -#undef FOREACH_CALLBACK_INVOKE_ID -#undef FOREACH_CALLBACK_INVOKE - -/** - * Loop over all of the ID's this data-block links to. - */ -void BKE_library_foreach_ID_link( - Main *bmain, ID *id, LibraryIDLinkCallback callback, void *user_data, int flag) -{ - library_foreach_ID_link(bmain, id, callback, user_data, flag, NULL); -} - -/** - * re-usable function, use when replacing ID's - */ -void BKE_library_update_ID_link_user(ID *id_dst, ID *id_src, const int cb_flag) -{ - if (cb_flag & IDWALK_CB_USER) { - id_us_min(id_src); - id_us_plus(id_dst); - } - else if (cb_flag & IDWALK_CB_USER_ONE) { - id_us_ensure_real(id_dst); - } -} - -/** - * Say whether given \a id_type_owner can use (in any way) a data-block of \a id_type_used. - * - * This is a 'simplified' abstract version of #BKE_library_foreach_ID_link() above, - * quite useful to reduce* useless iterations in some cases. - */ -/* XXX This has to be fully rethink, basing check on ID type is not really working anymore - * (and even worth once IDProps will support ID pointers), - * we'll have to do some quick checks on IDs themselves... */ -bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used) -{ - /* any type of ID can be used in custom props. */ - if (id_owner->properties) { - return true; - } - - const short id_type_owner = GS(id_owner->name); - - /* IDProps of armature bones and nodes, and bNode->id can use virtually any type of ID. */ - if (ELEM(id_type_owner, ID_NT, ID_AR)) { - return true; - } - - if (ntreeFromID(id_owner)) { - return true; - } - - if (BKE_animdata_from_id(id_owner)) { - /* AnimationData can use virtually any kind of data-blocks, through drivers especially. */ - return true; - } - - switch ((ID_Type)id_type_owner) { - case ID_LI: - return ELEM(id_type_used, ID_LI); - case ID_SCE: - return (ELEM(id_type_used, - ID_OB, - ID_WO, - ID_SCE, - ID_MC, - ID_MA, - ID_GR, - ID_TXT, - ID_LS, - ID_MSK, - ID_SO, - ID_GD, - ID_BR, - ID_PAL, - ID_IM, - ID_NT)); - case ID_OB: - /* Could be more specific, but simpler to just always say 'yes' here. */ - return true; - case ID_ME: - return ELEM(id_type_used, ID_ME, ID_KE, ID_MA, ID_IM); - case ID_CU: - return ELEM(id_type_used, ID_OB, ID_KE, ID_MA, ID_VF); - case ID_MB: - return ELEM(id_type_used, ID_MA); - case ID_MA: - return (ELEM(id_type_used, ID_TE, ID_GR)); - case ID_TE: - return (ELEM(id_type_used, ID_IM, ID_OB)); - case ID_LT: - return ELEM(id_type_used, ID_KE); - case ID_LA: - return (ELEM(id_type_used, ID_TE)); - case ID_CA: - return ELEM(id_type_used, ID_OB); - case ID_KE: - /* Warning! key->from, could be more types in future? */ - return ELEM(id_type_used, ID_ME, ID_CU, ID_LT); - case ID_SCR: - return ELEM(id_type_used, ID_SCE); - case ID_WO: - return (ELEM(id_type_used, ID_TE)); - case ID_SPK: - return ELEM(id_type_used, ID_SO); - case ID_GR: - return ELEM(id_type_used, ID_OB, ID_GR); - case ID_NT: - /* Could be more specific, but node.id has no type restriction... */ - return true; - case ID_BR: - return ELEM(id_type_used, ID_BR, ID_IM, ID_PC, ID_TE, ID_MA); - case ID_PA: - return ELEM(id_type_used, ID_OB, ID_GR, ID_TE); - case ID_MC: - return ELEM(id_type_used, ID_GD, ID_IM); - case ID_MSK: - /* WARNING! mask->parent.id, not typed. */ - return ELEM(id_type_used, ID_MC); - case ID_LS: - return (ELEM(id_type_used, ID_TE, ID_OB)); - case ID_LP: - return ELEM(id_type_used, ID_IM); - case ID_GD: - return ELEM(id_type_used, ID_MA); - case ID_WS: - return ELEM(id_type_used, ID_SCR, ID_SCE); - case ID_IM: - case ID_VF: - case ID_TXT: - case ID_SO: - case ID_AR: - case ID_AC: - case ID_WM: - case ID_PAL: - case ID_PC: - case ID_CF: - /* Those types never use/reference other IDs... */ - return false; - case ID_IP: - /* Deprecated... */ - return false; - } - return false; -} - -/* ***** ID users iterator. ***** */ -typedef struct IDUsersIter { - ID *id; - - ListBase *lb_array[MAX_LIBARRAY]; - int lb_idx; - - ID *curr_id; - int count_direct, count_indirect; /* Set by callback. */ -} IDUsersIter; - -static int foreach_libblock_id_users_callback(void *user_data, - ID *UNUSED(self_id), - ID **id_p, - int cb_flag) -{ - IDUsersIter *iter = user_data; - - if (*id_p) { - /* 'Loopback' ID pointers (the ugly 'from' ones, Object->proxy_from and Key->from). - * Those are not actually ID usage, we can ignore them here. - */ - if (cb_flag & IDWALK_CB_LOOPBACK) { - return IDWALK_RET_NOP; - } - - if (*id_p == iter->id) { -#if 0 - printf( - "%s uses %s (refcounted: %d, userone: %d, used_one: %d, used_one_active: %d, " - "indirect_usage: %d)\n", - iter->curr_id->name, - iter->id->name, - (cb_flag & IDWALK_USER) ? 1 : 0, - (cb_flag & IDWALK_USER_ONE) ? 1 : 0, - (iter->id->tag & LIB_TAG_EXTRAUSER) ? 1 : 0, - (iter->id->tag & LIB_TAG_EXTRAUSER_SET) ? 1 : 0, - (cb_flag & IDWALK_INDIRECT_USAGE) ? 1 : 0); -#endif - if (cb_flag & IDWALK_CB_INDIRECT_USAGE) { - iter->count_indirect++; - } - else { - iter->count_direct++; - } - } - } - - return IDWALK_RET_NOP; -} - -/** - * Return the number of times given \a id_user uses/references \a id_used. - * - * \note This only checks for pointer references of an ID, shallow usages - * (like e.g. by RNA paths, as done for FCurves) are not detected at all. - * - * \param id_user: the ID which is supposed to use (reference) \a id_used. - * \param id_used: the ID which is supposed to be used (referenced) by \a id_user. - * \return the number of direct usages/references of \a id_used by \a id_user. - */ -int BKE_library_ID_use_ID(ID *id_user, ID *id_used) -{ - IDUsersIter iter; - - /* We do not care about iter.lb_array/lb_idx here... */ - iter.id = id_used; - iter.curr_id = id_user; - iter.count_direct = iter.count_indirect = 0; - - BKE_library_foreach_ID_link( - NULL, iter.curr_id, foreach_libblock_id_users_callback, (void *)&iter, IDWALK_READONLY); - - return iter.count_direct + iter.count_indirect; -} - -static bool library_ID_is_used(Main *bmain, void *idv, const bool check_linked) -{ - IDUsersIter iter; - ListBase *lb_array[MAX_LIBARRAY]; - ID *id = idv; - int i = set_listbasepointers(bmain, lb_array); - bool is_defined = false; - - iter.id = id; - iter.count_direct = iter.count_indirect = 0; - while (i-- && !is_defined) { - ID *id_curr = lb_array[i]->first; - - if (!id_curr || !BKE_library_id_can_use_idtype(id_curr, GS(id->name))) { - continue; - } - - for (; id_curr && !is_defined; id_curr = id_curr->next) { - if (id_curr == id) { - /* We are not interested in self-usages (mostly from drivers or bone constraints...). */ - continue; - } - iter.curr_id = id_curr; - BKE_library_foreach_ID_link( - bmain, id_curr, foreach_libblock_id_users_callback, &iter, IDWALK_READONLY); - - is_defined = ((check_linked ? iter.count_indirect : iter.count_direct) != 0); - } - } - - return is_defined; -} - -/** - * Check whether given ID is used locally (i.e. by another non-linked ID). - */ -bool BKE_library_ID_is_locally_used(Main *bmain, void *idv) -{ - return library_ID_is_used(bmain, idv, false); -} - -/** - * Check whether given ID is used indirectly (i.e. by another linked ID). - */ -bool BKE_library_ID_is_indirectly_used(Main *bmain, void *idv) -{ - return library_ID_is_used(bmain, idv, true); -} - -/** - * Combine #BKE_library_ID_is_locally_used() and #BKE_library_ID_is_indirectly_used() - * in a single call. - */ -void BKE_library_ID_test_usages(Main *bmain, void *idv, bool *is_used_local, bool *is_used_linked) -{ - IDUsersIter iter; - ListBase *lb_array[MAX_LIBARRAY]; - ID *id = idv; - int i = set_listbasepointers(bmain, lb_array); - bool is_defined = false; - - iter.id = id; - iter.count_direct = iter.count_indirect = 0; - while (i-- && !is_defined) { - ID *id_curr = lb_array[i]->first; - - if (!id_curr || !BKE_library_id_can_use_idtype(id_curr, GS(id->name))) { - continue; - } - - for (; id_curr && !is_defined; id_curr = id_curr->next) { - if (id_curr == id) { - /* We are not interested in self-usages (mostly from drivers or bone constraints...). */ - continue; - } - iter.curr_id = id_curr; - BKE_library_foreach_ID_link( - bmain, id_curr, foreach_libblock_id_users_callback, &iter, IDWALK_READONLY); - - is_defined = (iter.count_direct != 0 && iter.count_indirect != 0); - } - } - - *is_used_local = (iter.count_direct != 0); - *is_used_linked = (iter.count_indirect != 0); -} - -/* ***** IDs usages.checking/tagging. ***** */ -static int foreach_libblock_used_linked_data_tag_clear_cb(void *user_data, - ID *self_id, - ID **id_p, - int cb_flag) -{ - bool *is_changed = user_data; - - if (*id_p) { - /* The infamous 'from' pointers (Key.from, Object.proxy_from, ...). - * those are not actually ID usage, so we ignore them here. */ - if (cb_flag & IDWALK_CB_LOOPBACK) { - return IDWALK_RET_NOP; - } - - /* If checked id is used by an assumed used ID, - * then it is also used and not part of any linked archipelago. */ - if (!(self_id->tag & LIB_TAG_DOIT) && ((*id_p)->tag & LIB_TAG_DOIT)) { - (*id_p)->tag &= ~LIB_TAG_DOIT; - *is_changed = true; - } - } - - return IDWALK_RET_NOP; -} - -/** - * Detect orphaned linked data blocks (i.e. linked data not used (directly or indirectly) - * in any way by any local data), including complex cases like 'linked archipelagoes', i.e. - * linked data-blocks that use each other in loops, - * which prevents their deletion by 'basic' usage checks. - * - * \param do_init_tag: if \a true, all linked data are checked, if \a false, - * only linked data-blocks already tagged with #LIB_TAG_DOIT are checked. - */ -void BKE_library_unused_linked_data_set_tag(Main *bmain, const bool do_init_tag) -{ - ID *id; - - if (do_init_tag) { - FOREACH_MAIN_ID_BEGIN (bmain, id) { - if (id->lib && (id->tag & LIB_TAG_INDIRECT) != 0) { - id->tag |= LIB_TAG_DOIT; - } - else { - id->tag &= ~LIB_TAG_DOIT; - } - } - FOREACH_MAIN_ID_END; - } - - for (bool do_loop = true; do_loop;) { - do_loop = false; - FOREACH_MAIN_ID_BEGIN (bmain, id) { - /* We only want to check that ID if it is currently known as used... */ - if ((id->tag & LIB_TAG_DOIT) == 0) { - BKE_library_foreach_ID_link( - bmain, id, foreach_libblock_used_linked_data_tag_clear_cb, &do_loop, IDWALK_READONLY); - } - } - FOREACH_MAIN_ID_END; - } -} - -/** - * Untag linked data blocks used by other untagged linked data-blocks. - * Used to detect data-blocks that we can forcefully make local - * (instead of copying them to later get rid of original): - * All data-blocks we want to make local are tagged by caller, - * after this function has ran caller knows data-blocks still tagged can directly be made local, - * since they are only used by other data-blocks that will also be made fully local. - */ -void BKE_library_indirectly_used_data_tag_clear(Main *bmain) -{ - ListBase *lb_array[MAX_LIBARRAY]; - - bool do_loop = true; - while (do_loop) { - int i = set_listbasepointers(bmain, lb_array); - do_loop = false; - - while (i--) { - for (ID *id = lb_array[i]->first; id; id = id->next) { - if (id->lib == NULL || id->tag & LIB_TAG_DOIT) { - /* Local or non-indirectly-used ID (so far), no need to check it further. */ - continue; - } - BKE_library_foreach_ID_link( - bmain, id, foreach_libblock_used_linked_data_tag_clear_cb, &do_loop, IDWALK_READONLY); - } - } - } -} diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c deleted file mode 100644 index 72b94e7b356..00000000000 --- a/source/blender/blenkernel/intern/library_remap.c +++ /dev/null @@ -1,1184 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup bke - * - * Contains management of ID's and libraries remap, unlink and free logic. - */ - -#include -#include -#include -#include -#include -#include - -#include "CLG_log.h" - -#include "MEM_guardedalloc.h" - -/* all types are needed here, in order to do memory operations */ -#include "DNA_anim_types.h" -#include "DNA_armature_types.h" -#include "DNA_brush_types.h" -#include "DNA_camera_types.h" -#include "DNA_cachefile_types.h" -#include "DNA_collection_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_ipo_types.h" -#include "DNA_key_types.h" -#include "DNA_light_types.h" -#include "DNA_lattice_types.h" -#include "DNA_linestyle_types.h" -#include "DNA_material_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meta_types.h" -#include "DNA_movieclip_types.h" -#include "DNA_mask_types.h" -#include "DNA_node_types.h" -#include "DNA_object_types.h" -#include "DNA_lightprobe_types.h" -#include "DNA_scene_types.h" -#include "DNA_screen_types.h" -#include "DNA_speaker_types.h" -#include "DNA_sound_types.h" -#include "DNA_text_types.h" -#include "DNA_vfont_types.h" -#include "DNA_windowmanager_types.h" -#include "DNA_workspace_types.h" -#include "DNA_world_types.h" - -#include "BLI_blenlib.h" -#include "BLI_utildefines.h" - -#include "BKE_action.h" -#include "BKE_animsys.h" -#include "BKE_armature.h" -#include "BKE_brush.h" -#include "BKE_camera.h" -#include "BKE_cachefile.h" -#include "BKE_collection.h" -#include "BKE_curve.h" -#include "BKE_fcurve.h" -#include "BKE_font.h" -#include "BKE_gpencil.h" -#include "BKE_idprop.h" -#include "BKE_image.h" -#include "BKE_ipo.h" -#include "BKE_key.h" -#include "BKE_light.h" -#include "BKE_lattice.h" -#include "BKE_layer.h" -#include "BKE_library.h" -#include "BKE_library_override.h" -#include "BKE_library_query.h" -#include "BKE_library_remap.h" -#include "BKE_linestyle.h" -#include "BKE_mesh.h" -#include "BKE_material.h" -#include "BKE_main.h" -#include "BKE_mask.h" -#include "BKE_mball.h" -#include "BKE_modifier.h" -#include "BKE_movieclip.h" -#include "BKE_multires.h" -#include "BKE_node.h" -#include "BKE_object.h" -#include "BKE_paint.h" -#include "BKE_particle.h" -#include "BKE_lightprobe.h" -#include "BKE_speaker.h" -#include "BKE_sound.h" -#include "BKE_screen.h" -#include "BKE_scene.h" -#include "BKE_text.h" -#include "BKE_texture.h" -#include "BKE_workspace.h" -#include "BKE_world.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_build.h" - -#ifdef WITH_PYTHON -# include "BPY_extern.h" -#endif - -static CLG_LogRef LOG = {"bke.library_remap"}; - -static BKE_library_free_window_manager_cb free_windowmanager_cb = NULL; - -void BKE_library_callback_free_window_manager_set(BKE_library_free_window_manager_cb func) -{ - free_windowmanager_cb = func; -} - -static BKE_library_free_notifier_reference_cb free_notifier_reference_cb = NULL; - -void BKE_library_callback_free_notifier_reference_set(BKE_library_free_notifier_reference_cb func) -{ - free_notifier_reference_cb = func; -} - -static BKE_library_remap_editor_id_reference_cb remap_editor_id_reference_cb = NULL; - -void BKE_library_callback_remap_editor_id_reference_set( - BKE_library_remap_editor_id_reference_cb func) -{ - remap_editor_id_reference_cb = func; -} - -typedef struct IDRemap { - Main *bmain; /* Only used to trigger depsgraph updates in the right bmain. */ - ID *old_id; - ID *new_id; - /** The ID in which we are replacing old_id by new_id usages. */ - ID *id; - short flag; - - /* 'Output' data. */ - short status; - /** Number of direct usecases that could not be remapped (e.g.: obdata when in edit mode). */ - int skipped_direct; - /** Number of indirect usecases that could not be remapped. */ - int skipped_indirect; - /** Number of skipped usecases that refcount the datablock. */ - int skipped_refcounted; -} IDRemap; - -/* IDRemap->flag enums defined in BKE_library.h */ - -/* IDRemap->status */ -enum { - /* *** Set by callback. *** */ - ID_REMAP_IS_LINKED_DIRECT = 1 << 0, /* new_id is directly linked in current .blend. */ - ID_REMAP_IS_USER_ONE_SKIPPED = 1 << 1, /* There was some skipped 'user_one' usages of old_id. */ -}; - -static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id_p, int cb_flag) -{ - if (cb_flag & IDWALK_CB_PRIVATE) { - return IDWALK_RET_NOP; - } - - IDRemap *id_remap_data = user_data; - ID *old_id = id_remap_data->old_id; - ID *new_id = id_remap_data->new_id; - ID *id = id_remap_data->id; - - if (!old_id) { /* Used to cleanup all IDs used by a specific one. */ - BLI_assert(!new_id); - old_id = *id_p; - } - - if (*id_p && (*id_p == old_id)) { - /* Better remap to NULL than not remapping at all, - * then we can handle it as a regular remap-to-NULL case. */ - if ((cb_flag & IDWALK_CB_NEVER_SELF) && (new_id == id_self)) { - new_id = NULL; - } - - const bool is_reference = (cb_flag & IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE) != 0; - const bool is_indirect = (cb_flag & IDWALK_CB_INDIRECT_USAGE) != 0; - const bool skip_indirect = (id_remap_data->flag & ID_REMAP_SKIP_INDIRECT_USAGE) != 0; - /* Note: proxy usage implies LIB_TAG_EXTERN, so on this aspect it is direct, - * on the other hand since they get reset to lib data on file open/reload it is indirect too. - * Edit Mode is also a 'skip direct' case. */ - const bool is_obj = (GS(id->name) == ID_OB); - const bool is_obj_proxy = (is_obj && (((Object *)id)->proxy || ((Object *)id)->proxy_group)); - const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id)); - const bool is_never_null = ((cb_flag & IDWALK_CB_NEVER_NULL) && (new_id == NULL) && - (id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0); - const bool skip_reference = (id_remap_data->flag & ID_REMAP_SKIP_OVERRIDE_LIBRARY) != 0; - const bool skip_never_null = (id_remap_data->flag & ID_REMAP_SKIP_NEVER_NULL_USAGE) != 0; - -#ifdef DEBUG_PRINT - printf( - "In %s (lib %p): Remapping %s (%p) to %s (%p) " - "(is_indirect: %d, skip_indirect: %d, is_reference: %d, skip_reference: %d)\n", - id->name, - id->lib, - old_id->name, - old_id, - new_id ? new_id->name : "", - new_id, - is_indirect, - skip_indirect, - is_reference, - skip_reference); -#endif - - if ((id_remap_data->flag & ID_REMAP_FLAG_NEVER_NULL_USAGE) && - (cb_flag & IDWALK_CB_NEVER_NULL)) { - id->tag |= LIB_TAG_DOIT; - } - - /* Special hack in case it's Object->data and we are in edit mode, and new_id is not NULL - * (otherwise, we follow common NEVER_NULL flags). - * (skipped_indirect too). */ - if ((is_never_null && skip_never_null) || - (is_obj_editmode && (((Object *)id)->data == *id_p) && new_id != NULL) || - (skip_indirect && is_indirect) || (is_reference && skip_reference)) { - if (is_indirect) { - id_remap_data->skipped_indirect++; - if (is_obj) { - Object *ob = (Object *)id; - if (ob->data == *id_p && ob->proxy != NULL) { - /* And another 'Proudly brought to you by Proxy Hell' hack! - * This will allow us to avoid clearing 'LIB_EXTERN' flag of obdata of proxies... */ - id_remap_data->skipped_direct++; - } - } - } - else if (is_never_null || is_obj_editmode || is_reference) { - id_remap_data->skipped_direct++; - } - else { - BLI_assert(0); - } - if (cb_flag & IDWALK_CB_USER) { - id_remap_data->skipped_refcounted++; - } - else if (cb_flag & IDWALK_CB_USER_ONE) { - /* No need to count number of times this happens, just a flag is enough. */ - id_remap_data->status |= ID_REMAP_IS_USER_ONE_SKIPPED; - } - } - else { - if (!is_never_null) { - *id_p = new_id; - DEG_id_tag_update_ex(id_remap_data->bmain, - id_self, - ID_RECALC_COPY_ON_WRITE | ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - } - if (cb_flag & IDWALK_CB_USER) { - /* NOTE: We don't user-count IDs which are not in the main database. - * This is because in certain conditions we can have data-blocks in - * the main which are referencing data-blocks outside of it. - * For example, BKE_mesh_new_from_object() called on an evaluated - * object will cause such situation. - */ - if ((old_id->tag & LIB_TAG_NO_MAIN) == 0) { - id_us_min(old_id); - } - if (new_id != NULL && (new_id->tag & LIB_TAG_NO_MAIN) == 0) { - /* We do not want to handle LIB_TAG_INDIRECT/LIB_TAG_EXTERN here. */ - new_id->us++; - } - } - else if (cb_flag & IDWALK_CB_USER_ONE) { - id_us_ensure_real(new_id); - /* We cannot affect old_id->us directly, LIB_TAG_EXTRAUSER(_SET) - * are assumed to be set as needed, that extra user is processed in final handling. */ - } - if (!is_indirect || is_obj_proxy) { - id_remap_data->status |= ID_REMAP_IS_LINKED_DIRECT; - } - /* We need to remap proxy_from pointer of remapped proxy... sigh. */ - if (is_obj_proxy && new_id != NULL) { - Object *ob = (Object *)id; - if (ob->proxy == (Object *)new_id) { - ob->proxy->proxy_from = ob; - } - } - } - } - - return IDWALK_RET_NOP; -} - -static void libblock_remap_data_preprocess(IDRemap *r_id_remap_data) -{ - switch (GS(r_id_remap_data->id->name)) { - case ID_OB: { - ID *old_id = r_id_remap_data->old_id; - if (!old_id || GS(old_id->name) == ID_AR) { - Object *ob = (Object *)r_id_remap_data->id; - /* Object's pose holds reference to armature bones... sic */ - /* Note that in theory, we should have to bother about - * linked/non-linked/never-null/etc. flags/states. - * Fortunately, this is just a tag, so we can accept to 'over-tag' a bit for pose recalc, - * and avoid another complex and risky condition nightmare like the one we have in - * foreach_libblock_remap_callback()... */ - if (ob->pose && (!old_id || ob->data == old_id)) { - BLI_assert(ob->type == OB_ARMATURE); - ob->pose->flag |= POSE_RECALC; - /* We need to clear pose bone pointers immediately, things like undo writefile may be - * called before pose is actually recomputed, can lead to segfault... */ - BKE_pose_clear_pointers(ob->pose); - } - } - break; - } - default: - break; - } -} - -/** - * Can be called with both old_ob and new_ob being NULL, - * this means we have to check whole Main database then. - */ -static void libblock_remap_data_postprocess_object_update(Main *bmain, - Object *old_ob, - Object *new_ob) -{ - if (new_ob == NULL) { - /* In case we unlinked old_ob (new_ob is NULL), the object has already - * been removed from the scenes and their collections. We still have - * to remove the NULL children from collections not used in any scene. */ - BKE_collections_object_remove_nulls(bmain); - } - - BKE_main_collection_sync_remap(bmain); - - if (old_ob == NULL) { - for (Object *ob = bmain->objects.first; ob != NULL; ob = ob->id.next) { - if (ob->type == OB_MBALL && BKE_mball_is_basis(ob)) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - } - } - } - else { - for (Object *ob = bmain->objects.first; ob != NULL; ob = ob->id.next) { - if (ob->type == OB_MBALL && BKE_mball_is_basis_for(ob, old_ob)) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - break; /* There is only one basis... */ - } - } - } -} - -/* Can be called with both old_collection and new_collection being NULL, - * this means we have to check whole Main database then. */ -static void libblock_remap_data_postprocess_collection_update(Main *bmain, - Collection *UNUSED(old_collection), - Collection *new_collection) -{ - if (new_collection == NULL) { - /* XXX Complex cases can lead to NULL pointers in other collections than old_collection, - * and BKE_main_collection_sync_remap() does not tolerate any of those, so for now always check - * whole existing collections for NULL pointers. - * I'd consider optimizing that whole collection remapping process a TODO for later. */ - BKE_collections_child_remove_nulls(bmain, NULL /*old_collection*/); - } - else { - /* Temp safe fix, but a "tad" brute force... We should probably be able to use parents from - * old_collection instead? */ - BKE_main_collections_parent_relations_rebuild(bmain); - } - - BKE_main_collection_sync_remap(bmain); -} - -static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *ob, ID *new_id) -{ - if (ob->data == new_id) { - switch (GS(new_id->name)) { - case ID_ME: - multires_force_sculpt_rebuild(ob); - break; - case ID_CU: - BKE_curve_type_test(ob); - break; - default: - break; - } - test_object_modifiers(ob); - BKE_object_materials_test(bmain, ob, new_id); - } -} - -static void libblock_remap_data_postprocess_nodetree_update(Main *bmain, ID *new_id) -{ - /* Update all group nodes using a node group. */ - ntreeUpdateAllUsers(bmain, new_id); -} - -/** - * Execute the 'data' part of the remapping (that is, all ID pointers from other ID data-blocks). - * - * Behavior differs depending on whether given \a id is NULL or not: - * - \a id NULL: \a old_id must be non-NULL, \a new_id may be NULL (unlinking \a old_id) or not - * (remapping \a old_id to \a new_id). - * The whole \a bmain database is checked, and all pointers to \a old_id - * are remapped to \a new_id. - * - \a id is non-NULL: - * + If \a old_id is NULL, \a new_id must also be NULL, - * and all ID pointers from \a id are cleared - * (i.e. \a id does not references any other data-block anymore). - * + If \a old_id is non-NULL, behavior is as with a NULL \a id, but only within given \a id. - * - * \param bmain: the Main data storage to operate on (must never be NULL). - * \param id: the data-block to operate on - * (can be NULL, in which case we operate over all IDs from given bmain). - * \param old_id: the data-block to dereference (may be NULL if \a id is non-NULL). - * \param new_id: the new data-block to replace \a old_id references with (may be NULL). - * \param r_id_remap_data: if non-NULL, the IDRemap struct to use - * (uselful to retrieve info about remapping process). - */ -ATTR_NONNULL(1) -static void libblock_remap_data( - Main *bmain, ID *id, ID *old_id, ID *new_id, const short remap_flags, IDRemap *r_id_remap_data) -{ - IDRemap id_remap_data; - const int foreach_id_flags = (remap_flags & ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE) != 0 ? - IDWALK_NO_INDIRECT_PROXY_DATA_USAGE : - IDWALK_NOP; - - if (r_id_remap_data == NULL) { - r_id_remap_data = &id_remap_data; - } - r_id_remap_data->bmain = bmain; - r_id_remap_data->old_id = old_id; - r_id_remap_data->new_id = new_id; - r_id_remap_data->id = NULL; - r_id_remap_data->flag = remap_flags; - r_id_remap_data->status = 0; - r_id_remap_data->skipped_direct = 0; - r_id_remap_data->skipped_indirect = 0; - r_id_remap_data->skipped_refcounted = 0; - - if (id) { -#ifdef DEBUG_PRINT - printf("\tchecking id %s (%p, %p)\n", id->name, id, id->lib); -#endif - r_id_remap_data->id = id; - libblock_remap_data_preprocess(r_id_remap_data); - BKE_library_foreach_ID_link( - NULL, id, foreach_libblock_remap_callback, (void *)r_id_remap_data, foreach_id_flags); - } - else { - /* Note that this is a very 'brute force' approach, - * maybe we could use some depsgraph to only process objects actually using given old_id... - * sounds rather unlikely currently, though, so this will do for now. */ - ID *id_curr; - - FOREACH_MAIN_ID_BEGIN (bmain, id_curr) { - if (BKE_library_id_can_use_idtype(id_curr, GS(old_id->name))) { - /* Note that we cannot skip indirect usages of old_id here (if requested), - * we still need to check it for the user count handling... - * XXX No more true (except for debug usage of those skipping counters). */ - r_id_remap_data->id = id_curr; - libblock_remap_data_preprocess(r_id_remap_data); - BKE_library_foreach_ID_link(NULL, - id_curr, - foreach_libblock_remap_callback, - (void *)r_id_remap_data, - foreach_id_flags); - } - } - FOREACH_MAIN_ID_END; - } - - /* XXX We may not want to always 'transfer' fake-user from old to new id... - * Think for now it's desired behavior though, - * we can always add an option (flag) to control this later if needed. */ - if (old_id && (old_id->flag & LIB_FAKEUSER)) { - id_fake_user_clear(old_id); - id_fake_user_set(new_id); - } - - id_us_clear_real(old_id); - - if (new_id && (new_id->tag & LIB_TAG_INDIRECT) && - (r_id_remap_data->status & ID_REMAP_IS_LINKED_DIRECT)) { - new_id->tag &= ~LIB_TAG_INDIRECT; - new_id->flag &= ~LIB_INDIRECT_WEAK_LINK; - new_id->tag |= LIB_TAG_EXTERN; - } - -#ifdef DEBUG_PRINT - printf("%s: %d occurrences skipped (%d direct and %d indirect ones)\n", - __func__, - r_id_remap_data->skipped_direct + r_id_remap_data->skipped_indirect, - r_id_remap_data->skipped_direct, - r_id_remap_data->skipped_indirect); -#endif -} - -/** - * Replace all references in given Main to \a old_id by \a new_id - * (if \a new_id is NULL, it unlinks \a old_id). - */ -void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const short remap_flags) -{ - IDRemap id_remap_data; - ID *old_id = old_idv; - ID *new_id = new_idv; - int skipped_direct, skipped_refcounted; - - BLI_assert(old_id != NULL); - BLI_assert((new_id == NULL) || GS(old_id->name) == GS(new_id->name)); - BLI_assert(old_id != new_id); - - libblock_remap_data(bmain, NULL, old_id, new_id, remap_flags, &id_remap_data); - - if (free_notifier_reference_cb) { - free_notifier_reference_cb(old_id); - } - - /* We assume editors do not hold references to their IDs... This is false in some cases - * (Image is especially tricky here), - * editors' code is to handle refcount (id->us) itself then. */ - if (remap_editor_id_reference_cb) { - remap_editor_id_reference_cb(old_id, new_id); - } - - skipped_direct = id_remap_data.skipped_direct; - skipped_refcounted = id_remap_data.skipped_refcounted; - - /* If old_id was used by some ugly 'user_one' stuff (like Image or Clip editors...), and user - * count has actually been incremented for that, we have to decrease once more its user count... - * unless we had to skip some 'user_one' cases. */ - if ((old_id->tag & LIB_TAG_EXTRAUSER_SET) && - !(id_remap_data.status & ID_REMAP_IS_USER_ONE_SKIPPED)) { - id_us_clear_real(old_id); - } - - if (old_id->us - skipped_refcounted < 0) { - CLOG_ERROR(&LOG, - "Error in remapping process from '%s' (%p) to '%s' (%p): " - "wrong user count in old ID after process (summing up to %d)", - old_id->name, - old_id, - new_id ? new_id->name : "", - new_id, - old_id->us - skipped_refcounted); - BLI_assert(0); - } - - if (skipped_direct == 0) { - /* old_id is assumed to not be used directly anymore... */ - if (old_id->lib && (old_id->tag & LIB_TAG_EXTERN)) { - old_id->tag &= ~LIB_TAG_EXTERN; - old_id->tag |= LIB_TAG_INDIRECT; - } - } - - /* Some after-process updates. - * This is a bit ugly, but cannot see a way to avoid it. - * Maybe we should do a per-ID callback for this instead? */ - switch (GS(old_id->name)) { - case ID_OB: - libblock_remap_data_postprocess_object_update(bmain, (Object *)old_id, (Object *)new_id); - break; - case ID_GR: - libblock_remap_data_postprocess_collection_update( - bmain, (Collection *)old_id, (Collection *)new_id); - break; - case ID_ME: - case ID_CU: - case ID_MB: - if (new_id) { /* Only affects us in case obdata was relinked (changed). */ - for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) { - libblock_remap_data_postprocess_obdata_relink(bmain, ob, new_id); - } - } - break; - default: - break; - } - - /* Node trees may virtually use any kind of data-block... */ - /* XXX Yuck!!!! nodetree update can do pretty much any thing when talking about py nodes, - * including creating new data-blocks (see T50385), so we need to unlock main here. :( - * Why can't we have re-entrent locks? */ - BKE_main_unlock(bmain); - libblock_remap_data_postprocess_nodetree_update(bmain, new_id); - BKE_main_lock(bmain); - - /* Full rebuild of DEG! */ - DEG_relations_tag_update(bmain); -} - -void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, const short remap_flags) -{ - BKE_main_lock(bmain); - - BKE_libblock_remap_locked(bmain, old_idv, new_idv, remap_flags); - - BKE_main_unlock(bmain); -} - -/** - * Unlink given \a id from given \a bmain - * (does not touch to indirect, i.e. library, usages of the ID). - * - * \param do_flag_never_null: If true, all IDs using \a idv in a 'non-NULL' way are flagged by - * #LIB_TAG_DOIT flag (quite obviously, 'non-NULL' usages can never be unlinked by this function). - */ -void BKE_libblock_unlink(Main *bmain, - void *idv, - const bool do_flag_never_null, - const bool do_skip_indirect) -{ - const short remap_flags = (do_skip_indirect ? ID_REMAP_SKIP_INDIRECT_USAGE : 0) | - (do_flag_never_null ? ID_REMAP_FLAG_NEVER_NULL_USAGE : 0); - - BKE_main_lock(bmain); - - BKE_libblock_remap_locked(bmain, idv, NULL, remap_flags); - - BKE_main_unlock(bmain); -} - -/** - * Similar to libblock_remap, but only affects IDs used by given \a idv ID. - * - * \param old_idv: Unlike BKE_libblock_remap, can be NULL, - * in which case all ID usages by given \a idv will be cleared. - * \param us_min_never_null: If \a true and new_id is NULL, - * 'NEVER_NULL' ID usages keep their old id, but this one still gets its user count decremented - * (needed when given \a idv is going to be deleted right after being unlinked). - */ -/* Should be able to replace all _relink() funcs (constraints, rigidbody, etc.) ? */ -/* XXX Arg! Naming... :( - * _relink? avoids confusion with _remap, but is confusing with _unlink - * _remap_used_ids? - * _remap_datablocks? - * BKE_id_remap maybe? - * ... sigh - */ -void BKE_libblock_relink_ex( - Main *bmain, void *idv, void *old_idv, void *new_idv, const short remap_flags) -{ - ID *id = idv; - ID *old_id = old_idv; - ID *new_id = new_idv; - - /* No need to lock here, we are only affecting given ID, not bmain database. */ - - BLI_assert(id); - if (old_id) { - BLI_assert((new_id == NULL) || GS(old_id->name) == GS(new_id->name)); - BLI_assert(old_id != new_id); - } - else { - BLI_assert(new_id == NULL); - } - - libblock_remap_data(bmain, id, old_id, new_id, remap_flags, NULL); - - /* Some after-process updates. - * This is a bit ugly, but cannot see a way to avoid it. - * Maybe we should do a per-ID callback for this instead? - */ - switch (GS(id->name)) { - case ID_SCE: - case ID_GR: { - if (old_id) { - switch (GS(old_id->name)) { - case ID_OB: - libblock_remap_data_postprocess_object_update( - bmain, (Object *)old_id, (Object *)new_id); - break; - case ID_GR: - libblock_remap_data_postprocess_collection_update( - bmain, (Collection *)old_id, (Collection *)new_id); - break; - default: - break; - } - } - else { - /* No choice but to check whole objects/collections. */ - libblock_remap_data_postprocess_collection_update(bmain, NULL, NULL); - libblock_remap_data_postprocess_object_update(bmain, NULL, NULL); - } - break; - } - case ID_OB: - if (new_id) { /* Only affects us in case obdata was relinked (changed). */ - libblock_remap_data_postprocess_obdata_relink(bmain, (Object *)id, new_id); - } - break; - default: - break; - } - - DEG_relations_tag_update(bmain); -} - -static int id_relink_to_newid_looper(void *UNUSED(user_data), - ID *UNUSED(self_id), - ID **id_pointer, - const int cb_flag) -{ - if (cb_flag & IDWALK_CB_PRIVATE) { - return IDWALK_RET_NOP; - } - - ID *id = *id_pointer; - if (id) { - /* See: NEW_ID macro */ - if (id->newid) { - BKE_library_update_ID_link_user(id->newid, id, cb_flag); - *id_pointer = id->newid; - } - else if (id->tag & LIB_TAG_NEW) { - id->tag &= ~LIB_TAG_NEW; - BKE_libblock_relink_to_newid(id); - } - } - return IDWALK_RET_NOP; -} - -/** - * Similar to #libblock_relink_ex, - * but is remapping IDs to their newid value if non-NULL, in given \a id. - * - * Very specific usage, not sure we'll keep it on the long run, - * currently only used in Object/Collection duplication code... - */ -void BKE_libblock_relink_to_newid(ID *id) -{ - if (ID_IS_LINKED(id)) { - return; - } - - BKE_library_foreach_ID_link(NULL, id, id_relink_to_newid_looper, NULL, 0); -} - -void BKE_libblock_free_data(ID *id, const bool do_id_user) -{ - if (id->properties) { - IDP_FreePropertyContent_ex(id->properties, do_id_user); - MEM_freeN(id->properties); - } - - if (id->override_library) { - BKE_override_library_free(&id->override_library, do_id_user); - } - - /* XXX TODO remove animdata handling from each type's freeing func, - * and do it here, like for copy! */ -} - -void BKE_libblock_free_datablock(ID *id, const int UNUSED(flag)) -{ - const short type = GS(id->name); - switch (type) { - case ID_SCE: - BKE_scene_free_ex((Scene *)id, false); - break; - case ID_LI: - BKE_library_free((Library *)id); - break; - case ID_OB: - BKE_object_free((Object *)id); - break; - case ID_ME: - BKE_mesh_free((Mesh *)id); - break; - case ID_CU: - BKE_curve_free((Curve *)id); - break; - case ID_MB: - BKE_mball_free((MetaBall *)id); - break; - case ID_MA: - BKE_material_free((Material *)id); - break; - case ID_TE: - BKE_texture_free((Tex *)id); - break; - case ID_IM: - BKE_image_free((Image *)id); - break; - case ID_LT: - BKE_lattice_free((Lattice *)id); - break; - case ID_LA: - BKE_light_free((Light *)id); - break; - case ID_CA: - BKE_camera_free((Camera *)id); - break; - case ID_IP: /* Deprecated. */ - BKE_ipo_free((Ipo *)id); - break; - case ID_KE: - BKE_key_free((Key *)id); - break; - case ID_WO: - BKE_world_free((World *)id); - break; - case ID_SCR: - BKE_screen_free((bScreen *)id); - break; - case ID_VF: - BKE_vfont_free((VFont *)id); - break; - case ID_TXT: - BKE_text_free((Text *)id); - break; - case ID_SPK: - BKE_speaker_free((Speaker *)id); - break; - case ID_LP: - BKE_lightprobe_free((LightProbe *)id); - break; - case ID_SO: - BKE_sound_free((bSound *)id); - break; - case ID_GR: - BKE_collection_free((Collection *)id); - break; - case ID_AR: - BKE_armature_free((bArmature *)id); - break; - case ID_AC: - BKE_action_free((bAction *)id); - break; - case ID_NT: - ntreeFreeTree((bNodeTree *)id); - break; - case ID_BR: - BKE_brush_free((Brush *)id); - break; - case ID_PA: - BKE_particlesettings_free((ParticleSettings *)id); - break; - case ID_WM: - if (free_windowmanager_cb) { - free_windowmanager_cb(NULL, (wmWindowManager *)id); - } - break; - case ID_GD: - BKE_gpencil_free((bGPdata *)id, true); - break; - case ID_MC: - BKE_movieclip_free((MovieClip *)id); - break; - case ID_MSK: - BKE_mask_free((Mask *)id); - break; - case ID_LS: - BKE_linestyle_free((FreestyleLineStyle *)id); - break; - case ID_PAL: - BKE_palette_free((Palette *)id); - break; - case ID_PC: - BKE_paint_curve_free((PaintCurve *)id); - break; - case ID_CF: - BKE_cachefile_free((CacheFile *)id); - break; - case ID_WS: - BKE_workspace_free((WorkSpace *)id); - break; - } -} - -/** - * Complete ID freeing, extended version for corner cases. - * Can override default (and safe!) freeing process, to gain some speed up. - * - * At that point, given id is assumed to not be used by any other data-block already - * (might not be actually true, in case e.g. several inter-related IDs get freed together...). - * However, they might still be using (referencing) other IDs, this code takes care of it if - * #LIB_TAG_NO_USER_REFCOUNT is not defined. - * - * \param bmain: #Main database containing the freed #ID, - * can be NULL in case it's a temp ID outside of any #Main. - * \param idv: Pointer to ID to be freed. - * \param flag: Set of \a LIB_ID_FREE_... flags controlling/overriding usual freeing process, - * 0 to get default safe behavior. - * \param use_flag_from_idtag: Still use freeing info flags from given #ID datablock, - * even if some overriding ones are passed in \a flag parameter. - */ -void BKE_id_free_ex(Main *bmain, void *idv, int flag, const bool use_flag_from_idtag) -{ - ID *id = idv; - - if (use_flag_from_idtag) { - if ((id->tag & LIB_TAG_NO_MAIN) != 0) { - flag |= LIB_ID_FREE_NO_MAIN | LIB_ID_FREE_NO_UI_USER | LIB_ID_FREE_NO_DEG_TAG; - } - else { - flag &= ~LIB_ID_FREE_NO_MAIN; - } - - if ((id->tag & LIB_TAG_NO_USER_REFCOUNT) != 0) { - flag |= LIB_ID_FREE_NO_USER_REFCOUNT; - } - else { - flag &= ~LIB_ID_FREE_NO_USER_REFCOUNT; - } - - if ((id->tag & LIB_TAG_NOT_ALLOCATED) != 0) { - flag |= LIB_ID_FREE_NOT_ALLOCATED; - } - else { - flag &= ~LIB_ID_FREE_NOT_ALLOCATED; - } - } - - BLI_assert((flag & LIB_ID_FREE_NO_MAIN) != 0 || bmain != NULL); - BLI_assert((flag & LIB_ID_FREE_NO_MAIN) != 0 || (flag & LIB_ID_FREE_NOT_ALLOCATED) == 0); - BLI_assert((flag & LIB_ID_FREE_NO_MAIN) != 0 || (flag & LIB_ID_FREE_NO_USER_REFCOUNT) == 0); - - const short type = GS(id->name); - - if (bmain && (flag & LIB_ID_FREE_NO_DEG_TAG) == 0) { - DEG_id_type_tag(bmain, type); - } - -#ifdef WITH_PYTHON -# ifdef WITH_PYTHON_SAFETY - BPY_id_release(id); -# endif - if (id->py_instance) { - BPY_DECREF_RNA_INVALIDATE(id->py_instance); - } -#endif - - if ((flag & LIB_ID_FREE_NO_USER_REFCOUNT) == 0) { - BKE_libblock_relink_ex(bmain, id, NULL, NULL, 0); - } - - BKE_libblock_free_datablock(id, flag); - - /* avoid notifying on removed data */ - if ((flag & LIB_ID_FREE_NO_MAIN) == 0) { - BKE_main_lock(bmain); - } - - if ((flag & LIB_ID_FREE_NO_UI_USER) == 0) { - if (free_notifier_reference_cb) { - free_notifier_reference_cb(id); - } - - if (remap_editor_id_reference_cb) { - remap_editor_id_reference_cb(id, NULL); - } - } - - if ((flag & LIB_ID_FREE_NO_MAIN) == 0) { - ListBase *lb = which_libbase(bmain, type); - BLI_remlink(lb, id); - } - - BKE_libblock_free_data(id, (flag & LIB_ID_FREE_NO_USER_REFCOUNT) == 0); - - if ((flag & LIB_ID_FREE_NO_MAIN) == 0) { - BKE_main_unlock(bmain); - } - - if ((flag & LIB_ID_FREE_NOT_ALLOCATED) == 0) { - MEM_freeN(id); - } -} - -/** - * Complete ID freeing, should be usable in most cases (even for out-of-Main IDs). - * - * See #BKE_id_free_ex description for full details. - * - * \param bmain: Main database containing the freed ID, - * can be NULL in case it's a temp ID outside of any Main. - * \param idv: Pointer to ID to be freed. - */ -void BKE_id_free(Main *bmain, void *idv) -{ - BKE_id_free_ex(bmain, idv, 0, true); -} - -/** - * Not really a freeing function by itself, - * it decrements usercount of given id, and only frees it if it reaches 0. - */ -void BKE_id_free_us(Main *bmain, void *idv) /* test users */ -{ - ID *id = idv; - - id_us_min(id); - - /* XXX This is a temp (2.77) hack so that we keep same behavior as in 2.76 regarding collections - * when deleting an object. Since only 'user_one' usage of objects is collections, - * and only 'real user' usage of objects is scenes, removing that 'user_one' tag when there - * is no more real (scene) users of an object ensures it gets fully unlinked. - * But only for local objects, not linked ones! - * Otherwise, there is no real way to get rid of an object anymore - - * better handling of this is TODO. - */ - if ((GS(id->name) == ID_OB) && (id->us == 1) && (id->lib == NULL)) { - id_us_clear_real(id); - } - - if (id->us == 0) { - BKE_libblock_unlink(bmain, id, false, false); - - BKE_id_free(bmain, id); - } -} - -static void id_delete(Main *bmain, const bool do_tagged_deletion) -{ - const int tag = LIB_TAG_DOIT; - ListBase *lbarray[MAX_LIBARRAY]; - Link dummy_link = {0}; - int base_count, i; - - /* Used by batch tagged deletion, when we call BKE_id_free then, id is no more in Main database, - * and has already properly unlinked its other IDs usages. - * UI users are always cleared in BKE_libblock_remap_locked() call, so we can always skip it. */ - const int free_flag = LIB_ID_FREE_NO_UI_USER | - (do_tagged_deletion ? LIB_ID_FREE_NO_MAIN | LIB_ID_FREE_NO_USER_REFCOUNT : - 0); - ListBase tagged_deleted_ids = {NULL}; - - base_count = set_listbasepointers(bmain, lbarray); - - BKE_main_lock(bmain); - if (do_tagged_deletion) { - /* Main idea of batch deletion is to remove all IDs to be deleted from Main database. - * This means that we won't have to loop over all deleted IDs to remove usages - * of other deleted IDs. - * This gives tremendous speed-up when deleting a large amount of IDs from a Main - * containing thousands of those. - * This also means that we have to be very careful here, as we by-pass many 'common' - * processing, hence risking to 'corrupt' at least user counts, if not IDs themselves. */ - bool keep_looping = true; - while (keep_looping) { - ID *id, *id_next; - ID *last_remapped_id = tagged_deleted_ids.last; - keep_looping = false; - - /* First tag and remove from Main all datablocks directly from target lib. - * Note that we go forward here, since we want to check dependencies before users - * (e.g. meshes before objects). Avoids to have to loop twice. */ - for (i = 0; i < base_count; i++) { - ListBase *lb = lbarray[i]; - - for (id = lb->first; id; id = id_next) { - id_next = id->next; - /* Note: in case we delete a library, we also delete all its datablocks! */ - if ((id->tag & tag) || (id->lib != NULL && (id->lib->id.tag & tag))) { - BLI_remlink(lb, id); - BLI_addtail(&tagged_deleted_ids, id); - /* Do not tag as no_main now, we want to unlink it first (lower-level ID management - * code has some specific handling of 'nom main' - * IDs that would be a problem in that case). */ - id->tag |= tag; - keep_looping = true; - } - } - } - if (last_remapped_id == NULL) { - dummy_link.next = tagged_deleted_ids.first; - last_remapped_id = (ID *)(&dummy_link); - } - for (id = last_remapped_id->next; id; id = id->next) { - /* Will tag 'never NULL' users of this ID too. - * Note that we cannot use BKE_libblock_unlink() here, - * since it would ignore indirect (and proxy!) - * links, this can lead to nasty crashing here in second, actual deleting loop. - * Also, this will also flag users of deleted data that cannot be unlinked - * (object using deleted obdata, etc.), so that they also get deleted. */ - BKE_libblock_remap_locked( - bmain, id, NULL, ID_REMAP_FLAG_NEVER_NULL_USAGE | ID_REMAP_FORCE_NEVER_NULL_USAGE); - /* Since we removed ID from Main, - * we also need to unlink its own other IDs usages ourself. */ - BKE_libblock_relink_ex(bmain, id, NULL, NULL, 0); - /* Now we can safely mark that ID as not being in Main database anymore. */ - id->tag |= LIB_TAG_NO_MAIN; - /* This is needed because we may not have remapped usages - * of that ID by other deleted ones. */ - // id->us = 0; /* Is it actually? */ - } - } - } - else { - /* First tag all datablocks directly from target lib. - * Note that we go forward here, since we want to check dependencies before users - * (e.g. meshes before objects). - * Avoids to have to loop twice. */ - for (i = 0; i < base_count; i++) { - ListBase *lb = lbarray[i]; - ID *id, *id_next; - - for (id = lb->first; id; id = id_next) { - id_next = id->next; - /* Note: in case we delete a library, we also delete all its datablocks! */ - if ((id->tag & tag) || (id->lib != NULL && (id->lib->id.tag & tag))) { - id->tag |= tag; - - /* Will tag 'never NULL' users of this ID too. - * Note that we cannot use BKE_libblock_unlink() here, since it would ignore indirect - * (and proxy!) links, this can lead to nasty crashing here in second, - * actual deleting loop. - * Also, this will also flag users of deleted data that cannot be unlinked - * (object using deleted obdata, etc.), so that they also get deleted. */ - BKE_libblock_remap_locked( - bmain, id, NULL, ID_REMAP_FLAG_NEVER_NULL_USAGE | ID_REMAP_FORCE_NEVER_NULL_USAGE); - } - } - } - } - BKE_main_unlock(bmain); - - /* In usual reversed order, such that all usage of a given ID, even 'never NULL' ones, - * have been already cleared when we reach it - * (e.g. Objects being processed before meshes, they'll have already released their 'reference' - * over meshes when we come to freeing obdata). */ - for (i = do_tagged_deletion ? 1 : base_count; i--;) { - ListBase *lb = lbarray[i]; - ID *id, *id_next; - - for (id = do_tagged_deletion ? tagged_deleted_ids.first : lb->first; id; id = id_next) { - id_next = id->next; - if (id->tag & tag) { - if (id->us != 0) { -#ifdef DEBUG_PRINT - printf("%s: deleting %s (%d)\n", __func__, id->name, id->us); -#endif - BLI_assert(id->us == 0); - } - BKE_id_free_ex(bmain, id, free_flag, !do_tagged_deletion); - } - } - } - - bmain->is_memfile_undo_written = false; -} - -/** - * Properly delete a single ID from given \a bmain database. - */ -void BKE_id_delete(Main *bmain, void *idv) -{ - BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); - ((ID *)idv)->tag |= LIB_TAG_DOIT; - - id_delete(bmain, false); -} - -/** - * Properly delete all IDs tagged with \a LIB_TAG_DOIT, in given \a bmain database. - * - * This is more efficient than calling #BKE_id_delete repetitively on a large set of IDs - * (several times faster when deleting most of the IDs at once)... - * - * \warning Considered experimental for now, seems to be working OK but this is - * risky code in a complicated area. - */ -void BKE_id_multi_tagged_delete(Main *bmain) -{ - id_delete(bmain, true); -} diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c index ea728f61733..e517b4f8f18 100644 --- a/source/blender/blenkernel/intern/light.c +++ b/source/blender/blenkernel/intern/light.c @@ -41,7 +41,7 @@ #include "BKE_colortools.h" #include "BKE_icons.h" #include "BKE_light.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" @@ -74,7 +74,7 @@ Light *BKE_light_add(Main *bmain, const char *name) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_light_copy_data(Main *bmain, Light *la_dst, const Light *la_src, const int flag) { diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c index 3cba3aa9611..c6d025439d5 100644 --- a/source/blender/blenkernel/intern/lightprobe.c +++ b/source/blender/blenkernel/intern/lightprobe.c @@ -30,7 +30,7 @@ #include "BLI_utildefines.h" #include "BKE_animsys.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_lightprobe.h" #include "BKE_main.h" @@ -84,7 +84,7 @@ void *BKE_lightprobe_add(Main *bmain, const char *name) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_lightprobe_copy_data(Main *UNUSED(bmain), LightProbe *UNUSED(probe_dst), diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index 3efc493b43e..3396a2e448e 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -40,7 +40,7 @@ #include "BKE_colorband.h" #include "BKE_context.h" #include "BKE_freestyle.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_linestyle.h" #include "BKE_node.h" #include "BKE_colortools.h" @@ -134,7 +134,7 @@ void BKE_linestyle_free(FreestyleLineStyle *linestyle) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_linestyle_copy_data(struct Main *bmain, FreestyleLineStyle *linestyle_dst, diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index 8e2c3a11ac0..0c17a2c0856 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -35,8 +35,8 @@ #include "DNA_ID.h" #include "BKE_global.h" -#include "BKE_library.h" -#include "BKE_library_query.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" #include "BKE_main.h" #include "IMB_imbuf.h" diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index fc087ff91b2..513e2863dd5 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -46,7 +46,7 @@ #include "BKE_animsys.h" #include "BKE_curve.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_mask.h" #include "BKE_node.h" @@ -883,7 +883,7 @@ Mask *BKE_mask_copy_nolib(Mask *mask) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_mask_copy_data(Main *UNUSED(bmain), Mask *mask_dst, diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 84b3ea27025..af5e867ac38 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -55,7 +55,7 @@ #include "BKE_gpencil.h" #include "BKE_icons.h" #include "BKE_image.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_mesh.h" @@ -157,7 +157,7 @@ Material *BKE_gpencil_material_add(Main *bmain, const char *name) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_material_copy_data(Main *bmain, Material *ma_dst, const Material *ma_src, const int flag) { diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index c5cb95927ec..1659199656b 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -51,7 +51,7 @@ #include "BKE_animsys.h" #include "BKE_curve.h" #include "BKE_scene.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_displist.h" #include "BKE_mball.h" #include "BKE_object.h" @@ -102,7 +102,7 @@ MetaBall *BKE_mball_add(Main *bmain, const char *name) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_mball_copy_data(Main *UNUSED(bmain), MetaBall *mb_dst, diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 098da997d8f..09529582a43 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -44,7 +44,7 @@ #include "BKE_key.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_material.h" #include "BKE_modifier.h" #include "BKE_multires.h" @@ -563,7 +563,7 @@ Mesh *BKE_mesh_add(Main *bmain, const char *name) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_mesh_copy_data(Main *bmain, Mesh *me_dst, const Mesh *me_src, const int flag) { diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c index c48b3b511d9..68d535e6405 100644 --- a/source/blender/blenkernel/intern/mesh_convert.c +++ b/source/blender/blenkernel/intern/mesh_convert.c @@ -40,12 +40,12 @@ #include "BKE_DerivedMesh.h" #include "BKE_editmesh.h" #include "BKE_key.h" -#include "BKE_library_query.h" +#include "BKE_lib_query.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_modifier.h" #include "BKE_displist.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_material.h" #include "BKE_mball.h" /* these 2 are only used by conversion functions */ diff --git a/source/blender/blenkernel/intern/mesh_merge.c b/source/blender/blenkernel/intern/mesh_merge.c index ae808b85323..d646e988c60 100644 --- a/source/blender/blenkernel/intern/mesh_merge.c +++ b/source/blender/blenkernel/intern/mesh_merge.c @@ -33,7 +33,7 @@ #include "BLI_ghash.h" #include "BKE_customdata.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" diff --git a/source/blender/blenkernel/intern/mesh_mirror.c b/source/blender/blenkernel/intern/mesh_mirror.c index 271591d98f0..47338aef3cf 100644 --- a/source/blender/blenkernel/intern/mesh_mirror.c +++ b/source/blender/blenkernel/intern/mesh_mirror.c @@ -27,8 +27,8 @@ #include "DNA_meshdata_types.h" #include "DNA_object_types.h" -#include "BKE_library.h" -#include "BKE_library_query.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" #include "BKE_mesh.h" #include "BKE_mesh_mirror.h" #include "BKE_modifier.h" diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.c b/source/blender/blenkernel/intern/mesh_remesh_voxel.c index d616ff30787..afc380fd369 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.c +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.c @@ -41,7 +41,7 @@ #include "BKE_editmesh.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_customdata.h" #include "BKE_bvhutils.h" #include "BKE_mesh_remesh_voxel.h" /* own include */ diff --git a/source/blender/blenkernel/intern/mesh_runtime.c b/source/blender/blenkernel/intern/mesh_runtime.c index 4c3761c7ffc..3e21e044a02 100644 --- a/source/blender/blenkernel/intern/mesh_runtime.c +++ b/source/blender/blenkernel/intern/mesh_runtime.c @@ -33,7 +33,7 @@ #include "BLI_threads.h" #include "BKE_bvhutils.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_subdiv_ccg.h" diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index a784be9c645..2591edb502e 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -53,8 +53,8 @@ #include "BKE_global.h" #include "BKE_idcode.h" #include "BKE_key.h" -#include "BKE_library.h" -#include "BKE_library_query.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" #include "BKE_mesh.h" #include "BKE_multires.h" #include "BKE_object.h" diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index 9385a9ae24d..6ea642f9876 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -54,7 +54,7 @@ #include "BKE_animsys.h" #include "BKE_colortools.h" #include "BKE_global.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_movieclip.h" #include "BKE_node.h" @@ -1639,7 +1639,7 @@ void BKE_movieclip_free(MovieClip *clip) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_movieclip_copy_data(Main *UNUSED(bmain), MovieClip *clip_dst, diff --git a/source/blender/blenkernel/intern/multires_reshape.c b/source/blender/blenkernel/intern/multires_reshape.c index 4e4a8831518..0a8248b25f1 100644 --- a/source/blender/blenkernel/intern/multires_reshape.c +++ b/source/blender/blenkernel/intern/multires_reshape.c @@ -32,7 +32,7 @@ #include "BLI_task.h" #include "BKE_ccg.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_modifier.h" diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 7aa8837d139..2a29644f0d7 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -48,7 +48,7 @@ #include "BKE_action.h" #include "BKE_fcurve.h" #include "BKE_global.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_nla.h" #include "BKE_sound.h" @@ -161,7 +161,7 @@ void BKE_nla_tracks_free(ListBase *tracks, bool do_id_user) * * \param use_same_action: When true, the existing action is used (instead of being duplicated) * \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_... - * flags in BKE_library.h + * flags in BKE_lib_id.h */ NlaStrip *BKE_nlastrip_copy(Main *bmain, NlaStrip *strip, @@ -215,7 +215,7 @@ NlaStrip *BKE_nlastrip_copy(Main *bmain, /** * Copy a single NLA Track. * \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_... - * flags in BKE_library.h + * flags in BKE_lib_id.h */ NlaTrack *BKE_nlatrack_copy(Main *bmain, NlaTrack *nlt, @@ -249,7 +249,7 @@ NlaTrack *BKE_nlatrack_copy(Main *bmain, /** * Copy all NLA data. * \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_... - * flags in BKE_library.h + * flags in BKE_lib_id.h */ void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int flag) { diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 94c06e46cb9..64897d05c96 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -53,7 +53,7 @@ #include "BKE_animsys.h" #include "BKE_global.h" #include "BKE_idprop.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" @@ -1499,7 +1499,7 @@ bNodeTree *ntreeAddTree(Main *bmain, const char *name, const char *idname) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_node_tree_copy_data(Main *UNUSED(bmain), bNodeTree *ntree_dst, diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index cf43dec132a..51d397a44bc 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -89,9 +89,9 @@ #include "BKE_light.h" #include "BKE_layer.h" #include "BKE_lattice.h" -#include "BKE_library.h" -#include "BKE_library_query.h" -#include "BKE_library_remap.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" +#include "BKE_lib_remap.h" #include "BKE_linestyle.h" #include "BKE_mesh.h" #include "BKE_editmesh.h" @@ -1325,7 +1325,7 @@ void BKE_object_transform_copy(Object *ob_tar, const Object *ob_src) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_object_copy_data(Main *bmain, Object *ob_dst, const Object *ob_src, const int flag) { diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 25f6682e9a7..e72540ed499 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -55,7 +55,7 @@ #include "BKE_gpencil.h" #include "BKE_image.h" #include "BKE_key.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" #include "BKE_mesh_runtime.h" @@ -498,7 +498,7 @@ PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_paint_curve_copy_data(Main *UNUSED(bmain), PaintCurve *pc_dst, @@ -587,7 +587,7 @@ Palette *BKE_palette_add(Main *bmain, const char *name) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_palette_copy_data(Main *UNUSED(bmain), Palette *palette_dst, diff --git a/source/blender/blenkernel/intern/paint_toolslots.c b/source/blender/blenkernel/intern/paint_toolslots.c index 1d10db06139..e9601109fd5 100644 --- a/source/blender/blenkernel/intern/paint_toolslots.c +++ b/source/blender/blenkernel/intern/paint_toolslots.c @@ -29,7 +29,7 @@ #include "BLI_utildefines.h" #include "BKE_main.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_brush.h" #include "BKE_paint.h" diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index d68c7a947ae..bda768626b4 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -65,7 +65,7 @@ #include "BKE_particle.h" #include "BKE_material.h" #include "BKE_key.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_modifier.h" #include "BKE_mesh.h" #include "BKE_cdderivedmesh.h" /* for weight_to_rgb() */ @@ -3812,7 +3812,7 @@ void BKE_particlesettings_twist_curve_init(ParticleSettings *part) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_particlesettings_copy_data(Main *UNUSED(bmain), ParticleSettings *part_dst, diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c index 1189785ce0f..7a0998a1ee7 100644 --- a/source/blender/blenkernel/intern/particle_distribute.c +++ b/source/blender/blenkernel/intern/particle_distribute.c @@ -41,7 +41,7 @@ #include "DNA_scene_types.h" #include "BKE_global.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_object.h" #include "BKE_particle.h" diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 95726e145b4..4c4e88da6c7 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -62,8 +62,8 @@ #include "BKE_collision.h" #include "BKE_colortools.h" #include "BKE_effect.h" -#include "BKE_library.h" -#include "BKE_library_query.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" #include "BKE_particle.h" #include "BKE_collection.h" diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 5e1611cb3f9..90a4a2dee23 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -57,7 +57,7 @@ #include "BKE_collection.h" #include "BKE_dynamicpaint.h" #include "BKE_global.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_object.h" diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index c57808f3dee..c2180e50b74 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -62,8 +62,8 @@ #include "BKE_rigidbody.h" #include "BKE_scene.h" #ifdef WITH_BULLET -# include "BKE_library.h" -# include "BKE_library_query.h" +# include "BKE_lib_id.h" +# include "BKE_lib_query.h" #endif #include "DEG_depsgraph.h" diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 86b15641f81..fd1f030c3d3 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -73,8 +73,8 @@ #include "BKE_idprop.h" #include "BKE_image.h" #include "BKE_layer.h" -#include "BKE_library.h" -#include "BKE_library_remap.h" +#include "BKE_lib_id.h" +#include "BKE_lib_remap.h" #include "BKE_linestyle.h" #include "BKE_main.h" #include "BKE_mask.h" @@ -145,7 +145,7 @@ static void remove_sequencer_fcurves(Scene *sce) } } -/* flag -- copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). */ +/* flag -- copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag) { if (toolsettings == NULL) { @@ -243,7 +243,7 @@ void BKE_toolsettings_free(ToolSettings *toolsettings) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_scene_copy_data(Main *bmain, Scene *sce_dst, const Scene *sce_src, const int flag) { diff --git a/source/blender/blenkernel/intern/seqeffects.c b/source/blender/blenkernel/intern/seqeffects.c index 8dfe01ae1fd..fc5831ed132 100644 --- a/source/blender/blenkernel/intern/seqeffects.c +++ b/source/blender/blenkernel/intern/seqeffects.c @@ -43,7 +43,7 @@ #include "DNA_space_types.h" #include "BKE_fcurve.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_sequencer.h" diff --git a/source/blender/blenkernel/intern/seqprefetch.c b/source/blender/blenkernel/intern/seqprefetch.c index 8c9097e1d4e..915935addcd 100644 --- a/source/blender/blenkernel/intern/seqprefetch.c +++ b/source/blender/blenkernel/intern/seqprefetch.c @@ -40,7 +40,7 @@ #include "IMB_imbuf_types.h" #include "BKE_animsys.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_scene.h" #include "BKE_main.h" #include "BKE_context.h" diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 4dc2177ec75..0908fb7eeb8 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -68,7 +68,7 @@ #include "BKE_fcurve.h" #include "BKE_scene.h" #include "BKE_mask.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_idprop.h" #include "DEG_depsgraph.h" diff --git a/source/blender/blenkernel/intern/shader_fx.c b/source/blender/blenkernel/intern/shader_fx.c index c999e1ad14e..49799aff1d6 100644 --- a/source/blender/blenkernel/intern/shader_fx.c +++ b/source/blender/blenkernel/intern/shader_fx.c @@ -38,8 +38,8 @@ #include "DNA_gpencil_types.h" #include "DNA_shader_fx_types.h" -#include "BKE_library.h" -#include "BKE_library_query.h" +#include "BKE_lib_id.h" +#include "BKE_lib_query.h" #include "BKE_gpencil.h" #include "BKE_shader_fx.h" #include "BKE_object.h" diff --git a/source/blender/blenkernel/intern/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c index 49a295c6a9e..adc344c29d8 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.c +++ b/source/blender/blenkernel/intern/shrinkwrap.c @@ -44,7 +44,7 @@ #include "BKE_cdderivedmesh.h" #include "BKE_DerivedMesh.h" #include "BKE_lattice.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_modifier.h" #include "BKE_deform.h" diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index 84d135c7f32..719978c64ad 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -52,7 +52,7 @@ #include "BKE_global.h" #include "BKE_main.h" #include "BKE_sound.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_packedFile.h" #include "BKE_sequencer.h" #include "BKE_scene.h" @@ -192,7 +192,7 @@ void BKE_sound_free(bSound *sound) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_sound_copy_data(Main *UNUSED(bmain), bSound *sound_dst, diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c index 46a74e25b8b..7ab0f9231fa 100644 --- a/source/blender/blenkernel/intern/speaker.c +++ b/source/blender/blenkernel/intern/speaker.c @@ -27,7 +27,7 @@ #include "BLI_utildefines.h" #include "BKE_animsys.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_speaker.h" @@ -57,7 +57,7 @@ void *BKE_speaker_add(Main *bmain, const char *name) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_speaker_copy_data(Main *UNUSED(bmain), Speaker *UNUSED(spk_dst), diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 163792984ad..c6428211db3 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -47,7 +47,7 @@ #include "DNA_node_types.h" #include "DNA_material_types.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_text.h" #include "BKE_node.h" @@ -401,7 +401,7 @@ Text *BKE_text_load(Main *bmain, const char *file, const char *relpath) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_text_copy_data(Main *UNUSED(bmain), Text *ta_dst, diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index b1ae71c609f..35026288663 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -48,7 +48,7 @@ #include "BKE_main.h" #include "BKE_colorband.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_image.h" #include "BKE_material.h" #include "BKE_texture.h" @@ -313,7 +313,7 @@ MTex *BKE_texture_mtex_add_id(ID *id, int slot) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_texture_copy_data(Main *bmain, Tex *tex_dst, const Tex *tex_src, const int flag) { diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index d2038c34056..500c58095e6 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -49,7 +49,7 @@ #include "BKE_fcurve.h" #include "BKE_tracking.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_movieclip.h" #include "BKE_object.h" #include "BKE_scene.h" diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c index 36a58a6ca02..8fb099ac953 100644 --- a/source/blender/blenkernel/intern/undo_system.c +++ b/source/blender/blenkernel/intern/undo_system.c @@ -36,7 +36,7 @@ #include "BKE_context.h" #include "BKE_global.h" -#include "BKE_library_override.h" +#include "BKE_lib_override.h" #include "BKE_main.h" #include "BKE_undo_system.h" diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index f58c20a7d72..cd1bae6d798 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -28,7 +28,7 @@ #include "BKE_global.h" #include "BKE_idprop.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_scene.h" #include "BKE_object.h" diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index 992e4333049..02c2a884bda 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -37,7 +37,7 @@ #include "BKE_animsys.h" #include "BKE_icons.h" -#include "BKE_library.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" #include "BKE_world.h" @@ -94,7 +94,7 @@ World *BKE_world_add(Main *bmain, const char *name) * * WARNING! This function will not handle ID user count! * - * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). + * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more). */ void BKE_world_copy_data(Main *bmain, World *wrld_dst, const World *wrld_src, const int flag) { diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 5152643aaa1..deb9592b4f9 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -42,7 +42,7 @@ # include "BKE_global.h" # include "BKE_idprop.h" # include "BKE_image.h" -# include "BKE_library.h" +# include "BKE_lib_id.h" # include "BKE_main.h" # include "BKE_report.h" # include "BKE_sound.h" -- cgit v1.2.3