Outliner: DragDrop objects to groups
authorCampbell Barton <ideasman42@gmail.com>
Fri, 6 Feb 2015 07:10:46 +0000 (18:10 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Fri, 6 Feb 2015 08:03:09 +0000 (19:03 +1100)
Support drag&drop objects to groups in the outliner.

D989 by @lichtwerk

source/blender/blenkernel/BKE_group.h
source/blender/blenkernel/intern/group.c
source/blender/editors/object/object_group.c
source/blender/editors/space_outliner/outliner_edit.c
source/blender/editors/space_outliner/outliner_intern.h
source/blender/editors/space_outliner/outliner_ops.c
source/blender/editors/space_outliner/space_outliner.c

index f528fe8c7f9a58cad743db2f7290230b623c78f6..820e1ea14946ac80eb1554f666a618b6e07608a1 100644 (file)
@@ -50,6 +50,7 @@ bool          BKE_group_object_add(struct Group *group, struct Object *ob, struc
 bool          BKE_group_object_unlink(struct Group *group, struct Object *ob, struct Scene *scene, struct Base *base);
 struct Group *BKE_group_object_find(struct Group *group, struct Object *ob);
 bool          BKE_group_object_exists(struct Group *group, struct Object *ob);
+bool          BKE_group_object_cyclic_check(struct Main *bmain, struct Object *object, struct Group *group);
 bool          BKE_group_is_animated(struct Group *group, struct Object *parent);
 
 void          BKE_group_tag_recalc(struct Group *group);
index 1f9cf2e11b7f1d984f2acb34bbf3af0f614fccab..96eb95968f3c0b4ad4bf0fbe13b378814ce43fc2 100644 (file)
@@ -219,6 +219,43 @@ static int group_object_unlink_internal(Group *group, Object *ob)
        return removed;
 }
 
+static bool group_object_cyclic_check_internal(Object *object, Group *group) {
+
+       if (object->dup_group) {
+               Group *dup_group = object->dup_group;
+               if ((dup_group->id.flag & LIB_DOIT) == 0) {
+                       /* Cycle already exists in groups, let's prevent further crappyness */
+                       return true;
+               }
+               /* flag the object to identify cyclic dependencies in further dupli groups */
+               dup_group->id.flag &= ~LIB_DOIT;
+
+               if (dup_group == group)
+                       return true;
+               else {
+                       GroupObject *gob;
+                       for (gob = dup_group->gobject.first; gob; gob = gob->next) {
+                               if (group_object_cyclic_check_internal(gob->ob, group)) {
+                                       return true;
+                               }
+                       }
+               }
+
+               /* un-flag the object, it's allowed to have the same group multiple times in parallel */
+               dup_group->id.flag |= LIB_DOIT;
+       }
+
+       return false;
+}
+
+bool BKE_group_object_cyclic_check(Main *bmain, Object *object, Group *group)
+{
+       /* first flag all groups */
+       BKE_main_id_tag_listbase(&bmain->group, true);
+
+       return group_object_cyclic_check_internal(object, group);
+}
+
 bool BKE_group_object_unlink(Group *group, Object *object, Scene *scene, Base *base)
 {
        if (group_object_unlink_internal(group, object)) {
index 20e2e22cdf81a5757a2ae9ce5a2fdafa50a8139f..3c43f2729bdf36d4a406b9acccc35d4c242c945a 100644 (file)
 
 /********************* 3d view operators ***********************/
 
-static bool group_link_early_exit_check(Group *group, Object *object)
-{
-       GroupObject *group_object;
-
-       for (group_object = group->gobject.first; group_object; group_object = group_object->next) {
-               if (group_object->ob == object) {
-                       return true;
-               }
-       }
-
-       return false;
-}
-
-static bool check_object_instances_group_recursive(Object *object, Group *group)
-{
-       if (object->dup_group) {
-               Group *dup_group = object->dup_group;
-               if ((dup_group->id.flag & LIB_DOIT) == 0) {
-                       /* Cycle already exists in groups, let's prevent further crappyness */
-                       return true;
-               }
-               /* flag the object to identify cyclic dependencies in further dupli groups */
-               dup_group->id.flag &= ~LIB_DOIT;
-               
-               if (dup_group == group)
-                       return true;
-               else {
-                       GroupObject *gob;
-                       for (gob = dup_group->gobject.first; gob; gob = gob->next) {
-                               if (check_object_instances_group_recursive(gob->ob, group))
-                                       return true;
-                       }
-               }
-               
-               /* un-flag the object, it's allowed to have the same group multiple times in parallel */
-               dup_group->id.flag |= LIB_DOIT;
-       }
-       
-       return false;
-}
-
 /* can be called with C == NULL */
 static EnumPropertyItem *group_object_active_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
 {
@@ -185,15 +144,12 @@ static int objects_add_active_exec(bContext *C, wmOperator *op)
                if (!BKE_group_object_exists(group, ob))
                        continue;
 
-               /* for recursive check */
-               BKE_main_id_tag_listbase(&bmain->group, true);
-
                CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
                {
-                       if (group_link_early_exit_check(group, base->object))
+                       if (BKE_group_object_exists(group, base->object))
                                continue;
 
-                       if (!check_object_instances_group_recursive(base->object, group)) {
+                       if (!BKE_group_object_cyclic_check(bmain, base->object, group)) {
                                BKE_group_object_add(group, base->object, scene, base);
                                updated = true;
                        }
@@ -486,7 +442,7 @@ static int group_link_exec(bContext *C, wmOperator *op)
         * we could sckip all the dependency check and just consider
         * operator is finished.
         */
-       if (group_link_early_exit_check(group, ob)) {
+       if (BKE_group_object_exists(group, ob)) {
                return OPERATOR_FINISHED;
        }
 
@@ -495,8 +451,7 @@ static int group_link_exec(bContext *C, wmOperator *op)
         * It is also  bad idea to add object to group which is in group which
         * contains our current object.
         */
-       BKE_main_id_tag_listbase(&bmain->group, true);
-       if (check_object_instances_group_recursive(ob, group)) {
+       if (BKE_group_object_cyclic_check(bmain, ob, group)) {
                BKE_report(op->reports, RPT_ERROR, "Could not add the group because of dependency cycle detected");
                return OPERATOR_CANCELLED;
        }
index 9514a04e46f6f13ee5e81b2948b7eb43d73e5c33..d17ab33d0fa06bbfd8c53ed0c1fc49945f6f0ecd 100644 (file)
@@ -50,6 +50,7 @@
 #include "BKE_report.h"
 #include "BKE_scene.h"
 #include "BKE_material.h"
+#include "BKE_group.h"
 
 #include "ED_object.h"
 #include "ED_screen.h"
@@ -1833,3 +1834,65 @@ void OUTLINER_OT_material_drop(wmOperatorType *ot)
        RNA_def_string(ot->srna, "material", "Material", MAX_ID_NAME, "Material", "Target Material");
 }
 
+static int group_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+       Main *bmain = CTX_data_main(C);
+       Group *group = NULL;
+       Object *ob = NULL;
+       Scene *scene = CTX_data_scene(C);
+       SpaceOops *soops = CTX_wm_space_outliner(C);
+       ARegion *ar = CTX_wm_region(C);
+       TreeElement *te = NULL;
+       char ob_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) {
+               group = (Group *)BKE_libblock_find_name(ID_GR, te->name);
+
+               RNA_string_get(op->ptr, "object", ob_name);
+               ob = (Object *)BKE_libblock_find_name(ID_OB, ob_name);
+
+               if (ELEM(NULL, group, ob)) {
+                       return OPERATOR_CANCELLED;
+               }
+               if (BKE_group_object_exists(group, ob)) {
+                       return OPERATOR_FINISHED;
+               }
+
+               if (BKE_group_object_cyclic_check(bmain, ob, group)) {
+                       BKE_report(op->reports, RPT_ERROR, "Could not add the group because of dependency cycle detected");
+                       return OPERATOR_CANCELLED;
+               }
+
+               BKE_group_object_add(group, ob, scene, NULL);
+               WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+
+               return OPERATOR_FINISHED;
+       }
+
+       return OPERATOR_CANCELLED;
+}
+
+void OUTLINER_OT_group_link(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Link Object to Group";
+       ot->description = "Link Object to Group in Outliner";
+       ot->idname = "OUTLINER_OT_group_link";
+
+       /* api callbacks */
+       ot->invoke = group_link_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");
+}
index c7ac561734a1375d7ef63c70dc8ae13751894a73..26283dfdd832437736e0c024d0bf0c64a80787c6 100644 (file)
@@ -237,6 +237,7 @@ 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_group_link(struct wmOperatorType *ot);
 
 /* outliner_tools.c ---------------------------------------------- */
 
index 4f13454ef340dacb9996aeaef48dac465202ca89..d54ae3f22a719c53460de99243d7caf08a59ae96 100644 (file)
@@ -77,6 +77,7 @@ void outliner_operatortypes(void)
        WM_operatortype_append(OUTLINER_OT_parent_clear);
        WM_operatortype_append(OUTLINER_OT_scene_drop);
        WM_operatortype_append(OUTLINER_OT_material_drop);
+       WM_operatortype_append(OUTLINER_OT_group_link);
 }
 
 void outliner_keymap(wmKeyConfig *keyconf)
index 171a11c114e09deee0361220d25b4304c57ce50d..df5a1c8d8c6261a19f563fb458c2296ab316791c 100644 (file)
@@ -230,6 +230,30 @@ static void outliner_material_drop_copy(wmDrag *drag, wmDropBox *drop)
        RNA_string_set(drop->ptr, "material", id->name + 2);
 }
 
+static int outliner_group_link_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);
+                       return (te && te->idcode == ID_GR && TREESTORE(te)->type == 0);
+               }
+       }
+       return 0;
+}
+
+static void outliner_group_link_copy(wmDrag *drag, wmDropBox *drop)
+{
+       ID *id = drag->poin;
+       RNA_string_set(drop->ptr, "object", id->name + 2);
+}
+
 /* region dropbox definition */
 static void outliner_dropboxes(void)
 {
@@ -239,6 +263,7 @@ static void outliner_dropboxes(void)
        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_group_link", outliner_group_link_poll, outliner_group_link_copy);
 }
 
 static void outliner_main_area_draw(const bContext *C, ARegion *ar)