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