Outliner: DragDrop objects to groups
Support drag&drop objects to groups in the outliner. D989 by @lichtwerk
This commit is contained in:
parent
b752597805
commit
61c66a996c
|
@ -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);
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -61,47 +61,6 @@
|
|||
|
||||
/********************* 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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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 ---------------------------------------------- */
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue