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