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