diff options
author | Joshua Leung <aligorith@gmail.com> | 2012-01-22 14:20:30 +0400 |
---|---|---|
committer | Joshua Leung <aligorith@gmail.com> | 2012-01-22 14:20:30 +0400 |
commit | 98fd7c294881d0452b0c9eeb3e8b3a6b604567d9 (patch) | |
tree | 48998273383bc95c369a8ce67cdaa2825c5b0192 /source | |
parent | c8cff5e1c4a8a5b61f11ee708460c994903f14e7 (diff) |
Patch [#27790] Drag and drop parenting in outliner
Submitted by Perry Parks (scuey)
From the patch:
This patch enables drag and drop parenting for objects in the outliner.
Drag and drop is supported for a selection of multiple objects as well. Also,
all of the "special" parenting tasks (armature, curve, lattice) are possible
through the usual parenting context menus. For example, drag a mesh object onto
an armature and you are prompted for using bone envelopes, automatic weights,
etc.
Demonstration on Vimeo: http://vimeo.com/25698606
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/editors/include/ED_object.h | 24 | ||||
-rw-r--r-- | source/blender/editors/object/object_relations.c | 117 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_edit.c | 275 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_intern.h | 6 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_ops.c | 3 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/space_outliner.c | 86 |
6 files changed, 454 insertions, 57 deletions
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 51d3c3f021b..f3e780d715e 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -40,6 +40,7 @@ struct bConstraint; struct bContext; struct bPoseChannel; struct Curve; +struct EnumPropertyItem; struct KeyBlock; struct Lattice; struct Main; @@ -64,6 +65,29 @@ void ED_operatortypes_object(void); void ED_operatormacros_object(void); void ED_keymap_object(struct wmKeyConfig *keyconf); +/* object_relations.c */ +typedef enum eParentType { + PAR_OBJECT, + PAR_ARMATURE, + PAR_ARMATURE_NAME, + PAR_ARMATURE_ENVELOPE, + PAR_ARMATURE_AUTO, + PAR_BONE, + PAR_CURVE, + PAR_FOLLOW, + PAR_PATH_CONST, + PAR_LATTICE, + PAR_VERTEX, + PAR_TRIA +} eParentType; + +extern struct EnumPropertyItem prop_clear_parent_types[]; +extern struct EnumPropertyItem prop_make_parent_types[]; + +int ED_object_parent_set(struct bContext *C, struct wmOperator *op, struct Object *par, int partype); +void ED_object_parent_clear(struct bContext *C, int type); + + /* generic editmode keys like pet * do_pet * 0: No diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index b1c8f8012a5..37461377506 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -421,43 +421,47 @@ void OBJECT_OT_proxy_make (wmOperatorType *ot) /********************** Clear Parent Operator ******************* */ -static EnumPropertyItem prop_clear_parent_types[] = { +EnumPropertyItem prop_clear_parent_types[] = { {0, "CLEAR", 0, "Clear Parent", ""}, {1, "CLEAR_KEEP_TRANSFORM", 0, "Clear and Keep Transformation", ""}, {2, "CLEAR_INVERSE", 0, "Clear Parent Inverse", ""}, {0, NULL, 0, NULL, NULL} }; -/* note, poll should check for editable scene */ -static int parent_clear_exec(bContext *C, wmOperator *op) +void ED_object_parent_clear(bContext *C, int type) { Main *bmain= CTX_data_main(C); Scene *scene= CTX_data_scene(C); - int type= RNA_enum_get(op->ptr, "type"); - - CTX_DATA_BEGIN(C, Object*, ob, selected_editable_objects) { + CTX_DATA_BEGIN(C, Object*, ob, selected_editable_objects) + { if(ob->parent == NULL) continue; if(type == 0) { ob->parent= NULL; - } + } else if(type == 1) { ob->parent= NULL; object_apply_mat4(ob, ob->obmat, TRUE, FALSE); } else if(type == 2) unit_m4(ob->parentinv); - + ob->recalc |= OB_RECALC_OB|OB_RECALC_DATA|OB_RECALC_TIME; } CTX_DATA_END; - + DAG_scene_sort(bmain, scene); DAG_ids_flush_update(bmain, 0); WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL); WM_event_add_notifier(C, NC_OBJECT|ND_PARENT, NULL); +} + +/* note, poll should check for editable scene */ +static int parent_clear_exec(bContext *C, wmOperator *op) +{ + ED_object_parent_clear(C, RNA_enum_get(op->ptr, "type")); return OPERATOR_FINISHED; } @@ -483,35 +487,6 @@ void OBJECT_OT_parent_clear(wmOperatorType *ot) /* ******************** Make Parent Operator *********************** */ -#define PAR_OBJECT 0 -#define PAR_ARMATURE 1 -#define PAR_ARMATURE_NAME 2 -#define PAR_ARMATURE_ENVELOPE 3 -#define PAR_ARMATURE_AUTO 4 -#define PAR_BONE 5 -#define PAR_CURVE 6 -#define PAR_FOLLOW 7 -#define PAR_PATH_CONST 8 -#define PAR_LATTICE 9 -#define PAR_VERTEX 10 -#define PAR_TRIA 11 - -static EnumPropertyItem prop_make_parent_types[] = { - {PAR_OBJECT, "OBJECT", 0, "Object", ""}, - {PAR_ARMATURE, "ARMATURE", 0, "Armature Deform", ""}, - {PAR_ARMATURE_NAME, "ARMATURE_NAME", 0, " With Empty Groups", ""}, - {PAR_ARMATURE_AUTO, "ARMATURE_AUTO", 0, " With Automatic Weights", ""}, - {PAR_ARMATURE_ENVELOPE, "ARMATURE_ENVELOPE", 0, " With Envelope Weights", ""}, - {PAR_BONE, "BONE", 0, "Bone", ""}, - {PAR_CURVE, "CURVE", 0, "Curve Deform", ""}, - {PAR_FOLLOW, "FOLLOW", 0, "Follow Path", ""}, - {PAR_PATH_CONST, "PATH_CONST", 0, "Path Constraint", ""}, - {PAR_LATTICE, "LATTICE", 0, "Lattice Deform", ""}, - {PAR_VERTEX, "VERTEX", 0, "Vertex", ""}, - {PAR_TRIA, "TRIA", 0, "Triangle", ""}, - {0, NULL, 0, NULL, NULL} -}; - void ED_object_parent(Object *ob, Object *par, int type, const char *substr) { if (!par || BKE_object_parent_loop_check(par, ob)) { @@ -529,13 +504,28 @@ void ED_object_parent(Object *ob, Object *par, int type, const char *substr) BLI_strncpy(ob->parsubstr, substr, sizeof(ob->parsubstr)); } -static int parent_set_exec(bContext *C, wmOperator *op) +/* Operator Property */ +EnumPropertyItem prop_make_parent_types[] = { + {PAR_OBJECT, "OBJECT", 0, "Object", ""}, + {PAR_ARMATURE, "ARMATURE", 0, "Armature Deform", ""}, + {PAR_ARMATURE_NAME, "ARMATURE_NAME", 0, " With Empty Groups", ""}, + {PAR_ARMATURE_AUTO, "ARMATURE_AUTO", 0, " With Automatic Weights", ""}, + {PAR_ARMATURE_ENVELOPE, "ARMATURE_ENVELOPE", 0, " With Envelope Weights", ""}, + {PAR_BONE, "BONE", 0, "Bone", ""}, + {PAR_CURVE, "CURVE", 0, "Curve Deform", ""}, + {PAR_FOLLOW, "FOLLOW", 0, "Follow Path", ""}, + {PAR_PATH_CONST, "PATH_CONST", 0, "Path Constraint", ""}, + {PAR_LATTICE, "LATTICE", 0, "Lattice Deform", ""}, + {PAR_VERTEX, "VERTEX", 0, "Vertex", ""}, + {PAR_TRIA, "TRIA", 0, "Triangle", ""}, + {0, NULL, 0, NULL, NULL} +}; + +int ED_object_parent_set(bContext *C, wmOperator *op, Object *par, int partype) { Main *bmain= CTX_data_main(C); Scene *scene= CTX_data_scene(C); - Object *par= ED_object_active_context(C); bPoseChannel *pchan= NULL; - int partype= RNA_enum_get(op->ptr, "type"); int pararm= ELEM4(partype, PAR_ARMATURE, PAR_ARMATURE_NAME, PAR_ARMATURE_ENVELOPE, PAR_ARMATURE_AUTO); par->recalc |= OB_RECALC_OB; @@ -543,7 +533,7 @@ static int parent_set_exec(bContext *C, wmOperator *op) /* preconditions */ if(partype==PAR_FOLLOW || partype==PAR_PATH_CONST) { if(par->type!=OB_CURVE) - return OPERATOR_CANCELLED; + return 0; else { Curve *cu= par->data; @@ -574,15 +564,14 @@ static int parent_set_exec(bContext *C, wmOperator *op) if(pchan==NULL) { BKE_report(op->reports, RPT_ERROR, "No active Bone"); - return OPERATOR_CANCELLED; + return 0; } } /* context iterator */ - CTX_DATA_BEGIN(C, Object*, ob, selected_editable_objects) { - - if(ob!=par) { - + CTX_DATA_BEGIN(C, Object*, ob, selected_editable_objects) + { + if (ob!=par) { if (BKE_object_parent_loop_check(par, ob)) { BKE_report(op->reports, RPT_ERROR, "Loop in parents"); } @@ -591,10 +580,11 @@ static int parent_set_exec(bContext *C, wmOperator *op) /* apply transformation of previous parenting */ /* object_apply_mat4(ob, ob->obmat); */ /* removed because of bug [#23577] */ - + /* set the parent (except for follow-path constraint option) */ - if(partype != PAR_PATH_CONST) + if (partype != PAR_PATH_CONST) { ob->parent= par; + } /* handle types */ if (pchan) @@ -602,9 +592,10 @@ static int parent_set_exec(bContext *C, wmOperator *op) else ob->parsubstr[0]= 0; - if(partype == PAR_PATH_CONST) - ; /* don't do anything here, since this is not technically "parenting" */ - else if( ELEM(partype, PAR_CURVE, PAR_LATTICE) || pararm ) + if (partype == PAR_PATH_CONST) { + /* don't do anything here, since this is not technically "parenting" */ + } + else if (ELEM(partype, PAR_CURVE, PAR_LATTICE) || (pararm)) { /* partype is now set to PAROBJECT so that invisible 'virtual' modifiers don't need to be created * NOTE: the old (2.4x) method was to set ob->partype = PARSKEL, creating the virtual modifiers @@ -614,10 +605,10 @@ static int parent_set_exec(bContext *C, wmOperator *op) /* BUT, to keep the deforms, we need a modifier, and then we need to set the object that it uses */ // XXX currently this should only happen for meshes, curves, surfaces, and lattices - this stuff isn't available for metas yet - if (ELEM5(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) + if (ELEM5(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { ModifierData *md; - + switch (partype) { case PAR_CURVE: /* curve deform */ md= ED_object_modifier_add(op->reports, bmain, scene, ob, NULL, eModifierType_Curve); @@ -684,15 +675,27 @@ static int parent_set_exec(bContext *C, wmOperator *op) } } CTX_DATA_END; - + DAG_scene_sort(bmain, scene); DAG_ids_flush_update(bmain, 0); WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL); WM_event_add_notifier(C, NC_OBJECT|ND_PARENT, NULL); - - return OPERATOR_FINISHED; + + return 1; +} + +static int parent_set_exec(bContext *C, wmOperator *op) +{ + Object *par= ED_object_active_context(C); + int partype= RNA_enum_get(op->ptr, "type"); + + if(ED_object_parent_set(C, op, par, partype)) + return OPERATOR_FINISHED; + else + return OPERATOR_CANCELLED; } + static int parent_set_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event)) { Object *ob= ED_object_active_context(C); diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 0ccbf9127c6..12f4af3637e 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -1429,3 +1429,278 @@ void OUTLINER_OT_keyingset_remove_selected(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; } + +/* ******************** Parent Drop Operator *********************** */ + +static int parent_drop_exec(bContext *C, wmOperator *op) +{ + Object *par = NULL; + int partype = -1; + char parname[32]; + + partype= RNA_enum_get(op->ptr, "type"); + RNA_string_get(op->ptr, "parent", parname); + par= (Object *)find_id("OB", parname); + + ED_object_parent_set(C, op, par, partype); + + return OPERATOR_FINISHED; +} + +/* Used for drag and drop parenting */ +TreeElement *outliner_dropzone_parent(bContext *C, wmEvent *event, TreeElement *te, float *fmval) +{ + SpaceOops *soops= CTX_wm_space_outliner(C); + TreeStoreElem *tselem= TREESTORE(te); + + 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)) { + /* always makes active object */ + if (te->idcode == ID_OB) { + return te; + } + else { + return NULL; + } + } + } + + /* Not it. Let's look at its children. */ + if ((tselem->flag & TSE_CLOSED)==0 && (te->subtree.first)) { + for (te = te->subtree.first; te; te = te->next) { + TreeElement *te_valid; + te_valid= outliner_dropzone_parent(C, event, te, fmval); + if (te_valid) return te_valid; + } + } + return NULL; +} + +static int parent_drop_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + Object *par= NULL; + Object *ob= NULL; + SpaceOops *soops= CTX_wm_space_outliner(C); + ARegion *ar= CTX_wm_region(C); + Scene *scene= CTX_data_scene(C); + TreeElement *te= NULL; + TreeElement *te_found= 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 */ + for (te= soops->tree.first; te; te= te->next) { + te_found= outliner_dropzone_parent(C, event, te, fmval); + if (te_found) break; + } + + if(te_found) { + RNA_string_set(op->ptr, "parent", te_found->name); + /* Identify parent and child */ + RNA_string_get(op->ptr, "child", childname); + ob= (Object *)find_id("OB", childname); + RNA_string_get(op->ptr, "parent", parname); + par= (Object *)find_id("OB", parname); + + if (ELEM(NULL, ob, par)) { + if (par == NULL) printf("par==NULL\n"); + return OPERATOR_CANCELLED; + } + if (ob == par) { + return OPERATOR_CANCELLED; + } + + /* check dragged object (child) is active */ + if (ob != CTX_data_active_object(C)) + ED_base_object_select(object_in_scene(ob, scene), BA_SELECT); + + if ((par->type != OB_ARMATURE) && (par->type != OB_CURVE) && (par->type != OB_LATTICE)) { + ED_object_parent_set(C, op, par, partype); + } + else { + /* Menu creation */ + uiPopupMenu *pup= uiPupMenuBegin(C, "Set Parent To", ICON_NONE); + uiLayout *layout= uiPupMenuLayout(pup); + + PointerRNA ptr; + + WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop"); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_OBJECT); + /* Cannot use uiItemEnumO()... have multiple properties to set. */ + uiItemFullO(layout, "OUTLINER_OT_parent_drop", "Object", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); + + /* par becomes parent, make the associated menus */ + if (par->type==OB_ARMATURE) { + WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop"); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_ARMATURE); + uiItemFullO(layout, "OUTLINER_OT_parent_drop", "Armature Deform", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); + + WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop"); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_ARMATURE_NAME); + uiItemFullO(layout, "OUTLINER_OT_parent_drop", " With Empty Groups", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); + + WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop"); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_ARMATURE_ENVELOPE); + uiItemFullO(layout, "OUTLINER_OT_parent_drop", " With Envelope Weights", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); + + WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop"); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_ARMATURE_AUTO); + uiItemFullO(layout, "OUTLINER_OT_parent_drop", " With Automatic Weights", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); + + WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop"); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_BONE); + uiItemFullO(layout, "OUTLINER_OT_parent_drop", "Bone", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); + } + else if (par->type==OB_CURVE) { + WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop"); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_CURVE); + uiItemFullO(layout, "OUTLINER_OT_parent_drop", "Curve Deform", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); + + WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop"); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_FOLLOW); + uiItemFullO(layout, "OUTLINER_OT_parent_drop", "Follow Path", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); + + WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop"); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_PATH_CONST); + uiItemFullO(layout, "OUTLINER_OT_parent_drop", "Path Constraint", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); + } + else if (par->type == OB_LATTICE) { + WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop"); + RNA_string_set(&ptr, "parent", parname); + RNA_string_set(&ptr, "child", childname); + RNA_enum_set(&ptr, "type", PAR_LATTICE); + uiItemFullO(layout, "OUTLINER_OT_parent_drop", "Lattice Deform", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0); + } + + uiPupMenuEnd(C, pup); + + return OPERATOR_CANCELLED; + } + } + 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; + + /* 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", ""); +} + +int outliner_dropzone_parent_clear(bContext *C, wmEvent *event, TreeElement *te, float *fmval) +{ + SpaceOops *soops= CTX_wm_space_outliner(C); + TreeStoreElem *tselem= TREESTORE(te); + + /* Check for row */ + if ((fmval[1] > te->ys) && (fmval[1] < (te->ys + UI_UNIT_Y))) { + /* Ignore drop on scene tree elements */ + if ((fmval[0] > te->xs + UI_UNIT_X) && (fmval[0] < te->xend)) { + if ((te->idcode == ID_SCE) && + !ELEM3(tselem->type, TSE_R_LAYER_BASE, TSE_R_LAYER, TSE_R_PASS)) + { + return 0; + } + // Other codes to ignore? + } + + /* Left or right of: (+), first icon, and name */ + if ((fmval[0] < (te->xs + UI_UNIT_X)) || (fmval[0] > te->xend)) { + return 1; + } + else if (te->idcode != ID_OB) { + return 1; + } + + return 0; // ID_OB, but mouse in undefined dropzone. + } + + /* Not this row. Let's look at its children. */ + if ((tselem->flag & TSE_CLOSED)==0 && (te->subtree.first)) { + for (te = te->subtree.first; te; te = te->next) { + if (outliner_dropzone_parent_clear(C, event, te, fmval)) + return 1; + } + } + return 0; +} + +static int parent_clear_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) +{ + Scene *scene= CTX_data_scene(C); + Object *ob= NULL; + char obname[MAX_ID_NAME]; + + RNA_string_get(op->ptr, "dragged_obj", obname); + ob= (Object *)find_id("OB", obname); + + /* check dragged object (child) is active */ + if (ob != CTX_data_active_object(C)) + ED_base_object_select(object_in_scene(ob, scene), BA_SELECT); + + ED_object_parent_clear(C, RNA_enum_get(op->ptr, "type")); + + 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= ED_operator_outliner_active; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* 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", ""); +} diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 3b6b4334880..be33fc2e37a 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -188,6 +188,9 @@ void group_toggle_renderability_cb(struct bContext *C, struct Scene *scene, Tree void item_rename_cb(struct bContext *C, struct Scene *scene, TreeElement *te, struct TreeStoreElem *tsep, struct TreeStoreElem *tselem); +TreeElement *outliner_dropzone_parent(struct bContext *C, struct wmEvent *event, struct TreeElement *te, float *fmval); +int outliner_dropzone_parent_clear(struct bContext *C, struct wmEvent *event, struct TreeElement *te, float *fmval); + /* ...................................................... */ void OUTLINER_OT_item_activate(struct wmOperatorType *ot); @@ -215,6 +218,9 @@ void OUTLINER_OT_keyingset_remove_selected(struct wmOperatorType *ot); void OUTLINER_OT_drivers_add_selected(struct wmOperatorType *ot); void OUTLINER_OT_drivers_delete_selected(struct wmOperatorType *ot); +void OUTLINER_OT_parent_drop(struct wmOperatorType *ot); +void OUTLINER_OT_parent_clear(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 0de26cece93..17434d0f6c9 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -77,6 +77,9 @@ void outliner_operatortypes(void) WM_operatortype_append(OUTLINER_OT_drivers_add_selected); WM_operatortype_append(OUTLINER_OT_drivers_delete_selected); + + WM_operatortype_append(OUTLINER_OT_parent_drop); + WM_operatortype_append(OUTLINER_OT_parent_clear); } void outliner_keymap(wmKeyConfig *keyconf) diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index 673ddaebc5f..5b20c170362 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -50,6 +50,8 @@ #include "BIF_gl.h" +#include "RNA_access.h" + #include "UI_resources.h" #include "UI_view2d.h" @@ -58,6 +60,7 @@ static void outliner_main_area_init(wmWindowManager *wm, ARegion *ar) { + ListBase *lb; wmKeyMap *keymap; UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_LIST, ar->winx, ar->winy); @@ -66,6 +69,88 @@ static void outliner_main_area_init(wmWindowManager *wm, ARegion *ar) keymap= WM_keymap_find(wm->defaultconf, "Outliner", SPACE_OUTLINER, 0); /* don't pass on view2d mask, it's always set with scrollbar space, hide fails */ WM_event_add_keymap_handler_bb(&ar->handlers, keymap, NULL, &ar->winrct); + + /* Add dropboxes */ + lb = WM_dropboxmap_find("Outliner", SPACE_OUTLINER, RGN_TYPE_WINDOW); + WM_event_add_dropbox_handler(&ar->handlers, lb); +} + +static int outliner_parent_drop_poll(bContext *C, wmDrag *drag, 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(drag->type == WM_DRAG_ID) { + ID *id = (ID *)drag->poin; + if( GS(id->name) == ID_OB ) { + /* Ensure item under cursor is valid drop target */ + /* Find object hovered over */ + for(te= soops->tree.first; te; te= te->next) { + TreeElement *te_valid; + te_valid= outliner_dropzone_parent(C, event, te, fmval); + if(te_valid) return 1; + } + } + } + return 0; +} + +static void outliner_parent_drop_copy(wmDrag *drag, wmDropBox *drop) +{ + ID *id = (ID *)drag->poin; + + RNA_string_set(drop->ptr, "child", id->name+2); +} + +static int outliner_parent_clear_poll(bContext *C, wmDrag *drag, 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(drag->type == WM_DRAG_ID) { + ID *id = (ID *)drag->poin; + if( GS(id->name) == ID_OB ) { + //TODO: Check if no parent? + /* Ensure location under cursor is valid dropzone */ + for(te= soops->tree.first; te; te= te->next) { + if(outliner_dropzone_parent_clear(C, event, te, fmval)) return 1; + } + /* Check if mouse cursor is below the tree */ + te= soops->tree.last; + while(((te->flag & TE_LAZY_CLOSED)==0) && (te->subtree.last)) { + te= te->subtree.last; + } + if(fmval[1] < te->ys) return 1; + } + } + return 0; +} + +static void outliner_parent_clear_copy(wmDrag *drag, wmDropBox *drop) +{ + ID *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_string_set(drop->ptr, "type", 0); +} + +/* 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); } static void outliner_main_area_draw(const bContext *C, ARegion *ar) @@ -302,6 +387,7 @@ void ED_spacetype_outliner(void) st->duplicate= outliner_duplicate; st->operatortypes= outliner_operatortypes; st->keymap= outliner_keymap; + st->dropboxes= outliner_dropboxes; /* regions: main window */ art= MEM_callocN(sizeof(ARegionType), "spacetype time region"); |