Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/space_outliner/outliner_edit.c
| Show First 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
| #include "BLI_path_util.h" | #include "BLI_path_util.h" | ||||
| #include "BLI_mempool.h" | #include "BLI_mempool.h" | ||||
| #include "BLI_stack.h" | #include "BLI_stack.h" | ||||
| #include "BLI_string.h" | #include "BLI_string.h" | ||||
| #include "BLT_translation.h" | #include "BLT_translation.h" | ||||
| #include "BKE_animsys.h" | #include "BKE_animsys.h" | ||||
| #include "BKE_collection.h" | |||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_idcode.h" | #include "BKE_idcode.h" | ||||
| #include "BKE_layer.h" | #include "BKE_layer.h" | ||||
| #include "BKE_library.h" | #include "BKE_library.h" | ||||
| #include "BKE_library_query.h" | #include "BKE_library_query.h" | ||||
| #include "BKE_library_remap.h" | #include "BKE_library_remap.h" | ||||
| #include "BKE_main.h" | #include "BKE_main.h" | ||||
| #include "BKE_outliner_treehash.h" | #include "BKE_outliner_treehash.h" | ||||
| #include "BKE_report.h" | #include "BKE_report.h" | ||||
| #include "BKE_scene.h" | #include "BKE_scene.h" | ||||
| #include "BKE_material.h" | #include "BKE_material.h" | ||||
| #include "BKE_group.h" | |||||
| #include "DEG_depsgraph_build.h" | #include "DEG_depsgraph_build.h" | ||||
| #include "../blenloader/BLO_readfile.h" | #include "../blenloader/BLO_readfile.h" | ||||
| #include "ED_object.h" | #include "ED_object.h" | ||||
| #include "ED_outliner.h" | #include "ED_outliner.h" | ||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| ▲ Show 20 Lines • Show All 176 Lines • ▼ Show 20 Lines | void OUTLINER_OT_item_openclose(wmOperatorType *ot) | ||||
| ot->poll = ED_operator_outliner_active; | ot->poll = ED_operator_outliner_active; | ||||
| RNA_def_boolean(ot->srna, "all", 1, "All", "Close or open all items"); | RNA_def_boolean(ot->srna, "all", 1, "All", "Close or open all items"); | ||||
| } | } | ||||
| /* Rename --------------------------------------------------- */ | /* Rename --------------------------------------------------- */ | ||||
| static void do_item_rename(const Scene *scene, ARegion *ar, TreeElement *te, TreeStoreElem *tselem, | static void do_item_rename(ARegion *ar, TreeElement *te, TreeStoreElem *tselem, | ||||
| ReportList *reports) | ReportList *reports) | ||||
| { | { | ||||
| bool add_textbut = false; | bool add_textbut = false; | ||||
| /* can't rename rna datablocks entries or listbases */ | /* can't rename rna datablocks entries or listbases */ | ||||
| if (ELEM(tselem->type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE, TSE_SCENE_OBJECTS_BASE)) { | if (ELEM(tselem->type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE, TSE_SCENE_OBJECTS_BASE)) { | ||||
| /* do nothing */; | /* do nothing */; | ||||
| } | } | ||||
| else if (ELEM(tselem->type, TSE_ANIM_DATA, TSE_NLA, TSE_DEFGROUP_BASE, TSE_CONSTRAINT_BASE, TSE_MODIFIER_BASE, | else if (ELEM(tselem->type, TSE_ANIM_DATA, TSE_NLA, TSE_DEFGROUP_BASE, TSE_CONSTRAINT_BASE, TSE_MODIFIER_BASE, | ||||
| TSE_DRIVER_BASE, TSE_POSE_BASE, TSE_POSEGRP_BASE, TSE_R_LAYER_BASE)) | TSE_DRIVER_BASE, TSE_POSE_BASE, TSE_POSEGRP_BASE, TSE_R_LAYER_BASE, TSE_SCENE_COLLECTION_BASE)) | ||||
| { | { | ||||
| BKE_report(reports, RPT_WARNING, "Cannot edit builtin name"); | BKE_report(reports, RPT_WARNING, "Cannot edit builtin name"); | ||||
| } | } | ||||
| else if (ELEM(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) { | else if (ELEM(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) { | ||||
| BKE_report(reports, RPT_WARNING, "Cannot edit sequence name"); | BKE_report(reports, RPT_WARNING, "Cannot edit sequence name"); | ||||
| } | } | ||||
| else if (ELEM(tselem->type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION)) { | else if (outliner_is_collection_tree_element(te)) { | ||||
| SceneCollection *master = BKE_collection_master(&scene->id); | Collection *collection = outliner_collection_from_tree_element(te); | ||||
| if ((tselem->type == TSE_SCENE_COLLECTION && te->directdata == master) || | if (collection->flag & COLLECTION_IS_MASTER) { | ||||
| (((LayerCollection *)te->directdata)->scene_collection == master)) | |||||
| { | |||||
| BKE_report(reports, RPT_WARNING, "Cannot edit name of master collection"); | BKE_report(reports, RPT_WARNING, "Cannot edit name of master collection"); | ||||
| } | } | ||||
| else { | else { | ||||
| add_textbut = true; | add_textbut = true; | ||||
| } | } | ||||
| } | } | ||||
| else if (ID_IS_LINKED(tselem->id)) { | else if (ID_IS_LINKED(tselem->id)) { | ||||
| BKE_report(reports, RPT_WARNING, "Cannot edit external libdata"); | BKE_report(reports, RPT_WARNING, "Cannot edit external libdata"); | ||||
| } | } | ||||
| else if (te->idcode == ID_LI && ((Library *)tselem->id)->parent) { | else if (te->idcode == ID_LI && ((Library *)tselem->id)->parent) { | ||||
| BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library"); | BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library"); | ||||
| } | } | ||||
| else { | else { | ||||
| add_textbut = true; | add_textbut = true; | ||||
| } | } | ||||
| if (add_textbut) { | if (add_textbut) { | ||||
| tselem->flag |= TSE_TEXTBUT; | tselem->flag |= TSE_TEXTBUT; | ||||
| ED_region_tag_redraw(ar); | ED_region_tag_redraw(ar); | ||||
| } | } | ||||
| } | } | ||||
| void item_rename_cb( | void item_rename_cb( | ||||
| bContext *C, ReportList *reports, Scene *scene, TreeElement *te, | bContext *C, ReportList *reports, Scene *UNUSED(scene), TreeElement *te, | ||||
| TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) | TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data)) | ||||
| { | { | ||||
| ARegion *ar = CTX_wm_region(C); | ARegion *ar = CTX_wm_region(C); | ||||
| do_item_rename(scene, ar, te, tselem, reports); | do_item_rename(ar, te, tselem, reports); | ||||
| } | } | ||||
| static int do_outliner_item_rename(const Scene *scene, ReportList *reports, ARegion *ar, TreeElement *te, | static int do_outliner_item_rename(ReportList *reports, ARegion *ar, TreeElement *te, | ||||
| const float mval[2]) | const float mval[2]) | ||||
| { | { | ||||
| if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) { | if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) { | ||||
| TreeStoreElem *tselem = TREESTORE(te); | TreeStoreElem *tselem = TREESTORE(te); | ||||
| /* click on name */ | /* click on name */ | ||||
| if (mval[0] > te->xs + UI_UNIT_X * 2 && mval[0] < te->xend) { | if (mval[0] > te->xs + UI_UNIT_X * 2 && mval[0] < te->xend) { | ||||
| do_item_rename(scene, ar, te, tselem, reports); | do_item_rename(ar, te, tselem, reports); | ||||
| return 1; | return 1; | ||||
| } | } | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| for (te = te->subtree.first; te; te = te->next) { | for (te = te->subtree.first; te; te = te->next) { | ||||
| if (do_outliner_item_rename(scene, reports, ar, te, mval)) return 1; | if (do_outliner_item_rename(reports, ar, te, mval)) return 1; | ||||
| } | } | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static int outliner_item_rename(bContext *C, wmOperator *op, const wmEvent *event) | static int outliner_item_rename(bContext *C, wmOperator *op, const wmEvent *event) | ||||
| { | { | ||||
| ARegion *ar = CTX_wm_region(C); | ARegion *ar = CTX_wm_region(C); | ||||
| SpaceOops *soops = CTX_wm_space_outliner(C); | SpaceOops *soops = CTX_wm_space_outliner(C); | ||||
| TreeElement *te; | TreeElement *te; | ||||
| float fmval[2]; | float fmval[2]; | ||||
| bool changed = false; | bool changed = false; | ||||
| UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); | 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) { | for (te = soops->tree.first; te; te = te->next) { | ||||
| if (do_outliner_item_rename(CTX_data_scene(C), op->reports, ar, te, fmval)) { | if (do_outliner_item_rename(op->reports, ar, te, fmval)) { | ||||
| changed = true; | changed = true; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| return changed ? OPERATOR_FINISHED : OPERATOR_PASS_THROUGH; | return changed ? OPERATOR_FINISHED : OPERATOR_PASS_THROUGH; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 1,560 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| Object *par = NULL; | Object *par = NULL; | ||||
| Object *ob = NULL; | Object *ob = NULL; | ||||
| SpaceOops *soops = CTX_wm_space_outliner(C); | SpaceOops *soops = CTX_wm_space_outliner(C); | ||||
| ARegion *ar = CTX_wm_region(C); | ARegion *ar = CTX_wm_region(C); | ||||
| Main *bmain = CTX_data_main(C); | Main *bmain = CTX_data_main(C); | ||||
| Scene *scene = NULL; | Scene *scene = NULL; | ||||
| TreeElement *te = NULL; | TreeElement *te = NULL; | ||||
| TreeStoreElem *tselem; | |||||
| char childname[MAX_ID_NAME]; | char childname[MAX_ID_NAME]; | ||||
| char parname[MAX_ID_NAME]; | char parname[MAX_ID_NAME]; | ||||
| int partype = 0; | int partype = 0; | ||||
| float fmval[2]; | float fmval[2]; | ||||
| UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); | UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); | ||||
| /* Find object hovered over */ | /* Find object hovered over */ | ||||
| te = outliner_dropzone_find(soops, fmval, true); | te = outliner_dropzone_find(soops, fmval, true); | ||||
| tselem = te ? TREESTORE(te) : NULL; | |||||
| if (tselem && ELEM(tselem->type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION)) { | |||||
| SceneCollection *sc = outliner_scene_collection_from_tree_element(te); | |||||
| scene = BKE_scene_find_from_collection(bmain, sc); | |||||
| BLI_assert(scene); | |||||
| RNA_string_get(op->ptr, "child", childname); | |||||
| ob = (Object *)BKE_libblock_find_name(ID_OB, childname); | |||||
| BKE_collection_object_add(&scene->id, sc, ob); | |||||
| DEG_relations_tag_update(bmain); | if (te) { | ||||
| WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene); | |||||
| } | |||||
| else if (te) { | |||||
| RNA_string_set(op->ptr, "parent", te->name); | RNA_string_set(op->ptr, "parent", te->name); | ||||
| /* Identify parent and child */ | /* Identify parent and child */ | ||||
| RNA_string_get(op->ptr, "child", childname); | RNA_string_get(op->ptr, "child", childname); | ||||
| ob = (Object *)BKE_libblock_find_name(ID_OB, childname); | ob = (Object *)BKE_libblock_find_name(ID_OB, childname); | ||||
| RNA_string_get(op->ptr, "parent", parname); | RNA_string_get(op->ptr, "parent", parname); | ||||
| par = (Object *)BKE_libblock_find_name(ID_OB, parname); | par = (Object *)BKE_libblock_find_name(ID_OB, parname); | ||||
| if (ELEM(NULL, ob, par)) { | if (ELEM(NULL, ob, par)) { | ||||
| ▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | void OUTLINER_OT_parent_drop(wmOperatorType *ot) | ||||
| RNA_def_enum(ot->srna, "type", prop_make_parent_types, 0, "Type", ""); | RNA_def_enum(ot->srna, "type", prop_make_parent_types, 0, "Type", ""); | ||||
| } | } | ||||
| static int outliner_parenting_poll(bContext *C) | static int outliner_parenting_poll(bContext *C) | ||||
| { | { | ||||
| SpaceOops *soops = CTX_wm_space_outliner(C); | SpaceOops *soops = CTX_wm_space_outliner(C); | ||||
| if (soops) { | if (soops) { | ||||
| if (soops->outlinevis == SO_SCENES) { | if (ELEM(soops->outlinevis, SO_SCENES, SO_OBJECTS)) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (soops->outlinevis == SO_COLLECTIONS) { | |||||
| return (soops->filter & SO_FILTER_NO_COLLECTION); | |||||
| } | |||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| static int parent_clear_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) | static int parent_clear_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) | ||||
| { | { | ||||
| Main *bmain = CTX_data_main(C); | Main *bmain = CTX_data_main(C); | ||||
| ▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | if (te) { | ||||
| if (ELEM(NULL, ob, scene) || ID_IS_LINKED(scene)) { | if (ELEM(NULL, ob, scene) || ID_IS_LINKED(scene)) { | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| if (BKE_scene_has_object(scene, ob)) { | if (BKE_scene_has_object(scene, ob)) { | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| SceneCollection *sc; | Collection *collection; | ||||
| if (scene != CTX_data_scene(C)) { | if (scene != CTX_data_scene(C)) { | ||||
| /* when linking to an inactive scene link to the master collection */ | /* when linking to an inactive scene link to the master collection */ | ||||
| sc = BKE_collection_master(&scene->id); | collection = BKE_collection_master(scene); | ||||
| } | } | ||||
| else { | else { | ||||
| sc = CTX_data_scene_collection(C); | collection = CTX_data_collection(C); | ||||
| } | } | ||||
| BKE_collection_object_add(&scene->id, sc, ob); | BKE_collection_object_add(bmain, collection, ob); | ||||
| for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) { | for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) { | ||||
| Base *base = BKE_view_layer_base_find(view_layer, ob); | Base *base = BKE_view_layer_base_find(view_layer, ob); | ||||
| if (base) { | if (base) { | ||||
| ED_object_base_select(base, BA_SELECT); | ED_object_base_select(base, BA_SELECT); | ||||
| } | } | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | void OUTLINER_OT_material_drop(wmOperatorType *ot) | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; | ||||
| /* properties */ | /* properties */ | ||||
| RNA_def_string(ot->srna, "object", "Object", MAX_ID_NAME, "Object", "Target Object"); | RNA_def_string(ot->srna, "object", "Object", MAX_ID_NAME, "Object", "Target Object"); | ||||
| RNA_def_string(ot->srna, "material", "Material", MAX_ID_NAME, "Material", "Target Material"); | RNA_def_string(ot->srna, "material", "Material", MAX_ID_NAME, "Material", "Target Material"); | ||||
| } | } | ||||
| static int group_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) | /* ******************** Collection Drop Operator *********************** */ | ||||
| static int collection_drop_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) | |||||
| { | { | ||||
| /* TODO: implement */ | |||||
| #if 0 | |||||
| Object *par = NULL, *ob = NULL; | |||||
| Main *bmain = CTX_data_main(C); | Main *bmain = CTX_data_main(C); | ||||
| Group *group = NULL; | Scene *scene = CTX_data_scene(C); | ||||
| Object *ob = NULL; | int partype = -1; | ||||
| char parname[MAX_ID_NAME], childname[MAX_ID_NAME]; | |||||
| RNA_string_get(op->ptr, "parent", parname); | |||||
| par = (Object *)BKE_libblock_find_name(ID_OB, parname); | |||||
| RNA_string_get(op->ptr, "child", childname); | |||||
| ob = (Object *)BKE_libblock_find_name(ID_OB, childname); | |||||
| if (ID_IS_LINKED(ob)) { | |||||
| BKE_report(op->reports, RPT_INFO, "Can't edit library linked object"); | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| ED_object_parent_set(op->reports, C, scene, ob, par, partype, false, false, NULL); | |||||
| DEG_relations_tag_update(bmain); | |||||
| WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); | |||||
| WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL); | |||||
| #endif | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| static int collection_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) | |||||
| { | |||||
| SpaceOops *soops = CTX_wm_space_outliner(C); | SpaceOops *soops = CTX_wm_space_outliner(C); | ||||
| ARegion *ar = CTX_wm_region(C); | ARegion *ar = CTX_wm_region(C); | ||||
| TreeElement *te = NULL; | Main *bmain = CTX_data_main(C); | ||||
| char ob_name[MAX_ID_NAME - 2]; | char childname[MAX_ID_NAME]; | ||||
| float fmval[2]; | float fmval[2]; | ||||
| UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); | UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); | ||||
| /* Find object hovered over */ | /* Find object hovered over */ | ||||
| te = outliner_dropzone_find(soops, fmval, true); | TreeElement *te = outliner_dropzone_find(soops, fmval, true); | ||||
| if (te) { | |||||
| group = (Group *)BKE_libblock_find_name(ID_GR, te->name); | |||||
| RNA_string_get(op->ptr, "object", ob_name); | if (!te || !outliner_is_collection_tree_element(te)) { | ||||
| ob = (Object *)BKE_libblock_find_name(ID_OB, ob_name); | |||||
| if (ELEM(NULL, group, ob)) { | |||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||
| } | } | ||||
| if (BKE_group_object_exists(group, ob)) { | |||||
| return OPERATOR_FINISHED; | |||||
| } | |||||
| if (BKE_group_object_cyclic_check(bmain, ob, group)) { | Collection *collection = outliner_collection_from_tree_element(te); | ||||
| BKE_report(op->reports, RPT_ERROR, "Could not add the group because of dependency cycle detected"); | |||||
| return OPERATOR_CANCELLED; | |||||
| } | |||||
| BKE_group_object_add(group, ob); | // TODO: don't use scene, makes no sense anymore | ||||
| WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); | // TODO: move rather than link, change hover text | ||||
| Scene *scene = BKE_scene_find_from_collection(bmain, collection); | |||||
| BLI_assert(scene); | |||||
| RNA_string_get(op->ptr, "child", childname); | |||||
| Object *ob = (Object *)BKE_libblock_find_name(ID_OB, childname); | |||||
| BKE_collection_object_add(bmain, collection, ob); | |||||
| return OPERATOR_FINISHED; | DEG_relations_tag_update(bmain); | ||||
| } | WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene); | ||||
| return OPERATOR_CANCELLED; | return OPERATOR_FINISHED; | ||||
| } | } | ||||
| void OUTLINER_OT_group_link(wmOperatorType *ot) | void OUTLINER_OT_collection_drop(wmOperatorType *ot) | ||||
| { | { | ||||
| /* identifiers */ | /* identifiers */ | ||||
| ot->name = "Link Object to Group"; | ot->name = "Link to Collection"; // TODO: rename to move? | ||||
| ot->description = "Link Object to Group in Outliner"; | ot->description = "Drag to move to collection in Outliner"; | ||||
| ot->idname = "OUTLINER_OT_group_link"; | ot->idname = "OUTLINER_OT_collection_drop"; | ||||
| /* api callbacks */ | /* api callbacks */ | ||||
| ot->invoke = group_link_invoke; | ot->invoke = collection_drop_invoke; | ||||
| ot->exec = collection_drop_exec; | |||||
| ot->poll = ED_operator_outliner_active; | ot->poll = ED_operator_outliner_active; | ||||
| /* flags */ | /* flags */ | ||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; | ||||
| /* properties */ | /* properties */ | ||||
| RNA_def_string(ot->srna, "object", "Object", MAX_ID_NAME, "Object", "Target Object"); | RNA_def_string(ot->srna, "child", "Object", MAX_ID_NAME, "Child", "Child Object"); | ||||
| RNA_def_string(ot->srna, "parent", "Collection", MAX_ID_NAME, "Parent", "Parent Collection"); | |||||
| } | } | ||||