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