svn merge ^/trunk/blender -r44024:44076
[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 void do_item_rename(ARegion *ar, TreeElement *te, TreeStoreElem *tselem, ReportList *reports)
222 {
223         /* can't rename rna datablocks entries */
224         if(ELEM3(tselem->type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) {
225                         /* do nothing */;
226         }
227         else if(ELEM10(tselem->type, TSE_ANIM_DATA, TSE_NLA, TSE_DEFGROUP_BASE, TSE_CONSTRAINT_BASE, TSE_MODIFIER_BASE,
228                                      TSE_SCRIPT_BASE, TSE_POSE_BASE, TSE_POSEGRP_BASE, TSE_R_LAYER_BASE, TSE_R_PASS))
229         {
230                         BKE_report(reports, RPT_WARNING, "Cannot edit builtin name");
231         }
232         else if(ELEM3(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) {
233                 BKE_report(reports, RPT_WARNING, "Cannot edit sequence name");
234         }
235         else if(tselem->id->lib) {
236                 // XXX                                          error_libdata();
237         } 
238         else if(te->idcode == ID_LI && te->parent) {
239                 BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library");
240         } 
241         else {
242                 tselem->flag |= TSE_TEXTBUT;
243                 ED_region_tag_redraw(ar);
244         }
245 }
246
247 void item_rename_cb(bContext *C, Scene *UNUSED(scene), TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
248 {
249         ARegion *ar= CTX_wm_region(C);
250         ReportList *reports= CTX_wm_reports(C); // XXX
251         do_item_rename(ar, te, tselem, reports) ;
252 }
253
254 static int do_outliner_item_rename(bContext *C, ARegion *ar, SpaceOops *soops, TreeElement *te, const float mval[2])
255 {       
256         ReportList *reports= CTX_wm_reports(C); // XXX
257         
258         if(mval[1]>te->ys && mval[1]<te->ys+UI_UNIT_Y) {
259                 TreeStoreElem *tselem= TREESTORE(te);
260                 
261                 /* name and first icon */
262                 if(mval[0]>te->xs+UI_UNIT_X && mval[0]<te->xend) {
263                         
264                         do_item_rename(ar, te, tselem, reports) ;
265                 }
266                 return 1;
267         }
268         
269         for(te= te->subtree.first; te; te= te->next) {
270                 if(do_outliner_item_rename(C, ar, soops, te, mval)) return 1;
271         }
272         return 0;
273 }
274
275 static int outliner_item_rename(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
276 {
277         ARegion *ar= CTX_wm_region(C);
278         SpaceOops *soops= CTX_wm_space_outliner(C);
279         TreeElement *te;
280         float fmval[2];
281         
282         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], fmval, fmval+1);
283         
284         for(te= soops->tree.first; te; te= te->next) {
285                 if(do_outliner_item_rename(C, ar, soops, te, fmval)) break;
286         }
287         
288         return OPERATOR_FINISHED;
289 }
290
291
292 void OUTLINER_OT_item_rename(wmOperatorType *ot)
293 {
294         ot->name= "Rename Item";
295         ot->idname= "OUTLINER_OT_item_rename";
296         ot->description= "Rename item under cursor";
297         
298         ot->invoke= outliner_item_rename;
299         
300         ot->poll= ED_operator_outliner_active;
301 }
302
303 /* ************************************************************** */
304 /* Setting Toggling Operators */
305
306 /* =============================================== */
307 /* Toggling Utilities (Exported) */
308
309 /* Apply Settings ------------------------------- */
310
311 static int outliner_count_levels(SpaceOops *soops, ListBase *lb, int curlevel)
312 {
313         TreeElement *te;
314         int level=curlevel, lev;
315         
316         for(te= lb->first; te; te= te->next) {
317                 
318                 lev= outliner_count_levels(soops, &te->subtree, curlevel+1);
319                 if(lev>level) level= lev;
320         }
321         return level;
322 }
323
324 int outliner_has_one_flag(SpaceOops *soops, ListBase *lb, short flag, short curlevel)
325 {
326         TreeElement *te;
327         TreeStoreElem *tselem;
328         int level;
329         
330         for(te= lb->first; te; te= te->next) {
331                 tselem= TREESTORE(te);
332                 if(tselem->flag & flag) return curlevel;
333                 
334                 level= outliner_has_one_flag(soops, &te->subtree, flag, curlevel+1);
335                 if(level) return level;
336         }
337         return 0;
338 }
339
340 void outliner_set_flag(SpaceOops *soops, ListBase *lb, short flag, short set)
341 {
342         TreeElement *te;
343         TreeStoreElem *tselem;
344         
345         for(te= lb->first; te; te= te->next) {
346                 tselem= TREESTORE(te);
347                 if(set==0) tselem->flag &= ~flag;
348                 else tselem->flag |= flag;
349                 outliner_set_flag(soops, &te->subtree, flag, set);
350         }
351 }
352
353 /* Restriction Columns ------------------------------- */
354
355 /* same check needed for both object operation and restrict column button func
356  * return 0 when in edit mode (cannot restrict view or select)
357  * otherwise return 1 */
358 int common_restrict_check(bContext *C, Object *ob)
359 {
360         /* Don't allow hide an object in edit mode,
361          * check the bug #22153 and #21609, #23977
362          */
363         Object *obedit= CTX_data_edit_object(C);
364         if (obedit && obedit == ob) {
365                 /* found object is hidden, reset */
366                 if (ob->restrictflag & OB_RESTRICT_VIEW)
367                         ob->restrictflag &= ~OB_RESTRICT_VIEW;
368                 /* found object is unselectable, reset */
369                 if (ob->restrictflag & OB_RESTRICT_SELECT)
370                         ob->restrictflag &= ~OB_RESTRICT_SELECT;
371                 return 0;
372         }
373         
374         return 1;
375 }
376
377 /* =============================================== */
378 /* Restriction toggles */
379
380 /* Toggle Visibility ---------------------------------------- */
381
382 void object_toggle_visibility_cb(bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
383 {
384         Base *base= (Base *)te->directdata;
385         Object *ob = (Object *)tselem->id;
386         
387         /* add check for edit mode */
388         if(!common_restrict_check(C, ob)) return;
389         
390         if(base || (base= object_in_scene(ob, scene))) {
391                 if((base->object->restrictflag ^= OB_RESTRICT_VIEW)) {
392                         ED_base_object_select(base, BA_DESELECT);
393                 }
394         }
395 }
396
397 void group_toggle_visibility_cb(bContext *UNUSED(C), Scene *scene, TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
398 {
399         Group *group= (Group *)tselem->id;
400         restrictbutton_gr_restrict_flag(scene, group, OB_RESTRICT_VIEW);
401 }
402
403 static int outliner_toggle_visibility_exec(bContext *C, wmOperator *UNUSED(op))
404 {
405         SpaceOops *soops= CTX_wm_space_outliner(C);
406         Scene *scene= CTX_data_scene(C);
407         ARegion *ar= CTX_wm_region(C);
408         
409         outliner_do_object_operation(C, scene, soops, &soops->tree, object_toggle_visibility_cb);
410         
411         WM_event_add_notifier(C, NC_SCENE|ND_OB_VISIBLE, scene);
412         ED_region_tag_redraw(ar);
413         
414         return OPERATOR_FINISHED;
415 }
416
417 void OUTLINER_OT_visibility_toggle(wmOperatorType *ot)
418 {
419         /* identifiers */
420         ot->name= "Toggle Visibility";
421         ot->idname= "OUTLINER_OT_visibility_toggle";
422         ot->description= "Toggle the visibility of selected items";
423         
424         /* callbacks */
425         ot->exec= outliner_toggle_visibility_exec;
426         ot->poll= ED_operator_outliner_active_no_editobject;
427         
428         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
429 }
430
431 /* Toggle Selectability ---------------------------------------- */
432
433 void object_toggle_selectability_cb(bContext *UNUSED(C), Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
434 {
435         Base *base= (Base *)te->directdata;
436         
437         if(base==NULL) base= object_in_scene((Object *)tselem->id, scene);
438         if(base) {
439                 base->object->restrictflag^=OB_RESTRICT_SELECT;
440         }
441 }
442
443 void group_toggle_selectability_cb(bContext *UNUSED(C), Scene *scene, TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
444 {
445         Group *group= (Group *)tselem->id;
446         restrictbutton_gr_restrict_flag(scene, group, OB_RESTRICT_SELECT);
447 }
448
449 static int outliner_toggle_selectability_exec(bContext *C, wmOperator *UNUSED(op))
450 {
451         SpaceOops *soops= CTX_wm_space_outliner(C);
452         Scene *scene= CTX_data_scene(C);
453         ARegion *ar= CTX_wm_region(C);
454         
455         outliner_do_object_operation(C, scene, soops, &soops->tree, object_toggle_selectability_cb);
456         
457         WM_event_add_notifier(C, NC_SCENE|ND_OB_SELECT, scene);
458         ED_region_tag_redraw(ar);
459         
460         return OPERATOR_FINISHED;
461 }
462
463 void OUTLINER_OT_selectability_toggle(wmOperatorType *ot)
464 {
465         /* identifiers */
466         ot->name= "Toggle Selectability";
467         ot->idname= "OUTLINER_OT_selectability_toggle";
468         ot->description= "Toggle the selectability";
469         
470         /* callbacks */
471         ot->exec= outliner_toggle_selectability_exec;
472         ot->poll= ED_operator_outliner_active_no_editobject;
473         
474         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
475 }
476
477 /* Toggle Renderability ---------------------------------------- */
478
479 void object_toggle_renderability_cb(bContext *UNUSED(C), Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
480 {
481         Base *base= (Base *)te->directdata;
482         
483         if(base==NULL) base= object_in_scene((Object *)tselem->id, scene);
484         if(base) {
485                 base->object->restrictflag^=OB_RESTRICT_RENDER;
486         }
487 }
488
489 void group_toggle_renderability_cb(bContext *UNUSED(C), Scene *scene, TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
490 {
491         Group *group= (Group *)tselem->id;
492         restrictbutton_gr_restrict_flag(scene, group, OB_RESTRICT_RENDER);
493 }
494
495 static int outliner_toggle_renderability_exec(bContext *C, wmOperator *UNUSED(op))
496 {
497         SpaceOops *soops= CTX_wm_space_outliner(C);
498         Scene *scene= CTX_data_scene(C);
499         
500         outliner_do_object_operation(C, scene, soops, &soops->tree, object_toggle_renderability_cb);
501         
502         WM_event_add_notifier(C, NC_SCENE|ND_OB_RENDER, scene);
503         
504         return OPERATOR_FINISHED;
505 }
506
507 void OUTLINER_OT_renderability_toggle(wmOperatorType *ot)
508 {
509         /* identifiers */
510         ot->name= "Toggle Renderability";
511         ot->idname= "OUTLINER_OT_renderability_toggle";
512         ot->description= "Toggle the renderability of selected items";
513         
514         /* callbacks */
515         ot->exec= outliner_toggle_renderability_exec;
516         ot->poll= ED_operator_outliner_active;
517         
518         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
519 }
520
521 /* =============================================== */
522 /* Outliner setting toggles */
523
524 /* Toggle Expanded (Outliner) ---------------------------------------- */
525
526 static int outliner_toggle_expanded_exec(bContext *C, wmOperator *UNUSED(op))
527 {
528         SpaceOops *soops= CTX_wm_space_outliner(C);
529         ARegion *ar= CTX_wm_region(C);
530         
531         if (outliner_has_one_flag(soops, &soops->tree, TSE_CLOSED, 1))
532                 outliner_set_flag(soops, &soops->tree, TSE_CLOSED, 0);
533         else 
534                 outliner_set_flag(soops, &soops->tree, TSE_CLOSED, 1);
535         
536         ED_region_tag_redraw(ar);
537         
538         return OPERATOR_FINISHED;
539 }
540
541 void OUTLINER_OT_expanded_toggle(wmOperatorType *ot)
542 {
543         /* identifiers */
544         ot->name= "Expand/Collapse All";
545         ot->idname= "OUTLINER_OT_expanded_toggle";
546         ot->description= "Expand/Collapse all items";
547         
548         /* callbacks */
549         ot->exec= outliner_toggle_expanded_exec;
550         ot->poll= ED_operator_outliner_active;
551         
552         /* no undo or registry, UI option */
553 }
554
555 /* Toggle Selected (Outliner) ---------------------------------------- */
556
557 static int outliner_toggle_selected_exec(bContext *C, wmOperator *UNUSED(op))
558 {
559         SpaceOops *soops= CTX_wm_space_outliner(C);
560         ARegion *ar= CTX_wm_region(C);
561         Scene *scene= CTX_data_scene(C);
562         
563         if (outliner_has_one_flag(soops, &soops->tree, TSE_SELECTED, 1))
564                 outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
565         else 
566                 outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 1);
567         
568         soops->storeflag |= SO_TREESTORE_REDRAW;
569         
570         WM_event_add_notifier(C, NC_SCENE|ND_OB_SELECT, scene);
571         ED_region_tag_redraw(ar);
572         
573         return OPERATOR_FINISHED;
574 }
575
576 void OUTLINER_OT_selected_toggle(wmOperatorType *ot)
577 {
578         /* identifiers */
579         ot->name= "Toggle Selected";
580         ot->idname= "OUTLINER_OT_selected_toggle";
581         ot->description= "Toggle the Outliner selection of items";
582         
583         /* callbacks */
584         ot->exec= outliner_toggle_selected_exec;
585         ot->poll= ED_operator_outliner_active;
586         
587         /* no undo or registry, UI option */
588 }
589
590 /* ************************************************************** */
591 /* Hotkey Only Operators */
592
593 /* Show Active --------------------------------------------------- */
594
595 static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op))
596 {
597         SpaceOops *so= CTX_wm_space_outliner(C);
598         Scene *scene= CTX_data_scene(C);
599         ARegion *ar= CTX_wm_region(C);
600         View2D *v2d= &ar->v2d;
601         
602         TreeElement *te;
603         int xdelta, ytop;
604         
605         // TODO: make this get this info from context instead...
606         if (OBACT == NULL) 
607                 return OPERATOR_CANCELLED;
608         
609         te= outliner_find_id(so, &so->tree, (ID *)OBACT);
610         if (te) {
611                 /* make te->ys center of view */
612                 ytop= (int)(te->ys + (v2d->mask.ymax - v2d->mask.ymin)/2);
613                 if (ytop>0) ytop= 0;
614                 
615                 v2d->cur.ymax= (float)ytop;
616                 v2d->cur.ymin= (float)(ytop-(v2d->mask.ymax - v2d->mask.ymin));
617                 
618                 /* make te->xs ==> te->xend center of view */
619                 xdelta = (int)(te->xs - v2d->cur.xmin);
620                 v2d->cur.xmin += xdelta;
621                 v2d->cur.xmax += xdelta;
622                 
623                 so->storeflag |= SO_TREESTORE_REDRAW;
624         }
625         
626         ED_region_tag_redraw(ar);
627         
628         return OPERATOR_FINISHED;
629 }
630
631 void OUTLINER_OT_show_active(wmOperatorType *ot)
632 {
633         /* identifiers */
634         ot->name= "Show Active";
635         ot->idname= "OUTLINER_OT_show_active";
636         ot->description= "Adjust the view so that the active Object is shown centered";
637         
638         /* callbacks */
639         ot->exec= outliner_show_active_exec;
640         ot->poll= ED_operator_outliner_active;
641 }
642
643 /* View Panning --------------------------------------------------- */
644
645 static int outliner_scroll_page_exec(bContext *C, wmOperator *op)
646 {
647         ARegion *ar= CTX_wm_region(C);
648         int dy= ar->v2d.mask.ymax - ar->v2d.mask.ymin;
649         int up= 0;
650         
651         if(RNA_boolean_get(op->ptr, "up"))
652                 up= 1;
653
654         if(up == 0) dy= -dy;
655         ar->v2d.cur.ymin+= dy;
656         ar->v2d.cur.ymax+= dy;
657         
658         ED_region_tag_redraw(ar);
659         
660         return OPERATOR_FINISHED;
661 }
662
663
664 void OUTLINER_OT_scroll_page(wmOperatorType *ot)
665 {
666         /* identifiers */
667         ot->name= "Scroll Page";
668         ot->idname= "OUTLINER_OT_scroll_page";
669         ot->description= "Scroll page up or down";
670         
671         /* callbacks */
672         ot->exec= outliner_scroll_page_exec;
673         ot->poll= ED_operator_outliner_active;
674         
675         /* properties */
676         RNA_def_boolean(ot->srna, "up", 0, "Up", "Scroll up one page");
677 }
678
679 /* Search ------------------------------------------------------- */
680 // TODO: probably obsolete now with filtering?
681
682 #if 0
683
684 /* recursive helper for function below */
685 static void outliner_set_coordinates_element(SpaceOops *soops, TreeElement *te, int startx, int *starty)
686 {
687         TreeStoreElem *tselem= TREESTORE(te);
688         
689         /* store coord and continue, we need coordinates for elements outside view too */
690         te->xs= (float)startx;
691         te->ys= (float)(*starty);
692         *starty-= UI_UNIT_Y;
693         
694         if(TSELEM_OPEN(tselem,soops)) {
695                 TreeElement *ten;
696                 for(ten= te->subtree.first; ten; ten= ten->next) {
697                         outliner_set_coordinates_element(soops, ten, startx+UI_UNIT_X, starty);
698                 }
699         }
700         
701 }
702
703 /* to retrieve coordinates with redrawing the entire tree */
704 static void outliner_set_coordinates(ARegion *ar, SpaceOops *soops)
705 {
706         TreeElement *te;
707         int starty= (int)(ar->v2d.tot.ymax)-UI_UNIT_Y;
708         int startx= 0;
709         
710         for(te= soops->tree.first; te; te= te->next) {
711                 outliner_set_coordinates_element(soops, te, startx, &starty);
712         }
713 }
714
715 /* find next element that has this name */
716 static TreeElement *outliner_find_named(SpaceOops *soops, ListBase *lb, char *name, int flags, TreeElement *prev, int *prevFound)
717 {
718         TreeElement *te, *tes;
719         
720         for (te= lb->first; te; te= te->next) {
721                 int found = outliner_filter_has_name(te, name, flags);
722                 
723                 if(found) {
724                         /* name is right, but is element the previous one? */
725                         if (prev) {
726                                 if ((te != prev) && (*prevFound)) 
727                                         return te;
728                                 if (te == prev) {
729                                         *prevFound = 1;
730                                 }
731                         }
732                         else
733                                 return te;
734                 }
735                 
736                 tes= outliner_find_named(soops, &te->subtree, name, flags, prev, prevFound);
737                 if(tes) return tes;
738         }
739
740         /* nothing valid found */
741         return NULL;
742 }
743
744 static void outliner_find_panel(Scene *UNUSED(scene), ARegion *ar, SpaceOops *soops, int again, int flags) 
745 {
746         ReportList *reports = NULL; // CTX_wm_reports(C);
747         TreeElement *te= NULL;
748         TreeElement *last_find;
749         TreeStoreElem *tselem;
750         int ytop, xdelta, prevFound=0;
751         char name[sizeof(soops->search_string)];
752         
753         /* get last found tree-element based on stored search_tse */
754         last_find= outliner_find_tse(soops, &soops->search_tse);
755         
756         /* determine which type of search to do */
757         if (again && last_find) {
758                 /* no popup panel - previous + user wanted to search for next after previous */         
759                 BLI_strncpy(name, soops->search_string, sizeof(name));
760                 flags= soops->search_flags;
761                 
762                 /* try to find matching element */
763                 te= outliner_find_named(soops, &soops->tree, name, flags, last_find, &prevFound);
764                 if (te==NULL) {
765                         /* no more matches after previous, start from beginning again */
766                         prevFound= 1;
767                         te= outliner_find_named(soops, &soops->tree, name, flags, last_find, &prevFound);
768                 }
769         }
770         else {
771                 /* pop up panel - no previous, or user didn't want search after previous */
772                 name[0]= '\0';
773 // XXX          if (sbutton(name, 0, sizeof(name)-1, "Find: ") && name[0]) {
774 //                      te= outliner_find_named(soops, &soops->tree, name, flags, NULL, &prevFound);
775 //              }
776 //              else return; /* XXX RETURN! XXX */
777         }
778
779         /* do selection and reveal */
780         if (te) {
781                 tselem= TREESTORE(te);
782                 if (tselem) {
783                         /* expand branches so that it will be visible, we need to get correct coordinates */
784                         if( outliner_open_back(soops, te))
785                                 outliner_set_coordinates(ar, soops);
786                         
787                         /* deselect all visible, and select found element */
788                         outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
789                         tselem->flag |= TSE_SELECTED;
790                         
791                         /* make te->ys center of view */
792                         ytop= (int)(te->ys + (ar->v2d.mask.ymax-ar->v2d.mask.ymin)/2);
793                         if(ytop>0) ytop= 0;
794                         ar->v2d.cur.ymax= (float)ytop;
795                         ar->v2d.cur.ymin= (float)(ytop-(ar->v2d.mask.ymax-ar->v2d.mask.ymin));
796                         
797                         /* make te->xs ==> te->xend center of view */
798                         xdelta = (int)(te->xs - ar->v2d.cur.xmin);
799                         ar->v2d.cur.xmin += xdelta;
800                         ar->v2d.cur.xmax += xdelta;
801                         
802                         /* store selection */
803                         soops->search_tse= *tselem;
804                         
805                         BLI_strncpy(soops->search_string, name, sizeof(soops->search_string));
806                         soops->search_flags= flags;
807                         
808                         /* redraw */
809                         soops->storeflag |= SO_TREESTORE_REDRAW;
810                 }
811         }
812         else {
813                 /* no tree-element found */
814                 BKE_report(reports, RPT_WARNING, "Not found: %s", name);
815         }
816 }
817 #endif
818
819 /* Show One Level ----------------------------------------------- */
820
821 /* helper function for Show/Hide one level operator */
822 static void outliner_openclose_level(SpaceOops *soops, ListBase *lb, int curlevel, int level, int open)
823 {
824         TreeElement *te;
825         TreeStoreElem *tselem;
826         
827         for(te= lb->first; te; te= te->next) {
828                 tselem= TREESTORE(te);
829                 
830                 if(open) {
831                         if(curlevel<=level) tselem->flag &= ~TSE_CLOSED;
832                 }
833                 else {
834                         if(curlevel>=level) tselem->flag |= TSE_CLOSED;
835                 }
836                 
837                 outliner_openclose_level(soops, &te->subtree, curlevel+1, level, open);
838         }
839 }
840
841 static int outliner_one_level_exec(bContext *C, wmOperator *op)
842 {
843         SpaceOops *soops= CTX_wm_space_outliner(C);
844         ARegion *ar= CTX_wm_region(C);
845         int add= RNA_boolean_get(op->ptr, "open");
846         int level;
847         
848         level= outliner_has_one_flag(soops, &soops->tree, TSE_CLOSED, 1);
849         if(add==1) {
850                 if(level) outliner_openclose_level(soops, &soops->tree, 1, level, 1);
851         }
852         else {
853                 if(level==0) level= outliner_count_levels(soops, &soops->tree, 0);
854                 if(level) outliner_openclose_level(soops, &soops->tree, 1, level-1, 0);
855         }
856         
857         ED_region_tag_redraw(ar);
858         
859         return OPERATOR_FINISHED;
860 }
861
862 void OUTLINER_OT_show_one_level(wmOperatorType *ot)
863 {
864         /* identifiers */
865         ot->name= "Show/Hide One Level";
866         ot->idname= "OUTLINER_OT_show_one_level";
867         ot->description= "Expand/collapse all entries by one level";
868         
869         /* callbacks */
870         ot->exec= outliner_one_level_exec;
871         ot->poll= ED_operator_outliner_active;
872         
873         /* no undo or registry, UI option */
874         
875         /* properties */
876         RNA_def_boolean(ot->srna, "open", 1, "Open", "Expand all entries one level deep");
877 }
878
879 /* Show Hierarchy ----------------------------------------------- */
880
881 /* helper function for tree_element_shwo_hierarchy() - recursively checks whether subtrees have any objects*/
882 static int subtree_has_objects(SpaceOops *soops, ListBase *lb)
883 {
884         TreeElement *te;
885         TreeStoreElem *tselem;
886         
887         for(te= lb->first; te; te= te->next) {
888                 tselem= TREESTORE(te);
889                 if(tselem->type==0 && te->idcode==ID_OB) return 1;
890                 if( subtree_has_objects(soops, &te->subtree)) return 1;
891         }
892         return 0;
893 }
894
895 /* recursive helper function for Show Hierarchy operator */
896 static void tree_element_show_hierarchy(Scene *scene, SpaceOops *soops, ListBase *lb)
897 {
898         TreeElement *te;
899         TreeStoreElem *tselem;
900
901         /* open all object elems, close others */
902         for(te= lb->first; te; te= te->next) {
903                 tselem= TREESTORE(te);
904                 
905                 if(tselem->type==0) {
906                         if(te->idcode==ID_SCE) {
907                                 if(tselem->id!=(ID *)scene) tselem->flag |= TSE_CLOSED;
908                                         else tselem->flag &= ~TSE_CLOSED;
909                         }
910                         else if(te->idcode==ID_OB) {
911                                 if(subtree_has_objects(soops, &te->subtree)) tselem->flag &= ~TSE_CLOSED;
912                                 else tselem->flag |= TSE_CLOSED;
913                         }
914                 }
915                 else tselem->flag |= TSE_CLOSED;
916                 
917                 if(TSELEM_OPEN(tselem,soops)) tree_element_show_hierarchy(scene, soops, &te->subtree);
918         }
919 }
920
921 /* show entire object level hierarchy */
922 static int outliner_show_hierarchy_exec(bContext *C, wmOperator *UNUSED(op))
923 {
924         SpaceOops *soops= CTX_wm_space_outliner(C);
925         ARegion *ar= CTX_wm_region(C);
926         Scene *scene= CTX_data_scene(C);
927         
928         /* recursively open/close levels */
929         tree_element_show_hierarchy(scene, soops, &soops->tree);
930         
931         ED_region_tag_redraw(ar);
932         
933         return OPERATOR_FINISHED;
934 }
935
936 void OUTLINER_OT_show_hierarchy(wmOperatorType *ot)
937 {
938         /* identifiers */
939         ot->name= "Show Hierarchy";
940         ot->idname= "OUTLINER_OT_show_hierarchy";
941         ot->description= "Open all object entries and close all others";
942         
943         /* callbacks */
944         ot->exec= outliner_show_hierarchy_exec;
945         ot->poll= ED_operator_outliner_active; //  TODO: shouldn't be allowed in RNA views...
946         
947         /* no undo or registry, UI option */
948 }
949
950 /* ************************************************************** */
951 /* ANIMATO OPERATIONS */
952 /* KeyingSet and Driver Creation - Helper functions */
953
954 /* specialized poll callback for these operators to work in Datablocks view only */
955 static int ed_operator_outliner_datablocks_active(bContext *C)
956 {
957         ScrArea *sa= CTX_wm_area(C);
958         if ((sa) && (sa->spacetype==SPACE_OUTLINER)) {
959                 SpaceOops *so= CTX_wm_space_outliner(C);
960                 return (so->outlinevis == SO_DATABLOCKS);
961         }
962         return 0;
963 }
964
965
966 /* Helper func to extract an RNA path from selected tree element 
967  * NOTE: the caller must zero-out all values of the pointers that it passes here first, as
968  * this function does not do that yet 
969  */
970 static void tree_element_to_path(SpaceOops *soops, TreeElement *te, TreeStoreElem *tselem, 
971                                                         ID **id, char **path, int *array_index, short *flag, short *UNUSED(groupmode))
972 {
973         ListBase hierarchy = {NULL, NULL};
974         LinkData *ld;
975         TreeElement *tem, *temnext, *temsub;
976         TreeStoreElem *tse /* , *tsenext */ /* UNUSED */;
977         PointerRNA *ptr, *nextptr;
978         PropertyRNA *prop;
979         char *newpath=NULL;
980         
981         /* optimise tricks:
982          *      - Don't do anything if the selected item is a 'struct', but arrays are allowed
983          */
984         if (tselem->type == TSE_RNA_STRUCT)
985                 return;
986         
987         /* Overview of Algorithm:
988          *      1. Go up the chain of parents until we find the 'root', taking note of the 
989          *         levels encountered in reverse-order (i.e. items are added to the start of the list
990          *      for more convenient looping later)
991          *      2. Walk down the chain, adding from the first ID encountered 
992          *         (which will become the 'ID' for the KeyingSet Path), and build a  
993          *              path as we step through the chain
994          */
995          
996         /* step 1: flatten out hierarchy of parents into a flat chain */
997         for (tem= te->parent; tem; tem= tem->parent) {
998                 ld= MEM_callocN(sizeof(LinkData), "LinkData for tree_element_to_path()");
999                 ld->data= tem;
1000                 BLI_addhead(&hierarchy, ld);
1001         }
1002         
1003         /* step 2: step down hierarchy building the path (NOTE: addhead in previous loop was needed so that we can loop like this) */
1004         for (ld= hierarchy.first; ld; ld= ld->next) {
1005                 /* get data */
1006                 tem= (TreeElement *)ld->data;
1007                 tse= TREESTORE(tem);
1008                 ptr= &tem->rnaptr;
1009                 prop= tem->directdata;
1010                 
1011                 /* check if we're looking for first ID, or appending to path */
1012                 if (*id) {
1013                         /* just 'append' property to path 
1014                          *      - to prevent memory leaks, we must write to newpath not path, then free old path + swap them
1015                          */
1016                         if(tse->type == TSE_RNA_PROPERTY) {
1017                                 if(RNA_property_type(prop) == PROP_POINTER) {
1018                                         /* for pointer we just append property name */
1019                                         newpath= RNA_path_append(*path, ptr, prop, 0, NULL);
1020                                 }
1021                                 else if(RNA_property_type(prop) == PROP_COLLECTION) {
1022                                         char buf[128], *name;
1023                                         
1024                                         temnext= (TreeElement*)(ld->next->data);
1025                                         /* tsenext= TREESTORE(temnext); */ /* UNUSED */
1026                                         
1027                                         nextptr= &temnext->rnaptr;
1028                                         name= RNA_struct_name_get_alloc(nextptr, buf, sizeof(buf), NULL);
1029                                         
1030                                         if(name) {
1031                                                 /* if possible, use name as a key in the path */
1032                                                 newpath= RNA_path_append(*path, NULL, prop, 0, name);
1033                                                 
1034                                                 if(name != buf)
1035                                                         MEM_freeN(name);
1036                                         }
1037                                         else {
1038                                                 /* otherwise use index */
1039                                                 int index= 0;
1040                                                 
1041                                                 for(temsub=tem->subtree.first; temsub; temsub=temsub->next, index++)
1042                                                         if(temsub == temnext)
1043                                                                 break;
1044                                                 
1045                                                 newpath= RNA_path_append(*path, NULL, prop, index, NULL);
1046                                         }
1047                                         
1048                                         ld= ld->next;
1049                                 }
1050                         }
1051                         
1052                         if(newpath) {
1053                                 if (*path) MEM_freeN(*path);
1054                                 *path= newpath;
1055                                 newpath= NULL;
1056                         }
1057                 }
1058                 else {
1059                         /* no ID, so check if entry is RNA-struct, and if that RNA-struct is an ID datablock to extract info from */
1060                         if (tse->type == TSE_RNA_STRUCT) {
1061                                 /* ptr->data not ptr->id.data seems to be the one we want, since ptr->data is sometimes the owner of this ID? */
1062                                 if(RNA_struct_is_ID(ptr->type)) {
1063                                         *id= (ID *)ptr->data;
1064                                         
1065                                         /* clear path */
1066                                         if(*path) {
1067                                                 MEM_freeN(*path);
1068                                                 path= NULL;
1069                                         }
1070                                 }
1071                         }
1072                 }
1073         }
1074
1075         /* step 3: if we've got an ID, add the current item to the path */
1076         if (*id) {
1077                 /* add the active property to the path */
1078                 ptr= &te->rnaptr;
1079                 prop= te->directdata;
1080                 
1081                 /* array checks */
1082                 if (tselem->type == TSE_RNA_ARRAY_ELEM) {
1083                         /* item is part of an array, so must set the array_index */
1084                         *array_index= te->index;
1085                 }
1086                 else if (RNA_property_array_length(ptr, prop)) {
1087                         /* entire array was selected, so keyframe all */
1088                         *flag |= KSP_FLAG_WHOLE_ARRAY;
1089                 }
1090                 
1091                 /* path */
1092                 newpath= RNA_path_append(*path, NULL, prop, 0, NULL);
1093                 if (*path) MEM_freeN(*path);
1094                 *path= newpath;
1095         }
1096
1097         /* free temp data */
1098         BLI_freelistN(&hierarchy);
1099 }
1100
1101 /* =============================================== */
1102 /* Driver Operations */
1103
1104 /* These operators are only available in databrowser mode for now, as
1105  * they depend on having RNA paths and/or hierarchies available.
1106  */
1107 enum {
1108         DRIVERS_EDITMODE_ADD    = 0,
1109         DRIVERS_EDITMODE_REMOVE,
1110 } /*eDrivers_EditModes*/;
1111
1112 /* Utilities ---------------------------------- */ 
1113
1114 /* Recursively iterate over tree, finding and working on selected items */
1115 static void do_outliner_drivers_editop(SpaceOops *soops, ListBase *tree, ReportList *reports, short mode)
1116 {
1117         TreeElement *te;
1118         TreeStoreElem *tselem;
1119         
1120         for (te= tree->first; te; te=te->next) {
1121                 tselem= TREESTORE(te);
1122                 
1123                 /* if item is selected, perform operation */
1124                 if (tselem->flag & TSE_SELECTED) {
1125                         ID *id= NULL;
1126                         char *path= NULL;
1127                         int array_index= 0;
1128                         short flag= 0;
1129                         short groupmode= KSP_GROUP_KSNAME;
1130                         
1131                         /* check if RNA-property described by this selected element is an animateable prop */
1132                         if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) && RNA_property_animateable(&te->rnaptr, te->directdata)) {
1133                                 /* get id + path + index info from the selected element */
1134                                 tree_element_to_path(soops, te, tselem, 
1135                                                 &id, &path, &array_index, &flag, &groupmode);
1136                         }
1137                         
1138                         /* only if ID and path were set, should we perform any actions */
1139                         if (id && path) {
1140                                 short dflags = CREATEDRIVER_WITH_DEFAULT_DVAR;
1141                                 int arraylen = 1;
1142                                 
1143                                 /* array checks */
1144                                 if (flag & KSP_FLAG_WHOLE_ARRAY) {
1145                                         /* entire array was selected, so add drivers for all */
1146                                         arraylen= RNA_property_array_length(&te->rnaptr, te->directdata);
1147                                 }
1148                                 else
1149                                         arraylen= array_index;
1150                                 
1151                                 /* we should do at least one step */
1152                                 if (arraylen == array_index)
1153                                         arraylen++;
1154                                 
1155                                 /* for each array element we should affect, add driver */
1156                                 for (; array_index < arraylen; array_index++) {
1157                                         /* action depends on mode */
1158                                         switch (mode) {
1159                                                 case DRIVERS_EDITMODE_ADD:
1160                                                 {
1161                                                         /* add a new driver with the information obtained (only if valid) */
1162                                                         ANIM_add_driver(reports, id, path, array_index, dflags, DRIVER_TYPE_PYTHON);
1163                                                 }
1164                                                         break;
1165                                                 case DRIVERS_EDITMODE_REMOVE:
1166                                                 {
1167                                                         /* remove driver matching the information obtained (only if valid) */
1168                                                         ANIM_remove_driver(reports, id, path, array_index, dflags);
1169                                                 }
1170                                                         break;
1171                                         }
1172                                 }
1173                                 
1174                                 /* free path, since it had to be generated */
1175                                 MEM_freeN(path);
1176                         }
1177                         
1178                         
1179                 }
1180                 
1181                 /* go over sub-tree */
1182                 if (TSELEM_OPEN(tselem,soops))
1183                         do_outliner_drivers_editop(soops, &te->subtree, reports, mode);
1184         }
1185 }
1186
1187 /* Add Operator ---------------------------------- */
1188
1189 static int outliner_drivers_addsel_exec(bContext *C, wmOperator *op)
1190 {
1191         SpaceOops *soutliner= CTX_wm_space_outliner(C);
1192         
1193         /* check for invalid states */
1194         if (soutliner == NULL)
1195                 return OPERATOR_CANCELLED;
1196         
1197         /* recursively go into tree, adding selected items */
1198         do_outliner_drivers_editop(soutliner, &soutliner->tree, op->reports, DRIVERS_EDITMODE_ADD);
1199         
1200         /* send notifiers */
1201         WM_event_add_notifier(C, NC_ANIMATION|ND_FCURVES_ORDER, NULL); // XXX
1202         
1203         return OPERATOR_FINISHED;
1204 }
1205
1206 void OUTLINER_OT_drivers_add_selected(wmOperatorType *ot)
1207 {
1208         /* api callbacks */
1209         ot->idname= "OUTLINER_OT_drivers_add_selected";
1210         ot->name= "Add Drivers for Selected";
1211         ot->description= "Add drivers to selected items";
1212         
1213         /* api callbacks */
1214         ot->exec= outliner_drivers_addsel_exec;
1215         ot->poll= ed_operator_outliner_datablocks_active;
1216         
1217         /* flags */
1218         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1219 }
1220
1221
1222 /* Remove Operator ---------------------------------- */
1223
1224 static int outliner_drivers_deletesel_exec(bContext *C, wmOperator *op)
1225 {
1226         SpaceOops *soutliner= CTX_wm_space_outliner(C);
1227         
1228         /* check for invalid states */
1229         if (soutliner == NULL)
1230                 return OPERATOR_CANCELLED;
1231         
1232         /* recursively go into tree, adding selected items */
1233         do_outliner_drivers_editop(soutliner, &soutliner->tree, op->reports, DRIVERS_EDITMODE_REMOVE);
1234         
1235         /* send notifiers */
1236         WM_event_add_notifier(C, ND_KEYS, NULL); // XXX
1237         
1238         return OPERATOR_FINISHED;
1239 }
1240
1241 void OUTLINER_OT_drivers_delete_selected(wmOperatorType *ot)
1242 {
1243         /* identifiers */
1244         ot->idname= "OUTLINER_OT_drivers_delete_selected";
1245         ot->name= "Delete Drivers for Selected";
1246         ot->description= "Delete drivers assigned to selected items";
1247         
1248         /* api callbacks */
1249         ot->exec= outliner_drivers_deletesel_exec;
1250         ot->poll= ed_operator_outliner_datablocks_active;
1251         
1252         /* flags */
1253         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1254 }
1255
1256 /* =============================================== */
1257 /* Keying Set Operations */
1258
1259 /* These operators are only available in databrowser mode for now, as
1260  * they depend on having RNA paths and/or hierarchies available.
1261  */
1262 enum {
1263         KEYINGSET_EDITMODE_ADD  = 0,
1264         KEYINGSET_EDITMODE_REMOVE,
1265 } /*eKeyingSet_EditModes*/;
1266
1267 /* Utilities ---------------------------------- */ 
1268  
1269 /* find the 'active' KeyingSet, and add if not found (if adding is allowed) */
1270 // TODO: should this be an API func?
1271 static KeyingSet *verify_active_keyingset(Scene *scene, short add)
1272 {
1273         KeyingSet *ks= NULL;
1274         
1275         /* sanity check */
1276         if (scene == NULL)
1277                 return NULL;
1278         
1279         /* try to find one from scene */
1280         if (scene->active_keyingset > 0)
1281                 ks= BLI_findlink(&scene->keyingsets, scene->active_keyingset-1);
1282                 
1283         /* add if none found */
1284         // XXX the default settings have yet to evolve
1285         if ((add) && (ks==NULL)) {
1286                 ks= BKE_keyingset_add(&scene->keyingsets, NULL, KEYINGSET_ABSOLUTE, 0);
1287                 scene->active_keyingset= BLI_countlist(&scene->keyingsets);
1288         }
1289         
1290         return ks;
1291 }
1292
1293 /* Recursively iterate over tree, finding and working on selected items */
1294 static void do_outliner_keyingset_editop(SpaceOops *soops, KeyingSet *ks, ListBase *tree, short mode)
1295 {
1296         TreeElement *te;
1297         TreeStoreElem *tselem;
1298         
1299         for (te= tree->first; te; te=te->next) {
1300                 tselem= TREESTORE(te);
1301                 
1302                 /* if item is selected, perform operation */
1303                 if (tselem->flag & TSE_SELECTED) {
1304                         ID *id= NULL;
1305                         char *path= NULL;
1306                         int array_index= 0;
1307                         short flag= 0;
1308                         short groupmode= KSP_GROUP_KSNAME;
1309                         
1310                         /* check if RNA-property described by this selected element is an animateable prop */
1311                         if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) && RNA_property_animateable(&te->rnaptr, te->directdata)) {
1312                                 /* get id + path + index info from the selected element */
1313                                 tree_element_to_path(soops, te, tselem, 
1314                                                 &id, &path, &array_index, &flag, &groupmode);
1315                         }
1316                         
1317                         /* only if ID and path were set, should we perform any actions */
1318                         if (id && path) {
1319                                 /* action depends on mode */
1320                                 switch (mode) {
1321                                         case KEYINGSET_EDITMODE_ADD:
1322                                         {
1323                                                 /* add a new path with the information obtained (only if valid) */
1324                                                 // TODO: what do we do with group name? for now, we don't supply one, and just let this use the KeyingSet name
1325                                                 BKE_keyingset_add_path(ks, id, NULL, path, array_index, flag, groupmode);
1326                                                 ks->active_path= BLI_countlist(&ks->paths);
1327                                         }
1328                                                 break;
1329                                         case KEYINGSET_EDITMODE_REMOVE:
1330                                         {
1331                                                 /* find the relevant path, then remove it from the KeyingSet */
1332                                                 KS_Path *ksp= BKE_keyingset_find_path(ks, id, NULL, path, array_index, groupmode);
1333                                                 
1334                                                 if (ksp) {
1335                                                         /* free path's data */
1336                                                         BKE_keyingset_free_path(ks, ksp);
1337
1338                                                         ks->active_path= 0;
1339                                                 }
1340                                         }
1341                                                 break;
1342                                 }
1343                                 
1344                                 /* free path, since it had to be generated */
1345                                 MEM_freeN(path);
1346                         }
1347                 }
1348                 
1349                 /* go over sub-tree */
1350                 if (TSELEM_OPEN(tselem,soops))
1351                         do_outliner_keyingset_editop(soops, ks, &te->subtree, mode);
1352         }
1353 }
1354
1355 /* Add Operator ---------------------------------- */
1356
1357 static int outliner_keyingset_additems_exec(bContext *C, wmOperator *op)
1358 {
1359         SpaceOops *soutliner= CTX_wm_space_outliner(C);
1360         Scene *scene= CTX_data_scene(C);
1361         KeyingSet *ks= verify_active_keyingset(scene, 1);
1362         
1363         /* check for invalid states */
1364         if (ks == NULL) {
1365                 BKE_report(op->reports, RPT_ERROR, "Operation requires an Active Keying Set");
1366                 return OPERATOR_CANCELLED;
1367         }
1368         if (soutliner == NULL)
1369                 return OPERATOR_CANCELLED;
1370         
1371         /* recursively go into tree, adding selected items */
1372         do_outliner_keyingset_editop(soutliner, ks, &soutliner->tree, KEYINGSET_EDITMODE_ADD);
1373         
1374         /* send notifiers */
1375         WM_event_add_notifier(C, NC_SCENE|ND_KEYINGSET, NULL);
1376         
1377         return OPERATOR_FINISHED;
1378 }
1379
1380 void OUTLINER_OT_keyingset_add_selected(wmOperatorType *ot)
1381 {
1382         /* identifiers */
1383         ot->idname= "OUTLINER_OT_keyingset_add_selected";
1384         ot->name= "Keying Set Add Selected";
1385         ot->description= "Add selected items (blue-grey rows) to active Keying Set";
1386         
1387         /* api callbacks */
1388         ot->exec= outliner_keyingset_additems_exec;
1389         ot->poll= ed_operator_outliner_datablocks_active;
1390         
1391         /* flags */
1392         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1393 }
1394
1395
1396 /* Remove Operator ---------------------------------- */
1397
1398 static int outliner_keyingset_removeitems_exec(bContext *C, wmOperator *UNUSED(op))
1399 {
1400         SpaceOops *soutliner= CTX_wm_space_outliner(C);
1401         Scene *scene= CTX_data_scene(C);
1402         KeyingSet *ks= verify_active_keyingset(scene, 1);
1403         
1404         /* check for invalid states */
1405         if (soutliner == NULL)
1406                 return OPERATOR_CANCELLED;
1407         
1408         /* recursively go into tree, adding selected items */
1409         do_outliner_keyingset_editop(soutliner, ks, &soutliner->tree, KEYINGSET_EDITMODE_REMOVE);
1410         
1411         /* send notifiers */
1412         WM_event_add_notifier(C, NC_SCENE|ND_KEYINGSET, NULL);
1413         
1414         return OPERATOR_FINISHED;
1415 }
1416
1417 void OUTLINER_OT_keyingset_remove_selected(wmOperatorType *ot)
1418 {
1419         /* identifiers */
1420         ot->idname= "OUTLINER_OT_keyingset_remove_selected";
1421         ot->name= "Keying Set Remove Selected";
1422         ot->description = "Remove selected items (blue-grey rows) from active Keying Set";
1423         
1424         /* api callbacks */
1425         ot->exec= outliner_keyingset_removeitems_exec;
1426         ot->poll= ed_operator_outliner_datablocks_active;
1427         
1428         /* flags */
1429         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1430 }
1431
1432 /* ******************** Parent Drop Operator *********************** */
1433
1434 static int parent_drop_exec(bContext *C, wmOperator *op)
1435 {
1436         Object *par = NULL;
1437         int partype = -1;
1438         char parname[32];
1439
1440         partype= RNA_enum_get(op->ptr, "type");
1441         RNA_string_get(op->ptr, "parent", parname);
1442         par= (Object *)find_id("OB", parname);
1443
1444         ED_object_parent_set(C, op, par, partype);
1445
1446         return OPERATOR_FINISHED;
1447 }
1448
1449 /* Used for drag and drop parenting */
1450 TreeElement *outliner_dropzone_parent(bContext *C, wmEvent *event, TreeElement *te, float *fmval)
1451 {
1452         SpaceOops *soops= CTX_wm_space_outliner(C);
1453         TreeStoreElem *tselem= TREESTORE(te);
1454
1455         if ((fmval[1] > te->ys) && (fmval[1] < (te->ys + UI_UNIT_Y))) {
1456                 /* name and first icon */
1457                 if ((fmval[0] > te->xs + UI_UNIT_X) && (fmval[0] < te->xend)) {
1458                         /* always makes active object */
1459                         if (te->idcode == ID_OB) {
1460                                 return te;
1461                         }
1462                         else {
1463                                 return NULL;
1464                         }
1465                 }
1466         }
1467
1468         /* Not it.  Let's look at its children. */
1469         if ((tselem->flag & TSE_CLOSED)==0 && (te->subtree.first)) {
1470                 for (te = te->subtree.first; te; te = te->next) {
1471                         TreeElement *te_valid;
1472                         te_valid= outliner_dropzone_parent(C, event, te, fmval);
1473                         if (te_valid) return te_valid;
1474                 }
1475         }
1476         return NULL;
1477 }
1478
1479 static int parent_drop_invoke(bContext *C, wmOperator *op, wmEvent *event)
1480 {
1481         Object *par= NULL;
1482         Object *ob= NULL;
1483         SpaceOops *soops= CTX_wm_space_outliner(C);
1484         ARegion *ar= CTX_wm_region(C);
1485         Scene *scene= CTX_data_scene(C);
1486         TreeElement *te= NULL;
1487         TreeElement *te_found= NULL;
1488         char childname[MAX_ID_NAME];
1489         char parname[MAX_ID_NAME];
1490         int partype= 0;
1491         float fmval[2];
1492
1493         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
1494
1495         /* Find object hovered over */
1496         for (te= soops->tree.first; te; te= te->next) {
1497                 te_found= outliner_dropzone_parent(C, event, te, fmval);
1498                 if (te_found) break;
1499         }
1500
1501         if(te_found) {
1502                 RNA_string_set(op->ptr, "parent", te_found->name);
1503                 /* Identify parent and child */
1504                 RNA_string_get(op->ptr, "child", childname);
1505                 ob= (Object *)find_id("OB", childname);
1506                 RNA_string_get(op->ptr, "parent", parname);
1507                 par= (Object *)find_id("OB", parname);
1508                 
1509                 if (ELEM(NULL, ob, par)) {
1510                         if (par == NULL) printf("par==NULL\n");
1511                         return OPERATOR_CANCELLED;
1512                 }
1513                 if (ob == par) {
1514                         return OPERATOR_CANCELLED;
1515                 }
1516                 
1517                 /* check dragged object (child) is active */
1518                 if (ob != CTX_data_active_object(C))
1519                         ED_base_object_select(object_in_scene(ob, scene), BA_SELECT);
1520                 
1521                 if ((par->type != OB_ARMATURE) && (par->type != OB_CURVE) && (par->type != OB_LATTICE)) {
1522                         ED_object_parent_set(C, op, par, partype);
1523                 }
1524                 else {
1525                         /* Menu creation */
1526                         uiPopupMenu *pup= uiPupMenuBegin(C, "Set Parent To", ICON_NONE);
1527                         uiLayout *layout= uiPupMenuLayout(pup);
1528                         
1529                         PointerRNA ptr;
1530                         
1531                         WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop");
1532                         RNA_string_set(&ptr, "parent", parname);
1533                         RNA_string_set(&ptr, "child", childname);
1534                         RNA_enum_set(&ptr, "type", PAR_OBJECT);
1535                         /* Cannot use uiItemEnumO()... have multiple properties to set. */
1536                         uiItemFullO(layout, "OUTLINER_OT_parent_drop", "Object", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0);
1537                         
1538                         /* par becomes parent, make the associated menus */
1539                         if (par->type==OB_ARMATURE) {
1540                                 WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop");
1541                                 RNA_string_set(&ptr, "parent", parname);
1542                                 RNA_string_set(&ptr, "child", childname);
1543                                 RNA_enum_set(&ptr, "type", PAR_ARMATURE);
1544                                 uiItemFullO(layout, "OUTLINER_OT_parent_drop", "Armature Deform", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0);
1545                                 
1546                                 WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop");
1547                                 RNA_string_set(&ptr, "parent", parname);
1548                                 RNA_string_set(&ptr, "child", childname);
1549                                 RNA_enum_set(&ptr, "type", PAR_ARMATURE_NAME);
1550                                 uiItemFullO(layout, "OUTLINER_OT_parent_drop", "   With Empty Groups", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0);
1551                                 
1552                                 WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop");
1553                                 RNA_string_set(&ptr, "parent", parname);
1554                                 RNA_string_set(&ptr, "child", childname);
1555                                 RNA_enum_set(&ptr, "type", PAR_ARMATURE_ENVELOPE);
1556                                 uiItemFullO(layout, "OUTLINER_OT_parent_drop", "   With Envelope Weights", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0);
1557                                 
1558                                 WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop");
1559                                 RNA_string_set(&ptr, "parent", parname);
1560                                 RNA_string_set(&ptr, "child", childname);
1561                                 RNA_enum_set(&ptr, "type", PAR_ARMATURE_AUTO);
1562                                 uiItemFullO(layout, "OUTLINER_OT_parent_drop", "   With Automatic Weights", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0);
1563                                 
1564                                 WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop");
1565                                 RNA_string_set(&ptr, "parent", parname);
1566                                 RNA_string_set(&ptr, "child", childname);
1567                                 RNA_enum_set(&ptr, "type", PAR_BONE);
1568                                 uiItemFullO(layout, "OUTLINER_OT_parent_drop", "Bone", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0);
1569                         }
1570                         else if (par->type==OB_CURVE) {
1571                                 WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop");
1572                                 RNA_string_set(&ptr, "parent", parname);
1573                                 RNA_string_set(&ptr, "child", childname);
1574                                 RNA_enum_set(&ptr, "type", PAR_CURVE);
1575                                 uiItemFullO(layout, "OUTLINER_OT_parent_drop", "Curve Deform", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0);
1576                                 
1577                                 WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop");
1578                                 RNA_string_set(&ptr, "parent", parname);
1579                                 RNA_string_set(&ptr, "child", childname);
1580                                 RNA_enum_set(&ptr, "type", PAR_FOLLOW);
1581                                 uiItemFullO(layout, "OUTLINER_OT_parent_drop", "Follow Path", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0);
1582                                 
1583                                 WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop");
1584                                 RNA_string_set(&ptr, "parent", parname);
1585                                 RNA_string_set(&ptr, "child", childname);
1586                                 RNA_enum_set(&ptr, "type", PAR_PATH_CONST);
1587                                 uiItemFullO(layout, "OUTLINER_OT_parent_drop", "Path Constraint", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0);
1588                         }
1589                         else if (par->type == OB_LATTICE) {
1590                                 WM_operator_properties_create(&ptr, "OUTLINER_OT_parent_drop");
1591                                 RNA_string_set(&ptr, "parent", parname);
1592                                 RNA_string_set(&ptr, "child", childname);
1593                                 RNA_enum_set(&ptr, "type", PAR_LATTICE);
1594                                 uiItemFullO(layout, "OUTLINER_OT_parent_drop", "Lattice Deform", 0, ptr.data, WM_OP_EXEC_DEFAULT, 0);
1595                         }
1596                         
1597                         uiPupMenuEnd(C, pup);
1598                         
1599                         return OPERATOR_CANCELLED;
1600                 }
1601         }
1602         else {
1603                 return OPERATOR_CANCELLED;
1604         }
1605
1606         return OPERATOR_FINISHED;
1607 }
1608
1609 void OUTLINER_OT_parent_drop(wmOperatorType *ot)
1610 {
1611         /* identifiers */
1612         ot->name= "Drop to Set Parent";
1613         ot->description = "Drag to parent in Outliner";
1614         ot->idname= "OUTLINER_OT_parent_drop";
1615
1616         /* api callbacks */
1617         ot->invoke= parent_drop_invoke;
1618         ot->exec= parent_drop_exec;
1619
1620         ot->poll= ED_operator_outliner_active;
1621
1622         /* flags */
1623         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1624
1625         /* properties */
1626         RNA_def_string(ot->srna, "child", "Object", MAX_ID_NAME, "Child", "Child Object");
1627         RNA_def_string(ot->srna, "parent", "Object", MAX_ID_NAME, "Parent", "Parent Object");
1628         RNA_def_enum(ot->srna, "type", prop_make_parent_types, 0, "Type", "");
1629 }
1630
1631 int outliner_dropzone_parent_clear(bContext *C, wmEvent *event, TreeElement *te, float *fmval)
1632 {
1633         SpaceOops *soops= CTX_wm_space_outliner(C);
1634         TreeStoreElem *tselem= TREESTORE(te);
1635
1636         /* Check for row */
1637         if ((fmval[1] > te->ys) && (fmval[1] < (te->ys + UI_UNIT_Y))) {
1638                 /* Ignore drop on scene tree elements */
1639                 if ((fmval[0] > te->xs + UI_UNIT_X) && (fmval[0] < te->xend)) {
1640                         if ((te->idcode == ID_SCE) && 
1641                                 !ELEM3(tselem->type, TSE_R_LAYER_BASE, TSE_R_LAYER, TSE_R_PASS))
1642                         {
1643                                 return 0;
1644                         }
1645                         // Other codes to ignore?
1646                 }
1647                 
1648                 /* Left or right of: (+), first icon, and name */
1649                 if ((fmval[0] < (te->xs + UI_UNIT_X)) || (fmval[0] > te->xend)) {
1650                         return 1;
1651                 }
1652                 else if (te->idcode != ID_OB) {
1653                         return 1;
1654                 }
1655                 
1656                 return 0;               // ID_OB, but mouse in undefined dropzone.
1657         }
1658
1659         /* Not this row.  Let's look at its children. */
1660         if ((tselem->flag & TSE_CLOSED)==0 && (te->subtree.first)) {
1661                 for (te = te->subtree.first; te; te = te->next) {
1662                         if (outliner_dropzone_parent_clear(C, event, te, fmval)) 
1663                                 return 1;
1664                 }
1665         }
1666         return 0;
1667 }
1668
1669 static int parent_clear_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1670 {
1671         Scene *scene= CTX_data_scene(C);
1672         Object *ob= NULL;
1673         char obname[MAX_ID_NAME];
1674
1675         RNA_string_get(op->ptr, "dragged_obj", obname);
1676         ob= (Object *)find_id("OB", obname);
1677
1678         /* check dragged object (child) is active */
1679         if (ob != CTX_data_active_object(C))
1680                 ED_base_object_select(object_in_scene(ob, scene), BA_SELECT);
1681
1682         ED_object_parent_clear(C, RNA_enum_get(op->ptr, "type"));
1683
1684         return OPERATOR_FINISHED;
1685 }
1686
1687 void OUTLINER_OT_parent_clear(wmOperatorType *ot)
1688 {
1689         /* identifiers */
1690         ot->name= "Drop to Clear Parent";
1691         ot->description = "Drag to clear parent in Outliner";
1692         ot->idname= "OUTLINER_OT_parent_clear";
1693
1694         /* api callbacks */
1695         ot->invoke= parent_clear_invoke;
1696
1697         ot->poll= ED_operator_outliner_active;
1698
1699         /* flags */
1700         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1701
1702         /* properties */
1703         RNA_def_string(ot->srna, "dragged_obj", "Object", MAX_ID_NAME, "Child", "Child Object");
1704         RNA_def_enum(ot->srna, "type", prop_clear_parent_types, 0, "Type", "");
1705 }