From 7547c6a250cd6f36c9894605b822380a1261febf Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 22 Jun 2016 18:05:55 +0200 Subject: ID-Remap, step two: add some user-level tools. This commit adds operators and Outliner menu entries to reload or relocate a library, and to delete or replace a datablock. RNA ID API is also extended to allow ID deletion and remapping from python. Review task: D2027 (https://developer.blender.org/D2027). Reviewed by campbellbarton, thanks a bunch. --- .../blender/editors/space_outliner/outliner_edit.c | 368 +++++++++++++++++++-- .../editors/space_outliner/outliner_intern.h | 15 +- .../blender/editors/space_outliner/outliner_ops.c | 4 +- .../editors/space_outliner/outliner_tools.c | 71 +++- 4 files changed, 405 insertions(+), 53 deletions(-) (limited to 'source/blender/editors/space_outliner') diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 8cee696b2ac..687869ae727 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -29,17 +29,23 @@ * \ingroup spoutliner */ +#include + #include "MEM_guardedalloc.h" #include "DNA_anim_types.h" #include "DNA_group_types.h" +#include "DNA_ID.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" #include "DNA_material_types.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_path_util.h" #include "BLI_mempool.h" +#include "BLI_stack.h" +#include "BLI_string.h" #include "BLT_translation.h" @@ -47,7 +53,9 @@ #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_global.h" +#include "BKE_idcode.h" #include "BKE_library.h" +#include "BKE_library_remap.h" #include "BKE_main.h" #include "BKE_outliner_treehash.h" #include "BKE_report.h" @@ -55,6 +63,8 @@ #include "BKE_material.h" #include "BKE_group.h" +#include "../blenloader/BLO_readfile.h" + #include "ED_object.h" #include "ED_outliner.h" #include "ED_screen.h" @@ -70,6 +80,9 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "GPU_material.h" #include "outliner_intern.h" @@ -291,64 +304,294 @@ void OUTLINER_OT_item_rename(wmOperatorType *ot) ot->poll = ED_operator_outliner_active; } -/* Library delete --------------------------------------------------- */ +/* ID delete --------------------------------------------------- */ -static void lib_delete(bContext *C, TreeElement *te, TreeStoreElem *tselem, ReportList *reports) +static void id_delete(bContext *C, TreeElement *te, TreeStoreElem *tselem) { - Library *lib = (Library *)tselem->id; - ListBase *lbarray[MAX_LIBARRAY]; - int a; + Main *bmain = CTX_data_main(C); + ID *id = tselem->id; - BLI_assert(te->idcode == ID_LI && lib != NULL); + BLI_assert(te->idcode != 0 && id != NULL); + BLI_assert(te->idcode != ID_LI || ((Library *)id)->parent == NULL); UNUSED_VARS_NDEBUG(te); - /* We simply set all ID from given lib (including lib itself) to zero user count. - * It is not possible to do a proper cleanup without a save/reload in current master. */ - a = set_listbasepointers(CTX_data_main(C), lbarray); - while (a--) { - ListBase *lb = lbarray[a]; - ID *id; - - for (id = lb->first; id; id = id->next) { - if (id->lib == lib) { - id_fake_user_clear(id); - id->us = 0; + BKE_libblock_delete(bmain, id); + + WM_event_add_notifier(C, NC_WINDOW, NULL); +} + +void id_delete_cb( + bContext *C, Scene *UNUSED(scene), TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, + void *UNUSED(user_data)) +{ + id_delete(C, te, tselem); +} + +static int outliner_id_delete_invoke_do(bContext *C, ReportList *reports, TreeElement *te, const float mval[2]) +{ + if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) { + TreeStoreElem *tselem = TREESTORE(te); + + if (te->idcode != 0 && tselem->id) { + if (te->idcode == ID_LI && ((Library *)tselem->id)->parent) { + BKE_reportf(reports, RPT_ERROR_INVALID_INPUT, + "Cannot delete indirectly linked library '%s'", ((Library *)tselem->id)->filepath); + return OPERATOR_CANCELLED; + } + id_delete(C, te, tselem); + return OPERATOR_FINISHED; + } + } + else { + for (te = te->subtree.first; te; te = te->next) { + int ret; + if ((ret = outliner_id_delete_invoke_do(C, reports, te, mval))) { + return ret; } } } - BKE_reportf(reports, RPT_WARNING, - "Please save and reload .blend file to complete deletion of '%s' library", - ((Library *)tselem->id)->filepath); + return 0; } -void lib_delete_cb( - bContext *C, Scene *UNUSED(scene), TreeElement *te, +static int outliner_id_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + TreeElement *te; + float fmval[2]; + + BLI_assert(ar && soops); + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + for (te = soops->tree.first; te; te = te->next) { + int ret; + + if ((ret = outliner_id_delete_invoke_do(C, op->reports, te, fmval))) { + return ret; + } + } + + return OPERATOR_CANCELLED; +} + +void OUTLINER_OT_id_delete(wmOperatorType *ot) +{ + ot->name = "Delete Datablock"; + ot->idname = "OUTLINER_OT_id_delete"; + ot->description = "Delete the ID under cursor"; + + ot->invoke = outliner_id_delete_invoke; + ot->poll = ED_operator_outliner_active; +} + +/* ID remap --------------------------------------------------- */ + +static int outliner_id_remap_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + + const short id_type = (short)RNA_enum_get(op->ptr, "id_type"); + ID *old_id = BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "old_id")); + ID *new_id = BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "new_id")); + + /* check for invalid states */ + if (soops == NULL) + return OPERATOR_CANCELLED; + + if (!(old_id && (old_id != new_id) && (GS(old_id->name) == GS(new_id->name)))) { + return OPERATOR_CANCELLED; + } + + 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 */ + DAG_scene_relations_rebuild(bmain, scene); + + /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */ + GPU_materials_free(); + + WM_event_add_notifier(C, NC_WINDOW, NULL); + + return OPERATOR_FINISHED; +} + +static bool outliner_id_remap_find_tree_element(bContext *C, wmOperator *op, ListBase *tree, const float y) +{ + TreeElement *te; + + for (te = tree->first; te; te = te->next) { + if (y > te->ys && y < te->ys + UI_UNIT_Y) { + TreeStoreElem *tselem = TREESTORE(te); + + if (tselem->type == 0 && tselem->id) { + printf("found id %s (%p)!\n", tselem->id->name, 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) +{ + SpaceOops *soops = CTX_wm_space_outliner(C); + ARegion *ar = 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(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + outliner_id_remap_find_tree_element(C, op, &soops->tree, fmval[1]); + } + + return WM_operator_props_dialog_popup(C, op, 200, 100); +} + +static EnumPropertyItem *outliner_id_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) +{ + EnumPropertyItem item_tmp = {0}, *item = NULL; + int totitem = 0; + int i = 0; + + short id_type = (short)RNA_enum_get(ptr, "id_type"); + ID *id = which_libbase(CTX_data_main(C), id_type)->first; + + for (; id; 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"; + ot->description = ""; + + /* callbacks */ + ot->invoke = outliner_id_remap_invoke; + ot->exec = outliner_id_remap_exec; + ot->poll = ED_operator_outliner_active; + + ot->flag = 0; + + RNA_def_enum(ot->srna, "id_type", rna_enum_id_type_items, ID_OB, "ID Type", ""); + + 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, NULL, NULL, outliner_id_itemf); + RNA_def_property_flag(prop, 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, NULL, NULL, outliner_id_itemf); + RNA_def_property_flag(ot->prop, PROP_ENUM_NO_TRANSLATE); +} + +void id_remap_cb( + bContext *C, Scene *UNUSED(scene), TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) { - lib_delete(C, te, tselem, CTX_wm_reports(C)); + wmOperatorType *ot = WM_operatortype_find("OUTLINER_OT_id_remap", false); + PointerRNA op_props; + + BLI_assert(tselem->id != NULL); + + 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); + + WM_operator_properties_free(&op_props); } -static int outliner_lib_delete_invoke_do(bContext *C, ReportList *reports, TreeElement *te, const float mval[2]) +/* Library relocate/reload --------------------------------------------------- */ + +static int lib_relocate( + bContext *C, TreeElement *te, TreeStoreElem *tselem, wmOperatorType *ot, const bool reload) +{ + PointerRNA op_props; + int ret = 0; + + BLI_assert(te->idcode == ID_LI && tselem->id != NULL); + UNUSED_VARS_NDEBUG(te); + + WM_operator_properties_create_ptr(&op_props, ot); + + RNA_string_set(&op_props, "library", tselem->id->name + 2); + + if (reload) { + Library *lib = (Library *)tselem->id; + char dir[FILE_MAXDIR], filename[FILE_MAX]; + + BLI_split_dirfile(lib->filepath, dir, filename, sizeof(dir), sizeof(filename)); + + printf("%s, %s\n", tselem->id->name, lib->filepath); + + /* We assume if both paths in lib are not the same then lib->name was relative... */ + RNA_boolean_set(&op_props, "relative_path", BLI_path_cmp(lib->filepath, lib->name) != 0); + + RNA_string_set(&op_props, "directory", dir); + RNA_string_set(&op_props, "filename", filename); + + ret = WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props); + } + else { + ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_props); + } + + WM_operator_properties_free(&op_props); + + return ret; +} + +static int outliner_lib_relocate_invoke_do( + bContext *C, ReportList *reports, TreeElement *te, const float mval[2], const bool reload) { if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) { TreeStoreElem *tselem = TREESTORE(te); if (te->idcode == ID_LI && tselem->id) { - if (((Library *)tselem->id)->parent) { + if (((Library *)tselem->id)->parent && !reload) { BKE_reportf(reports, RPT_ERROR_INVALID_INPUT, - "Cannot delete indirectly linked library '%s'", ((Library *)tselem->id)->filepath); + "Cannot relocate indirectly linked library '%s'", ((Library *)tselem->id)->filepath); return OPERATOR_CANCELLED; } + else { + wmOperatorType *ot = WM_operatortype_find(reload ? "WM_OT_lib_reload" : "WM_OT_lib_relocate", false); - lib_delete(C, te, tselem, reports); - return OPERATOR_FINISHED; + return lib_relocate(C, te, tselem, ot, reload); + } } } else { for (te = te->subtree.first; te; te = te->next) { int ret; - if ((ret = outliner_lib_delete_invoke_do(C, reports, te, mval))) { + if ((ret = outliner_lib_relocate_invoke_do(C, reports, te, mval, reload))) { return ret; } } @@ -357,7 +600,7 @@ static int outliner_lib_delete_invoke_do(bContext *C, ReportList *reports, TreeE return 0; } -static int outliner_lib_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int outliner_lib_relocate_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *ar = CTX_wm_region(C); SpaceOops *soops = CTX_wm_space_outliner(C); @@ -371,7 +614,7 @@ static int outliner_lib_delete_invoke(bContext *C, wmOperator *op, const wmEvent for (te = soops->tree.first; te; te = te->next) { int ret; - if ((ret = outliner_lib_delete_invoke_do(C, op->reports, te, fmval))) { + if ((ret = outliner_lib_relocate_invoke_do(C, op->reports, te, fmval, false))) { return ret; } } @@ -379,16 +622,69 @@ static int outliner_lib_delete_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } -void OUTLINER_OT_lib_delete(wmOperatorType *ot) +void OUTLINER_OT_lib_relocate(wmOperatorType *ot) { - ot->name = "Delete Library"; - ot->idname = "OUTLINER_OT_lib_delete"; - ot->description = "Delete the library under cursor (needs a save/reload)"; + ot->name = "Relocate Library"; + ot->idname = "OUTLINER_OT_lib_relocate"; + ot->description = "Relocate the library under cursor"; - ot->invoke = outliner_lib_delete_invoke; + ot->invoke = outliner_lib_relocate_invoke; ot->poll = ED_operator_outliner_active; } +/* XXX This does not work with several items + * (it is only called once in the end, due to the 'deffered' filebrowser invocation through event system...). */ +void lib_relocate_cb( + bContext *C, Scene *UNUSED(scene), TreeElement *te, + TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) +{ + wmOperatorType *ot = WM_operatortype_find("WM_OT_lib_relocate", false); + + lib_relocate(C, te, tselem, ot, false); +} + + +static int outliner_lib_reload_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + TreeElement *te; + float fmval[2]; + + BLI_assert(ar && soops); + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + for (te = soops->tree.first; te; te = te->next) { + int ret; + + if ((ret = outliner_lib_relocate_invoke_do(C, op->reports, te, fmval, true))) { + return ret; + } + } + + return OPERATOR_CANCELLED; +} + +void OUTLINER_OT_lib_reload(wmOperatorType *ot) +{ + ot->name = "Reload Library"; + ot->idname = "OUTLINER_OT_lib_reload"; + ot->description = "Reload the library under cursor"; + + ot->invoke = outliner_lib_reload_invoke; + ot->poll = ED_operator_outliner_active; +} + +void lib_reload_cb( + bContext *C, Scene *UNUSED(scene), TreeElement *te, + TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) +{ + wmOperatorType *ot = WM_operatortype_find("WM_OT_lib_reload", false); + + lib_relocate(C, te, tselem, ot, true); +} + /* ************************************************************** */ /* Setting Toggling Operators */ diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 2e46ffa6437..d68815c5e57 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -179,8 +179,17 @@ void group_toggle_selectability_cb(struct bContext *C, struct Scene *scene, Tree void group_toggle_renderability_cb(struct bContext *C, struct Scene *scene, TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); void item_rename_cb(struct bContext *C, struct Scene *scene, TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); +void lib_relocate_cb( + struct bContext *C, struct Scene *scene, struct TreeElement *te, + struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); +void lib_reload_cb( + struct bContext *C, struct Scene *scene, struct TreeElement *te, + struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); -void lib_delete_cb( +void id_delete_cb( + struct bContext *C, struct Scene *scene, struct TreeElement *te, + struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); +void id_remap_cb( struct bContext *C, struct Scene *scene, struct TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); @@ -190,8 +199,10 @@ TreeElement *outliner_dropzone_find(const struct SpaceOops *soops, const float f void OUTLINER_OT_item_activate(struct wmOperatorType *ot); void OUTLINER_OT_item_openclose(struct wmOperatorType *ot); void OUTLINER_OT_item_rename(struct wmOperatorType *ot); +void OUTLINER_OT_lib_relocate(struct wmOperatorType *ot); +void OUTLINER_OT_lib_reload(struct wmOperatorType *ot); -void OUTLINER_OT_lib_delete(struct wmOperatorType *ot); +void OUTLINER_OT_id_delete(struct wmOperatorType *ot); void OUTLINER_OT_show_one_level(struct wmOperatorType *ot); void OUTLINER_OT_show_active(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c index 720cfe12567..776717c8443 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -47,13 +47,15 @@ void outliner_operatortypes(void) WM_operatortype_append(OUTLINER_OT_select_border); WM_operatortype_append(OUTLINER_OT_item_openclose); WM_operatortype_append(OUTLINER_OT_item_rename); - WM_operatortype_append(OUTLINER_OT_lib_delete); WM_operatortype_append(OUTLINER_OT_operation); WM_operatortype_append(OUTLINER_OT_scene_operation); WM_operatortype_append(OUTLINER_OT_object_operation); WM_operatortype_append(OUTLINER_OT_group_operation); WM_operatortype_append(OUTLINER_OT_lib_operation); + 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_data_operation); WM_operatortype_append(OUTLINER_OT_animdata_operation); WM_operatortype_append(OUTLINER_OT_action_set); diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 2a210e382a2..265df4a8def 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -850,6 +850,7 @@ enum { OL_OP_SELECT_HIERARCHY, OL_OP_DELETE, OL_OP_DELETE_HIERARCHY, + OL_OP_REMAP, OL_OP_LOCALIZED, /* disabled, see below */ OL_OP_TOGVIS, OL_OP_TOGSEL, @@ -863,6 +864,8 @@ static EnumPropertyItem prop_object_op_types[] = { {OL_OP_SELECT_HIERARCHY, "SELECT_HIERARCHY", 0, "Select Hierarchy", ""}, {OL_OP_DELETE, "DELETE", 0, "Delete", ""}, {OL_OP_DELETE_HIERARCHY, "DELETE_HIERARCHY", 0, "Delete Hierarchy", ""}, + {OL_OP_REMAP, "REMAP", 0, "Remap Users", + "Make all users of selected datablocks to use instead a new chosen one"}, {OL_OP_TOGVIS, "TOGVIS", 0, "Toggle Visible", ""}, {OL_OP_TOGSEL, "TOGSEL", 0, "Toggle Selectable", ""}, {OL_OP_TOGREN, "TOGREN", 0, "Toggle Renderable", ""}, @@ -932,6 +935,10 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op) str = "Delete Object Hierarchy"; WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); } + else if (event == OL_OP_REMAP) { + outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_remap_cb, NULL); + str = "Remap ID"; + } else if (event == OL_OP_LOCALIZED) { /* disabled, see above enum (ton) */ outliner_do_object_operation(C, scene, soops, &soops->tree, id_local_cb); str = "Localized Objects"; @@ -989,6 +996,8 @@ typedef enum eOutliner_PropGroupOps { OL_GROUPOP_UNLINK = 1, OL_GROUPOP_LOCAL, OL_GROUPOP_LINK, + OL_GROUPOP_DELETE, + OL_GROUPOP_REMAP, OL_GROUPOP_INSTANCE, OL_GROUPOP_TOGVIS, OL_GROUPOP_TOGSEL, @@ -1000,6 +1009,9 @@ static EnumPropertyItem prop_group_op_types[] = { {OL_GROUPOP_UNLINK, "UNLINK", 0, "Unlink Group", ""}, {OL_GROUPOP_LOCAL, "LOCAL", 0, "Make Local Group", ""}, {OL_GROUPOP_LINK, "LINK", 0, "Link Group Objects to Scene", ""}, + {OL_GROUPOP_DELETE, "DELETE", 0, "Delete Group", "WARNING: no undo"}, + {OL_GROUPOP_REMAP, "REMAP", 0, "Remap Users", + "Make all users of selected datablocks to use instead current (clicked) one"}, {OL_GROUPOP_INSTANCE, "INSTANCE", 0, "Instance Groups in Scene", ""}, {OL_GROUPOP_TOGVIS, "TOGVIS", 0, "Toggle Visible Group", ""}, {OL_GROUPOP_TOGSEL, "TOGSEL", 0, "Toggle Selectable", ""}, @@ -1032,6 +1044,14 @@ static int outliner_group_operation_exec(bContext *C, wmOperator *op) break; case OL_GROUPOP_INSTANCE: outliner_do_libdata_operation(C, scene, soops, &soops->tree, group_instance_cb, NULL); + /* works without this except if you try render right after, see: 22027 */ + DAG_relations_tag_update(CTX_data_main(C)); + break; + case OL_GROUPOP_DELETE: + WM_operator_name_call(C, "OUTLINER_OT_id_delete", WM_OP_INVOKE_REGION_WIN, NULL); + break; + case OL_GROUPOP_REMAP: + outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_remap_cb, NULL); break; case OL_GROUPOP_TOGVIS: outliner_do_libdata_operation(C, scene, soops, &soops->tree, group_toggle_visibility_cb, NULL); @@ -1049,11 +1069,6 @@ static int outliner_group_operation_exec(bContext *C, wmOperator *op) BLI_assert(0); } - if (event == 3) { /* instance */ - /* works without this except if you try render right after, see: 22027 */ - DAG_relations_tag_update(CTX_data_main(C)); - } - ED_undo_push(C, prop_group_op_types[event - 1].name); WM_event_add_notifier(C, NC_GROUP, NULL); @@ -1086,6 +1101,8 @@ typedef enum eOutlinerIdOpTypes { OUTLINER_IDOP_UNLINK, OUTLINER_IDOP_LOCAL, OUTLINER_IDOP_SINGLE, + OUTLINER_IDOP_DELETE, + OUTLINER_IDOP_REMAP, OUTLINER_IDOP_FAKE_ADD, OUTLINER_IDOP_FAKE_CLEAR, @@ -1099,6 +1116,9 @@ static EnumPropertyItem prop_id_op_types[] = { {OUTLINER_IDOP_UNLINK, "UNLINK", 0, "Unlink", ""}, {OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""}, {OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""}, + {OUTLINER_IDOP_DELETE, "DELETE", 0, "Delete", "WARNING: no undo"}, + {OUTLINER_IDOP_REMAP, "REMAP", 0, "Remap Users", + "Make all users of selected datablocks to use instead current (clicked) one"}, {OUTLINER_IDOP_FAKE_ADD, "ADD_FAKE", 0, "Add Fake User", "Ensure datablock gets saved even if it isn't in use (e.g. for motion and material libraries)"}, {OUTLINER_IDOP_FAKE_CLEAR, "CLEAR_FAKE", 0, "Clear Fake User", ""}, @@ -1188,6 +1208,20 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } break; } + case OUTLINER_IDOP_DELETE: + { + if (idlevel > 0) { + outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_delete_cb, NULL); + } + break; + } + case OUTLINER_IDOP_REMAP: + { + if (idlevel > 0) { + outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_remap_cb, NULL); + } + break; + } case OUTLINER_IDOP_FAKE_ADD: { /* set fake user */ @@ -1259,13 +1293,16 @@ typedef enum eOutlinerLibOpTypes { OL_LIB_RENAME, OL_LIB_DELETE, + OL_LIB_RELOCATE, + OL_LIB_RELOAD, } eOutlinerLibOpTypes; static EnumPropertyItem outliner_lib_op_type_items[] = { {OL_LIB_RENAME, "RENAME", 0, "Rename", ""}, - {OL_LIB_DELETE, "DELETE", 0, "Delete", "Delete this library and all its item from Blender (needs a save/reload)"}, + {OL_LIB_DELETE, "DELETE", 0, "Delete", "Delete this library and all its item from Blender - WARNING: no undo"}, + {OL_LIB_RELOCATE, "RELOCATE", 0, "Relocate", "Select a new path for this library, and reload all its data"}, + {OL_LIB_RELOAD, "RELOAD", 0, "Reload", "Reload all data from this library"}, {0, NULL, 0, NULL, NULL} - }; static int outliner_lib_operation_exec(bContext *C, wmOperator *op) @@ -1295,13 +1332,19 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op) } case OL_LIB_DELETE: { - /* delete */ - outliner_do_libdata_operation(C, scene, soops, &soops->tree, lib_delete_cb, NULL); - - WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL); - /* Note: no undo possible here really, not 100% sure why... - * Probably because of library optimizations in undo/redo process? */ - /* ED_undo_push(C, "Rename"); */ + outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_delete_cb, NULL); + break; + } + case OL_LIB_RELOCATE: + { + /* rename */ + outliner_do_libdata_operation(C, scene, soops, &soops->tree, lib_relocate_cb, NULL); + break; + } + case OL_LIB_RELOAD: + { + /* rename */ + outliner_do_libdata_operation(C, scene, soops, &soops->tree, lib_reload_cb, NULL); break; } default: -- cgit v1.2.3