Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / space_outliner / outliner_tree.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_tree.c
29  *  \ingroup spoutliner
30  */
31  
32 #include <math.h>
33 #include <string.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "DNA_anim_types.h"
38 #include "DNA_armature_types.h"
39 #include "DNA_constraint_types.h"
40 #include "DNA_camera_types.h"
41 #include "DNA_cachefile_types.h"
42 #include "DNA_gpencil_types.h"
43 #include "DNA_group_types.h"
44 #include "DNA_key_types.h"
45 #include "DNA_lamp_types.h"
46 #include "DNA_material_types.h"
47 #include "DNA_mesh_types.h"
48 #include "DNA_meta_types.h"
49 #include "DNA_lightprobe_types.h"
50 #include "DNA_particle_types.h"
51 #include "DNA_scene_types.h"
52 #include "DNA_world_types.h"
53 #include "DNA_sequence_types.h"
54 #include "DNA_speaker_types.h"
55 #include "DNA_object_types.h"
56 #include "DNA_linestyle_types.h"
57
58 #include "BLI_blenlib.h"
59 #include "BLI_utildefines.h"
60 #include "BLI_mempool.h"
61 #include "BLI_fnmatch.h"
62
63 #include "BLT_translation.h"
64
65 #include "BKE_fcurve.h"
66 #include "BKE_main.h"
67 #include "BKE_layer.h"
68 #include "BKE_library.h"
69 #include "BKE_modifier.h"
70 #include "BKE_sequencer.h"
71 #include "BKE_idcode.h"
72 #include "BKE_outliner_treehash.h"
73
74 #include "DEG_depsgraph.h"
75 #include "DEG_depsgraph_build.h"
76
77 #include "ED_armature.h"
78 #include "ED_screen.h"
79
80 #include "WM_api.h"
81 #include "WM_types.h"
82
83 #include "RNA_access.h"
84
85 #include "UI_interface.h"
86
87 #include "outliner_intern.h"
88
89 #ifdef WIN32
90 #  include "BLI_math_base.h" /* M_PI */
91 #endif
92
93 /* prototypes */
94 static void outliner_add_layer_collections_recursive(
95         SpaceOops *soops, const EvaluationContext *eval_ctx,
96         ListBase *tree, ID *id, ListBase *layer_collections, TreeElement *parent_ten,
97         const bool show_objects);
98 static void outliner_add_view_layer(
99         SpaceOops *soops, const EvaluationContext *eval_ctx,
100         ListBase *tree, TreeElement *parent,
101         Scene *scene, ViewLayer *layer, const bool show_objects);
102 static void outliner_make_hierarchy(ListBase *lb);
103
104 /* ********************************************************* */
105 /* Persistent Data */
106
107 static void outliner_storage_cleanup(SpaceOops *soops)
108 {
109         BLI_mempool *ts = soops->treestore;
110         
111         if (ts) {
112                 TreeStoreElem *tselem;
113                 int unused = 0;
114                 
115                 /* each element used once, for ID blocks with more users to have each a treestore */
116                 BLI_mempool_iter iter;
117
118                 BLI_mempool_iternew(ts, &iter);
119                 while ((tselem = BLI_mempool_iterstep(&iter))) {
120                         tselem->used = 0;
121                 }
122                 
123                 /* cleanup only after reading file or undo step, and always for
124                  * RNA datablocks view in order to save memory */
125                 if (soops->storeflag & SO_TREESTORE_CLEANUP) {
126                         soops->storeflag &= ~SO_TREESTORE_CLEANUP;
127
128                         BLI_mempool_iternew(ts, &iter);
129                         while ((tselem = BLI_mempool_iterstep(&iter))) {
130                                 if (tselem->id == NULL) unused++;
131                         }
132                         
133                         if (unused) {
134                                 if (BLI_mempool_len(ts) == unused) {
135                                         BLI_mempool_destroy(ts);
136                                         soops->treestore = NULL;
137                                         if (soops->treehash) {
138                                                 BKE_outliner_treehash_free(soops->treehash);
139                                                 soops->treehash = NULL;
140                                         }
141                                 }
142                                 else {
143                                         TreeStoreElem *tsenew;
144                                         BLI_mempool *new_ts = BLI_mempool_create(sizeof(TreeStoreElem), BLI_mempool_len(ts) - unused,
145                                                                                  512, BLI_MEMPOOL_ALLOW_ITER);
146                                         BLI_mempool_iternew(ts, &iter);
147                                         while ((tselem = BLI_mempool_iterstep(&iter))) {
148                                                 if (tselem->id) {
149                                                         tsenew = BLI_mempool_alloc(new_ts);
150                                                         *tsenew = *tselem;
151                                                 }
152                                         }
153                                         BLI_mempool_destroy(ts);
154                                         soops->treestore = new_ts;
155                                         if (soops->treehash) {
156                                                 /* update hash table to fix broken pointers */
157                                                 BKE_outliner_treehash_rebuild_from_treestore(soops->treehash, soops->treestore);
158                                         }
159                                 }
160                         }
161                 }
162         }
163 }
164
165 static void check_persistent(SpaceOops *soops, TreeElement *te, ID *id, short type, short nr)
166 {
167         TreeStoreElem *tselem;
168         
169         if (soops->treestore == NULL) {
170                 /* if treestore was not created in readfile.c, create it here */
171                 soops->treestore = BLI_mempool_create(sizeof(TreeStoreElem), 1, 512, BLI_MEMPOOL_ALLOW_ITER);
172                 
173         }
174         if (soops->treehash == NULL) {
175                 soops->treehash = BKE_outliner_treehash_create_from_treestore(soops->treestore);
176         }
177
178         /* find any unused tree element in treestore and mark it as used
179          * (note that there may be multiple unused elements in case of linked objects) */
180         tselem = BKE_outliner_treehash_lookup_unused(soops->treehash, type, nr, id);
181         if (tselem) {
182                 te->store_elem = tselem;
183                 tselem->used = 1;
184                 return;
185         }
186
187         /* add 1 element to treestore */
188         tselem = BLI_mempool_alloc(soops->treestore);
189         tselem->type = type;
190         tselem->nr = type ? nr : 0;
191         tselem->id = id;
192         tselem->used = 0;
193         tselem->flag = TSE_CLOSED;
194         te->store_elem = tselem;
195         BKE_outliner_treehash_add_element(soops->treehash, tselem);
196 }
197
198 /* ********************************************************* */
199 /* Tree Management */
200
201 void outliner_free_tree(ListBase *tree)
202 {
203         for (TreeElement *element = tree->first, *element_next; element; element = element_next) {
204                 element_next = element->next;
205                 outliner_free_tree_element(element, tree);
206         }
207 }
208
209 void outliner_cleanup_tree(SpaceOops *soops)
210 {
211         outliner_free_tree(&soops->tree);
212         outliner_storage_cleanup(soops);
213 }
214
215 /**
216  * Free \a element and its sub-tree and remove its link in \a parent_subtree.
217  *
218  * \note Does not remove the TreeStoreElem of \a element!
219  * \param parent_subtree Subtree of the parent element, so the list containing \a element.
220  */
221 void outliner_free_tree_element(TreeElement *element, ListBase *parent_subtree)
222 {
223         BLI_assert(BLI_findindex(parent_subtree, element) > -1);
224         BLI_remlink(parent_subtree, element);
225
226         outliner_free_tree(&element->subtree);
227
228         if (element->flag & TE_FREE_NAME) {
229                 MEM_freeN((void *)element->name);
230         }
231         MEM_freeN(element);
232 }
233
234
235 /* ********************************************************* */
236
237 /* Prototype, see functions below */
238 static TreeElement *outliner_add_element(
239         SpaceOops *soops, const EvaluationContext *eval_ctx,
240         ListBase *lb, void *idv, TreeElement *parent, short type, short index);
241
242 /* -------------------------------------------------------- */
243
244 /* special handling of hierarchical non-lib data */
245 static void outliner_add_bone(
246         SpaceOops *soops, const EvaluationContext *eval_ctx,
247         ListBase *lb, ID *id, Bone *curBone, TreeElement *parent, int *a)
248 {
249         TreeElement *te = outliner_add_element(soops, eval_ctx, lb, id, parent, TSE_BONE, *a);
250         
251         (*a)++;
252         te->name = curBone->name;
253         te->directdata = curBone;
254         
255         for (curBone = curBone->childbase.first; curBone; curBone = curBone->next) {
256                 outliner_add_bone(soops, eval_ctx, &te->subtree, id, curBone, te, a);
257         }
258 }
259
260 /* -------------------------------------------------------- */
261
262 #define LOG2I(x) (int)(log(x) / M_LN2)
263
264 static void outliner_add_passes(
265         SpaceOops *soops, const EvaluationContext *eval_ctx,
266         TreeElement *tenla, ID *id, ViewLayer *view_layer)
267 {
268         TreeStoreElem *tselem = NULL;
269         TreeElement *te = NULL;
270
271         /* log stuff is to convert bitflags (powers of 2) to small integers,
272          * in order to not overflow short tselem->nr */
273         
274         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_COMBINED));
275         te->name = IFACE_("Combined");
276         te->directdata = &view_layer->passflag;
277         
278         /* save cpu cycles, but we add the first to invoke an open/close triangle */
279         tselem = TREESTORE(tenla);
280         if (tselem->flag & TSE_CLOSED)
281                 return;
282         
283         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_Z));
284         te->name = IFACE_("Z");
285         te->directdata = &view_layer->passflag;
286
287         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_VECTOR));
288         te->name = IFACE_("Vector");
289         te->directdata = &view_layer->passflag;
290
291         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_NORMAL));
292         te->name = IFACE_("Normal");
293         te->directdata = &view_layer->passflag;
294
295         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_UV));
296         te->name = IFACE_("UV");
297         te->directdata = &view_layer->passflag;
298
299         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_MIST));
300         te->name = IFACE_("Mist");
301         te->directdata = &view_layer->passflag;
302
303         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_INDEXOB));
304         te->name = IFACE_("Index Object");
305         te->directdata = &view_layer->passflag;
306
307         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_INDEXMA));
308         te->name = IFACE_("Index Material");
309         te->directdata = &view_layer->passflag;
310
311         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_RGBA));
312         te->name = IFACE_("Color");
313         te->directdata = &view_layer->passflag;
314
315         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_DIFFUSE));
316         te->name = IFACE_("Diffuse");
317         te->directdata = &view_layer->passflag;
318
319         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_SPEC));
320         te->name = IFACE_("Specular");
321         te->directdata = &view_layer->passflag;
322
323         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_SHADOW));
324         te->name = IFACE_("Shadow");
325         te->directdata = &view_layer->passflag;
326
327         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_AO));
328         te->name = IFACE_("AO");
329         te->directdata = &view_layer->passflag;
330
331         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_REFLECT));
332         te->name = IFACE_("Reflection");
333         te->directdata = &view_layer->passflag;
334
335         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_REFRACT));
336         te->name = IFACE_("Refraction");
337         te->directdata = &view_layer->passflag;
338
339         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_INDIRECT));
340         te->name = IFACE_("Indirect");
341         te->directdata = &view_layer->passflag;
342
343         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_ENVIRONMENT));
344         te->name = IFACE_("Environment");
345         te->directdata = &view_layer->passflag;
346
347         te = outliner_add_element(soops, eval_ctx, &tenla->subtree, id, tenla, TSE_R_PASS, LOG2I(SCE_PASS_EMIT));
348         te->name = IFACE_("Emit");
349         te->directdata = &view_layer->passflag;
350 }
351
352 #undef LOG2I
353
354 static bool outliner_animdata_test(AnimData *adt)
355 {
356         if (adt)
357                 return (adt->action || adt->drivers.first || adt->nla_tracks.first);
358         return false;
359 }
360
361 #ifdef WITH_FREESTYLE
362 static void outliner_add_line_styles(SpaceOops *soops, ListBase *lb, Scene *sce, TreeElement *te)
363 {
364         ViewLayer *view_layer;
365         FreestyleLineSet *lineset;
366
367         for (view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) {
368                 for (lineset = view_layer->freestyle_config.linesets.first; lineset; lineset = lineset->next) {
369                         FreestyleLineStyle *linestyle = lineset->linestyle;
370                         if (linestyle) {
371                                 linestyle->id.tag |= LIB_TAG_DOIT;
372                         }
373                 }
374         }
375         for (view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) {
376                 for (lineset = view_layer->freestyle_config.linesets.first; lineset; lineset = lineset->next) {
377                         FreestyleLineStyle *linestyle = lineset->linestyle;
378                         if (linestyle) {
379                                 if (!(linestyle->id.tag & LIB_TAG_DOIT))
380                                         continue;
381                                 linestyle->id.tag &= ~LIB_TAG_DOIT;
382                                 outliner_add_element(soops, eval_ctx, lb, linestyle, te, 0, 0);
383                         }
384                 }
385         }
386 }
387 #endif
388
389 static void outliner_add_scene_contents(
390         SpaceOops *soops, const EvaluationContext *eval_ctx,
391         ListBase *lb, Scene *sce, TreeElement *te)
392 {
393         ViewLayer *view_layer;
394         TreeElement *tenla = outliner_add_element(soops, eval_ctx, lb, sce, te, TSE_R_LAYER_BASE, 0);
395         int a;
396         
397         tenla->name = IFACE_("View Layers");
398         for (a = 0, view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next, a++) {
399                 TreeElement *tenlay = outliner_add_element(soops, eval_ctx, &tenla->subtree, sce, te, TSE_R_LAYER, a);
400                 tenlay->name = view_layer->name;
401                 tenlay->directdata = &view_layer->flag;
402
403                 TreeElement *te_view_layers;
404                 te_view_layers = outliner_add_element(soops, eval_ctx, &tenlay->subtree, sce, tenlay, TSE_LAYER_COLLECTION_BASE, 0);
405                 te_view_layers->name = IFACE_("Collections");
406                 outliner_add_view_layer(soops, eval_ctx, &te_view_layers->subtree, te_view_layers, sce, view_layer, false);
407
408                 TreeElement *te_passes;
409                 te_passes = outliner_add_element(soops, eval_ctx, &tenlay->subtree, sce, tenlay, TSE_LAYER_COLLECTION_BASE, 0);
410                 te_passes->name = IFACE_("Passes");
411                 outliner_add_passes(soops, eval_ctx, te_passes, &sce->id, view_layer);
412         }
413         
414         // TODO: move this to the front?
415         if (outliner_animdata_test(sce->adt))
416                 outliner_add_element(soops, eval_ctx, lb, sce, te, TSE_ANIM_DATA, 0);
417                 
418         outliner_add_element(soops, eval_ctx, lb, sce->gpd, te, 0, 0);
419         
420 #ifdef WITH_FREESTYLE
421         if (STREQ(sce->view_render->engine_id, RE_engine_id_BLENDER_RENDER) && (sce->r.mode & R_EDGE_FRS))
422                 outliner_add_line_styles(soops, lb, sce, te);
423 #endif
424 }
425
426 TreeTraversalAction outliner_find_selected_objects(TreeElement *te, void *customdata)
427 {
428         struct ObjectsSelectedData *data = customdata;
429         TreeStoreElem *tselem = TREESTORE(te);
430
431         if (ELEM(tselem->type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION)) {
432                 return TRAVERSE_CONTINUE;
433         }
434
435         if (tselem->type || (tselem->id == NULL) || (GS(tselem->id->name) != ID_OB)) {
436                 return TRAVERSE_SKIP_CHILDS;
437         }
438
439         BLI_addtail(&data->objects_selected_array, BLI_genericNodeN(te));
440
441         return TRAVERSE_CONTINUE;
442 }
443
444 /**
445  * Move objects from a collection to another.
446  * We ignore the original object being inserted, we used it for polling only.
447  * Instead we move all the selected objects around.
448  */
449 static void outliner_object_reorder(
450         Main *bmain, SpaceOops *soops,
451         TreeElement *insert_element,
452         TreeElement *insert_handle, TreeElementInsertType action,
453         const wmEvent *event)
454 {
455         SceneCollection *sc = outliner_scene_collection_from_tree_element(insert_handle);
456         SceneCollection *sc_ob_parent = NULL;
457         ID *id = insert_handle->store_elem->id;
458
459         BLI_assert(action == TE_INSERT_INTO);
460         UNUSED_VARS_NDEBUG(action);
461
462         struct ObjectsSelectedData data = {
463                 .objects_selected_array  = {NULL, NULL},
464         };
465
466         const bool is_append = event->ctrl;
467
468         /* Make sure we include the originally inserted element as well. */
469         TREESTORE(insert_element)->flag |= TSE_SELECTED;
470
471         outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, outliner_find_selected_objects, &data);
472         LISTBASE_FOREACH (LinkData *, link, &data.objects_selected_array) {
473                 TreeElement *ten_selected = (TreeElement *)link->data;
474                 Object *ob = (Object *)TREESTORE(ten_selected)->id;
475
476                 if (is_append) {
477                         BKE_collection_object_add(id, sc, ob);
478                         continue;
479                 }
480
481                 /* Find parent scene-collection of object. */
482                 if (ten_selected->parent) {
483                         for (TreeElement *te_ob_parent = ten_selected->parent; te_ob_parent; te_ob_parent = te_ob_parent->parent) {
484                                 if (ELEM(TREESTORE(te_ob_parent)->type, TSE_SCENE_COLLECTION, TSE_LAYER_COLLECTION)) {
485                                         sc_ob_parent = outliner_scene_collection_from_tree_element(te_ob_parent);
486                                         break;
487                                 }
488                         }
489                 }
490                 else {
491                         sc_ob_parent = BKE_collection_master(id);
492                 }
493
494                 BKE_collection_object_move(id, sc, sc_ob_parent, ob);
495         }
496
497         BLI_freelistN(&data.objects_selected_array);
498
499         DEG_relations_tag_update(bmain);
500
501         /* TODO(sergey): Use proper flag for tagging here. */
502         DEG_id_tag_update(id, 0);
503
504         WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
505 }
506
507 static bool outliner_object_reorder_poll(
508         const TreeElement *insert_element,
509         TreeElement **io_insert_handle, TreeElementInsertType *io_action)
510 {
511         TreeStoreElem *tselem_handle = TREESTORE(*io_insert_handle);
512         if (ELEM(tselem_handle->type, TSE_SCENE_COLLECTION, TSE_LAYER_COLLECTION) &&
513             (insert_element->parent != *io_insert_handle))
514         {
515                 *io_action = TE_INSERT_INTO;
516                 return true;
517         }
518
519         return false;
520 }
521
522 // can be inlined if necessary
523 static void outliner_add_object_contents(
524         SpaceOops *soops, const EvaluationContext *eval_ctx,
525         TreeElement *te, TreeStoreElem *tselem, Object *ob)
526 {
527         te->reinsert = outliner_object_reorder;
528         te->reinsert_poll = outliner_object_reorder_poll;
529
530         if (outliner_animdata_test(ob->adt))
531                 outliner_add_element(soops, eval_ctx, &te->subtree, ob, te, TSE_ANIM_DATA, 0);
532
533         outliner_add_element(soops, eval_ctx, &te->subtree, ob->poselib, te, 0, 0); // XXX FIXME.. add a special type for this
534         
535         if (ob->proxy && !ID_IS_LINKED(ob))
536                 outliner_add_element(soops, eval_ctx, &te->subtree, ob->proxy, te, TSE_PROXY, 0);
537                 
538         outliner_add_element(soops, eval_ctx, &te->subtree, ob->gpd, te, 0, 0);
539         
540         outliner_add_element(soops, eval_ctx, &te->subtree, ob->data, te, 0, 0);
541         
542         if (ob->pose) {
543                 bArmature *arm = ob->data;
544                 bPoseChannel *pchan;
545                 TreeElement *tenla = outliner_add_element(
546                         soops, eval_ctx, &te->subtree, ob, te, TSE_POSE_BASE, 0);
547                 
548                 tenla->name = IFACE_("Pose");
549                 
550                 /* channels undefined in editmode, but we want the 'tenla' pose icon itself */
551                 if ((arm->edbo == NULL) && (eval_ctx->object_mode & OB_MODE_POSE)) {
552                         TreeElement *ten;
553                         int a = 0, const_index = 1000;    /* ensure unique id for bone constraints */
554                         
555                         for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next, a++) {
556                                 ten = outliner_add_element(
557                                         soops, eval_ctx, &tenla->subtree, ob, tenla, TSE_POSE_CHANNEL, a);
558                                 ten->name = pchan->name;
559                                 ten->directdata = pchan;
560                                 pchan->temp = (void *)ten;
561                                 
562                                 if (pchan->constraints.first) {
563                                         //Object *target;
564                                         bConstraint *con;
565                                         TreeElement *ten1;
566                                         TreeElement *tenla1 = outliner_add_element(
567                                                 soops, eval_ctx, &ten->subtree, ob, ten, TSE_CONSTRAINT_BASE, 0);
568                                         //char *str;
569                                         
570                                         tenla1->name = IFACE_("Constraints");
571                                         for (con = pchan->constraints.first; con; con = con->next, const_index++) {
572                                                 ten1 = outliner_add_element(
573                                                         soops, eval_ctx, &tenla1->subtree, ob, tenla1, TSE_CONSTRAINT, const_index);
574 #if 0 /* disabled as it needs to be reworked for recoded constraints system */
575                                                 target = get_constraint_target(con, &str);
576                                                 if (str && str[0]) ten1->name = str;
577                                                 else if (target) ten1->name = target->id.name + 2;
578                                                 else ten1->name = con->name;
579 #endif
580                                                 ten1->name = con->name;
581                                                 ten1->directdata = con;
582                                                 /* possible add all other types links? */
583                                         }
584                                 }
585                         }
586                         /* make hierarchy */
587                         ten = tenla->subtree.first;
588                         while (ten) {
589                                 TreeElement *nten = ten->next, *par;
590                                 tselem = TREESTORE(ten);
591                                 if (tselem->type == TSE_POSE_CHANNEL) {
592                                         pchan = (bPoseChannel *)ten->directdata;
593                                         if (pchan->parent) {
594                                                 BLI_remlink(&tenla->subtree, ten);
595                                                 par = (TreeElement *)pchan->parent->temp;
596                                                 BLI_addtail(&par->subtree, ten);
597                                                 ten->parent = par;
598                                         }
599                                 }
600                                 ten = nten;
601                         }
602                 }
603                 
604                 /* Pose Groups */
605                 if (ob->pose->agroups.first) {
606                         bActionGroup *agrp;
607                         TreeElement *ten_bonegrp = outliner_add_element(
608                                 soops, eval_ctx, &te->subtree, ob, te, TSE_POSEGRP_BASE, 0);
609                         int a = 0;
610
611                         ten_bonegrp->name = IFACE_("Bone Groups");
612                         for (agrp = ob->pose->agroups.first; agrp; agrp = agrp->next, a++) {
613                                 TreeElement *ten;
614                                 ten = outliner_add_element(
615                                         soops, eval_ctx, &ten_bonegrp->subtree, ob, ten_bonegrp, TSE_POSEGRP, a);
616                                 ten->name = agrp->name;
617                                 ten->directdata = agrp;
618                         }
619                 }
620         }
621         
622         for (int a = 0; a < ob->totcol; a++) {
623                 outliner_add_element(
624                         soops, eval_ctx, &te->subtree, ob->mat[a], te, 0, a);
625         }
626         
627         if (ob->constraints.first) {
628                 //Object *target;
629                 bConstraint *con;
630                 TreeElement *ten;
631                 TreeElement *tenla = outliner_add_element(soops, eval_ctx, &te->subtree, ob, te, TSE_CONSTRAINT_BASE, 0);
632                 //char *str;
633                 int a;
634                 
635                 tenla->name = IFACE_("Constraints");
636                 for (con = ob->constraints.first, a = 0; con; con = con->next, a++) {
637                         ten = outliner_add_element(soops, eval_ctx, &tenla->subtree, ob, tenla, TSE_CONSTRAINT, a);
638 #if 0 /* disabled due to constraints system targets recode... code here needs review */
639                         target = get_constraint_target(con, &str);
640                         if (str && str[0]) ten->name = str;
641                         else if (target) ten->name = target->id.name + 2;
642                         else ten->name = con->name;
643 #endif
644                         ten->name = con->name;
645                         ten->directdata = con;
646                         /* possible add all other types links? */
647                 }
648         }
649         
650         if (ob->modifiers.first) {
651                 ModifierData *md;
652                 TreeElement *ten_mod = outliner_add_element(soops, eval_ctx, &te->subtree, ob, te, TSE_MODIFIER_BASE, 0);
653                 int index;
654                 
655                 ten_mod->name = IFACE_("Modifiers");
656                 for (index = 0, md = ob->modifiers.first; md; index++, md = md->next) {
657                         TreeElement *ten = outliner_add_element(
658                                 soops, eval_ctx, &ten_mod->subtree, ob, ten_mod, TSE_MODIFIER, index);
659                         ten->name = md->name;
660                         ten->directdata = md;
661                         
662                         if (md->type == eModifierType_Lattice) {
663                                 outliner_add_element(
664                                         soops, eval_ctx, &ten->subtree, ((LatticeModifierData *) md)->object, ten, TSE_LINKED_OB, 0);
665                         }
666                         else if (md->type == eModifierType_Curve) {
667                                 outliner_add_element(
668                                         soops, eval_ctx, &ten->subtree, ((CurveModifierData *) md)->object, ten, TSE_LINKED_OB, 0);
669                         }
670                         else if (md->type == eModifierType_Armature) {
671                                 outliner_add_element(
672                                         soops, eval_ctx, &ten->subtree, ((ArmatureModifierData *) md)->object, ten, TSE_LINKED_OB, 0);
673                         }
674                         else if (md->type == eModifierType_Hook) {
675                                 outliner_add_element(
676                                         soops, eval_ctx, &ten->subtree, ((HookModifierData *) md)->object, ten, TSE_LINKED_OB, 0);
677                         }
678                         else if (md->type == eModifierType_ParticleSystem) {
679                                 ParticleSystem *psys = ((ParticleSystemModifierData *) md)->psys;
680                                 TreeElement *ten_psys;
681                                 
682                                 ten_psys = outliner_add_element(
683                                         soops, eval_ctx, &ten->subtree, ob, te, TSE_LINKED_PSYS, 0);
684                                 ten_psys->directdata = psys;
685                                 ten_psys->name = psys->part->id.name + 2;
686                         }
687                 }
688         }
689         
690         /* vertex groups */
691         if (ob->defbase.first) {
692                 bDeformGroup *defgroup;
693                 TreeElement *ten;
694                 TreeElement *tenla = outliner_add_element(
695                         soops, eval_ctx, &te->subtree, ob, te, TSE_DEFGROUP_BASE, 0);
696                 int a;
697                 
698                 tenla->name = IFACE_("Vertex Groups");
699                 for (defgroup = ob->defbase.first, a = 0; defgroup; defgroup = defgroup->next, a++) {
700                         ten = outliner_add_element(soops, eval_ctx, &tenla->subtree, ob, tenla, TSE_DEFGROUP, a);
701                         ten->name = defgroup->name;
702                         ten->directdata = defgroup;
703                 }
704         }
705         
706         /* duplicated group */
707         if (ob->dup_group) {
708                 outliner_add_element(soops, eval_ctx, &te->subtree, ob->dup_group, te, 0, 0);
709         }
710 }
711
712
713 // can be inlined if necessary
714 static void outliner_add_id_contents(
715         SpaceOops *soops, const EvaluationContext *eval_ctx,
716         TreeElement *te, TreeStoreElem *tselem, ID *id)
717 {
718         /* tuck pointer back in object, to construct hierarchy */
719         if (GS(id->name) == ID_OB) id->newid = (ID *)te;
720         
721         /* expand specific data always */
722         switch (GS(id->name)) {
723                 case ID_LI:
724                 {
725                         te->name = ((Library *)id)->name;
726                         break;
727                 }
728                 case ID_SCE:
729                 {
730                         outliner_add_scene_contents(soops, eval_ctx, &te->subtree, (Scene *)id, te);
731                         break;
732                 }
733                 case ID_OB:
734                 {
735                         outliner_add_object_contents(soops, eval_ctx, te, tselem, (Object *)id);
736                         break;
737                 }
738                 case ID_ME:
739                 {
740                         Mesh *me = (Mesh *)id;
741                         int a;
742
743                         if (outliner_animdata_test(me->adt)) {
744                                 outliner_add_element(soops, eval_ctx, &te->subtree, me, te, TSE_ANIM_DATA, 0);
745                         }
746
747                         outliner_add_element(soops, eval_ctx, &te->subtree, me->key, te, 0, 0);
748                         for (a = 0; a < me->totcol; a++)
749                                 outliner_add_element(soops, eval_ctx, &te->subtree, me->mat[a], te, 0, a);
750                         /* could do tfaces with image links, but the images are not grouped nicely.
751                          * would require going over all tfaces, sort images in use. etc... */
752                         break;
753                 }
754                 case ID_CU:
755                 {
756                         Curve *cu = (Curve *)id;
757                         int a;
758                         
759                         if (outliner_animdata_test(cu->adt))
760                                 outliner_add_element(soops, eval_ctx, &te->subtree, cu, te, TSE_ANIM_DATA, 0);
761                         
762                         for (a = 0; a < cu->totcol; a++)
763                                 outliner_add_element(soops, eval_ctx, &te->subtree, cu->mat[a], te, 0, a);
764                         break;
765                 }
766                 case ID_MB:
767                 {
768                         MetaBall *mb = (MetaBall *)id;
769                         int a;
770                         
771                         if (outliner_animdata_test(mb->adt))
772                                 outliner_add_element(soops, eval_ctx, &te->subtree, mb, te, TSE_ANIM_DATA, 0);
773                         
774                         for (a = 0; a < mb->totcol; a++)
775                                 outliner_add_element(soops, eval_ctx, &te->subtree, mb->mat[a], te, 0, a);
776                         break;
777                 }
778                 case ID_MA:
779                 {
780                         Material *ma = (Material *)id;
781                         int a;
782                         
783                         if (outliner_animdata_test(ma->adt)) {
784                                 outliner_add_element(soops, eval_ctx, &te->subtree, ma, te, TSE_ANIM_DATA, 0);
785                         }
786                         
787                         for (a = 0; a < MAX_MTEX; a++) {
788                                 if (ma->mtex[a]) {
789                                         outliner_add_element(soops, eval_ctx, &te->subtree, ma->mtex[a]->tex, te, 0, a);
790                                 }
791                         }
792                         break;
793                 }
794                 case ID_TE:
795                 {
796                         Tex *tex = (Tex *)id;
797                         
798                         if (outliner_animdata_test(tex->adt)) {
799                                 outliner_add_element(soops, eval_ctx, &te->subtree, tex, te, TSE_ANIM_DATA, 0);
800                         }
801                         outliner_add_element(soops, eval_ctx, &te->subtree, tex->ima, te, 0, 0);
802                         break;
803                 }
804                 case ID_CA:
805                 {
806                         Camera *ca = (Camera *)id;
807                         
808                         if (outliner_animdata_test(ca->adt))
809                                 outliner_add_element(soops, eval_ctx, &te->subtree, ca, te, TSE_ANIM_DATA, 0);
810                         break;
811                 }
812                 case ID_CF:
813                 {
814                         CacheFile *cache_file = (CacheFile *)id;
815
816                         if (outliner_animdata_test(cache_file->adt)) {
817                                 outliner_add_element(soops, eval_ctx, &te->subtree, cache_file, te, TSE_ANIM_DATA, 0);
818                         }
819
820                         break;
821                 }
822                 case ID_LA:
823                 {
824                         Lamp *la = (Lamp *)id;
825                         int a;
826                         
827                         if (outliner_animdata_test(la->adt))
828                                 outliner_add_element(soops, eval_ctx, &te->subtree, la, te, TSE_ANIM_DATA, 0);
829                         
830                         for (a = 0; a < MAX_MTEX; a++) {
831                                 if (la->mtex[a]) {
832                                         outliner_add_element(soops, eval_ctx, &te->subtree, la->mtex[a]->tex, te, 0, a);
833                                 }
834                         }
835                         break;
836                 }
837                 case ID_SPK:
838                 {
839                         Speaker *spk = (Speaker *)id;
840
841                         if (outliner_animdata_test(spk->adt)) {
842                                 outliner_add_element(soops, eval_ctx, &te->subtree, spk, te, TSE_ANIM_DATA, 0);
843                         }
844                         break;
845                 }
846                 case ID_LP:
847                 {
848                         LightProbe *prb = (LightProbe *)id;
849
850                         if (outliner_animdata_test(prb->adt)) {
851                                 outliner_add_element(soops, eval_ctx, &te->subtree, prb, te, TSE_ANIM_DATA, 0);
852                         }
853                         break;
854                 }
855                 case ID_WO:
856                 {
857                         World *wrld = (World *)id;
858                         int a;
859                         
860                         if (outliner_animdata_test(wrld->adt)) {
861                                 outliner_add_element(soops, eval_ctx, &te->subtree, wrld, te, TSE_ANIM_DATA, 0);
862                         }
863                         for (a = 0; a < MAX_MTEX; a++) {
864                                 if (wrld->mtex[a]) {
865                                         outliner_add_element(soops, eval_ctx, &te->subtree, wrld->mtex[a]->tex, te, 0, a);
866                                 }
867                         }
868                         break;
869                 }
870                 case ID_KE:
871                 {
872                         Key *key = (Key *)id;
873                         
874                         if (outliner_animdata_test(key->adt)) {
875                                 outliner_add_element(soops, eval_ctx, &te->subtree, key, te, TSE_ANIM_DATA, 0);
876                         }
877                         break;
878                 }
879                 case ID_AC:
880                 {
881                         // XXX do we want to be exposing the F-Curves here?
882                         //bAction *act = (bAction *)id;
883                         break;
884                 }
885                 case ID_AR:
886                 {
887                         bArmature *arm = (bArmature *)id;
888                         int a = 0;
889                         
890                         if (outliner_animdata_test(arm->adt)) {
891                                 outliner_add_element(soops, eval_ctx, &te->subtree, arm, te, TSE_ANIM_DATA, 0);
892                         }
893                         if (arm->edbo) {
894                                 EditBone *ebone;
895                                 TreeElement *ten;
896                                 
897                                 for (ebone = arm->edbo->first; ebone; ebone = ebone->next, a++) {
898                                         ten = outliner_add_element(soops, eval_ctx, &te->subtree, id, te, TSE_EBONE, a);
899                                         ten->directdata = ebone;
900                                         ten->name = ebone->name;
901                                         ebone->temp.p = ten;
902                                 }
903                                 /* make hierarchy */
904                                 ten = arm->edbo->first ? ((EditBone *)arm->edbo->first)->temp.p : NULL;
905                                 while (ten) {
906                                         TreeElement *nten = ten->next, *par;
907                                         ebone = (EditBone *)ten->directdata;
908                                         if (ebone->parent) {
909                                                 BLI_remlink(&te->subtree, ten);
910                                                 par = ebone->parent->temp.p;
911                                                 BLI_addtail(&par->subtree, ten);
912                                                 ten->parent = par;
913                                         }
914                                         ten = nten;
915                                 }
916                         }
917                         else {
918                                 /* do not extend Armature when we have posemode */
919                                 tselem = TREESTORE(te->parent);
920                                 if (GS(tselem->id->name) == ID_OB &&
921                                     (eval_ctx->object_mode & OB_MODE_POSE)
922                                     /* (((Object *)tselem->id)->mode & OB_MODE_POSE) */
923                                     )
924                                 {
925                                         /* pass */
926                                 }
927                                 else {
928                                         Bone *curBone;
929                                         for (curBone = arm->bonebase.first; curBone; curBone = curBone->next) {
930                                                 outliner_add_bone(soops, eval_ctx, &te->subtree, id, curBone, te, &a);
931                                         }
932                                 }
933                         }
934                         break;
935                 }
936                 case ID_LS:
937                 {
938                         FreestyleLineStyle *linestyle = (FreestyleLineStyle *)id;
939                         int a;
940
941                         if (outliner_animdata_test(linestyle->adt)) {
942                                 outliner_add_element(soops, eval_ctx, &te->subtree, linestyle, te, TSE_ANIM_DATA, 0);
943                         }
944
945                         for (a = 0; a < MAX_MTEX; a++) {
946                                 if (linestyle->mtex[a])
947                                         outliner_add_element(soops, eval_ctx, &te->subtree, linestyle->mtex[a]->tex, te, 0, a);
948                         }
949                         break;
950                 }
951                 case ID_GD:
952                 {
953                         bGPdata *gpd = (bGPdata *)id;
954                         bGPDlayer *gpl;
955                         int a = 0;
956                         
957                         if (outliner_animdata_test(gpd->adt))
958                                 outliner_add_element(soops, eval_ctx, &te->subtree, gpd, te, TSE_ANIM_DATA, 0);
959                         
960                         // TODO: base element for layers?
961                         for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
962                                 outliner_add_element(soops, eval_ctx, &te->subtree, gpl, te, TSE_GP_LAYER, a);
963                                 a++;
964                         }
965                         break;
966                 }
967                 default:
968                         break;
969         }
970 }
971
972 // TODO: this function needs to be split up! It's getting a bit too large...
973 // Note: "ID" is not always a real ID
974 static TreeElement *outliner_add_element(
975         SpaceOops *soops, const EvaluationContext *eval_ctx,
976         ListBase *lb, void *idv, TreeElement *parent, short type, short index)
977 {
978         TreeElement *te;
979         TreeStoreElem *tselem;
980         ID *id = idv;
981         
982         if (ELEM(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) {
983                 id = ((PointerRNA *)idv)->id.data;
984                 if (!id) id = ((PointerRNA *)idv)->data;
985         }
986         else if (type == TSE_GP_LAYER) {
987                 /* idv is the layer its self */
988                 id = TREESTORE(parent)->id;
989         }
990
991         /* exceptions */
992         if (type == TSE_ID_BASE) {
993                 /* pass */
994         }
995         else if (id == NULL) {
996                 return NULL;
997         }
998
999         if (type == 0) {
1000                 /* Zero type means real ID, ensure we do not get non-outliner ID types here... */
1001                 BLI_assert(TREESTORE_ID_TYPE(id));
1002         }
1003
1004         te = MEM_callocN(sizeof(TreeElement), "tree elem");
1005         /* add to the visual tree */
1006         BLI_addtail(lb, te);
1007         /* add to the storage */
1008         check_persistent(soops, te, id, type, index);
1009         tselem = TREESTORE(te);
1010         
1011         /* if we are searching for something expand to see child elements */
1012         if (SEARCHING_OUTLINER(soops))
1013                 tselem->flag |= TSE_CHILDSEARCH;
1014         
1015         te->parent = parent;
1016         te->index = index;   // for data arays
1017         if (ELEM(type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) {
1018                 /* pass */
1019         }
1020         else if (ELEM(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) {
1021                 /* pass */
1022         }
1023         else if (type == TSE_ANIM_DATA) {
1024                 /* pass */
1025         }
1026         else if (type == TSE_GP_LAYER) {
1027                 /* pass */
1028         }
1029         else if (ELEM(type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION)) {
1030                 /* pass */
1031         }
1032         else if (type == TSE_ID_BASE) {
1033                 /* pass */
1034         }
1035         else {
1036                 /* do here too, for blend file viewer, own ID_LI then shows file name */
1037                 if (GS(id->name) == ID_LI)
1038                         te->name = ((Library *)id)->name;
1039                 else
1040                         te->name = id->name + 2; // default, can be overridden by Library or non-ID data
1041                 te->idcode = GS(id->name);
1042         }
1043         
1044         if (type == 0) {
1045                 TreeStoreElem *tsepar = parent ? TREESTORE(parent) : NULL;
1046                 
1047                 /* ID datablock */
1048                 if (tsepar == NULL || tsepar->type != TSE_ID_BASE)
1049                         outliner_add_id_contents(soops, eval_ctx, te, tselem, id);
1050         }
1051         else if (type == TSE_ANIM_DATA) {
1052                 IdAdtTemplate *iat = (IdAdtTemplate *)idv;
1053                 AnimData *adt = (AnimData *)iat->adt;
1054                 
1055                 /* this element's info */
1056                 te->name = IFACE_("Animation");
1057                 te->directdata = adt;
1058                 
1059                 /* Action */
1060                 outliner_add_element(soops, eval_ctx, &te->subtree, adt->action, te, 0, 0);
1061                 
1062                 /* Drivers */
1063                 if (adt->drivers.first) {
1064                         TreeElement *ted = outliner_add_element(soops, eval_ctx, &te->subtree, adt, te, TSE_DRIVER_BASE, 0);
1065                         ID *lastadded = NULL;
1066                         FCurve *fcu;
1067                         
1068                         ted->name = IFACE_("Drivers");
1069                 
1070                         for (fcu = adt->drivers.first; fcu; fcu = fcu->next) {
1071                                 if (fcu->driver && fcu->driver->variables.first) {
1072                                         ChannelDriver *driver = fcu->driver;
1073                                         DriverVar *dvar;
1074                                         
1075                                         for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
1076                                                 /* loop over all targets used here */
1077                                                 DRIVER_TARGETS_USED_LOOPER(dvar) 
1078                                                 {
1079                                                         if (lastadded != dtar->id) {
1080                                                                 // XXX this lastadded check is rather lame, and also fails quite badly...
1081                                                                 outliner_add_element(soops, eval_ctx, &ted->subtree, dtar->id, ted, TSE_LINKED_OB, 0);
1082                                                                 lastadded = dtar->id;
1083                                                         }
1084                                                 }
1085                                                 DRIVER_TARGETS_LOOPER_END
1086                                         }
1087                                 }
1088                         }
1089                 }
1090                 
1091                 /* NLA Data */
1092                 if (adt->nla_tracks.first) {
1093                         TreeElement *tenla = outliner_add_element(soops, eval_ctx, &te->subtree, adt, te, TSE_NLA, 0);
1094                         NlaTrack *nlt;
1095                         int a = 0;
1096                         
1097                         tenla->name = IFACE_("NLA Tracks");
1098                         
1099                         for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
1100                                 TreeElement *tenlt = outliner_add_element(soops, eval_ctx, &tenla->subtree, nlt, tenla, TSE_NLA_TRACK, a);
1101                                 NlaStrip *strip;
1102                                 TreeElement *ten;
1103                                 int b = 0;
1104                                 
1105                                 tenlt->name = nlt->name;
1106                                 
1107                                 for (strip = nlt->strips.first; strip; strip = strip->next, b++) {
1108                                         ten = outliner_add_element(soops, eval_ctx, &tenlt->subtree, strip->act, tenlt, TSE_NLA_ACTION, b);
1109                                         if (ten) ten->directdata = strip;
1110                                 }
1111                         }
1112                 }
1113         }
1114         else if (type == TSE_GP_LAYER) {
1115                 bGPDlayer *gpl = (bGPDlayer *)idv;
1116                 
1117                 te->name = gpl->info;
1118                 te->directdata = gpl;
1119         }
1120         else if (type == TSE_SEQUENCE) {
1121                 Sequence *seq = (Sequence *) idv;
1122                 Sequence *p;
1123
1124                 /*
1125                  * The idcode is a little hack, but the outliner
1126                  * only check te->idcode if te->type is equal to zero,
1127                  * so this is "safe".
1128                  */
1129                 te->idcode = seq->type;
1130                 te->directdata = seq;
1131                 te->name = seq->name + 2;
1132
1133                 if (!(seq->type & SEQ_TYPE_EFFECT)) {
1134                         /*
1135                          * This work like the sequence.
1136                          * If the sequence have a name (not default name)
1137                          * show it, in other case put the filename.
1138                          */
1139
1140                         if (seq->type == SEQ_TYPE_META) {
1141                                 p = seq->seqbase.first;
1142                                 while (p) {
1143                                         outliner_add_element(soops, eval_ctx, &te->subtree, (void *)p, te, TSE_SEQUENCE, index);
1144                                         p = p->next;
1145                                 }
1146                         }
1147                         else
1148                                 outliner_add_element(soops, eval_ctx, &te->subtree, (void *)seq->strip, te, TSE_SEQ_STRIP, index);
1149                 }
1150         }
1151         else if (type == TSE_SEQ_STRIP) {
1152                 Strip *strip = (Strip *)idv;
1153
1154                 if (strip->dir[0] != '\0')
1155                         te->name = strip->dir;
1156                 else
1157                         te->name = IFACE_("Strip None");
1158                 te->directdata = strip;
1159         }
1160         else if (type == TSE_SEQUENCE_DUP) {
1161                 Sequence *seq = (Sequence *)idv;
1162
1163                 te->idcode = seq->type;
1164                 te->directdata = seq;
1165                 te->name = seq->strip->stripdata->name;
1166         }
1167         else if (ELEM(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) {
1168                 PointerRNA pptr, propptr, *ptr = (PointerRNA *)idv;
1169                 PropertyRNA *prop, *iterprop;
1170                 PropertyType proptype;
1171
1172                 /* Don't display arrays larger, weak but index is stored as a short,
1173                  * also the outliner isn't intended for editing such large data-sets. */
1174                 BLI_STATIC_ASSERT(sizeof(te->index) == 2, "Index is no longer short!");
1175                 const int tot_limit = SHRT_MAX;
1176
1177                 int a, tot;
1178
1179                 /* we do lazy build, for speed and to avoid infinite recusion */
1180
1181                 if (ptr->data == NULL) {
1182                         te->name = IFACE_("(empty)");
1183                 }
1184                 else if (type == TSE_RNA_STRUCT) {
1185                         /* struct */
1186                         te->name = RNA_struct_name_get_alloc(ptr, NULL, 0, NULL);
1187
1188                         if (te->name)
1189                                 te->flag |= TE_FREE_NAME;
1190                         else
1191                                 te->name = RNA_struct_ui_name(ptr->type);
1192
1193                         /* If searching don't expand RNA entries */
1194                         if (SEARCHING_OUTLINER(soops) && BLI_strcasecmp("RNA", te->name) == 0) tselem->flag &= ~TSE_CHILDSEARCH;
1195
1196                         iterprop = RNA_struct_iterator_property(ptr->type);
1197                         tot = RNA_property_collection_length(ptr, iterprop);
1198                         CLAMP_MAX(tot, tot_limit);
1199
1200                         /* auto open these cases */
1201                         if (!parent || (RNA_property_type(parent->directdata)) == PROP_POINTER)
1202                                 if (!tselem->used)
1203                                         tselem->flag &= ~TSE_CLOSED;
1204
1205                         if (TSELEM_OPEN(tselem, soops)) {
1206                                 for (a = 0; a < tot; a++) {
1207                                         RNA_property_collection_lookup_int(ptr, iterprop, a, &propptr);
1208                                         if (!(RNA_property_flag(propptr.data) & PROP_HIDDEN)) {
1209                                                 outliner_add_element(soops, eval_ctx, &te->subtree, (void *)ptr, te, TSE_RNA_PROPERTY, a);
1210                                         }
1211                                 }
1212                         }
1213                         else if (tot)
1214                                 te->flag |= TE_LAZY_CLOSED;
1215
1216                         te->rnaptr = *ptr;
1217                 }
1218                 else if (type == TSE_RNA_PROPERTY) {
1219                         /* property */
1220                         iterprop = RNA_struct_iterator_property(ptr->type);
1221                         RNA_property_collection_lookup_int(ptr, iterprop, index, &propptr);
1222
1223                         prop = propptr.data;
1224                         proptype = RNA_property_type(prop);
1225
1226                         te->name = RNA_property_ui_name(prop);
1227                         te->directdata = prop;
1228                         te->rnaptr = *ptr;
1229
1230                         /* If searching don't expand RNA entries */
1231                         if (SEARCHING_OUTLINER(soops) && BLI_strcasecmp("RNA", te->name) == 0) tselem->flag &= ~TSE_CHILDSEARCH;
1232
1233                         if (proptype == PROP_POINTER) {
1234                                 pptr = RNA_property_pointer_get(ptr, prop);
1235
1236                                 if (pptr.data) {
1237                                         if (TSELEM_OPEN(tselem, soops))
1238                                                 outliner_add_element(soops, eval_ctx, &te->subtree, (void *)&pptr, te, TSE_RNA_STRUCT, -1);
1239                                         else
1240                                                 te->flag |= TE_LAZY_CLOSED;
1241                                 }
1242                         }
1243                         else if (proptype == PROP_COLLECTION) {
1244                                 tot = RNA_property_collection_length(ptr, prop);
1245                                 CLAMP_MAX(tot, tot_limit);
1246
1247                                 if (TSELEM_OPEN(tselem, soops)) {
1248                                         for (a = 0; a < tot; a++) {
1249                                                 RNA_property_collection_lookup_int(ptr, prop, a, &pptr);
1250                                                 outliner_add_element(soops, eval_ctx, &te->subtree, (void *)&pptr, te, TSE_RNA_STRUCT, a);
1251                                         }
1252                                 }
1253                                 else if (tot)
1254                                         te->flag |= TE_LAZY_CLOSED;
1255                         }
1256                         else if (ELEM(proptype, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) {
1257                                 tot = RNA_property_array_length(ptr, prop);
1258                                 CLAMP_MAX(tot, tot_limit);
1259
1260                                 if (TSELEM_OPEN(tselem, soops)) {
1261                                         for (a = 0; a < tot; a++)
1262                                                 outliner_add_element(soops, eval_ctx, &te->subtree, (void *)ptr, te, TSE_RNA_ARRAY_ELEM, a);
1263                                 }
1264                                 else if (tot)
1265                                         te->flag |= TE_LAZY_CLOSED;
1266                         }
1267                 }
1268                 else if (type == TSE_RNA_ARRAY_ELEM) {
1269                         char c;
1270
1271                         prop = parent->directdata;
1272
1273                         te->directdata = prop;
1274                         te->rnaptr = *ptr;
1275                         te->index = index;
1276
1277                         c = RNA_property_array_item_char(prop, index);
1278
1279                         te->name = MEM_callocN(sizeof(char) * 20, "OutlinerRNAArrayName");
1280                         if (c) sprintf((char *)te->name, "  %c", c);
1281                         else sprintf((char *)te->name, "  %d", index + 1);
1282                         te->flag |= TE_FREE_NAME;
1283                 }
1284         }
1285         else if (type == TSE_KEYMAP) {
1286                 wmKeyMap *km = (wmKeyMap *)idv;
1287                 wmKeyMapItem *kmi;
1288                 char opname[OP_MAX_TYPENAME];
1289                 
1290                 te->directdata = idv;
1291                 te->name = km->idname;
1292                 
1293                 if (TSELEM_OPEN(tselem, soops)) {
1294                         int a = 0;
1295                         
1296                         for (kmi = km->items.first; kmi; kmi = kmi->next, a++) {
1297                                 const char *key = WM_key_event_string(kmi->type, false);
1298                                 
1299                                 if (key[0]) {
1300                                         wmOperatorType *ot = NULL;
1301                                         
1302                                         if (kmi->propvalue) {
1303                                                 /* pass */
1304                                         }
1305                                         else {
1306                                                 ot = WM_operatortype_find(kmi->idname, 0);
1307                                         }
1308                                         
1309                                         if (ot || kmi->propvalue) {
1310                                                 TreeElement *ten = outliner_add_element(
1311                                                         soops, eval_ctx, &te->subtree, kmi, te, TSE_KEYMAP_ITEM, a);
1312                                                 
1313                                                 ten->directdata = kmi;
1314                                                 
1315                                                 if (kmi->propvalue) {
1316                                                         ten->name = IFACE_("Modal map, not yet");
1317                                                 }
1318                                                 else {
1319                                                         WM_operator_py_idname(opname, ot->idname);
1320                                                         ten->name = BLI_strdup(opname);
1321                                                         ten->flag |= TE_FREE_NAME;
1322                                                 }
1323                                         }
1324                                 }
1325                         }
1326                 }
1327                 else 
1328                         te->flag |= TE_LAZY_CLOSED;
1329         }
1330
1331         if ((type != TSE_LAYER_COLLECTION) && (te->idcode == ID_GR)) {
1332                 Group *group = (Group *)id;
1333                 outliner_add_layer_collections_recursive(
1334                         soops, eval_ctx, &te->subtree, id, &group->view_layer->layer_collections, NULL, true);
1335         }
1336
1337         return te;
1338 }
1339
1340 /**
1341  * \note Really only removes \a tselem, not it's TreeElement instance or any children.
1342  */
1343 void outliner_remove_treestore_element(SpaceOops *soops, TreeStoreElem *tselem)
1344 {
1345         BKE_outliner_treehash_remove_element(soops->treehash, tselem);
1346         BLI_mempool_free(soops->treestore, tselem);
1347 }
1348
1349 /* ======================================================= */
1350 /* Sequencer mode tree building */
1351
1352 /* Helped function to put duplicate sequence in the same tree. */
1353 static int need_add_seq_dup(Sequence *seq)
1354 {
1355         Sequence *p;
1356
1357         if ((!seq->strip) || (!seq->strip->stripdata))
1358                 return(1);
1359
1360         /*
1361          * First check backward, if we found a duplicate
1362          * sequence before this, don't need it, just return.
1363          */
1364         p = seq->prev;
1365         while (p) {
1366                 if ((!p->strip) || (!p->strip->stripdata)) {
1367                         p = p->prev;
1368                         continue;
1369                 }
1370
1371                 if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name))
1372                         return(2);
1373                 p = p->prev;
1374         }
1375
1376         p = seq->next;
1377         while (p) {
1378                 if ((!p->strip) || (!p->strip->stripdata)) {
1379                         p = p->next;
1380                         continue;
1381                 }
1382
1383                 if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name))
1384                         return(0);
1385                 p = p->next;
1386         }
1387         return(1);
1388 }
1389
1390 static void outliner_add_seq_dup(
1391         SpaceOops *soops, const EvaluationContext *eval_ctx,
1392         Sequence *seq, TreeElement *te, short index)
1393 {
1394         /* TreeElement *ch; */ /* UNUSED */
1395         Sequence *p;
1396
1397         p = seq;
1398         while (p) {
1399                 if ((!p->strip) || (!p->strip->stripdata) || (p->strip->stripdata->name[0] == '\0')) {
1400                         p = p->next;
1401                         continue;
1402                 }
1403
1404                 if (STREQ(p->strip->stripdata->name, seq->strip->stripdata->name))
1405                         /* ch = */ /* UNUSED */ outliner_add_element(
1406                                 soops, eval_ctx, &te->subtree, (void *)p, te, TSE_SEQUENCE, index);
1407                 p = p->next;
1408         }
1409 }
1410
1411
1412 /* ----------------------------------------------- */
1413
1414
1415 static void outliner_add_library_contents(
1416         Main *mainvar, SpaceOops *soops, const EvaluationContext *eval_ctx,
1417         TreeElement *te, Library *lib)
1418 {
1419         TreeElement *ten;
1420         ListBase *lbarray[MAX_LIBARRAY];
1421         int a, tot;
1422         
1423         tot = set_listbasepointers(mainvar, lbarray);
1424         for (a = 0; a < tot; a++) {
1425                 if (lbarray[a]->first) {
1426                         ID *id = lbarray[a]->first;
1427                         
1428                         /* check if there's data in current lib */
1429                         for (; id; id = id->next)
1430                                 if (id->lib == lib)
1431                                         break;
1432                         
1433                         if (id) {
1434                                 ten = outliner_add_element(soops, eval_ctx, &te->subtree, lbarray[a], NULL, TSE_ID_BASE, 0);
1435                                 ten->directdata = lbarray[a];
1436                                 
1437                                 ten->name = BKE_idcode_to_name_plural(GS(id->name));
1438                                 if (ten->name == NULL)
1439                                         ten->name = "UNKNOWN";
1440                                 
1441                                 for (id = lbarray[a]->first; id; id = id->next) {
1442                                         if (id->lib == lib)
1443                                                 outliner_add_element(soops, eval_ctx, &ten->subtree, id, ten, 0, 0);
1444                                 }
1445                         }
1446                 }
1447         }
1448         
1449 }
1450
1451 static void outliner_add_orphaned_datablocks(Main *mainvar, SpaceOops *soops, const EvaluationContext *eval_ctx)
1452 {
1453         TreeElement *ten;
1454         ListBase *lbarray[MAX_LIBARRAY];
1455         int a, tot;
1456         
1457         tot = set_listbasepointers(mainvar, lbarray);
1458         for (a = 0; a < tot; a++) {
1459                 if (lbarray[a]->first) {
1460                         ID *id = lbarray[a]->first;
1461                         
1462                         /* check if there are any datablocks of this type which are orphans */
1463                         for (; id; id = id->next) {
1464                                 if (ID_REAL_USERS(id) <= 0)
1465                                         break;
1466                         }
1467                         
1468                         if (id) {
1469                                 /* header for this type of datablock */
1470                                 /* TODO's:
1471                                  *   - Add a parameter to BKE_idcode_to_name_plural to get a sane "user-visible" name instead?
1472                                  *   - Ensure that this uses nice icons for the datablock type involved instead of the dot?
1473                                  */
1474                                 ten = outliner_add_element(soops, eval_ctx, &soops->tree, lbarray[a], NULL, TSE_ID_BASE, 0);
1475                                 ten->directdata = lbarray[a];
1476                                 
1477                                 ten->name = BKE_idcode_to_name_plural(GS(id->name));
1478                                 if (ten->name == NULL)
1479                                         ten->name = "UNKNOWN";
1480                                 
1481                                 /* add the orphaned datablocks - these will not be added with any subtrees attached */
1482                                 for (id = lbarray[a]->first; id; id = id->next) {
1483                                         if (ID_REAL_USERS(id) <= 0)
1484                                                 outliner_add_element(soops, eval_ctx, &ten->subtree, id, ten, 0, 0);
1485                                 }
1486                         }
1487                 }
1488         }
1489 }
1490
1491 static void outliner_layer_collections_reorder(
1492         Main *bmain,
1493         SpaceOops *UNUSED(soops),
1494         TreeElement *insert_element, TreeElement *insert_handle, TreeElementInsertType action,
1495         const wmEvent *UNUSED(event))
1496 {
1497         LayerCollection *lc_insert = insert_element->directdata;
1498         LayerCollection *lc_handle = insert_handle->directdata;
1499         ID *id = insert_element->store_elem->id;
1500
1501         if (action == TE_INSERT_BEFORE) {
1502                 BKE_layer_collection_move_above(id, lc_handle, lc_insert);
1503         }
1504         else if (action == TE_INSERT_AFTER) {
1505                 BKE_layer_collection_move_below(id, lc_handle, lc_insert);
1506         }
1507         else if (action == TE_INSERT_INTO) {
1508                 BKE_layer_collection_move_into(id, lc_handle, lc_insert);
1509         }
1510         else {
1511                 BLI_assert(0);
1512         }
1513
1514         DEG_relations_tag_update(bmain);
1515 }
1516 static bool outliner_layer_collections_reorder_poll(
1517         const TreeElement *insert_element,
1518         TreeElement **io_insert_handle, TreeElementInsertType *UNUSED(io_action))
1519 {
1520         const TreeStoreElem *tselem_handle = TREESTORE(*io_insert_handle);
1521
1522         if (tselem_handle->id != insert_element->store_elem->id) {
1523         return false;
1524         }
1525
1526         return ELEM(tselem_handle->type, TSE_LAYER_COLLECTION);
1527 }
1528
1529 static void outliner_add_layer_collections_recursive(
1530         SpaceOops *soops, const EvaluationContext *eval_ctx,
1531         ListBase *tree, ID *id, ListBase *layer_collections, TreeElement *parent_ten,
1532         const bool show_objects)
1533 {
1534         for (LayerCollection *collection = layer_collections->first; collection; collection = collection->next) {
1535                 TreeElement *ten = outliner_add_element(soops, eval_ctx, tree, id, parent_ten, TSE_LAYER_COLLECTION, 0);
1536
1537                 ten->name = collection->scene_collection->name;
1538                 ten->directdata = collection;
1539                 ten->reinsert = outliner_layer_collections_reorder;
1540                 ten->reinsert_poll = outliner_layer_collections_reorder_poll;
1541
1542                 outliner_add_layer_collections_recursive(
1543                         soops, eval_ctx, &ten->subtree, id, &collection->layer_collections, ten, show_objects);
1544                 if (show_objects) {
1545                         for (LinkData *link = collection->object_bases.first; link; link = link->next) {
1546                                 Base *base = (Base *)link->data;
1547                                 TreeElement *te_object = outliner_add_element(soops, eval_ctx, &ten->subtree, base->object, ten, 0, 0);
1548                                 te_object->directdata = base;
1549                         }
1550                 }
1551                 outliner_make_hierarchy(&ten->subtree);
1552         }
1553 }
1554
1555 static void outliner_add_view_layer(
1556         SpaceOops *soops, const EvaluationContext *eval_ctx,
1557         ListBase *tree, TreeElement *parent,
1558         Scene *scene, ViewLayer *layer, const bool show_objects)
1559 {
1560         outliner_add_layer_collections_recursive(
1561                 soops, eval_ctx, tree, &scene->id, &layer->layer_collections, parent, show_objects);
1562 }
1563
1564 static void outliner_scene_collections_reorder(
1565         Main *bmain,
1566         SpaceOops *UNUSED(soops),
1567         TreeElement *insert_element, TreeElement *insert_handle, TreeElementInsertType action,
1568         const wmEvent *UNUSED(event))
1569 {
1570         SceneCollection *sc_insert = insert_element->directdata;
1571         SceneCollection *sc_handle = insert_handle->directdata;
1572         ID *id = insert_handle->store_elem->id;
1573         BLI_assert(id == insert_element->store_elem->id);
1574
1575         BLI_assert((action == TE_INSERT_INTO) || (sc_handle != BKE_collection_master(id)));
1576         if (action == TE_INSERT_BEFORE) {
1577                 BKE_collection_move_above(id, sc_handle, sc_insert);
1578         }
1579         else if (action == TE_INSERT_AFTER) {
1580                 BKE_collection_move_below(id, sc_handle, sc_insert);
1581         }
1582         else if (action == TE_INSERT_INTO) {
1583                 BKE_collection_move_into(id, sc_handle, sc_insert);
1584         }
1585         else {
1586                 BLI_assert(0);
1587         }
1588
1589         DEG_relations_tag_update(bmain);
1590 }
1591 static bool outliner_scene_collections_reorder_poll(
1592         const TreeElement *insert_element,
1593         TreeElement **io_insert_handle, TreeElementInsertType *io_action)
1594 {
1595         const TreeStoreElem *tselem_handle = TREESTORE(*io_insert_handle);
1596         ID *id = tselem_handle->id;
1597
1598         if (id != insert_element->store_elem->id) {
1599                 return false;
1600         }
1601
1602         if (!ELEM(tselem_handle->type, TSE_SCENE_COLLECTION)) {
1603                 return false;
1604         }
1605
1606         SceneCollection *sc_master = BKE_collection_master(id);
1607         SceneCollection *sc_handle = (*io_insert_handle)->directdata;
1608
1609         if (sc_handle == sc_master) {
1610                 /* exception: Can't insert before/after master selection, has to be one of its childs */
1611                 TreeElement *te_master = *io_insert_handle;
1612                 if (*io_action == TE_INSERT_BEFORE) {
1613                         /* can't go higher than master collection, insert into it */
1614                         *io_action = TE_INSERT_INTO;
1615                 }
1616                 else if (*io_action == TE_INSERT_AFTER) {
1617                         *io_insert_handle = te_master->subtree.last;
1618                 }
1619         }
1620         return true;
1621 }
1622
1623 BLI_INLINE void outliner_add_scene_collection_init(TreeElement *te, SceneCollection *collection)
1624 {
1625         te->name = collection->name;
1626         te->directdata = collection;
1627         te->reinsert = outliner_scene_collections_reorder;
1628         te->reinsert_poll = outliner_scene_collections_reorder_poll;
1629 }
1630
1631 BLI_INLINE void outliner_add_scene_collection_objects(
1632         SpaceOops *soops, const EvaluationContext *eval_ctx,
1633         ListBase *tree, SceneCollection *collection, TreeElement *parent)
1634 {
1635         for (LinkData *link = collection->objects.first; link; link = link->next) {
1636                 outliner_add_element(soops, eval_ctx, tree, link->data, parent, 0, 0);
1637         }
1638 }
1639
1640 static TreeElement *outliner_add_scene_collection_recursive(
1641         SpaceOops *soops, const EvaluationContext *eval_ctx,
1642         ListBase *tree, ID *id, SceneCollection *scene_collection, TreeElement *parent_ten)
1643 {
1644         TreeElement *ten = outliner_add_element(soops, eval_ctx, tree, id, parent_ten, TSE_SCENE_COLLECTION, 0);
1645         outliner_add_scene_collection_init(ten, scene_collection);
1646         outliner_add_scene_collection_objects(soops, eval_ctx, &ten->subtree, scene_collection, ten);
1647
1648         for (SceneCollection *scene_collection_nested = scene_collection->scene_collections.first;
1649              scene_collection_nested != NULL;
1650              scene_collection_nested = scene_collection_nested->next)
1651         {
1652                 outliner_add_scene_collection_recursive(
1653                         soops, eval_ctx, &ten->subtree, id, scene_collection_nested, ten);
1654         }
1655
1656         outliner_make_hierarchy(&ten->subtree);
1657         return ten;
1658 }
1659
1660 static void outliner_add_collections(
1661         SpaceOops *soops, const EvaluationContext *eval_ctx, Scene *scene)
1662 {
1663         SceneCollection *master_collection = BKE_collection_master(&scene->id);
1664         TreeElement *ten = outliner_add_scene_collection_recursive(
1665                 soops, eval_ctx, &soops->tree, &scene->id, master_collection, NULL);
1666         /* Master Collection should always be expanded. */
1667         TREESTORE(ten)->flag &= ~TSE_CLOSED;
1668 }
1669
1670 /* ======================================================= */
1671 /* Generic Tree Building helpers - order these are called is top to bottom */
1672
1673 /* Hierarchy --------------------------------------------- */
1674
1675 /* make sure elements are correctly nested */
1676 static void outliner_make_hierarchy(ListBase *lb)
1677 {
1678         TreeElement *te, *ten, *tep;
1679         TreeStoreElem *tselem;
1680
1681         /* build hierarchy */
1682         // XXX also, set extents here...
1683         te = lb->first;
1684         while (te) {
1685                 ten = te->next;
1686                 tselem = TREESTORE(te);
1687                 
1688                 if (tselem->type == 0 && te->idcode == ID_OB) {
1689                         Object *ob = (Object *)tselem->id;
1690                         if (ob->parent && ob->parent->id.newid) {
1691                                 BLI_remlink(lb, te);
1692                                 tep = (TreeElement *)ob->parent->id.newid;
1693                                 BLI_addtail(&tep->subtree, te);
1694                                 // set correct parent pointers
1695                                 for (te = tep->subtree.first; te; te = te->next) te->parent = tep;
1696                         }
1697                 }
1698                 te = ten;
1699         }
1700 }
1701
1702 /* Sorting ------------------------------------------------------ */
1703
1704 typedef struct tTreeSort {
1705         TreeElement *te;
1706         ID *id;
1707         const char *name;
1708         short idcode;
1709 } tTreeSort;
1710
1711 /* alphabetical comparator, tryping to put objects first */
1712 static int treesort_alpha_ob(const void *v1, const void *v2)
1713 {
1714         const tTreeSort *x1 = v1, *x2 = v2;
1715         int comp;
1716         
1717         /* first put objects last (hierarchy) */
1718         comp = (x1->idcode == ID_OB);
1719         if (x2->idcode == ID_OB) comp += 2;
1720         
1721         if (comp == 1) return 1;
1722         else if (comp == 2) return -1;
1723         else if (comp == 3) {
1724                 comp = strcmp(x1->name, x2->name);
1725                 
1726                 if (comp > 0) return 1;
1727                 else if (comp < 0) return -1;
1728                 return 0;
1729         }
1730         return 0;
1731 }
1732
1733 /* alphabetical comparator */
1734 static int treesort_alpha(const void *v1, const void *v2)
1735 {
1736         const tTreeSort *x1 = v1, *x2 = v2;
1737         int comp;
1738         
1739         comp = strcmp(x1->name, x2->name);
1740         
1741         if (comp > 0) return 1;
1742         else if (comp < 0) return -1;
1743         return 0;
1744 }
1745
1746
1747 /* this is nice option for later? doesnt look too useful... */
1748 #if 0
1749 static int treesort_obtype_alpha(const void *v1, const void *v2)
1750 {
1751         const tTreeSort *x1 = v1, *x2 = v2;
1752         
1753         /* first put objects last (hierarchy) */
1754         if (x1->idcode == ID_OB && x2->idcode != ID_OB) {
1755                 return 1;
1756         }
1757         else if (x2->idcode == ID_OB && x1->idcode != ID_OB) {
1758                 return -1;
1759         }
1760         else {
1761                 /* 2nd we check ob type */
1762                 if (x1->idcode == ID_OB && x2->idcode == ID_OB) {
1763                         if      (((Object *)x1->id)->type > ((Object *)x2->id)->type) return  1;
1764                         else if (((Object *)x1->id)->type > ((Object *)x2->id)->type) return -1;
1765                         else return 0;
1766                 }
1767                 else {
1768                         int comp = strcmp(x1->name, x2->name);
1769                         
1770                         if      (comp > 0) return  1;
1771                         else if (comp < 0) return -1;
1772                         return 0;
1773                 }
1774         }
1775 }
1776 #endif
1777
1778 /* sort happens on each subtree individual */
1779 static void outliner_sort(ListBase *lb)
1780 {
1781         TreeElement *te;
1782         TreeStoreElem *tselem;
1783
1784         te = lb->last;
1785         if (te == NULL) return;
1786         tselem = TREESTORE(te);
1787
1788         /* sorting rules; only object lists, ID lists, or deformgroups */
1789         if (ELEM(tselem->type, TSE_DEFGROUP, TSE_ID_BASE) || (tselem->type == 0 && te->idcode == ID_OB)) {
1790                 int totelem = BLI_listbase_count(lb);
1791
1792                 if (totelem > 1) {
1793                         tTreeSort *tear = MEM_mallocN(totelem * sizeof(tTreeSort), "tree sort array");
1794                         tTreeSort *tp = tear;
1795                         int skip = 0;
1796
1797                         for (te = lb->first; te; te = te->next, tp++) {
1798                                 tselem = TREESTORE(te);
1799                                 tp->te = te;
1800                                 tp->name = te->name;
1801                                 tp->idcode = te->idcode;
1802                                 
1803                                 if (tselem->type && tselem->type != TSE_DEFGROUP)
1804                                         tp->idcode = 0;  // don't sort this
1805                                 if (tselem->type == TSE_ID_BASE)
1806                                         tp->idcode = 1; // do sort this
1807                                 
1808                                 tp->id = tselem->id;
1809                         }
1810                         
1811                         /* just sort alphabetically */
1812                         if (tear->idcode == 1) {
1813                                 qsort(tear, totelem, sizeof(tTreeSort), treesort_alpha);
1814                         }
1815                         else {
1816                                 /* keep beginning of list */
1817                                 for (tp = tear, skip = 0; skip < totelem; skip++, tp++)
1818                                         if (tp->idcode) break;
1819                                 
1820                                 if (skip < totelem)
1821                                         qsort(tear + skip, totelem - skip, sizeof(tTreeSort), treesort_alpha_ob);
1822                         }
1823                         
1824                         BLI_listbase_clear(lb);
1825                         tp = tear;
1826                         while (totelem--) {
1827                                 BLI_addtail(lb, tp->te);
1828                                 tp++;
1829                         }
1830                         MEM_freeN(tear);
1831                 }
1832         }
1833         
1834         for (te = lb->first; te; te = te->next) {
1835                 outliner_sort(&te->subtree);
1836         }
1837 }
1838
1839 /* Filtering ----------------------------------------------- */
1840
1841 typedef struct OutlinerTreeElementFocus {
1842         TreeStoreElem *tselem;
1843         int ys;
1844 } OutlinerTreeElementFocus;
1845
1846 /**
1847  * Bring the outliner scrolling back to where it was in relation to the original focus element
1848  * Caller is expected to handle redrawing of ARegion.
1849  */
1850 static void outliner_restore_scrolling_position(SpaceOops *soops, ARegion *ar, OutlinerTreeElementFocus *focus)
1851 {
1852         View2D *v2d = &ar->v2d;
1853         int ytop;
1854
1855         if (focus->tselem != NULL) {
1856                 outliner_set_coordinates(ar, soops);
1857
1858                 TreeElement *te_new = outliner_find_tree_element(&soops->tree, focus->tselem);
1859
1860                 if (te_new != NULL) {
1861                         int ys_new, ys_old;
1862
1863                         ys_new = te_new->ys;
1864                         ys_old = focus->ys;
1865
1866                         ytop = v2d->cur.ymax + (ys_new - ys_old) -1;
1867                         if (ytop > 0) ytop = 0;
1868
1869                         v2d->cur.ymax = (float)ytop;
1870                         v2d->cur.ymin = (float)(ytop - BLI_rcti_size_y(&v2d->mask));
1871                 }
1872                 else {
1873                         return;
1874                 }
1875
1876                 soops->storeflag |= SO_TREESTORE_REDRAW;
1877         }
1878 }
1879
1880 static bool test_collection_callback(TreeElement *te)
1881 {
1882         TreeStoreElem *tselem = TREESTORE(te);
1883         return ELEM(tselem->type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION);
1884 }
1885
1886 static bool test_object_callback(TreeElement *te)
1887 {
1888         TreeStoreElem *tselem = TREESTORE(te);
1889         return ((tselem->type == 0) && (te->idcode == ID_OB));
1890 }
1891
1892 /**
1893  * See if TreeElement or any of its children pass the callback_test.
1894  */
1895 static TreeElement *outliner_find_first_desired_element_at_y_recursive(
1896         const SpaceOops *soops,
1897         TreeElement *te,
1898         const float limit,
1899         bool (*callback_test)(TreeElement *))
1900 {
1901         if (callback_test(te)) {
1902                 return te;
1903         }
1904
1905         if (TSELEM_OPEN(te->store_elem, soops)) {
1906                 TreeElement *te_iter, *te_sub;
1907                 for (te_iter = te->subtree.first; te_iter; te_iter = te_iter->next) {
1908                         te_sub = outliner_find_first_desired_element_at_y_recursive(soops, te_iter, limit, callback_test);
1909                         if (te_sub != NULL) {
1910                                 return te_sub;
1911                         }
1912                 }
1913         }
1914
1915         return NULL;
1916 }
1917
1918 /**
1919  * Find the first element that passes a test starting from a reference vertical coordinate
1920  *
1921  * If the element that is in the position is not what we are looking for, keep looking for its
1922  * children, siblings, and eventually, aunts, cousins, disntant families, ...
1923  *
1924  * Basically we keep going up and down the outliner tree from that point forward, until we find
1925  * what we are looking for. If we are past the visible range and we can't find a valid element
1926  * we return NULL.
1927  */
1928 static TreeElement *outliner_find_first_desired_element_at_y(
1929         const SpaceOops *soops,
1930         const float view_co,
1931         const float view_co_limit)
1932 {
1933         TreeElement *te, *te_sub;
1934         te = outliner_find_item_at_y(soops, &soops->tree, view_co);
1935
1936         bool (*callback_test)(TreeElement *);
1937         if (soops->filter & SO_FILTER_NO_COLLECTION) {
1938                 callback_test = test_object_callback;
1939         }
1940         else {
1941                 callback_test = test_collection_callback;
1942         }
1943
1944         while (te != NULL) {
1945                 te_sub = outliner_find_first_desired_element_at_y_recursive(soops, te, view_co_limit, callback_test);
1946                 if (te_sub != NULL) {
1947                         /* Skip the element if it was not visible to start with. */
1948                         if (te->ys + UI_UNIT_Y > view_co_limit) {
1949                                 return te_sub;
1950                         }
1951                         else {
1952                                 return NULL;
1953                         }
1954                 }
1955
1956                 if (te->next) {
1957                         te = te->next;
1958                         continue;
1959                 }
1960
1961                 if (te->parent == NULL) {
1962                         break;
1963                 }
1964
1965                 while (te->parent) {
1966                         if (te->parent->next) {
1967                                 te = te->parent->next;
1968                                 break;
1969                         }
1970                         te = te->parent;
1971                 }
1972         }
1973
1974         return NULL;
1975 }
1976
1977 /**
1978  * Store information of current outliner scrolling status to be restored later
1979  *
1980  * Finds the top-most collection visible in the outliner and populates the OutlinerTreeElementFocus
1981  * struct to retrieve this element later to make sure it is in the same original position as before filtering
1982  */
1983 static void outliner_store_scrolling_position(SpaceOops *soops, ARegion *ar, OutlinerTreeElementFocus *focus)
1984 {
1985         TreeElement *te;
1986         float limit = ar->v2d.cur.ymin;
1987
1988         outliner_set_coordinates(ar, soops);
1989
1990         te = outliner_find_first_desired_element_at_y(soops, ar->v2d.cur.ymax, limit);
1991
1992         if (te != NULL) {
1993                 focus->tselem = TREESTORE(te);
1994                 focus->ys = te->ys;
1995         }
1996         else {
1997                 focus->tselem = NULL;
1998         }
1999 }
2000
2001 static int outliner_exclude_filter_get(SpaceOops *soops)
2002 {
2003         int exclude_filter = soops->filter & ~(SO_FILTER_OB_STATE_VISIBLE |
2004                                                SO_FILTER_OB_STATE_SELECTED |
2005                                                SO_FILTER_OB_STATE_ACTIVE);
2006
2007         if (soops->filter & SO_FILTER_SEARCH) {
2008                 if (soops->search_string[0] == 0) {
2009                         exclude_filter &= ~SO_FILTER_SEARCH;
2010                 }
2011         }
2012
2013         /* Let's have this for the collection options at first. */
2014         if (!SUPPORT_FILTER_OUTLINER(soops)) {
2015                 return (exclude_filter & SO_FILTER_SEARCH);
2016         }
2017
2018         if ((exclude_filter & SO_FILTER_NO_OB_ALL) == 0) {
2019                 exclude_filter &= ~SO_FILTER_OB_TYPE;
2020         }
2021
2022         if (exclude_filter & SO_FILTER_OB_STATE) {
2023                 switch (soops->filter_state) {
2024                         case SO_FILTER_OB_VISIBLE:
2025                                 exclude_filter |= SO_FILTER_OB_STATE_VISIBLE;
2026                                 break;
2027                         case SO_FILTER_OB_SELECTED:
2028                                 exclude_filter |= SO_FILTER_OB_STATE_SELECTED;
2029                                 break;
2030                         case SO_FILTER_OB_ACTIVE:
2031                                 exclude_filter |= SO_FILTER_OB_STATE_ACTIVE;
2032                                 break;
2033                 }
2034         }
2035
2036         if ((exclude_filter & SO_FILTER_ANY) == 0) {
2037                 exclude_filter &= ~(SO_FILTER_OB_STATE);
2038         }
2039
2040         return exclude_filter;
2041 }
2042
2043 static bool outliner_element_visible_get(ViewLayer *view_layer, TreeElement *te, const int exclude_filter)
2044 {
2045         if ((exclude_filter & SO_FILTER_ENABLE) == 0) {
2046                 return true;
2047         }
2048
2049         TreeStoreElem *tselem = TREESTORE(te);
2050         if ((tselem->type == 0) && (te->idcode == ID_OB)) {
2051                 if ((exclude_filter & SO_FILTER_NO_OBJECT)) {
2052                         return false;
2053                 }
2054
2055                 Object *ob = (Object *)tselem->id;
2056                 Base *base = (Base *)te->directdata;
2057                 BLI_assert((base == NULL) || (base->object == ob));
2058
2059                 if (exclude_filter & SO_FILTER_OB_TYPE) {
2060                         switch (ob->type) {
2061                                 case OB_MESH:
2062                                         if (exclude_filter & SO_FILTER_NO_OB_MESH) {
2063                                                 return false;
2064                                         }
2065                                         break;
2066                                 case OB_ARMATURE:
2067                                         if (exclude_filter & SO_FILTER_NO_OB_ARMATURE) {
2068                                                 return false;
2069                                         }
2070                                         break;
2071                                 case OB_EMPTY:
2072                                         if (exclude_filter & SO_FILTER_NO_OB_EMPTY) {
2073                                                 return false;
2074                                         }
2075                                         break;
2076                                 case OB_LAMP:
2077                                         if (exclude_filter & SO_FILTER_NO_OB_LAMP) {
2078                                                 return false;
2079                                         }
2080                                         break;
2081                                 case OB_CAMERA:
2082                                         if (exclude_filter & SO_FILTER_NO_OB_CAMERA) {
2083                                                 return false;
2084                                         }
2085                                         break;
2086                                 default:
2087                                         if (exclude_filter & SO_FILTER_NO_OB_OTHERS) {
2088                                                 return false;
2089                                         }
2090                                         break;
2091                         }
2092                 }
2093
2094                 if (exclude_filter & SO_FILTER_OB_STATE) {
2095                         if (base == NULL) {
2096                                 base = BKE_view_layer_base_find(view_layer, ob);
2097
2098                                 if (base == NULL) {
2099                                         return false;
2100                                 }
2101                         }
2102
2103                         if (exclude_filter & SO_FILTER_OB_STATE_VISIBLE) {
2104                                 if ((base->flag & BASE_VISIBLED) == 0) {
2105                                         return false;
2106                                 }
2107                         }
2108                         else if (exclude_filter & SO_FILTER_OB_STATE_SELECTED) {
2109                                 if ((base->flag & BASE_SELECTED) == 0) {
2110                                         return false;
2111                                 }
2112                         }
2113                         else {
2114                                 BLI_assert(exclude_filter & SO_FILTER_OB_STATE_ACTIVE);
2115                                 if (base != BASACT(view_layer)) {
2116                                         return false;
2117                                 }
2118                         }
2119                 }
2120
2121                 if ((te->parent != NULL) &&
2122                     (TREESTORE(te->parent)->type == 0) && (te->parent->idcode == ID_OB))
2123                 {
2124                         if (exclude_filter & SO_FILTER_NO_CHILDREN) {
2125                                 return false;
2126                         }
2127                 }
2128         }
2129         else if (te->parent != NULL &&
2130             TREESTORE(te->parent)->type == 0 && te->parent->idcode == ID_OB)
2131         {
2132                 if (exclude_filter & SO_FILTER_NO_OB_CONTENT) {
2133                         return false;
2134                 }
2135         }
2136
2137         return true;
2138 }
2139
2140 static bool outliner_filter_has_name(TreeElement *te, const char *name, int flags)
2141 {
2142         int fn_flag = 0;
2143
2144         if ((flags & SO_FIND_CASE_SENSITIVE) == 0)
2145                 fn_flag |= FNM_CASEFOLD;
2146
2147         return fnmatch(name, te->name, fn_flag) == 0;
2148 }
2149
2150 static int outliner_filter_subtree(
2151         SpaceOops *soops, ViewLayer *view_layer, ListBase *lb, const char *search_string, const int exclude_filter)
2152 {
2153         TreeElement *te, *te_next;
2154         TreeStoreElem *tselem;
2155
2156         for (te = lb->first; te; te = te_next) {
2157                 te_next = te->next;
2158
2159                 if ((outliner_element_visible_get(view_layer, te, exclude_filter) == false)) {
2160                         outliner_free_tree_element(te, lb);
2161                         continue;
2162                 }
2163                 else if ((exclude_filter & SO_FILTER_SEARCH) == 0) {
2164                         /* Filter subtree too. */
2165                         outliner_filter_subtree(soops, view_layer, &te->subtree, search_string, exclude_filter);
2166                         continue;
2167                 }
2168
2169                 if (!outliner_filter_has_name(te, search_string, soops->search_flags)) {
2170                         /* item isn't something we're looking for, but...
2171                          *  - if the subtree is expanded, check if there are any matches that can be easily found
2172                          *              so that searching for "cu" in the default scene will still match the Cube
2173                          *      - otherwise, we can't see within the subtree and the item doesn't match,
2174                          *              so these can be safely ignored (i.e. the subtree can get freed)
2175                          */
2176                         tselem = TREESTORE(te);
2177
2178                         /* flag as not a found item */
2179                         tselem->flag &= ~TSE_SEARCHMATCH;
2180                         
2181                         if ((!TSELEM_OPEN(tselem, soops)) ||
2182                             outliner_filter_subtree(soops, view_layer, &te->subtree, search_string, exclude_filter) == 0)
2183                         {
2184                                 outliner_free_tree_element(te, lb);
2185                         }
2186                 }
2187                 else {
2188                         tselem = TREESTORE(te);
2189
2190                         /* flag as a found item - we can then highlight it */
2191                         tselem->flag |= TSE_SEARCHMATCH;
2192
2193                         /* filter subtree too */
2194                         outliner_filter_subtree(soops, view_layer, &te->subtree, search_string, exclude_filter);
2195                 }
2196         }
2197
2198         /* if there are still items in the list, that means that there were still some matches */
2199         return (BLI_listbase_is_empty(lb) == false);
2200 }
2201
2202 static void outliner_filter_tree(SpaceOops *soops, ViewLayer *view_layer)
2203 {
2204         char search_buff[sizeof(((struct SpaceOops *)NULL)->search_string) + 2];
2205         char *search_string;
2206
2207         const int exclude_filter = outliner_exclude_filter_get(soops);
2208
2209         if (exclude_filter == 0) {
2210                 return;
2211         }
2212
2213         if (soops->search_flags & SO_FIND_COMPLETE) {
2214                 search_string = soops->search_string;
2215         }
2216         else {
2217                 /* Implicitly add heading/trailing wildcards if needed. */
2218                 BLI_strncpy_ensure_pad(search_buff, soops->search_string, '*', sizeof(search_buff));
2219                 search_string = search_buff;
2220         }
2221
2222         outliner_filter_subtree(soops, view_layer, &soops->tree, search_string, exclude_filter);
2223 }
2224
2225 /* ======================================================= */
2226 /* Main Tree Building API */
2227
2228 /* Main entry point for building the tree data-structure that the outliner represents */
2229 // TODO: split each mode into its own function?
2230 void outliner_build_tree(
2231         Main *mainvar, const EvaluationContext *eval_ctx, Scene *scene,
2232         ViewLayer *view_layer, SpaceOops *soops, ARegion *ar)
2233 {
2234         TreeElement *te = NULL, *ten;
2235         TreeStoreElem *tselem;
2236         int show_opened = !soops->treestore || !BLI_mempool_len(soops->treestore); /* on first view, we open scenes */
2237
2238         /* Are we looking for something - we want to tag parents to filter child matches
2239          * - NOT in datablocks view - searching all datablocks takes way too long to be useful
2240          * - this variable is only set once per tree build */
2241         if (soops->search_string[0] != 0 && soops->outlinevis != SO_DATABLOCKS)
2242                 soops->search_flags |= SO_SEARCH_RECURSIVE;
2243         else
2244                 soops->search_flags &= ~SO_SEARCH_RECURSIVE;
2245
2246         if (soops->treehash && (soops->storeflag & SO_TREESTORE_REBUILD)) {
2247                 soops->storeflag &= ~SO_TREESTORE_REBUILD;
2248                 BKE_outliner_treehash_rebuild_from_treestore(soops->treehash, soops->treestore);
2249         }
2250
2251         if (soops->tree.first && (soops->storeflag & SO_TREESTORE_REDRAW))
2252                 return;
2253
2254         OutlinerTreeElementFocus focus;
2255         outliner_store_scrolling_position(soops, ar, &focus);
2256
2257         outliner_free_tree(&soops->tree);
2258         outliner_storage_cleanup(soops);
2259         
2260         /* options */
2261         if (soops->outlinevis == SO_LIBRARIES) {
2262                 Library *lib;
2263                 
2264                 /* current file first - mainvar provides tselem with unique pointer - not used */
2265                 ten = outliner_add_element(soops, eval_ctx, &soops->tree, mainvar, NULL, TSE_ID_BASE, 0);
2266                 ten->name = IFACE_("Current File");
2267
2268                 tselem = TREESTORE(ten);
2269                 if (!tselem->used)
2270                         tselem->flag &= ~TSE_CLOSED;
2271                 
2272                 outliner_add_library_contents(mainvar, soops, eval_ctx, ten, NULL);
2273                 
2274                 for (lib = mainvar->library.first; lib; lib = lib->id.next) {
2275                         ten = outliner_add_element(soops, eval_ctx, &soops->tree, lib, NULL, 0, 0);
2276                         lib->id.newid = (ID *)ten;
2277                         
2278                         outliner_add_library_contents(mainvar, soops, eval_ctx, ten, lib);
2279
2280                 }
2281                 /* make hierarchy */
2282                 ten = soops->tree.first;
2283                 ten = ten->next;  /* first one is main */
2284                 while (ten) {
2285                         TreeElement *nten = ten->next, *par;
2286                         tselem = TREESTORE(ten);
2287                         lib = (Library *)tselem->id;
2288                         if (lib && lib->parent) {
2289                                 par = (TreeElement *)lib->parent->id.newid;
2290                                 if (tselem->id->tag & LIB_TAG_INDIRECT) {
2291                                         /* Only remove from 'first level' if lib is not also directly used. */
2292                                         BLI_remlink(&soops->tree, ten);
2293                                         BLI_addtail(&par->subtree, ten);
2294                                         ten->parent = par;
2295                                 }
2296                                 else {
2297                                         /* Else, make a new copy of the libtree for our parent. */
2298                                         TreeElement *dupten = outliner_add_element(soops, eval_ctx, &par->subtree, lib, NULL, 0, 0);
2299                                         outliner_add_library_contents(mainvar, soops, eval_ctx, dupten, lib);
2300                                         dupten->parent = par;
2301                                 }
2302                         }
2303                         ten = nten;
2304                 }
2305                 /* restore newid pointers */
2306                 for (lib = mainvar->library.first; lib; lib = lib->id.next)
2307                         lib->id.newid = NULL;
2308                 
2309         }
2310         else if (soops->outlinevis == SO_SCENES) {
2311                 Scene *sce;
2312                 for (sce = mainvar->scene.first; sce; sce = sce->id.next) {
2313                         te = outliner_add_element(soops, eval_ctx, &soops->tree, sce, NULL, 0, 0);
2314                         tselem = TREESTORE(te);
2315
2316                         if (sce == scene && show_opened) {
2317                                 tselem->flag &= ~TSE_CLOSED;
2318                         }
2319
2320                         outliner_make_hierarchy(&te->subtree);
2321                 }
2322         }
2323         else if (soops->outlinevis == SO_GROUPS) {
2324                 Group *group;
2325                 for (group = mainvar->group.first; group; group = group->id.next) {
2326                         te = outliner_add_element(soops, eval_ctx, &soops->tree, group, NULL, 0, 0);
2327                         outliner_make_hierarchy(&te->subtree);
2328                 }
2329         }
2330         else if (soops->outlinevis == SO_SEQUENCE) {
2331                 Sequence *seq;
2332                 Editing *ed = BKE_sequencer_editing_get(scene, false);
2333                 int op;
2334
2335                 if (ed == NULL)
2336                         return;
2337
2338                 seq = ed->seqbasep->first;
2339                 if (!seq)
2340                         return;
2341
2342                 while (seq) {
2343                         op = need_add_seq_dup(seq);
2344                         if (op == 1) {
2345                                 /* ten = */ outliner_add_element(soops, eval_ctx, &soops->tree, (void *)seq, NULL, TSE_SEQUENCE, 0);
2346                         }
2347                         else if (op == 0) {
2348                                 ten = outliner_add_element(soops, eval_ctx, &soops->tree, (void *)seq, NULL, TSE_SEQUENCE_DUP, 0);
2349                                 outliner_add_seq_dup(soops, eval_ctx, seq, ten, 0);
2350                         }
2351                         seq = seq->next;
2352                 }
2353         }
2354         else if (soops->outlinevis == SO_DATABLOCKS) {
2355                 PointerRNA mainptr;
2356
2357                 RNA_main_pointer_create(mainvar, &mainptr);
2358
2359                 ten = outliner_add_element(soops, eval_ctx, &soops->tree, (void *)&mainptr, NULL, TSE_RNA_STRUCT, -1);
2360
2361                 if (show_opened) {
2362                         tselem = TREESTORE(ten);
2363                         tselem->flag &= ~TSE_CLOSED;
2364                 }
2365         }
2366         else if (soops->outlinevis == SO_ID_ORPHANS) {
2367                 outliner_add_orphaned_datablocks(mainvar, soops, eval_ctx);
2368         }
2369         else if (soops->outlinevis == SO_VIEW_LAYER) {
2370                 if ((soops->filter & SO_FILTER_ENABLE) && (soops->filter & SO_FILTER_NO_COLLECTION)) {
2371                         for (Base *base = view_layer->object_bases.first; base; base = base->next) {
2372                                 TreeElement *te_object = outliner_add_element(soops, eval_ctx, &soops->tree, base->object, NULL, 0, 0);
2373                                 te_object->directdata = base;
2374                         }
2375                         outliner_make_hierarchy(&soops->tree);
2376                 }
2377                 else {
2378                         outliner_add_view_layer(soops, eval_ctx, &soops->tree, NULL, scene, view_layer, true);
2379                 }
2380         }
2381         else if (soops->outlinevis == SO_COLLECTIONS) {
2382                 if ((soops->filter & SO_FILTER_ENABLE) && (soops->filter & SO_FILTER_NO_COLLECTION)) {
2383                         FOREACH_SCENE_OBJECT(scene, ob)
2384                         {
2385                                 outliner_add_element(soops, eval_ctx, &soops->tree, ob, NULL, 0, 0);
2386                         }
2387                         FOREACH_SCENE_OBJECT_END
2388                         outliner_make_hierarchy(&soops->tree);
2389                 }
2390                 else {
2391                         outliner_add_collections(soops, eval_ctx, scene);
2392                 }
2393         }
2394         else {
2395                 if (BASACT(view_layer)) {
2396                         ten = outliner_add_element(soops, eval_ctx, &soops->tree, OBACT(view_layer), NULL, 0, 0);
2397                         ten->directdata = BASACT(view_layer);
2398                 }
2399         }
2400
2401         if ((soops->flag & SO_SKIP_SORT_ALPHA) == 0) {
2402                 outliner_sort(&soops->tree);
2403         }
2404
2405         outliner_filter_tree(soops, view_layer);
2406         outliner_restore_scrolling_position(soops, ar, &focus);
2407
2408         BKE_main_id_clear_newpoins(mainvar);
2409 }
2410
2411