89eb3b9d95395e1ee44eabda71ea17e98a1e1e4f
[blender.git] / source / blender / editors / space_outliner / outliner_tools.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2004 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup spoutliner
22  */
23
24 #include "MEM_guardedalloc.h"
25
26 #include "DNA_anim_types.h"
27 #include "DNA_armature_types.h"
28 #include "DNA_collection_types.h"
29 #include "DNA_gpencil_types.h"
30 #include "DNA_light_types.h"
31 #include "DNA_linestyle_types.h"
32 #include "DNA_material_types.h"
33 #include "DNA_mesh_types.h"
34 #include "DNA_meta_types.h"
35 #include "DNA_scene_types.h"
36 #include "DNA_sequence_types.h"
37 #include "DNA_world_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_constraint_types.h"
40 #include "DNA_modifier_types.h"
41
42 #include "BLI_blenlib.h"
43 #include "BLI_utildefines.h"
44
45 #include "BKE_animsys.h"
46 #include "BKE_collection.h"
47 #include "BKE_context.h"
48 #include "BKE_constraint.h"
49 #include "BKE_fcurve.h"
50 #include "BKE_global.h"
51 #include "BKE_layer.h"
52 #include "BKE_library.h"
53 #include "BKE_library_override.h"
54 #include "BKE_library_query.h"
55 #include "BKE_main.h"
56 #include "BKE_report.h"
57 #include "BKE_scene.h"
58 #include "BKE_screen.h"
59 #include "BKE_sequencer.h"
60
61 #include "DEG_depsgraph.h"
62 #include "DEG_depsgraph_build.h"
63
64 #include "ED_armature.h"
65 #include "ED_object.h"
66 #include "ED_scene.h"
67 #include "ED_screen.h"
68 #include "ED_sequencer.h"
69 #include "ED_undo.h"
70
71 #include "WM_api.h"
72 #include "WM_types.h"
73 #include "WM_message.h"
74
75 #include "UI_interface.h"
76 #include "UI_view2d.h"
77 #include "UI_resources.h"
78
79 #include "RNA_access.h"
80 #include "RNA_define.h"
81 #include "RNA_enum_types.h"
82
83 #include "outliner_intern.h"
84
85 /* ****************************************************** */
86
87 /* ************ SELECTION OPERATIONS ********* */
88
89 static void set_operation_types(SpaceOutliner *soops,
90                                 ListBase *lb,
91                                 int *scenelevel,
92                                 int *objectlevel,
93                                 int *idlevel,
94                                 int *datalevel)
95 {
96   TreeElement *te;
97   TreeStoreElem *tselem;
98
99   for (te = lb->first; te; te = te->next) {
100     tselem = TREESTORE(te);
101     if (tselem->flag & TSE_SELECTED) {
102       /* Layer collection points to collection ID. */
103       if (!ELEM(tselem->type, 0, TSE_LAYER_COLLECTION)) {
104         if (*datalevel == 0) {
105           *datalevel = tselem->type;
106         }
107         else if (*datalevel != tselem->type) {
108           *datalevel = -1;
109         }
110       }
111       else {
112         const int idcode = (int)GS(tselem->id->name);
113         bool is_standard_id = false;
114         switch ((ID_Type)idcode) {
115           case ID_SCE:
116             *scenelevel = 1;
117             break;
118           case ID_OB:
119             *objectlevel = 1;
120             break;
121
122           case ID_ME:
123           case ID_CU:
124           case ID_MB:
125           case ID_LT:
126           case ID_LA:
127           case ID_AR:
128           case ID_CA:
129           case ID_SPK:
130           case ID_MA:
131           case ID_TE:
132           case ID_IP:
133           case ID_IM:
134           case ID_SO:
135           case ID_KE:
136           case ID_WO:
137           case ID_AC:
138           case ID_TXT:
139           case ID_GR:
140           case ID_LS:
141           case ID_LI:
142           case ID_VF:
143           case ID_NT:
144           case ID_BR:
145           case ID_PA:
146           case ID_GD:
147           case ID_MC:
148           case ID_MSK:
149           case ID_PAL:
150           case ID_PC:
151           case ID_CF:
152           case ID_WS:
153           case ID_LP:
154             is_standard_id = true;
155             break;
156           case ID_WM:
157           case ID_SCR:
158             /* Those are ignored here. */
159             /* Note: while Screens should be manageable here, deleting a screen used by a workspace
160              * will cause crashes when trying to use that workspace, so for now let's play minimal,
161              * safe change. */
162             break;
163         }
164         if (idcode == ID_NLA) {
165           /* Fake one, not an actual ID type... */
166           is_standard_id = true;
167         }
168
169         if (is_standard_id) {
170           if (*idlevel == 0) {
171             *idlevel = idcode;
172           }
173           else if (*idlevel != idcode) {
174             *idlevel = -1;
175           }
176           if (ELEM(*datalevel, TSE_VIEW_COLLECTION_BASE, TSE_SCENE_COLLECTION_BASE)) {
177             *datalevel = 0;
178           }
179         }
180       }
181     }
182     if (TSELEM_OPEN(tselem, soops)) {
183       set_operation_types(soops, &te->subtree, scenelevel, objectlevel, idlevel, datalevel);
184     }
185   }
186 }
187
188 static void unlink_action_cb(bContext *C,
189                              ReportList *UNUSED(reports),
190                              Scene *UNUSED(scene),
191                              TreeElement *UNUSED(te),
192                              TreeStoreElem *tsep,
193                              TreeStoreElem *UNUSED(tselem),
194                              void *UNUSED(user_data))
195 {
196   /* just set action to NULL */
197   BKE_animdata_set_action(CTX_wm_reports(C), tsep->id, NULL);
198 }
199
200 static void unlink_material_cb(bContext *UNUSED(C),
201                                ReportList *UNUSED(reports),
202                                Scene *UNUSED(scene),
203                                TreeElement *te,
204                                TreeStoreElem *tsep,
205                                TreeStoreElem *UNUSED(tselem),
206                                void *UNUSED(user_data))
207 {
208   Material **matar = NULL;
209   int a, totcol = 0;
210
211   if (GS(tsep->id->name) == ID_OB) {
212     Object *ob = (Object *)tsep->id;
213     totcol = ob->totcol;
214     matar = ob->mat;
215   }
216   else if (GS(tsep->id->name) == ID_ME) {
217     Mesh *me = (Mesh *)tsep->id;
218     totcol = me->totcol;
219     matar = me->mat;
220   }
221   else if (GS(tsep->id->name) == ID_CU) {
222     Curve *cu = (Curve *)tsep->id;
223     totcol = cu->totcol;
224     matar = cu->mat;
225   }
226   else if (GS(tsep->id->name) == ID_MB) {
227     MetaBall *mb = (MetaBall *)tsep->id;
228     totcol = mb->totcol;
229     matar = mb->mat;
230   }
231   else {
232     BLI_assert(0);
233   }
234
235   if (LIKELY(matar != NULL)) {
236     for (a = 0; a < totcol; a++) {
237       if (a == te->index && matar[a]) {
238         id_us_min(&matar[a]->id);
239         matar[a] = NULL;
240       }
241     }
242   }
243 }
244
245 static void unlink_texture_cb(bContext *UNUSED(C),
246                               ReportList *UNUSED(reports),
247                               Scene *UNUSED(scene),
248                               TreeElement *te,
249                               TreeStoreElem *tsep,
250                               TreeStoreElem *UNUSED(tselem),
251                               void *UNUSED(user_data))
252 {
253   MTex **mtex = NULL;
254   int a;
255
256   if (GS(tsep->id->name) == ID_LS) {
257     FreestyleLineStyle *ls = (FreestyleLineStyle *)tsep->id;
258     mtex = ls->mtex;
259   }
260   else {
261     return;
262   }
263
264   for (a = 0; a < MAX_MTEX; a++) {
265     if (a == te->index && mtex[a]) {
266       if (mtex[a]->tex) {
267         id_us_min(&mtex[a]->tex->id);
268         mtex[a]->tex = NULL;
269       }
270     }
271   }
272 }
273
274 static void unlink_collection_cb(bContext *C,
275                                  ReportList *UNUSED(reports),
276                                  Scene *UNUSED(scene),
277                                  TreeElement *UNUSED(te),
278                                  TreeStoreElem *tsep,
279                                  TreeStoreElem *tselem,
280                                  void *UNUSED(user_data))
281 {
282   Main *bmain = CTX_data_main(C);
283   Collection *collection = (Collection *)tselem->id;
284
285   if (tsep) {
286     if (GS(tsep->id->name) == ID_OB) {
287       Object *ob = (Object *)tsep->id;
288       ob->instance_collection = NULL;
289       DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
290       DEG_relations_tag_update(bmain);
291     }
292     else if (GS(tsep->id->name) == ID_GR) {
293       Collection *parent = (Collection *)tsep->id;
294       id_fake_user_set(&collection->id);
295       BKE_collection_child_remove(bmain, parent, collection);
296       DEG_id_tag_update(&parent->id, ID_RECALC_COPY_ON_WRITE);
297       DEG_relations_tag_update(bmain);
298     }
299     else if (GS(tsep->id->name) == ID_SCE) {
300       Scene *scene = (Scene *)tsep->id;
301       Collection *parent = BKE_collection_master(scene);
302       id_fake_user_set(&collection->id);
303       BKE_collection_child_remove(bmain, parent, collection);
304       DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
305       DEG_relations_tag_update(bmain);
306     }
307   }
308 }
309
310 static void unlink_object_cb(bContext *C,
311                              ReportList *UNUSED(reports),
312                              Scene *UNUSED(scene),
313                              TreeElement *te,
314                              TreeStoreElem *tsep,
315                              TreeStoreElem *tselem,
316                              void *UNUSED(user_data))
317 {
318   if (tsep && tsep->id) {
319     Main *bmain = CTX_data_main(C);
320     Object *ob = (Object *)tselem->id;
321
322     if (GS(tsep->id->name) == ID_OB) {
323       /* Parented objects need to find which collection to unlink from. */
324       TreeElement *te_parent = te->parent;
325       while (tsep && GS(tsep->id->name) == ID_OB) {
326         te_parent = te_parent->parent;
327         tsep = te_parent ? TREESTORE(te_parent) : NULL;
328       }
329     }
330
331     if (tsep && tsep->id) {
332       if (GS(tsep->id->name) == ID_GR) {
333         Collection *parent = (Collection *)tsep->id;
334         BKE_collection_object_remove(bmain, parent, ob, true);
335         DEG_id_tag_update(&parent->id, ID_RECALC_COPY_ON_WRITE);
336         DEG_relations_tag_update(bmain);
337       }
338       else if (GS(tsep->id->name) == ID_SCE) {
339         Scene *scene = (Scene *)tsep->id;
340         Collection *parent = BKE_collection_master(scene);
341         BKE_collection_object_remove(bmain, parent, ob, true);
342         DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
343         DEG_relations_tag_update(bmain);
344       }
345     }
346   }
347 }
348
349 static void unlink_world_cb(bContext *UNUSED(C),
350                             ReportList *UNUSED(reports),
351                             Scene *UNUSED(scene),
352                             TreeElement *UNUSED(te),
353                             TreeStoreElem *tsep,
354                             TreeStoreElem *tselem,
355                             void *UNUSED(user_data))
356 {
357   Scene *parscene = (Scene *)tsep->id;
358   World *wo = (World *)tselem->id;
359
360   /* need to use parent scene not just scene, otherwise may end up getting wrong one */
361   id_us_min(&wo->id);
362   parscene->world = NULL;
363 }
364
365 static void outliner_do_libdata_operation(bContext *C,
366                                           ReportList *reports,
367                                           Scene *scene,
368                                           SpaceOutliner *soops,
369                                           ListBase *lb,
370                                           outliner_operation_cb operation_cb,
371                                           void *user_data)
372 {
373   TreeElement *te;
374   TreeStoreElem *tselem;
375
376   for (te = lb->first; te; te = te->next) {
377     tselem = TREESTORE(te);
378     if (tselem->flag & TSE_SELECTED) {
379       if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION)) {
380         TreeStoreElem *tsep = te->parent ? TREESTORE(te->parent) : NULL;
381         operation_cb(C, reports, scene, te, tsep, tselem, user_data);
382       }
383     }
384     if (TSELEM_OPEN(tselem, soops)) {
385       outliner_do_libdata_operation(
386           C, reports, scene, soops, &te->subtree, operation_cb, user_data);
387     }
388   }
389 }
390
391 /* ******************************************** */
392 typedef enum eOutliner_PropSceneOps {
393   OL_SCENE_OP_DELETE = 1,
394 } eOutliner_PropSceneOps;
395
396 static const EnumPropertyItem prop_scene_op_types[] = {
397     {OL_SCENE_OP_DELETE, "DELETE", ICON_X, "Delete", ""},
398     {0, NULL, 0, NULL, NULL},
399 };
400
401 static bool outliner_do_scene_operation(
402     bContext *C,
403     eOutliner_PropSceneOps event,
404     ListBase *lb,
405     bool (*operation_cb)(bContext *, eOutliner_PropSceneOps, TreeElement *, TreeStoreElem *))
406 {
407   TreeElement *te;
408   TreeStoreElem *tselem;
409   bool success = false;
410
411   for (te = lb->first; te; te = te->next) {
412     tselem = TREESTORE(te);
413     if (tselem->flag & TSE_SELECTED) {
414       if (operation_cb(C, event, te, tselem)) {
415         success = true;
416       }
417     }
418   }
419
420   return success;
421 }
422
423 static bool scene_cb(bContext *C,
424                      eOutliner_PropSceneOps event,
425                      TreeElement *UNUSED(te),
426                      TreeStoreElem *tselem)
427 {
428   Scene *scene = (Scene *)tselem->id;
429
430   if (event == OL_SCENE_OP_DELETE) {
431     if (ED_scene_delete(C, CTX_data_main(C), scene)) {
432       WM_event_add_notifier(C, NC_SCENE | NA_REMOVED, scene);
433     }
434     else {
435       return false;
436     }
437   }
438
439   return true;
440 }
441
442 static int outliner_scene_operation_exec(bContext *C, wmOperator *op)
443 {
444   SpaceOutliner *soops = CTX_wm_space_outliner(C);
445   const eOutliner_PropSceneOps event = RNA_enum_get(op->ptr, "type");
446
447   if (outliner_do_scene_operation(C, event, &soops->tree, scene_cb) == false) {
448     return OPERATOR_CANCELLED;
449   }
450
451   if (event == OL_SCENE_OP_DELETE) {
452     outliner_cleanup_tree(soops);
453     ED_undo_push(C, "Delete Scene(s)");
454   }
455   else {
456     BLI_assert(0);
457     return OPERATOR_CANCELLED;
458   }
459
460   return OPERATOR_FINISHED;
461 }
462
463 void OUTLINER_OT_scene_operation(wmOperatorType *ot)
464 {
465   /* identifiers */
466   ot->name = "Outliner Scene Operation";
467   ot->idname = "OUTLINER_OT_scene_operation";
468   ot->description = "Context menu for scene operations";
469
470   /* callbacks */
471   ot->invoke = WM_menu_invoke;
472   ot->exec = outliner_scene_operation_exec;
473   ot->poll = ED_operator_outliner_active;
474
475   ot->flag = 0;
476
477   ot->prop = RNA_def_enum(ot->srna, "type", prop_scene_op_types, 0, "Scene Operation", "");
478 }
479 /* ******************************************** */
480
481 static void object_select_cb(bContext *C,
482                              ReportList *UNUSED(reports),
483                              Scene *UNUSED(scene),
484                              TreeElement *UNUSED(te),
485                              TreeStoreElem *UNUSED(tsep),
486                              TreeStoreElem *tselem,
487                              void *UNUSED(user_data))
488 {
489   ViewLayer *view_layer = CTX_data_view_layer(C);
490   Object *ob = (Object *)tselem->id;
491   Base *base = BKE_view_layer_base_find(view_layer, ob);
492
493   if (base) {
494     ED_object_base_select(base, BA_SELECT);
495   }
496 }
497
498 static void object_select_hierarchy_cb(bContext *C,
499                                        ReportList *UNUSED(reports),
500                                        Scene *UNUSED(scene),
501                                        TreeElement *te,
502                                        TreeStoreElem *UNUSED(tsep),
503                                        TreeStoreElem *tselem,
504                                        void *UNUSED(user_data))
505 {
506   /* Don't extend because this toggles, which is nice for Ctrl-Click but not for a menu item.
507    * it's especially confusing when multiple items are selected since some toggle on/off. */
508   outliner_item_do_activate_from_tree_element(C, te, tselem, false, true);
509 }
510
511 static void object_deselect_cb(bContext *C,
512                                ReportList *UNUSED(reports),
513                                Scene *UNUSED(scene),
514                                TreeElement *UNUSED(te),
515                                TreeStoreElem *UNUSED(tsep),
516                                TreeStoreElem *tselem,
517                                void *UNUSED(user_data))
518 {
519   ViewLayer *view_layer = CTX_data_view_layer(C);
520   Object *ob = (Object *)tselem->id;
521   Base *base = BKE_view_layer_base_find(view_layer, ob);
522
523   if (base) {
524     base->flag &= ~BASE_SELECTED;
525   }
526 }
527
528 static void object_delete_cb(bContext *C,
529                              ReportList *reports,
530                              Scene *scene,
531                              TreeElement *UNUSED(te),
532                              TreeStoreElem *UNUSED(tsep),
533                              TreeStoreElem *tselem,
534                              void *UNUSED(user_data))
535 {
536   Object *ob = (Object *)tselem->id;
537   if (ob) {
538     Main *bmain = CTX_data_main(C);
539     if (ob->id.tag & LIB_TAG_INDIRECT) {
540       BKE_reportf(
541           reports, RPT_WARNING, "Cannot delete indirectly linked object '%s'", ob->id.name + 2);
542       return;
543     }
544     else if (BKE_library_ID_is_indirectly_used(bmain, ob) && ID_REAL_USERS(ob) <= 1 &&
545              ID_EXTRA_USERS(ob) == 0) {
546       BKE_reportf(reports,
547                   RPT_WARNING,
548                   "Cannot delete object '%s' from scene '%s', indirectly used objects need at "
549                   "least one user",
550                   ob->id.name + 2,
551                   scene->id.name + 2);
552       return;
553     }
554
555     // check also library later
556     if (ob == CTX_data_edit_object(C)) {
557       ED_object_editmode_exit(C, EM_FREEDATA);
558     }
559     ED_object_base_free_and_unlink(CTX_data_main(C), scene, ob);
560     /* leave for ED_outliner_id_unref to handle */
561 #if 0
562     te->directdata = NULL;
563     tselem->id = NULL;
564 #endif
565   }
566 }
567
568 static void id_local_cb(bContext *C,
569                         ReportList *UNUSED(reports),
570                         Scene *UNUSED(scene),
571                         TreeElement *UNUSED(te),
572                         TreeStoreElem *UNUSED(tsep),
573                         TreeStoreElem *tselem,
574                         void *UNUSED(user_data))
575 {
576   if (ID_IS_LINKED(tselem->id) && (tselem->id->tag & LIB_TAG_EXTERN)) {
577     Main *bmain = CTX_data_main(C);
578     /* if the ID type has no special local function,
579      * just clear the lib */
580     if (id_make_local(bmain, tselem->id, false, false) == false) {
581       id_clear_lib_data(bmain, tselem->id);
582     }
583     else {
584       BKE_main_id_clear_newpoins(bmain);
585     }
586   }
587 }
588
589 static void id_override_library_cb(bContext *C,
590                                    ReportList *UNUSED(reports),
591                                    Scene *UNUSED(scene),
592                                    TreeElement *UNUSED(te),
593                                    TreeStoreElem *UNUSED(tsep),
594                                    TreeStoreElem *tselem,
595                                    void *UNUSED(user_data))
596 {
597   if (ID_IS_LINKED(tselem->id) && (tselem->id->tag & LIB_TAG_EXTERN)) {
598     Main *bmain = CTX_data_main(C);
599     ID *override_id = BKE_override_library_create_from_id(bmain, tselem->id);
600     if (override_id != NULL) {
601       BKE_main_id_clear_newpoins(bmain);
602     }
603   }
604 }
605
606 static void id_fake_user_set_cb(bContext *UNUSED(C),
607                                 ReportList *UNUSED(reports),
608                                 Scene *UNUSED(scene),
609                                 TreeElement *UNUSED(te),
610                                 TreeStoreElem *UNUSED(tsep),
611                                 TreeStoreElem *tselem,
612                                 void *UNUSED(user_data))
613 {
614   ID *id = tselem->id;
615
616   id_fake_user_set(id);
617 }
618
619 static void id_fake_user_clear_cb(bContext *UNUSED(C),
620                                   ReportList *UNUSED(reports),
621                                   Scene *UNUSED(scene),
622                                   TreeElement *UNUSED(te),
623                                   TreeStoreElem *UNUSED(tsep),
624                                   TreeStoreElem *tselem,
625                                   void *UNUSED(user_data))
626 {
627   ID *id = tselem->id;
628
629   id_fake_user_clear(id);
630 }
631
632 static void id_select_linked_cb(bContext *C,
633                                 ReportList *UNUSED(reports),
634                                 Scene *UNUSED(scene),
635                                 TreeElement *UNUSED(te),
636                                 TreeStoreElem *UNUSED(tsep),
637                                 TreeStoreElem *tselem,
638                                 void *UNUSED(user_data))
639 {
640   ID *id = tselem->id;
641
642   ED_object_select_linked_by_id(C, id);
643 }
644
645 static void singleuser_action_cb(bContext *C,
646                                  ReportList *UNUSED(reports),
647                                  Scene *UNUSED(scene),
648                                  TreeElement *UNUSED(te),
649                                  TreeStoreElem *tsep,
650                                  TreeStoreElem *tselem,
651                                  void *UNUSED(user_data))
652 {
653   ID *id = tselem->id;
654
655   if (id) {
656     IdAdtTemplate *iat = (IdAdtTemplate *)tsep->id;
657     PointerRNA ptr = {{NULL}};
658     PropertyRNA *prop;
659
660     RNA_pointer_create(&iat->id, &RNA_AnimData, iat->adt, &ptr);
661     prop = RNA_struct_find_property(&ptr, "action");
662
663     id_single_user(C, id, &ptr, prop);
664   }
665 }
666
667 static void singleuser_world_cb(bContext *C,
668                                 ReportList *UNUSED(reports),
669                                 Scene *UNUSED(scene),
670                                 TreeElement *UNUSED(te),
671                                 TreeStoreElem *tsep,
672                                 TreeStoreElem *tselem,
673                                 void *UNUSED(user_data))
674 {
675   ID *id = tselem->id;
676
677   /* need to use parent scene not just scene, otherwise may end up getting wrong one */
678   if (id) {
679     Scene *parscene = (Scene *)tsep->id;
680     PointerRNA ptr = {{NULL}};
681     PropertyRNA *prop;
682
683     RNA_id_pointer_create(&parscene->id, &ptr);
684     prop = RNA_struct_find_property(&ptr, "world");
685
686     id_single_user(C, id, &ptr, prop);
687   }
688 }
689
690 /**
691  * \param select_recurse: Set to false for operations which are already
692  * recursively operating on their children.
693  */
694 void outliner_do_object_operation_ex(bContext *C,
695                                      ReportList *reports,
696                                      Scene *scene_act,
697                                      SpaceOutliner *soops,
698                                      ListBase *lb,
699                                      outliner_operation_cb operation_cb,
700                                      void *user_data,
701                                      bool select_recurse)
702 {
703   TreeElement *te;
704
705   for (te = lb->first; te; te = te->next) {
706     TreeStoreElem *tselem = TREESTORE(te);
707     bool select_handled = false;
708     if (tselem->flag & TSE_SELECTED) {
709       if (tselem->type == 0 && te->idcode == ID_OB) {
710         // when objects selected in other scenes... dunno if that should be allowed
711         Scene *scene_owner = (Scene *)outliner_search_back(soops, te, ID_SCE);
712         if (scene_owner && scene_act != scene_owner) {
713           WM_window_set_active_scene(CTX_data_main(C), C, CTX_wm_window(C), scene_owner);
714         }
715         /* important to use 'scene_owner' not scene_act else deleting objects can crash.
716          * only use 'scene_act' when 'scene_owner' is NULL, which can happen when the
717          * outliner isn't showing scenes: Visible Layer draw mode for eg. */
718         operation_cb(
719             C, reports, scene_owner ? scene_owner : scene_act, te, NULL, tselem, user_data);
720         select_handled = true;
721       }
722     }
723     if (TSELEM_OPEN(tselem, soops)) {
724       if ((select_handled == false) || select_recurse) {
725         outliner_do_object_operation_ex(
726             C, reports, scene_act, soops, &te->subtree, operation_cb, NULL, select_recurse);
727       }
728     }
729   }
730 }
731
732 void outliner_do_object_operation(bContext *C,
733                                   ReportList *reports,
734                                   Scene *scene_act,
735                                   SpaceOutliner *soops,
736                                   ListBase *lb,
737                                   outliner_operation_cb operation_cb)
738 {
739   outliner_do_object_operation_ex(C, reports, scene_act, soops, lb, operation_cb, NULL, true);
740 }
741
742 /* ******************************************** */
743
744 static void clear_animdata_cb(int UNUSED(event),
745                               TreeElement *UNUSED(te),
746                               TreeStoreElem *tselem,
747                               void *UNUSED(arg))
748 {
749   BKE_animdata_free(tselem->id, true);
750 }
751
752 static void unlinkact_animdata_cb(int UNUSED(event),
753                                   TreeElement *UNUSED(te),
754                                   TreeStoreElem *tselem,
755                                   void *UNUSED(arg))
756 {
757   /* just set action to NULL */
758   BKE_animdata_set_action(NULL, tselem->id, NULL);
759 }
760
761 static void cleardrivers_animdata_cb(int UNUSED(event),
762                                      TreeElement *UNUSED(te),
763                                      TreeStoreElem *tselem,
764                                      void *UNUSED(arg))
765 {
766   IdAdtTemplate *iat = (IdAdtTemplate *)tselem->id;
767
768   /* just free drivers - stored as a list of F-Curves */
769   free_fcurves(&iat->adt->drivers);
770 }
771
772 static void refreshdrivers_animdata_cb(int UNUSED(event),
773                                        TreeElement *UNUSED(te),
774                                        TreeStoreElem *tselem,
775                                        void *UNUSED(arg))
776 {
777   IdAdtTemplate *iat = (IdAdtTemplate *)tselem->id;
778   FCurve *fcu;
779
780   /* Loop over drivers, performing refresh
781    * (i.e. check graph_buttons.c and rna_fcurve.c for details). */
782   for (fcu = iat->adt->drivers.first; fcu; fcu = fcu->next) {
783     fcu->flag &= ~FCURVE_DISABLED;
784
785     if (fcu->driver) {
786       fcu->driver->flag &= ~DRIVER_FLAG_INVALID;
787     }
788   }
789 }
790
791 /* --------------------------------- */
792
793 typedef enum eOutliner_PropDataOps {
794   OL_DOP_SELECT = 1,
795   OL_DOP_DESELECT,
796   OL_DOP_HIDE,
797   OL_DOP_UNHIDE,
798   OL_DOP_SELECT_LINKED,
799 } eOutliner_PropDataOps;
800
801 typedef enum eOutliner_PropConstraintOps {
802   OL_CONSTRAINTOP_ENABLE = 1,
803   OL_CONSTRAINTOP_DISABLE,
804   OL_CONSTRAINTOP_DELETE,
805 } eOutliner_PropConstraintOps;
806
807 typedef enum eOutliner_PropModifierOps {
808   OL_MODIFIER_OP_TOGVIS = 1,
809   OL_MODIFIER_OP_TOGREN,
810   OL_MODIFIER_OP_DELETE,
811 } eOutliner_PropModifierOps;
812
813 static void pchan_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *UNUSED(arg))
814 {
815   bPoseChannel *pchan = (bPoseChannel *)te->directdata;
816
817   if (event == OL_DOP_SELECT) {
818     pchan->bone->flag |= BONE_SELECTED;
819   }
820   else if (event == OL_DOP_DESELECT) {
821     pchan->bone->flag &= ~BONE_SELECTED;
822   }
823   else if (event == OL_DOP_HIDE) {
824     pchan->bone->flag |= BONE_HIDDEN_P;
825     pchan->bone->flag &= ~BONE_SELECTED;
826   }
827   else if (event == OL_DOP_UNHIDE) {
828     pchan->bone->flag &= ~BONE_HIDDEN_P;
829   }
830 }
831
832 static void bone_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *UNUSED(arg))
833 {
834   Bone *bone = (Bone *)te->directdata;
835
836   if (event == OL_DOP_SELECT) {
837     bone->flag |= BONE_SELECTED;
838   }
839   else if (event == OL_DOP_DESELECT) {
840     bone->flag &= ~BONE_SELECTED;
841   }
842   else if (event == OL_DOP_HIDE) {
843     bone->flag |= BONE_HIDDEN_P;
844     bone->flag &= ~BONE_SELECTED;
845   }
846   else if (event == OL_DOP_UNHIDE) {
847     bone->flag &= ~BONE_HIDDEN_P;
848   }
849 }
850
851 static void ebone_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *UNUSED(arg))
852 {
853   EditBone *ebone = (EditBone *)te->directdata;
854
855   if (event == OL_DOP_SELECT) {
856     ebone->flag |= BONE_SELECTED;
857   }
858   else if (event == OL_DOP_DESELECT) {
859     ebone->flag &= ~BONE_SELECTED;
860   }
861   else if (event == OL_DOP_HIDE) {
862     ebone->flag |= BONE_HIDDEN_A;
863     ebone->flag &= ~BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL;
864   }
865   else if (event == OL_DOP_UNHIDE) {
866     ebone->flag &= ~BONE_HIDDEN_A;
867   }
868 }
869
870 static void sequence_cb(int event, TreeElement *te, TreeStoreElem *tselem, void *scene_ptr)
871 {
872   Sequence *seq = (Sequence *)te->directdata;
873   if (event == OL_DOP_SELECT) {
874     Scene *scene = (Scene *)scene_ptr;
875     Editing *ed = BKE_sequencer_editing_get(scene, false);
876     if (BLI_findindex(ed->seqbasep, seq) != -1) {
877       ED_sequencer_select_sequence_single(scene, seq, true);
878     }
879   }
880
881   (void)tselem;
882 }
883
884 static void gp_layer_cb(int event,
885                         TreeElement *te,
886                         TreeStoreElem *UNUSED(tselem),
887                         void *UNUSED(arg))
888 {
889   bGPDlayer *gpl = (bGPDlayer *)te->directdata;
890
891   if (event == OL_DOP_SELECT) {
892     gpl->flag |= GP_LAYER_SELECT;
893   }
894   else if (event == OL_DOP_DESELECT) {
895     gpl->flag &= ~GP_LAYER_SELECT;
896   }
897   else if (event == OL_DOP_HIDE) {
898     gpl->flag |= GP_LAYER_HIDE;
899   }
900   else if (event == OL_DOP_UNHIDE) {
901     gpl->flag &= ~GP_LAYER_HIDE;
902   }
903 }
904
905 static void data_select_linked_cb(int event,
906                                   TreeElement *te,
907                                   TreeStoreElem *UNUSED(tselem),
908                                   void *C_v)
909 {
910   if (event == OL_DOP_SELECT_LINKED) {
911     if (RNA_struct_is_ID(te->rnaptr.type)) {
912       bContext *C = (bContext *)C_v;
913       ID *id = te->rnaptr.data;
914
915       ED_object_select_linked_by_id(C, id);
916     }
917   }
918 }
919
920 static void constraint_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *C_v)
921 {
922   bContext *C = C_v;
923   Main *bmain = CTX_data_main(C);
924   SpaceOutliner *soops = CTX_wm_space_outliner(C);
925   bConstraint *constraint = (bConstraint *)te->directdata;
926   Object *ob = (Object *)outliner_search_back(soops, te, ID_OB);
927
928   if (event == OL_CONSTRAINTOP_ENABLE) {
929     constraint->flag &= ~CONSTRAINT_OFF;
930     ED_object_constraint_update(bmain, ob);
931     WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
932   }
933   else if (event == OL_CONSTRAINTOP_DISABLE) {
934     constraint->flag = CONSTRAINT_OFF;
935     ED_object_constraint_update(bmain, ob);
936     WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
937   }
938   else if (event == OL_CONSTRAINTOP_DELETE) {
939     ListBase *lb = NULL;
940
941     if (TREESTORE(te->parent->parent)->type == TSE_POSE_CHANNEL) {
942       lb = &((bPoseChannel *)te->parent->parent->directdata)->constraints;
943     }
944     else {
945       lb = &ob->constraints;
946     }
947
948     if (BKE_constraint_remove_ex(lb, ob, constraint, true)) {
949       /* there's no active constraint now, so make sure this is the case */
950       BKE_constraints_active_set(&ob->constraints, NULL);
951
952       /* needed to set the flags on posebones correctly */
953       ED_object_constraint_update(bmain, ob);
954
955       WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob);
956       te->store_elem->flag &= ~TSE_SELECTED;
957     }
958   }
959 }
960
961 static void modifier_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *Carg)
962 {
963   bContext *C = (bContext *)Carg;
964   Main *bmain = CTX_data_main(C);
965   SpaceOutliner *soops = CTX_wm_space_outliner(C);
966   ModifierData *md = (ModifierData *)te->directdata;
967   Object *ob = (Object *)outliner_search_back(soops, te, ID_OB);
968
969   if (event == OL_MODIFIER_OP_TOGVIS) {
970     md->mode ^= eModifierMode_Realtime;
971     DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
972     WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
973   }
974   else if (event == OL_MODIFIER_OP_TOGREN) {
975     md->mode ^= eModifierMode_Render;
976     DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
977     WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
978   }
979   else if (event == OL_MODIFIER_OP_DELETE) {
980     ED_object_modifier_remove(NULL, bmain, ob, md);
981     WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER | NA_REMOVED, ob);
982     te->store_elem->flag &= ~TSE_SELECTED;
983   }
984 }
985
986 static void outliner_do_data_operation(
987     SpaceOutliner *soops,
988     int type,
989     int event,
990     ListBase *lb,
991     void (*operation_cb)(int, TreeElement *, TreeStoreElem *, void *),
992     void *arg)
993 {
994   TreeElement *te;
995   TreeStoreElem *tselem;
996
997   for (te = lb->first; te; te = te->next) {
998     tselem = TREESTORE(te);
999     if (tselem->flag & TSE_SELECTED) {
1000       if (tselem->type == type) {
1001         operation_cb(event, te, tselem, arg);
1002       }
1003     }
1004     if (TSELEM_OPEN(tselem, soops)) {
1005       outliner_do_data_operation(soops, type, event, &te->subtree, operation_cb, arg);
1006     }
1007   }
1008 }
1009
1010 static Base *outline_delete_hierarchy(bContext *C, ReportList *reports, Scene *scene, Base *base)
1011 {
1012   Base *child_base, *base_next;
1013   Object *parent;
1014   ViewLayer *view_layer = CTX_data_view_layer(C);
1015
1016   if (!base) {
1017     return NULL;
1018   }
1019
1020   for (child_base = view_layer->object_bases.first; child_base; child_base = base_next) {
1021     base_next = child_base->next;
1022     for (parent = child_base->object->parent; parent && (parent != base->object);
1023          parent = parent->parent) {
1024       /* pass */
1025     }
1026     if (parent) {
1027       base_next = outline_delete_hierarchy(C, reports, scene, child_base);
1028     }
1029   }
1030
1031   base_next = base->next;
1032
1033   Main *bmain = CTX_data_main(C);
1034   if (base->object->id.tag & LIB_TAG_INDIRECT) {
1035     BKE_reportf(reports,
1036                 RPT_WARNING,
1037                 "Cannot delete indirectly linked object '%s'",
1038                 base->object->id.name + 2);
1039     return base_next;
1040   }
1041   else if (BKE_library_ID_is_indirectly_used(bmain, base->object) &&
1042            ID_REAL_USERS(base->object) <= 1 && ID_EXTRA_USERS(base->object) == 0) {
1043     BKE_reportf(reports,
1044                 RPT_WARNING,
1045                 "Cannot delete object '%s' from scene '%s', indirectly used objects need at least "
1046                 "one user",
1047                 base->object->id.name + 2,
1048                 scene->id.name + 2);
1049     return base_next;
1050   }
1051   ED_object_base_free_and_unlink(CTX_data_main(C), scene, base->object);
1052   return base_next;
1053 }
1054
1055 static void object_delete_hierarchy_cb(bContext *C,
1056                                        ReportList *reports,
1057                                        Scene *scene,
1058                                        TreeElement *te,
1059                                        TreeStoreElem *UNUSED(tsep),
1060                                        TreeStoreElem *tselem,
1061                                        void *UNUSED(user_data))
1062 {
1063   ViewLayer *view_layer = CTX_data_view_layer(C);
1064   Base *base = (Base *)te->directdata;
1065   Object *obedit = CTX_data_edit_object(C);
1066
1067   if (!base) {
1068     base = BKE_view_layer_base_find(view_layer, (Object *)tselem->id);
1069   }
1070   if (base) {
1071     /* Check also library later. */
1072     for (; obedit && (obedit != base->object); obedit = obedit->parent) {
1073       /* pass */
1074     }
1075     if (obedit == base->object) {
1076       ED_object_editmode_exit(C, EM_FREEDATA);
1077     }
1078
1079     outline_delete_hierarchy(C, reports, scene, base);
1080     /* leave for ED_outliner_id_unref to handle */
1081 #if 0
1082     te->directdata = NULL;
1083     tselem->id = NULL;
1084 #endif
1085   }
1086
1087   DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1088   WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
1089 }
1090
1091 static Base *outline_batch_delete_hierarchy(
1092     ReportList *reports, Main *bmain, ViewLayer *view_layer, Scene *scene, Base *base)
1093 {
1094   Base *child_base, *base_next;
1095   Object *object, *parent;
1096
1097   if (!base) {
1098     return NULL;
1099   }
1100
1101   object = base->object;
1102   for (child_base = view_layer->object_bases.first; child_base; child_base = base_next) {
1103     base_next = child_base->next;
1104     for (parent = child_base->object->parent; parent && (parent != object);
1105          parent = parent->parent) {
1106       /* pass */
1107     }
1108     if (parent) {
1109       base_next = outline_batch_delete_hierarchy(reports, bmain, view_layer, scene, child_base);
1110     }
1111   }
1112
1113   base_next = base->next;
1114
1115   if (object->id.tag & LIB_TAG_INDIRECT) {
1116     BKE_reportf(reports,
1117                 RPT_WARNING,
1118                 "Cannot delete indirectly linked object '%s'",
1119                 base->object->id.name + 2);
1120     return base_next;
1121   }
1122   else if (BKE_library_ID_is_indirectly_used(bmain, object) && ID_REAL_USERS(object) <= 1 &&
1123            ID_EXTRA_USERS(object) == 0) {
1124     BKE_reportf(reports,
1125                 RPT_WARNING,
1126                 "Cannot delete object '%s' from scene '%s', indirectly used objects need at least "
1127                 "one user",
1128                 object->id.name + 2,
1129                 scene->id.name + 2);
1130     return base_next;
1131   }
1132
1133   DEG_id_tag_update_ex(bmain, &object->id, ID_RECALC_BASE_FLAGS);
1134   BKE_scene_collections_object_remove(bmain, scene, object, false);
1135
1136   if (object->id.us == 0) {
1137     object->id.tag |= LIB_TAG_DOIT;
1138   }
1139
1140   return base_next;
1141 }
1142
1143 static void object_batch_delete_hierarchy_cb(bContext *C,
1144                                              ReportList *reports,
1145                                              Scene *scene,
1146                                              TreeElement *te,
1147                                              TreeStoreElem *UNUSED(tsep),
1148                                              TreeStoreElem *tselem,
1149                                              void *UNUSED(user_data))
1150 {
1151   ViewLayer *view_layer = CTX_data_view_layer(C);
1152   Base *base = (Base *)te->directdata;
1153   Object *obedit = CTX_data_edit_object(C);
1154
1155   if (!base) {
1156     base = BKE_view_layer_base_find(view_layer, (Object *)tselem->id);
1157   }
1158   if (base) {
1159     /* Check also library later. */
1160     for (; obedit && (obedit != base->object); obedit = obedit->parent) {
1161       /* pass */
1162     }
1163     if (obedit == base->object) {
1164       ED_object_editmode_exit(C, EM_FREEDATA);
1165     }
1166
1167     outline_batch_delete_hierarchy(reports, CTX_data_main(C), view_layer, scene, base);
1168     /* leave for ED_outliner_id_unref to handle */
1169 #if 0
1170     te->directdata = NULL;
1171     tselem->id = NULL;
1172 #endif
1173   }
1174 }
1175
1176 /* **************************************** */
1177
1178 enum {
1179   OL_OP_SELECT = 1,
1180   OL_OP_DESELECT,
1181   OL_OP_SELECT_HIERARCHY,
1182   OL_OP_DELETE,
1183   OL_OP_DELETE_HIERARCHY,
1184   OL_OP_REMAP,
1185   OL_OP_LOCALIZED, /* disabled, see below */
1186   OL_OP_TOGVIS,
1187   OL_OP_TOGSEL,
1188   OL_OP_TOGREN,
1189   OL_OP_RENAME,
1190   OL_OP_OBJECT_MODE_ENTER,
1191   OL_OP_OBJECT_MODE_EXIT,
1192 };
1193
1194 static const EnumPropertyItem prop_object_op_types[] = {
1195     {OL_OP_SELECT, "SELECT", ICON_RESTRICT_SELECT_OFF, "Select", ""},
1196     {OL_OP_DESELECT, "DESELECT", 0, "Deselect", ""},
1197     {OL_OP_SELECT_HIERARCHY, "SELECT_HIERARCHY", 0, "Select Hierarchy", ""},
1198     {OL_OP_DELETE, "DELETE", ICON_X, "Delete", ""},
1199     {OL_OP_DELETE_HIERARCHY, "DELETE_HIERARCHY", 0, "Delete Hierarchy", ""},
1200     {OL_OP_REMAP,
1201      "REMAP",
1202      0,
1203      "Remap Users",
1204      "Make all users of selected data-blocks to use instead a new chosen one"},
1205     {OL_OP_RENAME, "RENAME", 0, "Rename", ""},
1206     {OL_OP_OBJECT_MODE_ENTER, "OBJECT_MODE_ENTER", 0, "Enter Mode", ""},
1207     {OL_OP_OBJECT_MODE_EXIT, "OBJECT_MODE_EXIT", 0, "Exit Mode", ""},
1208     {0, NULL, 0, NULL, NULL},
1209 };
1210
1211 static int outliner_object_operation_exec(bContext *C, wmOperator *op)
1212 {
1213   struct wmMsgBus *mbus = CTX_wm_message_bus(C);
1214   Main *bmain = CTX_data_main(C);
1215   Scene *scene = CTX_data_scene(C);
1216   wmWindow *win = CTX_wm_window(C);
1217   SpaceOutliner *soops = CTX_wm_space_outliner(C);
1218   int event;
1219   const char *str = NULL;
1220
1221   /* check for invalid states */
1222   if (soops == NULL) {
1223     return OPERATOR_CANCELLED;
1224   }
1225
1226   event = RNA_enum_get(op->ptr, "type");
1227
1228   if (event == OL_OP_SELECT) {
1229     Scene *sce = scene;  // to be able to delete, scenes are set...
1230     outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_select_cb);
1231     if (scene != sce) {
1232       WM_window_set_active_scene(bmain, C, win, sce);
1233     }
1234
1235     str = "Select Objects";
1236     DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1237     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1238   }
1239   else if (event == OL_OP_SELECT_HIERARCHY) {
1240     Scene *sce = scene;  // to be able to delete, scenes are set...
1241     outliner_do_object_operation_ex(
1242         C, op->reports, scene, soops, &soops->tree, object_select_hierarchy_cb, NULL, false);
1243     if (scene != sce) {
1244       WM_window_set_active_scene(bmain, C, win, sce);
1245     }
1246     str = "Select Object Hierarchy";
1247     DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1248     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1249   }
1250   else if (event == OL_OP_DESELECT) {
1251     outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_deselect_cb);
1252     str = "Deselect Objects";
1253     DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1254     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1255   }
1256   else if (event == OL_OP_DELETE) {
1257     ViewLayer *view_layer = CTX_data_view_layer(C);
1258     const Base *basact_prev = BASACT(view_layer);
1259
1260     outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_delete_cb);
1261
1262     /* XXX: tree management normally happens from draw_outliner(), but when
1263      *      you're clicking to fast on Delete object from context menu in
1264      *      outliner several mouse events can be handled in one cycle without
1265      *      handling notifiers/redraw which leads to deleting the same object twice.
1266      *      cleanup tree here to prevent such cases. */
1267     outliner_cleanup_tree(soops);
1268
1269     DEG_relations_tag_update(bmain);
1270     str = "Delete Objects";
1271     DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1272     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1273     if (basact_prev != BASACT(view_layer)) {
1274       WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
1275       WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, LayerObjects, active);
1276     }
1277   }
1278   else if (event == OL_OP_DELETE_HIERARCHY) {
1279     ViewLayer *view_layer = CTX_data_view_layer(C);
1280     const Base *basact_prev = BASACT(view_layer);
1281
1282     /* Keeping old 'safe and slow' code for a bit (new one enabled on 28/01/2019). */
1283     if (G.debug_value == 666) {
1284       outliner_do_object_operation_ex(
1285           C, op->reports, scene, soops, &soops->tree, object_delete_hierarchy_cb, NULL, false);
1286     }
1287     else {
1288       BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
1289
1290       outliner_do_object_operation_ex(C,
1291                                       op->reports,
1292                                       scene,
1293                                       soops,
1294                                       &soops->tree,
1295                                       object_batch_delete_hierarchy_cb,
1296                                       NULL,
1297                                       false);
1298
1299       BKE_id_multi_tagged_delete(bmain);
1300     }
1301
1302     /* XXX: See OL_OP_DELETE comment above. */
1303     outliner_cleanup_tree(soops);
1304
1305     DEG_relations_tag_update(bmain);
1306     str = "Delete Object Hierarchy";
1307     DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1308     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1309     if (basact_prev != BASACT(view_layer)) {
1310       WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
1311       WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, LayerObjects, active);
1312     }
1313   }
1314   else if (event == OL_OP_REMAP) {
1315     outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_remap_cb, NULL);
1316     str = "Remap ID";
1317   }
1318   else if (event == OL_OP_LOCALIZED) { /* disabled, see above enum (ton) */
1319     outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, id_local_cb);
1320     str = "Localized Objects";
1321   }
1322   else if (event == OL_OP_RENAME) {
1323     outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, item_rename_cb);
1324     str = "Rename Object";
1325   }
1326   else if (event == OL_OP_OBJECT_MODE_ENTER) {
1327     outliner_do_object_operation(
1328         C, op->reports, scene, soops, &soops->tree, item_object_mode_enter_cb);
1329     str = "Enter Current Mode";
1330   }
1331   else if (event == OL_OP_OBJECT_MODE_EXIT) {
1332     outliner_do_object_operation(
1333         C, op->reports, scene, soops, &soops->tree, item_object_mode_exit_cb);
1334     str = "Exit Current Mode";
1335   }
1336   else {
1337     BLI_assert(0);
1338     return OPERATOR_CANCELLED;
1339   }
1340
1341   ED_undo_push(C, str);
1342
1343   return OPERATOR_FINISHED;
1344 }
1345
1346 void OUTLINER_OT_object_operation(wmOperatorType *ot)
1347 {
1348   /* identifiers */
1349   ot->name = "Outliner Object Operation";
1350   ot->idname = "OUTLINER_OT_object_operation";
1351
1352   /* callbacks */
1353   ot->invoke = WM_menu_invoke;
1354   ot->exec = outliner_object_operation_exec;
1355   ot->poll = ED_operator_outliner_active;
1356
1357   ot->flag = 0;
1358
1359   ot->prop = RNA_def_enum(ot->srna, "type", prop_object_op_types, 0, "Object Operation", "");
1360 }
1361
1362 /* **************************************** */
1363
1364 typedef enum eOutlinerIdOpTypes {
1365   OUTLINER_IDOP_INVALID = 0,
1366
1367   OUTLINER_IDOP_UNLINK,
1368   OUTLINER_IDOP_LOCAL,
1369   OUTLINER_IDOP_OVERRIDE_LIBRARY,
1370   OUTLINER_IDOP_SINGLE,
1371   OUTLINER_IDOP_DELETE,
1372   OUTLINER_IDOP_REMAP,
1373
1374   OUTLINER_IDOP_COPY,
1375   OUTLINER_IDOP_PASTE,
1376
1377   OUTLINER_IDOP_FAKE_ADD,
1378   OUTLINER_IDOP_FAKE_CLEAR,
1379   OUTLINER_IDOP_RENAME,
1380
1381   OUTLINER_IDOP_SELECT_LINKED,
1382 } eOutlinerIdOpTypes;
1383
1384 // TODO: implement support for changing the ID-block used
1385 static const EnumPropertyItem prop_id_op_types[] = {
1386     {OUTLINER_IDOP_UNLINK, "UNLINK", 0, "Unlink", ""},
1387     {OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""},
1388     {OUTLINER_IDOP_OVERRIDE_LIBRARY,
1389      "OVERRIDE_LIBRARY",
1390      0,
1391      "Add Library Override",
1392      "Add a local override of this linked data-block"},
1393     {OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""},
1394     {OUTLINER_IDOP_DELETE, "DELETE", ICON_X, "Delete", ""},
1395     {OUTLINER_IDOP_REMAP,
1396      "REMAP",
1397      0,
1398      "Remap Users",
1399      "Make all users of selected data-blocks to use instead current (clicked) one"},
1400     {0, "", 0, NULL, NULL},
1401     {OUTLINER_IDOP_COPY, "COPY", ICON_COPYDOWN, "Copy", ""},
1402     {OUTLINER_IDOP_PASTE, "PASTE", ICON_PASTEDOWN, "Paste", ""},
1403     {0, "", 0, NULL, NULL},
1404     {OUTLINER_IDOP_FAKE_ADD,
1405      "ADD_FAKE",
1406      0,
1407      "Add Fake User",
1408      "Ensure data-block gets saved even if it isn't in use (e.g. for motion and material "
1409      "libraries)"},
1410     {OUTLINER_IDOP_FAKE_CLEAR, "CLEAR_FAKE", 0, "Clear Fake User", ""},
1411     {OUTLINER_IDOP_RENAME, "RENAME", 0, "Rename", ""},
1412     {OUTLINER_IDOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""},
1413     {0, NULL, 0, NULL, NULL},
1414 };
1415
1416 static const EnumPropertyItem *outliner_id_operation_itemf(bContext *UNUSED(C),
1417                                                            PointerRNA *UNUSED(ptr),
1418                                                            PropertyRNA *UNUSED(prop),
1419                                                            bool *r_free)
1420 {
1421   if (BKE_override_library_is_enabled()) {
1422     *r_free = false;
1423     return prop_id_op_types;
1424   }
1425
1426   EnumPropertyItem *items = NULL;
1427   int totitem = 0;
1428
1429   for (const EnumPropertyItem *it = prop_id_op_types; it->identifier != NULL; it++) {
1430     if (it->value == OUTLINER_IDOP_OVERRIDE_LIBRARY) {
1431       continue;
1432     }
1433     RNA_enum_item_add(&items, &totitem, it);
1434   }
1435   RNA_enum_item_end(&items, &totitem);
1436   *r_free = true;
1437
1438   return items;
1439 }
1440
1441 static int outliner_id_operation_exec(bContext *C, wmOperator *op)
1442 {
1443   Scene *scene = CTX_data_scene(C);
1444   SpaceOutliner *soops = CTX_wm_space_outliner(C);
1445   int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1446   eOutlinerIdOpTypes event;
1447
1448   /* check for invalid states */
1449   if (soops == NULL) {
1450     return OPERATOR_CANCELLED;
1451   }
1452
1453   set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1454
1455   event = RNA_enum_get(op->ptr, "type");
1456
1457   switch (event) {
1458     case OUTLINER_IDOP_UNLINK: {
1459       /* unlink datablock from its parent */
1460       if (objectlevel) {
1461         outliner_do_libdata_operation(
1462             C, op->reports, scene, soops, &soops->tree, unlink_object_cb, NULL);
1463
1464         WM_event_add_notifier(C, NC_SCENE | ND_LAYER, NULL);
1465         ED_undo_push(C, "Unlink Object");
1466         break;
1467       }
1468
1469       switch (idlevel) {
1470         case ID_AC:
1471           outliner_do_libdata_operation(
1472               C, op->reports, scene, soops, &soops->tree, unlink_action_cb, NULL);
1473
1474           WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
1475           ED_undo_push(C, "Unlink action");
1476           break;
1477         case ID_MA:
1478           outliner_do_libdata_operation(
1479               C, op->reports, scene, soops, &soops->tree, unlink_material_cb, NULL);
1480
1481           WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, NULL);
1482           ED_undo_push(C, "Unlink material");
1483           break;
1484         case ID_TE:
1485           outliner_do_libdata_operation(
1486               C, op->reports, scene, soops, &soops->tree, unlink_texture_cb, NULL);
1487
1488           WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, NULL);
1489           ED_undo_push(C, "Unlink texture");
1490           break;
1491         case ID_WO:
1492           outliner_do_libdata_operation(
1493               C, op->reports, scene, soops, &soops->tree, unlink_world_cb, NULL);
1494
1495           WM_event_add_notifier(C, NC_SCENE | ND_WORLD, NULL);
1496           ED_undo_push(C, "Unlink world");
1497           break;
1498         case ID_GR:
1499           outliner_do_libdata_operation(
1500               C, op->reports, scene, soops, &soops->tree, unlink_collection_cb, NULL);
1501
1502           WM_event_add_notifier(C, NC_SCENE | ND_LAYER, NULL);
1503           ED_undo_push(C, "Unlink Collection");
1504           break;
1505         default:
1506           BKE_report(op->reports, RPT_WARNING, "Not yet implemented");
1507           break;
1508       }
1509       break;
1510     }
1511     case OUTLINER_IDOP_LOCAL: {
1512       /* make local */
1513       outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_local_cb, NULL);
1514       ED_undo_push(C, "Localized Data");
1515       break;
1516     }
1517     case OUTLINER_IDOP_OVERRIDE_LIBRARY: {
1518       if (BKE_override_library_is_enabled()) {
1519         /* make local */
1520         outliner_do_libdata_operation(
1521             C, op->reports, scene, soops, &soops->tree, id_override_library_cb, NULL);
1522         ED_undo_push(C, "Overridden Data");
1523       }
1524       break;
1525     }
1526     case OUTLINER_IDOP_SINGLE: {
1527       /* make single user */
1528       switch (idlevel) {
1529         case ID_AC:
1530           outliner_do_libdata_operation(
1531               C, op->reports, scene, soops, &soops->tree, singleuser_action_cb, NULL);
1532
1533           WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
1534           ED_undo_push(C, "Single-User Action");
1535           break;
1536
1537         case ID_WO:
1538           outliner_do_libdata_operation(
1539               C, op->reports, scene, soops, &soops->tree, singleuser_world_cb, NULL);
1540
1541           WM_event_add_notifier(C, NC_SCENE | ND_WORLD, NULL);
1542           ED_undo_push(C, "Single-User World");
1543           break;
1544
1545         default:
1546           BKE_report(op->reports, RPT_WARNING, "Not yet implemented");
1547           break;
1548       }
1549       break;
1550     }
1551     case OUTLINER_IDOP_DELETE: {
1552       if (idlevel > 0) {
1553         outliner_do_libdata_operation(
1554             C, op->reports, scene, soops, &soops->tree, id_delete_cb, NULL);
1555         ED_undo_push(C, "Delete");
1556       }
1557       break;
1558     }
1559     case OUTLINER_IDOP_REMAP: {
1560       if (idlevel > 0) {
1561         outliner_do_libdata_operation(
1562             C, op->reports, scene, soops, &soops->tree, id_remap_cb, NULL);
1563         ED_undo_push(C, "Remap");
1564       }
1565       break;
1566     }
1567     case OUTLINER_IDOP_COPY: {
1568       WM_operator_name_call(C, "OUTLINER_OT_id_copy", WM_OP_INVOKE_DEFAULT, NULL);
1569       break;
1570     }
1571     case OUTLINER_IDOP_PASTE: {
1572       WM_operator_name_call(C, "OUTLINER_OT_id_paste", WM_OP_INVOKE_DEFAULT, NULL);
1573       ED_undo_push(C, "Paste");
1574       break;
1575     }
1576     case OUTLINER_IDOP_FAKE_ADD: {
1577       /* set fake user */
1578       outliner_do_libdata_operation(
1579           C, op->reports, scene, soops, &soops->tree, id_fake_user_set_cb, NULL);
1580
1581       WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
1582       ED_undo_push(C, "Add Fake User");
1583       break;
1584     }
1585     case OUTLINER_IDOP_FAKE_CLEAR: {
1586       /* clear fake user */
1587       outliner_do_libdata_operation(
1588           C, op->reports, scene, soops, &soops->tree, id_fake_user_clear_cb, NULL);
1589
1590       WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
1591       ED_undo_push(C, "Clear Fake User");
1592       break;
1593     }
1594     case OUTLINER_IDOP_RENAME: {
1595       /* rename */
1596       outliner_do_libdata_operation(
1597           C, op->reports, scene, soops, &soops->tree, item_rename_cb, NULL);
1598
1599       WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
1600       ED_undo_push(C, "Rename");
1601       break;
1602     }
1603     case OUTLINER_IDOP_SELECT_LINKED:
1604       outliner_do_libdata_operation(
1605           C, op->reports, scene, soops, &soops->tree, id_select_linked_cb, NULL);
1606       ED_undo_push(C, "Select");
1607       break;
1608
1609     default:
1610       // invalid - unhandled
1611       break;
1612   }
1613
1614   /* wrong notifier still... */
1615   WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
1616
1617   // XXX: this is just so that outliner is always up to date
1618   WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, NULL);
1619
1620   return OPERATOR_FINISHED;
1621 }
1622
1623 void OUTLINER_OT_id_operation(wmOperatorType *ot)
1624 {
1625   /* identifiers */
1626   ot->name = "Outliner ID data Operation";
1627   ot->idname = "OUTLINER_OT_id_operation";
1628
1629   /* callbacks */
1630   ot->invoke = WM_menu_invoke;
1631   ot->exec = outliner_id_operation_exec;
1632   ot->poll = ED_operator_outliner_active;
1633
1634   ot->flag = 0;
1635
1636   ot->prop = RNA_def_enum(ot->srna, "type", prop_id_op_types, 0, "ID data Operation", "");
1637   RNA_def_enum_funcs(ot->prop, outliner_id_operation_itemf);
1638 }
1639
1640 /* **************************************** */
1641
1642 typedef enum eOutlinerLibOpTypes {
1643   OL_LIB_INVALID = 0,
1644
1645   OL_LIB_RENAME,
1646   OL_LIB_DELETE,
1647   OL_LIB_RELOCATE,
1648   OL_LIB_RELOAD,
1649 } eOutlinerLibOpTypes;
1650
1651 static const EnumPropertyItem outliner_lib_op_type_items[] = {
1652     {OL_LIB_RENAME, "RENAME", 0, "Rename", ""},
1653     {OL_LIB_DELETE,
1654      "DELETE",
1655      ICON_X,
1656      "Delete",
1657      "Delete this library and all its item from Blender - WARNING: no undo"},
1658     {OL_LIB_RELOCATE,
1659      "RELOCATE",
1660      0,
1661      "Relocate",
1662      "Select a new path for this library, and reload all its data"},
1663     {OL_LIB_RELOAD, "RELOAD", ICON_FILE_REFRESH, "Reload", "Reload all data from this library"},
1664     {0, NULL, 0, NULL, NULL},
1665 };
1666
1667 static int outliner_lib_operation_exec(bContext *C, wmOperator *op)
1668 {
1669   Scene *scene = CTX_data_scene(C);
1670   SpaceOutliner *soops = CTX_wm_space_outliner(C);
1671   int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1672   eOutlinerLibOpTypes event;
1673
1674   /* check for invalid states */
1675   if (soops == NULL) {
1676     return OPERATOR_CANCELLED;
1677   }
1678
1679   set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1680
1681   event = RNA_enum_get(op->ptr, "type");
1682
1683   switch (event) {
1684     case OL_LIB_RENAME: {
1685       /* rename */
1686       outliner_do_libdata_operation(
1687           C, op->reports, scene, soops, &soops->tree, item_rename_cb, NULL);
1688
1689       WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
1690       ED_undo_push(C, "Rename Library");
1691       break;
1692     }
1693     case OL_LIB_DELETE: {
1694       outliner_do_libdata_operation(
1695           C, op->reports, scene, soops, &soops->tree, id_delete_cb, NULL);
1696       ED_undo_push(C, "Delete Library");
1697       break;
1698     }
1699     case OL_LIB_RELOCATE: {
1700       /* rename */
1701       outliner_do_libdata_operation(
1702           C, op->reports, scene, soops, &soops->tree, lib_relocate_cb, NULL);
1703       ED_undo_push(C, "Relocate Library");
1704       break;
1705     }
1706     case OL_LIB_RELOAD: {
1707       /* rename */
1708       outliner_do_libdata_operation(
1709           C, op->reports, scene, soops, &soops->tree, lib_reload_cb, NULL);
1710       break;
1711     }
1712     default:
1713       /* invalid - unhandled */
1714       break;
1715   }
1716
1717   /* wrong notifier still... */
1718   WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
1719
1720   /* XXX: this is just so that outliner is always up to date */
1721   WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, NULL);
1722
1723   return OPERATOR_FINISHED;
1724 }
1725
1726 void OUTLINER_OT_lib_operation(wmOperatorType *ot)
1727 {
1728   /* identifiers */
1729   ot->name = "Outliner Library Operation";
1730   ot->idname = "OUTLINER_OT_lib_operation";
1731
1732   /* callbacks */
1733   ot->invoke = WM_menu_invoke;
1734   ot->exec = outliner_lib_operation_exec;
1735   ot->poll = ED_operator_outliner_active;
1736
1737   ot->prop = RNA_def_enum(
1738       ot->srna, "type", outliner_lib_op_type_items, 0, "Library Operation", "");
1739 }
1740
1741 /* **************************************** */
1742
1743 static void outliner_do_id_set_operation(
1744     SpaceOutliner *soops,
1745     int type,
1746     ListBase *lb,
1747     ID *newid,
1748     void (*operation_cb)(TreeElement *, TreeStoreElem *, TreeStoreElem *, ID *))
1749 {
1750   TreeElement *te;
1751   TreeStoreElem *tselem;
1752
1753   for (te = lb->first; te; te = te->next) {
1754     tselem = TREESTORE(te);
1755     if (tselem->flag & TSE_SELECTED) {
1756       if (tselem->type == type) {
1757         TreeStoreElem *tsep = te->parent ? TREESTORE(te->parent) : NULL;
1758         operation_cb(te, tselem, tsep, newid);
1759       }
1760     }
1761     if (TSELEM_OPEN(tselem, soops)) {
1762       outliner_do_id_set_operation(soops, type, &te->subtree, newid, operation_cb);
1763     }
1764   }
1765 }
1766
1767 /* ------------------------------------------ */
1768
1769 static void actionset_id_cb(TreeElement *UNUSED(te),
1770                             TreeStoreElem *tselem,
1771                             TreeStoreElem *tsep,
1772                             ID *actId)
1773 {
1774   bAction *act = (bAction *)actId;
1775
1776   if (tselem->type == TSE_ANIM_DATA) {
1777     /* "animation" entries - action is child of this */
1778     BKE_animdata_set_action(NULL, tselem->id, act);
1779   }
1780   /* TODO: if any other "expander" channels which own actions need to support this menu,
1781    * add: tselem->type = ...
1782    */
1783   else if (tsep && (tsep->type == TSE_ANIM_DATA)) {
1784     /* "animation" entries case again */
1785     BKE_animdata_set_action(NULL, tsep->id, act);
1786   }
1787   // TODO: other cases not supported yet
1788 }
1789
1790 static int outliner_action_set_exec(bContext *C, wmOperator *op)
1791 {
1792   SpaceOutliner *soops = CTX_wm_space_outliner(C);
1793   int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1794
1795   bAction *act;
1796
1797   /* check for invalid states */
1798   if (soops == NULL) {
1799     return OPERATOR_CANCELLED;
1800   }
1801   set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1802
1803   /* get action to use */
1804   act = BLI_findlink(&CTX_data_main(C)->actions, RNA_enum_get(op->ptr, "action"));
1805
1806   if (act == NULL) {
1807     BKE_report(op->reports, RPT_ERROR, "No valid action to add");
1808     return OPERATOR_CANCELLED;
1809   }
1810   else if (act->idroot == 0) {
1811     /* Hopefully in this case (i.e. library of userless actions),
1812      * the user knows what they're doing. */
1813     BKE_reportf(op->reports,
1814                 RPT_WARNING,
1815                 "Action '%s' does not specify what data-blocks it can be used on "
1816                 "(try setting the 'ID Root Type' setting from the data-blocks editor "
1817                 "for this action to avoid future problems)",
1818                 act->id.name + 2);
1819   }
1820
1821   /* perform action if valid channel */
1822   if (datalevel == TSE_ANIM_DATA) {
1823     outliner_do_id_set_operation(soops, datalevel, &soops->tree, (ID *)act, actionset_id_cb);
1824   }
1825   else if (idlevel == ID_AC) {
1826     outliner_do_id_set_operation(soops, idlevel, &soops->tree, (ID *)act, actionset_id_cb);
1827   }
1828   else {
1829     return OPERATOR_CANCELLED;
1830   }
1831
1832   /* set notifier that things have changed */
1833   WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
1834   ED_undo_push(C, "Set action");
1835
1836   /* done */
1837   return OPERATOR_FINISHED;
1838 }
1839
1840 void OUTLINER_OT_action_set(wmOperatorType *ot)
1841 {
1842   PropertyRNA *prop;
1843
1844   /* identifiers */
1845   ot->name = "Outliner Set Action";
1846   ot->idname = "OUTLINER_OT_action_set";
1847   ot->description = "Change the active action used";
1848
1849   /* api callbacks */
1850   ot->invoke = WM_enum_search_invoke;
1851   ot->exec = outliner_action_set_exec;
1852   ot->poll = ED_operator_outliner_active;
1853
1854   /* flags */
1855   ot->flag = 0;
1856
1857   /* props */
1858   // TODO: this would be nicer as an ID-pointer...
1859   prop = RNA_def_enum(ot->srna, "action", DummyRNA_NULL_items, 0, "Action", "");
1860   RNA_def_enum_funcs(prop, RNA_action_itemf);
1861   RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
1862   ot->prop = prop;
1863 }
1864
1865 /* **************************************** */
1866
1867 typedef enum eOutliner_AnimDataOps {
1868   OUTLINER_ANIMOP_INVALID = 0,
1869
1870   OUTLINER_ANIMOP_CLEAR_ADT,
1871
1872   OUTLINER_ANIMOP_SET_ACT,
1873   OUTLINER_ANIMOP_CLEAR_ACT,
1874
1875   OUTLINER_ANIMOP_REFRESH_DRV,
1876   OUTLINER_ANIMOP_CLEAR_DRV
1877
1878   // OUTLINER_ANIMOP_COPY_DRIVERS,
1879   // OUTLINER_ANIMOP_PASTE_DRIVERS
1880 } eOutliner_AnimDataOps;
1881
1882 static const EnumPropertyItem prop_animdata_op_types[] = {
1883     {OUTLINER_ANIMOP_CLEAR_ADT,
1884      "CLEAR_ANIMDATA",
1885      0,
1886      "Clear Animation Data",
1887      "Remove this animation data container"},
1888     {OUTLINER_ANIMOP_SET_ACT, "SET_ACT", 0, "Set Action", ""},
1889     {OUTLINER_ANIMOP_CLEAR_ACT, "CLEAR_ACT", 0, "Unlink Action", ""},
1890     {OUTLINER_ANIMOP_REFRESH_DRV, "REFRESH_DRIVERS", 0, "Refresh Drivers", ""},
1891     //{OUTLINER_ANIMOP_COPY_DRIVERS, "COPY_DRIVERS", 0, "Copy Drivers", ""},
1892     //{OUTLINER_ANIMOP_PASTE_DRIVERS, "PASTE_DRIVERS", 0, "Paste Drivers", ""},
1893     {OUTLINER_ANIMOP_CLEAR_DRV, "CLEAR_DRIVERS", 0, "Clear Drivers", ""},
1894     {0, NULL, 0, NULL, NULL},
1895 };
1896
1897 static int outliner_animdata_operation_exec(bContext *C, wmOperator *op)
1898 {
1899   SpaceOutliner *soops = CTX_wm_space_outliner(C);
1900   int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1901   eOutliner_AnimDataOps event;
1902   short updateDeps = 0;
1903
1904   /* check for invalid states */
1905   if (soops == NULL) {
1906     return OPERATOR_CANCELLED;
1907   }
1908
1909   event = RNA_enum_get(op->ptr, "type");
1910   set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1911
1912   if (datalevel != TSE_ANIM_DATA) {
1913     return OPERATOR_CANCELLED;
1914   }
1915
1916   /* perform the core operation */
1917   switch (event) {
1918     case OUTLINER_ANIMOP_CLEAR_ADT:
1919       /* Remove Animation Data - this may remove the active action, in some cases... */
1920       outliner_do_data_operation(soops, datalevel, event, &soops->tree, clear_animdata_cb, NULL);
1921
1922       WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
1923       ED_undo_push(C, "Clear Animation Data");
1924       break;
1925
1926     case OUTLINER_ANIMOP_SET_ACT:
1927       /* delegate once again... */
1928       WM_operator_name_call(C, "OUTLINER_OT_action_set", WM_OP_INVOKE_REGION_WIN, NULL);
1929       break;
1930
1931     case OUTLINER_ANIMOP_CLEAR_ACT:
1932       /* clear active action - using standard rules */
1933       outliner_do_data_operation(
1934           soops, datalevel, event, &soops->tree, unlinkact_animdata_cb, NULL);
1935
1936       WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
1937       ED_undo_push(C, "Unlink action");
1938       break;
1939
1940     case OUTLINER_ANIMOP_REFRESH_DRV:
1941       outliner_do_data_operation(
1942           soops, datalevel, event, &soops->tree, refreshdrivers_animdata_cb, NULL);
1943
1944       WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, NULL);
1945       // ED_undo_push(C, "Refresh Drivers"); /* no undo needed - shouldn't have any impact? */
1946       updateDeps = 1;
1947       break;
1948
1949     case OUTLINER_ANIMOP_CLEAR_DRV:
1950       outliner_do_data_operation(
1951           soops, datalevel, event, &soops->tree, cleardrivers_animdata_cb, NULL);
1952
1953       WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, NULL);
1954       ED_undo_push(C, "Clear Drivers");
1955       updateDeps = 1;
1956       break;
1957
1958     default:  // invalid
1959       break;
1960   }
1961
1962   /* update dependencies */
1963   if (updateDeps) {
1964     /* rebuild depsgraph for the new deps */
1965     DEG_relations_tag_update(CTX_data_main(C));
1966   }
1967
1968   return OPERATOR_FINISHED;
1969 }
1970
1971 void OUTLINER_OT_animdata_operation(wmOperatorType *ot)
1972 {
1973   /* identifiers */
1974   ot->name = "Outliner Animation Data Operation";
1975   ot->idname = "OUTLINER_OT_animdata_operation";
1976
1977   /* callbacks */
1978   ot->invoke = WM_menu_invoke;
1979   ot->exec = outliner_animdata_operation_exec;
1980   ot->poll = ED_operator_outliner_active;
1981
1982   ot->flag = 0;
1983
1984   ot->prop = RNA_def_enum(ot->srna, "type", prop_animdata_op_types, 0, "Animation Operation", "");
1985 }
1986
1987 /* **************************************** */
1988
1989 static const EnumPropertyItem prop_constraint_op_types[] = {
1990     {OL_CONSTRAINTOP_ENABLE, "ENABLE", ICON_HIDE_OFF, "Enable", ""},
1991     {OL_CONSTRAINTOP_DISABLE, "DISABLE", ICON_HIDE_ON, "Disable", ""},
1992     {OL_CONSTRAINTOP_DELETE, "DELETE", ICON_X, "Delete", ""},
1993     {0, NULL, 0, NULL, NULL},
1994 };
1995
1996 static int outliner_constraint_operation_exec(bContext *C, wmOperator *op)
1997 {
1998   SpaceOutliner *soops = CTX_wm_space_outliner(C);
1999   int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
2000   eOutliner_PropConstraintOps event;
2001
2002   event = RNA_enum_get(op->ptr, "type");
2003   set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
2004
2005   outliner_do_data_operation(soops, datalevel, event, &soops->tree, constraint_cb, C);
2006
2007   if (event == OL_CONSTRAINTOP_DELETE) {
2008     outliner_cleanup_tree(soops);
2009   }
2010
2011   ED_undo_push(C, "Constraint operation");
2012
2013   return OPERATOR_FINISHED;
2014 }
2015
2016 void OUTLINER_OT_constraint_operation(wmOperatorType *ot)
2017 {
2018   /* identifiers */
2019   ot->name = "Outliner Constraint Operation";
2020   ot->idname = "OUTLINER_OT_constraint_operation";
2021
2022   /* callbacks */
2023   ot->invoke = WM_menu_invoke;
2024   ot->exec = outliner_constraint_operation_exec;
2025   ot->poll = ED_operator_outliner_active;
2026
2027   ot->flag = 0;
2028
2029   ot->prop = RNA_def_enum(
2030       ot->srna, "type", prop_constraint_op_types, 0, "Constraint Operation", "");
2031 }
2032
2033 /* ******************** */
2034
2035 static const EnumPropertyItem prop_modifier_op_types[] = {
2036     {OL_MODIFIER_OP_TOGVIS, "TOGVIS", ICON_RESTRICT_VIEW_OFF, "Toggle viewport use", ""},
2037     {OL_MODIFIER_OP_TOGREN, "TOGREN", ICON_RESTRICT_RENDER_OFF, "Toggle render use", ""},
2038     {OL_MODIFIER_OP_DELETE, "DELETE", ICON_X, "Delete", ""},
2039     {0, NULL, 0, NULL, NULL},
2040 };
2041
2042 static int outliner_modifier_operation_exec(bContext *C, wmOperator *op)
2043 {
2044   SpaceOutliner *soops = CTX_wm_space_outliner(C);
2045   int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
2046   eOutliner_PropModifierOps event;
2047
2048   event = RNA_enum_get(op->ptr, "type");
2049   set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
2050
2051   outliner_do_data_operation(soops, datalevel, event, &soops->tree, modifier_cb, C);
2052
2053   if (event == OL_MODIFIER_OP_DELETE) {
2054     outliner_cleanup_tree(soops);
2055   }
2056
2057   ED_undo_push(C, "Modifier operation");
2058
2059   return OPERATOR_FINISHED;
2060 }
2061
2062 void OUTLINER_OT_modifier_operation(wmOperatorType *ot)
2063 {
2064   /* identifiers */
2065   ot->name = "Outliner Modifier Operation";
2066   ot->idname = "OUTLINER_OT_modifier_operation";
2067
2068   /* callbacks */
2069   ot->invoke = WM_menu_invoke;
2070   ot->exec = outliner_modifier_operation_exec;
2071   ot->poll = ED_operator_outliner_active;
2072
2073   ot->flag = 0;
2074
2075   ot->prop = RNA_def_enum(ot->srna, "type", prop_modifier_op_types, 0, "Modifier Operation", "");
2076 }
2077
2078 /* ******************** */
2079
2080 // XXX: select linked is for RNA structs only
2081 static const EnumPropertyItem prop_data_op_types[] = {
2082     {OL_DOP_SELECT, "SELECT", 0, "Select", ""},
2083     {OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""},
2084     {OL_DOP_HIDE, "HIDE", 0, "Hide", ""},
2085     {OL_DOP_UNHIDE, "UNHIDE", 0, "Unhide", ""},
2086     {OL_DOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""},
2087     {0, NULL, 0, NULL, NULL},
2088 };
2089
2090 static int outliner_data_operation_exec(bContext *C, wmOperator *op)
2091 {
2092   SpaceOutliner *soops = CTX_wm_space_outliner(C);
2093   int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
2094   eOutliner_PropDataOps event;
2095
2096   /* check for invalid states */
2097   if (soops == NULL) {
2098     return OPERATOR_CANCELLED;
2099   }
2100
2101   event = RNA_enum_get(op->ptr, "type");
2102   set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
2103
2104   switch (datalevel) {
2105     case TSE_POSE_CHANNEL: {
2106       outliner_do_data_operation(soops, datalevel, event, &soops->tree, pchan_cb, NULL);
2107       WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
2108       ED_undo_push(C, "PoseChannel operation");
2109
2110       break;
2111     }
2112     case TSE_BONE: {
2113       outliner_do_data_operation(soops, datalevel, event, &soops->tree, bone_cb, NULL);
2114       WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
2115       ED_undo_push(C, "Bone operation");
2116
2117       break;
2118     }
2119     case TSE_EBONE: {
2120       outliner_do_data_operation(soops, datalevel, event, &soops->tree, ebone_cb, NULL);
2121       WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
2122       ED_undo_push(C, "EditBone operation");
2123
2124       break;
2125     }
2126     case TSE_SEQUENCE: {
2127       Scene *scene = CTX_data_scene(C);
2128       outliner_do_data_operation(soops, datalevel, event, &soops->tree, sequence_cb, scene);
2129
2130       break;
2131     }
2132     case TSE_GP_LAYER: {
2133       outliner_do_data_operation(soops, datalevel, event, &soops->tree, gp_layer_cb, NULL);
2134       WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL);
2135       ED_undo_push(C, "Grease Pencil Layer operation");
2136
2137       break;
2138     }
2139     case TSE_RNA_STRUCT:
2140       if (event == OL_DOP_SELECT_LINKED) {
2141         outliner_do_data_operation(
2142             soops, datalevel, event, &soops->tree, data_select_linked_cb, C);
2143       }
2144
2145       break;
2146
2147     default:
2148       BKE_report(op->reports, RPT_WARNING, "Not yet implemented");
2149       break;
2150   }
2151
2152   return OPERATOR_FINISHED;
2153 }
2154
2155 void OUTLINER_OT_data_operation(wmOperatorType *ot)
2156 {
2157   /* identifiers */
2158   ot->name = "Outliner Data Operation";
2159   ot->idname = "OUTLINER_OT_data_operation";
2160
2161   /* callbacks */
2162   ot->invoke = WM_menu_invoke;
2163   ot->exec = outliner_data_operation_exec;
2164   ot->poll = ED_operator_outliner_active;
2165
2166   ot->flag = 0;
2167
2168   ot->prop = RNA_def_enum(ot->srna, "type", prop_data_op_types, 0, "Data Operation", "");
2169 }
2170
2171 /* ******************** */
2172
2173 static int outliner_operator_menu(bContext *C, const char *opname)
2174 {
2175   wmOperatorType *ot = WM_operatortype_find(opname, false);
2176   uiPopupMenu *pup = UI_popup_menu_begin(C, WM_operatortype_name(ot, NULL), ICON_NONE);
2177   uiLayout *layout = UI_popup_menu_layout(pup);
2178
2179   /* set this so the default execution context is the same as submenus */
2180   uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_REGION_WIN);
2181   uiItemsEnumO(layout, ot->idname, RNA_property_identifier(ot->prop));
2182
2183   MenuType *mt = WM_menutype_find("OUTLINER_MT_context", false);
2184   if (mt) {
2185     uiItemS(layout);
2186     UI_menutype_draw(C, mt, layout);
2187   }
2188
2189   UI_popup_menu_end(C, pup);
2190
2191   return OPERATOR_INTERFACE;
2192 }
2193
2194 static int do_outliner_operation_event(
2195     bContext *C, ARegion *ar, SpaceOutliner *soops, TreeElement *te, const float mval[2])
2196 {
2197   ReportList *reports = CTX_wm_reports(C);  // XXX...
2198
2199   if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
2200     int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
2201     TreeStoreElem *tselem = TREESTORE(te);
2202
2203     /* select object that's clicked on and popup context menu */
2204     if (!(tselem->flag & TSE_SELECTED)) {
2205
2206       if (outliner_flag_is_any_test(&soops->tree, TSE_SELECTED, 1)) {
2207         outliner_flag_set(&soops->tree, TSE_SELECTED, 0);
2208       }
2209
2210       tselem->flag |= TSE_SELECTED;
2211
2212       /* Only redraw, don't rebuild here because TreeElement pointers will
2213        * become invalid and operations will crash. */
2214       ED_region_tag_redraw_no_rebuild(ar);
2215     }
2216
2217     set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
2218
2219     if (scenelevel) {
2220       if (objectlevel || datalevel || idlevel) {
2221         BKE_report(reports, RPT_WARNING, "Mixed selection");
2222         return OPERATOR_CANCELLED;
2223       }
2224       else {
2225         return outliner_operator_menu(C, "OUTLINER_OT_scene_operation");
2226       }
2227     }
2228     else if (objectlevel) {
2229       WM_menu_name_call(C, "OUTLINER_MT_object", WM_OP_INVOKE_REGION_WIN);
2230       return OPERATOR_FINISHED;
2231     }
2232     else if (idlevel) {
2233       if (idlevel == -1 || datalevel) {
2234         BKE_report(reports, RPT_WARNING, "Mixed selection");
2235         return OPERATOR_CANCELLED;
2236       }
2237       else {
2238         switch (idlevel) {
2239           case ID_GR:
2240             WM_menu_name_call(C, "OUTLINER_MT_collection", WM_OP_INVOKE_REGION_WIN);
2241             return OPERATOR_FINISHED;
2242             break;
2243           case ID_LI:
2244             return outliner_operator_menu(C, "OUTLINER_OT_lib_operation");
2245             break;
2246           default:
2247             return outliner_operator_menu(C, "OUTLINER_OT_id_operation");
2248             break;
2249         }
2250       }
2251     }
2252     else if (datalevel) {
2253       if (datalevel == -1) {
2254         BKE_report(reports, RPT_WARNING, "Mixed selection");
2255         return OPERATOR_CANCELLED;
2256       }
2257       else {
2258         if (datalevel == TSE_ANIM_DATA) {
2259           return outliner_operator_menu(C, "OUTLINER_OT_animdata_operation");
2260         }
2261         else if (datalevel == TSE_DRIVER_BASE) {
2262           /* do nothing... no special ops needed yet */
2263           return OPERATOR_CANCELLED;
2264         }
2265         else if (datalevel == TSE_LAYER_COLLECTION) {
2266           WM_menu_name_call(C, "OUTLINER_MT_collection", WM_OP_INVOKE_REGION_WIN);
2267           return OPERATOR_FINISHED;
2268         }
2269         else if (ELEM(datalevel, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) {
2270           WM_menu_name_call(C, "OUTLINER_MT_collection_new", WM_OP_INVOKE_REGION_WIN);
2271           return OPERATOR_FINISHED;
2272         }
2273         else if (datalevel == TSE_ID_BASE) {
2274           /* do nothing... there are no ops needed here yet */
2275         }
2276         else if (datalevel == TSE_CONSTRAINT) {
2277           return outliner_operator_menu(C, "OUTLINER_OT_constraint_operation");
2278         }
2279         else if (datalevel == TSE_MODIFIER) {
2280           return outliner_operator_menu(C, "OUTLINER_OT_modifier_operation");
2281         }
2282         else {
2283           return outliner_operator_menu(C, "OUTLINER_OT_data_operation");
2284         }
2285       }
2286     }
2287
2288     return 0;
2289   }
2290
2291   for (te = te->subtree.first; te; te = te->next) {
2292     int retval = do_outliner_operation_event(C, ar, soops, te, mval);
2293     if (retval) {
2294       return retval;
2295     }
2296   }
2297
2298   return 0;
2299 }
2300
2301 static int outliner_operation(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
2302 {
2303   ARegion *ar = CTX_wm_region(C);
2304   SpaceOutliner *soops = CTX_wm_space_outliner(C);
2305   uiBut *but = UI_context_active_but_get(C);
2306   TreeElement *te;
2307   float fmval[2];
2308
2309   if (but) {
2310     UI_but_tooltip_timer_remove(C, but);
2311   }
2312
2313   UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
2314
2315   for (te = soops->tree.first; te; te = te->next) {
2316     int retval = do_outliner_operation_event(C, ar, soops, te, fmval);
2317     if (retval) {
2318       return retval;
2319     }
2320   }
2321
2322   /* Menus for clicking in empty space. */
2323   if (soops->outlinevis == SO_VIEW_LAYER) {
2324     WM_menu_name_call(C, "OUTLINER_MT_collection_new", WM_OP_INVOKE_REGION_WIN);
2325     return OPERATOR_FINISHED;
2326   }
2327
2328   WM_menu_name_call(C, "OUTLINER_MT_context", WM_OP_INVOKE_REGION_WIN);
2329   return OPERATOR_FINISHED;
2330 }
2331
2332 /* Menu only! Calls other operators */
2333 void OUTLINER_OT_operation(wmOperatorType *ot)
2334 {
2335   ot->name = "Context Menu";
2336   ot->idname = "OUTLINER_OT_operation";
2337   ot->description = "Context menu for item operations";
2338
2339   ot->invoke = outliner_operation;
2340
2341   ot->poll = ED_operator_outliner_active;
2342 }
2343
2344 /* ****************************************************** */