Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Leung <aligorith@gmail.com>2012-01-22 14:20:30 +0400
committerJoshua Leung <aligorith@gmail.com>2012-01-22 14:20:30 +0400
commit98fd7c294881d0452b0c9eeb3e8b3a6b604567d9 (patch)
tree48998273383bc95c369a8ce67cdaa2825c5b0192 /source/blender/editors/space_outliner
parentc8cff5e1c4a8a5b61f11ee708460c994903f14e7 (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/blender/editors/space_outliner')
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.c275
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.h6
-rw-r--r--source/blender/editors/space_outliner/outliner_ops.c3
-rw-r--r--source/blender/editors/space_outliner/space_outliner.c86
4 files changed, 370 insertions, 0 deletions
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");