Merge with trunk up to r38584.
[blender.git] / source / blender / editors / space_outliner / outliner_edit.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_edit.c
29  *  \ingroup spoutliner
30  */
31
32 #include <math.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <stddef.h>
36
37 #include "MEM_guardedalloc.h"
38
39 #include "DNA_anim_types.h"
40 #include "DNA_armature_types.h"
41 #include "DNA_constraint_types.h"
42 #include "DNA_camera_types.h"
43 #include "DNA_group_types.h"
44 #include "DNA_key_types.h"
45 #include "DNA_lamp_types.h"
46 #include "DNA_material_types.h"
47 #include "DNA_mesh_types.h"
48 #include "DNA_meta_types.h"
49 #include "DNA_particle_types.h"
50 #include "DNA_scene_types.h"
51 #include "DNA_world_types.h"
52 #include "DNA_sequence_types.h"
53 #include "DNA_object_types.h"
54
55 #include "BLI_blenlib.h"
56 #include "BLI_utildefines.h"
57 #include "BLI_math_base.h"
58
59 #if defined WIN32 && !defined _LIBC
60 # include "BLI_fnmatch.h" /* use fnmatch included in blenlib */
61 #else
62 #  ifndef _GNU_SOURCE
63 #    define _GNU_SOURCE
64 #  endif
65 # include <fnmatch.h>
66 #endif
67
68
69 #include "BKE_animsys.h"
70 #include "BKE_context.h"
71 #include "BKE_deform.h"
72 #include "BKE_depsgraph.h"
73 #include "BKE_fcurve.h"
74 #include "BKE_global.h"
75 #include "BKE_group.h"
76 #include "BKE_library.h"
77 #include "BKE_main.h"
78 #include "BKE_modifier.h"
79 #include "BKE_report.h"
80 #include "BKE_scene.h"
81 #include "BKE_sequencer.h"
82
83 #include "ED_armature.h"
84 #include "ED_object.h"
85 #include "ED_screen.h"
86 #include "ED_util.h"
87
88 #include "WM_api.h"
89 #include "WM_types.h"
90
91 #include "BIF_gl.h"
92 #include "BIF_glutil.h"
93
94 #include "UI_interface.h"
95 #include "UI_interface_icons.h"
96 #include "UI_resources.h"
97 #include "UI_view2d.h"
98
99 #include "RNA_access.h"
100 #include "RNA_define.h"
101 #include "RNA_enum_types.h"
102
103 #include "ED_keyframing.h"
104
105 #include "outliner_intern.h"
106
107 /* ************************************************************** */
108 /* Unused Utilities */
109 // XXX: where to place these?
110
111 /* This is not used anywhere at the moment */
112 #if 0
113 /* return 1 when levels were opened */
114 static int outliner_open_back(SpaceOops *soops, TreeElement *te)
115 {
116         TreeStoreElem *tselem;
117         int retval= 0;
118         
119         for (te= te->parent; te; te= te->parent) {
120                 tselem= TREESTORE(te);
121                 if (tselem->flag & TSE_CLOSED) { 
122                         tselem->flag &= ~TSE_CLOSED;
123                         retval= 1;
124                 }
125         }
126         return retval;
127 }
128
129 static void outliner_open_reveal(SpaceOops *soops, ListBase *lb, TreeElement *teFind, int *found)
130 {
131         TreeElement *te;
132         TreeStoreElem *tselem;
133         
134         for (te= lb->first; te; te= te->next) {
135                 /* check if this tree-element was the one we're seeking */
136                 if (te == teFind) {
137                         *found= 1;
138                         return;
139                 }
140                 
141                 /* try to see if sub-tree contains it then */
142                 outliner_open_reveal(soops, &te->subtree, teFind, found);
143                 if (*found) {
144                         tselem= TREESTORE(te);
145                         if (tselem->flag & TSE_CLOSED) 
146                                 tselem->flag &= ~TSE_CLOSED;
147                         return;
148                 }
149         }
150 }
151 #endif
152
153 /* ************************************************************** */
154 /* Click Activated */
155
156 /* Toggle Open/Closed ------------------------------------------- */
157
158 static int do_outliner_item_openclose(bContext *C, SpaceOops *soops, TreeElement *te, int all, const float mval[2])
159 {
160         
161         if(mval[1]>te->ys && mval[1]<te->ys+UI_UNIT_Y) {
162                 TreeStoreElem *tselem= TREESTORE(te);
163                 
164                 /* all below close/open? */
165                 if(all) {
166                         tselem->flag &= ~TSE_CLOSED;
167                         outliner_set_flag(soops, &te->subtree, TSE_CLOSED, !outliner_has_one_flag(soops, &te->subtree, TSE_CLOSED, 1));
168                 }
169                 else {
170                         if(tselem->flag & TSE_CLOSED) tselem->flag &= ~TSE_CLOSED;
171                         else tselem->flag |= TSE_CLOSED;
172                 }
173                 
174                 return 1;
175         }
176         
177         for(te= te->subtree.first; te; te= te->next) {
178                 if(do_outliner_item_openclose(C, soops, te, all, mval)) 
179                         return 1;
180         }
181         return 0;
182         
183 }
184
185 /* event can enterkey, then it opens/closes */
186 static int outliner_item_openclose(bContext *C, wmOperator *op, wmEvent *event)
187 {
188         ARegion *ar= CTX_wm_region(C);
189         SpaceOops *soops= CTX_wm_space_outliner(C);
190         TreeElement *te;
191         float fmval[2];
192         int all= RNA_boolean_get(op->ptr, "all");
193         
194         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], fmval, fmval+1);
195         
196         for(te= soops->tree.first; te; te= te->next) {
197                 if(do_outliner_item_openclose(C, soops, te, all, fmval)) 
198                         break;
199         }
200
201         ED_region_tag_redraw(ar);
202         
203         return OPERATOR_FINISHED;
204 }
205
206 void OUTLINER_OT_item_openclose(wmOperatorType *ot)
207 {
208         ot->name= "Open/Close Item";
209         ot->idname= "OUTLINER_OT_item_openclose";
210         ot->description= "Toggle whether item under cursor is enabled or closed";
211         
212         ot->invoke= outliner_item_openclose;
213         
214         ot->poll= ED_operator_outliner_active;
215         
216         RNA_def_boolean(ot->srna, "all", 1, "All", "Close or open all items.");
217 }
218
219 /* Rename --------------------------------------------------- */
220
221 static int do_outliner_item_rename(bContext *C, ARegion *ar, SpaceOops *soops, TreeElement *te, const float mval[2])
222 {       
223         ReportList *reports= CTX_wm_reports(C); // XXX
224         
225         if(mval[1]>te->ys && mval[1]<te->ys+UI_UNIT_Y) {
226                 TreeStoreElem *tselem= TREESTORE(te);
227                 
228                 /* name and first icon */
229                 if(mval[0]>te->xs+UI_UNIT_X && mval[0]<te->xend) {
230                         
231                         /* can't rename rna datablocks entries */
232                         if(ELEM3(tselem->type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM))
233                            ;
234                         else if(ELEM10(tselem->type, TSE_ANIM_DATA, TSE_NLA, TSE_DEFGROUP_BASE, TSE_CONSTRAINT_BASE, TSE_MODIFIER_BASE, TSE_SCRIPT_BASE, TSE_POSE_BASE, TSE_POSEGRP_BASE, TSE_R_LAYER_BASE, TSE_R_PASS)) 
235                                         BKE_report(reports, RPT_WARNING, "Cannot edit builtin name");
236                         else if(ELEM3(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP))
237                                 BKE_report(reports, RPT_WARNING, "Cannot edit sequence name");
238                         else if(tselem->id->lib) {
239                                 // XXX                                          error_libdata();
240                         } 
241                         else if(te->idcode == ID_LI && te->parent) {
242                                 BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library");
243                         } 
244                         else {
245                                 tselem->flag |= TSE_TEXTBUT;
246                                 ED_region_tag_redraw(ar);
247                         }
248                 }
249                 return 1;
250         }
251         
252         for(te= te->subtree.first; te; te= te->next) {
253                 if(do_outliner_item_rename(C, ar, soops, te, mval)) return 1;
254         }
255         return 0;
256 }
257
258 static int outliner_item_rename(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
259 {
260         ARegion *ar= CTX_wm_region(C);
261         SpaceOops *soops= CTX_wm_space_outliner(C);
262         TreeElement *te;
263         float fmval[2];
264         
265         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], fmval, fmval+1);
266         
267         for(te= soops->tree.first; te; te= te->next) {
268                 if(do_outliner_item_rename(C, ar, soops, te, fmval)) break;
269         }
270         
271         return OPERATOR_FINISHED;
272 }
273
274
275 void OUTLINER_OT_item_rename(wmOperatorType *ot)
276 {
277         ot->name= "Rename Item";
278         ot->idname= "OUTLINER_OT_item_rename";
279         ot->description= "Rename item under cursor";
280         
281         ot->invoke= outliner_item_rename;
282         
283         ot->poll= ED_operator_outliner_active;
284 }
285
286 /* ************************************************************** */
287 /* Setting Toggling Operators */
288
289 /* =============================================== */
290 /* Toggling Utilities (Exported) */
291
292 /* Apply Settings ------------------------------- */
293
294 static int outliner_count_levels(SpaceOops *soops, ListBase *lb, int curlevel)
295 {
296         TreeElement *te;
297         int level=curlevel, lev;
298         
299         for(te= lb->first; te; te= te->next) {
300                 
301                 lev= outliner_count_levels(soops, &te->subtree, curlevel+1);
302                 if(lev>level) level= lev;
303         }
304         return level;
305 }
306
307 int outliner_has_one_flag(SpaceOops *soops, ListBase *lb, short flag, short curlevel)
308 {
309         TreeElement *te;
310         TreeStoreElem *tselem;
311         int level;
312         
313         for(te= lb->first; te; te= te->next) {
314                 tselem= TREESTORE(te);
315                 if(tselem->flag & flag) return curlevel;
316                 
317                 level= outliner_has_one_flag(soops, &te->subtree, flag, curlevel+1);
318                 if(level) return level;
319         }
320         return 0;
321 }
322
323 void outliner_set_flag(SpaceOops *soops, ListBase *lb, short flag, short set)
324 {
325         TreeElement *te;
326         TreeStoreElem *tselem;
327         
328         for(te= lb->first; te; te= te->next) {
329                 tselem= TREESTORE(te);
330                 if(set==0) tselem->flag &= ~flag;
331                 else tselem->flag |= flag;
332                 outliner_set_flag(soops, &te->subtree, flag, set);
333         }
334 }
335
336 /* Restriction Columns ------------------------------- */
337
338 /* same check needed for both object operation and restrict column button func
339  * return 0 when in edit mode (cannot restrict view or select)
340  * otherwise return 1 */
341 int common_restrict_check(bContext *C, Object *ob)
342 {
343         /* Don't allow hide an object in edit mode,
344          * check the bug #22153 and #21609, #23977
345          */
346         Object *obedit= CTX_data_edit_object(C);
347         if (obedit && obedit == ob) {
348                 /* found object is hidden, reset */
349                 if (ob->restrictflag & OB_RESTRICT_VIEW)
350                         ob->restrictflag &= ~OB_RESTRICT_VIEW;
351                 /* found object is unselectable, reset */
352                 if (ob->restrictflag & OB_RESTRICT_SELECT)
353                         ob->restrictflag &= ~OB_RESTRICT_SELECT;
354                 return 0;
355         }
356         
357         return 1;
358 }
359
360 /* =============================================== */
361 /* Restriction toggles */
362
363 /* Toggle Visibility ---------------------------------------- */
364
365 void object_toggle_visibility_cb(bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
366 {
367         Base *base= (Base *)te->directdata;
368         Object *ob = (Object *)tselem->id;
369         
370         /* add check for edit mode */
371         if(!common_restrict_check(C, ob)) return;
372         
373         if(base || (base= object_in_scene(ob, scene))) {
374                 if((base->object->restrictflag ^= OB_RESTRICT_VIEW)) {
375                         ED_base_object_select(base, BA_DESELECT);
376                 }
377         }
378 }
379
380 static int outliner_toggle_visibility_exec(bContext *C, wmOperator *UNUSED(op))
381 {
382         SpaceOops *soops= CTX_wm_space_outliner(C);
383         Scene *scene= CTX_data_scene(C);
384         ARegion *ar= CTX_wm_region(C);
385         
386         outliner_do_object_operation(C, scene, soops, &soops->tree, object_toggle_visibility_cb);
387         
388         WM_event_add_notifier(C, NC_SCENE|ND_OB_VISIBLE, scene);
389         ED_region_tag_redraw(ar);
390         
391         return OPERATOR_FINISHED;
392 }
393
394 void OUTLINER_OT_visibility_toggle(wmOperatorType *ot)
395 {
396         /* identifiers */
397         ot->name= "Toggle Visibility";
398         ot->idname= "OUTLINER_OT_visibility_toggle";
399         ot->description= "Toggle the visibility of selected items";
400         
401         /* callbacks */
402         ot->exec= outliner_toggle_visibility_exec;
403         ot->poll= ED_operator_outliner_active_no_editobject;
404         
405         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
406 }
407
408 /* Toggle Selectability ---------------------------------------- */
409
410 void object_toggle_selectability_cb(bContext *UNUSED(C), Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
411 {
412         Base *base= (Base *)te->directdata;
413         
414         if(base==NULL) base= object_in_scene((Object *)tselem->id, scene);
415         if(base) {
416                 base->object->restrictflag^=OB_RESTRICT_SELECT;
417         }
418 }
419
420 static int outliner_toggle_selectability_exec(bContext *C, wmOperator *UNUSED(op))
421 {
422         SpaceOops *soops= CTX_wm_space_outliner(C);
423         Scene *scene= CTX_data_scene(C);
424         ARegion *ar= CTX_wm_region(C);
425         
426         outliner_do_object_operation(C, scene, soops, &soops->tree, object_toggle_selectability_cb);
427         
428         WM_event_add_notifier(C, NC_SCENE|ND_OB_SELECT, scene);
429         ED_region_tag_redraw(ar);
430         
431         return OPERATOR_FINISHED;
432 }
433
434 void OUTLINER_OT_selectability_toggle(wmOperatorType *ot)
435 {
436         /* identifiers */
437         ot->name= "Toggle Selectability";
438         ot->idname= "OUTLINER_OT_selectability_toggle";
439         ot->description= "Toggle the selectability";
440         
441         /* callbacks */
442         ot->exec= outliner_toggle_selectability_exec;
443         ot->poll= ED_operator_outliner_active_no_editobject;
444         
445         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
446 }
447
448 /* Toggle Renderability ---------------------------------------- */
449
450 void object_toggle_renderability_cb(bContext *UNUSED(C), Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
451 {
452         Base *base= (Base *)te->directdata;
453         
454         if(base==NULL) base= object_in_scene((Object *)tselem->id, scene);
455         if(base) {
456                 base->object->restrictflag^=OB_RESTRICT_RENDER;
457         }
458 }
459
460 static int outliner_toggle_renderability_exec(bContext *C, wmOperator *UNUSED(op))
461 {
462         SpaceOops *soops= CTX_wm_space_outliner(C);
463         Scene *scene= CTX_data_scene(C);
464         ARegion *ar= CTX_wm_region(C);
465         
466         outliner_do_object_operation(C, scene, soops, &soops->tree, object_toggle_renderability_cb);
467         
468         ED_region_tag_redraw(ar);
469         
470         return OPERATOR_FINISHED;
471 }
472
473 void OUTLINER_OT_renderability_toggle(wmOperatorType *ot)
474 {
475         /* identifiers */
476         ot->name= "Toggle Renderability";
477         ot->idname= "OUTLINER_OT_renderability_toggle";
478         ot->description= "Toggle the renderability of selected items";
479         
480         /* callbacks */
481         ot->exec= outliner_toggle_renderability_exec;
482         ot->poll= ED_operator_outliner_active;
483         
484         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
485 }
486
487 /* =============================================== */
488 /* Outliner setting toggles */
489
490 /* Toggle Expanded (Outliner) ---------------------------------------- */
491
492 static int outliner_toggle_expanded_exec(bContext *C, wmOperator *UNUSED(op))
493 {
494         SpaceOops *soops= CTX_wm_space_outliner(C);
495         ARegion *ar= CTX_wm_region(C);
496         
497         if (outliner_has_one_flag(soops, &soops->tree, TSE_CLOSED, 1))
498                 outliner_set_flag(soops, &soops->tree, TSE_CLOSED, 0);
499         else 
500                 outliner_set_flag(soops, &soops->tree, TSE_CLOSED, 1);
501         
502         ED_region_tag_redraw(ar);
503         
504         return OPERATOR_FINISHED;
505 }
506
507 void OUTLINER_OT_expanded_toggle(wmOperatorType *ot)
508 {
509         /* identifiers */
510         ot->name= "Expand/Collapse All";
511         ot->idname= "OUTLINER_OT_expanded_toggle";
512         ot->description= "Expand/Collapse all items";
513         
514         /* callbacks */
515         ot->exec= outliner_toggle_expanded_exec;
516         ot->poll= ED_operator_outliner_active;
517         
518         /* no undo or registry, UI option */
519 }
520
521 /* Toggle Selected (Outliner) ---------------------------------------- */
522
523 static int outliner_toggle_selected_exec(bContext *C, wmOperator *UNUSED(op))
524 {
525         SpaceOops *soops= CTX_wm_space_outliner(C);
526         ARegion *ar= CTX_wm_region(C);
527         Scene *scene= CTX_data_scene(C);
528         
529         if (outliner_has_one_flag(soops, &soops->tree, TSE_SELECTED, 1))
530                 outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
531         else 
532                 outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 1);
533         
534         soops->storeflag |= SO_TREESTORE_REDRAW;
535         
536         WM_event_add_notifier(C, NC_SCENE|ND_OB_SELECT, scene);
537         ED_region_tag_redraw(ar);
538         
539         return OPERATOR_FINISHED;
540 }
541
542 void OUTLINER_OT_selected_toggle(wmOperatorType *ot)
543 {
544         /* identifiers */
545         ot->name= "Toggle Selected";
546         ot->idname= "OUTLINER_OT_selected_toggle";
547         ot->description= "Toggle the Outliner selection of items";
548         
549         /* callbacks */
550         ot->exec= outliner_toggle_selected_exec;
551         ot->poll= ED_operator_outliner_active;
552         
553         /* no undo or registry, UI option */
554 }
555
556 /* ************************************************************** */
557 /* Hotkey Only Operators */
558
559 /* Show Active --------------------------------------------------- */
560
561 static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op))
562 {
563         SpaceOops *so= CTX_wm_space_outliner(C);
564         Scene *scene= CTX_data_scene(C);
565         ARegion *ar= CTX_wm_region(C);
566         View2D *v2d= &ar->v2d;
567         
568         TreeElement *te;
569         int xdelta, ytop;
570         
571         // TODO: make this get this info from context instead...
572         if (OBACT == NULL) 
573                 return OPERATOR_CANCELLED;
574         
575         te= outliner_find_id(so, &so->tree, (ID *)OBACT);
576         if (te) {
577                 /* make te->ys center of view */
578                 ytop= (int)(te->ys + (v2d->mask.ymax - v2d->mask.ymin)/2);
579                 if (ytop>0) ytop= 0;
580                 
581                 v2d->cur.ymax= (float)ytop;
582                 v2d->cur.ymin= (float)(ytop-(v2d->mask.ymax - v2d->mask.ymin));
583                 
584                 /* make te->xs ==> te->xend center of view */
585                 xdelta = (int)(te->xs - v2d->cur.xmin);
586                 v2d->cur.xmin += xdelta;
587                 v2d->cur.xmax += xdelta;
588                 
589                 so->storeflag |= SO_TREESTORE_REDRAW;
590         }
591         
592         ED_region_tag_redraw(ar);
593         
594         return OPERATOR_FINISHED;
595 }
596
597 void OUTLINER_OT_show_active(wmOperatorType *ot)
598 {
599         /* identifiers */
600         ot->name= "Show Active";
601         ot->idname= "OUTLINER_OT_show_active";
602         ot->description= "Adjust the view so that the active Object is shown centered";
603         
604         /* callbacks */
605         ot->exec= outliner_show_active_exec;
606         ot->poll= ED_operator_outliner_active;
607 }
608
609 /* View Panning --------------------------------------------------- */
610
611 static int outliner_scroll_page_exec(bContext *C, wmOperator *op)
612 {
613         ARegion *ar= CTX_wm_region(C);
614         int dy= ar->v2d.mask.ymax - ar->v2d.mask.ymin;
615         int up= 0;
616         
617         if(RNA_boolean_get(op->ptr, "up"))
618                 up= 1;
619
620         if(up == 0) dy= -dy;
621         ar->v2d.cur.ymin+= dy;
622         ar->v2d.cur.ymax+= dy;
623         
624         ED_region_tag_redraw(ar);
625         
626         return OPERATOR_FINISHED;
627 }
628
629
630 void OUTLINER_OT_scroll_page(wmOperatorType *ot)
631 {
632         /* identifiers */
633         ot->name= "Scroll Page";
634         ot->idname= "OUTLINER_OT_scroll_page";
635         ot->description= "Scroll page up or down";
636         
637         /* callbacks */
638         ot->exec= outliner_scroll_page_exec;
639         ot->poll= ED_operator_outliner_active;
640         
641         /* properties */
642         RNA_def_boolean(ot->srna, "up", 0, "Up", "Scroll up one page.");
643 }
644
645 /* Search ------------------------------------------------------- */
646 // TODO: probably obsolete now with filtering?
647
648 #if 0
649
650 /* recursive helper for function below */
651 static void outliner_set_coordinates_element(SpaceOops *soops, TreeElement *te, int startx, int *starty)
652 {
653         TreeStoreElem *tselem= TREESTORE(te);
654         
655         /* store coord and continue, we need coordinates for elements outside view too */
656         te->xs= (float)startx;
657         te->ys= (float)(*starty);
658         *starty-= UI_UNIT_Y;
659         
660         if((tselem->flag & TSE_CLOSED)==0) {
661                 TreeElement *ten;
662                 for(ten= te->subtree.first; ten; ten= ten->next) {
663                         outliner_set_coordinates_element(soops, ten, startx+UI_UNIT_X, starty);
664                 }
665         }
666         
667 }
668
669 /* to retrieve coordinates with redrawing the entire tree */
670 static void outliner_set_coordinates(ARegion *ar, SpaceOops *soops)
671 {
672         TreeElement *te;
673         int starty= (int)(ar->v2d.tot.ymax)-UI_UNIT_Y;
674         int startx= 0;
675         
676         for(te= soops->tree.first; te; te= te->next) {
677                 outliner_set_coordinates_element(soops, te, startx, &starty);
678         }
679 }
680
681 /* find next element that has this name */
682 static TreeElement *outliner_find_named(SpaceOops *soops, ListBase *lb, char *name, int flags, TreeElement *prev, int *prevFound)
683 {
684         TreeElement *te, *tes;
685         
686         for (te= lb->first; te; te= te->next) {
687                 int found = outliner_filter_has_name(te, name, flags);
688                 
689                 if(found) {
690                         /* name is right, but is element the previous one? */
691                         if (prev) {
692                                 if ((te != prev) && (*prevFound)) 
693                                         return te;
694                                 if (te == prev) {
695                                         *prevFound = 1;
696                                 }
697                         }
698                         else
699                                 return te;
700                 }
701                 
702                 tes= outliner_find_named(soops, &te->subtree, name, flags, prev, prevFound);
703                 if(tes) return tes;
704         }
705
706         /* nothing valid found */
707         return NULL;
708 }
709
710 static void outliner_find_panel(Scene *UNUSED(scene), ARegion *ar, SpaceOops *soops, int again, int flags) 
711 {
712         ReportList *reports = NULL; // CTX_wm_reports(C);
713         TreeElement *te= NULL;
714         TreeElement *last_find;
715         TreeStoreElem *tselem;
716         int ytop, xdelta, prevFound=0;
717         char name[32];
718         
719         /* get last found tree-element based on stored search_tse */
720         last_find= outliner_find_tse(soops, &soops->search_tse);
721         
722         /* determine which type of search to do */
723         if (again && last_find) {
724                 /* no popup panel - previous + user wanted to search for next after previous */         
725                 BLI_strncpy(name, soops->search_string, sizeof(name));
726                 flags= soops->search_flags;
727                 
728                 /* try to find matching element */
729                 te= outliner_find_named(soops, &soops->tree, name, flags, last_find, &prevFound);
730                 if (te==NULL) {
731                         /* no more matches after previous, start from beginning again */
732                         prevFound= 1;
733                         te= outliner_find_named(soops, &soops->tree, name, flags, last_find, &prevFound);
734                 }
735         }
736         else {
737                 /* pop up panel - no previous, or user didn't want search after previous */
738                 strcpy(name, "");
739 // XXX          if (sbutton(name, 0, sizeof(name)-1, "Find: ") && name[0]) {
740 //                      te= outliner_find_named(soops, &soops->tree, name, flags, NULL, &prevFound);
741 //              }
742 //              else return; /* XXX RETURN! XXX */
743         }
744
745         /* do selection and reveal */
746         if (te) {
747                 tselem= TREESTORE(te);
748                 if (tselem) {
749                         /* expand branches so that it will be visible, we need to get correct coordinates */
750                         if( outliner_open_back(soops, te))
751                                 outliner_set_coordinates(ar, soops);
752                         
753                         /* deselect all visible, and select found element */
754                         outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
755                         tselem->flag |= TSE_SELECTED;
756                         
757                         /* make te->ys center of view */
758                         ytop= (int)(te->ys + (ar->v2d.mask.ymax-ar->v2d.mask.ymin)/2);
759                         if(ytop>0) ytop= 0;
760                         ar->v2d.cur.ymax= (float)ytop;
761                         ar->v2d.cur.ymin= (float)(ytop-(ar->v2d.mask.ymax-ar->v2d.mask.ymin));
762                         
763                         /* make te->xs ==> te->xend center of view */
764                         xdelta = (int)(te->xs - ar->v2d.cur.xmin);
765                         ar->v2d.cur.xmin += xdelta;
766                         ar->v2d.cur.xmax += xdelta;
767                         
768                         /* store selection */
769                         soops->search_tse= *tselem;
770                         
771                         BLI_strncpy(soops->search_string, name, 33);
772                         soops->search_flags= flags;
773                         
774                         /* redraw */
775                         soops->storeflag |= SO_TREESTORE_REDRAW;
776                 }
777         }
778         else {
779                 /* no tree-element found */
780                 BKE_report(reports, RPT_WARNING, "Not found: %s", name);
781         }
782 }
783 #endif
784
785 /* Show One Level ----------------------------------------------- */
786
787 /* helper function for Show/Hide one level operator */
788 static void outliner_openclose_level(SpaceOops *soops, ListBase *lb, int curlevel, int level, int open)
789 {
790         TreeElement *te;
791         TreeStoreElem *tselem;
792         
793         for(te= lb->first; te; te= te->next) {
794                 tselem= TREESTORE(te);
795                 
796                 if(open) {
797                         if(curlevel<=level) tselem->flag &= ~TSE_CLOSED;
798                 }
799                 else {
800                         if(curlevel>=level) tselem->flag |= TSE_CLOSED;
801                 }
802                 
803                 outliner_openclose_level(soops, &te->subtree, curlevel+1, level, open);
804         }
805 }
806
807 static int outliner_one_level_exec(bContext *C, wmOperator *op)
808 {
809         SpaceOops *soops= CTX_wm_space_outliner(C);
810         ARegion *ar= CTX_wm_region(C);
811         int add= RNA_boolean_get(op->ptr, "open");
812         int level;
813         
814         level= outliner_has_one_flag(soops, &soops->tree, TSE_CLOSED, 1);
815         if(add==1) {
816                 if(level) outliner_openclose_level(soops, &soops->tree, 1, level, 1);
817         }
818         else {
819                 if(level==0) level= outliner_count_levels(soops, &soops->tree, 0);
820                 if(level) outliner_openclose_level(soops, &soops->tree, 1, level-1, 0);
821         }
822         
823         ED_region_tag_redraw(ar);
824         
825         return OPERATOR_FINISHED;
826 }
827
828 void OUTLINER_OT_show_one_level(wmOperatorType *ot)
829 {
830         /* identifiers */
831         ot->name= "Show/Hide One Level";
832         ot->idname= "OUTLINER_OT_show_one_level";
833         ot->description= "Expand/collapse all entries by one level";
834         
835         /* callbacks */
836         ot->exec= outliner_one_level_exec;
837         ot->poll= ED_operator_outliner_active;
838         
839         /* no undo or registry, UI option */
840         
841         /* properties */
842         RNA_def_boolean(ot->srna, "open", 1, "Open", "Expand all entries one level deep.");
843 }
844
845 /* Show Hierarchy ----------------------------------------------- */
846
847 /* helper function for tree_element_shwo_hierarchy() - recursively checks whether subtrees have any objects*/
848 static int subtree_has_objects(SpaceOops *soops, ListBase *lb)
849 {
850         TreeElement *te;
851         TreeStoreElem *tselem;
852         
853         for(te= lb->first; te; te= te->next) {
854                 tselem= TREESTORE(te);
855                 if(tselem->type==0 && te->idcode==ID_OB) return 1;
856                 if( subtree_has_objects(soops, &te->subtree)) return 1;
857         }
858         return 0;
859 }
860
861 /* recursive helper function for Show Hierarchy operator */
862 static void tree_element_show_hierarchy(Scene *scene, SpaceOops *soops, ListBase *lb)
863 {
864         TreeElement *te;
865         TreeStoreElem *tselem;
866
867         /* open all object elems, close others */
868         for(te= lb->first; te; te= te->next) {
869                 tselem= TREESTORE(te);
870                 
871                 if(tselem->type==0) {
872                         if(te->idcode==ID_SCE) {
873                                 if(tselem->id!=(ID *)scene) tselem->flag |= TSE_CLOSED;
874                                         else tselem->flag &= ~TSE_CLOSED;
875                         }
876                         else if(te->idcode==ID_OB) {
877                                 if(subtree_has_objects(soops, &te->subtree)) tselem->flag &= ~TSE_CLOSED;
878                                 else tselem->flag |= TSE_CLOSED;
879                         }
880                 }
881                 else tselem->flag |= TSE_CLOSED;
882                 
883                 if(tselem->flag & TSE_CLOSED); else tree_element_show_hierarchy(scene, soops, &te->subtree);
884         }
885 }
886
887 /* show entire object level hierarchy */
888 static int outliner_show_hierarchy_exec(bContext *C, wmOperator *UNUSED(op))
889 {
890         SpaceOops *soops= CTX_wm_space_outliner(C);
891         ARegion *ar= CTX_wm_region(C);
892         Scene *scene= CTX_data_scene(C);
893         
894         /* recursively open/close levels */
895         tree_element_show_hierarchy(scene, soops, &soops->tree);
896         
897         ED_region_tag_redraw(ar);
898         
899         return OPERATOR_FINISHED;
900 }
901
902 void OUTLINER_OT_show_hierarchy(wmOperatorType *ot)
903 {
904         /* identifiers */
905         ot->name= "Show Hierarchy";
906         ot->idname= "OUTLINER_OT_show_hierarchy";
907         ot->description= "Open all object entries and close all others";
908         
909         /* callbacks */
910         ot->exec= outliner_show_hierarchy_exec;
911         ot->poll= ED_operator_outliner_active; //  TODO: shouldn't be allowed in RNA views...
912         
913         /* no undo or registry, UI option */
914 }
915
916 /* ************************************************************** */
917 /* ANIMATO OPERATIONS */
918 /* KeyingSet and Driver Creation - Helper functions */
919
920 /* specialised poll callback for these operators to work in Datablocks view only */
921 static int ed_operator_outliner_datablocks_active(bContext *C)
922 {
923         ScrArea *sa= CTX_wm_area(C);
924         if ((sa) && (sa->spacetype==SPACE_OUTLINER)) {
925                 SpaceOops *so= CTX_wm_space_outliner(C);
926                 return (so->outlinevis == SO_DATABLOCKS);
927         }
928         return 0;
929 }
930
931
932 /* Helper func to extract an RNA path from selected tree element 
933  * NOTE: the caller must zero-out all values of the pointers that it passes here first, as
934  * this function does not do that yet 
935  */
936 static void tree_element_to_path(SpaceOops *soops, TreeElement *te, TreeStoreElem *tselem, 
937                                                         ID **id, char **path, int *array_index, short *flag, short *UNUSED(groupmode))
938 {
939         ListBase hierarchy = {NULL, NULL};
940         LinkData *ld;
941         TreeElement *tem, *temnext, *temsub;
942         TreeStoreElem *tse, *tsenext;
943         PointerRNA *ptr, *nextptr;
944         PropertyRNA *prop;
945         char *newpath=NULL;
946         
947         /* optimise tricks:
948          *      - Don't do anything if the selected item is a 'struct', but arrays are allowed
949          */
950         if (tselem->type == TSE_RNA_STRUCT)
951                 return;
952         
953         /* Overview of Algorithm:
954          *      1. Go up the chain of parents until we find the 'root', taking note of the 
955          *         levels encountered in reverse-order (i.e. items are added to the start of the list
956          *      for more convenient looping later)
957          *      2. Walk down the chain, adding from the first ID encountered 
958          *         (which will become the 'ID' for the KeyingSet Path), and build a  
959          *              path as we step through the chain
960          */
961          
962         /* step 1: flatten out hierarchy of parents into a flat chain */
963         for (tem= te->parent; tem; tem= tem->parent) {
964                 ld= MEM_callocN(sizeof(LinkData), "LinkData for tree_element_to_path()");
965                 ld->data= tem;
966                 BLI_addhead(&hierarchy, ld);
967         }
968         
969         /* step 2: step down hierarchy building the path (NOTE: addhead in previous loop was needed so that we can loop like this) */
970         for (ld= hierarchy.first; ld; ld= ld->next) {
971                 /* get data */
972                 tem= (TreeElement *)ld->data;
973                 tse= TREESTORE(tem);
974                 ptr= &tem->rnaptr;
975                 prop= tem->directdata;
976                 
977                 /* check if we're looking for first ID, or appending to path */
978                 if (*id) {
979                         /* just 'append' property to path 
980                          *      - to prevent memory leaks, we must write to newpath not path, then free old path + swap them
981                          */
982                         if(tse->type == TSE_RNA_PROPERTY) {
983                                 if(RNA_property_type(prop) == PROP_POINTER) {
984                                         /* for pointer we just append property name */
985                                         newpath= RNA_path_append(*path, ptr, prop, 0, NULL);
986                                 }
987                                 else if(RNA_property_type(prop) == PROP_COLLECTION) {
988                                         char buf[128], *name;
989                                         
990                                         temnext= (TreeElement*)(ld->next->data);
991                                         tsenext= TREESTORE(temnext);
992                                         
993                                         nextptr= &temnext->rnaptr;
994                                         name= RNA_struct_name_get_alloc(nextptr, buf, sizeof(buf));
995                                         
996                                         if(name) {
997                                                 /* if possible, use name as a key in the path */
998                                                 newpath= RNA_path_append(*path, NULL, prop, 0, name);
999                                                 
1000                                                 if(name != buf)
1001                                                         MEM_freeN(name);
1002                                         }
1003                                         else {
1004                                                 /* otherwise use index */
1005                                                 int index= 0;
1006                                                 
1007                                                 for(temsub=tem->subtree.first; temsub; temsub=temsub->next, index++)
1008                                                         if(temsub == temnext)
1009                                                                 break;
1010                                                 
1011                                                 newpath= RNA_path_append(*path, NULL, prop, index, NULL);
1012                                         }
1013                                         
1014                                         ld= ld->next;
1015                                 }
1016                         }
1017                         
1018                         if(newpath) {
1019                                 if (*path) MEM_freeN(*path);
1020                                 *path= newpath;
1021                                 newpath= NULL;
1022                         }
1023                 }
1024                 else {
1025                         /* no ID, so check if entry is RNA-struct, and if that RNA-struct is an ID datablock to extract info from */
1026                         if (tse->type == TSE_RNA_STRUCT) {
1027                                 /* ptr->data not ptr->id.data seems to be the one we want, since ptr->data is sometimes the owner of this ID? */
1028                                 if(RNA_struct_is_ID(ptr->type)) {
1029                                         *id= (ID *)ptr->data;
1030                                         
1031                                         /* clear path */
1032                                         if(*path) {
1033                                                 MEM_freeN(*path);
1034                                                 path= NULL;
1035                                         }
1036                                 }
1037                         }
1038                 }
1039         }
1040
1041         /* step 3: if we've got an ID, add the current item to the path */
1042         if (*id) {
1043                 /* add the active property to the path */
1044                 ptr= &te->rnaptr;
1045                 prop= te->directdata;
1046                 
1047                 /* array checks */
1048                 if (tselem->type == TSE_RNA_ARRAY_ELEM) {
1049                         /* item is part of an array, so must set the array_index */
1050                         *array_index= te->index;
1051                 }
1052                 else if (RNA_property_array_length(ptr, prop)) {
1053                         /* entire array was selected, so keyframe all */
1054                         *flag |= KSP_FLAG_WHOLE_ARRAY;
1055                 }
1056                 
1057                 /* path */
1058                 newpath= RNA_path_append(*path, NULL, prop, 0, NULL);
1059                 if (*path) MEM_freeN(*path);
1060                 *path= newpath;
1061         }
1062
1063         /* free temp data */
1064         BLI_freelistN(&hierarchy);
1065 }
1066
1067 /* =============================================== */
1068 /* Driver Operations */
1069
1070 /* These operators are only available in databrowser mode for now, as
1071  * they depend on having RNA paths and/or hierarchies available.
1072  */
1073 enum {
1074         DRIVERS_EDITMODE_ADD    = 0,
1075         DRIVERS_EDITMODE_REMOVE,
1076 } /*eDrivers_EditModes*/;
1077
1078 /* Utilities ---------------------------------- */ 
1079
1080 /* Recursively iterate over tree, finding and working on selected items */
1081 static void do_outliner_drivers_editop(SpaceOops *soops, ListBase *tree, ReportList *reports, short mode)
1082 {
1083         TreeElement *te;
1084         TreeStoreElem *tselem;
1085         
1086         for (te= tree->first; te; te=te->next) {
1087                 tselem= TREESTORE(te);
1088                 
1089                 /* if item is selected, perform operation */
1090                 if (tselem->flag & TSE_SELECTED) {
1091                         ID *id= NULL;
1092                         char *path= NULL;
1093                         int array_index= 0;
1094                         short flag= 0;
1095                         short groupmode= KSP_GROUP_KSNAME;
1096                         
1097                         /* check if RNA-property described by this selected element is an animateable prop */
1098                         if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) && RNA_property_animateable(&te->rnaptr, te->directdata)) {
1099                                 /* get id + path + index info from the selected element */
1100                                 tree_element_to_path(soops, te, tselem, 
1101                                                 &id, &path, &array_index, &flag, &groupmode);
1102                         }
1103                         
1104                         /* only if ID and path were set, should we perform any actions */
1105                         if (id && path) {
1106                                 short dflags = CREATEDRIVER_WITH_DEFAULT_DVAR;
1107                                 int arraylen = 1;
1108                                 
1109                                 /* array checks */
1110                                 if (flag & KSP_FLAG_WHOLE_ARRAY) {
1111                                         /* entire array was selected, so add drivers for all */
1112                                         arraylen= RNA_property_array_length(&te->rnaptr, te->directdata);
1113                                 }
1114                                 else
1115                                         arraylen= array_index;
1116                                 
1117                                 /* we should do at least one step */
1118                                 if (arraylen == array_index)
1119                                         arraylen++;
1120                                 
1121                                 /* for each array element we should affect, add driver */
1122                                 for (; array_index < arraylen; array_index++) {
1123                                         /* action depends on mode */
1124                                         switch (mode) {
1125                                                 case DRIVERS_EDITMODE_ADD:
1126                                                 {
1127                                                         /* add a new driver with the information obtained (only if valid) */
1128                                                         ANIM_add_driver(reports, id, path, array_index, dflags, DRIVER_TYPE_PYTHON);
1129                                                 }
1130                                                         break;
1131                                                 case DRIVERS_EDITMODE_REMOVE:
1132                                                 {
1133                                                         /* remove driver matching the information obtained (only if valid) */
1134                                                         ANIM_remove_driver(reports, id, path, array_index, dflags);
1135                                                 }
1136                                                         break;
1137                                         }
1138                                 }
1139                                 
1140                                 /* free path, since it had to be generated */
1141                                 MEM_freeN(path);
1142                         }
1143                         
1144                         
1145                 }
1146                 
1147                 /* go over sub-tree */
1148                 if ((tselem->flag & TSE_CLOSED)==0)
1149                         do_outliner_drivers_editop(soops, &te->subtree, reports, mode);
1150         }
1151 }
1152
1153 /* Add Operator ---------------------------------- */
1154
1155 static int outliner_drivers_addsel_exec(bContext *C, wmOperator *op)
1156 {
1157         SpaceOops *soutliner= CTX_wm_space_outliner(C);
1158         
1159         /* check for invalid states */
1160         if (soutliner == NULL)
1161                 return OPERATOR_CANCELLED;
1162         
1163         /* recursively go into tree, adding selected items */
1164         do_outliner_drivers_editop(soutliner, &soutliner->tree, op->reports, DRIVERS_EDITMODE_ADD);
1165         
1166         /* send notifiers */
1167         WM_event_add_notifier(C, NC_ANIMATION|ND_FCURVES_ORDER, NULL); // XXX
1168         
1169         return OPERATOR_FINISHED;
1170 }
1171
1172 void OUTLINER_OT_drivers_add_selected(wmOperatorType *ot)
1173 {
1174         /* api callbacks */
1175         ot->idname= "OUTLINER_OT_drivers_add_selected";
1176         ot->name= "Add Drivers for Selected";
1177         ot->description= "Add drivers to selected items";
1178         
1179         /* api callbacks */
1180         ot->exec= outliner_drivers_addsel_exec;
1181         ot->poll= ed_operator_outliner_datablocks_active;
1182         
1183         /* flags */
1184         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1185 }
1186
1187
1188 /* Remove Operator ---------------------------------- */
1189
1190 static int outliner_drivers_deletesel_exec(bContext *C, wmOperator *op)
1191 {
1192         SpaceOops *soutliner= CTX_wm_space_outliner(C);
1193         
1194         /* check for invalid states */
1195         if (soutliner == NULL)
1196                 return OPERATOR_CANCELLED;
1197         
1198         /* recursively go into tree, adding selected items */
1199         do_outliner_drivers_editop(soutliner, &soutliner->tree, op->reports, DRIVERS_EDITMODE_REMOVE);
1200         
1201         /* send notifiers */
1202         WM_event_add_notifier(C, ND_KEYS, NULL); // XXX
1203         
1204         return OPERATOR_FINISHED;
1205 }
1206
1207 void OUTLINER_OT_drivers_delete_selected(wmOperatorType *ot)
1208 {
1209         /* identifiers */
1210         ot->idname= "OUTLINER_OT_drivers_delete_selected";
1211         ot->name= "Delete Drivers for Selected";
1212         ot->description= "Delete drivers assigned to selected items";
1213         
1214         /* api callbacks */
1215         ot->exec= outliner_drivers_deletesel_exec;
1216         ot->poll= ed_operator_outliner_datablocks_active;
1217         
1218         /* flags */
1219         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1220 }
1221
1222 /* =============================================== */
1223 /* Keying Set Operations */
1224
1225 /* These operators are only available in databrowser mode for now, as
1226  * they depend on having RNA paths and/or hierarchies available.
1227  */
1228 enum {
1229         KEYINGSET_EDITMODE_ADD  = 0,
1230         KEYINGSET_EDITMODE_REMOVE,
1231 } /*eKeyingSet_EditModes*/;
1232
1233 /* Utilities ---------------------------------- */ 
1234  
1235 /* find the 'active' KeyingSet, and add if not found (if adding is allowed) */
1236 // TODO: should this be an API func?
1237 static KeyingSet *verify_active_keyingset(Scene *scene, short add)
1238 {
1239         KeyingSet *ks= NULL;
1240         
1241         /* sanity check */
1242         if (scene == NULL)
1243                 return NULL;
1244         
1245         /* try to find one from scene */
1246         if (scene->active_keyingset > 0)
1247                 ks= BLI_findlink(&scene->keyingsets, scene->active_keyingset-1);
1248                 
1249         /* add if none found */
1250         // XXX the default settings have yet to evolve
1251         if ((add) && (ks==NULL)) {
1252                 ks= BKE_keyingset_add(&scene->keyingsets, NULL, KEYINGSET_ABSOLUTE, 0);
1253                 scene->active_keyingset= BLI_countlist(&scene->keyingsets);
1254         }
1255         
1256         return ks;
1257 }
1258
1259 /* Recursively iterate over tree, finding and working on selected items */
1260 static void do_outliner_keyingset_editop(SpaceOops *soops, KeyingSet *ks, ListBase *tree, short mode)
1261 {
1262         TreeElement *te;
1263         TreeStoreElem *tselem;
1264         
1265         for (te= tree->first; te; te=te->next) {
1266                 tselem= TREESTORE(te);
1267                 
1268                 /* if item is selected, perform operation */
1269                 if (tselem->flag & TSE_SELECTED) {
1270                         ID *id= NULL;
1271                         char *path= NULL;
1272                         int array_index= 0;
1273                         short flag= 0;
1274                         short groupmode= KSP_GROUP_KSNAME;
1275                         
1276                         /* check if RNA-property described by this selected element is an animateable prop */
1277                         if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) && RNA_property_animateable(&te->rnaptr, te->directdata)) {
1278                                 /* get id + path + index info from the selected element */
1279                                 tree_element_to_path(soops, te, tselem, 
1280                                                 &id, &path, &array_index, &flag, &groupmode);
1281                         }
1282                         
1283                         /* only if ID and path were set, should we perform any actions */
1284                         if (id && path) {
1285                                 /* action depends on mode */
1286                                 switch (mode) {
1287                                         case KEYINGSET_EDITMODE_ADD:
1288                                         {
1289                                                 /* add a new path with the information obtained (only if valid) */
1290                                                 // TODO: what do we do with group name? for now, we don't supply one, and just let this use the KeyingSet name
1291                                                 BKE_keyingset_add_path(ks, id, NULL, path, array_index, flag, groupmode);
1292                                                 ks->active_path= BLI_countlist(&ks->paths);
1293                                         }
1294                                                 break;
1295                                         case KEYINGSET_EDITMODE_REMOVE:
1296                                         {
1297                                                 /* find the relevant path, then remove it from the KeyingSet */
1298                                                 KS_Path *ksp= BKE_keyingset_find_path(ks, id, NULL, path, array_index, groupmode);
1299                                                 
1300                                                 if (ksp) {
1301                                                         /* free path's data */
1302                                                         BKE_keyingset_free_path(ks, ksp);
1303
1304                                                         ks->active_path= 0;
1305                                                 }
1306                                         }
1307                                                 break;
1308                                 }
1309                                 
1310                                 /* free path, since it had to be generated */
1311                                 MEM_freeN(path);
1312                         }
1313                 }
1314                 
1315                 /* go over sub-tree */
1316                 if ((tselem->flag & TSE_CLOSED)==0)
1317                         do_outliner_keyingset_editop(soops, ks, &te->subtree, mode);
1318         }
1319 }
1320
1321 /* Add Operator ---------------------------------- */
1322
1323 static int outliner_keyingset_additems_exec(bContext *C, wmOperator *op)
1324 {
1325         SpaceOops *soutliner= CTX_wm_space_outliner(C);
1326         Scene *scene= CTX_data_scene(C);
1327         KeyingSet *ks= verify_active_keyingset(scene, 1);
1328         
1329         /* check for invalid states */
1330         if (ks == NULL) {
1331                 BKE_report(op->reports, RPT_ERROR, "Operation requires an Active Keying Set");
1332                 return OPERATOR_CANCELLED;
1333         }
1334         if (soutliner == NULL)
1335                 return OPERATOR_CANCELLED;
1336         
1337         /* recursively go into tree, adding selected items */
1338         do_outliner_keyingset_editop(soutliner, ks, &soutliner->tree, KEYINGSET_EDITMODE_ADD);
1339         
1340         /* send notifiers */
1341         WM_event_add_notifier(C, NC_SCENE|ND_KEYINGSET, NULL);
1342         
1343         return OPERATOR_FINISHED;
1344 }
1345
1346 void OUTLINER_OT_keyingset_add_selected(wmOperatorType *ot)
1347 {
1348         /* identifiers */
1349         ot->idname= "OUTLINER_OT_keyingset_add_selected";
1350         ot->name= "Keying Set Add Selected";
1351         ot->description= "Add selected items (blue-grey rows) to active Keying Set";
1352         
1353         /* api callbacks */
1354         ot->exec= outliner_keyingset_additems_exec;
1355         ot->poll= ed_operator_outliner_datablocks_active;
1356         
1357         /* flags */
1358         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1359 }
1360
1361
1362 /* Remove Operator ---------------------------------- */
1363
1364 static int outliner_keyingset_removeitems_exec(bContext *C, wmOperator *UNUSED(op))
1365 {
1366         SpaceOops *soutliner= CTX_wm_space_outliner(C);
1367         Scene *scene= CTX_data_scene(C);
1368         KeyingSet *ks= verify_active_keyingset(scene, 1);
1369         
1370         /* check for invalid states */
1371         if (soutliner == NULL)
1372                 return OPERATOR_CANCELLED;
1373         
1374         /* recursively go into tree, adding selected items */
1375         do_outliner_keyingset_editop(soutliner, ks, &soutliner->tree, KEYINGSET_EDITMODE_REMOVE);
1376         
1377         /* send notifiers */
1378         WM_event_add_notifier(C, NC_SCENE|ND_KEYINGSET, NULL);
1379         
1380         return OPERATOR_FINISHED;
1381 }
1382
1383 void OUTLINER_OT_keyingset_remove_selected(wmOperatorType *ot)
1384 {
1385         /* identifiers */
1386         ot->idname= "OUTLINER_OT_keyingset_remove_selected";
1387         ot->name= "Keying Set Remove Selected";
1388         ot->description = "Remove selected items (blue-grey rows) from active Keying Set";
1389         
1390         /* api callbacks */
1391         ot->exec= outliner_keyingset_removeitems_exec;
1392         ot->poll= ed_operator_outliner_datablocks_active;
1393         
1394         /* flags */
1395         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1396 }