/** 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 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); }
/** Generate the mappings between used IDs and their users, and vice-versa. */ void BKE_main_relations_create(Main *bmain) { if (bmain->relations != NULL) { BKE_main_relations_free(bmain); } bmain->relations = MEM_mallocN(sizeof(*bmain->relations), __func__); bmain->relations->id_used_to_user = BLI_ghash_new( BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); bmain->relations->id_user_to_used = BLI_ghash_new( BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); bmain->relations->entry_pool = BLI_mempool_create( sizeof(MainIDRelationsEntry), 128, 128, BLI_MEMPOOL_NOP); ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { BKE_library_foreach_ID_link( NULL, id, main_relations_create_idlink_cb, bmain->relations, IDWALK_READONLY); }
/** * Execute the 'data' part of the remapping (that is, all ID pointers from other ID datablocks). * * 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 datablock 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 datablock to operate on (can be NULL, in which case we operate over all IDs from given bmain). * \param old_id: the datablock to dereference (may be NULL if \a id is non-NULL). * \param new_id: the new datablock 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; ListBase *lb_array[MAX_LIBARRAY]; int i; 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(id, foreach_libblock_remap_callback, (void *)r_id_remap_data, IDWALK_NOP); } else { i = set_listbasepointers(bmain, lb_array); /* Note that this is a very 'bruteforce' 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. */ while (i--) { ID *id_curr = lb_array[i]->first; if (!id_curr || !BKE_library_idtype_can_use_idtype(GS(id_curr->name), GS(old_id->name))) { continue; } for (; id_curr; id_curr = id_curr->next) { /* 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( id_curr, foreach_libblock_remap_callback, (void *)r_id_remap_data, IDWALK_NOP); } } } if (old_id && GS(old_id->name) == ID_OB) { BKE_sca_logic_links_remap(bmain, (Object *)old_id, (Object *)new_id); } /* XXX We may not want to always 'transfer' fakeuser 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->tag |= LIB_TAG_EXTERN; } #ifdef DEBUG_PRINT printf("%s: %d occurences 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 }
static PyObject *bpy_user_map(PyObject *UNUSED(self), PyObject *args, PyObject *kwds) { #if 0 /* If someone knows how to get a proper 'self' in that case... */ BPy_StructRNA *pyrna = (BPy_StructRNA *)self; Main *bmain = pyrna->ptr.data; #else Main *bmain = G.main; /* XXX Ugly, but should work! */ #endif static const char *kwlist[] = {"subset", "key_types", "value_types", NULL}; PyObject *subset = NULL; PyObject *key_types = NULL; PyObject *val_types = NULL; BLI_bitmap *key_types_bitmap = NULL; BLI_bitmap *val_types_bitmap = NULL; PyObject *ret = NULL; if (!PyArg_ParseTupleAndKeywords( args, kwds, "|O$O!O!:user_map", (char **)kwlist, &subset, &PySet_Type, &key_types, &PySet_Type, &val_types)) { return NULL; } if (key_types) { key_types_bitmap = pyrna_set_to_enum_bitmap( rna_enum_id_type_items, key_types, sizeof(short), true, USHRT_MAX, "key types"); if (key_types_bitmap == NULL) { goto error; } } if (val_types) { val_types_bitmap = pyrna_set_to_enum_bitmap( rna_enum_id_type_items, val_types, sizeof(short), true, USHRT_MAX, "value types"); if (val_types_bitmap == NULL) { goto error; } } IDUserMapData data_cb = {NULL}; if (subset) { PyObject *subset_fast = PySequence_Fast(subset, "user_map"); if (subset_fast == NULL) { goto error; } PyObject **subset_array = PySequence_Fast_ITEMS(subset_fast); Py_ssize_t subset_len = PySequence_Fast_GET_SIZE(subset_fast); data_cb.user_map = _PyDict_NewPresized(subset_len); data_cb.is_subset = true; for (; subset_len; subset_array++, subset_len--) { PyObject *set = PySet_New(NULL); PyDict_SetItem(data_cb.user_map, *subset_array, set); Py_DECREF(set); } Py_DECREF(subset_fast); } else { data_cb.user_map = PyDict_New(); } data_cb.types_bitmap = key_types_bitmap; ListBase *lb_array[MAX_LIBARRAY]; int lb_index; lb_index = set_listbasepointers(bmain, lb_array); while (lb_index--) { if (val_types_bitmap && lb_array[lb_index]->first) { if (!id_check_type(lb_array[lb_index]->first, val_types_bitmap)) { continue; } } for (ID *id = lb_array[lb_index]->first; id; id = id->next) { /* One-time init, ID is just used as placeholder here, we abuse this in iterator callback * to avoid having to rebuild a complete bpyrna object each time for the key searching * (where only ID pointer value is used). */ if (data_cb.py_id_key_lookup_only == NULL) { data_cb.py_id_key_lookup_only = pyrna_id_CreatePyObject(id); } if (!data_cb.is_subset) { PyObject *key = data_cb.py_id_key_lookup_only; PyObject *set; RNA_id_pointer_create(id, &((BPy_StructRNA *)key)->ptr); /* We have to insert the key now, otherwise ID unused would be missing from final dict... */ if ((set = PyDict_GetItem(data_cb.user_map, key)) == NULL) { /* Cannot use our placeholder key here! */ key = pyrna_id_CreatePyObject(id); set = PySet_New(NULL); PyDict_SetItem(data_cb.user_map, key, set); Py_DECREF(set); Py_DECREF(key); } } data_cb.id_curr = id; BKE_library_foreach_ID_link(id, foreach_libblock_id_user_map_callback, &data_cb, IDWALK_NOP); if (data_cb.py_id_curr) { Py_DECREF(data_cb.py_id_curr); data_cb.py_id_curr = NULL; } } } ret = data_cb.user_map; error: Py_XDECREF(data_cb.py_id_key_lookup_only); if (key_types_bitmap) { MEM_freeN(key_types_bitmap); } if (val_types_bitmap) { MEM_freeN(val_types_bitmap); } return ret; }