diff options
author | Bastien Montagne <bastien@blender.org> | 2022-05-17 17:09:28 +0300 |
---|---|---|
committer | Bastien Montagne <bastien@blender.org> | 2022-05-17 17:09:28 +0300 |
commit | 8c9805fc628223984ad0c664b5a49b7f9eec0caa (patch) | |
tree | 3e7bf59bf86db2b94c90b30e287f9fd123940335 /source/blender/editors/space_outliner | |
parent | 939c2387a158722cafa84fc09f0eb13b67c603fe (diff) |
Revert "Outliner: Remove the 'Remap data-block usages' operation."
This reverts commit 30534deced8dad16c566dd82db3edd462283de13.
After discussion and feedback from users, it's better to keep this tool
available until there is time to properly re-think the whole Outliner's
contextual menu.
Diffstat (limited to 'source/blender/editors/space_outliner')
4 files changed, 217 insertions, 0 deletions
diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index f4e28af3fca..1de45b0ec96 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -576,6 +576,187 @@ void OUTLINER_OT_id_delete(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name ID Remap Operator + * \{ */ + +static int outliner_id_remap_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); + + const short id_type = (short)RNA_enum_get(op->ptr, "id_type"); + ID *old_id = reinterpret_cast<ID *>( + BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "old_id"))); + ID *new_id = reinterpret_cast<ID *>( + BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "new_id"))); + + /* check for invalid states */ + if (space_outliner == nullptr) { + return OPERATOR_CANCELLED; + } + + if (!(old_id && new_id && (old_id != new_id) && (GS(old_id->name) == GS(new_id->name)))) { + BKE_reportf(op->reports, + RPT_ERROR_INVALID_INPUT, + "Invalid old/new ID pair ('%s' / '%s')", + old_id ? old_id->name : "Invalid ID", + new_id ? new_id->name : "Invalid ID"); + return OPERATOR_CANCELLED; + } + + if (ID_IS_LINKED(old_id)) { + BKE_reportf(op->reports, + RPT_WARNING, + "Old ID '%s' is linked from a library, indirect usages of this data-block will " + "not be remapped", + old_id->name); + } + + BKE_libblock_remap( + bmain, old_id, new_id, ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_NEVER_NULL_USAGE); + + BKE_main_lib_objects_recalc_all(bmain); + + /* recreate dependency graph to include new objects */ + DEG_relations_tag_update(bmain); + + /* Free gpu materials, some materials depend on existing objects, + * such as lights so freeing correctly refreshes. */ + GPU_materials_free(bmain); + + WM_event_add_notifier(C, NC_WINDOW, nullptr); + + return OPERATOR_FINISHED; +} + +static bool outliner_id_remap_find_tree_element(bContext *C, + wmOperator *op, + ListBase *tree, + const float y) +{ + LISTBASE_FOREACH (TreeElement *, te, tree) { + if (y > te->ys && y < te->ys + UI_UNIT_Y) { + TreeStoreElem *tselem = TREESTORE(te); + + if ((tselem->type == TSE_SOME_ID) && tselem->id) { + RNA_enum_set(op->ptr, "id_type", GS(tselem->id->name)); + RNA_enum_set_identifier(C, op->ptr, "new_id", tselem->id->name + 2); + RNA_enum_set_identifier(C, op->ptr, "old_id", tselem->id->name + 2); + return true; + } + } + if (outliner_id_remap_find_tree_element(C, op, &te->subtree, y)) { + return true; + } + } + return false; +} + +static int outliner_id_remap_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); + ARegion *region = CTX_wm_region(C); + float fmval[2]; + + if (!RNA_property_is_set(op->ptr, RNA_struct_find_property(op->ptr, "id_type"))) { + UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + outliner_id_remap_find_tree_element(C, op, &space_outliner->tree, fmval[1]); + } + + return WM_operator_props_dialog_popup(C, op, 400); +} + +static const EnumPropertyItem *outliner_id_itemf(bContext *C, + PointerRNA *ptr, + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + if (C == nullptr) { + return DummyRNA_NULL_items; + } + + EnumPropertyItem item_tmp = {0}, *item = nullptr; + int totitem = 0; + int i = 0; + + short id_type = (short)RNA_enum_get(ptr, "id_type"); + ID *id = reinterpret_cast<ID *>(which_libbase(CTX_data_main(C), id_type)->first); + + for (; id; id = reinterpret_cast<ID *>(id->next)) { + item_tmp.identifier = item_tmp.name = id->name + 2; + item_tmp.value = i++; + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} + +void OUTLINER_OT_id_remap(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Outliner ID Data Remap"; + ot->idname = "OUTLINER_OT_id_remap"; + + /* callbacks */ + ot->invoke = outliner_id_remap_invoke; + ot->exec = outliner_id_remap_exec; + ot->poll = ED_operator_outliner_active; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + prop = RNA_def_enum(ot->srna, "id_type", rna_enum_id_type_items, ID_OB, "ID Type", ""); + RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID); + /* Changing ID type wont make sense, would return early with "Invalid old/new ID pair" anyways. + */ + RNA_def_property_flag(prop, PROP_HIDDEN); + + prop = RNA_def_enum(ot->srna, "old_id", DummyRNA_NULL_items, 0, "Old ID", "Old ID to replace"); + RNA_def_property_enum_funcs_runtime(prop, nullptr, nullptr, outliner_id_itemf); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_ENUM_NO_TRANSLATE | PROP_HIDDEN)); + + ot->prop = RNA_def_enum(ot->srna, + "new_id", + DummyRNA_NULL_items, + 0, + "New ID", + "New ID to remap all selected IDs' users to"); + RNA_def_property_enum_funcs_runtime(ot->prop, nullptr, nullptr, outliner_id_itemf); + RNA_def_property_flag(ot->prop, PROP_ENUM_NO_TRANSLATE); +} + +void id_remap_fn(bContext *C, + ReportList *UNUSED(reports), + Scene *UNUSED(scene), + TreeElement *UNUSED(te), + TreeStoreElem *UNUSED(tsep), + TreeStoreElem *tselem, + void *UNUSED(user_data)) +{ + wmOperatorType *ot = WM_operatortype_find("OUTLINER_OT_id_remap", false); + PointerRNA op_props; + + BLI_assert(tselem->id != nullptr); + + WM_operator_properties_create_ptr(&op_props, ot); + + RNA_enum_set(&op_props, "id_type", GS(tselem->id->name)); + RNA_enum_set_identifier(C, &op_props, "old_id", tselem->id->name + 2); + + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_props, nullptr); + + WM_operator_properties_free(&op_props); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name ID Copy Operator * \{ */ diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index 2fdf327cbee..f3bcb7b0f1e 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -442,6 +442,13 @@ void id_delete_fn(struct bContext *C, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); +void id_remap_fn(struct bContext *C, + struct ReportList *reports, + struct Scene *scene, + struct TreeElement *te, + struct TreeStoreElem *tsep, + struct TreeStoreElem *tselem, + void *user_data); /** * To retrieve coordinates with redrawing the entire tree. @@ -516,6 +523,7 @@ void OUTLINER_OT_scene_operation(struct wmOperatorType *ot); void OUTLINER_OT_object_operation(struct wmOperatorType *ot); void OUTLINER_OT_lib_operation(struct wmOperatorType *ot); void OUTLINER_OT_id_operation(struct wmOperatorType *ot); +void OUTLINER_OT_id_remap(struct wmOperatorType *ot); void OUTLINER_OT_id_copy(struct wmOperatorType *ot); void OUTLINER_OT_id_paste(struct wmOperatorType *ot); void OUTLINER_OT_data_operation(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_outliner/outliner_ops.cc b/source/blender/editors/space_outliner/outliner_ops.cc index e053a94c572..8baac45666e 100644 --- a/source/blender/editors/space_outliner/outliner_ops.cc +++ b/source/blender/editors/space_outliner/outliner_ops.cc @@ -31,6 +31,7 @@ void outliner_operatortypes(void) WM_operatortype_append(OUTLINER_OT_lib_relocate); WM_operatortype_append(OUTLINER_OT_id_operation); WM_operatortype_append(OUTLINER_OT_id_delete); + WM_operatortype_append(OUTLINER_OT_id_remap); WM_operatortype_append(OUTLINER_OT_id_copy); WM_operatortype_append(OUTLINER_OT_id_paste); WM_operatortype_append(OUTLINER_OT_data_operation); diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index cb1f2bd7204..cc81d5ed68d 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -1700,6 +1700,7 @@ enum { OL_OP_SELECT = 1, OL_OP_DESELECT, OL_OP_SELECT_HIERARCHY, + OL_OP_REMAP, OL_OP_RENAME, }; @@ -1707,6 +1708,11 @@ static const EnumPropertyItem prop_object_op_types[] = { {OL_OP_SELECT, "SELECT", ICON_RESTRICT_SELECT_OFF, "Select", ""}, {OL_OP_DESELECT, "DESELECT", 0, "Deselect", ""}, {OL_OP_SELECT_HIERARCHY, "SELECT_HIERARCHY", 0, "Select Hierarchy", ""}, + {OL_OP_REMAP, + "REMAP", + 0, + "Remap Users", + "Make all users of selected data-blocks to use instead a new chosen one"}, {OL_OP_RENAME, "RENAME", 0, "Rename", ""}, {0, nullptr, 0, nullptr, nullptr}, }; @@ -1768,6 +1774,12 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) str = "Deselect Objects"; selection_changed = true; break; + case OL_OP_REMAP: + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, &space_outliner->tree, id_remap_fn, nullptr); + /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth + * trick does not work here). */ + break; case OL_OP_RENAME: outliner_do_object_operation( C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn); @@ -1976,6 +1988,7 @@ enum eOutlinerIdOpTypes { OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE, OUTLINER_IDOP_SINGLE, OUTLINER_IDOP_DELETE, + OUTLINER_IDOP_REMAP, OUTLINER_IDOP_COPY, OUTLINER_IDOP_PASTE, @@ -1993,6 +2006,11 @@ static const EnumPropertyItem prop_id_op_types[] = { {OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""}, {OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""}, {OUTLINER_IDOP_DELETE, "DELETE", ICON_X, "Delete", ""}, + {OUTLINER_IDOP_REMAP, + "REMAP", + 0, + "Remap Users", + "Make all users of selected data-blocks to use instead current (clicked) one"}, {0, "", 0, nullptr, nullptr}, {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE, "OVERRIDE_LIBRARY_CREATE", @@ -2411,6 +2429,15 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } break; } + case OUTLINER_IDOP_REMAP: { + if (idlevel > 0) { + outliner_do_libdata_operation( + C, op->reports, scene, space_outliner, &space_outliner->tree, id_remap_fn, nullptr); + /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth + * trick does not work here). */ + } + break; + } case OUTLINER_IDOP_COPY: { wm->op_undo_depth++; WM_operator_name_call(C, "OUTLINER_OT_id_copy", WM_OP_INVOKE_DEFAULT, nullptr, nullptr); |