Outliner: ContextMenus for constraints/modifiers
[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 static EnumPropertyItem prop_group_op_types[] = {
869         {0, "UNLINK",   0, "Unlink Group", ""},
870         {1, "LOCAL",    0, "Make Local Group", ""},
871         {2, "LINK",     0, "Link Group Objects to Scene", ""},
872         {3, "INSTANCE", 0, "Instance Groups in Scene", ""},
873         {4, "TOGVIS",   0, "Toggle Visible Group", ""},
874         {5, "TOGSEL",   0, "Toggle Selectable", ""},
875         {6, "TOGREN",   0, "Toggle Renderable", ""},
876         {7, "RENAME",   0, "Rename", ""},
877         {0, NULL, 0, NULL, NULL}
878 };
879
880 static int outliner_group_operation_exec(bContext *C, wmOperator *op)
881 {
882         Scene *scene = CTX_data_scene(C);
883         SpaceOops *soops = CTX_wm_space_outliner(C);
884         int event;
885         
886         /* check for invalid states */
887         if (soops == NULL)
888                 return OPERATOR_CANCELLED;
889         
890         event = RNA_enum_get(op->ptr, "type");
891
892         switch (event) {
893                 case 0: outliner_do_libdata_operation(C, scene, soops, &soops->tree, unlink_group_cb); break;
894                 case 1: outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_local_cb); break;
895                 case 2: outliner_do_libdata_operation(C, scene, soops, &soops->tree, group_linkobs2scene_cb); break;
896                 case 3: outliner_do_libdata_operation(C, scene, soops, &soops->tree, group_instance_cb); break;
897                 case 4: outliner_do_libdata_operation(C, scene, soops, &soops->tree, group_toggle_visibility_cb); break;
898                 case 5: outliner_do_libdata_operation(C, scene, soops, &soops->tree, group_toggle_selectability_cb); break;
899                 case 6: outliner_do_libdata_operation(C, scene, soops, &soops->tree, group_toggle_renderability_cb); break;
900                 case 7: outliner_do_libdata_operation(C, scene, soops, &soops->tree, item_rename_cb); break;
901                 default:
902                         BLI_assert(0);
903                         return OPERATOR_CANCELLED;
904         }
905         
906
907         if (event == 3) { /* instance */
908                 /* works without this except if you try render right after, see: 22027 */
909                 DAG_relations_tag_update(CTX_data_main(C));
910         }
911         
912         ED_undo_push(C, prop_group_op_types[event].name);
913         WM_event_add_notifier(C, NC_GROUP, NULL);
914         
915         return OPERATOR_FINISHED;
916 }
917
918
919 void OUTLINER_OT_group_operation(wmOperatorType *ot)
920 {
921         /* identifiers */
922         ot->name = "Outliner Group Operation";
923         ot->idname = "OUTLINER_OT_group_operation";
924         ot->description = "";
925         
926         /* callbacks */
927         ot->invoke = WM_menu_invoke;
928         ot->exec = outliner_group_operation_exec;
929         ot->poll = ED_operator_outliner_active;
930         
931         ot->flag = 0;
932         
933         ot->prop = RNA_def_enum(ot->srna, "type", prop_group_op_types, 0, "Group Operation", "");
934 }
935
936 /* **************************************** */
937
938 typedef enum eOutlinerIdOpTypes {
939         OUTLINER_IDOP_INVALID = 0,
940         
941         OUTLINER_IDOP_UNLINK,
942         OUTLINER_IDOP_LOCAL,
943         OUTLINER_IDOP_SINGLE,
944         
945         OUTLINER_IDOP_FAKE_ADD,
946         OUTLINER_IDOP_FAKE_CLEAR,
947         OUTLINER_IDOP_RENAME,
948
949         OUTLINER_IDOP_SELECT_LINKED
950 } eOutlinerIdOpTypes;
951
952 // TODO: implement support for changing the ID-block used
953 static EnumPropertyItem prop_id_op_types[] = {
954         {OUTLINER_IDOP_UNLINK, "UNLINK", 0, "Unlink", ""},
955         {OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""},
956         {OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""},
957         {OUTLINER_IDOP_FAKE_ADD, "ADD_FAKE", 0, "Add Fake User",
958          "Ensure datablock gets saved even if it isn't in use (e.g. for motion and material libraries)"},
959         {OUTLINER_IDOP_FAKE_CLEAR, "CLEAR_FAKE", 0, "Clear Fake User", ""},
960         {OUTLINER_IDOP_RENAME, "RENAME", 0, "Rename", ""},
961         {OUTLINER_IDOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""},
962         {0, NULL, 0, NULL, NULL}
963 };
964
965 static int outliner_id_operation_exec(bContext *C, wmOperator *op)
966 {
967         Scene *scene = CTX_data_scene(C);
968         SpaceOops *soops = CTX_wm_space_outliner(C);
969         int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
970         eOutlinerIdOpTypes event;
971         
972         /* check for invalid states */
973         if (soops == NULL)
974                 return OPERATOR_CANCELLED;
975         
976         set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
977         
978         event = RNA_enum_get(op->ptr, "type");
979         
980         switch (event) {
981                 case OUTLINER_IDOP_UNLINK:
982                 {
983                         /* unlink datablock from its parent */
984                         switch (idlevel) {
985                                 case ID_AC:
986                                         outliner_do_libdata_operation(C, scene, soops, &soops->tree, unlink_action_cb);
987                                         
988                                         WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
989                                         ED_undo_push(C, "Unlink action");
990                                         break;
991                                 case ID_MA:
992                                         outliner_do_libdata_operation(C, scene, soops, &soops->tree, unlink_material_cb);
993                                         
994                                         WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, NULL);
995                                         ED_undo_push(C, "Unlink material");
996                                         break;
997                                 case ID_TE:
998                                         outliner_do_libdata_operation(C, scene, soops, &soops->tree, unlink_texture_cb);
999                                         
1000                                         WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, NULL);
1001                                         ED_undo_push(C, "Unlink texture");
1002                                         break;
1003                                 case ID_WO:
1004                                         outliner_do_libdata_operation(C, scene, soops, &soops->tree, unlink_world_cb);
1005                                         
1006                                         WM_event_add_notifier(C, NC_SCENE | ND_WORLD, NULL);
1007                                         ED_undo_push(C, "Unlink world");
1008                                         break;
1009                                 default:
1010                                         BKE_report(op->reports, RPT_WARNING, "Not yet implemented");
1011                                         break;
1012                         }
1013                         break;
1014                 }
1015                 case OUTLINER_IDOP_LOCAL:
1016                 {
1017                         /* make local */
1018                         outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_local_cb);
1019                         ED_undo_push(C, "Localized Data");
1020                         break;
1021                 }
1022                 case OUTLINER_IDOP_SINGLE:
1023                 {
1024                         /* make single user */
1025                         switch (idlevel) {
1026                                 case ID_AC:
1027                                         outliner_do_libdata_operation(C, scene, soops, &soops->tree, singleuser_action_cb);
1028                                         
1029                                         WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
1030                                         ED_undo_push(C, "Single-User Action");
1031                                         break;
1032                                         
1033                                 case ID_WO:
1034                                         outliner_do_libdata_operation(C, scene, soops, &soops->tree, singleuser_world_cb);
1035                                         
1036                                         WM_event_add_notifier(C, NC_SCENE | ND_WORLD, NULL);
1037                                         ED_undo_push(C, "Single-User World");
1038                                         break;
1039                                         
1040                                 default:
1041                                         BKE_report(op->reports, RPT_WARNING, "Not yet implemented");
1042                                         break;
1043                         }
1044                         break;
1045                 }
1046                 case OUTLINER_IDOP_FAKE_ADD:
1047                 {
1048                         /* set fake user */
1049                         outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_fake_user_set_cb);
1050                         
1051                         WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
1052                         ED_undo_push(C, "Add Fake User");
1053                         break;
1054                 }
1055                 case OUTLINER_IDOP_FAKE_CLEAR:
1056                 {
1057                         /* clear fake user */
1058                         outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_fake_user_clear_cb);
1059                         
1060                         WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
1061                         ED_undo_push(C, "Clear Fake User");
1062                         break;
1063                 }
1064                 case OUTLINER_IDOP_RENAME:
1065                 {
1066                         /* rename */
1067                         outliner_do_libdata_operation(C, scene, soops, &soops->tree, item_rename_cb);
1068                         
1069                         WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
1070                         ED_undo_push(C, "Rename");
1071                         break;
1072                 }
1073                 case OUTLINER_IDOP_SELECT_LINKED:
1074                         outliner_do_libdata_operation(C, scene, soops, &soops->tree, id_select_linked_cb);
1075                         ED_undo_push(C, "Select");
1076                         break;
1077                         
1078                 default:
1079                         // invalid - unhandled
1080                         break;
1081         }
1082         
1083         /* wrong notifier still... */
1084         WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
1085         
1086         // XXX: this is just so that outliner is always up to date 
1087         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, NULL);
1088         
1089         return OPERATOR_FINISHED;
1090 }
1091
1092
1093 void OUTLINER_OT_id_operation(wmOperatorType *ot)
1094 {
1095         /* identifiers */
1096         ot->name = "Outliner ID data Operation";
1097         ot->idname = "OUTLINER_OT_id_operation";
1098         ot->description = "";
1099         
1100         /* callbacks */
1101         ot->invoke = WM_menu_invoke;
1102         ot->exec = outliner_id_operation_exec;
1103         ot->poll = ED_operator_outliner_active;
1104         
1105         ot->flag = 0;
1106         
1107         ot->prop = RNA_def_enum(ot->srna, "type", prop_id_op_types, 0, "ID data Operation", "");
1108 }
1109
1110 /* **************************************** */
1111
1112 static void outliner_do_id_set_operation(SpaceOops *soops, int type, ListBase *lb, ID *newid,
1113                                          void (*operation_cb)(TreeElement *, TreeStoreElem *, TreeStoreElem *, ID *))
1114 {
1115         TreeElement *te;
1116         TreeStoreElem *tselem;
1117         
1118         for (te = lb->first; te; te = te->next) {
1119                 tselem = TREESTORE(te);
1120                 if (tselem->flag & TSE_SELECTED) {
1121                         if (tselem->type == type) {
1122                                 TreeStoreElem *tsep = te->parent ? TREESTORE(te->parent) : NULL;
1123                                 operation_cb(te, tselem, tsep, newid);
1124                         }
1125                 }
1126                 if (TSELEM_OPEN(tselem, soops)) {
1127                         outliner_do_id_set_operation(soops, type, &te->subtree, newid, operation_cb);
1128                 }
1129         }
1130 }
1131
1132 /* ------------------------------------------ */
1133
1134 static void actionset_id_cb(TreeElement *UNUSED(te), TreeStoreElem *tselem, TreeStoreElem *tsep, ID *actId)
1135 {
1136         bAction *act = (bAction *)actId;
1137         
1138         if (tselem->type == TSE_ANIM_DATA) {
1139                 /* "animation" entries - action is child of this */
1140                 BKE_animdata_set_action(NULL, tselem->id, act);
1141         }
1142         /* TODO: if any other "expander" channels which own actions need to support this menu, 
1143          * add: tselem->type = ...
1144          */
1145         else if (tsep && (tsep->type == TSE_ANIM_DATA)) {
1146                 /* "animation" entries case again */
1147                 BKE_animdata_set_action(NULL, tsep->id, act);
1148         }
1149         // TODO: other cases not supported yet
1150 }
1151
1152 static int outliner_action_set_exec(bContext *C, wmOperator *op)
1153 {
1154         SpaceOops *soops = CTX_wm_space_outliner(C);
1155         int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1156         
1157         bAction *act;
1158         
1159         /* check for invalid states */
1160         if (soops == NULL)
1161                 return OPERATOR_CANCELLED;
1162         set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1163         
1164         /* get action to use */
1165         act = BLI_findlink(&CTX_data_main(C)->action, RNA_enum_get(op->ptr, "action"));
1166         
1167         if (act == NULL) {
1168                 BKE_report(op->reports, RPT_ERROR, "No valid action to add");
1169                 return OPERATOR_CANCELLED;
1170         }
1171         else if (act->idroot == 0) {
1172                 /* hopefully in this case (i.e. library of userless actions), the user knows what they're doing... */
1173                 BKE_reportf(op->reports, RPT_WARNING,
1174                             "Action '%s' does not specify what datablocks it can be used on "
1175                             "(try setting the 'ID Root Type' setting from the Datablocks Editor "
1176                             "for this action to avoid future problems)",
1177                             act->id.name + 2);
1178         }
1179         
1180         /* perform action if valid channel */
1181         if (datalevel == TSE_ANIM_DATA)
1182                 outliner_do_id_set_operation(soops, datalevel, &soops->tree, (ID *)act, actionset_id_cb);
1183         else if (idlevel == ID_AC)
1184                 outliner_do_id_set_operation(soops, idlevel, &soops->tree, (ID *)act, actionset_id_cb);
1185         else
1186                 return OPERATOR_CANCELLED;
1187                 
1188         /* set notifier that things have changed */
1189         WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
1190         ED_undo_push(C, "Set action");
1191         
1192         /* done */
1193         return OPERATOR_FINISHED;
1194 }
1195
1196 void OUTLINER_OT_action_set(wmOperatorType *ot)
1197 {
1198         PropertyRNA *prop;
1199
1200         /* identifiers */
1201         ot->name = "Outliner Set Action";
1202         ot->idname = "OUTLINER_OT_action_set";
1203         ot->description = "Change the active action used";
1204         
1205         /* api callbacks */
1206         ot->invoke = WM_enum_search_invoke;
1207         ot->exec = outliner_action_set_exec;
1208         ot->poll = ED_operator_outliner_active;
1209         
1210         /* flags */
1211         ot->flag = 0;
1212         
1213         /* props */
1214         // TODO: this would be nicer as an ID-pointer...
1215         prop = RNA_def_enum(ot->srna, "action", DummyRNA_NULL_items, 0, "Action", "");
1216         RNA_def_enum_funcs(prop, RNA_action_itemf);
1217         RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
1218         ot->prop = prop;
1219 }
1220
1221 /* **************************************** */
1222
1223 typedef enum eOutliner_AnimDataOps {
1224         OUTLINER_ANIMOP_INVALID = 0,
1225         
1226         OUTLINER_ANIMOP_CLEAR_ADT,
1227         
1228         OUTLINER_ANIMOP_SET_ACT,
1229         OUTLINER_ANIMOP_CLEAR_ACT,
1230         
1231         OUTLINER_ANIMOP_REFRESH_DRV,
1232         OUTLINER_ANIMOP_CLEAR_DRV
1233         
1234         //OUTLINER_ANIMOP_COPY_DRIVERS,
1235         //OUTLINER_ANIMOP_PASTE_DRIVERS
1236 } eOutliner_AnimDataOps;
1237
1238 static EnumPropertyItem prop_animdata_op_types[] = {
1239         {OUTLINER_ANIMOP_CLEAR_ADT, "CLEAR_ANIMDATA", 0, "Clear Animation Data", "Remove this animation data container"},
1240         {OUTLINER_ANIMOP_SET_ACT, "SET_ACT", 0, "Set Action", ""},
1241         {OUTLINER_ANIMOP_CLEAR_ACT, "CLEAR_ACT", 0, "Unlink Action", ""},
1242         {OUTLINER_ANIMOP_REFRESH_DRV, "REFRESH_DRIVERS", 0, "Refresh Drivers", ""},
1243         //{OUTLINER_ANIMOP_COPY_DRIVERS, "COPY_DRIVERS", 0, "Copy Drivers", ""},
1244         //{OUTLINER_ANIMOP_PASTE_DRIVERS, "PASTE_DRIVERS", 0, "Paste Drivers", ""},
1245         {OUTLINER_ANIMOP_CLEAR_DRV, "CLEAR_DRIVERS", 0, "Clear Drivers", ""},
1246         {0, NULL, 0, NULL, NULL}
1247 };
1248
1249 static int outliner_animdata_operation_exec(bContext *C, wmOperator *op)
1250 {
1251         SpaceOops *soops = CTX_wm_space_outliner(C);
1252         int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1253         eOutliner_AnimDataOps event;
1254         short updateDeps = 0;
1255         
1256         /* check for invalid states */
1257         if (soops == NULL)
1258                 return OPERATOR_CANCELLED;
1259         
1260         event = RNA_enum_get(op->ptr, "type");
1261         set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1262         
1263         if (datalevel != TSE_ANIM_DATA)
1264                 return OPERATOR_CANCELLED;
1265         
1266         /* perform the core operation */
1267         switch (event) {
1268                 case OUTLINER_ANIMOP_CLEAR_ADT:
1269                         /* Remove Animation Data - this may remove the active action, in some cases... */
1270                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, clear_animdata_cb, NULL);
1271                         
1272                         WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
1273                         ED_undo_push(C, "Clear Animation Data");
1274                         break;
1275                 
1276                 case OUTLINER_ANIMOP_SET_ACT:
1277                         /* delegate once again... */
1278                         WM_operator_name_call(C, "OUTLINER_OT_action_set", WM_OP_INVOKE_REGION_WIN, NULL);
1279                         break;
1280                 
1281                 case OUTLINER_ANIMOP_CLEAR_ACT:
1282                         /* clear active action - using standard rules */
1283                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, unlinkact_animdata_cb, NULL);
1284                         
1285                         WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
1286                         ED_undo_push(C, "Unlink action");
1287                         break;
1288                         
1289                 case OUTLINER_ANIMOP_REFRESH_DRV:
1290                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, refreshdrivers_animdata_cb, NULL);
1291                         
1292                         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, NULL);
1293                         //ED_undo_push(C, "Refresh Drivers"); /* no undo needed - shouldn't have any impact? */
1294                         updateDeps = 1;
1295                         break;
1296                         
1297                 case OUTLINER_ANIMOP_CLEAR_DRV:
1298                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, cleardrivers_animdata_cb, NULL);
1299                         
1300                         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, NULL);
1301                         ED_undo_push(C, "Clear Drivers");
1302                         updateDeps = 1;
1303                         break;
1304                         
1305                 default: // invalid
1306                         break;
1307         }
1308         
1309         /* update dependencies */
1310         if (updateDeps) {
1311                 /* rebuild depsgraph for the new deps */
1312                 DAG_relations_tag_update(CTX_data_main(C));
1313         }
1314         
1315         return OPERATOR_FINISHED;
1316 }
1317
1318
1319 void OUTLINER_OT_animdata_operation(wmOperatorType *ot)
1320 {
1321         /* identifiers */
1322         ot->name = "Outliner Animation Data Operation";
1323         ot->idname = "OUTLINER_OT_animdata_operation";
1324         ot->description = "";
1325         
1326         /* callbacks */
1327         ot->invoke = WM_menu_invoke;
1328         ot->exec = outliner_animdata_operation_exec;
1329         ot->poll = ED_operator_outliner_active;
1330         
1331         ot->flag = 0;
1332         
1333         ot->prop = RNA_def_enum(ot->srna, "type", prop_animdata_op_types, 0, "Animation Operation", "");
1334 }
1335
1336 /* **************************************** */
1337
1338 static EnumPropertyItem prop_constraint_op_types[] = {
1339         {OL_CONSTRAINTOP_ENABLE, "ENABLE", ICON_RESTRICT_VIEW_OFF, "Enable", ""},
1340         {OL_CONSTRAINTOP_DISABLE, "DISABLE", ICON_RESTRICT_VIEW_ON, "Disable", ""},
1341         {OL_CONSTRAINTOP_DELETE, "DELETE", ICON_X, "Delete", ""},
1342         {0, NULL, 0, NULL, NULL}
1343 };
1344
1345 static int outliner_constraint_operation_exec(bContext *C, wmOperator *op)
1346 {
1347         SpaceOops *soops = CTX_wm_space_outliner(C);
1348         int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1349         eOutliner_PropConstraintOps event;
1350
1351         event = RNA_enum_get(op->ptr, "type");
1352         set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1353
1354         outliner_do_data_operation(soops, datalevel, event, &soops->tree, constraint_cb, C);
1355
1356         if (event == OL_CONSTRAINTOP_DELETE) {
1357                 outliner_cleanup_tree(soops);
1358         }
1359
1360         ED_undo_push(C, "Constraint operation");
1361
1362         return OPERATOR_FINISHED;
1363 }
1364
1365 void OUTLINER_OT_constraint_operation(wmOperatorType *ot)
1366 {
1367         /* identifiers */
1368         ot->name = "Outliner Constraint Operation";
1369         ot->idname = "OUTLINER_OT_constraint_operation";
1370         ot->description = "Outliner ContextMenu Constraint Operation";
1371
1372         /* callbacks */
1373         ot->invoke = WM_menu_invoke;
1374         ot->exec = outliner_constraint_operation_exec;
1375         ot->poll = ED_operator_outliner_active;
1376
1377         ot->flag = 0;
1378
1379         ot->prop = RNA_def_enum(ot->srna, "type", prop_constraint_op_types, 0, "Constraint Operation", "");
1380 }
1381
1382 /* ******************** */
1383
1384 static EnumPropertyItem prop_modifier_op_types[] = {
1385         {OL_MODIFIER_OP_TOGVIS, "TOGVIS", ICON_RESTRICT_VIEW_OFF, "Toggle viewport use", ""},
1386         {OL_MODIFIER_OP_TOGREN, "TOGREN", ICON_RESTRICT_RENDER_OFF, "Toggle render use", ""},
1387         {OL_MODIFIER_OP_DELETE, "DELETE", ICON_X, "Delete", ""},
1388         {0, NULL, 0, NULL, NULL}
1389 };
1390
1391 static int outliner_modifier_operation_exec(bContext *C, wmOperator *op)
1392 {
1393         SpaceOops *soops = CTX_wm_space_outliner(C);
1394         int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1395         eOutliner_PropModifierOps event;
1396
1397         event = RNA_enum_get(op->ptr, "type");
1398         set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1399
1400         outliner_do_data_operation(soops, datalevel, event, &soops->tree, modifier_cb, C);
1401
1402         if (event == OL_MODIFIER_OP_DELETE) {
1403                 outliner_cleanup_tree(soops);
1404         }
1405
1406         ED_undo_push(C, "Modifier operation");
1407
1408         return OPERATOR_FINISHED;
1409 }
1410
1411 void OUTLINER_OT_modifier_operation(wmOperatorType *ot)
1412 {
1413         /* identifiers */
1414         ot->name = "Outliner Modifier Operation";
1415         ot->idname = "OUTLINER_OT_modifier_operation";
1416         ot->description = "Outliner ContextMenu Modifier Operation";
1417
1418         /* callbacks */
1419         ot->invoke = WM_menu_invoke;
1420         ot->exec = outliner_modifier_operation_exec;
1421         ot->poll = ED_operator_outliner_active;
1422
1423         ot->flag = 0;
1424
1425         ot->prop = RNA_def_enum(ot->srna, "type", prop_modifier_op_types, 0, "Modifier Operation", "");
1426 }
1427
1428 /* ******************** */
1429
1430 static EnumPropertyItem prop_data_op_types[] = {
1431         {OL_DOP_SELECT, "SELECT", 0, "Select", ""},
1432         {OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""},
1433         {OL_DOP_HIDE, "HIDE", 0, "Hide", ""},
1434         {OL_DOP_UNHIDE, "UNHIDE", 0, "Unhide", ""},
1435         {OL_DOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""},
1436         {0, NULL, 0, NULL, NULL}
1437 };
1438
1439 static int outliner_data_operation_exec(bContext *C, wmOperator *op)
1440 {
1441         SpaceOops *soops = CTX_wm_space_outliner(C);
1442         int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1443         eOutliner_PropDataOps event;
1444         
1445         /* check for invalid states */
1446         if (soops == NULL)
1447                 return OPERATOR_CANCELLED;
1448         
1449         event = RNA_enum_get(op->ptr, "type");
1450         set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1451         
1452         switch (datalevel) {
1453                 case TSE_POSE_CHANNEL:
1454                 {
1455                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, pchan_cb, NULL);
1456                         WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
1457                         ED_undo_push(C, "PoseChannel operation");
1458                 }
1459                         break;
1460                 
1461                 case TSE_BONE:
1462                 {
1463                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, bone_cb, NULL);
1464                         WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
1465                         ED_undo_push(C, "Bone operation");
1466                 }
1467                         break;
1468                         
1469                 case TSE_EBONE:
1470                 {
1471                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, ebone_cb, NULL);
1472                         WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
1473                         ED_undo_push(C, "EditBone operation");
1474                 }
1475                         break;
1476                         
1477                 case TSE_SEQUENCE:
1478                 {
1479                         Scene *scene = CTX_data_scene(C);
1480                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, sequence_cb, scene);
1481                 }
1482                         break;
1483                         
1484                 case TSE_RNA_STRUCT:
1485                         if (event == OL_DOP_SELECT_LINKED) {
1486                                 outliner_do_data_operation(soops, datalevel, event, &soops->tree, data_select_linked_cb, C);
1487                         }
1488                         break;
1489                         
1490                 default:
1491                         BKE_report(op->reports, RPT_WARNING, "Not yet implemented");
1492                         break;
1493         }
1494         
1495         return OPERATOR_FINISHED;
1496 }
1497
1498
1499 void OUTLINER_OT_data_operation(wmOperatorType *ot)
1500 {
1501         /* identifiers */
1502         ot->name = "Outliner Data Operation";
1503         ot->idname = "OUTLINER_OT_data_operation";
1504         ot->description = "";
1505         
1506         /* callbacks */
1507         ot->invoke = WM_menu_invoke;
1508         ot->exec = outliner_data_operation_exec;
1509         ot->poll = ED_operator_outliner_active;
1510         
1511         ot->flag = 0;
1512         
1513         ot->prop = RNA_def_enum(ot->srna, "type", prop_data_op_types, 0, "Data Operation", "");
1514 }
1515
1516
1517 /* ******************** */
1518
1519
1520 static int do_outliner_operation_event(bContext *C, Scene *scene, ARegion *ar, SpaceOops *soops,
1521                                        TreeElement *te, const wmEvent *event, const float mval[2])
1522 {
1523         ReportList *reports = CTX_wm_reports(C); // XXX...
1524         
1525         if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
1526                 int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1527                 TreeStoreElem *tselem = TREESTORE(te);
1528                 
1529                 /* select object that's clicked on and popup context menu */
1530                 if (!(tselem->flag & TSE_SELECTED)) {
1531                         
1532                         if (outliner_has_one_flag(soops, &soops->tree, TSE_SELECTED, 1))
1533                                 outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
1534                         
1535                         tselem->flag |= TSE_SELECTED;
1536                         /* redraw, same as outliner_select function */
1537                         soops->storeflag |= SO_TREESTORE_REDRAW;
1538                         ED_region_tag_redraw(ar);
1539                 }
1540                 
1541                 set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1542                 
1543                 if (scenelevel) {
1544                         //if (objectlevel || datalevel || idlevel) error("Mixed selection");
1545                         //else pupmenu("Scene Operations%t|Delete");
1546                 }
1547                 else if (objectlevel) {
1548                         WM_operator_name_call(C, "OUTLINER_OT_object_operation", WM_OP_INVOKE_REGION_WIN, NULL);
1549                 }
1550                 else if (idlevel) {
1551                         if (idlevel == -1 || datalevel) {
1552                                 BKE_report(reports, RPT_WARNING, "Mixed selection");
1553                         }
1554                         else {
1555                                 if (idlevel == ID_GR)
1556                                         WM_operator_name_call(C, "OUTLINER_OT_group_operation", WM_OP_INVOKE_REGION_WIN, NULL);
1557                                 else
1558                                         WM_operator_name_call(C, "OUTLINER_OT_id_operation", WM_OP_INVOKE_REGION_WIN, NULL);
1559                         }
1560                 }
1561                 else if (datalevel) {
1562                         if (datalevel == -1) {
1563                                 BKE_report(reports, RPT_WARNING, "Mixed selection");
1564                         }
1565                         else {
1566                                 if (datalevel == TSE_ANIM_DATA)
1567                                         WM_operator_name_call(C, "OUTLINER_OT_animdata_operation", WM_OP_INVOKE_REGION_WIN, NULL);
1568                                 else if (datalevel == TSE_DRIVER_BASE) {
1569                                         /* do nothing... no special ops needed yet */
1570                                 }
1571                                 else if (ELEM(datalevel, TSE_R_LAYER_BASE, TSE_R_LAYER, TSE_R_PASS)) {
1572                                         /*WM_operator_name_call(C, "OUTLINER_OT_renderdata_operation", WM_OP_INVOKE_REGION_WIN, NULL)*/
1573                                 }
1574                                 else if (datalevel == TSE_CONSTRAINT) {
1575                                         WM_operator_name_call(C, "OUTLINER_OT_constraint_operation", WM_OP_INVOKE_REGION_WIN, NULL);
1576                                 }
1577                                 else if (datalevel == TSE_MODIFIER) {
1578                                         WM_operator_name_call(C, "OUTLINER_OT_modifier_operation", WM_OP_INVOKE_REGION_WIN, NULL);
1579                                 }
1580                                 else {
1581                                         WM_operator_name_call(C, "OUTLINER_OT_data_operation", WM_OP_INVOKE_REGION_WIN, NULL);
1582                                 }
1583                         }
1584                 }
1585                 
1586                 return 1;
1587         }
1588         
1589         for (te = te->subtree.first; te; te = te->next) {
1590                 if (do_outliner_operation_event(C, scene, ar, soops, te, event, mval))
1591                         return 1;
1592         }
1593         return 0;
1594 }
1595
1596
1597 static int outliner_operation(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1598 {
1599         Scene *scene = CTX_data_scene(C);
1600         ARegion *ar = CTX_wm_region(C);
1601         SpaceOops *soops = CTX_wm_space_outliner(C);
1602         TreeElement *te;
1603         float fmval[2];
1604
1605         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
1606         
1607         for (te = soops->tree.first; te; te = te->next) {
1608                 if (do_outliner_operation_event(C, scene, ar, soops, te, event, fmval)) {
1609                         break;
1610                 }
1611         }
1612         
1613         return OPERATOR_FINISHED;
1614 }
1615
1616 /* Menu only! Calls other operators */
1617 void OUTLINER_OT_operation(wmOperatorType *ot)
1618 {
1619         ot->name = "Execute Operation";
1620         ot->idname = "OUTLINER_OT_operation";
1621         ot->description = "Context menu for item operations";
1622         
1623         ot->invoke = outliner_operation;
1624         
1625         ot->poll = ED_operator_outliner_active;
1626 }
1627
1628 /* ****************************************************** */