Blender V4.3
outliner_dragdrop.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2004 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cstring>
10
11#include "MEM_guardedalloc.h"
12
14#include "DNA_material_types.h"
15#include "DNA_object_types.h"
16#include "DNA_space_types.h"
17
18#include "BLI_listbase.h"
19#include "BLI_string.h"
20
21#include "BLT_translation.hh"
22
23#include "BKE_collection.hh"
24#include "BKE_context.hh"
25#include "BKE_layer.hh"
26#include "BKE_lib_id.hh"
27#include "BKE_main.hh"
28#include "BKE_material.h"
29#include "BKE_object.hh"
30#include "BKE_report.hh"
31
32#include "DEG_depsgraph.hh"
34
35#include "ED_object.hh"
36#include "ED_outliner.hh"
37#include "ED_screen.hh"
38
39#include "UI_interface.hh"
40#include "UI_view2d.hh"
41
42#include "RNA_access.hh"
43
44#include "WM_api.hh"
45#include "WM_types.hh"
46
47#include "outliner_intern.hh"
48
49namespace blender::ed::outliner {
50
52
53/* -------------------------------------------------------------------- */
58 const float fmval[2],
59 const bool children)
60{
61 if ((fmval[1] > te->ys) && (fmval[1] < (te->ys + UI_UNIT_Y))) {
62 /* name and first icon */
63 if ((fmval[0] > te->xs + UI_UNIT_X) && (fmval[0] < te->xend)) {
64 return te;
65 }
66 }
67 /* Not it. Let's look at its children. */
68 if (children && (TREESTORE(te)->flag & TSE_CLOSED) == 0 && (te->subtree.first)) {
69 LISTBASE_FOREACH (TreeElement *, te_sub, &te->subtree) {
70 TreeElement *te_valid = outliner_dropzone_element(te_sub, fmval, children);
71 if (te_valid) {
72 return te_valid;
73 }
74 }
75 }
76 return nullptr;
77}
78
79/* Find tree element to drop into. */
80static TreeElement *outliner_dropzone_find(const SpaceOutliner *space_outliner,
81 const float fmval[2],
82 const bool children)
83{
84 LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) {
85 TreeElement *te_valid = outliner_dropzone_element(te, fmval, children);
86 if (te_valid) {
87 return te_valid;
88 }
89 }
90 return nullptr;
91}
92
94{
95 ARegion *region = CTX_wm_region(C);
96 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
97 float fmval[2];
98 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
99
100 return outliner_dropzone_find(space_outliner, fmval, true);
101}
102
103static ID *outliner_ID_drop_find(bContext *C, const wmEvent *event, short idcode)
104{
105 TreeElement *te = outliner_drop_find(C, event);
106 TreeStoreElem *tselem = (te) ? TREESTORE(te) : nullptr;
107
108 if (te && (te->idcode == idcode) && (tselem->type == TSE_SOME_ID)) {
109 return tselem->id;
110 }
111 return nullptr;
112}
113
114/* Find tree element to drop into, with additional before and after reorder support. */
116 const int xy[2],
117 TreeElementInsertType *r_insert_type)
118{
119 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
120 ARegion *region = CTX_wm_region(C);
121 TreeElement *te_hovered;
122 float view_mval[2];
123
124 /* Empty tree, e.g. while filtered. */
125 if (BLI_listbase_is_empty(&space_outliner->tree)) {
126 return nullptr;
127 }
128
129 int mval[2];
130 mval[0] = xy[0] - region->winrct.xmin;
131 mval[1] = xy[1] - region->winrct.ymin;
132
133 UI_view2d_region_to_view(&region->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]);
134 te_hovered = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]);
135
136 if (te_hovered) {
137 /* Mouse hovers an element (ignoring x-axis),
138 * now find out how to insert the dragged item exactly. */
139 const float margin = UI_UNIT_Y * (1.0f / 4);
140
141 if (view_mval[1] < (te_hovered->ys + margin)) {
142 if (TSELEM_OPEN(TREESTORE(te_hovered), space_outliner) &&
143 !BLI_listbase_is_empty(&te_hovered->subtree))
144 {
145 /* inserting after a open item means we insert into it, but as first child */
146 if (BLI_listbase_is_empty(&te_hovered->subtree)) {
147 *r_insert_type = TE_INSERT_INTO;
148 return te_hovered;
149 }
150 *r_insert_type = TE_INSERT_BEFORE;
151 return static_cast<TreeElement *>(te_hovered->subtree.first);
152 }
153 *r_insert_type = TE_INSERT_AFTER;
154 return te_hovered;
155 }
156 if (view_mval[1] > (te_hovered->ys + (3 * margin))) {
157 *r_insert_type = TE_INSERT_BEFORE;
158 return te_hovered;
159 }
160 *r_insert_type = TE_INSERT_INTO;
161 return te_hovered;
162 }
163
164 /* Mouse doesn't hover any item (ignoring x-axis),
165 * so it's either above list bounds or below. */
166 TreeElement *first = static_cast<TreeElement *>(space_outliner->tree.first);
167 TreeElement *last = static_cast<TreeElement *>(space_outliner->tree.last);
168
169 if (view_mval[1] < last->ys) {
170 *r_insert_type = TE_INSERT_AFTER;
171 return last;
172 }
173 if (view_mval[1] > (first->ys + UI_UNIT_Y)) {
174 *r_insert_type = TE_INSERT_BEFORE;
175 return first;
176 }
177
179 return nullptr;
180}
181
182using CheckTypeFn = bool (*)(TreeElement *te);
183
185 TreeElement *te)
186{
187 while (te != nullptr) {
188 if (check_type(te)) {
189 return te;
190 }
191 te = te->parent;
192 }
193 return nullptr;
194}
195
200
202{
203 TreeStoreElem *tselem = TREESTORE(te);
204 return (tselem->type == TSE_SOME_ID) && te->idcode == ID_OB;
205}
206
208{
209 TreeStoreElem *tselem = TREESTORE(te);
210 return tselem->type == TSE_POSE_CHANNEL;
211}
212
214 const int xy[2],
215 TreeElementInsertType *r_insert_type)
216{
217 TreeElement *te = outliner_drop_insert_find(C, xy, r_insert_type);
218 if (!te) {
219 return nullptr;
220 }
221
223 te);
224 if (!collection_te) {
225 return nullptr;
226 }
227 Collection *collection = outliner_collection_from_tree_element(collection_te);
228
229 if (collection_te != te) {
230 *r_insert_type = TE_INSERT_INTO;
231 }
232
233 /* We can't insert before/after master collection. */
234 if (collection->flag & COLLECTION_IS_MASTER) {
235 *r_insert_type = TE_INSERT_INTO;
236 }
237
238 return collection_te;
239}
240
242 TreeElement *drop_te,
243 TreeElementInsertType insert_type,
244 ListBase *listbase)
245{
246 /* Find the element to insert after. Null is the start of the list. */
247 if (drag_te->index < drop_te->index) {
248 if (insert_type == TE_INSERT_BEFORE) {
249 drop_te = drop_te->prev;
250 }
251 }
252 else {
253 if (insert_type == TE_INSERT_AFTER) {
254 drop_te = drop_te->next;
255 }
256 }
257
258 if (drop_te == nullptr) {
259 return 0;
260 }
261
262 return BLI_findindex(listbase, drop_te->directdata);
263}
264
267/* -------------------------------------------------------------------- */
271static bool parent_drop_allowed(TreeElement *te, Object *potential_child)
272{
273 TreeStoreElem *tselem = TREESTORE(te);
274 if ((te->idcode != ID_OB) || (tselem->type != TSE_SOME_ID)) {
275 return false;
276 }
277
278 Object *potential_parent = (Object *)tselem->id;
279
280 if (potential_parent == potential_child) {
281 return false;
282 }
283 if (BKE_object_is_child_recursive(potential_child, potential_parent)) {
284 return false;
285 }
286 if (potential_parent == potential_child->parent) {
287 return false;
288 }
289
290 /* check that parent/child are both in the same scene */
291 Scene *scene = (Scene *)outliner_search_back(te, ID_SCE);
292
293 /* currently outliner organized in a way that if there's no parent scene
294 * element for object it means that all displayed objects belong to
295 * active scene and parenting them is allowed (sergey) */
296 if (scene) {
297 LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
298 BKE_view_layer_synced_ensure(scene, view_layer);
299 if (BKE_view_layer_base_find(view_layer, potential_child)) {
300 return true;
301 }
302 }
303 return false;
304 }
305 return true;
306}
307
309{
310 switch (space_outliner->outlinevis) {
311 case SO_VIEW_LAYER:
312 return space_outliner->filter & SO_FILTER_NO_COLLECTION;
313 case SO_SCENES:
314 return true;
315 default:
316 return false;
317 }
318}
319
320static bool parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
321{
322 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
323
324 bool changed = outliner_flag_set(*space_outliner, TSE_DRAG_ANY, false);
325 if (changed) {
327 }
328
329 Object *potential_child = (Object *)WM_drag_get_local_ID(drag, ID_OB);
330 if (!potential_child) {
331 return false;
332 }
333
334 if (!allow_parenting_without_modifier_key(space_outliner)) {
335 if ((event->modifier & KM_SHIFT) == 0) {
336 return false;
337 }
338 }
339
340 TreeElement *te = outliner_drop_find(C, event);
341 if (!te) {
342 return false;
343 }
344
345 if (parent_drop_allowed(te, potential_child)) {
346 TREESTORE(te)->flag |= TSE_DRAG_INTO;
348 return true;
349 }
350
351 return false;
352}
353
355 ReportList *reports,
356 wmDragID *drag,
357 Object *parent,
358 short parent_type,
359 const bool keep_transform)
360{
361 Main *bmain = CTX_data_main(C);
362 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
363
364 TreeElement *te = outliner_find_id(space_outliner, &space_outliner->tree, &parent->id);
365 Scene *scene = (Scene *)outliner_search_back(te, ID_SCE);
366
367 if (scene == nullptr) {
368 /* currently outliner organized in a way, that if there's no parent scene
369 * element for object it means that all displayed objects belong to
370 * active scene and parenting them is allowed (sergey)
371 */
372
373 scene = CTX_data_scene(C);
374 }
375
376 bool parent_set = false;
377 bool linked_objects = false;
378
379 for (wmDragID *drag_id = drag; drag_id; drag_id = drag_id->next) {
380 if (GS(drag_id->id->name) == ID_OB) {
381 Object *object = (Object *)drag_id->id;
382
383 /* Do nothing to linked data */
384 if (!BKE_id_is_editable(bmain, &object->id)) {
385 linked_objects = true;
386 continue;
387 }
388
390 reports, C, scene, object, parent, parent_type, false, keep_transform, nullptr))
391 {
392 parent_set = true;
393 }
394 }
395 }
396
397 if (linked_objects) {
398 BKE_report(reports, RPT_INFO, "Can't edit library linked or non-editable override object(s)");
399 }
400
401 if (parent_set) {
405 }
406}
407
408static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
409{
410 TreeElement *te = outliner_drop_find(C, event);
411 TreeStoreElem *tselem = te ? TREESTORE(te) : nullptr;
412
413 if (!(te && (te->idcode == ID_OB) && (tselem->type == TSE_SOME_ID))) {
414 return OPERATOR_CANCELLED;
415 }
416
417 Object *par = (Object *)tselem->id;
419
420 if (ELEM(nullptr, ob, par)) {
421 return OPERATOR_CANCELLED;
422 }
423 if (ob == par) {
424 return OPERATOR_CANCELLED;
425 }
426
427 if (event->custom != EVT_DATA_DRAGDROP) {
428 return OPERATOR_CANCELLED;
429 }
430
431 ListBase *lb = static_cast<ListBase *>(event->customdata);
432 wmDrag *drag = static_cast<wmDrag *>(lb->first);
433
435 op->reports,
436 static_cast<wmDragID *>(drag->ids.first),
437 par,
439 event->modifier & KM_ALT);
440
441 return OPERATOR_FINISHED;
442}
443
445{
446 /* identifiers */
447 ot->name = "Drop to Set Parent (hold Alt to keep transforms)";
448 ot->description = "Drag to parent in Outliner";
449 ot->idname = "OUTLINER_OT_parent_drop";
450
451 /* api callbacks */
453
455
456 /* flags */
458}
459
462/* -------------------------------------------------------------------- */
466static bool parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event)
467{
468 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
469
470 if (!allow_parenting_without_modifier_key(space_outliner)) {
471 if ((event->modifier & KM_SHIFT) == 0) {
472 return false;
473 }
474 }
475
476 Object *ob = (Object *)WM_drag_get_local_ID(drag, ID_OB);
477 if (!ob) {
478 return false;
479 }
480 if (!ob->parent) {
481 return false;
482 }
483
484 TreeElement *te = outliner_drop_find(C, event);
485 if (te) {
486 TreeStoreElem *tselem = TREESTORE(te);
487 ID *id = tselem->id;
488 if (!id) {
489 return true;
490 }
491
492 switch (GS(id->name)) {
493 case ID_OB:
495 case ID_GR:
496 return (event->modifier & KM_SHIFT) || ELEM(tselem->type, TSE_LIBRARY_OVERRIDE_BASE);
497 default:
498 return true;
499 }
500 }
501 else {
502 return true;
503 }
504}
505
506static int parent_clear_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event)
507{
508 Main *bmain = CTX_data_main(C);
509
510 if (event->custom != EVT_DATA_DRAGDROP) {
511 return OPERATOR_CANCELLED;
512 }
513
514 ListBase *lb = static_cast<ListBase *>(event->customdata);
515 wmDrag *drag = static_cast<wmDrag *>(lb->first);
516
517 LISTBASE_FOREACH (wmDragID *, drag_id, &drag->ids) {
518 if (GS(drag_id->id->name) == ID_OB) {
519 Object *object = (Object *)drag_id->id;
520
524 }
525 }
526
530 return OPERATOR_FINISHED;
531}
532
534{
535 /* identifiers */
536 ot->name = "Drop to Clear Parent (hold Alt to keep transforms)";
537 ot->description = "Drag to clear parent in Outliner";
538 ot->idname = "OUTLINER_OT_parent_clear";
539
540 /* api callbacks */
542
544
545 /* flags */
547}
548
551/* -------------------------------------------------------------------- */
555static bool scene_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
556{
557 /* Ensure item under cursor is valid drop target */
558 Object *ob = (Object *)WM_drag_get_local_ID(drag, ID_OB);
559 return (ob && (outliner_ID_drop_find(C, event, ID_SCE) != nullptr));
560}
561
562static int scene_drop_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event)
563{
564 Main *bmain = CTX_data_main(C);
565 Scene *scene = (Scene *)outliner_ID_drop_find(C, event, ID_SCE);
567
568 if (ELEM(nullptr, ob, scene) || !BKE_id_is_editable(bmain, &scene->id)) {
569 return OPERATOR_CANCELLED;
570 }
571
572 if (BKE_scene_has_object(scene, ob)) {
573 return OPERATOR_CANCELLED;
574 }
575
576 Collection *collection;
577 if (scene != CTX_data_scene(C)) {
578 /* when linking to an inactive scene link to the master collection */
579 collection = scene->master_collection;
580 }
581 else {
582 collection = CTX_data_collection(C);
583 }
584
585 BKE_collection_object_add(bmain, collection, ob);
586
587 LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
588 BKE_view_layer_synced_ensure(scene, view_layer);
589 Base *base = BKE_view_layer_base_find(view_layer, ob);
590 if (base) {
592 }
593 }
594
596
599
600 return OPERATOR_FINISHED;
601}
602
604{
605 /* identifiers */
606 ot->name = "Drop Object to Scene";
607 ot->description = "Drag object to scene in Outliner";
608 ot->idname = "OUTLINER_OT_scene_drop";
609
610 /* api callbacks */
612
614
615 /* flags */
617}
618
621/* -------------------------------------------------------------------- */
625static bool material_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
626{
627 /* Ensure item under cursor is valid drop target */
629 Object *ob = reinterpret_cast<Object *>(outliner_ID_drop_find(C, event, ID_OB));
630
631 return (!ELEM(nullptr, ob, ma) && ID_IS_EDITABLE(&ob->id) && !ID_IS_OVERRIDE_LIBRARY(&ob->id));
632}
633
634static int material_drop_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event)
635{
636 Main *bmain = CTX_data_main(C);
637 Object *ob = (Object *)outliner_ID_drop_find(C, event, ID_OB);
639
640 if (ELEM(nullptr, ob, ma) || !BKE_id_is_editable(bmain, &ob->id)) {
641 return OPERATOR_CANCELLED;
642 }
643
644 /* only drop grease pencil material on grease pencil objects */
645 if ((ma->gp_style != nullptr) && (ob->type != OB_GPENCIL_LEGACY)) {
646 return OPERATOR_CANCELLED;
647 }
648
650
654
655 return OPERATOR_FINISHED;
656}
657
659{
660 /* identifiers */
661 ot->name = "Drop Material on Object";
662 ot->description = "Drag material to object in Outliner";
663 ot->idname = "OUTLINER_OT_material_drop";
664
665 /* api callbacks */
667
669
670 /* flags */
672}
673
676/* -------------------------------------------------------------------- */
693
705
707 Object *ob,
708 bPoseChannel *pchan,
709 TreeElement *te,
710 TreeStoreElem *tselem,
711 void *directdata)
712{
713 StackDropData *drop_data = MEM_cnew<StackDropData>("datastack drop data");
714
715 drop_data->ob_parent = ob;
716 drop_data->pchan_parent = pchan;
717 drop_data->drag_tselem = tselem;
718 drop_data->drag_directdata = directdata;
719 drop_data->drag_index = te->index;
720
721 drag->poin = drop_data;
722 drag->flags |= WM_DRAG_FREE_DATA;
723}
724
725static bool datastack_drop_init(bContext *C, const wmEvent *event, StackDropData *drop_data)
726{
727 if (!ELEM(drop_data->drag_tselem->type,
734 {
735 return false;
736 }
737
738 TreeElement *te_target = outliner_drop_insert_find(C, event->xy, &drop_data->insert_type);
739 if (!te_target) {
740 return false;
741 }
742 TreeStoreElem *tselem_target = TREESTORE(te_target);
743
744 if (drop_data->drag_tselem == tselem_target) {
745 return false;
746 }
747
748 Object *ob = nullptr;
750 te_target);
751 if (object_te) {
752 ob = (Object *)TREESTORE(object_te)->id;
753 }
754
755 bPoseChannel *pchan = nullptr;
757 if (pchan_te) {
758 pchan = (bPoseChannel *)pchan_te->directdata;
759 }
760 if (pchan) {
761 ob = nullptr;
762 }
763
764 if (ob && !BKE_id_is_editable(CTX_data_main(C), &ob->id)) {
765 return false;
766 }
767
768 /* Drag a base for linking. */
769 if (ELEM(drop_data->drag_tselem->type,
773 {
774 drop_data->insert_type = TE_INSERT_INTO;
776
777 if (pchan && pchan != drop_data->pchan_parent) {
778 drop_data->drop_te = pchan_te;
779 tselem_target = TREESTORE(pchan_te);
780 }
781 else if (ob && ob != drop_data->ob_parent) {
782 drop_data->drop_te = object_te;
783 tselem_target = TREESTORE(object_te);
784 }
785 else {
786 return false;
787 }
788 }
789 else if (ob || pchan) {
790 /* Drag a single item. */
791 if (pchan && pchan != drop_data->pchan_parent) {
792 drop_data->insert_type = TE_INSERT_INTO;
794 drop_data->drop_te = pchan_te;
795 tselem_target = TREESTORE(pchan_te);
796 }
797 else if (ob && ob != drop_data->ob_parent) {
798 drop_data->insert_type = TE_INSERT_INTO;
800 drop_data->drop_te = object_te;
801 tselem_target = TREESTORE(object_te);
802 }
803 else if (tselem_target->type == drop_data->drag_tselem->type) {
804 if (drop_data->insert_type == TE_INSERT_INTO) {
805 return false;
806 }
808 drop_data->drop_te = te_target;
809 }
810 else {
811 return false;
812 }
813 }
814 else {
815 return false;
816 }
817
818 return true;
819}
820
821/* Ensure that grease pencil and object data remain separate. */
823{
824 TreeStoreElem *tselem = TREESTORE(drop_data->drop_te);
825 Object *ob_parent = drop_data->ob_parent;
826 Object *ob_dst = (Object *)tselem->id;
827
828 /* Don't allow data to be moved between objects and bones. */
829 if (tselem->type == TSE_CONSTRAINT) {
830 }
831 else if ((drop_data->pchan_parent && tselem->type != TSE_POSE_CHANNEL) ||
832 (!drop_data->pchan_parent && tselem->type == TSE_POSE_CHANNEL))
833 {
834 return false;
835 }
836
837 switch (drop_data->drag_tselem->type) {
839 case TSE_MODIFIER:
840 return (ob_parent->type == OB_GPENCIL_LEGACY) == (ob_dst->type == OB_GPENCIL_LEGACY);
841 break;
843 case TSE_CONSTRAINT:
844
845 break;
848 return ob_parent->type == OB_GPENCIL_LEGACY && ob_dst->type == OB_GPENCIL_LEGACY;
849 break;
850 }
851
852 return true;
853}
854
855static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
856{
857 if (drag->type != WM_DRAG_DATASTACK) {
858 return false;
859 }
860
861 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
862 ARegion *region = CTX_wm_region(C);
863 bool changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false);
864
865 StackDropData *drop_data = static_cast<StackDropData *>(drag->poin);
866 if (!drop_data) {
867 return false;
868 }
869
870 if (!datastack_drop_init(C, event, drop_data)) {
871 return false;
872 }
873
874 if (!datastack_drop_are_types_valid(drop_data)) {
875 return false;
876 }
877
878 TreeStoreElem *tselem_target = TREESTORE(drop_data->drop_te);
879 switch (drop_data->insert_type) {
880 case TE_INSERT_BEFORE:
881 tselem_target->flag |= TSE_DRAG_BEFORE;
882 break;
883 case TE_INSERT_AFTER:
884 tselem_target->flag |= TSE_DRAG_AFTER;
885 break;
886 case TE_INSERT_INTO:
887 tselem_target->flag |= TSE_DRAG_INTO;
888 break;
889 }
890
891 if (changed) {
893 }
894
895 return true;
896}
897
898static std::string datastack_drop_tooltip(bContext * /*C*/,
899 wmDrag *drag,
900 const int /*xy*/[2],
901 wmDropBox * /*drop*/)
902{
903 StackDropData *drop_data = static_cast<StackDropData *>(drag->poin);
904 switch (drop_data->drop_action) {
906 return TIP_("Reorder");
908 if (drop_data->pchan_parent) {
909 return TIP_("Copy to bone");
910 }
911 return TIP_("Copy to object");
912
914 if (drop_data->pchan_parent) {
915 return TIP_("Link all to bone");
916 }
917 return TIP_("Link all to object");
918 }
919 return {};
920}
921
922static void datastack_drop_link(bContext *C, StackDropData *drop_data)
923{
924 Main *bmain = CTX_data_main(C);
925 TreeStoreElem *tselem = TREESTORE(drop_data->drop_te);
926 Object *ob_dst = (Object *)tselem->id;
927
928 switch (drop_data->drag_tselem->type) {
930 object::modifier_link(C, ob_dst, drop_data->ob_parent);
931 break;
932 case TSE_CONSTRAINT_BASE: {
933 ListBase *src;
934
935 if (drop_data->pchan_parent) {
936 src = &drop_data->pchan_parent->constraints;
937 }
938 else {
939 src = &drop_data->ob_parent->constraints;
940 }
941
942 ListBase *dst;
943 if (tselem->type == TSE_POSE_CHANNEL) {
944 bPoseChannel *pchan = (bPoseChannel *)drop_data->drop_te->directdata;
945 dst = &pchan->constraints;
946 }
947 else {
948 dst = &ob_dst->constraints;
949 }
950
951 object::constraint_link(bmain, ob_dst, dst, src);
952 break;
953 }
955 if (ob_dst->type != OB_GPENCIL_LEGACY) {
956 return;
957 }
958
959 object::shaderfx_link(ob_dst, drop_data->ob_parent);
960 break;
961 }
962}
963
964static void datastack_drop_copy(bContext *C, StackDropData *drop_data)
965{
966 Main *bmain = CTX_data_main(C);
967
968 TreeStoreElem *tselem = TREESTORE(drop_data->drop_te);
969 Object *ob_dst = (Object *)tselem->id;
970
971 switch (drop_data->drag_tselem->type) {
972 case TSE_MODIFIER:
974 bmain,
976 drop_data->ob_parent,
977 static_cast<const ModifierData *>(drop_data->drag_directdata),
978 ob_dst,
979 CTX_wm_reports(C));
980 break;
981 case TSE_CONSTRAINT:
982 if (tselem->type == TSE_POSE_CHANNEL) {
984 bmain,
985 ob_dst,
986 static_cast<bPoseChannel *>(drop_data->drop_te->directdata),
987 static_cast<bConstraint *>(drop_data->drag_directdata));
988 }
989 else {
991 bmain, ob_dst, static_cast<bConstraint *>(drop_data->drag_directdata));
992 }
993 break;
994 case TSE_GPENCIL_EFFECT: {
995 if (ob_dst->type != OB_GPENCIL_LEGACY) {
996 return;
997 }
998
999 object::shaderfx_copy(ob_dst, static_cast<ShaderFxData *>(drop_data->drag_directdata));
1000 break;
1001 }
1002 }
1003}
1004
1005static void datastack_drop_reorder(bContext *C, ReportList *reports, StackDropData *drop_data)
1006{
1007 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1008
1009 TreeElement *drag_te = outliner_find_tree_element(&space_outliner->tree, drop_data->drag_tselem);
1010 if (!drag_te) {
1011 return;
1012 }
1013
1014 TreeElement *drop_te = drop_data->drop_te;
1015 TreeElementInsertType insert_type = drop_data->insert_type;
1016
1017 Object *ob = drop_data->ob_parent;
1018
1019 int index = 0;
1020 switch (drop_data->drag_tselem->type) {
1021 case TSE_MODIFIER:
1022 index = outliner_get_insert_index(drag_te, drop_te, insert_type, &ob->modifiers);
1025 ob,
1026 static_cast<ModifierData *>(drop_data->drag_directdata),
1027 index,
1028 true);
1029 break;
1030 case TSE_CONSTRAINT:
1031 if (drop_data->pchan_parent) {
1033 drag_te, drop_te, insert_type, &drop_data->pchan_parent->constraints);
1034 }
1035 else {
1036 index = outliner_get_insert_index(drag_te, drop_te, insert_type, &ob->constraints);
1037 }
1039 ob, static_cast<bConstraint *>(drop_data->drag_directdata), index);
1040
1041 break;
1042 case TSE_GPENCIL_EFFECT:
1043 index = outliner_get_insert_index(drag_te, drop_te, insert_type, &ob->shader_fx);
1045 reports, ob, static_cast<ShaderFxData *>(drop_data->drag_directdata), index);
1046 }
1047}
1048
1049static int datastack_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1050{
1051 if (event->custom != EVT_DATA_DRAGDROP) {
1052 return OPERATOR_CANCELLED;
1053 }
1054
1055 ListBase *lb = static_cast<ListBase *>(event->customdata);
1056 wmDrag *drag = static_cast<wmDrag *>(lb->first);
1057 StackDropData *drop_data = static_cast<StackDropData *>(drag->poin);
1058
1059 switch (drop_data->drop_action) {
1061 datastack_drop_link(C, drop_data);
1062 break;
1064 datastack_drop_copy(C, drop_data);
1065 break;
1067 datastack_drop_reorder(C, op->reports, drop_data);
1068 break;
1069 }
1070
1071 return OPERATOR_FINISHED;
1072}
1073
1075{
1076 /* identifiers */
1077 ot->name = "Data Stack Drop";
1078 ot->description = "Copy or reorder modifiers, constraints, and effects";
1079 ot->idname = "OUTLINER_OT_datastack_drop";
1080
1081 /* api callbacks */
1083
1085
1086 /* flags */
1088}
1089
1092/* -------------------------------------------------------------------- */
1103
1105{
1106 /* Can't change linked or override parent collections. */
1107 if (!id || !ID_IS_EDITABLE(id) || ID_IS_OVERRIDE_LIBRARY(id)) {
1108 return nullptr;
1109 }
1110
1111 /* Also support dropping into/from scene collection. */
1112 if (GS(id->name) == ID_SCE) {
1113 return ((Scene *)id)->master_collection;
1114 }
1115 if (GS(id->name) == ID_GR) {
1116 return (Collection *)id;
1117 }
1118
1119 return nullptr;
1120}
1121
1122static bool collection_drop_init(bContext *C, wmDrag *drag, const int xy[2], CollectionDrop *data)
1123{
1124 /* Get collection to drop into. */
1125 TreeElementInsertType insert_type;
1126 TreeElement *te = outliner_drop_insert_collection_find(C, xy, &insert_type);
1127 if (!te) {
1128 return false;
1129 }
1130
1132 if (!ID_IS_EDITABLE(to_collection) || ID_IS_OVERRIDE_LIBRARY(to_collection)) {
1133 return false;
1134 }
1135
1136 /* Get drag datablocks. */
1137 if (drag->type != WM_DRAG_ID) {
1138 return false;
1139 }
1140
1141 wmDragID *drag_id = static_cast<wmDragID *>(drag->ids.first);
1142 if (drag_id == nullptr) {
1143 return false;
1144 }
1145
1146 ID *id = drag_id->id;
1147 if (!(id && ELEM(GS(id->name), ID_GR, ID_OB))) {
1148 return false;
1149 }
1150
1151 /* Get collection to drag out of. */
1152 ID *parent = drag_id->from_parent;
1153 Collection *from_collection = collection_parent_from_ID(parent);
1154
1155 /* Currently this should not be allowed, cannot edit items in an override of a Collection. */
1156 if (from_collection != nullptr && ID_IS_OVERRIDE_LIBRARY(from_collection)) {
1157 return false;
1158 }
1159
1160 /* Get collections. */
1161 if (GS(id->name) == ID_GR) {
1162 if (id == &to_collection->id) {
1163 return false;
1164 }
1165 }
1166 else {
1167 insert_type = TE_INSERT_INTO;
1168 }
1169
1170 /* Currently this should not be allowed, cannot edit items in an override of a Collection. */
1171 if (ID_IS_OVERRIDE_LIBRARY(to_collection) &&
1172 !ELEM(insert_type, TE_INSERT_AFTER, TE_INSERT_BEFORE))
1173 {
1174 return false;
1175 }
1176
1177 data->from = from_collection;
1178 data->to = to_collection;
1179 data->te = te;
1180 data->insert_type = insert_type;
1181
1182 return true;
1183}
1184
1185static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
1186{
1187 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1188 ARegion *region = CTX_wm_region(C);
1189 bool changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false);
1190
1192 if (((event->modifier & KM_SHIFT) == 0) && collection_drop_init(C, drag, event->xy, &data)) {
1193 TreeElement *te = data.te;
1194 TreeStoreElem *tselem = TREESTORE(te);
1195 switch (data.insert_type) {
1196 case TE_INSERT_BEFORE:
1197 tselem->flag |= TSE_DRAG_BEFORE;
1198 changed = true;
1199 break;
1200 case TE_INSERT_AFTER:
1201 tselem->flag |= TSE_DRAG_AFTER;
1202 changed = true;
1203 break;
1204 case TE_INSERT_INTO: {
1205 tselem->flag |= TSE_DRAG_INTO;
1206 changed = true;
1207 break;
1208 }
1209 }
1210 if (changed) {
1212 }
1213 return true;
1214 }
1215 if (changed) {
1217 }
1218 return false;
1219}
1220
1221static std::string collection_drop_tooltip(bContext *C,
1222 wmDrag *drag,
1223 const int xy[2],
1224 wmDropBox * /*drop*/)
1225{
1226 wmWindow *win = CTX_wm_window(C);
1227 const wmEvent *event = win ? win->eventstate : nullptr;
1228
1230 if (event && ((event->modifier & KM_SHIFT) == 0) && collection_drop_init(C, drag, xy, &data)) {
1231 const bool is_link = !data.from || (event->modifier & KM_CTRL);
1232
1233 /* Test if we are moving within same parent collection. */
1234 bool same_level = false;
1235 LISTBASE_FOREACH (CollectionParent *, parent, &data.to->runtime.parents) {
1236 if (data.from == parent->collection) {
1237 same_level = true;
1238 }
1239 }
1240
1241 /* Tooltips when not moving directly into another collection i.e. mouse on border of
1242 * collections. Later we will decide which tooltip to return. */
1243 const bool tooltip_link = (is_link && !same_level);
1244 const char *tooltip_before = tooltip_link ? TIP_("Link before collection") :
1245 TIP_("Move before collection");
1246 const char *tooltip_between = tooltip_link ? TIP_("Link between collections") :
1247 TIP_("Move between collections");
1248 const char *tooltip_after = tooltip_link ? TIP_("Link after collection") :
1249 TIP_("Move after collection");
1250
1251 TreeElement *te = data.te;
1252 switch (data.insert_type) {
1253 case TE_INSERT_BEFORE:
1255 return tooltip_between;
1256 }
1257 return tooltip_before;
1258 case TE_INSERT_AFTER:
1260 return tooltip_between;
1261 }
1262 return tooltip_after;
1263 case TE_INSERT_INTO: {
1264 if (is_link) {
1265 return TIP_("Link inside collection");
1266 }
1267
1268 /* Check the type of the drag IDs to avoid the incorrect "Shift to parent"
1269 * for collections. Checking the type of the first ID works fine here since
1270 * all drag IDs are the same type. */
1271 wmDragID *drag_id = (wmDragID *)drag->ids.first;
1272 const bool is_object = (GS(drag_id->id->name) == ID_OB);
1273 if (is_object) {
1274 return TIP_("Move inside collection (Ctrl to link, Shift to parent)");
1275 }
1276 return TIP_("Move inside collection (Ctrl to link)");
1277 }
1278 }
1279 }
1280 return {};
1281}
1282
1283static int collection_drop_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event)
1284{
1285 Main *bmain = CTX_data_main(C);
1286 Scene *scene = CTX_data_scene(C);
1287
1288 if (event->custom != EVT_DATA_DRAGDROP) {
1289 return OPERATOR_CANCELLED;
1290 }
1291
1292 ListBase *lb = static_cast<ListBase *>(event->customdata);
1293 wmDrag *drag = static_cast<wmDrag *>(lb->first);
1294
1296 if (!collection_drop_init(C, drag, event->xy, &data)) {
1297 return OPERATOR_CANCELLED;
1298 }
1299
1300 /* Before/after insert handling. */
1301 Collection *relative = nullptr;
1302 bool relative_after = false;
1303
1304 if (ELEM(data.insert_type, TE_INSERT_BEFORE, TE_INSERT_AFTER)) {
1305 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1306
1307 relative = data.to;
1308 relative_after = (data.insert_type == TE_INSERT_AFTER);
1309
1310 TreeElement *parent_te = outliner_find_parent_element(&space_outliner->tree, nullptr, data.te);
1311 data.to = (parent_te) ? outliner_collection_from_tree_element(parent_te) : nullptr;
1312 }
1313
1314 if (!data.to) {
1315 return OPERATOR_CANCELLED;
1316 }
1317
1318 if (BKE_collection_is_empty(data.to)) {
1319 TREESTORE(data.te)->flag &= ~TSE_CLOSED;
1320 }
1321
1322 LISTBASE_FOREACH (wmDragID *, drag_id, &drag->ids) {
1323 /* Ctrl enables linking, so we don't need a from collection then. */
1324 Collection *from = (event->modifier & KM_CTRL) ?
1325 nullptr :
1326 collection_parent_from_ID(drag_id->from_parent);
1327
1328 if (GS(drag_id->id->name) == ID_OB) {
1329 /* Move/link object into collection. */
1330 Object *object = (Object *)drag_id->id;
1331
1332 if (from) {
1333 BKE_collection_object_move(bmain, scene, data.to, from, object);
1334 }
1335 else {
1336 BKE_collection_object_add(bmain, data.to, object);
1337 }
1338 }
1339 else if (GS(drag_id->id->name) == ID_GR) {
1340 /* Move/link collection into collection. */
1341 Collection *collection = (Collection *)drag_id->id;
1342
1343 if (collection != from) {
1344 BKE_collection_move(bmain, data.to, from, relative, relative_after, collection);
1345 }
1346 }
1347
1348 if (from) {
1349 DEG_id_tag_update(&from->id,
1351 }
1352 }
1353
1354 /* Update dependency graph. */
1358
1359 return OPERATOR_FINISHED;
1360}
1361
1363{
1364 /* identifiers */
1365 ot->name = "Move to Collection";
1366 ot->description = "Drag to move to collection in Outliner";
1367 ot->idname = "OUTLINER_OT_collection_drop";
1368
1369 /* api callbacks */
1372
1373 /* flags */
1375}
1376
1379/* -------------------------------------------------------------------- */
1383#define OUTLINER_DRAG_SCOLL_OUTSIDE_PAD 7 /* In UI units */
1384
1386 ARegion *region,
1387 const wmEvent *event)
1388{
1389 /* NOTE: using click-drag events to trigger dragging is fine,
1390 * it sends coordinates from where dragging was started */
1391 int mval[2];
1392 WM_event_drag_start_mval(event, region, mval);
1393
1394 const float my = UI_view2d_region_to_view_y(&region->v2d, mval[1]);
1395 return outliner_find_item_at_y(space_outliner, &space_outliner->tree, my);
1396}
1397
1398static int outliner_item_drag_drop_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event)
1399{
1400 ARegion *region = CTX_wm_region(C);
1401 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1402 TreeElement *te = outliner_item_drag_element_find(space_outliner, region, event);
1403
1404 int mval[2];
1405 WM_event_drag_start_mval(event, region, mval);
1406
1407 if (!te) {
1409 }
1410
1411 TreeStoreElem *tselem = TREESTORE(te);
1412 TreeElementIcon data = tree_element_get_icon(tselem, te);
1413 if (!data.drag_id) {
1415 }
1416
1417 float view_mval[2];
1418 UI_view2d_region_to_view(&region->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]);
1419 if (outliner_item_is_co_within_close_toggle(te, view_mval[0])) {
1421 }
1422 if (outliner_is_co_within_mode_column(space_outliner, view_mval)) {
1424 }
1425
1426 /* Scroll the view when dragging near edges, but not
1427 * when the drag goes too far outside the region. */
1428 {
1429 wmOperatorType *ot = WM_operatortype_find("VIEW2D_OT_edge_pan", true);
1430 PointerRNA op_ptr;
1432 RNA_float_set(&op_ptr, "outside_padding", OUTLINER_DRAG_SCOLL_OUTSIDE_PAD);
1435 }
1436
1437 const bool use_datastack_drag = ELEM(tselem->type,
1444
1445 const eWM_DragDataType wm_drag_type = use_datastack_drag ? WM_DRAG_DATASTACK : WM_DRAG_ID;
1446 wmDrag *drag = WM_drag_data_create(C, data.icon, wm_drag_type, nullptr, WM_DRAG_NOP);
1447
1448 if (use_datastack_drag) {
1449 TreeElement *te_bone = nullptr;
1450 bPoseChannel *pchan = outliner_find_parent_bone(te, &te_bone);
1451 datastack_drop_data_init(drag, (Object *)tselem->id, pchan, te, tselem, te->directdata);
1452 }
1453 else if (ELEM(GS(data.drag_id->name), ID_OB, ID_GR)) {
1454 /* For collections and objects we cheat and drag all selected. */
1455
1456 /* Only drag element under mouse if it was not selected before. */
1457 if ((tselem->flag & TSE_SELECTED) == 0) {
1458 outliner_flag_set(*space_outliner, TSE_SELECTED, 0);
1459 tselem->flag |= TSE_SELECTED;
1460 }
1461
1462 /* Gather all selected elements. */
1463 IDsSelectedData selected{};
1464
1465 if (GS(data.drag_id->name) == ID_OB) {
1466 outliner_tree_traverse(space_outliner,
1467 &space_outliner->tree,
1468 0,
1471 &selected);
1472 }
1473 else {
1474 outliner_tree_traverse(space_outliner,
1475 &space_outliner->tree,
1476 0,
1479 &selected);
1480 }
1481
1482 LISTBASE_FOREACH (LinkData *, link, &selected.selected_array) {
1483 TreeElement *te_selected = (TreeElement *)link->data;
1484 ID *id;
1485
1486 if (GS(data.drag_id->name) == ID_OB) {
1487 id = TREESTORE(te_selected)->id;
1488 }
1489 else {
1490 /* Keep collection hierarchies intact when dragging. */
1491 bool parent_selected = false;
1492 for (TreeElement *te_parent = te_selected->parent; te_parent;
1493 te_parent = te_parent->parent)
1494 {
1495 if (outliner_is_collection_tree_element(te_parent)) {
1496 if (TREESTORE(te_parent)->flag & TSE_SELECTED) {
1497 parent_selected = true;
1498 break;
1499 }
1500 }
1501 }
1502
1503 if (parent_selected) {
1504 continue;
1505 }
1506
1507 id = &outliner_collection_from_tree_element(te_selected)->id;
1508 }
1509
1510 /* Find parent collection. */
1511 Collection *parent = nullptr;
1512
1513 if (te_selected->parent) {
1514 for (TreeElement *te_parent = te_selected->parent; te_parent;
1515 te_parent = te_parent->parent)
1516 {
1517 if (outliner_is_collection_tree_element(te_parent)) {
1518 parent = outliner_collection_from_tree_element(te_parent);
1519 break;
1520 }
1521 }
1522 }
1523 else {
1524 Scene *scene = CTX_data_scene(C);
1525 parent = scene->master_collection;
1526 }
1527
1528 WM_drag_add_local_ID(drag, id, &parent->id);
1529 }
1530
1531 BLI_freelistN(&selected.selected_array);
1532 }
1533 else {
1534 /* Add single ID. */
1535 WM_drag_add_local_ID(drag, data.drag_id, data.drag_parent);
1536 }
1537
1539
1540 ED_outliner_select_sync_from_outliner(C, space_outliner);
1541
1543}
1544
1545/* Outliner drag and drop. This operator mostly exists to support dragging
1546 * from outliner text instead of only from the icon, and also to show a
1547 * hint in the status-bar key-map. */
1548
1550{
1551 ot->name = "Drag and Drop";
1552 ot->idname = "OUTLINER_OT_item_drag_drop";
1553 ot->description = "Drag and drop element to another place";
1554
1557}
1558
1559#undef OUTLINER_DRAG_SCOLL_OUTSIDE_PAD
1560
1563/* -------------------------------------------------------------------- */
1568{
1570
1571 WM_dropbox_add(lb, "OUTLINER_OT_parent_drop", parent_drop_poll, nullptr, nullptr, nullptr);
1572 WM_dropbox_add(lb, "OUTLINER_OT_parent_clear", parent_clear_poll, nullptr, nullptr, nullptr);
1573 WM_dropbox_add(lb, "OUTLINER_OT_scene_drop", scene_drop_poll, nullptr, nullptr, nullptr);
1574 WM_dropbox_add(lb, "OUTLINER_OT_material_drop", material_drop_poll, nullptr, nullptr, nullptr);
1575 WM_dropbox_add(lb,
1576 "OUTLINER_OT_datastack_drop",
1578 nullptr,
1579 nullptr,
1581 WM_dropbox_add(lb,
1582 "OUTLINER_OT_collection_drop",
1584 nullptr,
1585 nullptr,
1587}
1588
1591} // namespace blender::ed::outliner
bool BKE_collection_is_empty(const Collection *collection)
bool BKE_collection_move(Main *bmain, Collection *to_parent, Collection *from_parent, Collection *relative, bool relative_after, Collection *collection)
void BKE_collection_object_move(Main *bmain, Scene *scene, Collection *collection_dst, Collection *collection_src, Object *ob)
bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
ReportList * CTX_wm_reports(const bContext *C)
SpaceOutliner * CTX_wm_space_outliner(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
Collection * CTX_data_collection(const bContext *C)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
bool BKE_scene_has_object(Scene *scene, Object *ob)
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
bool BKE_id_is_editable(const Main *bmain, const ID *id)
Definition lib_id.cc:2456
General operations, lookup, etc. for materials.
void BKE_object_material_assign(struct Main *bmain, struct Object *ob, struct Material *ma, short act, int assign_type)
@ BKE_MAT_ASSIGN_USERPREF
General operations, lookup, etc. for blender objects.
bool BKE_object_is_child_recursive(const Object *ob_parent, const Object *ob_child)
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define ELEM(...)
#define TIP_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1068
@ ID_RECALC_HIERARCHY
Definition DNA_ID.h:1125
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:658
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:683
@ ID_SCE
@ ID_MA
@ ID_GR
@ ID_OB
Object groups, one object can be in many groups at once.
@ COLLECTION_IS_MASTER
Object is a sort of wrapper for general info.
@ OB_GPENCIL_LEGACY
@ TSE_POSE_CHANNEL
@ TSE_CONSTRAINT_BASE
@ TSE_MODIFIER_BASE
@ TSE_GPENCIL_EFFECT
@ TSE_LIBRARY_OVERRIDE_BASE
@ TSE_CONSTRAINT
@ TSE_GPENCIL_EFFECT_BASE
@ TSE_SOME_ID
@ TSE_MODIFIER
@ TSE_DRAG_AFTER
@ TSE_HIGHLIGHTED_ANY
@ TSE_SELECTED
@ TSE_DRAG_INTO
@ TSE_CLOSED
@ TSE_DRAG_BEFORE
@ TSE_DRAG_ANY
@ RGN_TYPE_WINDOW
@ SPACE_OUTLINER
@ SO_FILTER_NO_COLLECTION
@ SO_VIEW_LAYER
@ SO_SCENES
@ OPERATOR_PASS_THROUGH
void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *space_outliner)
bool ED_operator_region_outliner_active(bContext *C)
void ED_region_tag_redraw_no_rebuild(ARegion *region)
Definition area.cc:653
bool ED_operator_outliner_active(bContext *C)
Read Guarded memory(de)allocation.
#define UI_UNIT_Y
#define UI_UNIT_X
float UI_view2d_region_to_view_y(const View2D *v2d, float y)
Definition view2d.cc:1657
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1663
@ OPTYPE_INTERNAL
Definition WM_types.hh:182
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
@ WM_DRAG_NOP
Definition WM_types.hh:1180
@ WM_DRAG_FREE_DATA
Definition WM_types.hh:1181
#define ND_OB_SELECT
Definition WM_types.hh:409
#define NC_SCENE
Definition WM_types.hh:345
#define ND_PARENT
Definition WM_types.hh:434
#define NC_MATERIAL
Definition WM_types.hh:347
#define ND_TRANSFORM
Definition WM_types.hh:423
#define ND_LAYER
Definition WM_types.hh:417
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:218
eWM_DragDataType
Definition WM_types.hh:1152
@ WM_DRAG_DATASTACK
Definition WM_types.hh:1171
@ WM_DRAG_ID
Definition WM_types.hh:1153
@ KM_CTRL
Definition WM_types.hh:256
@ KM_ALT
Definition WM_types.hh:257
@ KM_SHIFT
Definition WM_types.hh:255
#define ND_OB_SHADING
Definition WM_types.hh:424
#define ND_SPACE_VIEW3D
Definition WM_types.hh:494
#define NC_OBJECT
Definition WM_types.hh:346
#define ND_SHADING_LINKS
Definition WM_types.hh:446
#define NC_SPACE
Definition WM_types.hh:359
#define GS(x)
Definition iris.cc:202
void constraint_link(Main *bmain, Object *ob_dst, ListBase *dst, ListBase *src)
void constraint_copy_for_object(Main *bmain, Object *ob_dst, bConstraint *con)
bool modifier_copy_to_object(Main *bmain, const Scene *scene, const Object *ob_src, const ModifierData *md, Object *ob_dst, ReportList *reports)
void constraint_copy_for_pose(Main *bmain, Object *ob_dst, bPoseChannel *pchan, bConstraint *con)
void base_select(Base *base, eObjectSelect_Mode mode)
void parent_clear(Object *ob, int type)
bool parent_set(ReportList *reports, const bContext *C, Scene *scene, Object *const ob, Object *const par, int partype, bool xmirror, bool keep_transform, const int vert_par[3])
void shaderfx_copy(Object *dst, ShaderFxData *fx)
bool constraint_move_to_index(Object *ob, bConstraint *con, int index)
bool modifier_move_to_index(ReportList *reports, eReportType error_type, Object *ob, ModifierData *md, int index, bool allow_partial)
bool shaderfx_move_to_index(ReportList *reports, Object *ob, ShaderFxData *fx, int index)
void modifier_link(bContext *C, Object *ob_dst, Object *ob_src)
void shaderfx_link(Object *dst, Object *src)
static bool scene_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
bool outliner_item_is_co_within_close_toggle(const TreeElement *te, float view_co_x)
static bool collection_drop_init(bContext *C, wmDrag *drag, const int xy[2], CollectionDrop *data)
static bool is_pchan_element(TreeElement *te)
static void datastack_drop_link(bContext *C, StackDropData *drop_data)
bool(*)(TreeElement *te) CheckTypeFn
static TreeElement * outliner_drop_find(bContext *C, const wmEvent *event)
static bool parent_drop_allowed(TreeElement *te, Object *potential_child)
static std::string datastack_drop_tooltip(bContext *, wmDrag *drag, const int[2], wmDropBox *)
bool outliner_is_collection_tree_element(const TreeElement *te)
static std::string collection_drop_tooltip(bContext *C, wmDrag *drag, const int xy[2], wmDropBox *)
static ID * outliner_ID_drop_find(bContext *C, const wmEvent *event, short idcode)
TreeElement * outliner_find_id(SpaceOutliner *space_outliner, ListBase *lb, const ID *id)
static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
static bool datastack_drop_are_types_valid(StackDropData *drop_data)
static int scene_drop_invoke(bContext *C, wmOperator *, const wmEvent *event)
static void datastack_drop_reorder(bContext *C, ReportList *reports, StackDropData *drop_data)
static int datastack_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
bool outliner_flag_set(const SpaceOutliner &space_outliner, const short flag, const short set)
static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
void OUTLINER_OT_collection_drop(wmOperatorType *ot)
TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
TreeElement * outliner_find_item_at_y(const SpaceOutliner *space_outliner, const ListBase *tree, float view_co_y)
static TreeElement * outliner_drop_insert_collection_find(bContext *C, const int xy[2], TreeElementInsertType *r_insert_type)
static int material_drop_invoke(bContext *C, wmOperator *, const wmEvent *event)
bPoseChannel * outliner_find_parent_bone(TreeElement *te, TreeElement **r_bone_te)
void OUTLINER_OT_datastack_drop(wmOperatorType *ot)
static TreeElement * outliner_drop_insert_find(bContext *C, const int xy[2], TreeElementInsertType *r_insert_type)
static void datastack_drop_data_init(wmDrag *drag, Object *ob, bPoseChannel *pchan, TreeElement *te, TreeStoreElem *tselem, void *directdata)
static int outliner_get_insert_index(TreeElement *drag_te, TreeElement *drop_te, TreeElementInsertType insert_type, ListBase *listbase)
TreeTraversalAction outliner_collect_selected_objects(TreeElement *te, void *customdata)
Collection * outliner_collection_from_tree_element(const TreeElement *te)
static bool parent_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
bool outliner_is_co_within_mode_column(SpaceOutliner *space_outliner, const float view_mval[2])
static int outliner_item_drag_drop_invoke(bContext *C, wmOperator *, const wmEvent *event)
void OUTLINER_OT_material_drop(wmOperatorType *ot)
void OUTLINER_OT_scene_drop(wmOperatorType *ot)
static TreeElement * outliner_dropzone_find(const SpaceOutliner *space_outliner, const float fmval[2], const bool children)
static int parent_clear_invoke(bContext *C, wmOperator *, const wmEvent *event)
static TreeElement * outliner_item_drag_element_find(SpaceOutliner *space_outliner, ARegion *region, const wmEvent *event)
void OUTLINER_OT_parent_drop(wmOperatorType *ot)
bool outliner_tree_traverse(const SpaceOutliner *space_outliner, ListBase *tree, int filter_te_flag, int filter_tselem_flag, TreeTraversalFunc func, void *customdata)
ID * outliner_search_back(TreeElement *te, short idcode)
static bool datastack_drop_init(bContext *C, const wmEvent *event, StackDropData *drop_data)
static bool allow_parenting_without_modifier_key(SpaceOutliner *space_outliner)
static void parent_drop_set_parents(bContext *C, ReportList *reports, wmDragID *drag, Object *parent, short parent_type, const bool keep_transform)
TreeElement * outliner_find_tree_element(ListBase *lb, const TreeStoreElem *store_elem)
TreeElement * outliner_find_parent_element(ListBase *lb, TreeElement *parent_te, const TreeElement *child_te)
static bool is_object_element(TreeElement *te)
static void datastack_drop_copy(bContext *C, StackDropData *drop_data)
static bool is_collection_element(TreeElement *te)
static TreeElement * outliner_data_from_tree_element_and_parents(CheckTypeFn check_type, TreeElement *te)
static int collection_drop_invoke(bContext *C, wmOperator *, const wmEvent *event)
static Collection * collection_parent_from_ID(ID *id)
void OUTLINER_OT_parent_clear(wmOperatorType *ot)
static TreeElement * outliner_dropzone_element(TreeElement *te, const float fmval[2], const bool children)
TreeTraversalAction outliner_collect_selected_collections(TreeElement *te, void *customdata)
static bool material_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
void OUTLINER_OT_item_drag_drop(wmOperatorType *ot)
static bool parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event)
static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
#define OUTLINER_DRAG_SCOLL_OUTSIDE_PAD
#define TREESTORE(a)
#define TSELEM_OPEN(telm, sv)
void RNA_float_set(PointerRNA *ptr, const char *name, float value)
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
void * last
void * first
struct MaterialGPencilStyle * gp_style
ListBase constraints
ListBase modifiers
ListBase shader_fx
struct Object * parent
wmDragID * next
Definition WM_types.hh:1188
ID * from_parent
Definition WM_types.hh:1190
eWM_DragDataType type
Definition WM_types.hh:1282
void * poin
Definition WM_types.hh:1283
ListBase ids
Definition WM_types.hh:1294
eWM_DragFlags flags
Definition WM_types.hh:1291
short custom
Definition WM_types.hh:758
int xy[2]
Definition WM_types.hh:726
int mval[2]
Definition WM_types.hh:728
uint8_t modifier
Definition WM_types.hh:739
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
const char * description
Definition WM_types.hh:996
struct ReportList * reports
struct wmEvent * eventstate
wmDropBox * WM_dropbox_add(ListBase *lb, const char *idname, bool(*poll)(bContext *C, wmDrag *drag, const wmEvent *event), void(*copy)(bContext *C, wmDrag *drag, wmDropBox *drop), void(*cancel)(Main *bmain, wmDrag *drag, wmDropBox *drop), WMDropboxTooltipFunc tooltip)
void WM_event_start_prepared_drag(bContext *C, wmDrag *drag)
wmDrag * WM_drag_data_create(bContext *C, int icon, eWM_DragDataType type, void *poin, uint flags)
ID * WM_drag_get_local_ID_from_event(const wmEvent *event, short idcode)
ListBase * WM_dropboxmap_find(const char *idname, int spaceid, int regionid)
void WM_drag_add_local_ID(wmDrag *drag, ID *id, ID *from_parent)
ID * WM_drag_get_local_ID(const wmDrag *drag, short idcode)
int xy[2]
Definition wm_draw.cc:170
void WM_event_drag_start_mval(const wmEvent *event, const ARegion *region, int r_mval[2])
void WM_main_add_notifier(uint type, void *reference)
int WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ EVT_DATA_DRAGDROP
wmOperatorType * ot
Definition wm_files.cc:4125
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
void WM_operator_properties_free(PointerRNA *ptr)
uint8_t flag
Definition wm_window.cc:138