Merge branch 'master' into blender2.8
[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 <string.h>
33
34 #include "MEM_guardedalloc.h"
35
36 #include "DNA_anim_types.h"
37 #include "DNA_group_types.h"
38 #include "DNA_ID.h"
39 #include "DNA_scene_types.h"
40 #include "DNA_object_types.h"
41 #include "DNA_material_types.h"
42
43 #include "BLI_blenlib.h"
44 #include "BLI_utildefines.h"
45 #include "BLI_path_util.h"
46 #include "BLI_mempool.h"
47 #include "BLI_stack.h"
48 #include "BLI_string.h"
49
50 #include "BLT_translation.h"
51
52 #include "BKE_animsys.h"
53 #include "BKE_context.h"
54 #include "BKE_global.h"
55 #include "BKE_idcode.h"
56 #include "BKE_layer.h"
57 #include "BKE_library.h"
58 #include "BKE_library_query.h"
59 #include "BKE_library_remap.h"
60 #include "BKE_main.h"
61 #include "BKE_outliner_treehash.h"
62 #include "BKE_report.h"
63 #include "BKE_scene.h"
64 #include "BKE_material.h"
65 #include "BKE_group.h"
66
67 #include "DEG_depsgraph_build.h"
68
69 #include "../blenloader/BLO_readfile.h"
70
71 #include "ED_object.h"
72 #include "ED_outliner.h"
73 #include "ED_screen.h"
74 #include "ED_keyframing.h"
75 #include "ED_armature.h"
76
77 #include "WM_api.h"
78 #include "WM_types.h"
79
80 #include "UI_interface.h"
81 #include "UI_resources.h"
82 #include "UI_view2d.h"
83
84 #include "RNA_access.h"
85 #include "RNA_define.h"
86 #include "RNA_enum_types.h"
87
88 #include "GPU_material.h"
89
90 #include "outliner_intern.h"
91
92 /* ************************************************************** */
93 /* Unused Utilities */
94 // XXX: where to place these?
95
96 /* This is not used anywhere at the moment */
97 #if 0
98 static void outliner_open_reveal(SpaceOops *soops, ListBase *lb, TreeElement *teFind, int *found)
99 {
100         TreeElement *te;
101         TreeStoreElem *tselem;
102         
103         for (te = lb->first; te; te = te->next) {
104                 /* check if this tree-element was the one we're seeking */
105                 if (te == teFind) {
106                         *found = 1;
107                         return;
108                 }
109                 
110                 /* try to see if sub-tree contains it then */
111                 outliner_open_reveal(soops, &te->subtree, teFind, found);
112                 if (*found) {
113                         tselem = TREESTORE(te);
114                         if (tselem->flag & TSE_CLOSED) 
115                                 tselem->flag &= ~TSE_CLOSED;
116                         return;
117                 }
118         }
119 }
120 #endif
121
122 static TreeElement *outliner_dropzone_element(TreeElement *te, const float fmval[2], const bool children)
123 {
124         if ((fmval[1] > te->ys) && (fmval[1] < (te->ys + UI_UNIT_Y))) {
125                 /* name and first icon */
126                 if ((fmval[0] > te->xs + UI_UNIT_X) && (fmval[0] < te->xend))
127                         return te;
128         }
129         /* Not it.  Let's look at its children. */
130         if (children && (TREESTORE(te)->flag & TSE_CLOSED) == 0 && (te->subtree.first)) {
131                 for (te = te->subtree.first; te; te = te->next) {
132                         TreeElement *te_valid = outliner_dropzone_element(te, fmval, children);
133                         if (te_valid)
134                                 return te_valid;
135                 }
136         }
137         return NULL;
138 }
139
140 /* Used for drag and drop parenting */
141 TreeElement *outliner_dropzone_find(const SpaceOops *soops, const float fmval[2], const bool children)
142 {
143         TreeElement *te;
144
145         for (te = soops->tree.first; te; te = te->next) {
146                 TreeElement *te_valid = outliner_dropzone_element(te, fmval, children);
147                 if (te_valid)
148                         return te_valid;
149         }
150         return NULL;
151 }
152
153
154 /* ************************************************************** */
155
156 /* Highlight --------------------------------------------------- */
157
158 static int outliner_highlight_update(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
159 {
160         ARegion *ar = CTX_wm_region(C);
161         SpaceOops *soops = CTX_wm_space_outliner(C);
162         const float my = UI_view2d_region_to_view_y(&ar->v2d, event->mval[1]);
163
164         TreeElement *hovered_te = outliner_find_item_at_y(soops, &soops->tree, my);
165         bool changed = false;
166
167         if (!hovered_te || !(hovered_te->store_elem->flag & TSE_HIGHLIGHTED)) {
168                 changed = outliner_set_flag(&soops->tree, TSE_HIGHLIGHTED, false);
169                 if (hovered_te) {
170                         hovered_te->store_elem->flag |= TSE_HIGHLIGHTED;
171                         changed = true;
172                 }
173         }
174
175         if (changed) {
176                 soops->storeflag |= SO_TREESTORE_REDRAW; /* only needs to redraw, no rebuild */
177                 ED_region_tag_redraw(ar);
178         }
179
180         return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
181 }
182
183 void OUTLINER_OT_highlight_update(wmOperatorType *ot)
184 {
185         ot->name = "Update Highlight";
186         ot->idname = "OUTLINER_OT_highlight_update";
187         ot->description = "Update the item highlight based on the current mouse position";
188
189         ot->invoke = outliner_highlight_update;
190
191         ot->poll = ED_operator_outliner_active;
192 }
193
194 /* Toggle Open/Closed ------------------------------------------- */
195
196 static int do_outliner_item_openclose(bContext *C, SpaceOops *soops, TreeElement *te, const bool all, const float mval[2])
197 {
198         
199         if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
200                 TreeStoreElem *tselem = TREESTORE(te);
201                 
202                 /* all below close/open? */
203                 if (all) {
204                         tselem->flag &= ~TSE_CLOSED;
205                         outliner_set_flag(&te->subtree, TSE_CLOSED, !outliner_has_one_flag(&te->subtree, TSE_CLOSED, 1));
206                 }
207                 else {
208                         if (tselem->flag & TSE_CLOSED) tselem->flag &= ~TSE_CLOSED;
209                         else tselem->flag |= TSE_CLOSED;
210                 }
211                 
212                 return 1;
213         }
214         
215         for (te = te->subtree.first; te; te = te->next) {
216                 if (do_outliner_item_openclose(C, soops, te, all, mval)) 
217                         return 1;
218         }
219         return 0;
220         
221 }
222
223 /* event can enterkey, then it opens/closes */
224 static int outliner_item_openclose(bContext *C, wmOperator *op, const wmEvent *event)
225 {
226         ARegion *ar = CTX_wm_region(C);
227         SpaceOops *soops = CTX_wm_space_outliner(C);
228         TreeElement *te;
229         float fmval[2];
230         const bool all = RNA_boolean_get(op->ptr, "all");
231         
232         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
233         
234         for (te = soops->tree.first; te; te = te->next) {
235                 if (do_outliner_item_openclose(C, soops, te, all, fmval)) 
236                         break;
237         }
238
239         ED_region_tag_redraw(ar);
240         
241         return OPERATOR_FINISHED;
242 }
243
244 void OUTLINER_OT_item_openclose(wmOperatorType *ot)
245 {
246         ot->name = "Open/Close Item";
247         ot->idname = "OUTLINER_OT_item_openclose";
248         ot->description = "Toggle whether item under cursor is enabled or closed";
249         
250         ot->invoke = outliner_item_openclose;
251         
252         ot->poll = ED_operator_outliner_active;
253         
254         RNA_def_boolean(ot->srna, "all", 1, "All", "Close or open all items");
255 }
256
257 /* Rename --------------------------------------------------- */
258
259 static void do_item_rename(const Scene *scene, ARegion *ar, TreeElement *te, TreeStoreElem *tselem,
260                            ReportList *reports)
261 {
262         bool add_textbut = false;
263
264         /* can't rename rna datablocks entries or listbases */
265         if (ELEM(tselem->type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE)) {
266                 /* do nothing */;
267         }
268         else if (ELEM(tselem->type, TSE_ANIM_DATA, TSE_NLA, TSE_DEFGROUP_BASE, TSE_CONSTRAINT_BASE, TSE_MODIFIER_BASE,
269                       TSE_DRIVER_BASE, TSE_POSE_BASE, TSE_POSEGRP_BASE, TSE_R_LAYER_BASE, TSE_R_PASS))
270         {
271                 BKE_report(reports, RPT_WARNING, "Cannot edit builtin name");
272         }
273         else if (ELEM(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) {
274                 BKE_report(reports, RPT_WARNING, "Cannot edit sequence name");
275         }
276         else if (ELEM(tselem->type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION)) {
277                 SceneCollection *master = BKE_collection_master(scene);
278
279                 if ((tselem->type == TSE_SCENE_COLLECTION && te->directdata == master) ||
280                     (((LayerCollection *)te->directdata)->scene_collection == master))
281                 {
282                         BKE_report(reports, RPT_WARNING, "Cannot edit name of master collection");
283                 }
284                 else {
285                         add_textbut = true;
286                 }
287         }
288         else if (ID_IS_LINKED(tselem->id)) {
289                 BKE_report(reports, RPT_WARNING, "Cannot edit external libdata");
290         }
291         else if (te->idcode == ID_LI && ((Library *)tselem->id)->parent) {
292                 BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library");
293         }
294         else {
295                 add_textbut = true;
296         }
297
298         if (add_textbut) {
299                 tselem->flag |= TSE_TEXTBUT;
300                 ED_region_tag_redraw(ar);
301         }
302 }
303
304 void item_rename_cb(
305         bContext *C, ReportList *reports, Scene *scene, TreeElement *te,
306         TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
307 {
308         ARegion *ar = CTX_wm_region(C);
309         do_item_rename(scene, ar, te, tselem, reports);
310 }
311
312 static int do_outliner_item_rename(const Scene *scene, ReportList *reports, ARegion *ar, TreeElement *te,
313                                    const float mval[2])
314 {
315         if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
316                 TreeStoreElem *tselem = TREESTORE(te);
317                 
318                 /* click on name */
319                 if (mval[0] > te->xs + UI_UNIT_X * 2 && mval[0] < te->xend) {
320                         do_item_rename(scene, ar, te, tselem, reports);
321                         return 1;
322                 }
323                 return 0;
324         }
325         
326         for (te = te->subtree.first; te; te = te->next) {
327                 if (do_outliner_item_rename(scene, reports, ar, te, mval)) return 1;
328         }
329         return 0;
330 }
331
332 static int outliner_item_rename(bContext *C, wmOperator *op, const wmEvent *event)
333 {
334         ARegion *ar = CTX_wm_region(C);
335         SpaceOops *soops = CTX_wm_space_outliner(C);
336         TreeElement *te;
337         float fmval[2];
338         bool changed = false;
339         
340         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
341         
342         for (te = soops->tree.first; te; te = te->next) {
343                 if (do_outliner_item_rename(CTX_data_scene(C), op->reports, ar, te, fmval)) {
344                         changed = true;
345                         break;
346                 }
347         }
348         
349         return changed ? OPERATOR_FINISHED : OPERATOR_PASS_THROUGH;
350 }
351
352
353 void OUTLINER_OT_item_rename(wmOperatorType *ot)
354 {
355         ot->name = "Rename Item";
356         ot->idname = "OUTLINER_OT_item_rename";
357         ot->description = "Rename item under cursor";
358         
359         ot->invoke = outliner_item_rename;
360         
361         ot->poll = ED_operator_outliner_active;
362 }
363
364 /* ID delete --------------------------------------------------- */
365
366 static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeStoreElem *tselem)
367 {
368         Main *bmain = CTX_data_main(C);
369         ID *id = tselem->id;
370
371         BLI_assert(te->idcode != 0 && id != NULL);
372         UNUSED_VARS_NDEBUG(te);
373
374         if (te->idcode == ID_LI && ((Library *)id)->parent != NULL) {
375                 BKE_reportf(reports, RPT_WARNING, "Cannot delete indirectly linked library '%s'", id->name);
376                 return;
377         }
378         if (id->tag & LIB_TAG_INDIRECT) {
379                 BKE_reportf(reports, RPT_WARNING, "Cannot delete indirectly linked id '%s'", id->name);
380                 return;
381         }
382         else if (BKE_library_ID_is_indirectly_used(bmain, id) && ID_REAL_USERS(id) <= 1) {
383                 BKE_reportf(reports, RPT_WARNING,
384                             "Cannot delete id '%s', indirectly used data-blocks need at least one user",
385                             id->name);
386                 return;
387         }
388
389
390         BKE_libblock_delete(bmain, id);
391
392         WM_event_add_notifier(C, NC_WINDOW, NULL);
393 }
394
395 void id_delete_cb(
396         bContext *C, ReportList *reports, Scene *UNUSED(scene),
397         TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
398 {
399         id_delete(C, reports, te, tselem);
400 }
401
402 static int outliner_id_delete_invoke_do(bContext *C, ReportList *reports, TreeElement *te, const float mval[2])
403 {
404         if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
405                 TreeStoreElem *tselem = TREESTORE(te);
406
407                 if (te->idcode != 0 && tselem->id) {
408                         if (te->idcode == ID_LI && ((Library *)tselem->id)->parent) {
409                                 BKE_reportf(reports, RPT_ERROR_INVALID_INPUT,
410                                             "Cannot delete indirectly linked library '%s'", ((Library *)tselem->id)->filepath);
411                                 return OPERATOR_CANCELLED;
412                         }
413                         id_delete(C, reports, te, tselem);
414                         return OPERATOR_FINISHED;
415                 }
416         }
417         else {
418                 for (te = te->subtree.first; te; te = te->next) {
419                         int ret;
420                         if ((ret = outliner_id_delete_invoke_do(C, reports, te, mval))) {
421                                 return ret;
422                         }
423                 }
424         }
425
426         return 0;
427 }
428
429 static int outliner_id_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event)
430 {
431         ARegion *ar = CTX_wm_region(C);
432         SpaceOops *soops = CTX_wm_space_outliner(C);
433         TreeElement *te;
434         float fmval[2];
435
436         BLI_assert(ar && soops);
437
438         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
439
440         for (te = soops->tree.first; te; te = te->next) {
441                 int ret;
442
443                 if ((ret = outliner_id_delete_invoke_do(C, op->reports, te, fmval))) {
444                         return ret;
445                 }
446         }
447
448         return OPERATOR_CANCELLED;
449 }
450
451 void OUTLINER_OT_id_delete(wmOperatorType *ot)
452 {
453         ot->name = "Delete Data-Block";
454         ot->idname = "OUTLINER_OT_id_delete";
455         ot->description = "Delete the ID under cursor";
456
457         ot->invoke = outliner_id_delete_invoke;
458         ot->poll = ED_operator_outliner_active;
459 }
460
461 /* ID remap --------------------------------------------------- */
462
463 static int outliner_id_remap_exec(bContext *C, wmOperator *op)
464 {
465         Main *bmain = CTX_data_main(C);
466         SpaceOops *soops = CTX_wm_space_outliner(C);
467
468         const short id_type = (short)RNA_enum_get(op->ptr, "id_type");
469         ID *old_id = BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "old_id"));
470         ID *new_id = BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "new_id"));
471
472         /* check for invalid states */
473         if (soops == NULL) {
474                 return OPERATOR_CANCELLED;
475         }
476
477         if (!(old_id && new_id && (old_id != new_id) && (GS(old_id->name) == GS(new_id->name)))) {
478                 BKE_reportf(op->reports, RPT_ERROR_INVALID_INPUT, "Invalid old/new ID pair ('%s' / '%s')",
479                             old_id ? old_id->name : "Invalid ID", new_id ? new_id->name : "Invalid ID");
480                 return OPERATOR_CANCELLED;
481         }
482
483         if (ID_IS_LINKED(old_id)) {
484                 BKE_reportf(op->reports, RPT_WARNING,
485                             "Old ID '%s' is linked from a library, indirect usages of this data-block will not be remapped",
486                             old_id->name);
487         }
488
489         BKE_libblock_remap(bmain, old_id, new_id,
490                            ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_NEVER_NULL_USAGE);
491
492         BKE_main_lib_objects_recalc_all(bmain);
493
494         /* recreate dependency graph to include new objects */
495         DEG_relations_tag_update(bmain);
496
497         /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */
498         GPU_materials_free();
499
500         WM_event_add_notifier(C, NC_WINDOW, NULL);
501
502         return OPERATOR_FINISHED;
503 }
504
505 static bool outliner_id_remap_find_tree_element(bContext *C, wmOperator *op, ListBase *tree, const float y)
506 {
507         TreeElement *te;
508
509         for (te = tree->first; te; te = te->next) {
510                 if (y > te->ys && y < te->ys + UI_UNIT_Y) {
511                         TreeStoreElem *tselem = TREESTORE(te);
512
513                         if (tselem->type == 0 && tselem->id) {
514                                 printf("found id %s (%p)!\n", tselem->id->name, tselem->id);
515
516                                 RNA_enum_set(op->ptr, "id_type", GS(tselem->id->name));
517                                 RNA_enum_set_identifier(C, op->ptr, "new_id", tselem->id->name + 2);
518                                 RNA_enum_set_identifier(C, op->ptr, "old_id", tselem->id->name + 2);
519                                 return true;
520                         }
521                 }
522                 if (outliner_id_remap_find_tree_element(C, op, &te->subtree, y)) {
523                         return true;
524                 }
525         }
526         return false;
527 }
528
529 static int outliner_id_remap_invoke(bContext *C, wmOperator *op, const wmEvent *event)
530 {
531         SpaceOops *soops = CTX_wm_space_outliner(C);
532         ARegion *ar = CTX_wm_region(C);
533         float fmval[2];
534
535         if (!RNA_property_is_set(op->ptr, RNA_struct_find_property(op->ptr, "id_type"))) {
536                 UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
537
538                 outliner_id_remap_find_tree_element(C, op, &soops->tree, fmval[1]);
539         }
540
541         return WM_operator_props_dialog_popup(C, op, 200, 100);
542 }
543
544 static const EnumPropertyItem *outliner_id_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
545 {
546         EnumPropertyItem item_tmp = {0}, *item = NULL;
547         int totitem = 0;
548         int i = 0;
549
550         short id_type = (short)RNA_enum_get(ptr, "id_type");
551         ID *id = which_libbase(CTX_data_main(C), id_type)->first;
552
553         for (; id; id = id->next) {
554                 item_tmp.identifier = item_tmp.name = id->name + 2;
555                 item_tmp.value = i++;
556                 RNA_enum_item_add(&item, &totitem, &item_tmp);
557         }
558
559         RNA_enum_item_end(&item, &totitem);
560         *r_free = true;
561
562         return item;
563 }
564
565 void OUTLINER_OT_id_remap(wmOperatorType *ot)
566 {
567         PropertyRNA *prop;
568
569         /* identifiers */
570         ot->name = "Outliner ID data Remap";
571         ot->idname = "OUTLINER_OT_id_remap";
572         ot->description = "";
573
574         /* callbacks */
575         ot->invoke = outliner_id_remap_invoke;
576         ot->exec = outliner_id_remap_exec;
577         ot->poll = ED_operator_outliner_active;
578
579         ot->flag = 0;
580
581         prop = RNA_def_enum(ot->srna, "id_type", rna_enum_id_type_items, ID_OB, "ID Type", "");
582         RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID);
583
584         prop = RNA_def_enum(ot->srna, "old_id", DummyRNA_NULL_items, 0, "Old ID", "Old ID to replace");
585         RNA_def_property_enum_funcs_runtime(prop, NULL, NULL, outliner_id_itemf);
586         RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE | PROP_HIDDEN);
587
588         ot->prop = RNA_def_enum(ot->srna, "new_id", DummyRNA_NULL_items, 0,
589                                 "New ID", "New ID to remap all selected IDs' users to");
590         RNA_def_property_enum_funcs_runtime(ot->prop, NULL, NULL, outliner_id_itemf);
591         RNA_def_property_flag(ot->prop, PROP_ENUM_NO_TRANSLATE);
592 }
593
594 void id_remap_cb(
595         bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te),
596         TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
597 {
598         wmOperatorType *ot = WM_operatortype_find("OUTLINER_OT_id_remap", false);
599         PointerRNA op_props;
600
601         BLI_assert(tselem->id != NULL);
602
603         WM_operator_properties_create_ptr(&op_props, ot);
604
605         RNA_enum_set(&op_props, "id_type", GS(tselem->id->name));
606         RNA_enum_set_identifier(C, &op_props, "old_id", tselem->id->name + 2);
607
608         WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_props);
609
610         WM_operator_properties_free(&op_props);
611 }
612
613 /* Library relocate/reload --------------------------------------------------- */
614
615 static int lib_relocate(
616         bContext *C, TreeElement *te, TreeStoreElem *tselem, wmOperatorType *ot, const bool reload)
617 {
618         PointerRNA op_props;
619         int ret = 0;
620
621         BLI_assert(te->idcode == ID_LI && tselem->id != NULL);
622         UNUSED_VARS_NDEBUG(te);
623
624         WM_operator_properties_create_ptr(&op_props, ot);
625
626         RNA_string_set(&op_props, "library", tselem->id->name + 2);
627
628         if (reload) {
629                 Library *lib = (Library *)tselem->id;
630                 char dir[FILE_MAXDIR], filename[FILE_MAX];
631
632                 BLI_split_dirfile(lib->filepath, dir, filename, sizeof(dir), sizeof(filename));
633
634                 printf("%s, %s\n", tselem->id->name, lib->filepath);
635
636                 /* We assume if both paths in lib are not the same then lib->name was relative... */
637                 RNA_boolean_set(&op_props, "relative_path", BLI_path_cmp(lib->filepath, lib->name) != 0);
638
639                 RNA_string_set(&op_props, "directory", dir);
640                 RNA_string_set(&op_props, "filename", filename);
641
642                 ret = WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props);
643         }
644         else {
645                 ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_props);
646         }
647
648         WM_operator_properties_free(&op_props);
649
650         return ret;
651 }
652
653 static int outliner_lib_relocate_invoke_do(
654         bContext *C, ReportList *reports, TreeElement *te, const float mval[2], const bool reload)
655 {
656         if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
657                 TreeStoreElem *tselem = TREESTORE(te);
658
659                 if (te->idcode == ID_LI && tselem->id) {
660                         if (((Library *)tselem->id)->parent && !reload) {
661                                 BKE_reportf(reports, RPT_ERROR_INVALID_INPUT,
662                                             "Cannot relocate indirectly linked library '%s'", ((Library *)tselem->id)->filepath);
663                                 return OPERATOR_CANCELLED;
664                         }
665                         else {
666                                 wmOperatorType *ot = WM_operatortype_find(reload ? "WM_OT_lib_reload" : "WM_OT_lib_relocate", false);
667
668                                 return lib_relocate(C, te, tselem, ot, reload);
669                         }
670                 }
671         }
672         else {
673                 for (te = te->subtree.first; te; te = te->next) {
674                         int ret;
675                         if ((ret = outliner_lib_relocate_invoke_do(C, reports, te, mval, reload))) {
676                                 return ret;
677                         }
678                 }
679         }
680
681         return 0;
682 }
683
684 static int outliner_lib_relocate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
685 {
686         ARegion *ar = CTX_wm_region(C);
687         SpaceOops *soops = CTX_wm_space_outliner(C);
688         TreeElement *te;
689         float fmval[2];
690
691         BLI_assert(ar && soops);
692
693         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
694
695         for (te = soops->tree.first; te; te = te->next) {
696                 int ret;
697
698                 if ((ret = outliner_lib_relocate_invoke_do(C, op->reports, te, fmval, false))) {
699                         return ret;
700                 }
701         }
702
703         return OPERATOR_CANCELLED;
704 }
705
706 void OUTLINER_OT_lib_relocate(wmOperatorType *ot)
707 {
708         ot->name = "Relocate Library";
709         ot->idname = "OUTLINER_OT_lib_relocate";
710         ot->description = "Relocate the library under cursor";
711
712         ot->invoke = outliner_lib_relocate_invoke;
713         ot->poll = ED_operator_outliner_active;
714 }
715
716 /* XXX This does not work with several items
717  *     (it is only called once in the end, due to the 'deferred' filebrowser invocation through event system...). */
718 void lib_relocate_cb(
719         bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *te,
720         TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
721 {
722         wmOperatorType *ot = WM_operatortype_find("WM_OT_lib_relocate", false);
723
724         lib_relocate(C, te, tselem, ot, false);
725 }
726
727
728 static int outliner_lib_reload_invoke(bContext *C, wmOperator *op, const wmEvent *event)
729 {
730         ARegion *ar = CTX_wm_region(C);
731         SpaceOops *soops = CTX_wm_space_outliner(C);
732         TreeElement *te;
733         float fmval[2];
734
735         BLI_assert(ar && soops);
736
737         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
738
739         for (te = soops->tree.first; te; te = te->next) {
740                 int ret;
741
742                 if ((ret = outliner_lib_relocate_invoke_do(C, op->reports, te, fmval, true))) {
743                         return ret;
744                 }
745         }
746
747         return OPERATOR_CANCELLED;
748 }
749
750 void OUTLINER_OT_lib_reload(wmOperatorType *ot)
751 {
752         ot->name = "Reload Library";
753         ot->idname = "OUTLINER_OT_lib_reload";
754         ot->description = "Reload the library under cursor";
755
756         ot->invoke = outliner_lib_reload_invoke;
757         ot->poll = ED_operator_outliner_active;
758 }
759
760 void lib_reload_cb(
761         bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *te,
762         TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
763 {
764         wmOperatorType *ot = WM_operatortype_find("WM_OT_lib_reload", false);
765
766         lib_relocate(C, te, tselem, ot, true);
767 }
768
769 /* ************************************************************** */
770 /* Setting Toggling Operators */
771
772 /* =============================================== */
773 /* Toggling Utilities (Exported) */
774
775 /* Apply Settings ------------------------------- */
776
777 static int outliner_count_levels(ListBase *lb, const int curlevel)
778 {
779         TreeElement *te;
780         int level = curlevel, lev;
781         
782         for (te = lb->first; te; te = te->next) {
783                 
784                 lev = outliner_count_levels(&te->subtree, curlevel + 1);
785                 if (lev > level) level = lev;
786         }
787         return level;
788 }
789
790 int outliner_has_one_flag(ListBase *lb, short flag, const int curlevel)
791 {
792         TreeElement *te;
793         TreeStoreElem *tselem;
794         int level;
795         
796         for (te = lb->first; te; te = te->next) {
797                 tselem = TREESTORE(te);
798                 if (tselem->flag & flag) return curlevel;
799                 
800                 level = outliner_has_one_flag(&te->subtree, flag, curlevel + 1);
801                 if (level) return level;
802         }
803         return 0;
804 }
805
806 /**
807  * Set or unset \a flag for all outliner elements in \a lb and sub-trees.
808  * \return if any flag was modified.
809  */
810 bool outliner_set_flag(ListBase *lb, short flag, short set)
811 {
812         TreeElement *te;
813         TreeStoreElem *tselem;
814         bool changed = false;
815         bool has_flag;
816
817         for (te = lb->first; te; te = te->next) {
818                 tselem = TREESTORE(te);
819                 has_flag = (tselem->flag & flag);
820                 if (set == 0) {
821                         if (has_flag) {
822                                 tselem->flag &= ~flag;
823                                 changed = true;
824                         }
825                 }
826                 else if (!has_flag) {
827                         tselem->flag |= flag;
828                         changed = true;
829                 }
830                 changed |= outliner_set_flag(&te->subtree, flag, set);
831         }
832
833         return changed;
834 }
835
836 /* Restriction Columns ------------------------------- */
837
838 /* same check needed for both object operation and restrict column button func
839  * return 0 when in edit mode (cannot restrict view or select)
840  * otherwise return 1 */
841 int common_restrict_check(bContext *C, Object *ob)
842 {
843         /* Don't allow hide an object in edit mode,
844          * check the bug #22153 and #21609, #23977
845          */
846         Object *obedit = CTX_data_edit_object(C);
847         if (obedit && obedit == ob) {
848                 /* found object is hidden, reset */
849                 if (ob->restrictflag & OB_RESTRICT_VIEW)
850                         ob->restrictflag &= ~OB_RESTRICT_VIEW;
851                 /* found object is unselectable, reset */
852                 if (ob->restrictflag & OB_RESTRICT_SELECT)
853                         ob->restrictflag &= ~OB_RESTRICT_SELECT;
854                 return 0;
855         }
856         
857         return 1;
858 }
859
860 /* =============================================== */
861 /* Outliner setting toggles */
862
863 /* Toggle Expanded (Outliner) ---------------------------------------- */
864
865 static int outliner_toggle_expanded_exec(bContext *C, wmOperator *UNUSED(op))
866 {
867         SpaceOops *soops = CTX_wm_space_outliner(C);
868         ARegion *ar = CTX_wm_region(C);
869         
870         if (outliner_has_one_flag(&soops->tree, TSE_CLOSED, 1))
871                 outliner_set_flag(&soops->tree, TSE_CLOSED, 0);
872         else 
873                 outliner_set_flag(&soops->tree, TSE_CLOSED, 1);
874         
875         ED_region_tag_redraw(ar);
876         
877         return OPERATOR_FINISHED;
878 }
879
880 void OUTLINER_OT_expanded_toggle(wmOperatorType *ot)
881 {
882         /* identifiers */
883         ot->name = "Expand/Collapse All";
884         ot->idname = "OUTLINER_OT_expanded_toggle";
885         ot->description = "Expand/Collapse all items";
886         
887         /* callbacks */
888         ot->exec = outliner_toggle_expanded_exec;
889         ot->poll = ED_operator_outliner_active;
890         
891         /* no undo or registry, UI option */
892 }
893
894 /* Toggle Selected (Outliner) ---------------------------------------- */
895
896 static int outliner_toggle_selected_exec(bContext *C, wmOperator *UNUSED(op))
897 {
898         SpaceOops *soops = CTX_wm_space_outliner(C);
899         ARegion *ar = CTX_wm_region(C);
900         Scene *scene = CTX_data_scene(C);
901         
902         if (outliner_has_one_flag(&soops->tree, TSE_SELECTED, 1))
903                 outliner_set_flag(&soops->tree, TSE_SELECTED, 0);
904         else 
905                 outliner_set_flag(&soops->tree, TSE_SELECTED, 1);
906         
907         soops->storeflag |= SO_TREESTORE_REDRAW;
908         
909         WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
910         ED_region_tag_redraw(ar);
911         
912         return OPERATOR_FINISHED;
913 }
914
915 void OUTLINER_OT_selected_toggle(wmOperatorType *ot)
916 {
917         /* identifiers */
918         ot->name = "Toggle Selected";
919         ot->idname = "OUTLINER_OT_selected_toggle";
920         ot->description = "Toggle the Outliner selection of items";
921         
922         /* callbacks */
923         ot->exec = outliner_toggle_selected_exec;
924         ot->poll = ED_operator_outliner_active;
925         
926         /* no undo or registry, UI option */
927 }
928
929 /* ************************************************************** */
930 /* Hotkey Only Operators */
931
932 /* Show Active --------------------------------------------------- */
933
934 static void outliner_set_coordinates_element_recursive(SpaceOops *soops, TreeElement *te, int startx, int *starty)
935 {
936         TreeStoreElem *tselem = TREESTORE(te);
937
938         /* store coord and continue, we need coordinates for elements outside view too */
939         te->xs = (float)startx;
940         te->ys = (float)(*starty);
941         *starty -= UI_UNIT_Y;
942
943         if (TSELEM_OPEN(tselem, soops)) {
944                 TreeElement *ten;
945                 for (ten = te->subtree.first; ten; ten = ten->next) {
946                         outliner_set_coordinates_element_recursive(soops, ten, startx + UI_UNIT_X, starty);
947                 }
948         }
949 }
950
951 /* to retrieve coordinates with redrawing the entire tree */
952 static void outliner_set_coordinates(ARegion *ar, SpaceOops *soops)
953 {
954         TreeElement *te;
955         int starty = (int)(ar->v2d.tot.ymax) - UI_UNIT_Y;
956
957         for (te = soops->tree.first; te; te = te->next) {
958                 outliner_set_coordinates_element_recursive(soops, te, 0, &starty);
959         }
960 }
961
962 /* return 1 when levels were opened */
963 static int outliner_open_back(TreeElement *te)
964 {
965         TreeStoreElem *tselem;
966         int retval = 0;
967
968         for (te = te->parent; te; te = te->parent) {
969                 tselem = TREESTORE(te);
970                 if (tselem->flag & TSE_CLOSED) {
971                         tselem->flag &= ~TSE_CLOSED;
972                         retval = 1;
973                 }
974         }
975         return retval;
976 }
977
978 static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op))
979 {
980         SpaceOops *so = CTX_wm_space_outliner(C);
981         SceneLayer *sl = CTX_data_scene_layer(C);
982         ARegion *ar = CTX_wm_region(C);
983         View2D *v2d = &ar->v2d;
984         
985         TreeElement *te;
986         int xdelta, ytop;
987
988         Object *obact = OBACT_NEW(sl);
989
990         if (!obact)
991                 return OPERATOR_CANCELLED;
992
993
994         te = outliner_find_id(so, &so->tree, &obact->id);
995
996         if (te != NULL && obact->type == OB_ARMATURE) {
997                 /* traverse down the bone hierarchy in case of armature */
998                 TreeElement *te_obact = te;
999
1000                 if (obact->mode & OB_MODE_POSE) {
1001                         bPoseChannel *pchan = CTX_data_active_pose_bone(C);
1002                         if (pchan) {
1003                                 te = outliner_find_posechannel(&te_obact->subtree, pchan);
1004                         }
1005                 }
1006                 else if (obact->mode & OB_MODE_EDIT) {
1007                         EditBone *ebone = CTX_data_active_bone(C);
1008                         if (ebone) {
1009                                 te = outliner_find_editbone(&te_obact->subtree, ebone);
1010                         }
1011                 }
1012         }
1013
1014         if (te) {
1015                 /* open up tree to active object/bone */
1016                 if (outliner_open_back(te)) {
1017                         outliner_set_coordinates(ar, so);
1018                 }
1019
1020                 /* make te->ys center of view */
1021                 ytop = te->ys + BLI_rcti_size_y(&v2d->mask) / 2;
1022                 if (ytop > 0) ytop = 0;
1023                 
1024                 v2d->cur.ymax = (float)ytop;
1025                 v2d->cur.ymin = (float)(ytop - BLI_rcti_size_y(&v2d->mask));
1026                 
1027                 /* make te->xs ==> te->xend center of view */
1028                 xdelta = (int)(te->xs - v2d->cur.xmin);
1029                 v2d->cur.xmin += xdelta;
1030                 v2d->cur.xmax += xdelta;
1031                 
1032                 so->storeflag |= SO_TREESTORE_REDRAW;
1033         }
1034         
1035         ED_region_tag_redraw(ar);
1036         
1037         return OPERATOR_FINISHED;
1038 }
1039
1040 void OUTLINER_OT_show_active(wmOperatorType *ot)
1041 {
1042         /* identifiers */
1043         ot->name = "Show Active";
1044         ot->idname = "OUTLINER_OT_show_active";
1045         ot->description = "Open up the tree and adjust the view so that the active Object is shown centered";
1046         
1047         /* callbacks */
1048         ot->exec = outliner_show_active_exec;
1049         ot->poll = ED_operator_outliner_active;
1050 }
1051
1052 /* View Panning --------------------------------------------------- */
1053
1054 static int outliner_scroll_page_exec(bContext *C, wmOperator *op)
1055 {
1056         ARegion *ar = CTX_wm_region(C);
1057         int dy = BLI_rcti_size_y(&ar->v2d.mask);
1058         int up = 0;
1059         
1060         if (RNA_boolean_get(op->ptr, "up"))
1061                 up = 1;
1062
1063         if (up == 0) dy = -dy;
1064         ar->v2d.cur.ymin += dy;
1065         ar->v2d.cur.ymax += dy;
1066         
1067         ED_region_tag_redraw(ar);
1068         
1069         return OPERATOR_FINISHED;
1070 }
1071
1072
1073 void OUTLINER_OT_scroll_page(wmOperatorType *ot)
1074 {
1075         PropertyRNA *prop;
1076
1077         /* identifiers */
1078         ot->name = "Scroll Page";
1079         ot->idname = "OUTLINER_OT_scroll_page";
1080         ot->description = "Scroll page up or down";
1081         
1082         /* callbacks */
1083         ot->exec = outliner_scroll_page_exec;
1084         ot->poll = ED_operator_outliner_active;
1085         
1086         /* properties */
1087         prop = RNA_def_boolean(ot->srna, "up", 0, "Up", "Scroll up one page");
1088         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1089 }
1090
1091 /* Search ------------------------------------------------------- */
1092 // TODO: probably obsolete now with filtering?
1093
1094 #if 0
1095
1096 /* find next element that has this name */
1097 static TreeElement *outliner_find_name(SpaceOops *soops, ListBase *lb, char *name, int flags,
1098                                        TreeElement *prev, int *prevFound)
1099 {
1100         TreeElement *te, *tes;
1101         
1102         for (te = lb->first; te; te = te->next) {
1103                 int found = outliner_filter_has_name(te, name, flags);
1104                 
1105                 if (found) {
1106                         /* name is right, but is element the previous one? */
1107                         if (prev) {
1108                                 if ((te != prev) && (*prevFound)) 
1109                                         return te;
1110                                 if (te == prev) {
1111                                         *prevFound = 1;
1112                                 }
1113                         }
1114                         else
1115                                 return te;
1116                 }
1117                 
1118                 tes = outliner_find_name(soops, &te->subtree, name, flags, prev, prevFound);
1119                 if (tes) return tes;
1120         }
1121
1122         /* nothing valid found */
1123         return NULL;
1124 }
1125
1126 static void outliner_find_panel(Scene *UNUSED(scene), ARegion *ar, SpaceOops *soops, int again, int flags) 
1127 {
1128         ReportList *reports = NULL; // CTX_wm_reports(C);
1129         TreeElement *te = NULL;
1130         TreeElement *last_find;
1131         TreeStoreElem *tselem;
1132         int ytop, xdelta, prevFound = 0;
1133         char name[sizeof(soops->search_string)];
1134         
1135         /* get last found tree-element based on stored search_tse */
1136         last_find = outliner_find_tse(soops, &soops->search_tse);
1137         
1138         /* determine which type of search to do */
1139         if (again && last_find) {
1140                 /* no popup panel - previous + user wanted to search for next after previous */
1141                 BLI_strncpy(name, soops->search_string, sizeof(name));
1142                 flags = soops->search_flags;
1143                 
1144                 /* try to find matching element */
1145                 te = outliner_find_name(soops, &soops->tree, name, flags, last_find, &prevFound);
1146                 if (te == NULL) {
1147                         /* no more matches after previous, start from beginning again */
1148                         prevFound = 1;
1149                         te = outliner_find_name(soops, &soops->tree, name, flags, last_find, &prevFound);
1150                 }
1151         }
1152         else {
1153                 /* pop up panel - no previous, or user didn't want search after previous */
1154                 name[0] = '\0';
1155 // XXX          if (sbutton(name, 0, sizeof(name) - 1, "Find: ") && name[0]) {
1156 //                      te = outliner_find_name(soops, &soops->tree, name, flags, NULL, &prevFound);
1157 //              }
1158 //              else return; /* XXX RETURN! XXX */
1159         }
1160
1161         /* do selection and reveal */
1162         if (te) {
1163                 tselem = TREESTORE(te);
1164                 if (tselem) {
1165                         /* expand branches so that it will be visible, we need to get correct coordinates */
1166                         if (outliner_open_back(soops, te))
1167                                 outliner_set_coordinates(ar, soops);
1168                         
1169                         /* deselect all visible, and select found element */
1170                         outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
1171                         tselem->flag |= TSE_SELECTED;
1172                         
1173                         /* make te->ys center of view */
1174                         ytop = (int)(te->ys + BLI_rctf_size_y(&ar->v2d.mask) / 2);
1175                         if (ytop > 0) ytop = 0;
1176                         ar->v2d.cur.ymax = (float)ytop;
1177                         ar->v2d.cur.ymin = (float)(ytop - BLI_rctf_size_y(&ar->v2d.mask));
1178                         
1179                         /* make te->xs ==> te->xend center of view */
1180                         xdelta = (int)(te->xs - ar->v2d.cur.xmin);
1181                         ar->v2d.cur.xmin += xdelta;
1182                         ar->v2d.cur.xmax += xdelta;
1183                         
1184                         /* store selection */
1185                         soops->search_tse = *tselem;
1186                         
1187                         BLI_strncpy(soops->search_string, name, sizeof(soops->search_string));
1188                         soops->search_flags = flags;
1189                         
1190                         /* redraw */
1191                         soops->storeflag |= SO_TREESTORE_REDRAW;
1192                 }
1193         }
1194         else {
1195                 /* no tree-element found */
1196                 BKE_reportf(reports, RPT_WARNING, "Not found: %s", name);
1197         }
1198 }
1199 #endif
1200
1201 /* Show One Level ----------------------------------------------- */
1202
1203 /* helper function for Show/Hide one level operator */
1204 static void outliner_openclose_level(ListBase *lb, int curlevel, int level, int open)
1205 {
1206         TreeElement *te;
1207         TreeStoreElem *tselem;
1208         
1209         for (te = lb->first; te; te = te->next) {
1210                 tselem = TREESTORE(te);
1211                 
1212                 if (open) {
1213                         if (curlevel <= level) tselem->flag &= ~TSE_CLOSED;
1214                 }
1215                 else {
1216                         if (curlevel >= level) tselem->flag |= TSE_CLOSED;
1217                 }
1218                 
1219                 outliner_openclose_level(&te->subtree, curlevel + 1, level, open);
1220         }
1221 }
1222
1223 static int outliner_one_level_exec(bContext *C, wmOperator *op)
1224 {
1225         SpaceOops *soops = CTX_wm_space_outliner(C);
1226         ARegion *ar = CTX_wm_region(C);
1227         const bool add = RNA_boolean_get(op->ptr, "open");
1228         int level;
1229         
1230         level = outliner_has_one_flag(&soops->tree, TSE_CLOSED, 1);
1231         if (add == 1) {
1232                 if (level) outliner_openclose_level(&soops->tree, 1, level, 1);
1233         }
1234         else {
1235                 if (level == 0) level = outliner_count_levels(&soops->tree, 0);
1236                 if (level) outliner_openclose_level(&soops->tree, 1, level - 1, 0);
1237         }
1238         
1239         ED_region_tag_redraw(ar);
1240         
1241         return OPERATOR_FINISHED;
1242 }
1243
1244 void OUTLINER_OT_show_one_level(wmOperatorType *ot)
1245 {
1246         PropertyRNA *prop;
1247
1248         /* identifiers */
1249         ot->name = "Show/Hide One Level";
1250         ot->idname = "OUTLINER_OT_show_one_level";
1251         ot->description = "Expand/collapse all entries by one level";
1252         
1253         /* callbacks */
1254         ot->exec = outliner_one_level_exec;
1255         ot->poll = ED_operator_outliner_active;
1256         
1257         /* no undo or registry, UI option */
1258         
1259         /* properties */
1260         prop = RNA_def_boolean(ot->srna, "open", 1, "Open", "Expand all entries one level deep");
1261         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1262 }
1263
1264 /* Show Hierarchy ----------------------------------------------- */
1265
1266 /* helper function for tree_element_shwo_hierarchy() - recursively checks whether subtrees have any objects*/
1267 static int subtree_has_objects(ListBase *lb)
1268 {
1269         TreeElement *te;
1270         TreeStoreElem *tselem;
1271         
1272         for (te = lb->first; te; te = te->next) {
1273                 tselem = TREESTORE(te);
1274                 if (tselem->type == 0 && te->idcode == ID_OB) return 1;
1275                 if (subtree_has_objects(&te->subtree)) return 1;
1276         }
1277         return 0;
1278 }
1279
1280 /* recursive helper function for Show Hierarchy operator */
1281 static void tree_element_show_hierarchy(Scene *scene, SpaceOops *soops, ListBase *lb)
1282 {
1283         TreeElement *te;
1284         TreeStoreElem *tselem;
1285
1286         /* open all object elems, close others */
1287         for (te = lb->first; te; te = te->next) {
1288                 tselem = TREESTORE(te);
1289                 
1290                 if (tselem->type == 0) {
1291                         if (te->idcode == ID_SCE) {
1292                                 if (tselem->id != (ID *)scene) tselem->flag |= TSE_CLOSED;
1293                                 else tselem->flag &= ~TSE_CLOSED;
1294                         }
1295                         else if (te->idcode == ID_OB) {
1296                                 if (subtree_has_objects(&te->subtree)) tselem->flag &= ~TSE_CLOSED;
1297                                 else tselem->flag |= TSE_CLOSED;
1298                         }
1299                 }
1300                 else {
1301                         tselem->flag |= TSE_CLOSED;
1302                 }
1303
1304                 if (TSELEM_OPEN(tselem, soops)) {
1305                         tree_element_show_hierarchy(scene, soops, &te->subtree);
1306                 }
1307         }
1308 }
1309
1310 /* show entire object level hierarchy */
1311 static int outliner_show_hierarchy_exec(bContext *C, wmOperator *UNUSED(op))
1312 {
1313         SpaceOops *soops = CTX_wm_space_outliner(C);
1314         ARegion *ar = CTX_wm_region(C);
1315         Scene *scene = CTX_data_scene(C);
1316         
1317         /* recursively open/close levels */
1318         tree_element_show_hierarchy(scene, soops, &soops->tree);
1319         
1320         ED_region_tag_redraw(ar);
1321         
1322         return OPERATOR_FINISHED;
1323 }
1324
1325 void OUTLINER_OT_show_hierarchy(wmOperatorType *ot)
1326 {
1327         /* identifiers */
1328         ot->name = "Show Hierarchy";
1329         ot->idname = "OUTLINER_OT_show_hierarchy";
1330         ot->description = "Open all object entries and close all others";
1331         
1332         /* callbacks */
1333         ot->exec = outliner_show_hierarchy_exec;
1334         ot->poll = ED_operator_outliner_active; //  TODO: shouldn't be allowed in RNA views...
1335         
1336         /* no undo or registry, UI option */
1337 }
1338
1339 /* ************************************************************** */
1340 /* ANIMATO OPERATIONS */
1341 /* KeyingSet and Driver Creation - Helper functions */
1342
1343 /* specialized poll callback for these operators to work in Datablocks view only */
1344 static int ed_operator_outliner_datablocks_active(bContext *C)
1345 {
1346         ScrArea *sa = CTX_wm_area(C);
1347         if ((sa) && (sa->spacetype == SPACE_OUTLINER)) {
1348                 SpaceOops *so = CTX_wm_space_outliner(C);
1349                 return (so->outlinevis == SO_DATABLOCKS);
1350         }
1351         return 0;
1352 }
1353
1354
1355 /* Helper func to extract an RNA path from selected tree element 
1356  * NOTE: the caller must zero-out all values of the pointers that it passes here first, as
1357  * this function does not do that yet 
1358  */
1359 static void tree_element_to_path(TreeElement *te, TreeStoreElem *tselem,
1360                                  ID **id, char **path, int *array_index, short *flag, short *UNUSED(groupmode))
1361 {
1362         ListBase hierarchy = {NULL, NULL};
1363         LinkData *ld;
1364         TreeElement *tem, *temnext, *temsub;
1365         TreeStoreElem *tse /* , *tsenext */ /* UNUSED */;
1366         PointerRNA *ptr, *nextptr;
1367         PropertyRNA *prop;
1368         char *newpath = NULL;
1369         
1370         /* optimize tricks:
1371          *      - Don't do anything if the selected item is a 'struct', but arrays are allowed
1372          */
1373         if (tselem->type == TSE_RNA_STRUCT)
1374                 return;
1375         
1376         /* Overview of Algorithm:
1377          *  1. Go up the chain of parents until we find the 'root', taking note of the
1378          *         levels encountered in reverse-order (i.e. items are added to the start of the list
1379          *      for more convenient looping later)
1380          *  2. Walk down the chain, adding from the first ID encountered
1381          *         (which will become the 'ID' for the KeyingSet Path), and build a  
1382          *      path as we step through the chain
1383          */
1384          
1385         /* step 1: flatten out hierarchy of parents into a flat chain */
1386         for (tem = te->parent; tem; tem = tem->parent) {
1387                 ld = MEM_callocN(sizeof(LinkData), "LinkData for tree_element_to_path()");
1388                 ld->data = tem;
1389                 BLI_addhead(&hierarchy, ld);
1390         }
1391         
1392         /* step 2: step down hierarchy building the path
1393          * (NOTE: addhead in previous loop was needed so that we can loop like this) */
1394         for (ld = hierarchy.first; ld; ld = ld->next) {
1395                 /* get data */
1396                 tem = (TreeElement *)ld->data;
1397                 tse = TREESTORE(tem);
1398                 ptr = &tem->rnaptr;
1399                 prop = tem->directdata;
1400                 
1401                 /* check if we're looking for first ID, or appending to path */
1402                 if (*id) {
1403                         /* just 'append' property to path 
1404                          * - to prevent memory leaks, we must write to newpath not path, then free old path + swap them
1405                          */
1406                         if (tse->type == TSE_RNA_PROPERTY) {
1407                                 if (RNA_property_type(prop) == PROP_POINTER) {
1408                                         /* for pointer we just append property name */
1409                                         newpath = RNA_path_append(*path, ptr, prop, 0, NULL);
1410                                 }
1411                                 else if (RNA_property_type(prop) == PROP_COLLECTION) {
1412                                         char buf[128], *name;
1413                                         
1414                                         temnext = (TreeElement *)(ld->next->data);
1415                                         /* tsenext = TREESTORE(temnext); */ /* UNUSED */
1416                                         
1417                                         nextptr = &temnext->rnaptr;
1418                                         name = RNA_struct_name_get_alloc(nextptr, buf, sizeof(buf), NULL);
1419                                         
1420                                         if (name) {
1421                                                 /* if possible, use name as a key in the path */
1422                                                 newpath = RNA_path_append(*path, NULL, prop, 0, name);
1423                                                 
1424                                                 if (name != buf)
1425                                                         MEM_freeN(name);
1426                                         }
1427                                         else {
1428                                                 /* otherwise use index */
1429                                                 int index = 0;
1430                                                 
1431                                                 for (temsub = tem->subtree.first; temsub; temsub = temsub->next, index++)
1432                                                         if (temsub == temnext)
1433                                                                 break;
1434                                                 
1435                                                 newpath = RNA_path_append(*path, NULL, prop, index, NULL);
1436                                         }
1437                                         
1438                                         ld = ld->next;
1439                                 }
1440                         }
1441                         
1442                         if (newpath) {
1443                                 if (*path) MEM_freeN(*path);
1444                                 *path = newpath;
1445                                 newpath = NULL;
1446                         }
1447                 }
1448                 else {
1449                         /* no ID, so check if entry is RNA-struct, and if that RNA-struct is an ID datablock to extract info from */
1450                         if (tse->type == TSE_RNA_STRUCT) {
1451                                 /* ptr->data not ptr->id.data seems to be the one we want,
1452                                  * since ptr->data is sometimes the owner of this ID? */
1453                                 if (RNA_struct_is_ID(ptr->type)) {
1454                                         *id = (ID *)ptr->data;
1455                                         
1456                                         /* clear path */
1457                                         if (*path) {
1458                                                 MEM_freeN(*path);
1459                                                 path = NULL;
1460                                         }
1461                                 }
1462                         }
1463                 }
1464         }
1465
1466         /* step 3: if we've got an ID, add the current item to the path */
1467         if (*id) {
1468                 /* add the active property to the path */
1469                 ptr = &te->rnaptr;
1470                 prop = te->directdata;
1471                 
1472                 /* array checks */
1473                 if (tselem->type == TSE_RNA_ARRAY_ELEM) {
1474                         /* item is part of an array, so must set the array_index */
1475                         *array_index = te->index;
1476                 }
1477                 else if (RNA_property_array_check(prop)) {
1478                         /* entire array was selected, so keyframe all */
1479                         *flag |= KSP_FLAG_WHOLE_ARRAY;
1480                 }
1481                 
1482                 /* path */
1483                 newpath = RNA_path_append(*path, NULL, prop, 0, NULL);
1484                 if (*path) MEM_freeN(*path);
1485                 *path = newpath;
1486         }
1487
1488         /* free temp data */
1489         BLI_freelistN(&hierarchy);
1490 }
1491
1492 /* =============================================== */
1493 /* Driver Operations */
1494
1495 /* These operators are only available in databrowser mode for now, as
1496  * they depend on having RNA paths and/or hierarchies available.
1497  */
1498 enum {
1499         DRIVERS_EDITMODE_ADD    = 0,
1500         DRIVERS_EDITMODE_REMOVE,
1501 } /*eDrivers_EditModes*/;
1502
1503 /* Utilities ---------------------------------- */ 
1504
1505 /* Recursively iterate over tree, finding and working on selected items */
1506 static void do_outliner_drivers_editop(SpaceOops *soops, ListBase *tree, ReportList *reports, short mode)
1507 {
1508         TreeElement *te;
1509         TreeStoreElem *tselem;
1510         
1511         for (te = tree->first; te; te = te->next) {
1512                 tselem = TREESTORE(te);
1513                 
1514                 /* if item is selected, perform operation */
1515                 if (tselem->flag & TSE_SELECTED) {
1516                         ID *id = NULL;
1517                         char *path = NULL;
1518                         int array_index = 0;
1519                         short flag = 0;
1520                         short groupmode = KSP_GROUP_KSNAME;
1521                         
1522                         /* check if RNA-property described by this selected element is an animatable prop */
1523                         if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) &&
1524                             RNA_property_animateable(&te->rnaptr, te->directdata))
1525                         {
1526                                 /* get id + path + index info from the selected element */
1527                                 tree_element_to_path(te, tselem,
1528                                                      &id, &path, &array_index, &flag, &groupmode);
1529                         }
1530                         
1531                         /* only if ID and path were set, should we perform any actions */
1532                         if (id && path) {
1533                                 short dflags = CREATEDRIVER_WITH_DEFAULT_DVAR;
1534                                 int arraylen = 1;
1535                                 
1536                                 /* array checks */
1537                                 if (flag & KSP_FLAG_WHOLE_ARRAY) {
1538                                         /* entire array was selected, so add drivers for all */
1539                                         arraylen = RNA_property_array_length(&te->rnaptr, te->directdata);
1540                                 }
1541                                 else
1542                                         arraylen = array_index;
1543                                 
1544                                 /* we should do at least one step */
1545                                 if (arraylen == array_index)
1546                                         arraylen++;
1547                                 
1548                                 /* for each array element we should affect, add driver */
1549                                 for (; array_index < arraylen; array_index++) {
1550                                         /* action depends on mode */
1551                                         switch (mode) {
1552                                                 case DRIVERS_EDITMODE_ADD:
1553                                                 {
1554                                                         /* add a new driver with the information obtained (only if valid) */
1555                                                         ANIM_add_driver(reports, id, path, array_index, dflags, DRIVER_TYPE_PYTHON);
1556                                                         break;
1557                                                 }
1558                                                 case DRIVERS_EDITMODE_REMOVE:
1559                                                 {
1560                                                         /* remove driver matching the information obtained (only if valid) */
1561                                                         ANIM_remove_driver(reports, id, path, array_index, dflags);
1562                                                         break;
1563                                                 }
1564                                         }
1565                                 }
1566                                 
1567                                 /* free path, since it had to be generated */
1568                                 MEM_freeN(path);
1569                         }
1570                         
1571                         
1572                 }
1573                 
1574                 /* go over sub-tree */
1575                 if (TSELEM_OPEN(tselem, soops))
1576                         do_outliner_drivers_editop(soops, &te->subtree, reports, mode);
1577         }
1578 }
1579
1580 /* Add Operator ---------------------------------- */
1581
1582 static int outliner_drivers_addsel_exec(bContext *C, wmOperator *op)
1583 {
1584         SpaceOops *soutliner = CTX_wm_space_outliner(C);
1585         
1586         /* check for invalid states */
1587         if (soutliner == NULL)
1588                 return OPERATOR_CANCELLED;
1589         
1590         /* recursively go into tree, adding selected items */
1591         do_outliner_drivers_editop(soutliner, &soutliner->tree, op->reports, DRIVERS_EDITMODE_ADD);
1592         
1593         /* send notifiers */
1594         WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); // XXX
1595         
1596         return OPERATOR_FINISHED;
1597 }
1598
1599 void OUTLINER_OT_drivers_add_selected(wmOperatorType *ot)
1600 {
1601         /* api callbacks */
1602         ot->idname = "OUTLINER_OT_drivers_add_selected";
1603         ot->name = "Add Drivers for Selected";
1604         ot->description = "Add drivers to selected items";
1605         
1606         /* api callbacks */
1607         ot->exec = outliner_drivers_addsel_exec;
1608         ot->poll = ed_operator_outliner_datablocks_active;
1609         
1610         /* flags */
1611         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1612 }
1613
1614
1615 /* Remove Operator ---------------------------------- */
1616
1617 static int outliner_drivers_deletesel_exec(bContext *C, wmOperator *op)
1618 {
1619         SpaceOops *soutliner = CTX_wm_space_outliner(C);
1620         
1621         /* check for invalid states */
1622         if (soutliner == NULL)
1623                 return OPERATOR_CANCELLED;
1624         
1625         /* recursively go into tree, adding selected items */
1626         do_outliner_drivers_editop(soutliner, &soutliner->tree, op->reports, DRIVERS_EDITMODE_REMOVE);
1627         
1628         /* send notifiers */
1629         WM_event_add_notifier(C, ND_KEYS, NULL); // XXX
1630         
1631         return OPERATOR_FINISHED;
1632 }
1633
1634 void OUTLINER_OT_drivers_delete_selected(wmOperatorType *ot)
1635 {
1636         /* identifiers */
1637         ot->idname = "OUTLINER_OT_drivers_delete_selected";
1638         ot->name = "Delete Drivers for Selected";
1639         ot->description = "Delete drivers assigned to selected items";
1640         
1641         /* api callbacks */
1642         ot->exec = outliner_drivers_deletesel_exec;
1643         ot->poll = ed_operator_outliner_datablocks_active;
1644         
1645         /* flags */
1646         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1647 }
1648
1649 /* =============================================== */
1650 /* Keying Set Operations */
1651
1652 /* These operators are only available in databrowser mode for now, as
1653  * they depend on having RNA paths and/or hierarchies available.
1654  */
1655 enum {
1656         KEYINGSET_EDITMODE_ADD  = 0,
1657         KEYINGSET_EDITMODE_REMOVE,
1658 } /*eKeyingSet_EditModes*/;
1659
1660 /* Utilities ---------------------------------- */ 
1661  
1662 /* find the 'active' KeyingSet, and add if not found (if adding is allowed) */
1663 // TODO: should this be an API func?
1664 static KeyingSet *verify_active_keyingset(Scene *scene, short add)
1665 {
1666         KeyingSet *ks = NULL;
1667         
1668         /* sanity check */
1669         if (scene == NULL)
1670                 return NULL;
1671         
1672         /* try to find one from scene */
1673         if (scene->active_keyingset > 0)
1674                 ks = BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1);
1675                 
1676         /* add if none found */
1677         // XXX the default settings have yet to evolve
1678         if ((add) && (ks == NULL)) {
1679                 ks = BKE_keyingset_add(&scene->keyingsets, NULL, NULL, KEYINGSET_ABSOLUTE, 0);
1680                 scene->active_keyingset = BLI_listbase_count(&scene->keyingsets);
1681         }
1682         
1683         return ks;
1684 }
1685
1686 /* Recursively iterate over tree, finding and working on selected items */
1687 static void do_outliner_keyingset_editop(SpaceOops *soops, KeyingSet *ks, ListBase *tree, short mode)
1688 {
1689         TreeElement *te;
1690         TreeStoreElem *tselem;
1691         
1692         for (te = tree->first; te; te = te->next) {
1693                 tselem = TREESTORE(te);
1694                 
1695                 /* if item is selected, perform operation */
1696                 if (tselem->flag & TSE_SELECTED) {
1697                         ID *id = NULL;
1698                         char *path = NULL;
1699                         int array_index = 0;
1700                         short flag = 0;
1701                         short groupmode = KSP_GROUP_KSNAME;
1702                         
1703                         /* check if RNA-property described by this selected element is an animatable prop */
1704                         if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) &&
1705                             RNA_property_animateable(&te->rnaptr, te->directdata))
1706                         {
1707                                 /* get id + path + index info from the selected element */
1708                                 tree_element_to_path(te, tselem,
1709                                                      &id, &path, &array_index, &flag, &groupmode);
1710                         }
1711                         
1712                         /* only if ID and path were set, should we perform any actions */
1713                         if (id && path) {
1714                                 /* action depends on mode */
1715                                 switch (mode) {
1716                                         case KEYINGSET_EDITMODE_ADD:
1717                                         {
1718                                                 /* add a new path with the information obtained (only if valid) */
1719                                                 /* TODO: what do we do with group name?
1720                                                  * for now, we don't supply one, and just let this use the KeyingSet name */
1721                                                 BKE_keyingset_add_path(ks, id, NULL, path, array_index, flag, groupmode);
1722                                                 ks->active_path = BLI_listbase_count(&ks->paths);
1723                                                 break;
1724                                         }
1725                                         case KEYINGSET_EDITMODE_REMOVE:
1726                                         {
1727                                                 /* find the relevant path, then remove it from the KeyingSet */
1728                                                 KS_Path *ksp = BKE_keyingset_find_path(ks, id, NULL, path, array_index, groupmode);
1729                                                 
1730                                                 if (ksp) {
1731                                                         /* free path's data */
1732                                                         BKE_keyingset_free_path(ks, ksp);
1733
1734                                                         ks->active_path = 0;
1735                                                 }
1736                                                 break;
1737                                         }
1738                                 }
1739                                 
1740                                 /* free path, since it had to be generated */
1741                                 MEM_freeN(path);
1742                         }
1743                 }
1744                 
1745                 /* go over sub-tree */
1746                 if (TSELEM_OPEN(tselem, soops))
1747                         do_outliner_keyingset_editop(soops, ks, &te->subtree, mode);
1748         }
1749 }
1750
1751 /* Add Operator ---------------------------------- */
1752
1753 static int outliner_keyingset_additems_exec(bContext *C, wmOperator *op)
1754 {
1755         SpaceOops *soutliner = CTX_wm_space_outliner(C);
1756         Scene *scene = CTX_data_scene(C);
1757         KeyingSet *ks = verify_active_keyingset(scene, 1);
1758         
1759         /* check for invalid states */
1760         if (ks == NULL) {
1761                 BKE_report(op->reports, RPT_ERROR, "Operation requires an active keying set");
1762                 return OPERATOR_CANCELLED;
1763         }
1764         if (soutliner == NULL)
1765                 return OPERATOR_CANCELLED;
1766         
1767         /* recursively go into tree, adding selected items */
1768         do_outliner_keyingset_editop(soutliner, ks, &soutliner->tree, KEYINGSET_EDITMODE_ADD);
1769         
1770         /* send notifiers */
1771         WM_event_add_notifier(C, NC_SCENE | ND_KEYINGSET, NULL);
1772         
1773         return OPERATOR_FINISHED;
1774 }
1775
1776 void OUTLINER_OT_keyingset_add_selected(wmOperatorType *ot)
1777 {
1778         /* identifiers */
1779         ot->idname = "OUTLINER_OT_keyingset_add_selected";
1780         ot->name = "Keying Set Add Selected";
1781         ot->description = "Add selected items (blue-gray rows) to active Keying Set";
1782         
1783         /* api callbacks */
1784         ot->exec = outliner_keyingset_additems_exec;
1785         ot->poll = ed_operator_outliner_datablocks_active;
1786         
1787         /* flags */
1788         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1789 }
1790
1791
1792 /* Remove Operator ---------------------------------- */
1793
1794 static int outliner_keyingset_removeitems_exec(bContext *C, wmOperator *UNUSED(op))
1795 {
1796         SpaceOops *soutliner = CTX_wm_space_outliner(C);
1797         Scene *scene = CTX_data_scene(C);
1798         KeyingSet *ks = verify_active_keyingset(scene, 1);
1799         
1800         /* check for invalid states */
1801         if (soutliner == NULL)
1802                 return OPERATOR_CANCELLED;
1803         
1804         /* recursively go into tree, adding selected items */
1805         do_outliner_keyingset_editop(soutliner, ks, &soutliner->tree, KEYINGSET_EDITMODE_REMOVE);
1806         
1807         /* send notifiers */
1808         WM_event_add_notifier(C, NC_SCENE | ND_KEYINGSET, NULL);
1809         
1810         return OPERATOR_FINISHED;
1811 }
1812
1813 void OUTLINER_OT_keyingset_remove_selected(wmOperatorType *ot)
1814 {
1815         /* identifiers */
1816         ot->idname = "OUTLINER_OT_keyingset_remove_selected";
1817         ot->name = "Keying Set Remove Selected";
1818         ot->description = "Remove selected items (blue-gray rows) from active Keying Set";
1819         
1820         /* api callbacks */
1821         ot->exec = outliner_keyingset_removeitems_exec;
1822         ot->poll = ed_operator_outliner_datablocks_active;
1823         
1824         /* flags */
1825         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1826 }
1827
1828
1829 /* ************************************************************** */
1830 /* ORPHANED DATABLOCKS */
1831
1832 static int ed_operator_outliner_id_orphans_active(bContext *C)
1833 {
1834         ScrArea *sa = CTX_wm_area(C);
1835         if ((sa) && (sa->spacetype == SPACE_OUTLINER)) {
1836                 SpaceOops *so = CTX_wm_space_outliner(C);
1837                 return (so->outlinevis == SO_ID_ORPHANS);
1838         }
1839         return 0;
1840 }
1841
1842 /* Purge Orphans Operator --------------------------------------- */
1843
1844 static int outliner_orphans_purge_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
1845 {
1846         /* present a prompt to informing users that this change is irreversible */
1847         return WM_operator_confirm_message(C, op,
1848                                            "Purging unused data-blocks cannot be undone and saves to current .blend file. "
1849                                            "Click here to proceed...");
1850 }
1851
1852 static int outliner_orphans_purge_exec(bContext *C, wmOperator *UNUSED(op))
1853 {
1854         /* Firstly, ensure that the file has been saved,
1855          * so that the latest changes since the last save
1856          * are retained...
1857          */
1858         WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL);
1859         
1860         /* Now, reload the file to get rid of the orphans... */
1861         WM_operator_name_call(C, "WM_OT_revert_mainfile", WM_OP_EXEC_DEFAULT, NULL);
1862         return OPERATOR_FINISHED;
1863 }
1864
1865 void OUTLINER_OT_orphans_purge(wmOperatorType *ot)
1866 {
1867         /* identifiers */
1868         ot->idname = "OUTLINER_OT_orphans_purge";
1869         ot->name = "Purge All";
1870         ot->description = "Clear all orphaned data-blocks without any users from the file "
1871                           "(cannot be undone, saves to current .blend file)";
1872         
1873         /* callbacks */
1874         ot->invoke = outliner_orphans_purge_invoke;
1875         ot->exec = outliner_orphans_purge_exec;
1876         ot->poll = ed_operator_outliner_id_orphans_active;
1877         
1878         /* flags */
1879         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1880 }
1881
1882 /* ************************************************************** */
1883 /* DRAG AND DROP OPERATORS */
1884
1885 /* ******************** Parent Drop Operator *********************** */
1886
1887 static int parent_drop_exec(bContext *C, wmOperator *op)
1888 {
1889         Object *par = NULL, *ob = NULL;
1890         Main *bmain = CTX_data_main(C);
1891         Scene *scene = CTX_data_scene(C);
1892         int partype = -1;
1893         char parname[MAX_ID_NAME], childname[MAX_ID_NAME];
1894
1895         partype = RNA_enum_get(op->ptr, "type");
1896         RNA_string_get(op->ptr, "parent", parname);
1897         par = (Object *)BKE_libblock_find_name(ID_OB, parname);
1898         RNA_string_get(op->ptr, "child", childname);
1899         ob = (Object *)BKE_libblock_find_name(ID_OB, childname);
1900
1901         if (ID_IS_LINKED(ob)) {
1902                 BKE_report(op->reports, RPT_INFO, "Can't edit library linked object");
1903                 return OPERATOR_CANCELLED;
1904         }
1905
1906         ED_object_parent_set(op->reports, C, scene, ob, par, partype, false, false, NULL);
1907
1908         DEG_relations_tag_update(bmain);
1909         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
1910         WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL);
1911
1912         return OPERATOR_FINISHED;
1913 }
1914
1915 static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1916 {
1917         Object *par = NULL;
1918         Object *ob = NULL;
1919         SpaceOops *soops = CTX_wm_space_outliner(C);
1920         ARegion *ar = CTX_wm_region(C);
1921         Main *bmain = CTX_data_main(C);
1922         Scene *scene = NULL;
1923         TreeElement *te = NULL;
1924         TreeStoreElem *tselem;
1925         char childname[MAX_ID_NAME];
1926         char parname[MAX_ID_NAME];
1927         int partype = 0;
1928         float fmval[2];
1929
1930         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
1931
1932         /* Find object hovered over */
1933         te = outliner_dropzone_find(soops, fmval, true);
1934         tselem = te ? TREESTORE(te) : NULL;
1935
1936         if (tselem && ELEM(tselem->type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION)) {
1937                 SceneCollection *sc = outliner_scene_collection_from_tree_element(te);
1938
1939                 scene = BKE_scene_find_from_collection(bmain, sc);
1940                 BLI_assert(scene);
1941                 RNA_string_get(op->ptr, "child", childname);
1942                 ob = (Object *)BKE_libblock_find_name(ID_OB, childname);
1943                 BKE_collection_object_add(scene, sc, ob);
1944
1945                 DEG_relations_tag_update(bmain);
1946                 WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
1947         }
1948         else if (te) {
1949                 RNA_string_set(op->ptr, "parent", te->name);
1950                 /* Identify parent and child */
1951                 RNA_string_get(op->ptr, "child", childname);
1952                 ob = (Object *)BKE_libblock_find_name(ID_OB, childname);
1953                 RNA_string_get(op->ptr, "parent", parname);
1954                 par = (Object *)BKE_libblock_find_name(ID_OB, parname);
1955                 
1956                 if (ELEM(NULL, ob, par)) {
1957                         if (par == NULL) printf("par==NULL\n");
1958                         return OPERATOR_CANCELLED;
1959                 }
1960                 if (ob == par) {
1961                         return OPERATOR_CANCELLED;
1962                 }
1963                 if (ID_IS_LINKED(ob)) {
1964                         BKE_report(op->reports, RPT_INFO, "Can't edit library linked object");
1965                         return OPERATOR_CANCELLED;
1966                 }
1967                 
1968                 scene = (Scene *)outliner_search_back(soops, te, ID_SCE);
1969
1970                 if (scene == NULL) {
1971                         /* currently outlier organized in a way, that if there's no parent scene
1972                          * element for object it means that all displayed objects belong to
1973                          * active scene and parenting them is allowed (sergey)
1974                          */
1975
1976                         scene = CTX_data_scene(C);
1977                 }
1978
1979                 if ((par->type != OB_ARMATURE) && (par->type != OB_CURVE) && (par->type != OB_LATTICE)) {
1980                         if (ED_object_parent_set(op->reports, C, scene, ob, par, partype, false, false, NULL)) {
1981                                 DEG_relations_tag_update(bmain);
1982                                 WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
1983                                 WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL);
1984                         }
1985                 }
1986                 else {
1987                         /* Menu creation */
1988                         wmOperatorType *ot = WM_operatortype_find("OUTLINER_OT_parent_drop", false);
1989                         uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Set Parent To"), ICON_NONE);
1990                         uiLayout *layout = UI_popup_menu_layout(pup);
1991                         PointerRNA ptr;
1992                         
1993                         /* Cannot use uiItemEnumO()... have multiple properties to set. */
1994                         uiItemFullO_ptr(layout, ot, IFACE_("Object"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
1995                         RNA_string_set(&ptr, "parent", parname);
1996                         RNA_string_set(&ptr, "child", childname);
1997                         RNA_enum_set(&ptr, "type", PAR_OBJECT);
1998
1999                         /* par becomes parent, make the associated menus */
2000                         if (par->type == OB_ARMATURE) {
2001                                 uiItemFullO_ptr(layout, ot, IFACE_("Armature Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
2002                                 RNA_string_set(&ptr, "parent", parname);
2003                                 RNA_string_set(&ptr, "child", childname);
2004                                 RNA_enum_set(&ptr, "type", PAR_ARMATURE);
2005
2006                                 uiItemFullO_ptr(layout, ot, IFACE_("   With Empty Groups"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
2007                                 RNA_string_set(&ptr, "parent", parname);
2008                                 RNA_string_set(&ptr, "child", childname);
2009                                 RNA_enum_set(&ptr, "type", PAR_ARMATURE_NAME);
2010
2011                                 uiItemFullO_ptr(layout, ot, IFACE_("   With Envelope Weights"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
2012                                 RNA_string_set(&ptr, "parent", parname);
2013                                 RNA_string_set(&ptr, "child", childname);
2014                                 RNA_enum_set(&ptr, "type", PAR_ARMATURE_ENVELOPE);
2015
2016                                 uiItemFullO_ptr(layout, ot, IFACE_("   With Automatic Weights"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
2017                                 RNA_string_set(&ptr, "parent", parname);
2018                                 RNA_string_set(&ptr, "child", childname);
2019                                 RNA_enum_set(&ptr, "type", PAR_ARMATURE_AUTO);
2020
2021                                 uiItemFullO_ptr(layout, ot, IFACE_("Bone"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
2022                                 RNA_string_set(&ptr, "parent", parname);
2023                                 RNA_string_set(&ptr, "child", childname);
2024                                 RNA_enum_set(&ptr, "type", PAR_BONE);
2025                         }
2026                         else if (par->type == OB_CURVE) {
2027                                 uiItemFullO_ptr(layout, ot, IFACE_("Curve Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
2028                                 RNA_string_set(&ptr, "parent", parname);
2029                                 RNA_string_set(&ptr, "child", childname);
2030                                 RNA_enum_set(&ptr, "type", PAR_CURVE);
2031
2032                                 uiItemFullO_ptr(layout, ot, IFACE_("Follow Path"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
2033                                 RNA_string_set(&ptr, "parent", parname);
2034                                 RNA_string_set(&ptr, "child", childname);
2035                                 RNA_enum_set(&ptr, "type", PAR_FOLLOW);
2036
2037                                 uiItemFullO_ptr(layout, ot, IFACE_("Path Constraint"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
2038                                 RNA_string_set(&ptr, "parent", parname);
2039                                 RNA_string_set(&ptr, "child", childname);
2040                                 RNA_enum_set(&ptr, "type", PAR_PATH_CONST);
2041                         }
2042                         else if (par->type == OB_LATTICE) {
2043                                 uiItemFullO_ptr(layout, ot, IFACE_("Lattice Deform"), 0, NULL, WM_OP_EXEC_DEFAULT, 0, &ptr);
2044                                 RNA_string_set(&ptr, "parent", parname);
2045                                 RNA_string_set(&ptr, "child", childname);
2046                                 RNA_enum_set(&ptr, "type", PAR_LATTICE);
2047                         }
2048                         
2049                         UI_popup_menu_end(C, pup);
2050                         
2051                         return OPERATOR_INTERFACE;
2052                 }
2053         }
2054         else {
2055                 return OPERATOR_CANCELLED;
2056         }
2057
2058         return OPERATOR_FINISHED;
2059 }
2060
2061 void OUTLINER_OT_parent_drop(wmOperatorType *ot)
2062 {
2063         /* identifiers */
2064         ot->name = "Drop to Set Parent";
2065         ot->description = "Drag to parent in Outliner";
2066         ot->idname = "OUTLINER_OT_parent_drop";
2067
2068         /* api callbacks */
2069         ot->invoke = parent_drop_invoke;
2070         ot->exec = parent_drop_exec;
2071
2072         ot->poll = ED_operator_outliner_active;
2073
2074         /* flags */
2075         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
2076
2077         /* properties */
2078         RNA_def_string(ot->srna, "child", "Object", MAX_ID_NAME, "Child", "Child Object");
2079         RNA_def_string(ot->srna, "parent", "Object", MAX_ID_NAME, "Parent", "Parent Object");
2080         RNA_def_enum(ot->srna, "type", prop_make_parent_types, 0, "Type", "");
2081 }
2082
2083 static int outliner_parenting_poll(bContext *C)
2084 {
2085         SpaceOops *soops = CTX_wm_space_outliner(C);
2086
2087         if (soops) {
2088                 return ELEM(soops->outlinevis, SO_ALL_SCENES, SO_CUR_SCENE, SO_VISIBLE, SO_GROUPS);
2089         }
2090
2091         return false;
2092 }
2093
2094 static int parent_clear_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2095 {
2096         Main *bmain = CTX_data_main(C);
2097         Object *ob = NULL;
2098         SpaceOops *soops = CTX_wm_space_outliner(C);
2099         char obname[MAX_ID_NAME];
2100
2101         RNA_string_get(op->ptr, "dragged_obj", obname);
2102         ob = (Object *)BKE_libblock_find_name(ID_OB, obname);
2103
2104         /* search forwards to find the object */
2105         outliner_find_id(soops, &soops->tree, (ID *)ob);
2106
2107         ED_object_parent_clear(ob, RNA_enum_get(op->ptr, "type"));
2108
2109         DEG_relations_tag_update(bmain);
2110         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
2111         WM_event_add_notifier(C, NC_OBJECT | ND_PARENT, NULL);
2112         return OPERATOR_FINISHED;
2113 }
2114
2115 void OUTLINER_OT_parent_clear(wmOperatorType *ot)
2116 {
2117         /* identifiers */
2118         ot->name = "Drop to Clear Parent";
2119         ot->description = "Drag to clear parent in Outliner";
2120         ot->idname = "OUTLINER_OT_parent_clear";
2121
2122         /* api callbacks */
2123         ot->invoke = parent_clear_invoke;
2124
2125         ot->poll = outliner_parenting_poll;
2126
2127         /* flags */
2128         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
2129
2130         /* properties */
2131         RNA_def_string(ot->srna, "dragged_obj", "Object", MAX_ID_NAME, "Child", "Child Object");
2132         RNA_def_enum(ot->srna, "type", prop_clear_parent_types, 0, "Type", "");
2133 }
2134
2135 static int scene_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2136 {
2137         Scene *scene = NULL;
2138         Object *ob = NULL;
2139         SpaceOops *soops = CTX_wm_space_outliner(C);
2140         ARegion *ar = CTX_wm_region(C);
2141         Main *bmain = CTX_data_main(C);
2142         TreeElement *te = NULL;
2143         char obname[MAX_ID_NAME];
2144         float fmval[2];
2145
2146         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
2147
2148         /* Find object hovered over */
2149         te = outliner_dropzone_find(soops, fmval, false);
2150
2151         if (te) {
2152                 RNA_string_set(op->ptr, "scene", te->name);
2153                 scene = (Scene *)BKE_libblock_find_name(ID_SCE, te->name);
2154
2155                 RNA_string_get(op->ptr, "object", obname);
2156                 ob = (Object *)BKE_libblock_find_name(ID_OB, obname);
2157
2158                 if (ELEM(NULL, ob, scene) || ID_IS_LINKED(scene)) {
2159                         return OPERATOR_CANCELLED;
2160                 }
2161
2162                 if (BKE_scene_has_object(scene, ob)) {
2163                         return OPERATOR_CANCELLED;
2164                 }
2165
2166                 SceneCollection *sc;
2167                 if (scene != CTX_data_scene(C)) {
2168                         /* when linking to an inactive scene link to the master collection */
2169                         sc = BKE_collection_master(scene);
2170                 }
2171                 else {
2172                         sc = CTX_data_scene_collection(C);
2173                 }
2174
2175                 BKE_collection_object_add(scene, sc, ob);
2176
2177                 for (SceneLayer *sl = scene->render_layers.first; sl; sl = sl->next) {
2178                         Base *base = BKE_scene_layer_base_find(sl, ob);
2179                         if (base) {
2180                                 ED_object_base_select(base, BA_SELECT);
2181                         }
2182                 }
2183
2184                 DEG_relations_tag_update(bmain);
2185
2186                 WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, scene);
2187
2188                 return OPERATOR_FINISHED;
2189         }
2190
2191         return OPERATOR_CANCELLED;
2192 }
2193
2194 void OUTLINER_OT_scene_drop(wmOperatorType *ot)
2195 {
2196         /* identifiers */
2197         ot->name = "Drop Object to Scene";
2198         ot->description = "Drag object to scene in Outliner";
2199         ot->idname = "OUTLINER_OT_scene_drop";
2200
2201         /* api callbacks */
2202         ot->invoke = scene_drop_invoke;
2203
2204         ot->poll = ED_operator_outliner_active;
2205
2206         /* flags */
2207         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
2208
2209         /* properties */
2210         RNA_def_string(ot->srna, "object", "Object", MAX_ID_NAME, "Object", "Target Object");
2211         RNA_def_string(ot->srna, "scene", "Scene", MAX_ID_NAME, "Scene", "Target Scene");
2212 }
2213
2214 static int material_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2215 {
2216         Material *ma = NULL;
2217         Object *ob = NULL;
2218         SpaceOops *soops = CTX_wm_space_outliner(C);
2219         ARegion *ar = CTX_wm_region(C);
2220         TreeElement *te = NULL;
2221         char mat_name[MAX_ID_NAME - 2];
2222         float fmval[2];
2223
2224         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
2225
2226         /* Find object hovered over */
2227         te = outliner_dropzone_find(soops, fmval, true);
2228
2229         if (te) {
2230                 RNA_string_set(op->ptr, "object", te->name);
2231                 ob = (Object *)BKE_libblock_find_name(ID_OB, te->name);
2232
2233                 RNA_string_get(op->ptr, "material", mat_name);
2234                 ma = (Material *)BKE_libblock_find_name(ID_MA, mat_name);
2235
2236                 if (ELEM(NULL, ob, ma)) {
2237                         return OPERATOR_CANCELLED;
2238                 }
2239
2240                 assign_material(ob, ma, ob->totcol + 1, BKE_MAT_ASSIGN_USERPREF);
2241
2242                 WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, CTX_wm_view3d(C));
2243                 WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma);
2244
2245                 return OPERATOR_FINISHED;
2246         }
2247
2248         return OPERATOR_CANCELLED;
2249 }
2250
2251 void OUTLINER_OT_material_drop(wmOperatorType *ot)
2252 {
2253         /* identifiers */
2254         ot->name = "Drop Material on Object";
2255         ot->description = "Drag material to object in Outliner";
2256         ot->idname = "OUTLINER_OT_material_drop";
2257
2258         /* api callbacks */
2259         ot->invoke = material_drop_invoke;
2260
2261         ot->poll = ED_operator_outliner_active;
2262
2263         /* flags */
2264         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
2265
2266         /* properties */
2267         RNA_def_string(ot->srna, "object", "Object", MAX_ID_NAME, "Object", "Target Object");
2268         RNA_def_string(ot->srna, "material", "Material", MAX_ID_NAME, "Material", "Target Material");
2269 }
2270
2271 static int group_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2272 {
2273         Main *bmain = CTX_data_main(C);
2274         Group *group = NULL;
2275         Object *ob = NULL;
2276         SpaceOops *soops = CTX_wm_space_outliner(C);
2277         ARegion *ar = CTX_wm_region(C);
2278         TreeElement *te = NULL;
2279         char ob_name[MAX_ID_NAME - 2];
2280         float fmval[2];
2281
2282         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
2283
2284         /* Find object hovered over */
2285         te = outliner_dropzone_find(soops, fmval, true);
2286
2287         if (te) {
2288                 group = (Group *)BKE_libblock_find_name(ID_GR, te->name);
2289
2290                 RNA_string_get(op->ptr, "object", ob_name);
2291                 ob = (Object *)BKE_libblock_find_name(ID_OB, ob_name);
2292
2293                 if (ELEM(NULL, group, ob)) {
2294                         return OPERATOR_CANCELLED;
2295                 }
2296                 if (BKE_group_object_exists(group, ob)) {
2297                         return OPERATOR_FINISHED;
2298                 }
2299
2300                 if (BKE_group_object_cyclic_check(bmain, ob, group)) {
2301                         BKE_report(op->reports, RPT_ERROR, "Could not add the group because of dependency cycle detected");
2302                         return OPERATOR_CANCELLED;
2303                 }
2304
2305                 BKE_group_object_add(group, ob);
2306                 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
2307
2308                 return OPERATOR_FINISHED;
2309         }
2310
2311         return OPERATOR_CANCELLED;
2312 }
2313
2314 void OUTLINER_OT_group_link(wmOperatorType *ot)
2315 {
2316         /* identifiers */
2317         ot->name = "Link Object to Group";
2318         ot->description = "Link Object to Group in Outliner";
2319         ot->idname = "OUTLINER_OT_group_link";
2320
2321         /* api callbacks */
2322         ot->invoke = group_link_invoke;
2323
2324         ot->poll = ED_operator_outliner_active;
2325
2326         /* flags */
2327         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
2328
2329         /* properties */
2330         RNA_def_string(ot->srna, "object", "Object", MAX_ID_NAME, "Object", "Target Object");
2331 }