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