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