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