diff options
author | Brecht Van Lommel <brechtvanlommel@gmail.com> | 2018-08-07 11:23:07 +0300 |
---|---|---|
committer | Brecht Van Lommel <brechtvanlommel@gmail.com> | 2018-08-10 18:53:00 +0300 |
commit | 367ada71a0d1fab75701c30895726e3f7f315f3a (patch) | |
tree | d9b68427c64ae0f4041018688f0b9352c1689cec /source/blender/editors | |
parent | 71bd30d8c02ab5670902f8955d24a5c127d76108 (diff) |
Cleanup: move most outliner drag & drop code into one file.
Diffstat (limited to 'source/blender/editors')
-rw-r--r-- | source/blender/editors/space_outliner/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_dragdrop.c | 1115 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_edit.c | 533 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_intern.h | 23 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_ops.c | 347 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/space_outliner.c | 190 |
6 files changed, 1131 insertions, 1078 deletions
diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index 96d27c6fd89..796e8732fca 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -39,6 +39,7 @@ set(INC_SYS set(SRC outliner_collections.c + outliner_dragdrop.c outliner_draw.c outliner_edit.c outliner_ops.c diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c new file mode 100644 index 00000000000..f37cd7fa5e4 --- /dev/null +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -0,0 +1,1115 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2004 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_outliner/outliner_dragdrop.c + * \ingroup spoutliner + */ + +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_group_types.h" +#include "DNA_material_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" + +#include "BLI_listbase.h" + +#include "BLT_translation.h" + +#include "BKE_collection.h" +#include "BKE_context.h" +#include "BKE_layer.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_report.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +#include "ED_object.h" +#include "ED_outliner.h" +#include "ED_screen.h" + +#include "UI_interface.h" +#include "UI_resources.h" +#include "UI_view2d.h" + +#include "GPU_state.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "outliner_intern.h" + +/* ******************** Drop Target Find *********************** */ + +static TreeElement *outliner_dropzone_element(TreeElement *te, const float fmval[2], const bool children) +{ + if ((fmval[1] > te->ys) && (fmval[1] < (te->ys + UI_UNIT_Y))) { + /* name and first icon */ + if ((fmval[0] > te->xs + UI_UNIT_X) && (fmval[0] < te->xend)) + return te; + } + /* Not it. Let's look at its children. */ + if (children && (TREESTORE(te)->flag & TSE_CLOSED) == 0 && (te->subtree.first)) { + for (te = te->subtree.first; te; te = te->next) { + TreeElement *te_valid = outliner_dropzone_element(te, fmval, children); + if (te_valid) + return te_valid; + } + } + return NULL; +} + +/* Find tree element to drop into. */ +static TreeElement *outliner_dropzone_find(const SpaceOops *soops, const float fmval[2], const bool children) +{ + TreeElement *te; + + for (te = soops->tree.first; te; te = te->next) { + TreeElement *te_valid = outliner_dropzone_element(te, fmval, children); + if (te_valid) + return te_valid; + } + return NULL; +} + +/* ******************** Parent Drop Operator *********************** */ + +static bool parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + float fmval[2]; + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + if (drag->type == WM_DRAG_ID) { + ID *id = drag->poin; + if (GS(id->name) == ID_OB) { + /* Ensure item under cursor is valid drop target */ + TreeElement *te = outliner_dropzone_find(soops, fmval, true); + TreeStoreElem *tselem = te ? TREESTORE(te) : NULL; + + if (!te) { + /* pass */ + } + else if (te->idcode == ID_OB && tselem->type == 0) { + Scene *scene; + ID *te_id = tselem->id; + + /* check if dropping self or parent */ + if (te_id == id || (Object *)te_id == ((Object *)id)->parent) + return 0; + + /* check that parent/child are both in the same scene */ + scene = (Scene *)outliner_search_back(soops, te, ID_SCE); + + /* currently outliner organized in a way that if there's no parent scene + * element for object it means that all displayed objects belong to + * active scene and parenting them is allowed (sergey) + */ + if (!scene) { + return 1; + } + else { + for (ViewLayer *view_layer = scene->view_layers.first; + view_layer; + view_layer = view_layer->next) + { + if (BKE_view_layer_base_find(view_layer, (Object *)id)) { + return 1; + } + } + } + } + } + } + return 0; +} + +static void parent_drop_copy(wmDrag *drag, wmDropBox *drop) +{ + ID *id = drag->poin; + + RNA_string_set(drop->ptr, "child", id->name + 2); +} + +static int parent_drop_exec(bContext *C, wmOperator *op) +{ + Object *par = NULL, *ob = NULL; + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + int partype = -1; + char parname[MAX_ID_NAME], childname[MAX_ID_NAME]; + + partype = RNA_enum_get(op->ptr, "type"); + RNA_string_get(op->ptr, "parent", parname); + par = (Object *)BKE_libblock_find_name(bmain, ID_OB, parname); + RNA_string_get(op->ptr, "child", childname); + ob = (Object *)BKE_libblock_find_name(bmain, 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); + + return OPERATOR_FINISHED; +} + +static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Object *par = NULL; + Object *ob = NULL; + SpaceOops *soops = CTX_wm_space_outliner(C); + ARegion *ar = CTX_wm_region(C); + Main *bmain = CTX_data_main(C); + Scene *scene = NULL; + TreeElement *te = NULL; + char childname[MAX_ID_NAME]; + char parname[MAX_ID_NAME]; + int partype = 0; + float fmval[2]; + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + /* Find object hovered over */ + te = outliner_dropzone_find(soops, fmval, true); + + if (te) { + RNA_string_set(op->ptr, "parent", te->name); + /* Identify parent and child */ + RNA_string_get(op->ptr, "child", childname); + ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, childname); + RNA_string_get(op->ptr, "parent", parname); + par = (Object *)BKE_libblock_find_name(bmain, ID_OB, parname); + + if (ELEM(NULL, ob, par)) { + if (par == NULL) printf("par==NULL\n"); + return OPERATOR_CANCELLED; + } + if (ob == par) { + return OPERATOR_CANCELLED; + } + if (ID_IS_LINKED(ob)) { + BKE_report(op->reports, RPT_INFO, "Can't edit library linked object"); + return OPERATOR_CANCELLED; + } + + scene = (Scene *)outliner_search_back(soops, te, ID_SCE); + + if (scene == NULL) { + /* currently outlier organized in a way, that if there's no parent scene + * element for object it means that all displayed objects belong to + * active scene and parenting them is allowed (sergey) + */ + + scene = CTX_data_scene(C); + } + + if ((par->type != OB_ARMATURE) && (par->type != OB_CURVE) && (par->type != OB_LATTICE)) { + if (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); + } + } + else { + /* Menu creation */ + wmOperatorType *ot = WM_operatortype_find("OUTLINER_OT_parent_drop", false); + uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Set Parent To"), ICON_NONE); + uiLayout *layout = UI_popup_menu_layout(pup); + PointerRNA ptr; + + /* Cannot use uiItemEnumO()... have multiple properties to set. */ + uiItemFullO_ptr(layout, ot, IFACE_("Object"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_OBJECT); + + /* par becomes parent, make the associated menus */ + if (par->type == OB_ARMATURE) { + uiItemFullO_ptr(layout, ot, IFACE_("Armature Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_ARMATURE); + + uiItemFullO_ptr(layout, ot, IFACE_(" With Empty Groups"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_ARMATURE_NAME); + + uiItemFullO_ptr(layout, ot, IFACE_(" With Envelope Weights"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_ARMATURE_ENVELOPE); + + uiItemFullO_ptr(layout, ot, IFACE_(" With Automatic Weights"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_ARMATURE_AUTO); + + uiItemFullO_ptr(layout, ot, IFACE_("Bone"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_BONE); + } + else if (par->type == OB_CURVE) { + uiItemFullO_ptr(layout, ot, IFACE_("Curve Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_CURVE); + + uiItemFullO_ptr(layout, ot, IFACE_("Follow Path"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_FOLLOW); + + uiItemFullO_ptr(layout, ot, IFACE_("Path Constraint"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_PATH_CONST); + } + else if (par->type == OB_LATTICE) { + uiItemFullO_ptr(layout, ot, IFACE_("Lattice Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_LATTICE); + } + + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; + } + } + else { + return OPERATOR_CANCELLED; + } + + return OPERATOR_FINISHED; +} + +void OUTLINER_OT_parent_drop(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Drop to Set Parent"; + ot->description = "Drag to parent in Outliner"; + ot->idname = "OUTLINER_OT_parent_drop"; + + /* api callbacks */ + ot->invoke = parent_drop_invoke; + ot->exec = parent_drop_exec; + + ot->poll = ED_operator_outliner_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + /* properties */ + RNA_def_string(ot->srna, "child", "Object", MAX_ID_NAME, "Child", "Child Object"); + RNA_def_string(ot->srna, "parent", "Object", MAX_ID_NAME, "Parent", "Parent Object"); + RNA_def_enum(ot->srna, "type", prop_make_parent_types, 0, "Type", ""); +} + +static bool parenting_poll(bContext *C) +{ + SpaceOops *soops = CTX_wm_space_outliner(C); + + if (soops) { + if (soops->outlinevis == SO_SCENES) { + return true; + } + else if ((soops->outlinevis == SO_VIEW_LAYER) && + (soops->filter & SO_FILTER_NO_COLLECTION)) + { + return true; + } + } + + return false; +} + +/* ******************** Parent Clear Operator *********************** */ + +static bool parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + TreeElement *te = NULL; + float fmval[2]; + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + if (!ELEM(soops->outlinevis, SO_VIEW_LAYER)) { + return false; + } + + if (drag->type == WM_DRAG_ID) { + ID *id = drag->poin; + if (GS(id->name) == ID_OB) { + if (((Object *)id)->parent) { + if ((te = outliner_dropzone_find(soops, fmval, true))) { + TreeStoreElem *tselem = TREESTORE(te); + + switch (te->idcode) { + case ID_SCE: + return (ELEM(tselem->type, TSE_R_LAYER_BASE, TSE_R_LAYER)); + case ID_OB: + return (ELEM(tselem->type, TSE_MODIFIER_BASE, TSE_CONSTRAINT_BASE)); + /* Other codes to ignore? */ + } + } + return (te == NULL); + } + } + } + return 0; +} + +static void parent_clear_copy(wmDrag *drag, wmDropBox *drop) +{ + ID *id = drag->poin; + RNA_string_set(drop->ptr, "dragged_obj", id->name + 2); + + /* Set to simple parent clear type. Avoid menus for drag and drop if possible. + * If desired, user can toggle the different "Clear Parent" types in the operator + * menu on tool shelf. */ + RNA_enum_set(drop->ptr, "type", 0); +} + +static int parent_clear_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + Main *bmain = CTX_data_main(C); + Object *ob = NULL; + SpaceOops *soops = CTX_wm_space_outliner(C); + char obname[MAX_ID_NAME]; + + RNA_string_get(op->ptr, "dragged_obj", obname); + ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, obname); + + /* search forwards to find the object */ + outliner_find_id(soops, &soops->tree, (ID *)ob); + + ED_object_parent_clear(ob, RNA_enum_get(op->ptr, "type")); + + 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); + return OPERATOR_FINISHED; +} + +void OUTLINER_OT_parent_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Drop to Clear Parent"; + ot->description = "Drag to clear parent in Outliner"; + ot->idname = "OUTLINER_OT_parent_clear"; + + /* api callbacks */ + ot->invoke = parent_clear_invoke; + + ot->poll = parenting_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + /* properties */ + RNA_def_string(ot->srna, "dragged_obj", "Object", MAX_ID_NAME, "Child", "Child Object"); + RNA_def_enum(ot->srna, "type", prop_clear_parent_types, 0, "Type", ""); +} + +/* ******************** Scene Drop Operator *********************** */ + +static bool scene_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + float fmval[2]; + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + if (drag->type == WM_DRAG_ID) { + ID *id = drag->poin; + if (GS(id->name) == ID_OB) { + /* Ensure item under cursor is valid drop target */ + TreeElement *te = outliner_dropzone_find(soops, fmval, false); + return (te && te->idcode == ID_SCE && TREESTORE(te)->type == 0); + } + } + return 0; +} + +static void scene_drop_copy(wmDrag *drag, wmDropBox *drop) +{ + ID *id = drag->poin; + + RNA_string_set(drop->ptr, "object", id->name + 2); +} + +static int scene_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Scene *scene = NULL; + Object *ob = NULL; + SpaceOops *soops = CTX_wm_space_outliner(C); + ARegion *ar = CTX_wm_region(C); + Main *bmain = CTX_data_main(C); + TreeElement *te = NULL; + char obname[MAX_ID_NAME]; + float fmval[2]; + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + /* Find object hovered over */ + te = outliner_dropzone_find(soops, fmval, false); + + if (te) { + RNA_string_set(op->ptr, "scene", te->name); + scene = (Scene *)BKE_libblock_find_name(bmain, ID_SCE, te->name); + + RNA_string_get(op->ptr, "object", obname); + ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, obname); + + if (ELEM(NULL, ob, scene) || ID_IS_LINKED(scene)) { + return OPERATOR_CANCELLED; + } + + if (BKE_scene_has_object(scene, ob)) { + return OPERATOR_CANCELLED; + } + + Collection *collection; + if (scene != CTX_data_scene(C)) { + /* when linking to an inactive scene link to the master collection */ + collection = BKE_collection_master(scene); + } + else { + collection = CTX_data_collection(C); + } + + BKE_collection_object_add(bmain, collection, ob); + + 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); + if (base) { + ED_object_base_select(base, BA_SELECT); + } + } + + DEG_relations_tag_update(bmain); + + DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); + WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, scene); + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void OUTLINER_OT_scene_drop(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Drop Object to Scene"; + ot->description = "Drag object to scene in Outliner"; + ot->idname = "OUTLINER_OT_scene_drop"; + + /* api callbacks */ + ot->invoke = scene_drop_invoke; + + ot->poll = ED_operator_outliner_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + /* properties */ + RNA_def_string(ot->srna, "object", "Object", MAX_ID_NAME, "Object", "Target Object"); + RNA_def_string(ot->srna, "scene", "Scene", MAX_ID_NAME, "Scene", "Target Scene"); +} + +/* ******************** Material Drop Operator *********************** */ + +static bool material_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + float fmval[2]; + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + if (drag->type == WM_DRAG_ID) { + ID *id = drag->poin; + if (GS(id->name) == ID_MA) { + /* Ensure item under cursor is valid drop target */ + TreeElement *te = outliner_dropzone_find(soops, fmval, true); + return (te && te->idcode == ID_OB && TREESTORE(te)->type == 0); + } + } + return 0; +} + +static void material_drop_copy(wmDrag *drag, wmDropBox *drop) +{ + ID *id = drag->poin; + + RNA_string_set(drop->ptr, "material", id->name + 2); +} + +static int material_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Material *ma = NULL; + Object *ob = NULL; + Main *bmain = CTX_data_main(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + ARegion *ar = CTX_wm_region(C); + TreeElement *te = NULL; + char mat_name[MAX_ID_NAME - 2]; + float fmval[2]; + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + /* Find object hovered over */ + te = outliner_dropzone_find(soops, fmval, true); + + if (te) { + RNA_string_set(op->ptr, "object", te->name); + ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, te->name); + + RNA_string_get(op->ptr, "material", mat_name); + ma = (Material *)BKE_libblock_find_name(bmain, ID_MA, mat_name); + + if (ELEM(NULL, ob, ma)) { + return OPERATOR_CANCELLED; + } + + assign_material(bmain, ob, ma, ob->totcol + 1, BKE_MAT_ASSIGN_USERPREF); + + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, CTX_wm_view3d(C)); + WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma); + + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void OUTLINER_OT_material_drop(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Drop Material on Object"; + ot->description = "Drag material to object in Outliner"; + ot->idname = "OUTLINER_OT_material_drop"; + + /* api callbacks */ + ot->invoke = material_drop_invoke; + + ot->poll = ED_operator_outliner_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + /* properties */ + 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"); +} + +/* ******************** Collection Drop Operator *********************** */ + +static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + float fmval[2]; + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + if (drag->type == WM_DRAG_ID) { + ID *id = drag->poin; + if (ELEM(GS(id->name), ID_OB, ID_GR)) { + /* Ensure item under cursor is valid drop target */ + TreeElement *te = outliner_dropzone_find(soops, fmval, true); + return (te && outliner_is_collection_tree_element(te)); + } + } + return 0; +} + +static void collection_drop_copy(wmDrag *drag, wmDropBox *drop) +{ + ID *id = drag->poin; + RNA_string_set(drop->ptr, "child", id->name + 2); +} + +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); + Scene *scene = CTX_data_scene(C); + 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); + ARegion *ar = CTX_wm_region(C); + Main *bmain = CTX_data_main(C); + char childname[MAX_ID_NAME]; + float fmval[2]; + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); + + /* Find object hovered over */ + TreeElement *te = outliner_dropzone_find(soops, fmval, true); + + if (!te || !outliner_is_collection_tree_element(te)) { + return OPERATOR_CANCELLED; + } + + Collection *collection = outliner_collection_from_tree_element(te); + + // TODO: don't use scene, makes no sense anymore + // 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(bmain, ID_OB, childname); + BKE_collection_object_add(bmain, collection, ob); + + DEG_id_tag_update(&collection->id, DEG_TAG_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene); + + return OPERATOR_FINISHED; +} + +void OUTLINER_OT_collection_drop(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Link to Collection"; // TODO: rename to move? + ot->description = "Drag to move to collection in Outliner"; + ot->idname = "OUTLINER_OT_collection_drop"; + + /* api callbacks */ + ot->invoke = collection_drop_invoke; + ot->exec = collection_drop_exec; + + ot->poll = ED_operator_outliner_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + /* properties */ + 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"); +} + +/* ********************* Outliner Drag Operator ******************** */ + +typedef struct OutlinerDragDropTooltip { + TreeElement *te; + void *handle; +} OutlinerDragDropTooltip; + +static bool outliner_item_drag_drop_poll(bContext *C) +{ + SpaceOops *soops = CTX_wm_space_outliner(C); + return ED_operator_outliner_active(C) && + /* Only collection display modes supported for now. Others need more design work */ + ELEM(soops->outlinevis, SO_VIEW_LAYER, SO_LIBRARIES); +} + +static TreeElement *outliner_item_drag_element_find(SpaceOops *soops, ARegion *ar, const wmEvent *event) +{ + /* note: using EVT_TWEAK_ events to trigger dragging is fine, + * it sends coordinates from where dragging was started */ + const float my = UI_view2d_region_to_view_y(&ar->v2d, event->mval[1]); + return outliner_find_item_at_y(soops, &soops->tree, my); +} + +static void outliner_item_drag_end(wmWindow *win, OutlinerDragDropTooltip *data) +{ + MEM_SAFE_FREE(data->te->drag_data); + + if (data->handle) { + WM_draw_cb_exit(win, data->handle); + } + + MEM_SAFE_FREE(data); +} + +static void outliner_item_drag_get_insert_data( + const SpaceOops *soops, ARegion *ar, const wmEvent *event, TreeElement *te_dragged, + TreeElement **r_te_insert_handle, TreeElementInsertType *r_insert_type) +{ + TreeElement *te_hovered; + float view_mval[2]; + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + te_hovered = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]); + + if (te_hovered) { + /* mouse hovers an element (ignoring x-axis), now find out how to insert the dragged item exactly */ + + if (te_hovered == te_dragged) { + *r_te_insert_handle = te_dragged; + } + else if (te_hovered != te_dragged) { + const float margin = UI_UNIT_Y * (1.0f / 4); + + *r_te_insert_handle = te_hovered; + if (view_mval[1] < (te_hovered->ys + margin)) { + if (TSELEM_OPEN(TREESTORE(te_hovered), soops)) { + /* inserting after a open item means we insert into it, but as first child */ + if (BLI_listbase_is_empty(&te_hovered->subtree)) { + *r_insert_type = TE_INSERT_INTO; + } + else { + *r_insert_type = TE_INSERT_BEFORE; + *r_te_insert_handle = te_hovered->subtree.first; + } + } + else { + *r_insert_type = TE_INSERT_AFTER; + } + } + else if (view_mval[1] > (te_hovered->ys + (3 * margin))) { + *r_insert_type = TE_INSERT_BEFORE; + } + else { + *r_insert_type = TE_INSERT_INTO; + } + } + } + else { + /* mouse doesn't hover any item (ignoring x-axis), so it's either above list bounds or below. */ + + TreeElement *first = soops->tree.first; + TreeElement *last = soops->tree.last; + + if (view_mval[1] < last->ys) { + *r_te_insert_handle = last; + *r_insert_type = TE_INSERT_AFTER; + } + else if (view_mval[1] > (first->ys + UI_UNIT_Y)) { + *r_te_insert_handle = first; + *r_insert_type = TE_INSERT_BEFORE; + } + else { + BLI_assert(0); + } + } +} + +static void outliner_item_drag_handle( + SpaceOops *soops, ARegion *ar, const wmEvent *event, TreeElement *te_dragged) +{ + TreeElement *te_insert_handle; + TreeElementInsertType insert_type; + + outliner_item_drag_get_insert_data(soops, ar, event, te_dragged, &te_insert_handle, &insert_type); + + if (!te_dragged->reinsert_poll && + /* there is no reinsert_poll, so we do some generic checks (same types and reinsert callback is available) */ + (TREESTORE(te_dragged)->type == TREESTORE(te_insert_handle)->type) && + te_dragged->reinsert) + { + /* pass */ + } + else if (te_dragged == te_insert_handle) { + /* nothing will happen anyway, no need to do poll check */ + } + else if (!te_dragged->reinsert_poll || + !te_dragged->reinsert_poll(te_dragged, &te_insert_handle, &insert_type)) + { + te_insert_handle = NULL; + } + te_dragged->drag_data->insert_type = insert_type; + te_dragged->drag_data->insert_handle = te_insert_handle; +} + +/** + * Returns true if it is a collection and empty. + */ +static bool is_empty_collection(TreeElement *te) +{ + Collection *collection = outliner_collection_from_tree_element(te); + + if (!collection) { + return false; + } + + return BLI_listbase_is_empty(&collection->gobject) && + BLI_listbase_is_empty(&collection->children); +} + +static bool outliner_item_drag_drop_apply( + Main *bmain, + Scene *scene, + SpaceOops *soops, + OutlinerDragDropTooltip *data, + const wmEvent *event) +{ + TreeElement *dragged_te = data->te; + TreeElement *insert_handle = dragged_te->drag_data->insert_handle; + TreeElementInsertType insert_type = dragged_te->drag_data->insert_type; + + if ((insert_handle == dragged_te) || !insert_handle) { + /* No need to do anything */ + } + else if (dragged_te->reinsert) { + BLI_assert(!dragged_te->reinsert_poll || dragged_te->reinsert_poll(dragged_te, &insert_handle, + &insert_type)); + /* call of assert above should not have changed insert_handle and insert_type at this point */ + BLI_assert(dragged_te->drag_data->insert_handle == insert_handle && + dragged_te->drag_data->insert_type == insert_type); + + /* If the collection was just created and you moved objects/collections inside it, + * it is strange to have it closed and we not see the newly dragged elements. */ + const bool should_open_collection = (insert_type == TE_INSERT_INTO) && is_empty_collection(insert_handle); + + dragged_te->reinsert(bmain, scene, soops, dragged_te, insert_handle, insert_type, event); + + if (should_open_collection && !is_empty_collection(insert_handle)) { + TREESTORE(insert_handle)->flag &= ~TSE_CLOSED; + } + return true; + } + + return false; +} + +static int outliner_item_drag_drop_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ARegion *ar = CTX_wm_region(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + OutlinerDragDropTooltip *data = op->customdata; + TreeElement *te_dragged = data->te; + int retval = OPERATOR_RUNNING_MODAL; + bool redraw = false; + bool skip_rebuild = true; + + switch (event->type) { + case EVT_MODAL_MAP: + if (event->val == OUTLINER_ITEM_DRAG_CONFIRM) { + if (outliner_item_drag_drop_apply(bmain, scene, soops, data, event)) { + skip_rebuild = false; + } + retval = OPERATOR_FINISHED; + } + else if (event->val == OUTLINER_ITEM_DRAG_CANCEL) { + retval = OPERATOR_CANCELLED; + } + else { + BLI_assert(0); + } + WM_event_add_mousemove(C); /* update highlight */ + outliner_item_drag_end(CTX_wm_window(C), data); + redraw = true; + break; + case MOUSEMOVE: + outliner_item_drag_handle(soops, ar, event, te_dragged); + redraw = true; + break; + } + + if (redraw) { + if (skip_rebuild) { + ED_region_tag_redraw_no_rebuild(ar); + } + else { + ED_region_tag_redraw(ar); + } + } + + return retval; +} + +static const char *outliner_drag_drop_tooltip_get( + const TreeElement *te_float) +{ + const char *name = NULL; + + const TreeElement *te_insert = te_float->drag_data->insert_handle; + if (te_float && outliner_is_collection_tree_element(te_float)) { + if (te_insert == NULL) { + name = TIP_("Move collection"); + } + else { + switch (te_float->drag_data->insert_type) { + case TE_INSERT_BEFORE: + if (te_insert->prev && outliner_is_collection_tree_element(te_insert->prev)) { + name = TIP_("Move between collections"); + } + else { + name = TIP_("Move before collection"); + } + break; + case TE_INSERT_AFTER: + if (te_insert->next && outliner_is_collection_tree_element(te_insert->next)) { + name = TIP_("Move between collections"); + } + else { + name = TIP_("Move after collection"); + } + break; + case TE_INSERT_INTO: + name = TIP_("Move inside collection"); + break; + } + } + } + else if ((TREESTORE(te_float)->type == 0) && (te_float->idcode == ID_OB)) { + name = TIP_("Move to collection (Ctrl to link)"); + } + + return name; +} + +static void outliner_drag_drop_tooltip_cb(const wmWindow *win, void *vdata) +{ + OutlinerDragDropTooltip *data = vdata; + const char *tooltip; + + int cursorx, cursory; + int x, y; + + tooltip = outliner_drag_drop_tooltip_get(data->te); + if (tooltip == NULL) { + return; + } + + cursorx = win->eventstate->x; + cursory = win->eventstate->y; + + x = cursorx + U.widget_unit; + y = cursory - U.widget_unit; + + /* Drawing. */ + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; + + const float col_fg[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f}; + + GPU_blend(true); + UI_fontstyle_draw_simple_backdrop(fstyle, x, y, tooltip, col_fg, col_bg); + GPU_blend(false); +} + +static int outliner_item_drag_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + SpaceOops *soops = CTX_wm_space_outliner(C); + TreeElement *te_dragged = outliner_item_drag_element_find(soops, ar, event); + + if (!te_dragged) { + return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); + } + + OutlinerDragDropTooltip *data = MEM_mallocN(sizeof(OutlinerDragDropTooltip), __func__); + data->te = te_dragged; + + op->customdata = data; + te_dragged->drag_data = MEM_callocN(sizeof(*te_dragged->drag_data), __func__); + /* by default we don't change the item position */ + te_dragged->drag_data->insert_handle = te_dragged; + /* unset highlighted tree element, dragged one will be highlighted instead */ + outliner_flag_set(&soops->tree, TSE_HIGHLIGHTED, false); + + ED_region_tag_redraw_no_rebuild(ar); + + WM_event_add_modal_handler(C, op); + + data->handle = WM_draw_cb_activate(CTX_wm_window(C), outliner_drag_drop_tooltip_cb, data); + + return OPERATOR_RUNNING_MODAL; +} + +/** + * Notes about Outliner Item Drag 'n Drop: + * Right now only collections display mode is supported. But ideally all/most modes would support this. There are + * just some open design questions that have to be answered: do we want to allow mixing order of different data types + * (like render-layers and objects)? Would that be a purely visual change or would that have any other effect? ... + */ +void OUTLINER_OT_item_drag_drop(wmOperatorType *ot) +{ + ot->name = "Drag and Drop"; + ot->idname = "OUTLINER_OT_item_drag_drop"; + ot->description = "Change the hierarchical position of an item by repositioning it using drag and drop"; + + ot->invoke = outliner_item_drag_drop_invoke; + ot->modal = outliner_item_drag_drop_modal; + + ot->poll = outliner_item_drag_drop_poll; + + ot->flag = OPTYPE_UNDO; +} + +/* *************************** Drop Boxes ************************** */ + +/* region dropbox definition */ +void outliner_dropboxes(void) +{ + ListBase *lb = WM_dropboxmap_find("Outliner", SPACE_OUTLINER, RGN_TYPE_WINDOW); + + WM_dropbox_add(lb, "OUTLINER_OT_parent_drop", parent_drop_poll, parent_drop_copy); + WM_dropbox_add(lb, "OUTLINER_OT_parent_clear", parent_clear_poll, parent_clear_copy); + WM_dropbox_add(lb, "OUTLINER_OT_scene_drop", scene_drop_poll, scene_drop_copy); + WM_dropbox_add(lb, "OUTLINER_OT_material_drop", material_drop_poll, material_drop_copy); + WM_dropbox_add(lb, "OUTLINER_OT_collection_drop", collection_drop_poll, collection_drop_copy); +} diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index d9bcf05fa29..6316b65fefb 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -90,68 +90,6 @@ #include "outliner_intern.h" /* ************************************************************** */ -/* Unused Utilities */ -// XXX: where to place these? - -/* This is not used anywhere at the moment */ -#if 0 -static void outliner_open_reveal(SpaceOops *soops, ListBase *lb, TreeElement *teFind, int *found) -{ - TreeElement *te; - TreeStoreElem *tselem; - - for (te = lb->first; te; te = te->next) { - /* check if this tree-element was the one we're seeking */ - if (te == teFind) { - *found = 1; - return; - } - - /* try to see if sub-tree contains it then */ - outliner_open_reveal(soops, &te->subtree, teFind, found); - if (*found) { - tselem = TREESTORE(te); - if (tselem->flag & TSE_CLOSED) - tselem->flag &= ~TSE_CLOSED; - return; - } - } -} -#endif - -static TreeElement *outliner_dropzone_element(TreeElement *te, const float fmval[2], const bool children) -{ - if ((fmval[1] > te->ys) && (fmval[1] < (te->ys + UI_UNIT_Y))) { - /* name and first icon */ - if ((fmval[0] > te->xs + UI_UNIT_X) && (fmval[0] < te->xend)) - return te; - } - /* Not it. Let's look at its children. */ - if (children && (TREESTORE(te)->flag & TSE_CLOSED) == 0 && (te->subtree.first)) { - for (te = te->subtree.first; te; te = te->next) { - TreeElement *te_valid = outliner_dropzone_element(te, fmval, children); - if (te_valid) - return te_valid; - } - } - return NULL; -} - -/* Used for drag and drop parenting */ -TreeElement *outliner_dropzone_find(const SpaceOops *soops, const float fmval[2], const bool children) -{ - TreeElement *te; - - for (te = soops->tree.first; te; te = te->next) { - TreeElement *te_valid = outliner_dropzone_element(te, fmval, children); - if (te_valid) - return te_valid; - } - return NULL; -} - - -/* ************************************************************** */ /* Highlight --------------------------------------------------- */ @@ -1951,474 +1889,3 @@ void OUTLINER_OT_orphans_purge(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } - -/* ************************************************************** */ -/* DRAG AND DROP OPERATORS */ - -/* ******************** Parent Drop Operator *********************** */ - -static int parent_drop_exec(bContext *C, wmOperator *op) -{ - Object *par = NULL, *ob = NULL; - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - int partype = -1; - char parname[MAX_ID_NAME], childname[MAX_ID_NAME]; - - partype = RNA_enum_get(op->ptr, "type"); - RNA_string_get(op->ptr, "parent", parname); - par = (Object *)BKE_libblock_find_name(bmain, ID_OB, parname); - RNA_string_get(op->ptr, "child", childname); - ob = (Object *)BKE_libblock_find_name(bmain, 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); - - return OPERATOR_FINISHED; -} - -static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - Object *par = NULL; - Object *ob = NULL; - SpaceOops *soops = CTX_wm_space_outliner(C); - ARegion *ar = CTX_wm_region(C); - Main *bmain = CTX_data_main(C); - Scene *scene = NULL; - TreeElement *te = NULL; - char childname[MAX_ID_NAME]; - char parname[MAX_ID_NAME]; - int partype = 0; - float fmval[2]; - - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); - - /* Find object hovered over */ - te = outliner_dropzone_find(soops, fmval, true); - - if (te) { - RNA_string_set(op->ptr, "parent", te->name); - /* Identify parent and child */ - RNA_string_get(op->ptr, "child", childname); - ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, childname); - RNA_string_get(op->ptr, "parent", parname); - par = (Object *)BKE_libblock_find_name(bmain, ID_OB, parname); - - if (ELEM(NULL, ob, par)) { - if (par == NULL) printf("par==NULL\n"); - return OPERATOR_CANCELLED; - } - if (ob == par) { - return OPERATOR_CANCELLED; - } - if (ID_IS_LINKED(ob)) { - BKE_report(op->reports, RPT_INFO, "Can't edit library linked object"); - return OPERATOR_CANCELLED; - } - - scene = (Scene *)outliner_search_back(soops, te, ID_SCE); - - if (scene == NULL) { - /* currently outlier organized in a way, that if there's no parent scene - * element for object it means that all displayed objects belong to - * active scene and parenting them is allowed (sergey) - */ - - scene = CTX_data_scene(C); - } - - if ((par->type != OB_ARMATURE) && (par->type != OB_CURVE) && (par->type != OB_LATTICE)) { - if (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); - } - } - else { - /* Menu creation */ - wmOperatorType *ot = WM_operatortype_find("OUTLINER_OT_parent_drop", false); - uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Set Parent To"), ICON_NONE); - uiLayout *layout = UI_popup_menu_layout(pup); - PointerRNA ptr; - - /* Cannot use uiItemEnumO()... have multiple properties to set. */ - uiItemFullO_ptr(layout, ot, IFACE_("Object"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_OBJECT); - - /* par becomes parent, make the associated menus */ - if (par->type == OB_ARMATURE) { - uiItemFullO_ptr(layout, ot, IFACE_("Armature Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_ARMATURE); - - uiItemFullO_ptr(layout, ot, IFACE_(" With Empty Groups"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_ARMATURE_NAME); - - uiItemFullO_ptr(layout, ot, IFACE_(" With Envelope Weights"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_ARMATURE_ENVELOPE); - - uiItemFullO_ptr(layout, ot, IFACE_(" With Automatic Weights"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_ARMATURE_AUTO); - - uiItemFullO_ptr(layout, ot, IFACE_("Bone"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_BONE); - } - else if (par->type == OB_CURVE) { - uiItemFullO_ptr(layout, ot, IFACE_("Curve Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_CURVE); - - uiItemFullO_ptr(layout, ot, IFACE_("Follow Path"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_FOLLOW); - - uiItemFullO_ptr(layout, ot, IFACE_("Path Constraint"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_PATH_CONST); - } - else if (par->type == OB_LATTICE) { - uiItemFullO_ptr(layout, ot, IFACE_("Lattice Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_string_set(&ptr, "parent", parname); - RNA_string_set(&ptr, "child", childname); - RNA_enum_set(&ptr, "type", PAR_LATTICE); - } - - UI_popup_menu_end(C, pup); - - return OPERATOR_INTERFACE; - } - } - else { - return OPERATOR_CANCELLED; - } - - return OPERATOR_FINISHED; -} - -void OUTLINER_OT_parent_drop(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Drop to Set Parent"; - ot->description = "Drag to parent in Outliner"; - ot->idname = "OUTLINER_OT_parent_drop"; - - /* api callbacks */ - ot->invoke = parent_drop_invoke; - ot->exec = parent_drop_exec; - - ot->poll = ED_operator_outliner_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - - /* properties */ - RNA_def_string(ot->srna, "child", "Object", MAX_ID_NAME, "Child", "Child Object"); - RNA_def_string(ot->srna, "parent", "Object", MAX_ID_NAME, "Parent", "Parent Object"); - RNA_def_enum(ot->srna, "type", prop_make_parent_types, 0, "Type", ""); -} - -static bool outliner_parenting_poll(bContext *C) -{ - SpaceOops *soops = CTX_wm_space_outliner(C); - - if (soops) { - if (soops->outlinevis == SO_SCENES) { - return true; - } - else if ((soops->outlinevis == SO_VIEW_LAYER) && - (soops->filter & SO_FILTER_NO_COLLECTION)) - { - return true; - } - } - - return false; -} - -static int parent_clear_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - Main *bmain = CTX_data_main(C); - Object *ob = NULL; - SpaceOops *soops = CTX_wm_space_outliner(C); - char obname[MAX_ID_NAME]; - - RNA_string_get(op->ptr, "dragged_obj", obname); - ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, obname); - - /* search forwards to find the object */ - outliner_find_id(soops, &soops->tree, (ID *)ob); - - ED_object_parent_clear(ob, RNA_enum_get(op->ptr, "type")); - - 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); - return OPERATOR_FINISHED; -} - -void OUTLINER_OT_parent_clear(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Drop to Clear Parent"; - ot->description = "Drag to clear parent in Outliner"; - ot->idname = "OUTLINER_OT_parent_clear"; - - /* api callbacks */ - ot->invoke = parent_clear_invoke; - - ot->poll = outliner_parenting_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - - /* properties */ - RNA_def_string(ot->srna, "dragged_obj", "Object", MAX_ID_NAME, "Child", "Child Object"); - RNA_def_enum(ot->srna, "type", prop_clear_parent_types, 0, "Type", ""); -} - -static int scene_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - Scene *scene = NULL; - Object *ob = NULL; - SpaceOops *soops = CTX_wm_space_outliner(C); - ARegion *ar = CTX_wm_region(C); - Main *bmain = CTX_data_main(C); - TreeElement *te = NULL; - char obname[MAX_ID_NAME]; - float fmval[2]; - - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); - - /* Find object hovered over */ - te = outliner_dropzone_find(soops, fmval, false); - - if (te) { - RNA_string_set(op->ptr, "scene", te->name); - scene = (Scene *)BKE_libblock_find_name(bmain, ID_SCE, te->name); - - RNA_string_get(op->ptr, "object", obname); - ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, obname); - - if (ELEM(NULL, ob, scene) || ID_IS_LINKED(scene)) { - return OPERATOR_CANCELLED; - } - - if (BKE_scene_has_object(scene, ob)) { - return OPERATOR_CANCELLED; - } - - Collection *collection; - if (scene != CTX_data_scene(C)) { - /* when linking to an inactive scene link to the master collection */ - collection = BKE_collection_master(scene); - } - else { - collection = CTX_data_collection(C); - } - - BKE_collection_object_add(bmain, collection, ob); - - 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); - if (base) { - ED_object_base_select(base, BA_SELECT); - } - } - - DEG_relations_tag_update(bmain); - - DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); - WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, scene); - - return OPERATOR_FINISHED; - } - - return OPERATOR_CANCELLED; -} - -void OUTLINER_OT_scene_drop(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Drop Object to Scene"; - ot->description = "Drag object to scene in Outliner"; - ot->idname = "OUTLINER_OT_scene_drop"; - - /* api callbacks */ - ot->invoke = scene_drop_invoke; - - ot->poll = ED_operator_outliner_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - - /* properties */ - RNA_def_string(ot->srna, "object", "Object", MAX_ID_NAME, "Object", "Target Object"); - RNA_def_string(ot->srna, "scene", "Scene", MAX_ID_NAME, "Scene", "Target Scene"); -} - -static int material_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - Material *ma = NULL; - Object *ob = NULL; - Main *bmain = CTX_data_main(C); - SpaceOops *soops = CTX_wm_space_outliner(C); - ARegion *ar = CTX_wm_region(C); - TreeElement *te = NULL; - char mat_name[MAX_ID_NAME - 2]; - float fmval[2]; - - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); - - /* Find object hovered over */ - te = outliner_dropzone_find(soops, fmval, true); - - if (te) { - RNA_string_set(op->ptr, "object", te->name); - ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, te->name); - - RNA_string_get(op->ptr, "material", mat_name); - ma = (Material *)BKE_libblock_find_name(bmain, ID_MA, mat_name); - - if (ELEM(NULL, ob, ma)) { - return OPERATOR_CANCELLED; - } - - assign_material(bmain, ob, ma, ob->totcol + 1, BKE_MAT_ASSIGN_USERPREF); - - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, CTX_wm_view3d(C)); - WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma); - - return OPERATOR_FINISHED; - } - - return OPERATOR_CANCELLED; -} - -void OUTLINER_OT_material_drop(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Drop Material on Object"; - ot->description = "Drag material to object in Outliner"; - ot->idname = "OUTLINER_OT_material_drop"; - - /* api callbacks */ - ot->invoke = material_drop_invoke; - - ot->poll = ED_operator_outliner_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - - /* properties */ - 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"); -} - -/* ******************** 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); - Scene *scene = CTX_data_scene(C); - 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); - ARegion *ar = CTX_wm_region(C); - Main *bmain = CTX_data_main(C); - char childname[MAX_ID_NAME]; - float fmval[2]; - - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); - - /* Find object hovered over */ - TreeElement *te = outliner_dropzone_find(soops, fmval, true); - - if (!te || !outliner_is_collection_tree_element(te)) { - return OPERATOR_CANCELLED; - } - - Collection *collection = outliner_collection_from_tree_element(te); - - // TODO: don't use scene, makes no sense anymore - // 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(bmain, ID_OB, childname); - BKE_collection_object_add(bmain, collection, ob); - - DEG_id_tag_update(&collection->id, DEG_TAG_COPY_ON_WRITE); - DEG_relations_tag_update(bmain); - WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene); - - return OPERATOR_FINISHED; -} - -void OUTLINER_OT_collection_drop(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Link to Collection"; // TODO: rename to move? - ot->description = "Drag to move to collection in Outliner"; - ot->idname = "OUTLINER_OT_collection_drop"; - - /* api callbacks */ - ot->invoke = collection_drop_invoke; - ot->exec = collection_drop_exec; - - ot->poll = ED_operator_outliner_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; - - /* properties */ - 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"); -} diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 461d4bd7c56..ecc092358d6 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -284,10 +284,23 @@ void item_object_mode_exit_cb( struct bContext *C, struct ReportList *reports, struct Scene *scene, TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem, void *user_data); -TreeElement *outliner_dropzone_find(const struct SpaceOops *soops, const float fmval[2], const bool children); - void outliner_set_coordinates(struct ARegion *ar, struct SpaceOops *soops); +/* outliner_dragdrop.c */ +enum { + OUTLINER_ITEM_DRAG_CANCEL, + OUTLINER_ITEM_DRAG_CONFIRM, +}; + +void outliner_dropboxes(void); + +void OUTLINER_OT_item_drag_drop(struct wmOperatorType *ot); +void OUTLINER_OT_parent_drop(struct wmOperatorType *ot); +void OUTLINER_OT_parent_clear(struct wmOperatorType *ot); +void OUTLINER_OT_scene_drop(struct wmOperatorType *ot); +void OUTLINER_OT_material_drop(struct wmOperatorType *ot); +void OUTLINER_OT_collection_drop(struct wmOperatorType *ot); + /* ...................................................... */ void OUTLINER_OT_highlight_update(struct wmOperatorType *ot); @@ -319,12 +332,6 @@ void OUTLINER_OT_drivers_delete_selected(struct wmOperatorType *ot); void OUTLINER_OT_orphans_purge(struct wmOperatorType *ot); -void OUTLINER_OT_parent_drop(struct wmOperatorType *ot); -void OUTLINER_OT_parent_clear(struct wmOperatorType *ot); -void OUTLINER_OT_scene_drop(struct wmOperatorType *ot); -void OUTLINER_OT_material_drop(struct wmOperatorType *ot); -void OUTLINER_OT_collection_drop(struct wmOperatorType *ot); - /* outliner_tools.c ---------------------------------------------- */ void OUTLINER_OT_operation(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c index 576038979d3..a4aad11a821 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -55,353 +55,6 @@ #include "outliner_intern.h" -typedef struct OutlinerDragDropTooltip { - TreeElement *te; - void *handle; -} OutlinerDragDropTooltip; - -enum { - OUTLINER_ITEM_DRAG_CANCEL, - OUTLINER_ITEM_DRAG_CONFIRM, -}; - -static bool outliner_item_drag_drop_poll(bContext *C) -{ - SpaceOops *soops = CTX_wm_space_outliner(C); - return ED_operator_outliner_active(C) && - /* Only collection display modes supported for now. Others need more design work */ - ELEM(soops->outlinevis, SO_VIEW_LAYER, SO_LIBRARIES); -} - -static TreeElement *outliner_item_drag_element_find(SpaceOops *soops, ARegion *ar, const wmEvent *event) -{ - /* note: using EVT_TWEAK_ events to trigger dragging is fine, - * it sends coordinates from where dragging was started */ - const float my = UI_view2d_region_to_view_y(&ar->v2d, event->mval[1]); - return outliner_find_item_at_y(soops, &soops->tree, my); -} - -static void outliner_item_drag_end(wmWindow *win, OutlinerDragDropTooltip *data) -{ - MEM_SAFE_FREE(data->te->drag_data); - - if (data->handle) { - WM_draw_cb_exit(win, data->handle); - } - - MEM_SAFE_FREE(data); -} - -static void outliner_item_drag_get_insert_data( - const SpaceOops *soops, ARegion *ar, const wmEvent *event, TreeElement *te_dragged, - TreeElement **r_te_insert_handle, TreeElementInsertType *r_insert_type) -{ - TreeElement *te_hovered; - float view_mval[2]; - - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); - te_hovered = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]); - - if (te_hovered) { - /* mouse hovers an element (ignoring x-axis), now find out how to insert the dragged item exactly */ - - if (te_hovered == te_dragged) { - *r_te_insert_handle = te_dragged; - } - else if (te_hovered != te_dragged) { - const float margin = UI_UNIT_Y * (1.0f / 4); - - *r_te_insert_handle = te_hovered; - if (view_mval[1] < (te_hovered->ys + margin)) { - if (TSELEM_OPEN(TREESTORE(te_hovered), soops)) { - /* inserting after a open item means we insert into it, but as first child */ - if (BLI_listbase_is_empty(&te_hovered->subtree)) { - *r_insert_type = TE_INSERT_INTO; - } - else { - *r_insert_type = TE_INSERT_BEFORE; - *r_te_insert_handle = te_hovered->subtree.first; - } - } - else { - *r_insert_type = TE_INSERT_AFTER; - } - } - else if (view_mval[1] > (te_hovered->ys + (3 * margin))) { - *r_insert_type = TE_INSERT_BEFORE; - } - else { - *r_insert_type = TE_INSERT_INTO; - } - } - } - else { - /* mouse doesn't hover any item (ignoring x-axis), so it's either above list bounds or below. */ - - TreeElement *first = soops->tree.first; - TreeElement *last = soops->tree.last; - - if (view_mval[1] < last->ys) { - *r_te_insert_handle = last; - *r_insert_type = TE_INSERT_AFTER; - } - else if (view_mval[1] > (first->ys + UI_UNIT_Y)) { - *r_te_insert_handle = first; - *r_insert_type = TE_INSERT_BEFORE; - } - else { - BLI_assert(0); - } - } -} - -static void outliner_item_drag_handle( - SpaceOops *soops, ARegion *ar, const wmEvent *event, TreeElement *te_dragged) -{ - TreeElement *te_insert_handle; - TreeElementInsertType insert_type; - - outliner_item_drag_get_insert_data(soops, ar, event, te_dragged, &te_insert_handle, &insert_type); - - if (!te_dragged->reinsert_poll && - /* there is no reinsert_poll, so we do some generic checks (same types and reinsert callback is available) */ - (TREESTORE(te_dragged)->type == TREESTORE(te_insert_handle)->type) && - te_dragged->reinsert) - { - /* pass */ - } - else if (te_dragged == te_insert_handle) { - /* nothing will happen anyway, no need to do poll check */ - } - else if (!te_dragged->reinsert_poll || - !te_dragged->reinsert_poll(te_dragged, &te_insert_handle, &insert_type)) - { - te_insert_handle = NULL; - } - te_dragged->drag_data->insert_type = insert_type; - te_dragged->drag_data->insert_handle = te_insert_handle; -} - -/** - * Returns true if it is a collection and empty. - */ -static bool is_empty_collection(TreeElement *te) -{ - Collection *collection = outliner_collection_from_tree_element(te); - - if (!collection) { - return false; - } - - return BLI_listbase_is_empty(&collection->gobject) && - BLI_listbase_is_empty(&collection->children); -} - -static bool outliner_item_drag_drop_apply( - Main *bmain, - Scene *scene, - SpaceOops *soops, - OutlinerDragDropTooltip *data, - const wmEvent *event) -{ - TreeElement *dragged_te = data->te; - TreeElement *insert_handle = dragged_te->drag_data->insert_handle; - TreeElementInsertType insert_type = dragged_te->drag_data->insert_type; - - if ((insert_handle == dragged_te) || !insert_handle) { - /* No need to do anything */ - } - else if (dragged_te->reinsert) { - BLI_assert(!dragged_te->reinsert_poll || dragged_te->reinsert_poll(dragged_te, &insert_handle, - &insert_type)); - /* call of assert above should not have changed insert_handle and insert_type at this point */ - BLI_assert(dragged_te->drag_data->insert_handle == insert_handle && - dragged_te->drag_data->insert_type == insert_type); - - /* If the collection was just created and you moved objects/collections inside it, - * it is strange to have it closed and we not see the newly dragged elements. */ - const bool should_open_collection = (insert_type == TE_INSERT_INTO) && is_empty_collection(insert_handle); - - dragged_te->reinsert(bmain, scene, soops, dragged_te, insert_handle, insert_type, event); - - if (should_open_collection && !is_empty_collection(insert_handle)) { - TREESTORE(insert_handle)->flag &= ~TSE_CLOSED; - } - return true; - } - - return false; -} - -static int outliner_item_drag_drop_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ARegion *ar = CTX_wm_region(C); - SpaceOops *soops = CTX_wm_space_outliner(C); - OutlinerDragDropTooltip *data = op->customdata; - TreeElement *te_dragged = data->te; - int retval = OPERATOR_RUNNING_MODAL; - bool redraw = false; - bool skip_rebuild = true; - - switch (event->type) { - case EVT_MODAL_MAP: - if (event->val == OUTLINER_ITEM_DRAG_CONFIRM) { - if (outliner_item_drag_drop_apply(bmain, scene, soops, data, event)) { - skip_rebuild = false; - } - retval = OPERATOR_FINISHED; - } - else if (event->val == OUTLINER_ITEM_DRAG_CANCEL) { - retval = OPERATOR_CANCELLED; - } - else { - BLI_assert(0); - } - WM_event_add_mousemove(C); /* update highlight */ - outliner_item_drag_end(CTX_wm_window(C), data); - redraw = true; - break; - case MOUSEMOVE: - outliner_item_drag_handle(soops, ar, event, te_dragged); - redraw = true; - break; - } - - if (redraw) { - if (skip_rebuild) { - ED_region_tag_redraw_no_rebuild(ar); - } - else { - ED_region_tag_redraw(ar); - } - } - - return retval; -} - -static const char *outliner_drag_drop_tooltip_get( - const TreeElement *te_float) -{ - const char *name = NULL; - - const TreeElement *te_insert = te_float->drag_data->insert_handle; - if (te_float && outliner_is_collection_tree_element(te_float)) { - if (te_insert == NULL) { - name = TIP_("Move collection"); - } - else { - switch (te_float->drag_data->insert_type) { - case TE_INSERT_BEFORE: - if (te_insert->prev && outliner_is_collection_tree_element(te_insert->prev)) { - name = TIP_("Move between collections"); - } - else { - name = TIP_("Move before collection"); - } - break; - case TE_INSERT_AFTER: - if (te_insert->next && outliner_is_collection_tree_element(te_insert->next)) { - name = TIP_("Move between collections"); - } - else { - name = TIP_("Move after collection"); - } - break; - case TE_INSERT_INTO: - name = TIP_("Move inside collection"); - break; - } - } - } - else if ((TREESTORE(te_float)->type == 0) && (te_float->idcode == ID_OB)) { - name = TIP_("Move to collection (Ctrl to link)"); - } - - return name; -} - -static void outliner_drag_drop_tooltip_cb(const wmWindow *win, void *vdata) -{ - OutlinerDragDropTooltip *data = vdata; - const char *tooltip; - - int cursorx, cursory; - int x, y; - - tooltip = outliner_drag_drop_tooltip_get(data->te); - if (tooltip == NULL) { - return; - } - - cursorx = win->eventstate->x; - cursory = win->eventstate->y; - - x = cursorx + U.widget_unit; - y = cursory - U.widget_unit; - - /* Drawing. */ - const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - - const float col_fg[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f}; - - GPU_blend(true); - UI_fontstyle_draw_simple_backdrop(fstyle, x, y, tooltip, col_fg, col_bg); - GPU_blend(false); -} - -static int outliner_item_drag_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - ARegion *ar = CTX_wm_region(C); - SpaceOops *soops = CTX_wm_space_outliner(C); - TreeElement *te_dragged = outliner_item_drag_element_find(soops, ar, event); - - if (!te_dragged) { - return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); - } - - OutlinerDragDropTooltip *data = MEM_mallocN(sizeof(OutlinerDragDropTooltip), __func__); - data->te = te_dragged; - - op->customdata = data; - te_dragged->drag_data = MEM_callocN(sizeof(*te_dragged->drag_data), __func__); - /* by default we don't change the item position */ - te_dragged->drag_data->insert_handle = te_dragged; - /* unset highlighted tree element, dragged one will be highlighted instead */ - outliner_flag_set(&soops->tree, TSE_HIGHLIGHTED, false); - - ED_region_tag_redraw_no_rebuild(ar); - - WM_event_add_modal_handler(C, op); - - data->handle = WM_draw_cb_activate(CTX_wm_window(C), outliner_drag_drop_tooltip_cb, data); - - return OPERATOR_RUNNING_MODAL; -} - -/** - * Notes about Outliner Item Drag 'n Drop: - * Right now only collections display mode is supported. But ideally all/most modes would support this. There are - * just some open design questions that have to be answered: do we want to allow mixing order of different data types - * (like render-layers and objects)? Would that be a purely visual change or would that have any other effect? ... - */ -static void OUTLINER_OT_item_drag_drop(wmOperatorType *ot) -{ - ot->name = "Drag and Drop"; - ot->idname = "OUTLINER_OT_item_drag_drop"; - ot->description = "Change the hierarchical position of an item by repositioning it using drag and drop"; - - ot->invoke = outliner_item_drag_drop_invoke; - ot->modal = outliner_item_drag_drop_modal; - - ot->poll = outliner_item_drag_drop_poll; - - ot->flag = OPTYPE_UNDO; -} - - /* ************************** registration **********************************/ void outliner_operatortypes(void) diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index e4e99f88bf4..b0d63aee7c0 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -93,196 +93,6 @@ static void outliner_main_region_init(wmWindowManager *wm, ARegion *ar) WM_event_add_dropbox_handler(&ar->handlers, lb); } -static bool outliner_parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) -{ - ARegion *ar = CTX_wm_region(C); - SpaceOops *soops = CTX_wm_space_outliner(C); - float fmval[2]; - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); - - if (drag->type == WM_DRAG_ID) { - ID *id = drag->poin; - if (GS(id->name) == ID_OB) { - /* Ensure item under cursor is valid drop target */ - TreeElement *te = outliner_dropzone_find(soops, fmval, true); - TreeStoreElem *tselem = te ? TREESTORE(te) : NULL; - - if (!te) { - /* pass */ - } - else if (te->idcode == ID_OB && tselem->type == 0) { - Scene *scene; - ID *te_id = tselem->id; - - /* check if dropping self or parent */ - if (te_id == id || (Object *)te_id == ((Object *)id)->parent) - return 0; - - /* check that parent/child are both in the same scene */ - scene = (Scene *)outliner_search_back(soops, te, ID_SCE); - - /* currently outliner organized in a way that if there's no parent scene - * element for object it means that all displayed objects belong to - * active scene and parenting them is allowed (sergey) - */ - if (!scene) { - return 1; - } - else { - for (ViewLayer *view_layer = scene->view_layers.first; - view_layer; - view_layer = view_layer->next) - { - if (BKE_view_layer_base_find(view_layer, (Object *)id)) { - return 1; - } - } - } - } - } - } - return 0; -} - -static void outliner_parent_drop_copy(wmDrag *drag, wmDropBox *drop) -{ - ID *id = drag->poin; - - RNA_string_set(drop->ptr, "child", id->name + 2); -} - -static bool outliner_parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event) -{ - ARegion *ar = CTX_wm_region(C); - SpaceOops *soops = CTX_wm_space_outliner(C); - TreeElement *te = NULL; - float fmval[2]; - - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); - - if (!ELEM(soops->outlinevis, SO_VIEW_LAYER)) { - return false; - } - - if (drag->type == WM_DRAG_ID) { - ID *id = drag->poin; - if (GS(id->name) == ID_OB) { - if (((Object *)id)->parent) { - if ((te = outliner_dropzone_find(soops, fmval, true))) { - TreeStoreElem *tselem = TREESTORE(te); - - switch (te->idcode) { - case ID_SCE: - return (ELEM(tselem->type, TSE_R_LAYER_BASE, TSE_R_LAYER)); - case ID_OB: - return (ELEM(tselem->type, TSE_MODIFIER_BASE, TSE_CONSTRAINT_BASE)); - /* Other codes to ignore? */ - } - } - return (te == NULL); - } - } - } - return 0; -} - -static void outliner_parent_clear_copy(wmDrag *drag, wmDropBox *drop) -{ - ID *id = drag->poin; - RNA_string_set(drop->ptr, "dragged_obj", id->name + 2); - - /* Set to simple parent clear type. Avoid menus for drag and drop if possible. - * If desired, user can toggle the different "Clear Parent" types in the operator - * menu on tool shelf. */ - RNA_enum_set(drop->ptr, "type", 0); -} - -static bool outliner_scene_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) -{ - ARegion *ar = CTX_wm_region(C); - SpaceOops *soops = CTX_wm_space_outliner(C); - float fmval[2]; - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); - - if (drag->type == WM_DRAG_ID) { - ID *id = drag->poin; - if (GS(id->name) == ID_OB) { - /* Ensure item under cursor is valid drop target */ - TreeElement *te = outliner_dropzone_find(soops, fmval, false); - return (te && te->idcode == ID_SCE && TREESTORE(te)->type == 0); - } - } - return 0; -} - -static void outliner_scene_drop_copy(wmDrag *drag, wmDropBox *drop) -{ - ID *id = drag->poin; - - RNA_string_set(drop->ptr, "object", id->name + 2); -} - -static bool outliner_material_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) -{ - ARegion *ar = CTX_wm_region(C); - SpaceOops *soops = CTX_wm_space_outliner(C); - float fmval[2]; - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); - - if (drag->type == WM_DRAG_ID) { - ID *id = drag->poin; - if (GS(id->name) == ID_MA) { - /* Ensure item under cursor is valid drop target */ - TreeElement *te = outliner_dropzone_find(soops, fmval, true); - return (te && te->idcode == ID_OB && TREESTORE(te)->type == 0); - } - } - return 0; -} - -static void outliner_material_drop_copy(wmDrag *drag, wmDropBox *drop) -{ - ID *id = drag->poin; - - RNA_string_set(drop->ptr, "material", id->name + 2); -} - -static bool outliner_collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) -{ - ARegion *ar = CTX_wm_region(C); - SpaceOops *soops = CTX_wm_space_outliner(C); - float fmval[2]; - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); - - if (drag->type == WM_DRAG_ID) { - ID *id = drag->poin; - if (ELEM(GS(id->name), ID_OB, ID_GR)) { - /* Ensure item under cursor is valid drop target */ - TreeElement *te = outliner_dropzone_find(soops, fmval, true); - return (te && outliner_is_collection_tree_element(te)); - } - } - return 0; -} - -static void outliner_collection_drop_copy(wmDrag *drag, wmDropBox *drop) -{ - ID *id = drag->poin; - RNA_string_set(drop->ptr, "child", id->name + 2); -} - -/* region dropbox definition */ -static void outliner_dropboxes(void) -{ - ListBase *lb = WM_dropboxmap_find("Outliner", SPACE_OUTLINER, RGN_TYPE_WINDOW); - - WM_dropbox_add(lb, "OUTLINER_OT_parent_drop", outliner_parent_drop_poll, outliner_parent_drop_copy); - WM_dropbox_add(lb, "OUTLINER_OT_parent_clear", outliner_parent_clear_poll, outliner_parent_clear_copy); - WM_dropbox_add(lb, "OUTLINER_OT_scene_drop", outliner_scene_drop_poll, outliner_scene_drop_copy); - WM_dropbox_add(lb, "OUTLINER_OT_material_drop", outliner_material_drop_poll, outliner_material_drop_copy); - WM_dropbox_add(lb, "OUTLINER_OT_collection_drop", outliner_collection_drop_poll, outliner_collection_drop_copy); -} - static void outliner_main_region_draw(const bContext *C, ARegion *ar) { View2D *v2d = &ar->v2d; |