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