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