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