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