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