Fix: Curve resample duplicates last point for cyclic splines
[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 "CLG_log.h"
27
28 #include "DNA_anim_types.h"
29 #include "DNA_armature_types.h"
30 #include "DNA_collection_types.h"
31 #include "DNA_constraint_types.h"
32 #include "DNA_gpencil_types.h"
33 #include "DNA_hair_types.h"
34 #include "DNA_light_types.h"
35 #include "DNA_linestyle_types.h"
36 #include "DNA_material_types.h"
37 #include "DNA_mesh_types.h"
38 #include "DNA_meta_types.h"
39 #include "DNA_modifier_types.h"
40 #include "DNA_object_types.h"
41 #include "DNA_pointcloud_types.h"
42 #include "DNA_scene_types.h"
43 #include "DNA_sequence_types.h"
44 #include "DNA_simulation_types.h"
45 #include "DNA_volume_types.h"
46 #include "DNA_world_types.h"
47
48 #include "BLI_blenlib.h"
49 #include "BLI_ghash.h"
50 #include "BLI_utildefines.h"
51
52 #include "BKE_anim_data.h"
53 #include "BKE_animsys.h"
54 #include "BKE_armature.h"
55 #include "BKE_collection.h"
56 #include "BKE_constraint.h"
57 #include "BKE_context.h"
58 #include "BKE_fcurve.h"
59 #include "BKE_global.h"
60 #include "BKE_idtype.h"
61 #include "BKE_layer.h"
62 #include "BKE_lib_id.h"
63 #include "BKE_lib_override.h"
64 #include "BKE_lib_query.h"
65 #include "BKE_main.h"
66 #include "BKE_object.h"
67 #include "BKE_report.h"
68 #include "BKE_scene.h"
69 #include "BKE_screen.h"
70
71 #include "DEG_depsgraph.h"
72 #include "DEG_depsgraph_build.h"
73
74 #include "ED_object.h"
75 #include "ED_outliner.h"
76 #include "ED_scene.h"
77 #include "ED_screen.h"
78 #include "ED_sequencer.h"
79 #include "ED_undo.h"
80
81 #include "WM_api.h"
82 #include "WM_message.h"
83 #include "WM_types.h"
84
85 #include "UI_interface.h"
86 #include "UI_resources.h"
87 #include "UI_view2d.h"
88
89 #include "RNA_access.h"
90 #include "RNA_define.h"
91 #include "RNA_enum_types.h"
92
93 #include "SEQ_sequencer.h"
94
95 #include "outliner_intern.h"
96
97 static CLG_LogRef LOG = {"ed.outliner.tools"};
98
99 /* -------------------------------------------------------------------- */
100 /** \name ID/Library/Data Set/Un-link Utilities
101  * \{ */
102
103 static void get_element_operation_type(
104     TreeElement *te, int *scenelevel, int *objectlevel, int *idlevel, int *datalevel)
105 {
106   TreeStoreElem *tselem = TREESTORE(te);
107   if (tselem->flag & TSE_SELECTED) {
108     /* Layer collection points to collection ID. */
109     if (!ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION)) {
110       if (*datalevel == 0) {
111         *datalevel = tselem->type;
112       }
113       else if (*datalevel != tselem->type) {
114         *datalevel = -1;
115       }
116     }
117     else {
118       const int idcode = (int)GS(tselem->id->name);
119       bool is_standard_id = false;
120       switch ((ID_Type)idcode) {
121         case ID_SCE:
122           *scenelevel = 1;
123           break;
124         case ID_OB:
125           *objectlevel = 1;
126           break;
127
128         case ID_ME:
129         case ID_CU:
130         case ID_MB:
131         case ID_LT:
132         case ID_LA:
133         case ID_AR:
134         case ID_CA:
135         case ID_SPK:
136         case ID_MA:
137         case ID_TE:
138         case ID_IP:
139         case ID_IM:
140         case ID_SO:
141         case ID_KE:
142         case ID_WO:
143         case ID_AC:
144         case ID_TXT:
145         case ID_GR:
146         case ID_LS:
147         case ID_LI:
148         case ID_VF:
149         case ID_NT:
150         case ID_BR:
151         case ID_PA:
152         case ID_GD:
153         case ID_MC:
154         case ID_MSK:
155         case ID_PAL:
156         case ID_PC:
157         case ID_CF:
158         case ID_WS:
159         case ID_LP:
160         case ID_HA:
161         case ID_PT:
162         case ID_VO:
163         case ID_SIM:
164           is_standard_id = true;
165           break;
166         case ID_WM:
167         case ID_SCR:
168           /* Those are ignored here. */
169           /* Note: while Screens should be manageable here, deleting a screen used by a workspace
170            * will cause crashes when trying to use that workspace, so for now let's play minimal,
171            * safe change. */
172           break;
173       }
174       if (idcode == ID_NLA) {
175         /* Fake one, not an actual ID type... */
176         is_standard_id = true;
177       }
178
179       if (is_standard_id) {
180         if (*idlevel == 0) {
181           *idlevel = idcode;
182         }
183         else if (*idlevel != idcode) {
184           *idlevel = -1;
185         }
186         if (ELEM(*datalevel, TSE_VIEW_COLLECTION_BASE, TSE_SCENE_COLLECTION_BASE)) {
187           *datalevel = 0;
188         }
189       }
190     }
191   }
192 }
193
194 static TreeElement *get_target_element(SpaceOutliner *space_outliner)
195 {
196   TreeElement *te = outliner_find_element_with_flag(&space_outliner->tree, TSE_ACTIVE);
197   BLI_assert(te);
198
199   return te;
200 }
201
202 static void unlink_action_fn(bContext *C,
203                              ReportList *UNUSED(reports),
204                              Scene *UNUSED(scene),
205                              TreeElement *UNUSED(te),
206                              TreeStoreElem *tsep,
207                              TreeStoreElem *UNUSED(tselem),
208                              void *UNUSED(user_data))
209 {
210   /* just set action to NULL */
211   BKE_animdata_set_action(CTX_wm_reports(C), tsep->id, NULL);
212 }
213
214 static void unlink_material_fn(bContext *UNUSED(C),
215                                ReportList *UNUSED(reports),
216                                Scene *UNUSED(scene),
217                                TreeElement *te,
218                                TreeStoreElem *tsep,
219                                TreeStoreElem *UNUSED(tselem),
220                                void *UNUSED(user_data))
221 {
222   Material **matar = NULL;
223   int a, totcol = 0;
224
225   if (GS(tsep->id->name) == ID_OB) {
226     Object *ob = (Object *)tsep->id;
227     totcol = ob->totcol;
228     matar = ob->mat;
229   }
230   else if (GS(tsep->id->name) == ID_ME) {
231     Mesh *me = (Mesh *)tsep->id;
232     totcol = me->totcol;
233     matar = me->mat;
234   }
235   else if (GS(tsep->id->name) == ID_CU) {
236     Curve *cu = (Curve *)tsep->id;
237     totcol = cu->totcol;
238     matar = cu->mat;
239   }
240   else if (GS(tsep->id->name) == ID_MB) {
241     MetaBall *mb = (MetaBall *)tsep->id;
242     totcol = mb->totcol;
243     matar = mb->mat;
244   }
245   else if (GS(tsep->id->name) == ID_HA) {
246     Hair *hair = (Hair *)tsep->id;
247     totcol = hair->totcol;
248     matar = hair->mat;
249   }
250   else if (GS(tsep->id->name) == ID_PT) {
251     PointCloud *pointcloud = (PointCloud *)tsep->id;
252     totcol = pointcloud->totcol;
253     matar = pointcloud->mat;
254   }
255   else if (GS(tsep->id->name) == ID_VO) {
256     Volume *volume = (Volume *)tsep->id;
257     totcol = volume->totcol;
258     matar = volume->mat;
259   }
260   else {
261     BLI_assert(0);
262   }
263
264   if (LIKELY(matar != NULL)) {
265     for (a = 0; a < totcol; a++) {
266       if (a == te->index && matar[a]) {
267         id_us_min(&matar[a]->id);
268         matar[a] = NULL;
269       }
270     }
271   }
272 }
273
274 static void unlink_texture_fn(bContext *UNUSED(C),
275                               ReportList *UNUSED(reports),
276                               Scene *UNUSED(scene),
277                               TreeElement *te,
278                               TreeStoreElem *tsep,
279                               TreeStoreElem *UNUSED(tselem),
280                               void *UNUSED(user_data))
281 {
282   MTex **mtex = NULL;
283   int a;
284
285   if (GS(tsep->id->name) == ID_LS) {
286     FreestyleLineStyle *ls = (FreestyleLineStyle *)tsep->id;
287     mtex = ls->mtex;
288   }
289   else {
290     return;
291   }
292
293   for (a = 0; a < MAX_MTEX; a++) {
294     if (a == te->index && mtex[a]) {
295       if (mtex[a]->tex) {
296         id_us_min(&mtex[a]->tex->id);
297         mtex[a]->tex = NULL;
298       }
299     }
300   }
301 }
302
303 static void unlink_collection_fn(bContext *C,
304                                  ReportList *UNUSED(reports),
305                                  Scene *UNUSED(scene),
306                                  TreeElement *UNUSED(te),
307                                  TreeStoreElem *tsep,
308                                  TreeStoreElem *tselem,
309                                  void *UNUSED(user_data))
310 {
311   Main *bmain = CTX_data_main(C);
312   Collection *collection = (Collection *)tselem->id;
313
314   if (tsep) {
315     if (GS(tsep->id->name) == ID_OB) {
316       Object *ob = (Object *)tsep->id;
317       ob->instance_collection = NULL;
318       DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
319       DEG_relations_tag_update(bmain);
320     }
321     else if (GS(tsep->id->name) == ID_GR) {
322       Collection *parent = (Collection *)tsep->id;
323       id_fake_user_set(&collection->id);
324       BKE_collection_child_remove(bmain, parent, collection);
325       DEG_id_tag_update(&parent->id, ID_RECALC_COPY_ON_WRITE);
326       DEG_relations_tag_update(bmain);
327     }
328     else if (GS(tsep->id->name) == ID_SCE) {
329       Scene *scene = (Scene *)tsep->id;
330       Collection *parent = scene->master_collection;
331       id_fake_user_set(&collection->id);
332       BKE_collection_child_remove(bmain, parent, collection);
333       DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
334       DEG_relations_tag_update(bmain);
335     }
336   }
337 }
338
339 static void unlink_object_fn(bContext *C,
340                              ReportList *UNUSED(reports),
341                              Scene *UNUSED(scene),
342                              TreeElement *te,
343                              TreeStoreElem *tsep,
344                              TreeStoreElem *tselem,
345                              void *UNUSED(user_data))
346 {
347   if (tsep && tsep->id) {
348     Main *bmain = CTX_data_main(C);
349     Object *ob = (Object *)tselem->id;
350
351     if (GS(tsep->id->name) == ID_OB) {
352       /* Parented objects need to find which collection to unlink from. */
353       TreeElement *te_parent = te->parent;
354       while (tsep && GS(tsep->id->name) == ID_OB) {
355         te_parent = te_parent->parent;
356         tsep = te_parent ? TREESTORE(te_parent) : NULL;
357       }
358     }
359
360     if (tsep && tsep->id) {
361       if (GS(tsep->id->name) == ID_GR) {
362         Collection *parent = (Collection *)tsep->id;
363         BKE_collection_object_remove(bmain, parent, ob, true);
364         DEG_id_tag_update(&parent->id, ID_RECALC_COPY_ON_WRITE);
365         DEG_relations_tag_update(bmain);
366       }
367       else if (GS(tsep->id->name) == ID_SCE) {
368         Scene *scene = (Scene *)tsep->id;
369         Collection *parent = scene->master_collection;
370         BKE_collection_object_remove(bmain, parent, ob, true);
371         DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
372         DEG_relations_tag_update(bmain);
373       }
374     }
375   }
376 }
377
378 static void unlink_world_fn(bContext *UNUSED(C),
379                             ReportList *UNUSED(reports),
380                             Scene *UNUSED(scene),
381                             TreeElement *UNUSED(te),
382                             TreeStoreElem *tsep,
383                             TreeStoreElem *tselem,
384                             void *UNUSED(user_data))
385 {
386   Scene *parscene = (Scene *)tsep->id;
387   World *wo = (World *)tselem->id;
388
389   /* need to use parent scene not just scene, otherwise may end up getting wrong one */
390   id_us_min(&wo->id);
391   parscene->world = NULL;
392 }
393
394 static void outliner_do_libdata_operation(bContext *C,
395                                           ReportList *reports,
396                                           Scene *scene,
397                                           SpaceOutliner *space_outliner,
398                                           ListBase *lb,
399                                           outliner_operation_fn operation_fn,
400                                           void *user_data)
401 {
402   LISTBASE_FOREACH (TreeElement *, te, lb) {
403     TreeStoreElem *tselem = TREESTORE(te);
404     if (tselem->flag & TSE_SELECTED) {
405       if (((tselem->type == TSE_SOME_ID) && (te->idcode != 0)) ||
406           tselem->type == TSE_LAYER_COLLECTION) {
407         TreeStoreElem *tsep = te->parent ? TREESTORE(te->parent) : NULL;
408         operation_fn(C, reports, scene, te, tsep, tselem, user_data);
409       }
410     }
411     if (TSELEM_OPEN(tselem, space_outliner)) {
412       outliner_do_libdata_operation(
413           C, reports, scene, space_outliner, &te->subtree, operation_fn, user_data);
414     }
415   }
416 }
417
418 /** \} */
419
420 /* -------------------------------------------------------------------- */
421 /** \name Scene Menu Operator
422  * \{ */
423
424 typedef enum eOutliner_PropSceneOps {
425   OL_SCENE_OP_DELETE = 1,
426 } eOutliner_PropSceneOps;
427
428 static const EnumPropertyItem prop_scene_op_types[] = {
429     {OL_SCENE_OP_DELETE, "DELETE", ICON_X, "Delete", ""},
430     {0, NULL, 0, NULL, NULL},
431 };
432
433 static bool outliner_do_scene_operation(
434     bContext *C,
435     eOutliner_PropSceneOps event,
436     ListBase *lb,
437     bool (*operation_fn)(bContext *, eOutliner_PropSceneOps, TreeElement *, TreeStoreElem *))
438 {
439   bool success = false;
440
441   LISTBASE_FOREACH (TreeElement *, te, lb) {
442     TreeStoreElem *tselem = TREESTORE(te);
443     if (tselem->flag & TSE_SELECTED) {
444       if (operation_fn(C, event, te, tselem)) {
445         success = true;
446       }
447     }
448   }
449
450   return success;
451 }
452
453 static bool scene_fn(bContext *C,
454                      eOutliner_PropSceneOps event,
455                      TreeElement *UNUSED(te),
456                      TreeStoreElem *tselem)
457 {
458   Scene *scene = (Scene *)tselem->id;
459
460   if (event == OL_SCENE_OP_DELETE) {
461     if (ED_scene_delete(C, CTX_data_main(C), scene)) {
462       WM_event_add_notifier(C, NC_SCENE | NA_REMOVED, scene);
463     }
464     else {
465       return false;
466     }
467   }
468
469   return true;
470 }
471
472 static int outliner_scene_operation_exec(bContext *C, wmOperator *op)
473 {
474   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
475   const eOutliner_PropSceneOps event = RNA_enum_get(op->ptr, "type");
476
477   if (outliner_do_scene_operation(C, event, &space_outliner->tree, scene_fn) == false) {
478     return OPERATOR_CANCELLED;
479   }
480
481   if (event == OL_SCENE_OP_DELETE) {
482     outliner_cleanup_tree(space_outliner);
483     ED_undo_push(C, "Delete Scene(s)");
484   }
485   else {
486     BLI_assert(0);
487     return OPERATOR_CANCELLED;
488   }
489
490   return OPERATOR_FINISHED;
491 }
492
493 void OUTLINER_OT_scene_operation(wmOperatorType *ot)
494 {
495   /* identifiers */
496   ot->name = "Outliner Scene Operation";
497   ot->idname = "OUTLINER_OT_scene_operation";
498   ot->description = "Context menu for scene operations";
499
500   /* callbacks */
501   ot->invoke = WM_menu_invoke;
502   ot->exec = outliner_scene_operation_exec;
503   ot->poll = ED_operator_outliner_active;
504
505   ot->flag = 0;
506
507   ot->prop = RNA_def_enum(ot->srna, "type", prop_scene_op_types, 0, "Scene Operation", "");
508 }
509
510 /** \} */
511
512 /* -------------------------------------------------------------------- */
513 /** \name Search Utilities
514  * \{ */
515
516 /**
517  * Stores the parent and a child element of a merged icon-row icon for
518  * the merged select popup menu. The sub-tree of the parent is searched and
519  * the child is needed to only show elements of the same type in the popup.
520  */
521 typedef struct MergedSearchData {
522   TreeElement *parent_element;
523   TreeElement *select_element;
524 } MergedSearchData;
525
526 static void merged_element_search_fn_recursive(
527     const ListBase *tree, short tselem_type, short type, const char *str, uiSearchItems *items)
528 {
529   char name[64];
530   int iconid;
531
532   LISTBASE_FOREACH (TreeElement *, te, tree) {
533     TreeStoreElem *tselem = TREESTORE(te);
534
535     if (tree_element_id_type_to_index(te) == type && tselem_type == tselem->type) {
536       if (BLI_strcasestr(te->name, str)) {
537         BLI_strncpy(name, te->name, 64);
538
539         iconid = tree_element_get_icon(tselem, te).icon;
540
541         /* Don't allow duplicate named items */
542         if (UI_search_items_find_index(items, name) == -1) {
543           if (!UI_search_item_add(items, name, te, iconid, 0, 0)) {
544             break;
545           }
546         }
547       }
548     }
549
550     merged_element_search_fn_recursive(&te->subtree, tselem_type, type, str, items);
551   }
552 }
553
554 /* Get a list of elements that match the search string */
555 static void merged_element_search_update_fn(const bContext *UNUSED(C),
556                                             void *data,
557                                             const char *str,
558                                             uiSearchItems *items,
559                                             const bool UNUSED(is_first))
560 {
561   MergedSearchData *search_data = (MergedSearchData *)data;
562   TreeElement *parent = search_data->parent_element;
563   TreeElement *te = search_data->select_element;
564
565   int type = tree_element_id_type_to_index(te);
566
567   merged_element_search_fn_recursive(&parent->subtree, TREESTORE(te)->type, type, str, items);
568 }
569
570 /* Activate an element from the merged element search menu */
571 static void merged_element_search_exec_fn(struct bContext *C, void *UNUSED(arg1), void *element)
572 {
573   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
574   TreeElement *te = (TreeElement *)element;
575
576   outliner_item_select(C, space_outliner, te, OL_ITEM_SELECT | OL_ITEM_ACTIVATE);
577
578   ED_outliner_select_sync_from_outliner(C, space_outliner);
579 }
580
581 /**
582  * Merged element search menu
583  * Created on activation of a merged or aggregated icon-row icon.
584  */
585 static uiBlock *merged_element_search_menu(bContext *C, ARegion *region, void *data)
586 {
587   static char search[64] = "";
588   uiBlock *block;
589   uiBut *but;
590
591   /* Clear search on each menu creation */
592   *search = '\0';
593
594   block = UI_block_begin(C, region, __func__, UI_EMBOSS);
595   UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
596   UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
597
598   short menu_width = 10 * UI_UNIT_X;
599   but = uiDefSearchBut(
600       block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, menu_width, UI_UNIT_Y, 0, 0, "");
601   UI_but_func_search_set(but,
602                          NULL,
603                          merged_element_search_update_fn,
604                          data,
605                          false,
606                          NULL,
607                          merged_element_search_exec_fn,
608                          NULL);
609   UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT);
610
611   /* Fake button to hold space for search items */
612   uiDefBut(block,
613            UI_BTYPE_LABEL,
614            0,
615            "",
616            10,
617            10 - UI_searchbox_size_y(),
618            menu_width,
619            UI_searchbox_size_y(),
620            NULL,
621            0,
622            0,
623            0,
624            0,
625            NULL);
626
627   /* Center the menu on the cursor */
628   UI_block_bounds_set_popup(block, 6, (const int[2]){-(menu_width / 2), 0});
629
630   return block;
631 }
632
633 void merged_element_search_menu_invoke(bContext *C,
634                                        TreeElement *parent_te,
635                                        TreeElement *activate_te)
636 {
637   MergedSearchData *select_data = MEM_callocN(sizeof(MergedSearchData), "merge_search_data");
638   select_data->parent_element = parent_te;
639   select_data->select_element = activate_te;
640
641   UI_popup_block_invoke(C, merged_element_search_menu, select_data, MEM_freeN);
642 }
643
644 static void object_select_fn(bContext *C,
645                              ReportList *UNUSED(reports),
646                              Scene *UNUSED(scene),
647                              TreeElement *UNUSED(te),
648                              TreeStoreElem *UNUSED(tsep),
649                              TreeStoreElem *tselem,
650                              void *UNUSED(user_data))
651 {
652   ViewLayer *view_layer = CTX_data_view_layer(C);
653   Object *ob = (Object *)tselem->id;
654   Base *base = BKE_view_layer_base_find(view_layer, ob);
655
656   if (base) {
657     ED_object_base_select(base, BA_SELECT);
658   }
659 }
660
661 /** \} */
662
663 /* -------------------------------------------------------------------- */
664 /** \name Callbacks (Selection, Users & Library) Utilities
665  * \{ */
666
667 static void object_select_hierarchy_fn(bContext *C,
668                                        ReportList *UNUSED(reports),
669                                        Scene *UNUSED(scene),
670                                        TreeElement *te,
671                                        TreeStoreElem *UNUSED(tsep),
672                                        TreeStoreElem *UNUSED(tselem),
673                                        void *UNUSED(user_data))
674 {
675   /* Don't extend because this toggles, which is nice for Ctrl-Click but not for a menu item.
676    * it's especially confusing when multiple items are selected since some toggle on/off. */
677   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
678   outliner_item_select(
679       C, space_outliner, te, OL_ITEM_SELECT | OL_ITEM_ACTIVATE | OL_ITEM_RECURSIVE);
680 }
681
682 static void object_deselect_fn(bContext *C,
683                                ReportList *UNUSED(reports),
684                                Scene *UNUSED(scene),
685                                TreeElement *UNUSED(te),
686                                TreeStoreElem *UNUSED(tsep),
687                                TreeStoreElem *tselem,
688                                void *UNUSED(user_data))
689 {
690   ViewLayer *view_layer = CTX_data_view_layer(C);
691   Object *ob = (Object *)tselem->id;
692   Base *base = BKE_view_layer_base_find(view_layer, ob);
693
694   if (base) {
695     base->flag &= ~BASE_SELECTED;
696   }
697 }
698
699 static void outliner_object_delete_fn(bContext *C, ReportList *reports, Scene *scene, Object *ob)
700 {
701   if (ob) {
702     Main *bmain = CTX_data_main(C);
703     if (ob->id.tag & LIB_TAG_INDIRECT) {
704       BKE_reportf(
705           reports, RPT_WARNING, "Cannot delete indirectly linked object '%s'", ob->id.name + 2);
706       return;
707     }
708     if (ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0 &&
709         BKE_library_ID_is_indirectly_used(bmain, ob)) {
710       BKE_reportf(reports,
711                   RPT_WARNING,
712                   "Cannot delete object '%s' from scene '%s', indirectly used objects need at "
713                   "least one user",
714                   ob->id.name + 2,
715                   scene->id.name + 2);
716       return;
717     }
718
719     /* Check also library later. */
720     if ((ob->mode & OB_MODE_EDIT) && BKE_object_is_in_editmode(ob)) {
721       ED_object_editmode_exit_ex(bmain, scene, ob, EM_FREEDATA);
722     }
723     BKE_id_delete(bmain, ob);
724   }
725 }
726
727 static void id_local_fn(bContext *C,
728                         ReportList *UNUSED(reports),
729                         Scene *UNUSED(scene),
730                         TreeElement *UNUSED(te),
731                         TreeStoreElem *UNUSED(tsep),
732                         TreeStoreElem *tselem,
733                         void *UNUSED(user_data))
734 {
735   if (ID_IS_LINKED(tselem->id) && (tselem->id->tag & LIB_TAG_EXTERN)) {
736     Main *bmain = CTX_data_main(C);
737     /* if the ID type has no special local function,
738      * just clear the lib */
739     if (BKE_lib_id_make_local(bmain, tselem->id, false, 0) == false) {
740       BKE_lib_id_clear_library_data(bmain, tselem->id);
741     }
742     else {
743       BKE_main_id_clear_newpoins(bmain);
744     }
745   }
746   else if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id)) {
747     BKE_lib_override_library_free(&tselem->id->override_library, true);
748   }
749 }
750
751 static void object_proxy_to_override_convert_fn(bContext *C,
752                                                 ReportList *reports,
753                                                 Scene *UNUSED(scene),
754                                                 TreeElement *UNUSED(te),
755                                                 TreeStoreElem *UNUSED(tsep),
756                                                 TreeStoreElem *tselem,
757                                                 void *UNUSED(user_data))
758 {
759   BLI_assert(TSE_IS_REAL_ID(tselem));
760   ID *id_proxy = tselem->id;
761   BLI_assert(GS(id_proxy->name) == ID_OB);
762   Object *ob_proxy = (Object *)id_proxy;
763   Scene *scene = CTX_data_scene(C);
764
765   if (ob_proxy->proxy == NULL) {
766     return;
767   }
768
769   if (!BKE_lib_override_library_proxy_convert(
770           CTX_data_main(C), scene, CTX_data_view_layer(C), ob_proxy)) {
771     BKE_reportf(
772         reports,
773         RPT_ERROR_INVALID_INPUT,
774         "Could not create a library override from proxy '%s' (might use already local data?)",
775         ob_proxy->id.name + 2);
776     return;
777   }
778
779   DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE);
780   WM_event_add_notifier(C, NC_WINDOW, NULL);
781 }
782
783 typedef struct OutlinerLibOverrideData {
784   bool do_hierarchy;
785   /**
786    * For resync operation, force keeping newly created override IDs (or original linked IDs)
787    * instead of re-applying relevant existing ID pointer property override operations. Helps
788    * solving broken overrides while not losing *all* of your overrides. */
789   bool do_resync_hierarchy_enforce;
790 } OutlinerLibOverrideData;
791
792 static void id_override_library_create_fn(bContext *C,
793                                           ReportList *reports,
794                                           Scene *scene,
795                                           TreeElement *te,
796                                           TreeStoreElem *tsep,
797                                           TreeStoreElem *tselem,
798                                           void *user_data)
799 {
800   BLI_assert(TSE_IS_REAL_ID(tselem));
801   ID *id_root = tselem->id;
802   OutlinerLibOverrideData *data = user_data;
803   const bool do_hierarchy = data->do_hierarchy;
804   bool success = false;
805
806   ID *id_reference = NULL;
807   bool is_override_instancing_object = false;
808   if (tsep != NULL && tsep->type == TSE_SOME_ID && tsep->id != NULL &&
809       GS(tsep->id->name) == ID_OB) {
810     Object *ob = (Object *)tsep->id;
811     if (ob->type == OB_EMPTY && &ob->instance_collection->id == id_root) {
812       BLI_assert(GS(id_root->name) == ID_GR);
813       /* Empty instantiating the collection we override, we need to pass it to BKE overriding code
814        * for proper handling. */
815       id_reference = tsep->id;
816       is_override_instancing_object = true;
817     }
818   }
819
820   if (ID_IS_OVERRIDABLE_LIBRARY(id_root) || (ID_IS_LINKED(id_root) && do_hierarchy)) {
821     Main *bmain = CTX_data_main(C);
822
823     id_root->tag |= LIB_TAG_DOIT;
824
825     /* For now, remap all local usages of linked ID to local override one here. */
826     ID *id_iter;
827     FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
828       if (ID_IS_LINKED(id_iter)) {
829         id_iter->tag &= ~LIB_TAG_DOIT;
830       }
831       else {
832         id_iter->tag |= LIB_TAG_DOIT;
833       }
834     }
835     FOREACH_MAIN_ID_END;
836
837     if (do_hierarchy) {
838       /* Tag all linked parents in tree hierarchy to be also overridden. */
839       while ((te = te->parent) != NULL) {
840         if (!TSE_IS_REAL_ID(te->store_elem)) {
841           continue;
842         }
843         if (!ID_IS_LINKED(te->store_elem->id)) {
844           break;
845         }
846         te->store_elem->id->tag |= LIB_TAG_DOIT;
847       }
848       success = BKE_lib_override_library_create(
849           bmain, CTX_data_scene(C), CTX_data_view_layer(C), id_root, id_reference);
850     }
851     else if (ID_IS_OVERRIDABLE_LIBRARY(id_root)) {
852       success = BKE_lib_override_library_create_from_id(bmain, id_root, true) != NULL;
853
854       /* Cleanup. */
855       BKE_main_id_clear_newpoins(bmain);
856       BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
857     }
858
859     /* Remove the instance empty from this scene, the items now have an overridden collection
860      * instead. */
861     if (success && is_override_instancing_object) {
862       ED_object_base_free_and_unlink(bmain, scene, (Object *)id_reference);
863     }
864   }
865   if (!success) {
866     BKE_reportf(reports,
867                 RPT_WARNING,
868                 "Could not create library override from data-block '%s'",
869                 id_root->name);
870   }
871 }
872
873 static void id_override_library_reset_fn(bContext *C,
874                                          ReportList *UNUSED(reports),
875                                          Scene *UNUSED(scene),
876                                          TreeElement *UNUSED(te),
877                                          TreeStoreElem *UNUSED(tsep),
878                                          TreeStoreElem *tselem,
879                                          void *user_data)
880 {
881   BLI_assert(TSE_IS_REAL_ID(tselem));
882   ID *id_root = tselem->id;
883   OutlinerLibOverrideData *data = user_data;
884   const bool do_hierarchy = data->do_hierarchy;
885
886   if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) {
887     Main *bmain = CTX_data_main(C);
888
889     if (do_hierarchy) {
890       BKE_lib_override_library_id_hierarchy_reset(bmain, id_root);
891     }
892     else {
893       BKE_lib_override_library_id_reset(bmain, id_root);
894     }
895
896     WM_event_add_notifier(C, NC_WM | ND_DATACHANGED, NULL);
897     WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
898   }
899   else {
900     CLOG_WARN(&LOG, "Could not reset library override of data block '%s'", id_root->name);
901   }
902 }
903
904 static void id_override_library_resync_fn(bContext *C,
905                                           ReportList *reports,
906                                           Scene *scene,
907                                           TreeElement *te,
908                                           TreeStoreElem *UNUSED(tsep),
909                                           TreeStoreElem *tselem,
910                                           void *user_data)
911 {
912   BLI_assert(TSE_IS_REAL_ID(tselem));
913   ID *id_root = tselem->id;
914   OutlinerLibOverrideData *data = user_data;
915   const bool do_hierarchy_enforce = data->do_resync_hierarchy_enforce;
916
917   if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) {
918     Main *bmain = CTX_data_main(C);
919
920     id_root->tag |= LIB_TAG_DOIT;
921
922     /* Tag all linked parents in tree hierarchy to be also overridden. */
923     while ((te = te->parent) != NULL) {
924       if (!TSE_IS_REAL_ID(te->store_elem)) {
925         continue;
926       }
927       if (!ID_IS_OVERRIDE_LIBRARY_REAL(te->store_elem->id)) {
928         break;
929       }
930       te->store_elem->id->tag |= LIB_TAG_DOIT;
931     }
932
933     BKE_lib_override_library_resync(
934         bmain, scene, CTX_data_view_layer(C), id_root, NULL, do_hierarchy_enforce, true, reports);
935
936     WM_event_add_notifier(C, NC_WINDOW, NULL);
937   }
938   else {
939     CLOG_WARN(&LOG, "Could not resync library override of data block '%s'", id_root->name);
940   }
941 }
942
943 static void id_override_library_delete_fn(bContext *C,
944                                           ReportList *UNUSED(reports),
945                                           Scene *UNUSED(scene),
946                                           TreeElement *te,
947                                           TreeStoreElem *UNUSED(tsep),
948                                           TreeStoreElem *tselem,
949                                           void *UNUSED(user_data))
950 {
951   BLI_assert(TSE_IS_REAL_ID(tselem));
952   ID *id_root = tselem->id;
953
954   if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) {
955     Main *bmain = CTX_data_main(C);
956
957     id_root->tag |= LIB_TAG_DOIT;
958
959     /* Tag all linked parents in tree hierarchy to be also overridden. */
960     while ((te = te->parent) != NULL) {
961       if (!TSE_IS_REAL_ID(te->store_elem)) {
962         continue;
963       }
964       if (!ID_IS_OVERRIDE_LIBRARY_REAL(te->store_elem->id)) {
965         break;
966       }
967       te->store_elem->id->tag |= LIB_TAG_DOIT;
968     }
969
970     BKE_lib_override_library_delete(bmain, id_root);
971
972     WM_event_add_notifier(C, NC_WINDOW, NULL);
973   }
974   else {
975     CLOG_WARN(&LOG, "Could not delete library override of data block '%s'", id_root->name);
976   }
977 }
978
979 static void id_fake_user_set_fn(bContext *UNUSED(C),
980                                 ReportList *UNUSED(reports),
981                                 Scene *UNUSED(scene),
982                                 TreeElement *UNUSED(te),
983                                 TreeStoreElem *UNUSED(tsep),
984                                 TreeStoreElem *tselem,
985                                 void *UNUSED(user_data))
986 {
987   ID *id = tselem->id;
988
989   id_fake_user_set(id);
990 }
991
992 static void id_fake_user_clear_fn(bContext *UNUSED(C),
993                                   ReportList *UNUSED(reports),
994                                   Scene *UNUSED(scene),
995                                   TreeElement *UNUSED(te),
996                                   TreeStoreElem *UNUSED(tsep),
997                                   TreeStoreElem *tselem,
998                                   void *UNUSED(user_data))
999 {
1000   ID *id = tselem->id;
1001
1002   id_fake_user_clear(id);
1003 }
1004
1005 static void id_select_linked_fn(bContext *C,
1006                                 ReportList *UNUSED(reports),
1007                                 Scene *UNUSED(scene),
1008                                 TreeElement *UNUSED(te),
1009                                 TreeStoreElem *UNUSED(tsep),
1010                                 TreeStoreElem *tselem,
1011                                 void *UNUSED(user_data))
1012 {
1013   ID *id = tselem->id;
1014
1015   ED_object_select_linked_by_id(C, id);
1016 }
1017
1018 static void singleuser_action_fn(bContext *C,
1019                                  ReportList *UNUSED(reports),
1020                                  Scene *UNUSED(scene),
1021                                  TreeElement *te,
1022                                  TreeStoreElem *tsep,
1023                                  TreeStoreElem *tselem,
1024                                  void *UNUSED(user_data))
1025 {
1026   /* This callback runs for all selected elements, some of which may not be actions which results
1027    * in a crash. */
1028   if (te->idcode != ID_AC) {
1029     return;
1030   }
1031
1032   ID *id = tselem->id;
1033
1034   if (id) {
1035     IdAdtTemplate *iat = (IdAdtTemplate *)tsep->id;
1036     PointerRNA ptr = {NULL};
1037     PropertyRNA *prop;
1038
1039     RNA_pointer_create(&iat->id, &RNA_AnimData, iat->adt, &ptr);
1040     prop = RNA_struct_find_property(&ptr, "action");
1041
1042     id_single_user(C, id, &ptr, prop);
1043   }
1044 }
1045
1046 static void singleuser_world_fn(bContext *C,
1047                                 ReportList *UNUSED(reports),
1048                                 Scene *UNUSED(scene),
1049                                 TreeElement *UNUSED(te),
1050                                 TreeStoreElem *tsep,
1051                                 TreeStoreElem *tselem,
1052                                 void *UNUSED(user_data))
1053 {
1054   ID *id = tselem->id;
1055
1056   /* need to use parent scene not just scene, otherwise may end up getting wrong one */
1057   if (id) {
1058     Scene *parscene = (Scene *)tsep->id;
1059     PointerRNA ptr = {NULL};
1060     PropertyRNA *prop;
1061
1062     RNA_id_pointer_create(&parscene->id, &ptr);
1063     prop = RNA_struct_find_property(&ptr, "world");
1064
1065     id_single_user(C, id, &ptr, prop);
1066   }
1067 }
1068
1069 /**
1070  * \param recurse_selected: Set to false for operations which are already
1071  * recursively operating on their children.
1072  */
1073 void outliner_do_object_operation_ex(bContext *C,
1074                                      ReportList *reports,
1075                                      Scene *scene_act,
1076                                      SpaceOutliner *space_outliner,
1077                                      ListBase *lb,
1078                                      outliner_operation_fn operation_fn,
1079                                      void *user_data,
1080                                      bool recurse_selected)
1081 {
1082   LISTBASE_FOREACH (TreeElement *, te, lb) {
1083     TreeStoreElem *tselem = TREESTORE(te);
1084     bool select_handled = false;
1085     if (tselem->flag & TSE_SELECTED) {
1086       if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) {
1087         /* When objects selected in other scenes... dunno if that should be allowed. */
1088         Scene *scene_owner = (Scene *)outliner_search_back(te, ID_SCE);
1089         if (scene_owner && scene_act != scene_owner) {
1090           WM_window_set_active_scene(CTX_data_main(C), C, CTX_wm_window(C), scene_owner);
1091         }
1092         /* Important to use 'scene_owner' not scene_act else deleting objects can crash.
1093          * only use 'scene_act' when 'scene_owner' is NULL, which can happen when the
1094          * outliner isn't showing scenes: Visible Layer draw mode for eg. */
1095         operation_fn(
1096             C, reports, scene_owner ? scene_owner : scene_act, te, NULL, tselem, user_data);
1097         select_handled = true;
1098       }
1099     }
1100     if (TSELEM_OPEN(tselem, space_outliner)) {
1101       if ((select_handled == false) || recurse_selected) {
1102         outliner_do_object_operation_ex(C,
1103                                         reports,
1104                                         scene_act,
1105                                         space_outliner,
1106                                         &te->subtree,
1107                                         operation_fn,
1108                                         NULL,
1109                                         recurse_selected);
1110       }
1111     }
1112   }
1113 }
1114
1115 void outliner_do_object_operation(bContext *C,
1116                                   ReportList *reports,
1117                                   Scene *scene_act,
1118                                   SpaceOutliner *space_outliner,
1119                                   ListBase *lb,
1120                                   outliner_operation_fn operation_fn)
1121 {
1122   outliner_do_object_operation_ex(
1123       C, reports, scene_act, space_outliner, lb, operation_fn, NULL, true);
1124 }
1125
1126 /** \} */
1127
1128 /* -------------------------------------------------------------------- */
1129 /** \name Internal Tagging Utilities
1130  * \{ */
1131
1132 static void clear_animdata_fn(int UNUSED(event),
1133                               TreeElement *UNUSED(te),
1134                               TreeStoreElem *tselem,
1135                               void *UNUSED(arg))
1136 {
1137   BKE_animdata_free(tselem->id, true);
1138   DEG_id_tag_update(tselem->id, ID_RECALC_ANIMATION);
1139 }
1140
1141 static void unlinkact_animdata_fn(int UNUSED(event),
1142                                   TreeElement *UNUSED(te),
1143                                   TreeStoreElem *tselem,
1144                                   void *UNUSED(arg))
1145 {
1146   /* just set action to NULL */
1147   BKE_animdata_set_action(NULL, tselem->id, NULL);
1148   DEG_id_tag_update(tselem->id, ID_RECALC_ANIMATION);
1149 }
1150
1151 static void cleardrivers_animdata_fn(int UNUSED(event),
1152                                      TreeElement *UNUSED(te),
1153                                      TreeStoreElem *tselem,
1154                                      void *UNUSED(arg))
1155 {
1156   IdAdtTemplate *iat = (IdAdtTemplate *)tselem->id;
1157
1158   /* just free drivers - stored as a list of F-Curves */
1159   BKE_fcurves_free(&iat->adt->drivers);
1160   DEG_id_tag_update(tselem->id, ID_RECALC_ANIMATION);
1161 }
1162
1163 static void refreshdrivers_animdata_fn(int UNUSED(event),
1164                                        TreeElement *UNUSED(te),
1165                                        TreeStoreElem *tselem,
1166                                        void *UNUSED(arg))
1167 {
1168   IdAdtTemplate *iat = (IdAdtTemplate *)tselem->id;
1169
1170   /* Loop over drivers, performing refresh
1171    * (i.e. check graph_buttons.c and rna_fcurve.c for details). */
1172   LISTBASE_FOREACH (FCurve *, fcu, &iat->adt->drivers) {
1173     fcu->flag &= ~FCURVE_DISABLED;
1174
1175     if (fcu->driver) {
1176       fcu->driver->flag &= ~DRIVER_FLAG_INVALID;
1177     }
1178   }
1179 }
1180
1181 /** \} */
1182
1183 /* -------------------------------------------------------------------- */
1184 /** \name Object Operation Utilities
1185  * \{ */
1186
1187 typedef enum eOutliner_PropDataOps {
1188   OL_DOP_SELECT = 1,
1189   OL_DOP_DESELECT,
1190   OL_DOP_HIDE,
1191   OL_DOP_UNHIDE,
1192   OL_DOP_SELECT_LINKED,
1193 } eOutliner_PropDataOps;
1194
1195 typedef enum eOutliner_PropConstraintOps {
1196   OL_CONSTRAINTOP_ENABLE = 1,
1197   OL_CONSTRAINTOP_DISABLE,
1198   OL_CONSTRAINTOP_DELETE,
1199 } eOutliner_PropConstraintOps;
1200
1201 typedef enum eOutliner_PropModifierOps {
1202   OL_MODIFIER_OP_TOGVIS = 1,
1203   OL_MODIFIER_OP_TOGREN,
1204   OL_MODIFIER_OP_DELETE,
1205 } eOutliner_PropModifierOps;
1206
1207 static void pchan_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *UNUSED(arg))
1208 {
1209   bPoseChannel *pchan = (bPoseChannel *)te->directdata;
1210
1211   if (event == OL_DOP_SELECT) {
1212     pchan->bone->flag |= BONE_SELECTED;
1213   }
1214   else if (event == OL_DOP_DESELECT) {
1215     pchan->bone->flag &= ~BONE_SELECTED;
1216   }
1217   else if (event == OL_DOP_HIDE) {
1218     pchan->bone->flag |= BONE_HIDDEN_P;
1219     pchan->bone->flag &= ~BONE_SELECTED;
1220   }
1221   else if (event == OL_DOP_UNHIDE) {
1222     pchan->bone->flag &= ~BONE_HIDDEN_P;
1223   }
1224 }
1225
1226 static void bone_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *UNUSED(arg))
1227 {
1228   Bone *bone = (Bone *)te->directdata;
1229
1230   if (event == OL_DOP_SELECT) {
1231     bone->flag |= BONE_SELECTED;
1232   }
1233   else if (event == OL_DOP_DESELECT) {
1234     bone->flag &= ~BONE_SELECTED;
1235   }
1236   else if (event == OL_DOP_HIDE) {
1237     bone->flag |= BONE_HIDDEN_P;
1238     bone->flag &= ~BONE_SELECTED;
1239   }
1240   else if (event == OL_DOP_UNHIDE) {
1241     bone->flag &= ~BONE_HIDDEN_P;
1242   }
1243 }
1244
1245 static void ebone_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *UNUSED(arg))
1246 {
1247   EditBone *ebone = (EditBone *)te->directdata;
1248
1249   if (event == OL_DOP_SELECT) {
1250     ebone->flag |= BONE_SELECTED;
1251   }
1252   else if (event == OL_DOP_DESELECT) {
1253     ebone->flag &= ~BONE_SELECTED;
1254   }
1255   else if (event == OL_DOP_HIDE) {
1256     ebone->flag |= BONE_HIDDEN_A;
1257     ebone->flag &= ~BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL;
1258   }
1259   else if (event == OL_DOP_UNHIDE) {
1260     ebone->flag &= ~BONE_HIDDEN_A;
1261   }
1262 }
1263
1264 static void sequence_fn(int event, TreeElement *te, TreeStoreElem *tselem, void *scene_ptr)
1265 {
1266   Sequence *seq = (Sequence *)te->directdata;
1267   if (event == OL_DOP_SELECT) {
1268     Scene *scene = (Scene *)scene_ptr;
1269     Editing *ed = SEQ_editing_get(scene, false);
1270     if (BLI_findindex(ed->seqbasep, seq) != -1) {
1271       ED_sequencer_select_sequence_single(scene, seq, true);
1272     }
1273   }
1274
1275   (void)tselem;
1276 }
1277
1278 static void gpencil_layer_fn(int event,
1279                              TreeElement *te,
1280                              TreeStoreElem *UNUSED(tselem),
1281                              void *UNUSED(arg))
1282 {
1283   bGPDlayer *gpl = (bGPDlayer *)te->directdata;
1284
1285   if (event == OL_DOP_SELECT) {
1286     gpl->flag |= GP_LAYER_SELECT;
1287   }
1288   else if (event == OL_DOP_DESELECT) {
1289     gpl->flag &= ~GP_LAYER_SELECT;
1290   }
1291   else if (event == OL_DOP_HIDE) {
1292     gpl->flag |= GP_LAYER_HIDE;
1293   }
1294   else if (event == OL_DOP_UNHIDE) {
1295     gpl->flag &= ~GP_LAYER_HIDE;
1296   }
1297 }
1298
1299 static void data_select_linked_fn(int event,
1300                                   TreeElement *te,
1301                                   TreeStoreElem *UNUSED(tselem),
1302                                   void *C_v)
1303 {
1304   if (event == OL_DOP_SELECT_LINKED) {
1305     if (RNA_struct_is_ID(te->rnaptr.type)) {
1306       bContext *C = (bContext *)C_v;
1307       ID *id = te->rnaptr.data;
1308
1309       ED_object_select_linked_by_id(C, id);
1310     }
1311   }
1312 }
1313
1314 static void constraint_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *C_v)
1315 {
1316   bContext *C = C_v;
1317   Main *bmain = CTX_data_main(C);
1318   bConstraint *constraint = (bConstraint *)te->directdata;
1319   Object *ob = (Object *)outliner_search_back(te, ID_OB);
1320
1321   if (event == OL_CONSTRAINTOP_ENABLE) {
1322     constraint->flag &= ~CONSTRAINT_OFF;
1323     ED_object_constraint_update(bmain, ob);
1324     WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
1325   }
1326   else if (event == OL_CONSTRAINTOP_DISABLE) {
1327     constraint->flag = CONSTRAINT_OFF;
1328     ED_object_constraint_update(bmain, ob);
1329     WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
1330   }
1331   else if (event == OL_CONSTRAINTOP_DELETE) {
1332     ListBase *lb = NULL;
1333
1334     if (TREESTORE(te->parent->parent)->type == TSE_POSE_CHANNEL) {
1335       lb = &((bPoseChannel *)te->parent->parent->directdata)->constraints;
1336     }
1337     else {
1338       lb = &ob->constraints;
1339     }
1340
1341     if (BKE_constraint_remove_ex(lb, ob, constraint, true)) {
1342       /* there's no active constraint now, so make sure this is the case */
1343       BKE_constraints_active_set(&ob->constraints, NULL);
1344
1345       /* needed to set the flags on posebones correctly */
1346       ED_object_constraint_update(bmain, ob);
1347
1348       WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob);
1349       te->store_elem->flag &= ~TSE_SELECTED;
1350     }
1351   }
1352 }
1353
1354 static void modifier_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *Carg)
1355 {
1356   bContext *C = (bContext *)Carg;
1357   Main *bmain = CTX_data_main(C);
1358   Scene *scene = CTX_data_scene(C);
1359   ModifierData *md = (ModifierData *)te->directdata;
1360   Object *ob = (Object *)outliner_search_back(te, ID_OB);
1361
1362   if (event == OL_MODIFIER_OP_TOGVIS) {
1363     md->mode ^= eModifierMode_Realtime;
1364     DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
1365     WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
1366   }
1367   else if (event == OL_MODIFIER_OP_TOGREN) {
1368     md->mode ^= eModifierMode_Render;
1369     DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
1370     WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
1371   }
1372   else if (event == OL_MODIFIER_OP_DELETE) {
1373     ED_object_modifier_remove(NULL, bmain, scene, ob, md);
1374     WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER | NA_REMOVED, ob);
1375     te->store_elem->flag &= ~TSE_SELECTED;
1376   }
1377 }
1378
1379 static void outliner_do_data_operation(
1380     SpaceOutliner *space_outliner,
1381     int type,
1382     int event,
1383     ListBase *lb,
1384     void (*operation_fn)(int, TreeElement *, TreeStoreElem *, void *),
1385     void *arg)
1386 {
1387   LISTBASE_FOREACH (TreeElement *, te, lb) {
1388     TreeStoreElem *tselem = TREESTORE(te);
1389     if (tselem->flag & TSE_SELECTED) {
1390       if (tselem->type == type) {
1391         operation_fn(event, te, tselem, arg);
1392       }
1393     }
1394     if (TSELEM_OPEN(tselem, space_outliner)) {
1395       outliner_do_data_operation(space_outliner, type, event, &te->subtree, operation_fn, arg);
1396     }
1397   }
1398 }
1399
1400 static Base *outline_batch_delete_hierarchy(
1401     ReportList *reports, Main *bmain, ViewLayer *view_layer, Scene *scene, Base *base)
1402 {
1403   Base *child_base, *base_next;
1404   Object *object, *parent;
1405
1406   if (!base) {
1407     return NULL;
1408   }
1409
1410   object = base->object;
1411   for (child_base = view_layer->object_bases.first; child_base; child_base = base_next) {
1412     base_next = child_base->next;
1413     for (parent = child_base->object->parent; parent && (parent != object);
1414          parent = parent->parent) {
1415       /* pass */
1416     }
1417     if (parent) {
1418       base_next = outline_batch_delete_hierarchy(reports, bmain, view_layer, scene, child_base);
1419     }
1420   }
1421
1422   base_next = base->next;
1423
1424   if (object->id.tag & LIB_TAG_INDIRECT) {
1425     BKE_reportf(reports,
1426                 RPT_WARNING,
1427                 "Cannot delete indirectly linked object '%s'",
1428                 base->object->id.name + 2);
1429     return base_next;
1430   }
1431   if (ID_REAL_USERS(object) <= 1 && ID_EXTRA_USERS(object) == 0 &&
1432       BKE_library_ID_is_indirectly_used(bmain, object)) {
1433     BKE_reportf(reports,
1434                 RPT_WARNING,
1435                 "Cannot delete object '%s' from scene '%s', indirectly used objects need at least "
1436                 "one user",
1437                 object->id.name + 2,
1438                 scene->id.name + 2);
1439     return base_next;
1440   }
1441
1442   DEG_id_tag_update_ex(bmain, &object->id, ID_RECALC_BASE_FLAGS);
1443   BKE_scene_collections_object_remove(bmain, scene, object, false);
1444
1445   if (object->id.us == 0) {
1446     object->id.tag |= LIB_TAG_DOIT;
1447   }
1448
1449   return base_next;
1450 }
1451
1452 static void object_batch_delete_hierarchy_fn(bContext *C,
1453                                              ReportList *reports,
1454                                              Scene *scene,
1455                                              Object *ob)
1456 {
1457   ViewLayer *view_layer = CTX_data_view_layer(C);
1458   Object *obedit = CTX_data_edit_object(C);
1459
1460   Base *base = BKE_view_layer_base_find(view_layer, ob);
1461
1462   if (base) {
1463     /* Check also library later. */
1464     for (; obedit && (obedit != base->object); obedit = obedit->parent) {
1465       /* pass */
1466     }
1467     if (obedit == base->object) {
1468       ED_object_editmode_exit(C, EM_FREEDATA);
1469     }
1470
1471     outline_batch_delete_hierarchy(reports, CTX_data_main(C), view_layer, scene, base);
1472   }
1473 }
1474
1475 /** \} */
1476
1477 /* -------------------------------------------------------------------- */
1478 /** \name Object Menu Operator
1479  * \{ */
1480
1481 enum {
1482   OL_OP_SELECT = 1,
1483   OL_OP_DESELECT,
1484   OL_OP_SELECT_HIERARCHY,
1485   OL_OP_REMAP,
1486   OL_OP_RENAME,
1487   OL_OP_PROXY_TO_OVERRIDE_CONVERT,
1488 };
1489
1490 static const EnumPropertyItem prop_object_op_types[] = {
1491     {OL_OP_SELECT, "SELECT", ICON_RESTRICT_SELECT_OFF, "Select", ""},
1492     {OL_OP_DESELECT, "DESELECT", 0, "Deselect", ""},
1493     {OL_OP_SELECT_HIERARCHY, "SELECT_HIERARCHY", 0, "Select Hierarchy", ""},
1494     {OL_OP_REMAP,
1495      "REMAP",
1496      0,
1497      "Remap Users",
1498      "Make all users of selected data-blocks to use instead a new chosen one"},
1499     {OL_OP_RENAME, "RENAME", 0, "Rename", ""},
1500     {OL_OP_PROXY_TO_OVERRIDE_CONVERT,
1501      "OBJECT_PROXY_TO_OVERRIDE",
1502      0,
1503      "Convert Proxy to Override",
1504      "Convert a Proxy object to a full library override, including all its dependencies"},
1505     {0, NULL, 0, NULL, NULL},
1506 };
1507
1508 static int outliner_object_operation_exec(bContext *C, wmOperator *op)
1509 {
1510   Main *bmain = CTX_data_main(C);
1511   Scene *scene = CTX_data_scene(C);
1512   wmWindow *win = CTX_wm_window(C);
1513   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1514   int event;
1515   const char *str = NULL;
1516   bool selection_changed = false;
1517
1518   /* check for invalid states */
1519   if (space_outliner == NULL) {
1520     return OPERATOR_CANCELLED;
1521   }
1522
1523   event = RNA_enum_get(op->ptr, "type");
1524
1525   if (event == OL_OP_SELECT) {
1526     Scene *sce = scene; /* To be able to delete, scenes are set... */
1527     outliner_do_object_operation(
1528         C, op->reports, scene, space_outliner, &space_outliner->tree, object_select_fn);
1529     if (scene != sce) {
1530       WM_window_set_active_scene(bmain, C, win, sce);
1531     }
1532
1533     str = "Select Objects";
1534     selection_changed = true;
1535   }
1536   else if (event == OL_OP_SELECT_HIERARCHY) {
1537     Scene *sce = scene; /* To be able to delete, scenes are set... */
1538     outliner_do_object_operation_ex(C,
1539                                     op->reports,
1540                                     scene,
1541                                     space_outliner,
1542                                     &space_outliner->tree,
1543                                     object_select_hierarchy_fn,
1544                                     NULL,
1545                                     false);
1546     if (scene != sce) {
1547       WM_window_set_active_scene(bmain, C, win, sce);
1548     }
1549     str = "Select Object Hierarchy";
1550     selection_changed = true;
1551   }
1552   else if (event == OL_OP_DESELECT) {
1553     outliner_do_object_operation(
1554         C, op->reports, scene, space_outliner, &space_outliner->tree, object_deselect_fn);
1555     str = "Deselect Objects";
1556     selection_changed = true;
1557   }
1558   else if (event == OL_OP_REMAP) {
1559     outliner_do_libdata_operation(
1560         C, op->reports, scene, space_outliner, &space_outliner->tree, id_remap_fn, NULL);
1561     /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
1562      * trick does not work here). */
1563   }
1564   else if (event == OL_OP_RENAME) {
1565     outliner_do_object_operation(
1566         C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn);
1567     str = "Rename Object";
1568   }
1569   else if (event == OL_OP_PROXY_TO_OVERRIDE_CONVERT) {
1570     outliner_do_object_operation(C,
1571                                  op->reports,
1572                                  scene,
1573                                  space_outliner,
1574                                  &space_outliner->tree,
1575                                  object_proxy_to_override_convert_fn);
1576     str = "Convert Proxy to Override";
1577   }
1578   else {
1579     BLI_assert(0);
1580     return OPERATOR_CANCELLED;
1581   }
1582
1583   if (selection_changed) {
1584     DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1585     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1586     ED_outliner_select_sync_from_object_tag(C);
1587   }
1588
1589   if (str != NULL) {
1590     ED_undo_push(C, str);
1591   }
1592
1593   return OPERATOR_FINISHED;
1594 }
1595
1596 void OUTLINER_OT_object_operation(wmOperatorType *ot)
1597 {
1598   /* identifiers */
1599   ot->name = "Outliner Object Operation";
1600   ot->idname = "OUTLINER_OT_object_operation";
1601
1602   /* callbacks */
1603   ot->invoke = WM_menu_invoke;
1604   ot->exec = outliner_object_operation_exec;
1605   ot->poll = ED_operator_outliner_active;
1606
1607   ot->flag = 0;
1608
1609   ot->prop = RNA_def_enum(ot->srna, "type", prop_object_op_types, 0, "Object Operation", "");
1610 }
1611
1612 /** \} */
1613
1614 /* -------------------------------------------------------------------- */
1615 /** \name Delete Object/Collection Operator
1616  * \{ */
1617
1618 typedef void (*OutlinerDeleteFunc)(bContext *C, ReportList *reports, Scene *scene, Object *ob);
1619
1620 static void outliner_do_object_delete(bContext *C,
1621                                       ReportList *reports,
1622                                       Scene *scene,
1623                                       GSet *objects_to_delete,
1624                                       OutlinerDeleteFunc delete_fn)
1625 {
1626   GSetIterator objects_to_delete_iter;
1627   GSET_ITER (objects_to_delete_iter, objects_to_delete) {
1628     Object *ob = (Object *)BLI_gsetIterator_getKey(&objects_to_delete_iter);
1629
1630     delete_fn(C, reports, scene, ob);
1631   }
1632 }
1633
1634 static TreeTraversalAction outliner_find_objects_to_delete(TreeElement *te, void *customdata)
1635 {
1636   GSet *objects_to_delete = (GSet *)customdata;
1637   TreeStoreElem *tselem = TREESTORE(te);
1638
1639   if (outliner_is_collection_tree_element(te)) {
1640     return TRAVERSE_CONTINUE;
1641   }
1642
1643   if ((tselem->type != TSE_SOME_ID) || (tselem->id == NULL) || (GS(tselem->id->name) != ID_OB)) {
1644     return TRAVERSE_SKIP_CHILDS;
1645   }
1646
1647   BLI_gset_add(objects_to_delete, tselem->id);
1648
1649   return TRAVERSE_CONTINUE;
1650 }
1651
1652 static int outliner_delete_exec(bContext *C, wmOperator *op)
1653 {
1654   Main *bmain = CTX_data_main(C);
1655   Scene *scene = CTX_data_scene(C);
1656   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1657   struct wmMsgBus *mbus = CTX_wm_message_bus(C);
1658   ViewLayer *view_layer = CTX_data_view_layer(C);
1659   const Base *basact_prev = BASACT(view_layer);
1660
1661   const bool delete_hierarchy = RNA_boolean_get(op->ptr, "hierarchy");
1662
1663   /* Get selected objects skipping duplicates to prevent deleting objects linked to multiple
1664    * collections twice */
1665   GSet *objects_to_delete = BLI_gset_ptr_new(__func__);
1666   outliner_tree_traverse(space_outliner,
1667                          &space_outliner->tree,
1668                          0,
1669                          TSE_SELECTED,
1670                          outliner_find_objects_to_delete,
1671                          objects_to_delete);
1672
1673   if (delete_hierarchy) {
1674     BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
1675
1676     outliner_do_object_delete(
1677         C, op->reports, scene, objects_to_delete, object_batch_delete_hierarchy_fn);
1678
1679     BKE_id_multi_tagged_delete(bmain);
1680   }
1681   else {
1682     outliner_do_object_delete(C, op->reports, scene, objects_to_delete, outliner_object_delete_fn);
1683   }
1684
1685   BLI_gset_free(objects_to_delete, NULL);
1686
1687   outliner_collection_delete(C, bmain, scene, op->reports, delete_hierarchy);
1688
1689   /* Tree management normally happens from draw_outliner(), but when
1690    * you're clicking too fast on Delete object from context menu in
1691    * outliner several mouse events can be handled in one cycle without
1692    * handling notifiers/redraw which leads to deleting the same object twice.
1693    * cleanup tree here to prevent such cases. */
1694   outliner_cleanup_tree(space_outliner);
1695
1696   DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
1697   DEG_relations_tag_update(bmain);
1698
1699   if (basact_prev != BASACT(view_layer)) {
1700     WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
1701     WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, LayerObjects, active);
1702   }
1703
1704   DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1705   WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1706   WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
1707   ED_outliner_select_sync_from_object_tag(C);
1708
1709   return OPERATOR_FINISHED;
1710 }
1711
1712 void OUTLINER_OT_delete(wmOperatorType *ot)
1713 {
1714   /* identifiers */
1715   ot->name = "Delete";
1716   ot->idname = "OUTLINER_OT_delete";
1717   ot->description = "Delete selected objects and collections";
1718
1719   /* callbacks */
1720   ot->exec = outliner_delete_exec;
1721   ot->poll = ED_operator_outliner_active;
1722
1723   /* flags */
1724   ot->flag |= OPTYPE_REGISTER | OPTYPE_UNDO;
1725
1726   /* properties */
1727   PropertyRNA *prop = RNA_def_boolean(
1728       ot->srna, "hierarchy", false, "Hierarchy", "Delete child objects and collections");
1729   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1730 }
1731
1732 /** \} */
1733
1734 /* -------------------------------------------------------------------- */
1735 /** \name ID-Data Menu Operator
1736  * \{ */
1737
1738 typedef enum eOutlinerIdOpTypes {
1739   OUTLINER_IDOP_INVALID = 0,
1740
1741   OUTLINER_IDOP_UNLINK,
1742   OUTLINER_IDOP_MARK_ASSET,
1743   OUTLINER_IDOP_CLEAR_ASSET,
1744   OUTLINER_IDOP_LOCAL,
1745   OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
1746   OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY,
1747   OUTLINER_IDOP_OVERRIDE_LIBRARY_PROXY_CONVERT,
1748   OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET,
1749   OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY,
1750   OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY,
1751   OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE,
1752   OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY,
1753   OUTLINER_IDOP_SINGLE,
1754   OUTLINER_IDOP_DELETE,
1755   OUTLINER_IDOP_REMAP,
1756
1757   OUTLINER_IDOP_COPY,
1758   OUTLINER_IDOP_PASTE,
1759
1760   OUTLINER_IDOP_FAKE_ADD,
1761   OUTLINER_IDOP_FAKE_CLEAR,
1762   OUTLINER_IDOP_RENAME,
1763
1764   OUTLINER_IDOP_SELECT_LINKED,
1765 } eOutlinerIdOpTypes;
1766
1767 /* TODO: implement support for changing the ID-block used. */
1768 static const EnumPropertyItem prop_id_op_types[] = {
1769     {OUTLINER_IDOP_UNLINK, "UNLINK", 0, "Unlink", ""},
1770     {OUTLINER_IDOP_MARK_ASSET, "MARK_ASSET", 0, "Mark Asset", ""},
1771     {OUTLINER_IDOP_CLEAR_ASSET, "CLEAR_ASSET", 0, "Clear Asset", ""},
1772     {OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""},
1773     {OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""},
1774     {OUTLINER_IDOP_DELETE, "DELETE", ICON_X, "Delete", ""},
1775     {OUTLINER_IDOP_REMAP,
1776      "REMAP",
1777      0,
1778      "Remap Users",
1779      "Make all users of selected data-blocks to use instead current (clicked) one"},
1780     {0, "", 0, NULL, NULL},
1781     {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
1782      "OVERRIDE_LIBRARY_CREATE",
1783      0,
1784      "Make Library Override",
1785      "Make a local override of this linked data-block"},
1786     {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY,
1787      "OVERRIDE_LIBRARY_CREATE_HIERARCHY",
1788      0,
1789      "Make Library Override Hierarchy",
1790      "Make a local override of this linked data-block, and its hierarchy of dependencies"},
1791     {OUTLINER_IDOP_OVERRIDE_LIBRARY_PROXY_CONVERT,
1792      "OVERRIDE_LIBRARY_PROXY_CONVERT",
1793      0,
1794      "Convert Proxy to Override",
1795      "Convert a Proxy object to a full library override, including all its dependencies"},
1796     {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET,
1797      "OVERRIDE_LIBRARY_RESET",
1798      0,
1799      "Reset Library Override",
1800      "Reset this local override to its linked values"},
1801     {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY,
1802      "OVERRIDE_LIBRARY_RESET_HIERARCHY",
1803      0,
1804      "Reset Library Override Hierarchy",
1805      "Reset this local override to its linked values, as well as its hierarchy of dependencies"},
1806     {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY,
1807      "OVERRIDE_LIBRARY_RESYNC_HIERARCHY",
1808      0,
1809      "Resync Library Override Hierarchy",
1810      "Rebuild this local override from its linked reference, as well as its hierarchy of "
1811      "dependencies"},
1812     {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE,
1813      "OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE",
1814      0,
1815      "Resync Library Override Hierarchy Enforce",
1816      "Rebuild this local override from its linked reference, as well as its hierarchy of "
1817      "dependencies, enforcing that hierarchy to match the linked data (i.e. ignoring exiting "
1818      "overrides on data-blocks pointer properties)"},
1819     {OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY,
1820      "OVERRIDE_LIBRARY_DELETE_HIERARCHY",
1821      0,
1822      "Delete Library Override Hierarchy",
1823      "Delete this local override (including its hierarchy of override dependencies) and relink "
1824      "its usages to the linked data-blocks"},
1825     {0, "", 0, NULL, NULL},
1826     {OUTLINER_IDOP_COPY, "COPY", ICON_COPYDOWN, "Copy", ""},
1827     {OUTLINER_IDOP_PASTE, "PASTE", ICON_PASTEDOWN, "Paste", ""},
1828     {0, "", 0, NULL, NULL},
1829     {OUTLINER_IDOP_FAKE_ADD,
1830      "ADD_FAKE",
1831      0,
1832      "Add Fake User",
1833      "Ensure data-block gets saved even if it isn't in use (e.g. for motion and material "
1834      "libraries)"},
1835     {OUTLINER_IDOP_FAKE_CLEAR, "CLEAR_FAKE", 0, "Clear Fake User", ""},
1836     {OUTLINER_IDOP_RENAME, "RENAME", 0, "Rename", ""},
1837     {OUTLINER_IDOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""},
1838     {0, NULL, 0, NULL, NULL},
1839 };
1840
1841 static bool outliner_id_operation_item_poll(bContext *C,
1842                                             PointerRNA *UNUSED(ptr),
1843                                             PropertyRNA *UNUSED(prop),
1844                                             const int enum_value)
1845 {
1846   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1847   TreeElement *te = get_target_element(space_outliner);
1848   TreeStoreElem *tselem = TREESTORE(te);
1849   if (!TSE_IS_REAL_ID(tselem)) {
1850     return false;
1851   }
1852
1853   switch (enum_value) {
1854     case OUTLINER_IDOP_MARK_ASSET:
1855     case OUTLINER_IDOP_CLEAR_ASSET:
1856       return U.experimental.use_asset_browser;
1857     case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE:
1858       if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id)) {
1859         return true;
1860       }
1861       return false;
1862     case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY:
1863       if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id) || (ID_IS_LINKED(tselem->id))) {
1864         return true;
1865       }
1866       return false;
1867     case OUTLINER_IDOP_OVERRIDE_LIBRARY_PROXY_CONVERT: {
1868       if (GS(tselem->id->name) == ID_OB) {
1869         Object *ob = (Object *)tselem->id;
1870
1871         if ((ob != NULL) && (ob->proxy != NULL)) {
1872           return true;
1873         }
1874       }
1875       return false;
1876     }
1877     case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET:
1878     case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY:
1879     case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY:
1880     case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE:
1881     case OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY:
1882       if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id) && !ID_IS_LINKED(tselem->id)) {
1883         return true;
1884       }
1885       return false;
1886     case OUTLINER_IDOP_SINGLE:
1887       if (ELEM(space_outliner->outlinevis, SO_SCENES, SO_VIEW_LAYER)) {
1888         return true;
1889       }
1890       /* TODO(dalai): enable in the few cases where this can be supported
1891        * (i.e., when we have a valid parent for the tselem). */
1892       return false;
1893   }
1894
1895   return true;
1896 }
1897
1898 static const EnumPropertyItem *outliner_id_operation_itemf(bContext *C,
1899                                                            PointerRNA *ptr,
1900                                                            PropertyRNA *prop,
1901                                                            bool *r_free)
1902 {
1903   EnumPropertyItem *items = NULL;
1904   int totitem = 0;
1905
1906   if ((C == NULL) || (ED_operator_outliner_active(C) == false)) {
1907     return prop_id_op_types;
1908   }
1909   for (const EnumPropertyItem *it = prop_id_op_types; it->identifier != NULL; it++) {
1910     if (!outliner_id_operation_item_poll(C, ptr, prop, it->value)) {
1911       continue;
1912     }
1913     RNA_enum_item_add(&items, &totitem, it);
1914   }
1915   RNA_enum_item_end(&items, &totitem);
1916   *r_free = true;
1917
1918   return items;
1919 }
1920
1921 static int outliner_id_operation_exec(bContext *C, wmOperator *op)
1922 {
1923   wmWindowManager *wm = CTX_wm_manager(C);
1924   Scene *scene = CTX_data_scene(C);
1925   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1926   int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1927
1928   /* check for invalid states */
1929   if (space_outliner == NULL) {
1930     return OPERATOR_CANCELLED;
1931   }
1932
1933   TreeElement *te = get_target_element(space_outliner);
1934   get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
1935
1936   eOutlinerIdOpTypes event = RNA_enum_get(op->ptr, "type");
1937   switch (event) {
1938     case OUTLINER_IDOP_UNLINK: {
1939       /* unlink datablock from its parent */
1940       if (objectlevel) {
1941         outliner_do_libdata_operation(
1942             C, op->reports, scene, space_outliner, &space_outliner->tree, unlink_object_fn, NULL);
1943
1944         WM_event_add_notifier(C, NC_SCENE | ND_LAYER, NULL);
1945         ED_undo_push(C, "Unlink Object");
1946         break;
1947       }
1948
1949       switch (idlevel) {
1950         case ID_AC:
1951           outliner_do_libdata_operation(C,
1952                                         op->reports,
1953                                         scene,
1954                                         space_outliner,
1955                                         &space_outliner->tree,
1956                                         unlink_action_fn,
1957                                         NULL);
1958
1959           WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
1960           ED_undo_push(C, "Unlink action");
1961           break;
1962         case ID_MA:
1963           outliner_do_libdata_operation(C,
1964                                         op->reports,
1965                                         scene,
1966                                         space_outliner,
1967                                         &space_outliner->tree,
1968                                         unlink_material_fn,
1969                                         NULL);
1970
1971           WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, NULL);
1972           ED_undo_push(C, "Unlink material");
1973           break;
1974         case ID_TE:
1975           outliner_do_libdata_operation(C,
1976                                         op->reports,
1977                                         scene,
1978                                         space_outliner,
1979                                         &space_outliner->tree,
1980                                         unlink_texture_fn,
1981                                         NULL);
1982
1983           WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, NULL);
1984           ED_undo_push(C, "Unlink texture");
1985           break;
1986         case ID_WO:
1987           outliner_do_libdata_operation(
1988               C, op->reports, scene, space_outliner, &space_outliner->tree, unlink_world_fn, NULL);
1989
1990           WM_event_add_notifier(C, NC_SCENE | ND_WORLD, NULL);
1991           ED_undo_push(C, "Unlink world");
1992           break;
1993         case ID_GR:
1994           outliner_do_libdata_operation(C,
1995                                         op->reports,
1996                                         scene,
1997                                         space_outliner,
1998                                         &space_outliner->tree,
1999                                         unlink_collection_fn,
2000                                         NULL);
2001
2002           WM_event_add_notifier(C, NC_SCENE | ND_LAYER, NULL);
2003           ED_undo_push(C, "Unlink Collection");
2004           break;
2005         default:
2006           BKE_report(op->reports, RPT_WARNING, "Not yet implemented");
2007           break;
2008       }
2009       break;
2010     }
2011     case OUTLINER_IDOP_MARK_ASSET: {
2012       WM_operator_name_call(C, "ASSET_OT_mark", WM_OP_EXEC_DEFAULT, NULL);
2013       break;
2014     }
2015     case OUTLINER_IDOP_CLEAR_ASSET: {
2016       WM_operator_name_call(C, "ASSET_OT_clear", WM_OP_EXEC_DEFAULT, NULL);
2017       break;
2018     }
2019     case OUTLINER_IDOP_LOCAL: {
2020       /* make local */
2021       outliner_do_libdata_operation(
2022           C, op->reports, scene, space_outliner, &space_outliner->tree, id_local_fn, NULL);
2023       ED_undo_push(C, "Localized Data");
2024       break;
2025     }
2026     case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: {
2027       outliner_do_libdata_operation(C,
2028                                     op->reports,
2029                                     scene,
2030                                     space_outliner,
2031                                     &space_outliner->tree,
2032                                     id_override_library_create_fn,
2033                                     &(OutlinerLibOverrideData){.do_hierarchy = false});
2034       ED_undo_push(C, "Overridden Data");
2035       break;
2036     }
2037     case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: {
2038       outliner_do_libdata_operation(C,
2039                                     op->reports,
2040                                     scene,
2041                                     space_outliner,
2042                                     &space_outliner->tree,
2043                                     id_override_library_create_fn,
2044                                     &(OutlinerLibOverrideData){.do_hierarchy = true});
2045       ED_undo_push(C, "Overridden Data Hierarchy");
2046       break;
2047     }
2048     case OUTLINER_IDOP_OVERRIDE_LIBRARY_PROXY_CONVERT: {
2049       outliner_do_object_operation(C,
2050                                    op->reports,
2051                                    scene,
2052                                    space_outliner,
2053                                    &space_outliner->tree,
2054                                    object_proxy_to_override_convert_fn);
2055       ED_undo_push(C, "Convert Proxy to Override");
2056       break;
2057     }
2058     case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: {
2059       outliner_do_libdata_operation(C,
2060                                     op->reports,
2061                                     scene,
2062                                     space_outliner,
2063                                     &space_outliner->tree,
2064                                     id_override_library_reset_fn,
2065                                     &(OutlinerLibOverrideData){.do_hierarchy = false});
2066       ED_undo_push(C, "Reset Overridden Data");
2067       break;
2068     }
2069     case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: {
2070       outliner_do_libdata_operation(C,
2071                                     op->reports,
2072                                     scene,
2073                                     space_outliner,
2074                                     &space_outliner->tree,
2075                                     id_override_library_reset_fn,
2076                                     &(OutlinerLibOverrideData){.do_hierarchy = true});
2077       ED_undo_push(C, "Reset Overridden Data Hierarchy");
2078       break;
2079     }
2080     case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: {
2081       outliner_do_libdata_operation(C,
2082                                     op->reports,
2083                                     scene,
2084                                     space_outliner,
2085                                     &space_outliner->tree,
2086                                     id_override_library_resync_fn,
2087                                     &(OutlinerLibOverrideData){.do_hierarchy = true});
2088       ED_undo_push(C, "Resync Overridden Data Hierarchy");
2089       break;
2090     }
2091     case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE: {
2092       outliner_do_libdata_operation(
2093           C,
2094           op->reports,
2095           scene,
2096           space_outliner,
2097           &space_outliner->tree,
2098           id_override_library_resync_fn,
2099           &(OutlinerLibOverrideData){.do_hierarchy = true, .do_resync_hierarchy_enforce = true});
2100       ED_undo_push(C, "Resync Overridden Data Hierarchy");
2101       break;
2102     }
2103     case OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY: {
2104       outliner_do_libdata_operation(C,
2105                                     op->reports,
2106                                     scene,
2107                                     space_outliner,
2108                                     &space_outliner->tree,
2109                                     id_override_library_delete_fn,
2110                                     &(OutlinerLibOverrideData){.do_hierarchy = true});
2111       ED_undo_push(C, "Delete Overridden Data Hierarchy");
2112       break;
2113     }
2114     case OUTLINER_IDOP_SINGLE: {
2115       /* make single user */
2116       switch (idlevel) {
2117         case ID_AC:
2118           outliner_do_libdata_operation(C,
2119                                         op->reports,
2120                                         scene,
2121                                         space_outliner,
2122                                         &space_outliner->tree,
2123                                         singleuser_action_fn,
2124                                         NULL);
2125
2126           WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
2127           ED_undo_push(C, "Single-User Action");
2128           break;
2129
2130         case ID_WO:
2131           outliner_do_libdata_operation(C,
2132                                         op->reports,
2133                                         scene,
2134                                         space_outliner,
2135                                         &space_outliner->tree,
2136                                         singleuser_world_fn,
2137                                         NULL);
2138
2139           WM_event_add_notifier(C, NC_SCENE | ND_WORLD, NULL);
2140           ED_undo_push(C, "Single-User World");
2141           break;
2142
2143         default:
2144           BKE_report(op->reports, RPT_WARNING, "Not yet implemented");
2145           break;
2146       }
2147       break;
2148     }
2149     case OUTLINER_IDOP_DELETE: {
2150       if (idlevel > 0) {
2151         outliner_do_libdata_operation(
2152             C, op->reports, scene, space_outliner, &space_outliner->tree, id_delete_fn, NULL);
2153         ED_undo_push(C, "Delete");
2154       }
2155       break;
2156     }
2157     case OUTLINER_IDOP_REMAP: {
2158       if (idlevel > 0) {
2159         outliner_do_libdata_operation(
2160             C, op->reports, scene, space_outliner, &space_outliner->tree, id_remap_fn, NULL);
2161         /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
2162          * trick does not work here). */
2163       }
2164       break;
2165     }
2166     case OUTLINER_IDOP_COPY: {
2167       wm->op_undo_depth++;
2168       WM_operator_name_call(C, "OUTLINER_OT_id_copy", WM_OP_INVOKE_DEFAULT, NULL);
2169       wm->op_undo_depth--;
2170       /* No need for undo, this operation does not change anything... */
2171       break;
2172     }
2173     case OUTLINER_IDOP_PASTE: {
2174       wm->op_undo_depth++;
2175       WM_operator_name_call(C, "OUTLINER_OT_id_paste", WM_OP_INVOKE_DEFAULT, NULL);
2176       wm->op_undo_depth--;
2177       ED_outliner_select_sync_from_all_tag(C);
2178       ED_undo_push(C, "Paste");
2179       break;
2180     }
2181     case OUTLINER_IDOP_FAKE_ADD: {
2182       /* set fake user */
2183       outliner_do_libdata_operation(
2184           C, op->reports, scene, space_outliner, &space_outliner->tree, id_fake_user_set_fn, NULL);
2185
2186       WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
2187       ED_undo_push(C, "Add Fake User");
2188       break;
2189     }
2190     case OUTLINER_IDOP_FAKE_CLEAR: {
2191       /* clear fake user */
2192       outliner_do_libdata_operation(C,
2193                                     op->reports,
2194                                     scene,
2195                                     space_outliner,
2196                                     &space_outliner->tree,
2197                                     id_fake_user_clear_fn,
2198                                     NULL);
2199
2200       WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
2201       ED_undo_push(C, "Clear Fake User");
2202       break;
2203     }
2204     case OUTLINER_IDOP_RENAME: {
2205       /* rename */
2206       outliner_do_libdata_operation(
2207           C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn, NULL);
2208
2209       WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
2210       ED_undo_push(C, "Rename");
2211       break;
2212     }
2213     case OUTLINER_IDOP_SELECT_LINKED:
2214       outliner_do_libdata_operation(
2215           C, op->reports, scene, space_outliner, &space_outliner->tree, id_select_linked_fn, NULL);
2216       ED_outliner_select_sync_from_all_tag(C);
2217       ED_undo_push(C, "Select");
2218       break;
2219
2220     default:
2221       /* Invalid - unhandled. */
2222       break;
2223   }
2224
2225   /* wrong notifier still... */
2226   WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
2227
2228   /* XXX: this is just so that outliner is always up to date. */
2229   WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, NULL);
2230
2231   return OPERATOR_FINISHED;
2232 }
2233
2234 void OUTLINER_OT_id_operation(wmOperatorType *ot)
2235 {
2236   /* identifiers */
2237   ot->name = "Outliner ID Data Operation";
2238   ot->idname = "OUTLINER_OT_id_operation";
2239
2240   /* callbacks */
2241   ot->invoke = WM_menu_invoke;
2242   ot->exec = outliner_id_operation_exec;
2243   ot->poll = ED_operator_outliner_active;
2244
2245   ot->flag = 0;
2246
2247   ot->prop = RNA_def_enum(ot->srna, "type", prop_id_op_types, 0, "ID Data Operation", "");
2248   RNA_def_enum_funcs(ot->prop, outliner_id_operation_itemf);
2249 }
2250
2251 /** \} */
2252
2253 /* -------------------------------------------------------------------- */
2254 /** \name Library Menu Operator
2255  * \{ */
2256
2257 typedef enum eOutlinerLibOpTypes {
2258   OL_LIB_INVALID = 0,
2259
2260   OL_LIB_RENAME,
2261   OL_LIB_DELETE,
2262   OL_LIB_RELOCATE,
2263   OL_LIB_RELOAD,
2264 } eOutlinerLibOpTypes;
2265
2266 static const EnumPropertyItem outliner_lib_op_type_items[] = {
2267     {OL_LIB_RENAME, "RENAME", 0, "Rename", ""},
2268     {OL_LIB_DELETE,
2269      "DELETE",
2270      ICON_X,
2271      "Delete",
2272      "Delete this library and all its item.\n"
2273      "Warning: No undo"},
2274     {OL_LIB_RELOCATE,
2275      "RELOCATE",
2276      0,
2277      "Relocate",
2278      "Select a new path for this library, and reload all its data"},
2279     {OL_LIB_RELOAD, "RELOAD", ICON_FILE_REFRESH, "Reload", "Reload all data from this library"},
2280     {0, NULL, 0, NULL, NULL},
2281 };
2282
2283 static int outliner_lib_operation_exec(bContext *C, wmOperator *op)
2284 {
2285   Scene *scene = CTX_data_scene(C);
2286   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
2287   int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
2288
2289   /* check for invalid states */
2290   if (space_outliner == NULL) {
2291     return OPERATOR_CANCELLED;
2292   }
2293
2294   TreeElement *te = get_target_element(space_outliner);
2295   get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
2296
2297   eOutlinerLibOpTypes event = RNA_enum_get(op->ptr, "type");
2298   switch (event) {
2299     case OL_LIB_RENAME: {
2300       outliner_do_libdata_operation(
2301           C, op->reports, scene, space_outliner, &space_outliner->tree, item_rename_fn, NULL);
2302
2303       WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
2304       ED_undo_push(C, "Rename Library");
2305       break;
2306     }
2307     case OL_LIB_DELETE: {
2308       outliner_do_libdata_operation(
2309           C, op->reports, scene, space_outliner, &space_outliner->tree, id_delete_fn, NULL);
2310       ED_undo_push(C, "Delete Library");
2311       break;
2312     }
2313     case OL_LIB_RELOCATE: {
2314       outliner_do_libdata_operation(
2315           C, op->reports, scene, space_outliner, &space_outliner->tree, lib_relocate_fn, NULL);
2316       /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
2317        * trick does not work here). */
2318       break;
2319     }
2320     case OL_LIB_RELOAD: {
2321       outliner_do_libdata_operation(
2322           C, op->reports, scene, space_outliner, &space_outliner->tree, lib_reload_fn, NULL);
2323       /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
2324        * trick does not work here). */
2325       break;
2326     }
2327     default:
2328       /* invalid - unhandled */
2329       break;
2330   }
2331
2332   /* wrong notifier still... */
2333   WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
2334
2335   /* XXX: this is just so that outliner is always up to date */
2336   WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, NULL);
2337
2338   return OPERATOR_FINISHED;
2339 }
2340
2341 void OUTLINER_OT_lib_operation(wmOperatorType *ot)
2342 {
2343   /* identifiers */
2344   ot->name = "Outliner Library Operation";
2345   ot->idname = "OUTLINER_OT_lib_operation";
2346
2347   /* callbacks */
2348   ot->invoke = WM_menu_invoke;
2349   ot->exec = outliner_lib_operation_exec;
2350   ot->poll = ED_operator_outliner_active;
2351
2352   ot->prop = RNA_def_enum(
2353       ot->srna, "type", outliner_lib_op_type_items, 0, "Library Operation", "");
2354 }
2355
2356 /** \} */
2357
2358 /* -------------------------------------------------------------------- */
2359 /** \name Outliner Set Active Action Operator
2360  * \{ */
2361
2362 static void outliner_do_id_set_operation(
2363     SpaceOutliner *space_outliner,
2364     int type,
2365     ListBase *lb,
2366     ID *newid,
2367     void (*operation_fn)(TreeElement *, TreeStoreElem *, TreeStoreElem *, ID *))
2368 {
2369   LISTBASE_FOREACH (TreeElement *, te, lb) {
2370     TreeStoreElem *tselem = TREESTORE(te);
2371     if (tselem->flag & TSE_SELECTED) {
2372       if (tselem->type == type) {
2373         TreeStoreElem *tsep = te->parent ? TREESTORE(te->parent) : NULL;
2374         operation_fn(te, tselem, tsep, newid);
2375       }
2376     }
2377     if (TSELEM_OPEN(tselem, space_outliner)) {
2378       outliner_do_id_set_operation(space_outliner, type, &te->subtree, newid, operation_fn);
2379     }
2380   }
2381 }
2382
2383 static void actionset_id_fn(TreeElement *UNUSED(te),
2384                             TreeStoreElem *tselem,
2385                             TreeStoreElem *tsep,
2386                             ID *actId)
2387 {
2388   bAction *act = (bAction *)actId;
2389
2390   if (tselem->type == TSE_ANIM_DATA) {
2391     /* "animation" entries - action is child of this */
2392     BKE_animdata_set_action(NULL, tselem->id, act);
2393   }
2394   /* TODO: if any other "expander" channels which own actions need to support this menu,
2395    * add: tselem->type = ...
2396    */
2397   else if (tsep && (tsep->type == TSE_ANIM_DATA)) {
2398     /* "animation" entries case again */
2399     BKE_animdata_set_action(NULL, tsep->id, act);
2400   }
2401   /* TODO: other cases not supported yet. */
2402 }
2403
2404 static int outliner_action_set_exec(bContext *C, wmOperator *op)
2405 {
2406   Main *bmain = CTX_data_main(C);
2407   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
2408   int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
2409
2410   bAction *act;
2411
2412   /* check for invalid states */
2413   if (space_outliner == NULL) {
2414     return OPERATOR_CANCELLED;
2415   }
2416
2417   TreeElement *te = get_target_element(space_outliner);
2418   get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
2419
2420   /* get action to use */
2421   act = BLI_findlink(&bmain->actions, RNA_enum_get(op->ptr, "action"));
2422
2423   if (act == NULL) {
2424     BKE_report(op->reports, RPT_ERROR, "No valid action to add");
2425     return OPERATOR_CANCELLED;
2426   }
2427   if (act->idroot == 0) {
2428     /* Hopefully in this case (i.e. library of userless actions),
2429      * the user knows what they're doing. */
2430     BKE_reportf(op->reports,
2431                 RPT_WARNING,
2432                 "Action '%s' does not specify what data-blocks it can be used on "
2433                 "(try setting the 'ID Root Type' setting from the data-blocks editor "
2434                 "for this action to avoid future problems)",
2435                 act->id.name + 2);
2436   }
2437
2438   /* perform action if valid channel */
2439   if (datalevel == TSE_ANIM_DATA) {
2440     outliner_do_id_set_operation(
2441         space_outliner, datalevel, &space_outliner->tree, (ID *)act, actionset_id_fn);
2442   }
2443   else if (idlevel == ID_AC) {
2444     outliner_do_id_set_operation(
2445         space_outliner, idlevel, &space_outliner->tree, (ID *)act, actionset_id_fn);
2446   }
2447   else {
2448     return OPERATOR_CANCELLED;
2449   }
2450
2451   /* set notifier that things have changed */
2452   WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
2453   ED_undo_push(C, "Set action");
2454
2455   /* done */
2456   return OPERATOR_FINISHED;
2457 }
2458
2459 void OUTLINER_OT_action_set(wmOperatorType *ot)
2460 {
2461   PropertyRNA *prop;
2462
2463   /* identifiers */
2464   ot->name = "Outliner Set Action";
2465   ot->idname = "OUTLINER_OT_action_set";
2466   ot->description = "Change the active action used";
2467
2468   /* api callbacks */
2469   ot->invoke = WM_enum_search_invoke;
2470   ot->exec = outliner_action_set_exec;
2471   ot->poll = ED_operator_outliner_active;
2472
2473   /* flags */
2474   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2475
2476   /* props */
2477   /* TODO: this would be nicer as an ID-pointer... */
2478   prop = RNA_def_enum(ot->srna, "action", DummyRNA_NULL_items, 0, "Action", "");
2479   RNA_def_enum_funcs(prop, RNA_action_itemf);
2480   RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
2481   ot->prop = prop;
2482 }
2483
2484 /** \} */
2485
2486 /* -------------------------------------------------------------------- */
2487 /** \name Animation Menu Operator
2488  * \{ */
2489
2490 typedef enum eOutliner_AnimDataOps {
2491   OUTLINER_ANIMOP_INVALID = 0,
2492
2493   OUTLINER_ANIMOP_CLEAR_ADT,
2494
2495   OUTLINER_ANIMOP_SET_ACT,
2496   OUTLINER_ANIMOP_CLEAR_ACT,
2497
2498   OUTLINER_ANIMOP_REFRESH_DRV,
2499   OUTLINER_ANIMOP_CLEAR_DRV
2500 } eOutliner_AnimDataOps;
2501
2502 static const EnumPropertyItem prop_animdata_op_types[] = {
2503     {OUTLINER_ANIMOP_CLEAR_ADT,
2504      "CLEAR_ANIMDATA",
2505      0,
2506      "Clear Animation Data",
2507      "Remove this animation data container"},
2508     {OUTLINER_ANIMOP_SET_ACT, "SET_ACT", 0, "Set Action", ""},
2509     {OUTLINER_ANIMOP_CLEAR_ACT, "CLEAR_ACT", 0, "Unlink Action", ""},
2510     {OUTLINER_ANIMOP_REFRESH_DRV, "REFRESH_DRIVERS", 0, "Refresh Drivers", ""},
2511     {OUTLINER_ANIMOP_CLEAR_DRV, "CLEAR_DRIVERS", 0, "Clear Drivers", ""},
2512     {0, NULL, 0, NULL, NULL},
2513 };
2514
2515 static int outliner_animdata_operation_exec(bContext *C, wmOperator *op)
2516 {
2517   wmWindowManager *wm = CTX_wm_manager(C);
2518   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
2519   int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
2520
2521   /* check for invalid states */
2522   if (space_outliner == NULL) {
2523     return OPERATOR_CANCELLED;
2524   }
2525
2526   TreeElement *te = get_target_element(space_outliner);
2527   get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
2528
2529   if (datalevel != TSE_ANIM_DATA) {
2530     return OPERATOR_CANCELLED;
2531   }
2532
2533   /* perform the core operation */
2534   eOutliner_AnimDataOps event = RNA_enum_get(op->ptr, "type");
2535   switch (event) {
2536     case OUTLINER_ANIMOP_CLEAR_ADT:
2537       /* Remove Animation Data - this may remove the active action, in some cases... */
2538       outliner_do_data_operation(
2539           space_outliner, datalevel, event, &space_outliner->tree, clear_animdata_fn, NULL);
2540
2541       WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
2542       ED_undo_push(C, "Clear Animation Data");
2543       break;
2544
2545     case OUTLINER_ANIMOP_SET_ACT:
2546       /* delegate once again... */
2547       wm->op_undo_depth++;
2548       WM_operator_name_call(C, "OUTLINER_OT_action_set", WM_OP_INVOKE_REGION_WIN, NULL);
2549       wm->op_undo_depth--;
2550       ED_undo_push(C, "Set active action");
2551       break;
2552
2553     case OUTLINER_ANIMOP_CLEAR_ACT:
2554       /* clear active action - using standard rules */
2555       outliner_do_data_operation(
2556           space_outliner, datalevel, event, &space_outliner->tree, unlinkact_animdata_fn, NULL);
2557
2558       WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
2559       ED_undo_push(C, "Unlink action");
2560       break;
2561
2562     case OUTLINER_ANIMOP_REFRESH_DRV:
2563       outliner_do_data_operation(space_outliner,
2564                                  datalevel,
2565                                  event,
2566                                  &space_outliner->tree,
2567                                  refreshdrivers_animdata_fn,
2568                                  NULL);
2569
2570       WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, NULL);
2571       /* ED_undo_push(C, "Refresh Drivers"); No undo needed - shouldn't have any impact? */
2572       break;
2573
2574     case OUTLINER_ANIMOP_CLEAR_DRV:
2575       outliner_do_data_operation(
2576           space_outliner, datalevel, event, &space_outliner->tree, cleardrivers_animdata_fn, NULL);
2577
2578       WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, NULL);
2579       ED_undo_push(C, "Clear Drivers");
2580       break;
2581
2582     default: /* Invalid. */
2583       break;
2584   }
2585
2586   /* update dependencies */
2587   DEG_relations_tag_update(CTX_data_main(C));
2588
2589   return OPERATOR_FINISHED;
2590 }
2591
2592 void OUTLINER_OT_animdata_operation(wmOperatorType *ot)
2593 {
2594   /* identifiers */
2595   ot->name = "Outliner Animation Data Operation";
2596   ot->idname = "OUTLINER_OT_animdata_operation";
2597
2598   /* callbacks */
2599   ot->invoke = WM_menu_invoke;
2600   ot->exec = outliner_animdata_operation_exec;
2601   ot->poll = ED_operator_outliner_active;
2602
2603   ot->flag = 0;
2604
2605   ot->prop = RNA_def_enum(ot->srna, "type", prop_animdata_op_types, 0, "Animation Operation", "");
2606 }
2607
2608 /** \} */
2609
2610 /* -------------------------------------------------------------------- */
2611 /** \name Constraint Menu Operator
2612  * \{ */
2613
2614 static const EnumPropertyItem prop_constraint_op_types[] = {
2615     {OL_CONSTRAINTOP_ENABLE, "ENABLE", ICON_HIDE_OFF, "Enable", ""},
2616     {OL_CONSTRAINTOP_DISABLE, "DISABLE", ICON_HIDE_ON, "Disable", ""},
2617     {OL_CONSTRAINTOP_DELETE, "DELETE", ICON_X, "Delete", ""},
2618     {0, NULL, 0, NULL, NULL},
2619 };
2620
2621 static int outliner_constraint_operation_exec(bContext *C, wmOperator *op)
2622 {
2623   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
2624   eOutliner_PropConstraintOps event = RNA_enum_get(op->ptr, "type");
2625
2626   outliner_do_data_operation(
2627       space_outliner, TSE_CONSTRAINT, event, &space_outliner->tree, constraint_fn, C);
2628
2629   if (event == OL_CONSTRAINTOP_DELETE) {
2630     outliner_cleanup_tree(space_outliner);
2631   }
2632
2633   ED_undo_push(C, "Constraint operation");
2634
2635   return OPERATOR_FINISHED;
2636 }
2637
2638 void OUTLINER_OT_constraint_operation(wmOperatorType *ot)
2639 {
2640   /* identifiers */
2641   ot->name = "Outliner Constraint Operation";
2642   ot->idname = "OUTLINER_OT_constraint_operation";
2643
2644   /* callbacks */
2645   ot->invoke = WM_menu_invoke;
2646   ot->exec = outliner_constraint_operation_exec;
2647   ot->poll = ED_operator_outliner_active;
2648
2649   ot->flag = 0;
2650
2651   ot->prop = RNA_def_enum(
2652       ot->srna, "type", prop_constraint_op_types, 0, "Constraint Operation", "");
2653 }
2654
2655 /** \} */
2656
2657 /* -------------------------------------------------------------------- */
2658 /** \name Modifier Menu Operator
2659  * \{ */
2660
2661 static const EnumPropertyItem prop_modifier_op_types[] = {
2662     {OL_MODIFIER_OP_TOGVIS, "TOGVIS", ICON_RESTRICT_VIEW_OFF, "Toggle Viewport Use", ""},
2663     {OL_MODIFIER_OP_TOGREN, "TOGREN", ICON_RESTRICT_RENDER_OFF, "Toggle Render Use", ""},
2664     {OL_MODIFIER_OP_DELETE, "DELETE", ICON_X, "Delete", ""},
2665     {0, NULL, 0, NULL, NULL},
2666 };
2667
2668 static int outliner_modifier_operation_exec(bContext *C, wmOperator *op)
2669 {
2670   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
2671   eOutliner_PropModifierOps event = RNA_enum_get(op->ptr, "type");
2672
2673   outliner_do_data_operation(
2674       space_outliner, TSE_MODIFIER, event, &space_outliner->tree, modifier_fn, C);
2675
2676   if (event == OL_MODIFIER_OP_DELETE) {
2677     outliner_cleanup_tree(space_outliner);
2678   }
2679
2680   ED_undo_push(C, "Modifier operation");
2681
2682   return OPERATOR_FINISHED;
2683 }
2684
2685 void OUTLINER_OT_modifier_operation(wmOperatorType *ot)
2686 {
2687   /* identifiers */
2688   ot->name = "Outliner Modifier Operation";
2689   ot->idname = "OUTLINER_OT_modifier_operation";
2690
2691   /* callbacks */
2692   ot->invoke = WM_menu_invoke;
2693   ot->exec = outliner_modifier_operation_exec;
2694   ot->poll = ED_operator_outliner_active;
2695
2696   ot->flag = 0;
2697
2698   ot->prop = RNA_def_enum(ot->srna, "type", prop_modifier_op_types, 0, "Modifier Operation", "");
2699 }
2700
2701 /** \} */
2702
2703 /* -------------------------------------------------------------------- */
2704 /** \name Data Menu Operator
2705  * \{ */
2706
2707 /* XXX: select linked is for RNA structs only. */
2708 static const EnumPropertyItem prop_data_op_types[] = {
2709     {OL_DOP_SELECT, "SELECT", 0, "Select", ""},
2710     {OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""},
2711     {OL_DOP_HIDE, "HIDE", 0, "Hide", ""},
2712     {OL_DOP_UNHIDE, "UNHIDE", 0, "Unhide", ""},
2713     {OL_DOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""},
2714     {0, NULL, 0, NULL, NULL},
2715 };
2716
2717 static int outliner_data_operation_exec(bContext *C, wmOperator *op)
2718 {
2719   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
2720   int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
2721
2722   /* check for invalid states */
2723   if (space_outliner == NULL) {
2724     return OPERATOR_CANCELLED;
2725   }
2726
2727   TreeElement *te = get_target_element(space_outliner);
2728   get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
2729
2730   eOutliner_PropDataOps event = RNA_enum_get(op->ptr, "type");
2731   switch (datalevel) {
2732     case TSE_POSE_CHANNEL: {
2733       outliner_do_data_operation(
2734           space_outliner, datalevel, event, &space_outliner->tree, pchan_fn, NULL);
2735       WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
2736       ED_undo_push(C, "PoseChannel operation");
2737
2738       break;
2739     }
2740     case TSE_BONE: {
2741       outliner_do_data_operation(
2742           space_outliner, datalevel, event, &space_outliner->tree, bone_fn, NULL);
2743       WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
2744       ED_undo_push(C, "Bone operation");
2745
2746       break;
2747     }
2748     case TSE_EBONE: {
2749       outliner_do_data_operation(
2750           space_outliner, datalevel, event, &space_outliner->tree, ebone_fn, NULL);
2751       WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
2752       ED_undo_push(C, "EditBone operation");
2753
2754       break;
2755     }
2756     case TSE_SEQUENCE: {
2757       Scene *scene = CTX_data_scene(C);
2758       outliner_do_data_operation(
2759           space_outliner, datalevel, event, &space_outliner->tree, sequence_fn, scene);
2760
2761       break;
2762     }
2763     case TSE_GP_LAYER: {
2764       outliner_do_data_operation(
2765           space_outliner, datalevel, event, &space_outliner->tree, gpencil_layer_fn, NULL);
2766       WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL);
2767       ED_undo_push(C, "Grease Pencil Layer operation");
2768
2769       break;
2770     }
2771     case TSE_RNA_STRUCT:
2772       if (event == OL_DOP_SELECT_LINKED) {
2773         outliner_do_data_operation(
2774             space_outliner, datalevel, event, &space_outliner->tree, data_select_linked_fn, C);
2775       }
2776
2777       break;
2778
2779     default:
2780       BKE_report(op->reports, RPT_WARNING, "Not yet implemented");
2781       break;
2782   }
2783
2784   return OPERATOR_FINISHED;
2785 }
2786
2787 void OUTLINER_OT_data_operation(wmOperatorType *ot)
2788 {
2789   /* identifiers */
2790   ot->name = "Outliner Data Operation";
2791   ot->idname = "OUTLINER_OT_data_operation";
2792
2793   /* callbacks */
2794   ot->invoke = WM_menu_invoke;
2795   ot->exec = outliner_data_operation_exec;
2796   ot->poll = ED_operator_outliner_active;
2797
2798   ot->flag = 0;
2799
2800   ot->prop = RNA_def_enum(ot->srna, "type", prop_data_op_types, 0, "Data Operation", "");
2801 }
2802
2803 /** \} */
2804
2805 /* -------------------------------------------------------------------- */
2806 /** \name Context Menu Operator
2807  * \{ */
2808
2809 static int outliner_operator_menu(bContext *C, const char *opname)
2810 {
2811   wmOperatorType *ot = WM_operatortype_find(opname, false);
2812   uiPopupMenu *pup = UI_popup_menu_begin(C, WM_operatortype_name(ot, NULL), ICON_NONE);
2813   uiLayout *layout = UI_popup_menu_layout(pup);
2814
2815   /* set this so the default execution context is the same as submenus */
2816   uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_REGION_WIN);
2817   uiItemsEnumO(layout, ot->idname, RNA_property_identifier(ot->prop));
2818
2819   uiItemS(layout);
2820
2821   uiItemMContents(layout, "OUTLINER_MT_context_menu");
2822
2823   UI_popup_menu_end(C, pup);
2824
2825   return OPERATOR_INTERFACE;
2826 }
2827
2828 static int do_outliner_operation_event(bContext *C,
2829                                        ReportList *reports,
2830                                        ARegion *region,
2831                                        SpaceOutliner *space_outliner,
2832                                        TreeElement *te)
2833 {
2834   int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
2835   TreeStoreElem *tselem = TREESTORE(te);
2836
2837   int select_flag = OL_ITEM_ACTIVATE | OL_ITEM_SELECT;
2838   if (tselem->flag & TSE_SELECTED) {
2839     select_flag |= OL_ITEM_EXTEND;
2840   }
2841
2842   outliner_item_select(C, space_outliner, te, select_flag);
2843
2844   /* Only redraw, don't rebuild here because TreeElement pointers will
2845    * become invalid and operations will crash. */
2846   ED_region_tag_redraw_no_rebuild(region);
2847   ED_outliner_select_sync_from_outliner(C, space_outliner);
2848
2849   get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
2850
2851   if (scenelevel) {
2852     if (objectlevel || datalevel || idlevel) {
2853       BKE_report(reports, RPT_WARNING, "Mixed selection");
2854       return OPERATOR_CANCELLED;
2855     }
2856     return outliner_operator_menu(C, "OUTLINER_OT_scene_operation");
2857   }
2858   if (objectlevel) {
2859     WM_menu_name_call(C, "OUTLINER_MT_object", WM_OP_INVOKE_REGION_WIN);
2860     return OPERATOR_FINISHED;
2861   }
2862   if (idlevel) {
2863     if (idlevel == -1 || datalevel) {
2864       BKE_report(reports, RPT_WARNING, "Mixed selection");
2865       return OPERATOR_CANCELLED;
2866     }
2867
2868     switch (idlevel) {
2869       case ID_GR:
2870         WM_menu_name_call(C, "OUTLINER_MT_collection", WM_OP_INVOKE_REGION_WIN);
2871         return OPERATOR_FINISHED;
2872         break;
2873       case ID_LI:
2874         return outliner_operator_menu(C, "OUTLINER_OT_lib_operation");
2875         break;
2876       default:
2877         return outliner_operator_menu(C, "OUTLINER_OT_id_operation");
2878         break;
2879     }
2880   }
2881   else if (datalevel) {
2882     if (datalevel == -1) {
2883       BKE_report(reports, RPT_WARNING, "Mixed selection");
2884       return OPERATOR_CANCELLED;
2885     }
2886     if (datalevel == TSE_ANIM_DATA) {
2887       return outliner_operator_menu(C, "OUTLINER_OT_animdata_operation");
2888     }
2889     if (datalevel == TSE_DRIVER_BASE) {
2890       /* do nothing... no special ops needed yet */
2891       return OPERATOR_CANCELLED;
2892     }
2893     if (datalevel == TSE_LAYER_COLLECTION) {
2894       WM_menu_name_call(C, "OUTLINER_MT_collection", WM_OP_INVOKE_REGION_WIN);
2895       return OPERATOR_FINISHED;
2896     }
2897     if (ELEM(datalevel, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) {
2898       WM_menu_name_call(C, "OUTLINER_MT_collection_new", WM_OP_INVOKE_REGION_WIN);
2899       return OPERATOR_FINISHED;
2900     }
2901     if (datalevel == TSE_ID_BASE) {
2902       /* do nothing... there are no ops needed here yet */
2903       return OPERATOR_CANCELLED;
2904     }
2905     if (datalevel == TSE_CONSTRAINT) {
2906       return outliner_operator_menu(C, "OUTLINER_OT_constraint_operation");
2907     }
2908     if (datalevel == TSE_MODIFIER) {
2909       return outliner_operator_menu(C, "OUTLINER_OT_modifier_operation");
2910     }
2911     return outliner_operator_menu(C, "OUTLINER_OT_data_operation");
2912   }
2913
2914   return OPERATOR_CANCELLED;
2915 }
2916
2917 static int outliner_operation(bContext *C, wmOperator *op, const wmEvent *event)
2918 {
2919   ARegion *region = CTX_wm_region(C);
2920   SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
2921   uiBut *but = UI_context_active_but_get(C);
2922   float view_mval[2];
2923
2924   if (but) {
2925     UI_but_tooltip_timer_remove(C, but);
2926   }
2927
2928   UI_view2d_region_to_view(
2929       &region->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]);
2930
2931   TreeElement *hovered_te = outliner_find_item_at_y(
2932       space_outliner, &space_outliner->tree, view_mval[1]);
2933   if (!hovered_te) {
2934     /* Let this fall through to 'OUTLINER_MT_context_menu'. */
2935     return OPERATOR_PASS_THROUGH;
2936   }
2937
2938   return do_outliner_operation_event(C, op->reports, region, space_outliner, hovered_te);
2939 }
2940
2941 /* Menu only! Calls other operators */
2942 void OUTLINER_OT_operation(wmOperatorType *ot)
2943 {
2944   ot->name = "Context Menu";
2945   ot->idname = "OUTLINER_OT_operation";
2946   ot->description = "Context menu for item operations";
2947
2948   ot->invoke = outliner_operation;
2949
2950   ot->poll = ED_operator_outliner_active;
2951 }
2952
2953 /** \} */