ID Management: Allow unique name check for linked IDs too.

This is mandatory for liboverride resync, since this feature may imply
we have to create linked overrides in libraries, and there may be
several copies of those.

This is also a first step to a more general support of IDmanagement-editing
library data.

Note that this commit should have absolutely no effect on current code,
as the only function allowed to check unique names for linked IDs
currently is `BKE_libblock_management_main_add`, which is unused.

This commit also adds some basic testing for `BKE_id_new_name_validate`.
This commit is contained in:
Bastien Montagne 2021-06-01 11:39:28 +02:00
parent 27910ccce6
commit db15c9d1bd
4 changed files with 81 additions and 14 deletions

View File

@ -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. */

View File

@ -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;
}
}

View File

@ -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

View File

@ -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) {