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