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