diff options
-rw-r--r-- | source/blender/blenkernel/BKE_lib_id.h | 6 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/lib_id.c | 27 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/lib_id_test.cc | 60 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_250.c | 2 |
4 files changed, 81 insertions, 14 deletions
diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 7ac45ac4883..e16507bf3cc 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -258,8 +258,10 @@ void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b); void id_sort_by_name(struct ListBase *lb, struct ID *id, struct ID *id_sorting_hint); void BKE_lib_id_expand_local(struct Main *bmain, struct ID *id); -bool BKE_id_new_name_validate(struct ListBase *lb, struct ID *id, const char *name) - ATTR_NONNULL(1, 2); +bool BKE_id_new_name_validate(struct ListBase *lb, + struct ID *id, + const char *name, + const bool do_linked_data) ATTR_NONNULL(1, 2); void BKE_lib_id_clear_library_data(struct Main *bmain, struct ID *id); /* Affect whole Main database. */ diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index f93bf494ee9..8a6fb5b3e9e 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -164,7 +164,7 @@ static void lib_id_clear_library_data_ex(Main *bmain, ID *id) 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)) { + if (BKE_id_new_name_validate(which_libbase(bmain, GS(id->name)), id, NULL, false)) { bmain->is_memfile_undo_written = false; } } @@ -833,7 +833,9 @@ void BKE_libblock_management_main_add(Main *bmain, void *idv) ListBase *lb = which_libbase(bmain, GS(id->name)); BKE_main_lock(bmain); BLI_addtail(lb, id); - BKE_id_new_name_validate(lb, id, NULL); + /* We need to allow adding extra datablocks into libraries too, e.g. to support generating new + * overrides for recursive resync. */ + BKE_id_new_name_validate(lb, id, NULL, true); /* alphabetic insertion: is in new_id */ id->tag &= ~(LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT); bmain->is_memfile_undo_written = false; @@ -989,7 +991,7 @@ void BKE_main_id_repair_duplicate_names_listbase(ListBase *lb) } 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); + BKE_id_new_name_validate(lb, id_array[i], NULL, false); } } BLI_gset_free(gset, NULL); @@ -1092,7 +1094,7 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl BKE_main_lock(bmain); BLI_addtail(lb, id); - BKE_id_new_name_validate(lb, id, name); + BKE_id_new_name_validate(lb, id, name, false); bmain->is_memfile_undo_written = false; /* alphabetic insertion: is in new_id */ BKE_main_unlock(bmain); @@ -1557,7 +1559,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ * 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 != id_test && id_test->lib == id->lib) { 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; @@ -1613,7 +1615,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ 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]) && + if ((id != id_test) && (id_test->lib == id->lib) && (name[0] == id_test->name[2]) && (ELEM(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, '.') == @@ -1702,15 +1704,18 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_ * * Only for local IDs (linked ones already have a unique ID in their library). * + * \param do_linked_data if true, also ensure a unique name in case the given \a id is linked + * (otherwise, just ensure that it is properly sorted). + * * \return true if a new name had to be created. */ -bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname) +bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const bool do_linked_data) { bool result = false; char name[MAX_ID_NAME - 2]; - /* If library, don't rename, but do ensure proper sorting. */ - if (ID_IS_LINKED(id)) { + /* If library, don't rename (unless explicitely required), but do ensure proper sorting. */ + if (!do_linked_data && ID_IS_LINKED(id)) { id_sort_by_name(lb, id, NULL); return result; @@ -2195,7 +2200,7 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) 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); + BKE_id_new_name_validate(lb, idtest, NULL, false); bmain->is_memfile_undo_written = false; } } @@ -2206,7 +2211,7 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name) 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)) { + if (BKE_id_new_name_validate(lb, id, name, false)) { bmain->is_memfile_undo_written = false; } } diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc index fbe4a15da1c..8e21ae88aa6 100644 --- a/source/blender/blenkernel/intern/lib_id_test.cc +++ b/source/blender/blenkernel/intern/lib_id_test.cc @@ -20,6 +20,7 @@ #include "MEM_guardedalloc.h" #include "BLI_listbase.h" +#include "BLI_string.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" @@ -110,4 +111,63 @@ TEST(lib_id_main_sort, linked_ids_1) test_lib_id_main_sort_free(&ctx); } +TEST(lib_id_main_unique_name, local_ids_1) +{ + LibIDMainSortTestContext ctx = {nullptr}; + test_lib_id_main_sort_init(&ctx); + EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); + + ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C")); + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); + test_lib_id_main_sort_check_order({id_a, id_b, id_c}); + + BLI_strncpy(id_c->name, id_a->name, sizeof(id_c->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_c, NULL, false); + EXPECT_TRUE(strcmp(id_c->name + 2, "OB_A.001") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_a); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_a, id_c, id_b}); + + test_lib_id_main_sort_free(&ctx); +} + +TEST(lib_id_main_unique_name, linked_ids_1) +{ + LibIDMainSortTestContext ctx = {nullptr}; + test_lib_id_main_sort_init(&ctx); + EXPECT_TRUE(BLI_listbase_is_empty(&ctx.bmain->libraries)); + + Library *lib_a = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_A")); + Library *lib_b = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LI_B")); + ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_C")); + ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_A")); + ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "OB_B")); + + id_a->lib = lib_a; + id_sort_by_name(&ctx.bmain->objects, id_a, nullptr); + id_b->lib = lib_a; + id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); + BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true); + EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A.001") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_c); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_c, id_a, id_b}); + + id_b->lib = lib_b; + id_sort_by_name(&ctx.bmain->objects, id_b, nullptr); + BLI_strncpy(id_b->name, id_a->name, sizeof(id_b->name)); + BKE_id_new_name_validate(&ctx.bmain->objects, id_b, NULL, true); + EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A") == 0); + EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0); + EXPECT_TRUE(ctx.bmain->objects.first == id_c); + EXPECT_TRUE(ctx.bmain->objects.last == id_b); + test_lib_id_main_sort_check_order({id_c, id_a, id_b}); + + test_lib_id_main_sort_free(&ctx); +} + } // namespace blender::bke::tests diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 6338dc95aba..990fc1d65d7 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -446,7 +446,7 @@ static void versions_gpencil_add_main(ListBase *lb, ID *id, const char *name) id->flag = LIB_FAKEUSER; *((short *)id->name) = ID_GD; - BKE_id_new_name_validate(lb, id, name); + BKE_id_new_name_validate(lb, id, name, false); /* alphabetic insertion: is in BKE_id_new_name_validate */ if ((id->tag & LIB_TAG_TEMP_MAIN) == 0) { |