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:
parent
c8cff5e1c4
commit
98fd7c2948
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user