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