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 \a 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); }
/** * 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) { ID *id = NULL; ListBase *lb = which_libbase(bmain, type); id = alloc_libblock_notest(type); if (id) { BKE_main_lock(bmain); BLI_addtail(lb, id); id->us = 1; id->icon_id = 0; *( (short *)id->name) = type; new_id(lb, id, name); /* alphabetic insertion: is in new_id */ BKE_main_unlock(bmain); } DAG_id_type_tag(bmain, type); return id; }
/** * used in headerbuttons.c image.c mesh.c screen.c sound.c and library.c * * \param do_id_user: if \a true, try to release other ID's 'references' hold by \a idv. * (only applies to main database) * \param do_ui_user: similar to do_id_user but makes sure UI does not hold references to * \a id. */ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user, const bool do_ui_user) { ID *id = idv; short type = GS(id->name); ListBase *lb = which_libbase(bmain, type); DAG_id_type_tag(bmain, type); #ifdef WITH_PYTHON BPY_id_release(id); #endif if (do_id_user) { BKE_libblock_relink_ex(bmain, id, NULL, NULL, true); } BKE_libblock_free_datablock(id, 0); /* avoid notifying on removed data */ BKE_main_lock(bmain); if (do_ui_user) { if (free_notifier_reference_cb) { free_notifier_reference_cb(id); } if (remap_editor_id_reference_cb) { remap_editor_id_reference_cb(id, NULL); } } BLI_remlink(lb, id); BKE_libblock_free_data(id, do_id_user); BKE_main_unlock(bmain); MEM_freeN(id); }
/** * used in headerbuttons.c image.c mesh.c screen.c sound.c and library.c * * \param do_id_user: if \a true, try to release other ID's 'references' hold by \a idv. */ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user) { ID *id = idv; short type = GS(id->name); ListBase *lb = which_libbase(bmain, type); DAG_id_type_tag(bmain, type); #ifdef WITH_PYTHON BPY_id_release(id); #endif if (do_id_user) { BKE_libblock_relink_ex(bmain, id, NULL, NULL, true); } switch (type) { case ID_SCE: BKE_scene_free((Scene *)id); 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_lamp_free((Lamp *)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_SO: BKE_sound_free((bSound *)id); break; case ID_GR: BKE_group_free((Group *)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; } /* avoid notifying on removed data */ BKE_main_lock(bmain); if (free_notifier_reference_cb) { free_notifier_reference_cb(id); } if (remap_editor_id_reference_cb) { remap_editor_id_reference_cb(id, NULL); } BLI_remlink(lb, id); BKE_libblock_free_data(bmain, id); BKE_main_unlock(bmain); MEM_freeN(id); }
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; } 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) { DAG_id_type_tag(bmain, type); } #ifdef WITH_PYTHON BPY_id_release(id); #endif if ((flag & LIB_ID_FREE_NO_USER_REFCOUNT) == 0) { BKE_libblock_relink_ex(bmain, id, NULL, NULL, true); } BKE_libblock_free_datablock(id, flag); /* avoid notifying on removed data */ if (bmain) { 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 (bmain) { BKE_main_unlock(bmain); } if ((flag & LIB_ID_FREE_NOT_ALLOCATED) == 0) { MEM_freeN(id); } }
/** * 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) { printf("Error in remapping process from '%s' (%p) to '%s' (%p): " "wrong user count in old ID after process (summing up to %d)\n", old_id->name, old_id, new_id ? new_id->name : "<NULL>", 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: if (!new_id) { /* Only affects us in case group was unlinked. */ for (Scene *sce = bmain->scene.first; sce; sce = sce->id.next) { libblock_remap_data_postprocess_group_scene_unlink(bmain, sce, old_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->object.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 DAG! */ DAG_relations_tag_update(bmain); }
/* Does not fix anything, but checks that all linked data-blocks are still valid (i.e. pointing to the right library). */ bool BLO_main_validate_libraries(struct Main *bmain, struct ReportList *reports) { ListBase mainlist; bool is_valid = true; BKE_main_lock(bmain); blo_split_main(&mainlist, bmain); ListBase *lbarray[MAX_LIBARRAY]; int i = set_listbasepointers(bmain, lbarray); while (i--) { for (ID *id = lbarray[i]->first; id != NULL; id = id->next) { if (id->lib != NULL) { is_valid = false; BKE_reportf(reports, RPT_ERROR, "ID %s is in local database while being linked from library %s!\n", id->name, id->lib->name); } } } for (Main *curmain = bmain->next; curmain != NULL; curmain = curmain->next) { Library *curlib = curmain->curlib; if (curlib == NULL) { BKE_reportf(reports, RPT_ERROR, "Library database with NULL library datablock!\n"); continue; } BKE_library_filepath_set(bmain, curlib, curlib->name); BlendHandle *bh = BLO_blendhandle_from_file(curlib->filepath, reports); if (bh == NULL) { BKE_reportf(reports, RPT_ERROR, "Library ID %s not found at expected path %s!\n", curlib->id.name, curlib->filepath); continue; } i = set_listbasepointers(curmain, lbarray); while (i--) { ID *id = lbarray[i]->first; if (id == NULL) { continue; } if (GS(id->name) == ID_LI) { is_valid = false; BKE_reportf(reports, RPT_ERROR, "Library ID %s in library %s, this should not happen!\n", id->name, curlib->name); continue; } int totnames = 0; LinkNode *names = BLO_blendhandle_get_datablock_names(bh, GS(id->name), &totnames); for (; id != NULL; id = id->next) { if (id->lib == NULL) { is_valid = false; BKE_reportf(reports, RPT_ERROR, "ID %s has NULL lib pointer while being in library %s!\n", id->name, curlib->name); continue; } if (id->lib != curlib) { is_valid = false; BKE_reportf(reports, RPT_ERROR, "ID %s has mismatched lib pointer!\n", id->name); continue; } LinkNode *name = names; for (; name; name = name->next) { char *str_name = (char *)name->link; if (id->name[2] == str_name[0] && STREQ(str_name, id->name + 2)) { break; } } if (name == NULL) { is_valid = false; BKE_reportf(reports, RPT_ERROR, "ID %s not found in library %s anymore!\n", id->name, id->lib->name); continue; } } BLI_linklist_free(names, free); } BLO_blendhandle_close(bh); } blo_join_main(&mainlist); BLI_assert(BLI_listbase_is_single(&mainlist)); BLI_assert(mainlist.first == (void *)bmain); BKE_main_unlock(bmain); return is_valid; }