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