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