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