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