Merge branch 'blender2.7'
[blender.git] / source / blender / editors / space_outliner / outliner_dragdrop.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2004 Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Joshua Leung
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/space_outliner/outliner_dragdrop.c
29  *  \ingroup spoutliner
30  */
31
32 #include <string.h>
33
34 #include "MEM_guardedalloc.h"
35
36 #include "DNA_collection_types.h"
37 #include "DNA_material_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_space_types.h"
40
41 #include "BLI_listbase.h"
42 #include "BLI_string.h"
43
44 #include "BLT_translation.h"
45
46 #include "BKE_collection.h"
47 #include "BKE_context.h"
48 #include "BKE_layer.h"
49 #include "BKE_library.h"
50 #include "BKE_main.h"
51 #include "BKE_material.h"
52 #include "BKE_object.h"
53 #include "BKE_report.h"
54 #include "BKE_scene.h"
55
56 #include "DEG_depsgraph.h"
57 #include "DEG_depsgraph_build.h"
58
59 #include "ED_object.h"
60 #include "ED_outliner.h"
61 #include "ED_screen.h"
62
63 #include "UI_interface.h"
64 #include "UI_resources.h"
65 #include "UI_view2d.h"
66
67 #include "GPU_state.h"
68
69 #include "RNA_access.h"
70 #include "RNA_define.h"
71 #include "RNA_enum_types.h"
72
73 #include "WM_api.h"
74 #include "WM_types.h"
75
76 #include "outliner_intern.h"
77
78 /* ******************** Drop Target Find *********************** */
79
80 static TreeElement *outliner_dropzone_element(TreeElement *te, const float fmval[2], const bool children)
81 {
82         if ((fmval[1] > te->ys) && (fmval[1] < (te->ys + UI_UNIT_Y))) {
83                 /* name and first icon */
84                 if ((fmval[0] > te->xs + UI_UNIT_X) && (fmval[0] < te->xend))
85                         return te;
86         }
87         /* Not it.  Let's look at its children. */
88         if (children && (TREESTORE(te)->flag & TSE_CLOSED) == 0 && (te->subtree.first)) {
89                 for (te = te->subtree.first; te; te = te->next) {
90                         TreeElement *te_valid = outliner_dropzone_element(te, fmval, children);
91                         if (te_valid)
92                                 return te_valid;
93                 }
94         }
95         return NULL;
96 }
97
98 /* Find tree element to drop into. */
99 static TreeElement *outliner_dropzone_find(const SpaceOops *soops, const float fmval[2], const bool children)
100 {
101         TreeElement *te;
102
103         for (te = soops->tree.first; te; te = te->next) {
104                 TreeElement *te_valid = outliner_dropzone_element(te, fmval, children);
105                 if (te_valid)
106                         return te_valid;
107         }
108         return NULL;
109 }
110
111 static TreeElement *outliner_drop_find(bContext *C, const wmEvent *event)
112 {
113         ARegion *ar = CTX_wm_region(C);
114         SpaceOops *soops = CTX_wm_space_outliner(C);
115         float fmval[2];
116         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
117
118         return outliner_dropzone_find(soops, fmval, true);
119 }
120
121 static ID *outliner_ID_drop_find(bContext *C, const wmEvent *event, short idcode)
122 {
123         TreeElement *te = outliner_drop_find(C, event);
124         TreeStoreElem *tselem = (te) ? TREESTORE(te) : NULL;
125
126         if (te && te->idcode == idcode && tselem->type == 0) {
127                 return tselem->id;
128         }
129         else {
130                 return NULL;
131         }
132 }
133
134 /* Find tree element to drop into, with additional before and after reorder support. */
135 static TreeElement *outliner_drop_insert_find(
136         bContext *C, const wmEvent *event,
137         TreeElementInsertType *r_insert_type)
138 {
139         SpaceOops *soops = CTX_wm_space_outliner(C);
140         ARegion *ar = CTX_wm_region(C);
141         TreeElement *te_hovered;
142         float view_mval[2];
143
144         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]);
145         te_hovered = outliner_find_item_at_y(soops, &soops->tree, view_mval[1]);
146
147         if (te_hovered) {
148                 /* mouse hovers an element (ignoring x-axis), now find out how to insert the dragged item exactly */
149                 const float margin = UI_UNIT_Y * (1.0f / 4);
150
151                 if (view_mval[1] < (te_hovered->ys + margin)) {
152                         if (TSELEM_OPEN(TREESTORE(te_hovered), soops)) {
153                                 /* inserting after a open item means we insert into it, but as first child */
154                                 if (BLI_listbase_is_empty(&te_hovered->subtree)) {
155                                         *r_insert_type = TE_INSERT_INTO;
156                                         return te_hovered;
157                                 }
158                                 else {
159                                         *r_insert_type = TE_INSERT_BEFORE;
160                                         return te_hovered->subtree.first;
161                                 }
162                         }
163                         else {
164                                 *r_insert_type = TE_INSERT_AFTER;
165                                 return te_hovered;
166                         }
167                 }
168                 else if (view_mval[1] > (te_hovered->ys + (3 * margin))) {
169                         *r_insert_type = TE_INSERT_BEFORE;
170                         return te_hovered;
171                 }
172                 else {
173                         *r_insert_type = TE_INSERT_INTO;
174                         return te_hovered;
175                 }
176         }
177         else {
178                 /* mouse doesn't hover any item (ignoring x-axis), so it's either above list bounds or below. */
179                 TreeElement *first = soops->tree.first;
180                 TreeElement *last = soops->tree.last;
181
182                 if (view_mval[1] < last->ys) {
183                         *r_insert_type = TE_INSERT_AFTER;
184                         return last;
185                 }
186                 else if (view_mval[1] > (first->ys + UI_UNIT_Y)) {
187                         *r_insert_type = TE_INSERT_BEFORE;
188                         return first;
189                 }
190                 else {
191                         BLI_assert(0);
192                         return NULL;
193                 }
194         }
195 }
196
197 static Collection *outliner_collection_from_tree_element_and_parents(TreeElement *te, TreeElement **r_te)
198 {
199         while (te != NULL) {
200                 Collection *collection = outliner_collection_from_tree_element(te);
201                 if (collection) {
202                         *r_te = te;
203                         return collection;
204                 }
205                 te = te->parent;
206         }
207         return NULL;
208 }
209
210 static TreeElement *outliner_drop_insert_collection_find(
211         bContext *C, const wmEvent *event,
212         TreeElementInsertType *r_insert_type)
213 {
214         TreeElement *te = outliner_drop_insert_find(C, event, r_insert_type);
215         if (!te) return NULL;
216
217         TreeElement *collection_te;
218         Collection *collection = outliner_collection_from_tree_element_and_parents(te, &collection_te);
219         if (!collection) return NULL;
220
221         if (collection_te != te) {
222                 *r_insert_type = TE_INSERT_INTO;
223         }
224
225         /* We can't insert before/after master collection. */
226         if (collection->flag & COLLECTION_IS_MASTER) {
227                 *r_insert_type = TE_INSERT_INTO;
228         }
229
230         return collection_te;
231 }
232
233 /* ******************** Parent Drop Operator *********************** */
234
235 static bool parent_drop_allowed(SpaceOops *soops, TreeElement *te, Object *potential_child)
236 {
237         TreeStoreElem *tselem = TREESTORE(te);
238         if (te->idcode != ID_OB || tselem->type != 0) {
239                 return false;
240         }
241
242         Object *potential_parent = (Object *)tselem->id;
243
244         if (potential_parent == potential_child) return false;
245         if (BKE_object_is_child_recursive(potential_child, potential_parent)) return false;
246         if (potential_parent == potential_child->parent) return false;
247
248         /* check that parent/child are both in the same scene */
249         Scene *scene = (Scene *)outliner_search_back(soops, te, ID_SCE);
250
251         /* currently outliner organized in a way that if there's no parent scene
252                 * element for object it means that all displayed objects belong to
253                 * active scene and parenting them is allowed (sergey)
254                 */
255         if (scene) {
256                 for (ViewLayer *view_layer = scene->view_layers.first;
257                      view_layer;
258                      view_layer = view_layer->next)
259                 {
260                         if (BKE_view_layer_base_find(view_layer, potential_child)) {
261                                 return true;
262                         }
263                 }
264                 return false;
265         }
266         else {
267                 return true;
268         }
269 }
270
271 static bool allow_parenting_without_modifier_key(SpaceOops *soops)
272 {
273         switch (soops->outlinevis) {
274                 case SO_VIEW_LAYER:
275                         return soops->filter & SO_FILTER_NO_COLLECTION;
276                 case SO_SCENES:
277                         return true;
278                 default:
279                         return false;
280         }
281 }
282
283 static bool parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event, const char **UNUSED(tooltip))
284 {
285         SpaceOops *soops = CTX_wm_space_outliner(C);
286
287         bool changed = outliner_flag_set(&soops->tree, TSE_DRAG_ANY, false);
288         if (changed) ED_region_tag_redraw_no_rebuild(CTX_wm_region(C));
289
290         Object *potential_child = (Object *)WM_drag_ID(drag, ID_OB);
291         if (!potential_child) return false;
292
293         if (!allow_parenting_without_modifier_key(soops)) {
294                 if (!event->shift) return false;
295         }
296
297         TreeElement *te = outliner_drop_find(C, event);
298         if (!te) return false;
299
300         if (parent_drop_allowed(soops, te, potential_child)) {
301                 TREESTORE(te)->flag |= TSE_DRAG_INTO;
302                 ED_region_tag_redraw_no_rebuild(CTX_wm_region(C));
303                 return true;
304         }
305
306         return false;
307 }
308
309 static int parent_drop_exec(bContext *C, wmOperator *op)
310 {
311         Object *par = NULL, *ob = NULL;
312         Main *bmain = CTX_data_main(C);
313         Scene *scene = CTX_data_scene(C);
314         int partype = -1;
315         char parname[MAX_NAME], childname[MAX_NAME];
316
317         partype = RNA_enum_get(op->ptr, "type");
318         RNA_string_get(op->ptr, "parent", parname);
319         par = (Object *)BKE_libblock_find_name(bmain, ID_OB, parname);
320         RNA_string_get(op->ptr, "child", childname);
321         ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, childname);
322
323         if (ID_IS_LINKED(ob)) {
324                 BKE_report(op->reports, RPT_INFO, "Can't edit library linked object");
325                 return OPERATOR_CANCELLED;
326         }
327
328         ED_object_parent_set(op->reports, C, scene, ob, par, partype, false, false, NULL);
329
330         DEG_relations_tag_update(bmain);
331         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
332         WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL);
333
334         return OPERATOR_FINISHED;
335 }
336
337 static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
338 {
339         Main *bmain = CTX_data_main(C);
340         SpaceOops *soops = CTX_wm_space_outliner(C);
341         TreeElement *te = outliner_drop_find(C, event);
342         TreeStoreElem *tselem = te ? TREESTORE(te) : NULL;
343
344         if (!(te && te->idcode == ID_OB && tselem->type == 0)) {
345                 return OPERATOR_CANCELLED;
346         }
347
348         Object *par = (Object *)tselem->id;
349         Object *ob = (Object *)WM_drag_ID_from_event(event, ID_OB);
350
351         if (ELEM(NULL, ob, par)) {
352                 return OPERATOR_CANCELLED;
353         }
354         if (ob == par) {
355                 return OPERATOR_CANCELLED;
356         }
357         if (ID_IS_LINKED(ob)) {
358                 BKE_report(op->reports, RPT_INFO, "Can't edit library linked object");
359                 return OPERATOR_CANCELLED;
360         }
361
362         char childname[MAX_NAME];
363         char parname[MAX_NAME];
364         STRNCPY(childname, ob->id.name + 2);
365         STRNCPY(parname, par->id.name + 2);
366         RNA_string_set(op->ptr, "child", childname);
367         RNA_string_set(op->ptr, "parent", parname);
368
369         Scene *scene = (Scene *)outliner_search_back(soops, te, ID_SCE);
370
371         if (scene == NULL) {
372                 /* currently outlier organized in a way, that if there's no parent scene
373                  * element for object it means that all displayed objects belong to
374                  * active scene and parenting them is allowed (sergey)
375                  */
376
377                 scene = CTX_data_scene(C);
378         }
379
380         if ((par->type != OB_ARMATURE) && (par->type != OB_CURVE) && (par->type != OB_LATTICE)) {
381                 int partype = 0;
382                 if (ED_object_parent_set(op->reports, C, scene, ob, par, partype, false, false, NULL)) {
383                         DEG_relations_tag_update(bmain);
384                         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
385                         WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL);
386                 }
387         }
388         else {
389                 /* Menu creation */
390                 wmOperatorType *ot = WM_operatortype_find("OUTLINER_OT_parent_drop", false);
391                 uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Set Parent To"), ICON_NONE);
392                 uiLayout *layout = UI_popup_menu_layout(pup);
393                 PointerRNA ptr;
394
395                 /* Cannot use uiItemEnumO()... have multiple properties to set. */
396                 uiItemFullO_ptr(layout, ot, IFACE_("Object"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
397                 RNA_string_set(&ptr, "parent", parname);
398                 RNA_string_set(&ptr, "child", childname);
399                 RNA_enum_set(&ptr, "type", PAR_OBJECT);
400
401                 /* par becomes parent, make the associated menus */
402                 if (par->type == OB_ARMATURE) {
403                         uiItemFullO_ptr(layout, ot, IFACE_("Armature Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
404                         RNA_string_set(&ptr, "parent", parname);
405                         RNA_string_set(&ptr, "child", childname);
406                         RNA_enum_set(&ptr, "type", PAR_ARMATURE);
407
408                         uiItemFullO_ptr(layout, ot, IFACE_("   With Empty Groups"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
409                         RNA_string_set(&ptr, "parent", parname);
410                         RNA_string_set(&ptr, "child", childname);
411                         RNA_enum_set(&ptr, "type", PAR_ARMATURE_NAME);
412
413                         uiItemFullO_ptr(layout, ot, IFACE_("   With Envelope Weights"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
414                         RNA_string_set(&ptr, "parent", parname);
415                         RNA_string_set(&ptr, "child", childname);
416                         RNA_enum_set(&ptr, "type", PAR_ARMATURE_ENVELOPE);
417
418                         uiItemFullO_ptr(layout, ot, IFACE_("   With Automatic Weights"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
419                         RNA_string_set(&ptr, "parent", parname);
420                         RNA_string_set(&ptr, "child", childname);
421                         RNA_enum_set(&ptr, "type", PAR_ARMATURE_AUTO);
422
423                         uiItemFullO_ptr(layout, ot, IFACE_("Bone"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
424                         RNA_string_set(&ptr, "parent", parname);
425                         RNA_string_set(&ptr, "child", childname);
426                         RNA_enum_set(&ptr, "type", PAR_BONE);
427                 }
428                 else if (par->type == OB_CURVE) {
429                         uiItemFullO_ptr(layout, ot, IFACE_("Curve Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
430                         RNA_string_set(&ptr, "parent", parname);
431                         RNA_string_set(&ptr, "child", childname);
432                         RNA_enum_set(&ptr, "type", PAR_CURVE);
433
434                         uiItemFullO_ptr(layout, ot, IFACE_("Follow Path"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
435                         RNA_string_set(&ptr, "parent", parname);
436                         RNA_string_set(&ptr, "child", childname);
437                         RNA_enum_set(&ptr, "type", PAR_FOLLOW);
438
439                         uiItemFullO_ptr(layout, ot, IFACE_("Path Constraint"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
440                         RNA_string_set(&ptr, "parent", parname);
441                         RNA_string_set(&ptr, "child", childname);
442                         RNA_enum_set(&ptr, "type", PAR_PATH_CONST);
443                 }
444                 else if (par->type == OB_LATTICE) {
445                         uiItemFullO_ptr(layout, ot, IFACE_("Lattice Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
446                         RNA_string_set(&ptr, "parent", parname);
447                         RNA_string_set(&ptr, "child", childname);
448                         RNA_enum_set(&ptr, "type", PAR_LATTICE);
449                 }
450
451                 UI_popup_menu_end(C, pup);
452
453                 return OPERATOR_INTERFACE;
454         }
455
456         return OPERATOR_FINISHED;
457 }
458
459 void OUTLINER_OT_parent_drop(wmOperatorType *ot)
460 {
461         /* identifiers */
462         ot->name = "Drop to Set Parent";
463         ot->description = "Drag to parent in Outliner";
464         ot->idname = "OUTLINER_OT_parent_drop";
465
466         /* api callbacks */
467         ot->invoke = parent_drop_invoke;
468         ot->exec = parent_drop_exec;
469
470         ot->poll = ED_operator_outliner_active;
471
472         /* flags */
473         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
474
475         /* properties */
476         RNA_def_string(ot->srna, "child", "Object", MAX_NAME, "Child", "Child Object");
477         RNA_def_string(ot->srna, "parent", "Object", MAX_NAME, "Parent", "Parent Object");
478         RNA_def_enum(ot->srna, "type", prop_make_parent_types, 0, "Type", "");
479 }
480
481 /* ******************** Parent Clear Operator *********************** */
482
483 static bool parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event, const char **UNUSED(tooltip))
484 {
485         SpaceOops *soops = CTX_wm_space_outliner(C);
486
487         if (!allow_parenting_without_modifier_key(soops)) {
488                 if (!event->shift) return false;
489         }
490
491         Object *ob = (Object *)WM_drag_ID(drag, ID_OB);
492         if (!ob) return false;
493         if (!ob->parent) return false;
494
495         TreeElement *te = outliner_drop_find(C, event);
496         if (te) {
497                 TreeStoreElem *tselem = TREESTORE(te);
498                 ID *id = tselem->id;
499                 if (!id) return true;
500
501                 switch (GS(id->name)) {
502                         case ID_OB:
503                                 return ELEM(tselem->type, TSE_MODIFIER_BASE, TSE_CONSTRAINT_BASE);
504                         case ID_GR:
505                                 return event->shift;
506                         default:
507                                 return true;
508                 }
509         }
510         else {
511                 return true;
512         }
513 }
514
515 static int parent_clear_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
516 {
517         Main *bmain = CTX_data_main(C);
518         Object *ob = (Object *)WM_drag_ID_from_event(event, ID_OB);
519
520         if (ob == NULL) {
521                 return OPERATOR_CANCELLED;
522         }
523
524         ED_object_parent_clear(ob, 0);
525
526         DEG_relations_tag_update(bmain);
527         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
528         WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL);
529         return OPERATOR_FINISHED;
530 }
531
532 void OUTLINER_OT_parent_clear(wmOperatorType *ot)
533 {
534         /* identifiers */
535         ot->name = "Drop to Clear Parent";
536         ot->description = "Drag to clear parent in Outliner";
537         ot->idname = "OUTLINER_OT_parent_clear";
538
539         /* api callbacks */
540         ot->invoke = parent_clear_invoke;
541
542         ot->poll = ED_operator_outliner_active;
543
544         /* flags */
545         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
546 }
547
548 /* ******************** Scene Drop Operator *********************** */
549
550 static bool scene_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event, const char **UNUSED(tooltip))
551 {
552         /* Ensure item under cursor is valid drop target */
553         Object *ob = (Object *)WM_drag_ID(drag, ID_OB);
554         return (ob && (outliner_ID_drop_find(C, event, ID_SCE) != NULL));
555 }
556
557 static int scene_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
558 {
559         Main *bmain = CTX_data_main(C);
560         Scene *scene = (Scene *)outliner_ID_drop_find(C, event, ID_SCE);
561         Object *ob = (Object *)WM_drag_ID_from_event(event, ID_OB);
562
563         if (ELEM(NULL, ob, scene) || ID_IS_LINKED(scene)) {
564                 return OPERATOR_CANCELLED;
565         }
566
567         if (BKE_scene_has_object(scene, ob)) {
568                 return OPERATOR_CANCELLED;
569         }
570
571         Collection *collection;
572         if (scene != CTX_data_scene(C)) {
573                 /* when linking to an inactive scene link to the master collection */
574                 collection = BKE_collection_master(scene);
575         }
576         else {
577                 collection = CTX_data_collection(C);
578         }
579
580         BKE_collection_object_add(bmain, collection, ob);
581
582         for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
583                 Base *base = BKE_view_layer_base_find(view_layer, ob);
584                 if (base) {
585                         ED_object_base_select(base, BA_SELECT);
586                 }
587         }
588
589         DEG_relations_tag_update(bmain);
590
591         DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
592         WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, scene);
593
594         return OPERATOR_FINISHED;
595 }
596
597 void OUTLINER_OT_scene_drop(wmOperatorType *ot)
598 {
599         /* identifiers */
600         ot->name = "Drop Object to Scene";
601         ot->description = "Drag object to scene in Outliner";
602         ot->idname = "OUTLINER_OT_scene_drop";
603
604         /* api callbacks */
605         ot->invoke = scene_drop_invoke;
606
607         ot->poll = ED_operator_outliner_active;
608
609         /* flags */
610         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
611 }
612
613 /* ******************** Material Drop Operator *********************** */
614
615 static bool material_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event, const char **UNUSED(tooltip))
616 {
617         /* Ensure item under cursor is valid drop target */
618         Material *ma = (Material *)WM_drag_ID(drag, ID_MA);
619         return (ma && (outliner_ID_drop_find(C, event, ID_OB) != NULL));
620 }
621
622 static int material_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
623 {
624         Main *bmain = CTX_data_main(C);
625         Object *ob = (Object *)outliner_ID_drop_find(C, event, ID_OB);
626         Material *ma = (Material *)WM_drag_ID_from_event(event, ID_MA);
627
628         if (ELEM(NULL, ob, ma)) {
629                 return OPERATOR_CANCELLED;
630         }
631
632         assign_material(bmain, ob, ma, ob->totcol + 1, BKE_MAT_ASSIGN_USERPREF);
633
634         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, CTX_wm_view3d(C));
635         WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma);
636
637         return OPERATOR_FINISHED;
638 }
639
640 void OUTLINER_OT_material_drop(wmOperatorType *ot)
641 {
642         /* identifiers */
643         ot->name = "Drop Material on Object";
644         ot->description = "Drag material to object in Outliner";
645         ot->idname = "OUTLINER_OT_material_drop";
646
647         /* api callbacks */
648         ot->invoke = material_drop_invoke;
649
650         ot->poll = ED_operator_outliner_active;
651
652         /* flags */
653         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
654 }
655
656 /* ******************** Collection Drop Operator *********************** */
657
658 typedef struct CollectionDrop {
659         Collection *from;
660         Collection *to;
661
662         TreeElement *te;
663         TreeElementInsertType insert_type;
664 } CollectionDrop;
665
666 static Collection *collection_parent_from_ID(ID *id)
667 {
668         /* Can't change linked parent collections. */
669         if (!id || ID_IS_LINKED(id)) {
670                 return NULL;
671         }
672
673         /* Also support dropping into/from scene collection. */
674         if (GS(id->name) == ID_SCE) {
675                 return ((Scene *)id)->master_collection;
676         }
677         else if (GS(id->name) == ID_GR) {
678                 return (Collection *)id;
679         }
680
681         return NULL;
682 }
683
684 static bool collection_drop_init(bContext *C, wmDrag *drag, const wmEvent *event, CollectionDrop *data)
685 {
686         SpaceOops *soops = CTX_wm_space_outliner(C);
687
688         /* Get collection to drop into. */
689         TreeElementInsertType insert_type;
690         TreeElement *te = outliner_drop_insert_collection_find(C, event, &insert_type);
691         if (!te) {
692                 return false;
693         }
694
695         Collection *to_collection = outliner_collection_from_tree_element(te);
696         if (ID_IS_LINKED(to_collection)) {
697                 return false;
698         }
699
700         /* Get drag datablocks. */
701         if (drag->type != WM_DRAG_ID) {
702                 return false;
703         }
704
705         wmDragID *drag_id = drag->ids.first;
706         if (drag_id == NULL) {
707                 return false;
708         }
709
710         ID *id = drag_id->id;
711         if (!(id && ELEM(GS(id->name), ID_GR, ID_OB))) {
712                 return false;
713         }
714
715         /* Get collection to drag out of. */
716         ID *parent = drag_id->from_parent;
717         Collection *from_collection = collection_parent_from_ID(parent);
718         if (event->ctrl || soops->outlinevis == SO_SCENES) {
719                 from_collection = NULL;
720         }
721
722         /* Get collections. */
723         if (GS(id->name) == ID_GR) {
724                 if (id == &to_collection->id) {
725                         return false;
726                 }
727         }
728         else {
729                 insert_type = TE_INSERT_INTO;
730         }
731
732         data->from = from_collection;
733         data->to = to_collection;
734         data->te = te;
735         data->insert_type = insert_type;
736
737         return true;
738 }
739
740 static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event, const char **tooltip)
741 {
742         SpaceOops *soops = CTX_wm_space_outliner(C);
743         ARegion *ar = CTX_wm_region(C);
744         bool changed = outliner_flag_set(&soops->tree, TSE_HIGHLIGHTED | TSE_DRAG_ANY, false);
745
746         CollectionDrop data;
747         if (!event->shift && collection_drop_init(C, drag, event, &data)) {
748                 TreeElement *te = data.te;
749                 TreeStoreElem *tselem = TREESTORE(te);
750                 if (!data.from || event->ctrl) {
751                         tselem->flag |= TSE_DRAG_INTO;
752                         changed = true;
753                         *tooltip = IFACE_("Link inside Collection");
754                 }
755                 else {
756                         switch (data.insert_type) {
757                                 case TE_INSERT_BEFORE:
758                                         tselem->flag |= TSE_DRAG_BEFORE;
759                                         changed = true;
760                                         if (te->prev && outliner_is_collection_tree_element(te->prev)) {
761                                                 *tooltip = TIP_("Move between collections");
762                                         }
763                                         else {
764                                                 *tooltip = TIP_("Move before collection");
765                                         }
766                                         break;
767                                 case TE_INSERT_AFTER:
768                                         tselem->flag |= TSE_DRAG_AFTER;
769                                         changed = true;
770                                         if (te->next && outliner_is_collection_tree_element(te->next)) {
771                                                 *tooltip = TIP_("Move between collections");
772                                         }
773                                         else {
774                                                 *tooltip = TIP_("Move after collection");
775                                         }
776                                         break;
777                                 case TE_INSERT_INTO:
778                                         tselem->flag |= TSE_DRAG_INTO;
779                                         changed = true;
780                                         *tooltip = TIP_("Move inside collection (Ctrl to link, Shift to parent)");
781                                         break;
782                         }
783                 }
784                 if (changed) ED_region_tag_redraw_no_rebuild(ar);
785                 return true;
786         }
787         else {
788                 if (changed) ED_region_tag_redraw_no_rebuild(ar);
789                 return false;
790         }
791 }
792
793 static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
794 {
795         Main *bmain = CTX_data_main(C);
796         Scene *scene = CTX_data_scene(C);
797
798         if (event->custom != EVT_DATA_DRAGDROP) {
799                 return OPERATOR_CANCELLED;
800         }
801
802         ListBase *lb = event->customdata;
803         wmDrag *drag = lb->first;
804
805         CollectionDrop data;
806         if (!collection_drop_init(C, drag, event, &data)) {
807                 return OPERATOR_CANCELLED;
808         }
809
810         /* Before/after insert handling. */
811         Collection *relative = NULL;
812         bool relative_after = false;
813
814         if (ELEM(data.insert_type, TE_INSERT_BEFORE, TE_INSERT_AFTER)) {
815                 SpaceOops *soops = CTX_wm_space_outliner(C);
816
817                 relative = data.to;
818                 relative_after = (data.insert_type == TE_INSERT_AFTER);
819
820                 TreeElement *parent_te = outliner_find_parent_element(&soops->tree, NULL, data.te);
821                 data.to = (parent_te) ? outliner_collection_from_tree_element(parent_te) : NULL;
822         }
823
824         if (!data.to) {
825                 return OPERATOR_CANCELLED;
826         }
827
828         if (BKE_collection_is_empty(data.to)) {
829                 TREESTORE(data.te)->flag &= ~TSE_CLOSED;
830         }
831
832         for (wmDragID *drag_id = drag->ids.first; drag_id; drag_id = drag_id->next) {
833                 /* Ctrl enables linking, so we don't need a from collection then. */
834                 Collection *from = (event->ctrl) ? NULL : collection_parent_from_ID(drag_id->from_parent);
835
836                 if (GS(drag_id->id->name) == ID_OB) {
837                         /* Move/link object into collection. */
838                         Object *object = (Object *)drag_id->id;
839
840                         if (from) {
841                                 BKE_collection_object_move(bmain, scene, data.to, from, object);
842                         }
843                         else {
844                                 BKE_collection_object_add(bmain, data.to, object);
845                         }
846                 }
847                 else if (GS(drag_id->id->name) == ID_GR) {
848                         /* Move/link collection into collection. */
849                         Collection *collection = (Collection *)drag_id->id;
850
851                         if (collection != from) {
852                                 BKE_collection_move(bmain, data.to, from, relative, relative_after, collection);
853                         }
854                 }
855
856                 if (from) {
857                         DEG_id_tag_update(&from->id, ID_RECALC_COPY_ON_WRITE);
858                 }
859         }
860
861         /* Update dependency graph. */
862         DEG_id_tag_update(&data.to->id, ID_RECALC_COPY_ON_WRITE);
863         DEG_relations_tag_update(bmain);
864         WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
865
866         return OPERATOR_FINISHED;
867 }
868
869 void OUTLINER_OT_collection_drop(wmOperatorType *ot)
870 {
871         /* identifiers */
872         ot->name = "Move to Collection";
873         ot->description = "Drag to move to collection in Outliner";
874         ot->idname = "OUTLINER_OT_collection_drop";
875
876         /* api callbacks */
877         ot->invoke = collection_drop_invoke;
878         ot->poll = ED_operator_outliner_active;
879
880         /* flags */
881         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
882 }
883
884 /* ********************* Outliner Drag Operator ******************** */
885
886 static TreeElement *outliner_item_drag_element_find(SpaceOops *soops, ARegion *ar, const wmEvent *event)
887 {
888         /* note: using EVT_TWEAK_ events to trigger dragging is fine,
889          * it sends coordinates from where dragging was started */
890         const float my = UI_view2d_region_to_view_y(&ar->v2d, event->mval[1]);
891         return outliner_find_item_at_y(soops, &soops->tree, my);
892 }
893
894 static int outliner_item_drag_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
895 {
896         ARegion *ar = CTX_wm_region(C);
897         SpaceOops *soops = CTX_wm_space_outliner(C);
898         TreeElement *te = outliner_item_drag_element_find(soops, ar, event);
899
900         if (!te) {
901                 return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
902         }
903
904         TreeElementIcon data = tree_element_get_icon(TREESTORE(te), te);
905         if (!data.drag_id) {
906                 return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH);
907         }
908
909         wmDrag *drag = WM_event_start_drag(C, data.icon, WM_DRAG_ID, NULL, 0.0, WM_DRAG_NOP);
910
911         if (ELEM(GS(data.drag_id->name), ID_OB, ID_GR)) {
912                 /* For collections and objects we cheat and drag all selected. */
913
914                 /* Only drag element under mouse if it was not selected before. */
915                 if ((TREESTORE(te)->flag & TSE_SELECTED) == 0) {
916                         outliner_flag_set(&soops->tree, TSE_SELECTED, 0);
917                         TREESTORE(te)->flag |= TSE_SELECTED;
918                 }
919
920                 /* Gather all selected elements. */
921                 struct IDsSelectedData selected = {
922                         .selected_array  = {NULL, NULL},
923                 };
924
925                 if (GS(data.drag_id->name) == ID_OB) {
926                         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_selected_objects, &selected);
927                 }
928                 else {
929                         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_selected_collections, &selected);
930                 }
931
932                 LISTBASE_FOREACH (LinkData *, link, &selected.selected_array) {
933                         TreeElement *te_selected = (TreeElement *)link->data;
934                         ID *id;
935
936                         if (GS(data.drag_id->name) == ID_OB) {
937                                 id = TREESTORE(te_selected)->id;
938                         }
939                         else {
940                                 /* Keep collection hierarchies intact when dragging. */
941                                 bool parent_selected = false;
942                                 for (TreeElement *te_parent = te_selected->parent; te_parent; te_parent = te_parent->parent) {
943                                         if (outliner_is_collection_tree_element(te_parent)) {
944                                                 if (TREESTORE(te_parent)->flag & TSE_SELECTED) {
945                                                         parent_selected = true;
946                                                         break;
947                                                 }
948                                         }
949                                 }
950
951                                 if (parent_selected) {
952                                         continue;
953                                 }
954
955                                 id = &outliner_collection_from_tree_element(te_selected)->id;
956                         }
957
958                         /* Find parent collection. */
959                         Collection *parent = NULL;
960
961                         if (te_selected->parent) {
962                                 for (TreeElement *te_parent = te_selected->parent; te_parent; te_parent = te_parent->parent) {
963                                         if (outliner_is_collection_tree_element(te_parent)) {
964                                                 parent = outliner_collection_from_tree_element(te_parent);
965                                                 break;
966                                         }
967                                 }
968                         }
969                         else {
970                                 Scene *scene = CTX_data_scene(C);
971                                 parent = BKE_collection_master(scene);
972                         }
973
974                         WM_drag_add_ID(drag, id, &parent->id);
975                 }
976
977                 BLI_freelistN(&selected.selected_array);
978         }
979         else {
980                 /* Add single ID. */
981                 WM_drag_add_ID(drag, data.drag_id, data.drag_parent);
982         }
983
984         return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
985 }
986
987 /* Outliner drag and drop. This operator mostly exists to support dragging
988  * from outliner text instead of only from the icon, and also to show a
989  * hint in the statusbar keymap. */
990
991 void OUTLINER_OT_item_drag_drop(wmOperatorType *ot)
992 {
993         ot->name = "Drag and Drop";
994         ot->idname = "OUTLINER_OT_item_drag_drop";
995         ot->description = "Drag and drop element to another place";
996
997         ot->invoke = outliner_item_drag_drop_invoke;
998         ot->poll = ED_operator_outliner_active;
999 }
1000
1001 /* *************************** Drop Boxes ************************** */
1002
1003 /* region dropbox definition */
1004 void outliner_dropboxes(void)
1005 {
1006         ListBase *lb = WM_dropboxmap_find("Outliner", SPACE_OUTLINER, RGN_TYPE_WINDOW);
1007
1008         WM_dropbox_add(lb, "OUTLINER_OT_parent_drop", parent_drop_poll, NULL);
1009         WM_dropbox_add(lb, "OUTLINER_OT_parent_clear", parent_clear_poll, NULL);
1010         WM_dropbox_add(lb, "OUTLINER_OT_scene_drop", scene_drop_poll, NULL);
1011         WM_dropbox_add(lb, "OUTLINER_OT_material_drop", material_drop_poll, NULL);
1012         WM_dropbox_add(lb, "OUTLINER_OT_collection_drop", collection_drop_poll, NULL);
1013 }