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
This commit is contained in:
Joshua Leung 2012-01-22 10:20:30 +00:00
parent c8cff5e1c4
commit 98fd7c2948
6 changed files with 454 additions and 57 deletions

@ -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

@ -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);

@ -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", "");
}

@ -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);

@ -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)

@ -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");