Changeset View
Changeset View
Standalone View
Standalone View
source/blender/windowmanager/intern/wm_files_link.c
| Show First 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | |||||
| #include "BLI_ghash.h" | #include "BLI_ghash.h" | ||||
| #include "BLI_linklist.h" | #include "BLI_linklist.h" | ||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BLI_memarena.h" | #include "BLI_memarena.h" | ||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BLO_readfile.h" | #include "BLO_readfile.h" | ||||
| #include "BLT_translation.h" | |||||
| #include "BKE_armature.h" | #include "BKE_armature.h" | ||||
| #include "BKE_blendfile_link_append.h" | |||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_global.h" | #include "BKE_global.h" | ||||
| #include "BKE_key.h" | #include "BKE_key.h" | ||||
| #include "BKE_layer.h" | #include "BKE_layer.h" | ||||
| #include "BKE_lib_id.h" | #include "BKE_lib_id.h" | ||||
| #include "BKE_lib_override.h" | #include "BKE_lib_override.h" | ||||
| #include "BKE_lib_query.h" | #include "BKE_lib_query.h" | ||||
| #include "BKE_lib_remap.h" | #include "BKE_lib_remap.h" | ||||
| ▲ Show 20 Lines • Show All 97 Lines • ▼ Show 20 Lines | static int wm_link_append_flag(wmOperator *op) | ||||
| } | } | ||||
| if (RNA_boolean_get(op->ptr, "instance_object_data")) { | if (RNA_boolean_get(op->ptr, "instance_object_data")) { | ||||
| flag |= BLO_LIBLINK_OBDATA_INSTANCE; | flag |= BLO_LIBLINK_OBDATA_INSTANCE; | ||||
| } | } | ||||
| return flag; | return flag; | ||||
| } | } | ||||
| typedef struct WMLinkAppendDataItem { | |||||
| char *name; | |||||
| BLI_bitmap | |||||
| *libraries; /* All libs (from WMLinkAppendData.libraries) to try to load this ID from. */ | |||||
| short idcode; | |||||
| /** Type of action to do to append this item, and other append-specific information. */ | |||||
| char append_action; | |||||
| char append_tag; | |||||
| ID *new_id; | |||||
| Library *source_library; | |||||
| void *customdata; | |||||
| } WMLinkAppendDataItem; | |||||
| typedef struct WMLinkAppendData { | |||||
| LinkNodePair libraries; | |||||
| LinkNodePair items; | |||||
| int num_libraries; | |||||
| int num_items; | |||||
| /** | |||||
| * Combines #eFileSel_Params_Flag from DNA_space_types.h & #eBLOLibLinkFlags from BLO_readfile.h | |||||
| */ | |||||
| int flag; | |||||
| /** Allows to easily find an existing items from an ID pointer. Used by append code. */ | |||||
| GHash *new_id_to_item; | |||||
| /** Runtime info used by append code to manage re-use of already appended matching IDs. */ | |||||
| GHash *library_weak_reference_mapping; | |||||
| /* Internal 'private' data */ | |||||
| MemArena *memarena; | |||||
| } WMLinkAppendData; | |||||
| typedef struct WMLinkAppendDataCallBack { | |||||
| WMLinkAppendData *lapp_data; | |||||
| WMLinkAppendDataItem *item; | |||||
| ReportList *reports; | |||||
| } WMLinkAppendDataCallBack; | |||||
| enum { | |||||
| WM_APPEND_ACT_UNSET = 0, | |||||
| WM_APPEND_ACT_KEEP_LINKED, | |||||
| WM_APPEND_ACT_REUSE_LOCAL, | |||||
| WM_APPEND_ACT_MAKE_LOCAL, | |||||
| WM_APPEND_ACT_COPY_LOCAL, | |||||
| }; | |||||
| enum { | |||||
| WM_APPEND_TAG_INDIRECT = 1 << 0, | |||||
| }; | |||||
| static WMLinkAppendData *wm_link_append_data_new(const int flag) | |||||
| { | |||||
| MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); | |||||
| WMLinkAppendData *lapp_data = BLI_memarena_calloc(ma, sizeof(*lapp_data)); | |||||
| lapp_data->flag = flag; | |||||
| lapp_data->memarena = ma; | |||||
| return lapp_data; | |||||
| } | |||||
| static void wm_link_append_data_free(WMLinkAppendData *lapp_data) | |||||
| { | |||||
| if (lapp_data->new_id_to_item != NULL) { | |||||
| BLI_ghash_free(lapp_data->new_id_to_item, NULL, NULL); | |||||
| } | |||||
| BLI_assert(lapp_data->library_weak_reference_mapping == NULL); | |||||
| BLI_memarena_free(lapp_data->memarena); | |||||
| } | |||||
| /* WARNING! *Never* call wm_link_append_data_library_add() after having added some items! */ | |||||
| static void wm_link_append_data_library_add(WMLinkAppendData *lapp_data, const char *libname) | |||||
| { | |||||
| size_t len = strlen(libname) + 1; | |||||
| char *libpath = BLI_memarena_alloc(lapp_data->memarena, len); | |||||
| BLI_strncpy(libpath, libname, len); | |||||
| BLI_linklist_append_arena(&lapp_data->libraries, libpath, lapp_data->memarena); | |||||
| lapp_data->num_libraries++; | |||||
| } | |||||
| static WMLinkAppendDataItem *wm_link_append_data_item_add(WMLinkAppendData *lapp_data, | |||||
| const char *idname, | |||||
| const short idcode, | |||||
| void *customdata) | |||||
| { | |||||
| WMLinkAppendDataItem *item = BLI_memarena_calloc(lapp_data->memarena, sizeof(*item)); | |||||
| size_t len = strlen(idname) + 1; | |||||
| item->name = BLI_memarena_alloc(lapp_data->memarena, len); | |||||
| BLI_strncpy(item->name, idname, len); | |||||
| item->idcode = idcode; | |||||
| item->libraries = BLI_BITMAP_NEW_MEMARENA(lapp_data->memarena, lapp_data->num_libraries); | |||||
| item->new_id = NULL; | |||||
| item->append_action = WM_APPEND_ACT_UNSET; | |||||
| item->customdata = customdata; | |||||
| BLI_linklist_append_arena(&lapp_data->items, item, lapp_data->memarena); | |||||
| lapp_data->num_items++; | |||||
| return item; | |||||
| } | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Library appending helper functions. | |||||
| * | |||||
| * FIXME: Deduplicate code with similar one in readfile.c | |||||
| * \{ */ | |||||
| static bool object_in_any_scene(Main *bmain, Object *ob) | |||||
| { | |||||
| LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { | |||||
| if (BKE_scene_object_find(sce, ob)) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| static bool object_in_any_collection(Main *bmain, Object *ob) | |||||
| { | |||||
| LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { | |||||
| if (BKE_collection_has_object(collection, ob)) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { | |||||
| if (scene->master_collection != NULL && | |||||
| BKE_collection_has_object(scene->master_collection, ob)) { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| return false; | |||||
| } | |||||
| static ID *wm_append_loose_data_instantiate_process_check(WMLinkAppendDataItem *item) | |||||
| { | |||||
| /* We consider that if we either kept it linked, or re-used already local data, instantiation | |||||
| * status of those should not be modified. */ | |||||
| if (!ELEM(item->append_action, WM_APPEND_ACT_COPY_LOCAL, WM_APPEND_ACT_MAKE_LOCAL)) { | |||||
| return NULL; | |||||
| } | |||||
| ID *id = item->new_id; | |||||
| if (id == NULL) { | |||||
| return NULL; | |||||
| } | |||||
| if (item->append_action == WM_APPEND_ACT_COPY_LOCAL) { | |||||
| BLI_assert(ID_IS_LINKED(id)); | |||||
| id = id->newid; | |||||
| if (id == NULL) { | |||||
| return NULL; | |||||
| } | |||||
| BLI_assert(!ID_IS_LINKED(id)); | |||||
| return id; | |||||
| } | |||||
| BLI_assert(!ID_IS_LINKED(id)); | |||||
| return id; | |||||
| } | |||||
| static void wm_append_loose_data_instantiate_ensure_active_collection( | |||||
| WMLinkAppendData *lapp_data, | |||||
| Main *bmain, | |||||
| Scene *scene, | |||||
| ViewLayer *view_layer, | |||||
| Collection **r_active_collection) | |||||
| { | |||||
| /* Find or add collection as needed. */ | |||||
| if (*r_active_collection == NULL) { | |||||
| if (lapp_data->flag & FILE_ACTIVE_COLLECTION) { | |||||
| LayerCollection *lc = BKE_layer_collection_get_active(view_layer); | |||||
| *r_active_collection = lc->collection; | |||||
| } | |||||
| else { | |||||
| *r_active_collection = BKE_collection_add( | |||||
| bmain, scene->master_collection, DATA_("Appended Data")); | |||||
| } | |||||
| } | |||||
| } | |||||
| /* TODO: De-duplicate this code with the one in readfile.c, think we need some utils code for that | |||||
| * in BKE. */ | |||||
| static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, | |||||
| Main *bmain, | |||||
| Scene *scene, | |||||
| ViewLayer *view_layer, | |||||
| const View3D *v3d) | |||||
| { | |||||
| if (scene == NULL) { | |||||
| /* In some cases, like the asset drag&drop e.g., the caller code manages instantiation itself. | |||||
| */ | |||||
| return; | |||||
| } | |||||
| LinkNode *itemlink; | |||||
| Collection *active_collection = NULL; | |||||
| const bool do_obdata = (lapp_data->flag & BLO_LIBLINK_OBDATA_INSTANCE) != 0; | |||||
| /* Do NOT make base active here! screws up GUI stuff, | |||||
| * if you want it do it at the editor level. */ | |||||
| const bool object_set_active = false; | |||||
| /* First pass on obdata to enable their instantiation by default, then do a second pass on | |||||
| * objects to clear it for any obdata already in use. */ | |||||
| if (do_obdata) { | |||||
| for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { | |||||
| WMLinkAppendDataItem *item = itemlink->link; | |||||
| ID *id = wm_append_loose_data_instantiate_process_check(item); | |||||
| if (id == NULL) { | |||||
| continue; | |||||
| } | |||||
| const ID_Type idcode = GS(id->name); | |||||
| if (!OB_DATA_SUPPORT_ID(idcode)) { | |||||
| continue; | |||||
| } | |||||
| id->tag |= LIB_TAG_DOIT; | |||||
| } | |||||
| for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { | |||||
| WMLinkAppendDataItem *item = itemlink->link; | |||||
| ID *id = item->new_id; | |||||
| if (id == NULL || GS(id->name) != ID_OB) { | |||||
| continue; | |||||
| } | |||||
| Object *ob = (Object *)id; | |||||
| Object *new_ob = (Object *)id->newid; | |||||
| if (ob->data != NULL) { | |||||
| ((ID *)(ob->data))->tag &= ~LIB_TAG_DOIT; | |||||
| } | |||||
| if (new_ob != NULL && new_ob->data != NULL) { | |||||
| ((ID *)(new_ob->data))->tag &= ~LIB_TAG_DOIT; | |||||
| } | |||||
| } | |||||
| } | |||||
| /* First do collections, then objects, then obdata. */ | |||||
| /* NOTE: For collections we only view_layer-instantiate duplicated collections that have | |||||
| * non-instantiated objects in them. */ | |||||
| for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { | |||||
| WMLinkAppendDataItem *item = itemlink->link; | |||||
| ID *id = wm_append_loose_data_instantiate_process_check(item); | |||||
| if (id == NULL || GS(id->name) != ID_GR) { | |||||
| continue; | |||||
| } | |||||
| /* We do not want to force instantiation of indirectly appended collections. Users can now | |||||
| * easily instantiate collections (and their objects) as needed by themselves. See T67032. */ | |||||
| /* We need to check that objects in that collections are already instantiated in a scene. | |||||
| * Otherwise, it's better to add the collection to the scene's active collection, than to | |||||
| * instantiate its objects in active scene's collection directly. See T61141. | |||||
| * | |||||
| * NOTE: We only check object directly into that collection, not recursively into its | |||||
| * children. | |||||
| */ | |||||
| Collection *collection = (Collection *)id; | |||||
| /* We always add collections directly selected by the user. */ | |||||
| bool do_add_collection = (item->append_tag & WM_APPEND_TAG_INDIRECT) == 0; | |||||
| if (!do_add_collection) { | |||||
| LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) { | |||||
| Object *ob = coll_ob->ob; | |||||
| if (!object_in_any_scene(bmain, ob)) { | |||||
| do_add_collection = true; | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| if (do_add_collection) { | |||||
| wm_append_loose_data_instantiate_ensure_active_collection( | |||||
| lapp_data, bmain, scene, view_layer, &active_collection); | |||||
| /* In case user requested instantiation of collections as empties, we do so for the one they | |||||
| * explicitly selected (originally directly linked IDs). */ | |||||
| if ((lapp_data->flag & BLO_LIBLINK_COLLECTION_INSTANCE) != 0 && | |||||
| (item->append_tag & WM_APPEND_TAG_INDIRECT) == 0) { | |||||
| /* BKE_object_add(...) messes with the selection. */ | |||||
| Object *ob = BKE_object_add_only_object(bmain, OB_EMPTY, collection->id.name + 2); | |||||
| ob->type = OB_EMPTY; | |||||
| ob->empty_drawsize = U.collection_instance_empty_size; | |||||
| const bool set_selected = (lapp_data->flag & FILE_AUTOSELECT) != 0; | |||||
| /* TODO: why is it OK to make this active here but not in other situations? | |||||
| * See other callers of #object_base_instance_init */ | |||||
| const bool set_active = set_selected; | |||||
| BLO_object_instantiate_object_base_instance_init( | |||||
| bmain, active_collection, ob, view_layer, v3d, lapp_data->flag, set_active); | |||||
| /* Assign the collection. */ | |||||
| ob->instance_collection = collection; | |||||
| id_us_plus(&collection->id); | |||||
| ob->transflag |= OB_DUPLICOLLECTION; | |||||
| copy_v3_v3(ob->loc, scene->cursor.location); | |||||
| } | |||||
| else { | |||||
| /* Add collection as child of active collection. */ | |||||
| BKE_collection_child_add(bmain, active_collection, collection); | |||||
| if ((lapp_data->flag & FILE_AUTOSELECT) != 0) { | |||||
| LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) { | |||||
| Object *ob = coll_ob->ob; | |||||
| Base *base = BKE_view_layer_base_find(view_layer, ob); | |||||
| if (base) { | |||||
| base->flag |= BASE_SELECTED; | |||||
| BKE_scene_object_base_flag_sync_from_base(base); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| /* NOTE: For objects we only view_layer-instantiate duplicated objects that are not yet used | |||||
| * anywhere. */ | |||||
| for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { | |||||
| WMLinkAppendDataItem *item = itemlink->link; | |||||
| ID *id = wm_append_loose_data_instantiate_process_check(item); | |||||
| if (id == NULL || GS(id->name) != ID_OB) { | |||||
| continue; | |||||
| } | |||||
| Object *ob = (Object *)id; | |||||
| if (object_in_any_collection(bmain, ob)) { | |||||
| continue; | |||||
| } | |||||
| wm_append_loose_data_instantiate_ensure_active_collection( | |||||
| lapp_data, bmain, scene, view_layer, &active_collection); | |||||
| CLAMP_MIN(ob->id.us, 0); | |||||
| ob->mode = OB_MODE_OBJECT; | |||||
| BLO_object_instantiate_object_base_instance_init( | |||||
| bmain, active_collection, ob, view_layer, v3d, lapp_data->flag, object_set_active); | |||||
| } | |||||
| if (!do_obdata) { | |||||
| return; | |||||
| } | |||||
| for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { | |||||
| WMLinkAppendDataItem *item = itemlink->link; | |||||
| ID *id = wm_append_loose_data_instantiate_process_check(item); | |||||
| if (id == NULL) { | |||||
| continue; | |||||
| } | |||||
| const ID_Type idcode = GS(id->name); | |||||
| if (!OB_DATA_SUPPORT_ID(idcode)) { | |||||
| continue; | |||||
| } | |||||
| if ((id->tag & LIB_TAG_DOIT) == 0) { | |||||
| continue; | |||||
| } | |||||
| wm_append_loose_data_instantiate_ensure_active_collection( | |||||
| lapp_data, bmain, scene, view_layer, &active_collection); | |||||
| const int type = BKE_object_obdata_to_type(id); | |||||
| BLI_assert(type != -1); | |||||
| Object *ob = BKE_object_add_only_object(bmain, type, id->name + 2); | |||||
| ob->data = id; | |||||
| id_us_plus(id); | |||||
| BKE_object_materials_test(bmain, ob, ob->data); | |||||
| BLO_object_instantiate_object_base_instance_init( | |||||
| bmain, active_collection, ob, view_layer, v3d, lapp_data->flag, object_set_active); | |||||
| copy_v3_v3(ob->loc, scene->cursor.location); | |||||
| id->tag &= ~LIB_TAG_DOIT; | |||||
| } | |||||
| /* Finally, add rigid body objects and constraints to current RB world(s). */ | |||||
| for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { | |||||
| WMLinkAppendDataItem *item = itemlink->link; | |||||
| ID *id = wm_append_loose_data_instantiate_process_check(item); | |||||
| if (id == NULL || GS(id->name) != ID_OB) { | |||||
| continue; | |||||
| } | |||||
| BKE_rigidbody_ensure_local_object(bmain, (Object *)id); | |||||
| } | |||||
| } | |||||
| /** \} */ | |||||
| static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data) | |||||
| { | |||||
| /* NOTE: It is important to also skip liboverride references here, as those should never be made | |||||
| * local. */ | |||||
| if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK | | |||||
| IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { | |||||
| return IDWALK_RET_NOP; | |||||
| } | |||||
| WMLinkAppendDataCallBack *data = cb_data->user_data; | |||||
| ID *id = *cb_data->id_pointer; | |||||
| if (id == NULL) { | |||||
| return IDWALK_RET_NOP; | |||||
| } | |||||
| if (!BKE_idtype_idcode_is_linkable(GS(id->name))) { | |||||
| /* While we do not want to add non-linkable ID (shape keys...) to the list of linked items, | |||||
| * unfortunately they can use fully linkable valid IDs too, like actions. Those need to be | |||||
| * processed, so we need to recursively deal with them here. */ | |||||
| /* NOTE: Since we are by-passing checks in `BKE_library_foreach_ID_link` by manually calling it | |||||
| * recursively, we need to take care of potential recursion cases ourselves (e.g.animdata of | |||||
| * shapekey referencing the shapekey itself). */ | |||||
| if (id != cb_data->id_self) { | |||||
| BKE_library_foreach_ID_link( | |||||
| cb_data->bmain, id, foreach_libblock_append_callback, data, IDWALK_NOP); | |||||
| } | |||||
| return IDWALK_RET_NOP; | |||||
| } | |||||
| const bool do_recursive = (data->lapp_data->flag & BLO_LIBLINK_APPEND_RECURSIVE) != 0; | |||||
| if (!do_recursive && cb_data->id_owner->lib != id->lib) { | |||||
| /* When `do_recursive` is false, we only make local IDs from same library(-ies) as the | |||||
| * initially directly linked ones. */ | |||||
| return IDWALK_RET_NOP; | |||||
| } | |||||
| WMLinkAppendDataItem *item = BLI_ghash_lookup(data->lapp_data->new_id_to_item, id); | |||||
| if (item == NULL) { | |||||
| item = wm_link_append_data_item_add(data->lapp_data, id->name, GS(id->name), NULL); | |||||
| item->new_id = id; | |||||
| item->source_library = id->lib; | |||||
| /* Since we did not have an item for that ID yet, we know user did not selected it explicitly, | |||||
| * it was rather linked indirectly. This info is important for instantiation of collections. */ | |||||
| item->append_tag |= WM_APPEND_TAG_INDIRECT; | |||||
| BLI_ghash_insert(data->lapp_data->new_id_to_item, id, item); | |||||
| } | |||||
| /* NOTE: currently there is no need to do anything else here, but in the future this would be | |||||
| * the place to add specific per-usage decisions on how to append an ID. */ | |||||
| return IDWALK_RET_NOP; | |||||
| } | |||||
| /* Perform append operation, using modern ID usage looper to detect which ID should be kept linked, | |||||
| * made local, duplicated as local, re-used from local etc. | |||||
| * | |||||
| * TODO: Expose somehow this logic to the two other parts of code performing actual append | |||||
| * (i.e. copy/paste and `bpy` link/append API). | |||||
| * Then we can heavily simplify #BKE_library_make_local(). */ | |||||
| static void wm_append_do(WMLinkAppendData *lapp_data, | |||||
| ReportList *reports, | |||||
| Main *bmain, | |||||
| Scene *scene, | |||||
| ViewLayer *view_layer, | |||||
| const View3D *v3d) | |||||
| { | |||||
| BLI_assert((lapp_data->flag & FILE_LINK) == 0); | |||||
| const bool set_fakeuser = (lapp_data->flag & BLO_LIBLINK_APPEND_SET_FAKEUSER) != 0; | |||||
| const bool do_reuse_local_id = (lapp_data->flag & BLO_LIBLINK_APPEND_LOCAL_ID_REUSE) != 0; | |||||
| const int make_local_common_flags = LIB_ID_MAKELOCAL_FULL_LIBRARY | | |||||
| ((lapp_data->flag & BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR) != | |||||
| 0 ? | |||||
| LIB_ID_MAKELOCAL_ASSET_DATA_CLEAR : | |||||
| 0); | |||||
| LinkNode *itemlink; | |||||
| /* Generate a mapping between newly linked IDs and their items, and tag linked IDs used as | |||||
| * liboverride references as already existing. */ | |||||
| lapp_data->new_id_to_item = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); | |||||
| for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { | |||||
| WMLinkAppendDataItem *item = itemlink->link; | |||||
| ID *id = item->new_id; | |||||
| if (id == NULL) { | |||||
| continue; | |||||
| } | |||||
| BLI_ghash_insert(lapp_data->new_id_to_item, id, item); | |||||
| /* This ensures that if a liboverride reference is also linked/used by some other appended | |||||
| * data, it gets a local copy instead of being made directly local, so that the liboverride | |||||
| * references remain valid (i.e. linked data). */ | |||||
| if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) { | |||||
| id->override_library->reference->tag |= LIB_TAG_PRE_EXISTING; | |||||
| } | |||||
| } | |||||
| lapp_data->library_weak_reference_mapping = BKE_main_library_weak_reference_create(bmain); | |||||
| /* NOTE: Since we append items for IDs not already listed (i.e. implicitly linked indirect | |||||
| * dependencies), this list will grow and we will process those IDs later, leading to a flatten | |||||
| * recursive processing of all the linked dependencies. */ | |||||
| for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { | |||||
| WMLinkAppendDataItem *item = itemlink->link; | |||||
| ID *id = item->new_id; | |||||
| if (id == NULL) { | |||||
| continue; | |||||
| } | |||||
| BLI_assert(item->customdata == NULL); | |||||
| /* In Append case linked IDs should never be marked as needing post-processing (instantiation | |||||
| * of loose objects etc.). */ | |||||
| BLI_assert((id->tag & LIB_TAG_DOIT) == 0); | |||||
| ID *existing_local_id = BKE_idtype_idcode_append_is_reusable(GS(id->name)) ? | |||||
| BKE_main_library_weak_reference_search_item( | |||||
| lapp_data->library_weak_reference_mapping, | |||||
| id->lib->filepath, | |||||
| id->name) : | |||||
| NULL; | |||||
| if (item->append_action != WM_APPEND_ACT_UNSET) { | |||||
| /* Already set, pass. */ | |||||
| } | |||||
| if (GS(id->name) == ID_OB && ((Object *)id)->proxy_from != NULL) { | |||||
| CLOG_INFO(&LOG, 3, "Appended ID '%s' is proxified, keeping it linked...", id->name); | |||||
| item->append_action = WM_APPEND_ACT_KEEP_LINKED; | |||||
| } | |||||
| else if (do_reuse_local_id && existing_local_id != NULL) { | |||||
| CLOG_INFO(&LOG, 3, "Appended ID '%s' as a matching local one, re-using it...", id->name); | |||||
| item->append_action = WM_APPEND_ACT_REUSE_LOCAL; | |||||
| item->customdata = existing_local_id; | |||||
| } | |||||
| else if (id->tag & LIB_TAG_PRE_EXISTING) { | |||||
| CLOG_INFO(&LOG, 3, "Appended ID '%s' was already linked, need to copy it...", id->name); | |||||
| item->append_action = WM_APPEND_ACT_COPY_LOCAL; | |||||
| } | |||||
| else { | |||||
| CLOG_INFO(&LOG, 3, "Appended ID '%s' will be made local...", id->name); | |||||
| item->append_action = WM_APPEND_ACT_MAKE_LOCAL; | |||||
| } | |||||
| /* Only check dependencies if we are not keeping linked data, nor re-using existing local data. | |||||
| */ | |||||
| if (!ELEM(item->append_action, WM_APPEND_ACT_KEEP_LINKED, WM_APPEND_ACT_REUSE_LOCAL)) { | |||||
| WMLinkAppendDataCallBack cb_data = { | |||||
| .lapp_data = lapp_data, .item = item, .reports = reports}; | |||||
| BKE_library_foreach_ID_link( | |||||
| bmain, id, foreach_libblock_append_callback, &cb_data, IDWALK_NOP); | |||||
| } | |||||
| /* If we found a matching existing local id but are not re-using it, we need to properly clear | |||||
| * its weak reference to linked data. */ | |||||
| if (existing_local_id != NULL && | |||||
| !ELEM(item->append_action, WM_APPEND_ACT_KEEP_LINKED, WM_APPEND_ACT_REUSE_LOCAL)) { | |||||
| BKE_main_library_weak_reference_remove_item(lapp_data->library_weak_reference_mapping, | |||||
| id->lib->filepath, | |||||
| id->name, | |||||
| existing_local_id); | |||||
| } | |||||
| } | |||||
| /* Effectively perform required operation on every linked ID. */ | |||||
| for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { | |||||
| WMLinkAppendDataItem *item = itemlink->link; | |||||
| ID *id = item->new_id; | |||||
| if (id == NULL) { | |||||
| continue; | |||||
| } | |||||
| ID *local_appended_new_id = NULL; | |||||
| char lib_filepath[FILE_MAX]; | |||||
| BLI_strncpy(lib_filepath, id->lib->filepath, sizeof(lib_filepath)); | |||||
| char lib_id_name[MAX_ID_NAME]; | |||||
| BLI_strncpy(lib_id_name, id->name, sizeof(lib_id_name)); | |||||
| switch (item->append_action) { | |||||
| case WM_APPEND_ACT_COPY_LOCAL: | |||||
| BKE_lib_id_make_local(bmain, id, make_local_common_flags | LIB_ID_MAKELOCAL_FORCE_COPY); | |||||
| local_appended_new_id = id->newid; | |||||
| break; | |||||
| case WM_APPEND_ACT_MAKE_LOCAL: | |||||
| BKE_lib_id_make_local(bmain, | |||||
| id, | |||||
| make_local_common_flags | LIB_ID_MAKELOCAL_FORCE_LOCAL | | |||||
| LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); | |||||
| BLI_assert(id->newid == NULL); | |||||
| local_appended_new_id = id; | |||||
| break; | |||||
| case WM_APPEND_ACT_KEEP_LINKED: | |||||
| /* Nothing to do here. */ | |||||
| break; | |||||
| case WM_APPEND_ACT_REUSE_LOCAL: | |||||
| /* We only need to set `newid` to ID found in previous loop, for proper remapping. */ | |||||
| ID_NEW_SET(id, item->customdata); | |||||
| /* This is not a 'new' local appended id, do not set `local_appended_new_id` here. */ | |||||
| break; | |||||
| case WM_APPEND_ACT_UNSET: | |||||
| CLOG_ERROR( | |||||
| &LOG, "Unexpected unset append action for '%s' ID, assuming 'keep link'", id->name); | |||||
| break; | |||||
| default: | |||||
| BLI_assert(0); | |||||
| } | |||||
| if (local_appended_new_id != NULL) { | |||||
| if (BKE_idtype_idcode_append_is_reusable(GS(local_appended_new_id->name))) { | |||||
| BKE_main_library_weak_reference_add_item(lapp_data->library_weak_reference_mapping, | |||||
| lib_filepath, | |||||
| lib_id_name, | |||||
| local_appended_new_id); | |||||
| } | |||||
| if (set_fakeuser) { | |||||
| if (!ELEM(GS(local_appended_new_id->name), ID_OB, ID_GR)) { | |||||
| /* Do not set fake user on objects nor collections (instancing). */ | |||||
| id_fake_user_set(local_appended_new_id); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| BKE_main_library_weak_reference_destroy(lapp_data->library_weak_reference_mapping); | |||||
| lapp_data->library_weak_reference_mapping = NULL; | |||||
| /* Remap IDs as needed. */ | |||||
| for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { | |||||
| WMLinkAppendDataItem *item = itemlink->link; | |||||
| if (item->append_action == WM_APPEND_ACT_KEEP_LINKED) { | |||||
| continue; | |||||
| } | |||||
| ID *id = item->new_id; | |||||
| if (id == NULL) { | |||||
| continue; | |||||
| } | |||||
| if (ELEM(item->append_action, WM_APPEND_ACT_COPY_LOCAL, WM_APPEND_ACT_REUSE_LOCAL)) { | |||||
| BLI_assert(ID_IS_LINKED(id)); | |||||
| id = id->newid; | |||||
| if (id == NULL) { | |||||
| continue; | |||||
| } | |||||
| } | |||||
| BLI_assert(!ID_IS_LINKED(id)); | |||||
| BKE_libblock_relink_to_newid(bmain, id, 0); | |||||
| } | |||||
| /* Remove linked IDs when a local existing data has been reused instead. */ | |||||
| BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); | |||||
| for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { | |||||
| WMLinkAppendDataItem *item = itemlink->link; | |||||
| if (item->append_action != WM_APPEND_ACT_REUSE_LOCAL) { | |||||
| continue; | |||||
| } | |||||
| ID *id = item->new_id; | |||||
| if (id == NULL) { | |||||
| continue; | |||||
| } | |||||
| BLI_assert(ID_IS_LINKED(id)); | |||||
| BLI_assert(id->newid != NULL); | |||||
| id->tag |= LIB_TAG_DOIT; | |||||
| item->new_id = id->newid; | |||||
| } | |||||
| BKE_id_multi_tagged_delete(bmain); | |||||
| /* Instantiate newly created (duplicated) IDs as needed. */ | |||||
| wm_append_loose_data_instantiate(lapp_data, bmain, scene, view_layer, v3d); | |||||
| /* Attempt to deal with object proxies. | |||||
| * | |||||
| * NOTE: Copied from `BKE_library_make_local`, but this is not really working (as in, not | |||||
| * producing any useful result in any known use case), neither here nor in | |||||
| * `BKE_library_make_local` currently. | |||||
| * Proxies are end of life anyway, so not worth spending time on this. */ | |||||
| for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { | |||||
| WMLinkAppendDataItem *item = itemlink->link; | |||||
| if (item->append_action != WM_APPEND_ACT_COPY_LOCAL) { | |||||
| continue; | |||||
| } | |||||
| ID *id = item->new_id; | |||||
| if (id == NULL) { | |||||
| continue; | |||||
| } | |||||
| BLI_assert(ID_IS_LINKED(id)); | |||||
| /* 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 (!ID_IS_LINKED(ob->proxy)) { | |||||
| CLOG_WARN(&LOG, | |||||
| "Proxy object %s will lose 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 lose 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 object_make_local() (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; | |||||
| } | |||||
| } | |||||
| } | |||||
| BKE_main_id_newptr_and_tag_clear(bmain); | |||||
| } | |||||
| static void wm_link_do(WMLinkAppendData *lapp_data, | |||||
| ReportList *reports, | |||||
| Main *bmain, | |||||
| Scene *scene, | |||||
| ViewLayer *view_layer, | |||||
| const View3D *v3d) | |||||
| { | |||||
| Main *mainl; | |||||
| BlendHandle *bh; | |||||
| Library *lib; | |||||
| const int flag = lapp_data->flag; | |||||
| const int id_tag_extra = 0; | |||||
| LinkNode *liblink, *itemlink; | |||||
| int lib_idx, item_idx; | |||||
| BLI_assert(lapp_data->num_items && lapp_data->num_libraries); | |||||
| for (lib_idx = 0, liblink = lapp_data->libraries.list; liblink; | |||||
| lib_idx++, liblink = liblink->next) { | |||||
| char *libname = liblink->link; | |||||
| BlendFileReadReport bf_reports = {.reports = reports}; | |||||
| if (STREQ(libname, BLO_EMBEDDED_STARTUP_BLEND)) { | |||||
| bh = BLO_blendhandle_from_memory( | |||||
| datatoc_startup_blend, datatoc_startup_blend_size, &bf_reports); | |||||
| } | |||||
| else { | |||||
| bh = BLO_blendhandle_from_file(libname, &bf_reports); | |||||
| } | |||||
| if (bh == NULL) { | |||||
| /* Unlikely since we just browsed it, but possible | |||||
| * Error reports will have been made by BLO_blendhandle_from_file() */ | |||||
| continue; | |||||
| } | |||||
| /* here appending/linking starts */ | |||||
| struct LibraryLink_Params liblink_params; | |||||
| BLO_library_link_params_init_with_context( | |||||
| &liblink_params, bmain, flag, id_tag_extra, scene, view_layer, v3d); | |||||
| /* In case of append, do not handle instantiation in linking process, but during append phase | |||||
| * (see #wm_append_loose_data_instantiate ). */ | |||||
| if ((flag & FILE_LINK) == 0) { | |||||
| liblink_params.flag &= ~BLO_LIBLINK_NEEDS_ID_TAG_DOIT; | |||||
| } | |||||
| mainl = BLO_library_link_begin(&bh, libname, &liblink_params); | |||||
| lib = mainl->curlib; | |||||
| BLI_assert(lib); | |||||
| UNUSED_VARS_NDEBUG(lib); | |||||
| if (mainl->versionfile < 250) { | |||||
| BKE_reportf(reports, | |||||
| RPT_WARNING, | |||||
| "Linking or appending from a very old .blend file format (%d.%d), no animation " | |||||
| "conversion will " | |||||
| "be done! You may want to re-save your lib file with current Blender", | |||||
| mainl->versionfile, | |||||
| mainl->subversionfile); | |||||
| } | |||||
| /* For each lib file, we try to link all items belonging to that lib, | |||||
| * and tag those successful to not try to load them again with the other libs. */ | |||||
| for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; | |||||
| item_idx++, itemlink = itemlink->next) { | |||||
| WMLinkAppendDataItem *item = itemlink->link; | |||||
| ID *new_id; | |||||
| if (!BLI_BITMAP_TEST(item->libraries, lib_idx)) { | |||||
| continue; | |||||
| } | |||||
| new_id = BLO_library_link_named_part(mainl, &bh, item->idcode, item->name, &liblink_params); | |||||
| if (new_id) { | |||||
| /* If the link is successful, clear item's libs 'todo' flags. | |||||
| * This avoids trying to link same item with other libraries to come. */ | |||||
| BLI_bitmap_set_all(item->libraries, false, lapp_data->num_libraries); | |||||
| item->new_id = new_id; | |||||
| item->source_library = new_id->lib; | |||||
| } | |||||
| } | |||||
| BLO_library_link_end(mainl, &bh, &liblink_params); | |||||
| BLO_blendhandle_close(bh); | |||||
| } | |||||
| } | |||||
| /** | /** | ||||
| * Check if an item defined by \a name and \a group can be appended/linked. | * Check if an item defined by \a name and \a group can be appended/linked. | ||||
| * | * | ||||
| * \param reports: Optionally report an error when an item can't be appended/linked. | * \param reports: Optionally report an error when an item can't be appended/linked. | ||||
| */ | */ | ||||
| static bool wm_link_append_item_poll(ReportList *reports, | static bool wm_link_append_item_poll(ReportList *reports, | ||||
| const char *path, | const char *path, | ||||
| const char *group, | const char *group, | ||||
| Show All 34 Lines | |||||
| } | } | ||||
| static int wm_link_append_exec(bContext *C, wmOperator *op) | static int wm_link_append_exec(bContext *C, wmOperator *op) | ||||
| { | { | ||||
| Main *bmain = CTX_data_main(C); | Main *bmain = CTX_data_main(C); | ||||
| Scene *scene = CTX_data_scene(C); | Scene *scene = CTX_data_scene(C); | ||||
| ViewLayer *view_layer = CTX_data_view_layer(C); | ViewLayer *view_layer = CTX_data_view_layer(C); | ||||
| PropertyRNA *prop; | PropertyRNA *prop; | ||||
| WMLinkAppendData *lapp_data; | BlendfileLinkAppendContext *lapp_context; | ||||
| char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX_LIBEXTRA], relname[FILE_MAX]; | char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX_LIBEXTRA], relname[FILE_MAX]; | ||||
| char *group, *name; | char *group, *name; | ||||
| int totfiles = 0; | int totfiles = 0; | ||||
| RNA_string_get(op->ptr, "filename", relname); | RNA_string_get(op->ptr, "filename", relname); | ||||
| RNA_string_get(op->ptr, "directory", root); | RNA_string_get(op->ptr, "directory", root); | ||||
| BLI_join_dirfile(path, sizeof(path), root, relname); | BLI_join_dirfile(path, sizeof(path), root, relname); | ||||
| ▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | static int wm_link_append_exec(bContext *C, wmOperator *op) | ||||
| /* tag everything, all untagged data can be made local | /* tag everything, all untagged data can be made local | ||||
| * its also generally useful to know what is new | * its also generally useful to know what is new | ||||
| * | * | ||||
| * take extra care BKE_main_id_flag_all(bmain, LIB_TAG_PRE_EXISTING, false) is called after! */ | * take extra care BKE_main_id_flag_all(bmain, LIB_TAG_PRE_EXISTING, false) is called after! */ | ||||
| BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); | BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); | ||||
| /* We define our working data... | /* We define our working data... | ||||
| * Note that here, each item 'uses' one library, and only one. */ | * Note that here, each item 'uses' one library, and only one. */ | ||||
| lapp_data = wm_link_append_data_new(flag); | lapp_context = BKE_blendfile_link_append_context_new(flag); | ||||
| BKE_blendfile_link_append_context_embedded_blendfile_set( | |||||
| lapp_context, datatoc_startup_blend, datatoc_startup_blend_size); | |||||
| if (totfiles != 0) { | if (totfiles != 0) { | ||||
| GHash *libraries = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__); | GHash *libraries = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__); | ||||
| int lib_idx = 0; | int lib_idx = 0; | ||||
| RNA_BEGIN (op->ptr, itemptr, "files") { | RNA_BEGIN (op->ptr, itemptr, "files") { | ||||
| RNA_string_get(&itemptr, "name", relname); | RNA_string_get(&itemptr, "name", relname); | ||||
| BLI_join_dirfile(path, sizeof(path), root, relname); | BLI_join_dirfile(path, sizeof(path), root, relname); | ||||
| if (BLO_library_path_explode(path, libname, &group, &name)) { | if (BLO_library_path_explode(path, libname, &group, &name)) { | ||||
| if (!wm_link_append_item_poll(NULL, path, group, name, do_append)) { | if (!wm_link_append_item_poll(NULL, path, group, name, do_append)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| if (!BLI_ghash_haskey(libraries, libname)) { | if (!BLI_ghash_haskey(libraries, libname)) { | ||||
| BLI_ghash_insert(libraries, BLI_strdup(libname), POINTER_FROM_INT(lib_idx)); | BLI_ghash_insert(libraries, BLI_strdup(libname), POINTER_FROM_INT(lib_idx)); | ||||
| lib_idx++; | lib_idx++; | ||||
| wm_link_append_data_library_add(lapp_data, libname); | BKE_blendfile_link_append_context_library_add(lapp_context, libname); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| RNA_END; | RNA_END; | ||||
| RNA_BEGIN (op->ptr, itemptr, "files") { | RNA_BEGIN (op->ptr, itemptr, "files") { | ||||
| RNA_string_get(&itemptr, "name", relname); | RNA_string_get(&itemptr, "name", relname); | ||||
| BLI_join_dirfile(path, sizeof(path), root, relname); | BLI_join_dirfile(path, sizeof(path), root, relname); | ||||
| if (BLO_library_path_explode(path, libname, &group, &name)) { | if (BLO_library_path_explode(path, libname, &group, &name)) { | ||||
| WMLinkAppendDataItem *item; | BlendfileLinkAppendContextItem *item; | ||||
| if (!wm_link_append_item_poll(op->reports, path, group, name, do_append)) { | if (!wm_link_append_item_poll(op->reports, path, group, name, do_append)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| lib_idx = POINTER_AS_INT(BLI_ghash_lookup(libraries, libname)); | lib_idx = POINTER_AS_INT(BLI_ghash_lookup(libraries, libname)); | ||||
| item = wm_link_append_data_item_add( | item = BKE_blendfile_link_append_context_item_add( | ||||
| lapp_data, name, BKE_idtype_idcode_from_name(group), NULL); | lapp_context, name, BKE_idtype_idcode_from_name(group), NULL); | ||||
| BLI_BITMAP_ENABLE(item->libraries, lib_idx); | BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, item, lib_idx); | ||||
| } | } | ||||
| } | } | ||||
| RNA_END; | RNA_END; | ||||
| BLI_ghash_free(libraries, MEM_freeN, NULL); | BLI_ghash_free(libraries, MEM_freeN, NULL); | ||||
| } | } | ||||
| else { | else { | ||||
| WMLinkAppendDataItem *item; | BlendfileLinkAppendContextItem *item; | ||||
| wm_link_append_data_library_add(lapp_data, libname); | BKE_blendfile_link_append_context_library_add(lapp_context, libname); | ||||
| item = wm_link_append_data_item_add(lapp_data, name, BKE_idtype_idcode_from_name(group), NULL); | item = BKE_blendfile_link_append_context_item_add( | ||||
| BLI_BITMAP_ENABLE(item->libraries, 0); | lapp_context, name, BKE_idtype_idcode_from_name(group), NULL); | ||||
| BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, item, 0); | |||||
| } | } | ||||
| if (lapp_data->num_items == 0) { | if (BKE_blendfile_link_append_context_is_empty(lapp_context)) { | ||||
| /* Early out in case there is nothing to link. */ | /* Early out in case there is nothing to link. */ | ||||
| wm_link_append_data_free(lapp_data); | BKE_blendfile_link_append_context_free(lapp_context); | ||||
| /* Clear pre existing tag. */ | /* Clear pre existing tag. */ | ||||
| BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); | BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| /* XXX We'd need re-entrant locking on Main for this to work... */ | /* XXX We'd need re-entrant locking on Main for this to work... */ | ||||
| // BKE_main_lock(bmain); | // BKE_main_lock(bmain); | ||||
| wm_link_do(lapp_data, op->reports, bmain, scene, view_layer, CTX_wm_view3d(C)); | BKE_blendfile_link(lapp_context, op->reports, bmain, scene, view_layer, CTX_wm_view3d(C)); | ||||
| // BKE_main_unlock(bmain); | // BKE_main_unlock(bmain); | ||||
| /* mark all library linked objects to be updated */ | /* mark all library linked objects to be updated */ | ||||
| BKE_main_lib_objects_recalc_all(bmain); | BKE_main_lib_objects_recalc_all(bmain); | ||||
| IMB_colormanagement_check_file_config(bmain); | IMB_colormanagement_check_file_config(bmain); | ||||
| /* append, rather than linking */ | /* append, rather than linking */ | ||||
| if (do_append) { | if (do_append) { | ||||
| wm_append_do(lapp_data, op->reports, bmain, scene, view_layer, CTX_wm_view3d(C)); | BKE_blendfile_append(lapp_context, op->reports, bmain, scene, view_layer, CTX_wm_view3d(C)); | ||||
| } | } | ||||
| wm_link_append_data_free(lapp_data); | BKE_blendfile_link_append_context_free(lapp_context); | ||||
| /* important we unset, otherwise these object won't | /* important we unset, otherwise these object won't | ||||
| * link into other scenes from this blend file */ | * link into other scenes from this blend file */ | ||||
| BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); | BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); | ||||
| /* TODO(sergey): Use proper flag for tagging here. */ | /* TODO(sergey): Use proper flag for tagging here. */ | ||||
| /* TODO(dalai): Temporary solution! | /* TODO(dalai): Temporary solution! | ||||
| ▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | static ID *wm_file_link_append_datablock_ex(Main *bmain, | ||||
| const char *id_name, | const char *id_name, | ||||
| const int flag) | const int flag) | ||||
| { | { | ||||
| const bool do_append = (flag & FILE_LINK) == 0; | const bool do_append = (flag & FILE_LINK) == 0; | ||||
| /* Tag everything so we can make local only the new datablock. */ | /* Tag everything so we can make local only the new datablock. */ | ||||
| BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); | BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); | ||||
| /* Define working data, with just the one item we want to link. */ | /* Define working data, with just the one item we want to link. */ | ||||
| WMLinkAppendData *lapp_data = wm_link_append_data_new(flag); | BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(flag); | ||||
| BKE_blendfile_link_append_context_embedded_blendfile_set( | |||||
| wm_link_append_data_library_add(lapp_data, filepath); | lapp_context, datatoc_startup_blend, datatoc_startup_blend_size); | ||||
| WMLinkAppendDataItem *item = wm_link_append_data_item_add(lapp_data, id_name, id_code, NULL); | |||||
| BLI_BITMAP_ENABLE(item->libraries, 0); | BKE_blendfile_link_append_context_library_add(lapp_context, filepath); | ||||
| BlendfileLinkAppendContextItem *item = BKE_blendfile_link_append_context_item_add( | |||||
| lapp_context, id_name, id_code, NULL); | |||||
| BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, item, 0); | |||||
| /* Link datablock. */ | /* Link datablock. */ | ||||
| wm_link_do(lapp_data, NULL, bmain, scene, view_layer, v3d); | BKE_blendfile_link(lapp_context, NULL, bmain, scene, view_layer, v3d); | ||||
| if (do_append) { | if (do_append) { | ||||
| wm_append_do(lapp_data, NULL, bmain, scene, view_layer, v3d); | BKE_blendfile_append(lapp_context, NULL, bmain, scene, view_layer, v3d); | ||||
| } | } | ||||
| /* Get linked datablock and free working data. */ | /* Get linked datablock and free working data. */ | ||||
| ID *id = item->new_id; | ID *id = BKE_blendfile_link_append_context_item_newid_get(lapp_context, item); | ||||
| wm_link_append_data_free(lapp_data); | BKE_blendfile_link_append_context_free(lapp_context); | ||||
| BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); | BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); | ||||
| return id; | return id; | ||||
| } | } | ||||
| /* | /* | ||||
| * NOTE: `scene` (and related `view_layer` and `v3d`) pointers may be NULL, in which case no | * NOTE: `scene` (and related `view_layer` and `v3d`) pointers may be NULL, in which case no | ||||
| ▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | if (lib) { | ||||
| WM_event_add_fileselect(C, op); | WM_event_add_fileselect(C, op); | ||||
| return OPERATOR_RUNNING_MODAL; | return OPERATOR_RUNNING_MODAL; | ||||
| } | } | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| static void lib_relocate_do_remap(Main *bmain, | |||||
| ID *old_id, | |||||
| ID *new_id, | |||||
| ReportList *reports, | |||||
| const bool do_reload, | |||||
| const short remap_flags) | |||||
| { | |||||
| BLI_assert(old_id); | |||||
| if (do_reload) { | |||||
| /* Since we asked for placeholders in case of missing IDs, | |||||
| * we expect to always get a valid one. */ | |||||
| BLI_assert(new_id); | |||||
| } | |||||
| if (new_id) { | |||||
| CLOG_INFO(&LOG, | |||||
| 4, | |||||
| "Before remap of %s, old_id users: %d, new_id users: %d", | |||||
| old_id->name, | |||||
| old_id->us, | |||||
| new_id->us); | |||||
| BKE_libblock_remap_locked(bmain, old_id, new_id, remap_flags); | |||||
| if (old_id->flag & LIB_FAKEUSER) { | |||||
| id_fake_user_clear(old_id); | |||||
| id_fake_user_set(new_id); | |||||
| } | |||||
| CLOG_INFO(&LOG, | |||||
| 4, | |||||
| "After remap of %s, old_id users: %d, new_id users: %d", | |||||
| old_id->name, | |||||
| old_id->us, | |||||
| new_id->us); | |||||
| /* In some cases, new_id might become direct link, remove parent of library in this case. */ | |||||
| if (new_id->lib->parent && (new_id->tag & LIB_TAG_INDIRECT) == 0) { | |||||
| if (do_reload) { | |||||
| BLI_assert_unreachable(); /* Should not happen in 'pure' reload case... */ | |||||
| } | |||||
| new_id->lib->parent = NULL; | |||||
| } | |||||
| } | |||||
| if (old_id->us > 0 && new_id && old_id->lib == new_id->lib) { | |||||
| /* Note that this *should* not happen - but better be safe than sorry in this area, | |||||
| * at least until we are 100% sure this cannot ever happen. | |||||
| * Also, we can safely assume names were unique so far, | |||||
| * so just replacing '.' by '~' should work, | |||||
| * but this does not totally rules out the possibility of name collision. */ | |||||
| size_t len = strlen(old_id->name); | |||||
| size_t dot_pos; | |||||
| bool has_num = false; | |||||
| for (dot_pos = len; dot_pos--;) { | |||||
| char c = old_id->name[dot_pos]; | |||||
| if (c == '.') { | |||||
| break; | |||||
| } | |||||
| if (c < '0' || c > '9') { | |||||
| has_num = false; | |||||
| break; | |||||
| } | |||||
| has_num = true; | |||||
| } | |||||
| if (has_num) { | |||||
| old_id->name[dot_pos] = '~'; | |||||
| } | |||||
| else { | |||||
| len = MIN2(len, MAX_ID_NAME - 7); | |||||
| BLI_strncpy(&old_id->name[len], "~000", 7); | |||||
| } | |||||
| id_sort_by_name(which_libbase(bmain, GS(old_id->name)), old_id, NULL); | |||||
| BKE_reportf( | |||||
| reports, | |||||
| RPT_WARNING, | |||||
| "Lib Reload: Replacing all references to old data-block '%s' by reloaded one failed, " | |||||
| "old one (%d remaining users) had to be kept and was renamed to '%s'", | |||||
| new_id->name, | |||||
| old_id->us, | |||||
| old_id->name); | |||||
| } | |||||
| } | |||||
| static void lib_relocate_do(bContext *C, | |||||
| Library *library, | |||||
| WMLinkAppendData *lapp_data, | |||||
| ReportList *reports, | |||||
| const bool do_reload) | |||||
| { | |||||
| ListBase *lbarray[INDEX_ID_MAX]; | |||||
| int lba_idx; | |||||
| LinkNode *itemlink; | |||||
| int item_idx; | |||||
| Main *bmain = CTX_data_main(C); | |||||
| Scene *scene = CTX_data_scene(C); | |||||
| ViewLayer *view_layer = CTX_data_view_layer(C); | |||||
| /* Remove all IDs to be reloaded from Main. */ | |||||
| lba_idx = set_listbasepointers(bmain, lbarray); | |||||
| while (lba_idx--) { | |||||
| ID *id = lbarray[lba_idx]->first; | |||||
| const short idcode = id ? GS(id->name) : 0; | |||||
| if (!id || !BKE_idtype_idcode_is_linkable(idcode)) { | |||||
| /* No need to reload non-linkable datatypes, | |||||
| * those will get relinked with their 'users ID'. */ | |||||
| continue; | |||||
| } | |||||
| for (; id; id = id->next) { | |||||
| if (id->lib == library) { | |||||
| WMLinkAppendDataItem *item; | |||||
| /* We remove it from current Main, and add it to items to link... */ | |||||
| /* Note that non-linkable IDs (like e.g. shapekeys) are also explicitly linked here... */ | |||||
| BLI_remlink(lbarray[lba_idx], id); | |||||
| /* Usual special code for ShapeKeys snowflakes... */ | |||||
| Key *old_key = BKE_key_from_id(id); | |||||
| if (old_key != NULL) { | |||||
| BLI_remlink(which_libbase(bmain, GS(old_key->id.name)), &old_key->id); | |||||
| } | |||||
| item = wm_link_append_data_item_add(lapp_data, id->name + 2, idcode, id); | |||||
| BLI_bitmap_set_all(item->libraries, true, lapp_data->num_libraries); | |||||
| CLOG_INFO(&LOG, 4, "Datablock to seek for: %s", id->name); | |||||
| } | |||||
| } | |||||
| } | |||||
| if (lapp_data->num_items == 0) { | |||||
| /* Early out in case there is nothing to do. */ | |||||
| return; | |||||
| } | |||||
| BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); | |||||
| /* We do not want any instantiation here! */ | |||||
| wm_link_do(lapp_data, reports, bmain, NULL, NULL, NULL); | |||||
| BKE_main_lock(bmain); | |||||
| /* We add back old id to bmain. | |||||
| * We need to do this in a first, separated loop, otherwise some of those may not be handled by | |||||
| * ID remapping, which means they would still reference old data to be deleted... */ | |||||
| for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; | |||||
| item_idx++, itemlink = itemlink->next) { | |||||
| WMLinkAppendDataItem *item = itemlink->link; | |||||
| ID *old_id = item->customdata; | |||||
| BLI_assert(old_id); | |||||
| BLI_addtail(which_libbase(bmain, GS(old_id->name)), old_id); | |||||
| /* Usual special code for ShapeKeys snowflakes... */ | |||||
| Key *old_key = BKE_key_from_id(old_id); | |||||
| if (old_key != NULL) { | |||||
| BLI_addtail(which_libbase(bmain, GS(old_key->id.name)), &old_key->id); | |||||
| } | |||||
| } | |||||
| /* Since our (old) reloaded IDs were removed from main, the user count done for them in linking | |||||
| * code is wrong, we need to redo it here after adding them back to main. */ | |||||
| BKE_main_id_refcount_recompute(bmain, false); | |||||
| /* Note that in reload case, we also want to replace indirect usages. */ | |||||
| const short remap_flags = ID_REMAP_SKIP_NEVER_NULL_USAGE | | |||||
| ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE | | |||||
| (do_reload ? 0 : ID_REMAP_SKIP_INDIRECT_USAGE); | |||||
| for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; | |||||
| item_idx++, itemlink = itemlink->next) { | |||||
| WMLinkAppendDataItem *item = itemlink->link; | |||||
| ID *old_id = item->customdata; | |||||
| ID *new_id = item->new_id; | |||||
| lib_relocate_do_remap(bmain, old_id, new_id, reports, do_reload, remap_flags); | |||||
| if (new_id == NULL) { | |||||
| continue; | |||||
| } | |||||
| /* Usual special code for ShapeKeys snowflakes... */ | |||||
| Key **old_key_p = BKE_key_from_id_p(old_id); | |||||
| if (old_key_p == NULL) { | |||||
| continue; | |||||
| } | |||||
| Key *old_key = *old_key_p; | |||||
| Key *new_key = BKE_key_from_id(new_id); | |||||
| if (old_key != NULL) { | |||||
| *old_key_p = NULL; | |||||
| id_us_min(&old_key->id); | |||||
| lib_relocate_do_remap(bmain, &old_key->id, &new_key->id, reports, do_reload, remap_flags); | |||||
| *old_key_p = old_key; | |||||
| id_us_plus_no_lib(&old_key->id); | |||||
| } | |||||
| } | |||||
| BKE_main_unlock(bmain); | |||||
| for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; | |||||
| item_idx++, itemlink = itemlink->next) { | |||||
| WMLinkAppendDataItem *item = itemlink->link; | |||||
| ID *old_id = item->customdata; | |||||
| if (old_id->us == 0) { | |||||
| BKE_id_free(bmain, old_id); | |||||
| } | |||||
| } | |||||
| /* Some datablocks can get reloaded/replaced 'silently' because they are not linkable | |||||
| * (shape keys e.g.), so we need another loop here to clear old ones if possible. */ | |||||
| lba_idx = set_listbasepointers(bmain, lbarray); | |||||
| while (lba_idx--) { | |||||
| ID *id, *id_next; | |||||
| for (id = lbarray[lba_idx]->first; id; id = id_next) { | |||||
| id_next = id->next; | |||||
| /* XXX That check may be a bit to generic/permissive? */ | |||||
| if (id->lib && (id->flag & LIB_TAG_PRE_EXISTING) && id->us == 0) { | |||||
| BKE_id_free(bmain, id); | |||||
| } | |||||
| } | |||||
| } | |||||
| /* Get rid of no more used libraries... */ | |||||
| BKE_main_id_tag_idcode(bmain, ID_LI, LIB_TAG_DOIT, true); | |||||
| lba_idx = set_listbasepointers(bmain, lbarray); | |||||
| while (lba_idx--) { | |||||
| ID *id; | |||||
| for (id = lbarray[lba_idx]->first; id; id = id->next) { | |||||
| if (id->lib) { | |||||
| id->lib->id.tag &= ~LIB_TAG_DOIT; | |||||
| } | |||||
| } | |||||
| } | |||||
| Library *lib, *lib_next; | |||||
| for (lib = which_libbase(bmain, ID_LI)->first; lib; lib = lib_next) { | |||||
| lib_next = lib->id.next; | |||||
| if (lib->id.tag & LIB_TAG_DOIT) { | |||||
| id_us_clear_real(&lib->id); | |||||
| if (lib->id.us == 0) { | |||||
| BKE_id_free(bmain, (ID *)lib); | |||||
| } | |||||
| } | |||||
| } | |||||
| /* Update overrides of reloaded linked data-blocks. */ | |||||
| ID *id; | |||||
| FOREACH_MAIN_ID_BEGIN (bmain, id) { | |||||
| if (ID_IS_LINKED(id) || !ID_IS_OVERRIDE_LIBRARY_REAL(id) || | |||||
| (id->tag & LIB_TAG_PRE_EXISTING) == 0) { | |||||
| continue; | |||||
| } | |||||
| if ((id->override_library->reference->tag & LIB_TAG_PRE_EXISTING) == 0) { | |||||
| BKE_lib_override_library_update(bmain, id); | |||||
| } | |||||
| } | |||||
| FOREACH_MAIN_ID_END; | |||||
| /* Resync overrides if needed. */ | |||||
| if (!USER_EXPERIMENTAL_TEST(&U, no_override_auto_resync)) { | |||||
| BKE_lib_override_library_main_resync(bmain, | |||||
| scene, | |||||
| view_layer, | |||||
| &(struct BlendFileReadReport){ | |||||
| .reports = reports, | |||||
| }); | |||||
| /* We need to rebuild some of the deleted override rules (for UI feedback purpose). */ | |||||
| BKE_lib_override_library_main_operations_create(bmain, true); | |||||
| } | |||||
| BKE_main_collection_sync(bmain); | |||||
| BKE_main_lib_objects_recalc_all(bmain); | |||||
| IMB_colormanagement_check_file_config(bmain); | |||||
| /* important we unset, otherwise these object won't | |||||
| * link into other scenes from this blend file */ | |||||
| BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); | |||||
| /* recreate dependency graph to include new objects */ | |||||
| DEG_relations_tag_update(bmain); | |||||
| } | |||||
| void WM_lib_reload(Library *lib, bContext *C, ReportList *reports) | void WM_lib_reload(Library *lib, bContext *C, ReportList *reports) | ||||
| { | { | ||||
| if (!BLO_has_bfile_extension(lib->filepath_abs)) { | if (!BLO_has_bfile_extension(lib->filepath_abs)) { | ||||
| BKE_reportf(reports, RPT_ERROR, "'%s' is not a valid library filepath", lib->filepath_abs); | BKE_reportf(reports, RPT_ERROR, "'%s' is not a valid library filepath", lib->filepath_abs); | ||||
| return; | return; | ||||
| } | } | ||||
| if (!BLI_exists(lib->filepath_abs)) { | if (!BLI_exists(lib->filepath_abs)) { | ||||
| BKE_reportf(reports, | BKE_reportf(reports, | ||||
| RPT_ERROR, | RPT_ERROR, | ||||
| "Trying to reload library '%s' from invalid path '%s'", | "Trying to reload library '%s' from invalid path '%s'", | ||||
| lib->id.name, | lib->id.name, | ||||
| lib->filepath_abs); | lib->filepath_abs); | ||||
| return; | return; | ||||
| } | } | ||||
| WMLinkAppendData *lapp_data = wm_link_append_data_new(BLO_LIBLINK_USE_PLACEHOLDERS | | Main *bmain = CTX_data_main(C); | ||||
| BLO_LIBLINK_FORCE_INDIRECT); | BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new( | ||||
| BLO_LIBLINK_USE_PLACEHOLDERS | BLO_LIBLINK_FORCE_INDIRECT); | |||||
| BKE_blendfile_link_append_context_library_add(lapp_context, lib->filepath_abs); | |||||
| wm_link_append_data_library_add(lapp_data, lib->filepath_abs); | BKE_blendfile_library_relocate( | ||||
| lapp_context, reports, lib, true, bmain, CTX_data_scene(C), CTX_data_view_layer(C)); | |||||
| BKE_blendfile_link_append_context_free(lapp_context); | |||||
| BKE_main_lib_objects_recalc_all(bmain); | |||||
| IMB_colormanagement_check_file_config(bmain); | |||||
| lib_relocate_do(C, lib, lapp_data, reports, true); | /* Important we unset, otherwise these object won't link into other scenes from this blend file. | ||||
| */ | |||||
| BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); | |||||
| wm_link_append_data_free(lapp_data); | /* Recreate dependency graph to include new IDs. */ | ||||
| DEG_relations_tag_update(bmain); | |||||
| WM_event_add_notifier(C, NC_WINDOW, NULL); | WM_event_add_notifier(C, NC_WINDOW, NULL); | ||||
| } | } | ||||
| static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) | static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload) | ||||
| { | { | ||||
| Library *lib; | Library *lib; | ||||
| char lib_name[MAX_NAME]; | char lib_name[MAX_NAME]; | ||||
| RNA_string_get(op->ptr, "library", lib_name); | RNA_string_get(op->ptr, "library", lib_name); | ||||
| lib = (Library *)BKE_libblock_find_name(CTX_data_main(C), ID_LI, lib_name); | lib = (Library *)BKE_libblock_find_name(CTX_data_main(C), ID_LI, lib_name); | ||||
| if (lib) { | if (lib) { | ||||
| Main *bmain = CTX_data_main(C); | Main *bmain = CTX_data_main(C); | ||||
| PropertyRNA *prop; | PropertyRNA *prop; | ||||
| WMLinkAppendData *lapp_data; | BlendfileLinkAppendContext *lapp_context; | ||||
| char path[FILE_MAX], root[FILE_MAXDIR], libname[FILE_MAX], relname[FILE_MAX]; | char path[FILE_MAX], root[FILE_MAXDIR], libname[FILE_MAX], relname[FILE_MAX]; | ||||
| short flag = 0; | short flag = 0; | ||||
| if (RNA_boolean_get(op->ptr, "relative_path")) { | if (RNA_boolean_get(op->ptr, "relative_path")) { | ||||
| flag |= FILE_RELPATH; | flag |= FILE_RELPATH; | ||||
| } | } | ||||
| Show All 33 Lines | if (BLI_path_cmp(BKE_main_blendfile_path(bmain), path) == 0) { | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| if (BLI_path_cmp(lib->filepath_abs, path) == 0) { | if (BLI_path_cmp(lib->filepath_abs, path) == 0) { | ||||
| CLOG_INFO(&LOG, 4, "We are supposed to reload '%s' lib (%d)", lib->filepath, lib->id.us); | CLOG_INFO(&LOG, 4, "We are supposed to reload '%s' lib (%d)", lib->filepath, lib->id.us); | ||||
| do_reload = true; | do_reload = true; | ||||
| lapp_data = wm_link_append_data_new(flag); | lapp_context = BKE_blendfile_link_append_context_new(flag); | ||||
| wm_link_append_data_library_add(lapp_data, path); | BKE_blendfile_link_append_context_library_add(lapp_context, path); | ||||
| } | } | ||||
| else { | else { | ||||
| int totfiles = 0; | int totfiles = 0; | ||||
| CLOG_INFO( | CLOG_INFO( | ||||
| &LOG, 4, "We are supposed to relocate '%s' lib to new '%s' one", lib->filepath, libname); | &LOG, 4, "We are supposed to relocate '%s' lib to new '%s' one", lib->filepath, libname); | ||||
| /* Check if something is indicated for relocate. */ | /* Check if something is indicated for relocate. */ | ||||
| prop = RNA_struct_find_property(op->ptr, "files"); | prop = RNA_struct_find_property(op->ptr, "files"); | ||||
| if (prop) { | if (prop) { | ||||
| totfiles = RNA_property_collection_length(op->ptr, prop); | totfiles = RNA_property_collection_length(op->ptr, prop); | ||||
| if (totfiles == 0) { | if (totfiles == 0) { | ||||
| if (!libname[0]) { | if (!libname[0]) { | ||||
| BKE_report(op->reports, RPT_ERROR, "Nothing indicated"); | BKE_report(op->reports, RPT_ERROR, "Nothing indicated"); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| lapp_data = wm_link_append_data_new(flag); | lapp_context = BKE_blendfile_link_append_context_new(flag); | ||||
| if (totfiles) { | if (totfiles) { | ||||
| RNA_BEGIN (op->ptr, itemptr, "files") { | RNA_BEGIN (op->ptr, itemptr, "files") { | ||||
| RNA_string_get(&itemptr, "name", relname); | RNA_string_get(&itemptr, "name", relname); | ||||
| BLI_join_dirfile(path, sizeof(path), root, relname); | BLI_join_dirfile(path, sizeof(path), root, relname); | ||||
| if (BLI_path_cmp(path, lib->filepath_abs) == 0 || !BLO_has_bfile_extension(relname)) { | if (BLI_path_cmp(path, lib->filepath_abs) == 0 || !BLO_has_bfile_extension(relname)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| CLOG_INFO(&LOG, 4, "\tCandidate new lib to reload datablocks from: %s", path); | CLOG_INFO(&LOG, 4, "\tCandidate new lib to reload datablocks from: %s", path); | ||||
| wm_link_append_data_library_add(lapp_data, path); | BKE_blendfile_link_append_context_library_add(lapp_context, path); | ||||
| } | } | ||||
| RNA_END; | RNA_END; | ||||
| } | } | ||||
| else { | else { | ||||
| CLOG_INFO(&LOG, 4, "\tCandidate new lib to reload datablocks from: %s", path); | CLOG_INFO(&LOG, 4, "\tCandidate new lib to reload datablocks from: %s", path); | ||||
| wm_link_append_data_library_add(lapp_data, path); | BKE_blendfile_link_append_context_library_add(lapp_context, path); | ||||
| } | } | ||||
| } | } | ||||
| if (do_reload) { | if (do_reload) { | ||||
| lapp_data->flag |= BLO_LIBLINK_USE_PLACEHOLDERS | BLO_LIBLINK_FORCE_INDIRECT; | BKE_blendfile_link_append_context_flag_set( | ||||
| lapp_context, BLO_LIBLINK_USE_PLACEHOLDERS | BLO_LIBLINK_FORCE_INDIRECT, true); | |||||
| } | } | ||||
| lib_relocate_do(C, lib, lapp_data, op->reports, do_reload); | BKE_blendfile_library_relocate(lapp_context, | ||||
| op->reports, | |||||
| lib, | |||||
| do_reload, | |||||
| bmain, | |||||
| CTX_data_scene(C), | |||||
| CTX_data_view_layer(C)); | |||||
| wm_link_append_data_free(lapp_data); | BKE_blendfile_link_append_context_free(lapp_context); | ||||
| /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */ | /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */ | ||||
| BLI_strncpy(G.lib, root, FILE_MAX); | BLI_strncpy(G.lib, root, FILE_MAX); | ||||
| BKE_main_lib_objects_recalc_all(bmain); | |||||
| IMB_colormanagement_check_file_config(bmain); | |||||
| /* Important we unset, otherwise these object won't link into other scenes from this blend | |||||
| * file. | |||||
| */ | |||||
| BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); | |||||
| /* Recreate dependency graph to include new IDs. */ | |||||
| DEG_relations_tag_update(bmain); | |||||
| WM_event_add_notifier(C, NC_WINDOW, NULL); | WM_event_add_notifier(C, NC_WINDOW, NULL); | ||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 62 Lines • Show Last 20 Lines | |||||