Cleanup: clang-format
[blender.git] / source / blender / editors / space_outliner / outliner_draw.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2004 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup spoutliner
22  */
23
24 #include "DNA_anim_types.h"
25 #include "DNA_armature_types.h"
26 #include "DNA_collection_types.h"
27 #include "DNA_gpencil_types.h"
28 #include "DNA_gpencil_modifier_types.h"
29 #include "DNA_light_types.h"
30 #include "DNA_lightprobe_types.h"
31 #include "DNA_object_types.h"
32 #include "DNA_scene_types.h"
33 #include "DNA_sequence_types.h"
34
35 #include "BLI_math.h"
36 #include "BLI_blenlib.h"
37 #include "BLI_string_utils.h"
38 #include "BLI_utildefines.h"
39 #include "BLI_mempool.h"
40
41 #include "BLT_translation.h"
42
43 #include "BKE_context.h"
44 #include "BKE_deform.h"
45 #include "BKE_fcurve.h"
46 #include "BKE_gpencil.h"
47 #include "BKE_idcode.h"
48 #include "BKE_layer.h"
49 #include "BKE_library.h"
50 #include "BKE_main.h"
51 #include "BKE_modifier.h"
52 #include "BKE_object.h"
53 #include "BKE_report.h"
54 #include "BKE_scene.h"
55
56 #include "DEG_depsgraph.h"
57 #include "DEG_depsgraph_build.h"
58
59 #include "ED_armature.h"
60 #include "ED_keyframing.h"
61 #include "ED_object.h"
62 #include "ED_screen.h"
63
64 #include "WM_api.h"
65 #include "WM_types.h"
66
67 #include "GPU_immediate.h"
68 #include "GPU_state.h"
69
70 #include "UI_interface.h"
71 #include "UI_interface_icons.h"
72 #include "UI_resources.h"
73 #include "UI_view2d.h"
74
75 #include "RNA_access.h"
76
77 #include "outliner_intern.h"
78
79 /* disable - this is far too slow - campbell */
80 // #define USE_GROUP_SELECT
81
82 /* ****************************************************** */
83 /* Tree Size Functions */
84
85 static void outliner_tree_dimensions_impl(SpaceOutliner *soops,
86                                           ListBase *lb,
87                                           int *width,
88                                           int *height)
89 {
90   for (TreeElement *te = lb->first; te; te = te->next) {
91     *width = MAX2(*width, te->xend);
92     if (height != NULL) {
93       *height += UI_UNIT_Y;
94     }
95
96     TreeStoreElem *tselem = TREESTORE(te);
97     if (TSELEM_OPEN(tselem, soops)) {
98       outliner_tree_dimensions_impl(soops, &te->subtree, width, height);
99     }
100     else {
101       outliner_tree_dimensions_impl(soops, &te->subtree, width, NULL);
102     }
103   }
104 }
105
106 static void outliner_tree_dimensions(SpaceOutliner *soops, int *r_width, int *r_height)
107 {
108   *r_width = 0;
109   *r_height = 0;
110   outliner_tree_dimensions_impl(soops, &soops->tree, r_width, r_height);
111 }
112
113 /**
114  * The active object is only needed for reference.
115  */
116 static bool is_object_data_in_editmode(const ID *id, const Object *obact)
117 {
118   const short id_type = GS(id->name);
119
120   if (id_type == ID_GD && obact && obact->data == id) {
121     bGPdata *gpd = (bGPdata *)id;
122     return GPENCIL_EDIT_MODE(gpd);
123   }
124
125   return ((obact && (obact->mode & OB_MODE_EDIT)) && (id && OB_DATA_SUPPORT_EDITMODE(id_type)) &&
126           (GS(((ID *)obact->data)->name) == id_type) && BKE_object_data_is_in_editmode(id));
127 }
128
129 /* ****************************************************** */
130
131 static void restrictbutton_recursive_ebone(bContext *C,
132                                            EditBone *ebone_parent,
133                                            int flag,
134                                            bool set_flag)
135 {
136   Object *obedit = CTX_data_edit_object(C);
137   bArmature *arm = obedit->data;
138   EditBone *ebone;
139
140   for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
141     if (ED_armature_ebone_is_child_recursive(ebone_parent, ebone)) {
142       if (set_flag) {
143         ebone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
144         ebone->flag |= flag;
145       }
146       else {
147         ebone->flag &= ~flag;
148       }
149     }
150   }
151 }
152
153 static void restrictbutton_recursive_bone(Bone *bone_parent, int flag, bool set_flag)
154 {
155   Bone *bone;
156   for (bone = bone_parent->childbase.first; bone; bone = bone->next) {
157     if (set_flag) {
158       bone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
159       bone->flag |= flag;
160     }
161     else {
162       bone->flag &= ~flag;
163     }
164     restrictbutton_recursive_bone(bone, flag, set_flag);
165   }
166 }
167
168 static void restrictbutton_r_lay_cb(bContext *C, void *poin, void *UNUSED(poin2))
169 {
170   WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, poin);
171 }
172
173 static void restrictbutton_bone_visibility_cb(bContext *C, void *UNUSED(poin), void *poin2)
174 {
175   Bone *bone = (Bone *)poin2;
176   if (bone->flag & BONE_HIDDEN_P) {
177     bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
178   }
179
180   if (CTX_wm_window(C)->eventstate->ctrl) {
181     restrictbutton_recursive_bone(bone, BONE_HIDDEN_P, (bone->flag & BONE_HIDDEN_P) != 0);
182   }
183
184   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
185 }
186
187 static void restrictbutton_bone_select_cb(bContext *C, void *UNUSED(poin), void *poin2)
188 {
189   Bone *bone = (Bone *)poin2;
190   if (bone->flag & BONE_UNSELECTABLE) {
191     bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
192   }
193
194   if (CTX_wm_window(C)->eventstate->ctrl) {
195     restrictbutton_recursive_bone(bone, BONE_UNSELECTABLE, (bone->flag & BONE_UNSELECTABLE) != 0);
196   }
197
198   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
199 }
200
201 static void restrictbutton_ebone_select_cb(bContext *C, void *UNUSED(poin), void *poin2)
202 {
203   EditBone *ebone = (EditBone *)poin2;
204
205   if (ebone->flag & BONE_UNSELECTABLE) {
206     ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
207   }
208
209   if (CTX_wm_window(C)->eventstate->ctrl) {
210     restrictbutton_recursive_ebone(
211         C, ebone, BONE_UNSELECTABLE, (ebone->flag & BONE_UNSELECTABLE) != 0);
212   }
213
214   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
215 }
216
217 static void restrictbutton_ebone_visibility_cb(bContext *C, void *UNUSED(poin), void *poin2)
218 {
219   EditBone *ebone = (EditBone *)poin2;
220   if (ebone->flag & BONE_HIDDEN_A) {
221     ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
222   }
223
224   if (CTX_wm_window(C)->eventstate->ctrl) {
225     restrictbutton_recursive_ebone(C, ebone, BONE_HIDDEN_A, (ebone->flag & BONE_HIDDEN_A) != 0);
226   }
227
228   WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
229 }
230
231 static void restrictbutton_gp_layer_flag_cb(bContext *C, void *poin, void *UNUSED(poin2))
232 {
233   ID *id = (ID *)poin;
234
235   DEG_id_tag_update(id, ID_RECALC_GEOMETRY);
236   WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
237 }
238
239 static void restrictbutton_id_user_toggle(bContext *UNUSED(C), void *poin, void *UNUSED(poin2))
240 {
241   ID *id = (ID *)poin;
242
243   BLI_assert(id != NULL);
244
245   if (id->flag & LIB_FAKEUSER) {
246     id_us_plus(id);
247   }
248   else {
249     id_us_min(id);
250   }
251 }
252
253 static void outliner_object_set_flag_recursive_cb(bContext *C,
254                                                   Base *base,
255                                                   Object *ob,
256                                                   const char *propname)
257 {
258   Main *bmain = CTX_data_main(C);
259   wmWindow *win = CTX_wm_window(C);
260   Scene *scene = CTX_data_scene(C);
261   ViewLayer *view_layer = CTX_data_view_layer(C);
262   PointerRNA ptr;
263
264   bool extend = (win->eventstate->shift != 0);
265
266   if (!extend) {
267     return;
268   }
269
270   /* Create PointerRNA and PropertyRNA for either Object or Base. */
271   ID *id = ob ? &ob->id : &scene->id;
272   StructRNA *struct_rna = ob ? &RNA_Object : &RNA_ObjectBase;
273   void *data = ob ? (void *)ob : (void *)base;
274
275   RNA_pointer_create(id, struct_rna, data, &ptr);
276   PropertyRNA *base_or_object_prop = RNA_struct_type_find_property(struct_rna, propname);
277   const bool value = RNA_property_boolean_get(&ptr, base_or_object_prop);
278
279   Object *ob_parent = ob ? ob : base->object;
280
281   for (Object *ob_iter = bmain->objects.first; ob_iter; ob_iter = ob_iter->id.next) {
282     if (BKE_object_is_child_recursive(ob_parent, ob_iter)) {
283       if (ob) {
284         RNA_id_pointer_create(&ob_iter->id, &ptr);
285         DEG_id_tag_update(&ob_iter->id, ID_RECALC_COPY_ON_WRITE);
286       }
287       else {
288         Base *base_iter = BKE_view_layer_base_find(view_layer, ob_iter);
289         RNA_pointer_create(&scene->id, &RNA_ObjectBase, base_iter, &ptr);
290       }
291       RNA_property_boolean_set(&ptr, base_or_object_prop, value);
292     }
293   }
294
295   /* We don't call RNA_property_update() due to performance, so we batch update them. */
296   if (ob) {
297     BKE_main_collection_sync_remap(bmain);
298     DEG_relations_tag_update(bmain);
299   }
300   else {
301     BKE_layer_collection_sync(scene, view_layer);
302     DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
303   }
304 }
305
306 /**
307  * Object properties.
308  * */
309 static void outliner__object_set_flag_recursive_cb(bContext *C, void *poin, void *poin2)
310 {
311   Object *ob = poin;
312   char *propname = poin2;
313   outliner_object_set_flag_recursive_cb(C, NULL, ob, propname);
314 }
315
316 /**
317  * Base properties.
318  * */
319 static void outliner__base_set_flag_recursive_cb(bContext *C, void *poin, void *poin2)
320 {
321   Base *base = poin;
322   char *propname = poin2;
323   outliner_object_set_flag_recursive_cb(C, base, NULL, propname);
324 }
325
326 /** Create either a RNA_LayerCollection or a RNA_Collection pointer. */
327 static void outliner_layer_or_collection_pointer_create(Scene *scene,
328                                                         LayerCollection *layer_collection,
329                                                         Collection *collection,
330                                                         PointerRNA *ptr)
331 {
332   if (collection) {
333     RNA_id_pointer_create(&collection->id, ptr);
334   }
335   else {
336     RNA_pointer_create(&scene->id, &RNA_LayerCollection, layer_collection, ptr);
337   }
338 }
339
340 /** Create either a RNA_ObjectBase or a RNA_Object pointer. */
341 static void outliner_base_or_object_pointer_create(ViewLayer *view_layer,
342                                                    Collection *collection,
343                                                    Object *ob,
344                                                    PointerRNA *ptr)
345 {
346   if (collection) {
347     RNA_id_pointer_create(&ob->id, ptr);
348   }
349   else {
350     Base *base = BKE_view_layer_base_find(view_layer, ob);
351     RNA_pointer_create(&base->object->id, &RNA_ObjectBase, base, ptr);
352   }
353 }
354
355 /* Note: Collection is only valid when we want to change the collection data, otherwise we get it
356  * from layer collection. Layer collection is valid whenever we are looking at a view layer. */
357 static void outliner_collection_set_flag_recursive(Scene *scene,
358                                                    ViewLayer *view_layer,
359                                                    LayerCollection *layer_collection,
360                                                    Collection *collection,
361                                                    PropertyRNA *layer_or_collection_prop,
362                                                    PropertyRNA *base_or_object_prop,
363                                                    const bool value)
364 {
365   if (layer_collection && layer_collection->flag & LAYER_COLLECTION_EXCLUDE) {
366     return;
367   }
368   PointerRNA ptr;
369   outliner_layer_or_collection_pointer_create(scene, layer_collection, collection, &ptr);
370   RNA_property_boolean_set(&ptr, layer_or_collection_prop, value);
371
372   /* Set the same flag for the nested objects as well. */
373   if (base_or_object_prop) {
374     /* Note: We can't use BKE_collection_object_cache_get()
375      * otherwise we would not take collection exclusion into account. */
376     for (CollectionObject *cob = layer_collection->collection->gobject.first; cob;
377          cob = cob->next) {
378
379       outliner_base_or_object_pointer_create(view_layer, collection, cob->ob, &ptr);
380       RNA_property_boolean_set(&ptr, base_or_object_prop, value);
381
382       if (collection) {
383         DEG_id_tag_update(&cob->ob->id, ID_RECALC_COPY_ON_WRITE);
384       }
385     }
386   }
387
388   /* Keep going recursively. */
389   ListBase *lb = (layer_collection ? &layer_collection->layer_collections : &collection->children);
390   for (Link *link = lb->first; link; link = link->next) {
391     LayerCollection *layer_collection_iter = layer_collection ? (LayerCollection *)link : NULL;
392     Collection *collection_iter = layer_collection ?
393                                       (collection ? layer_collection_iter->collection : NULL) :
394                                       ((CollectionChild *)link)->collection;
395     outliner_collection_set_flag_recursive(scene,
396                                            view_layer,
397                                            layer_collection_iter,
398                                            collection_iter,
399                                            layer_or_collection_prop,
400                                            base_or_object_prop,
401                                            value);
402   }
403
404   if (collection) {
405     DEG_id_tag_update(&collection->id, ID_RECALC_COPY_ON_WRITE);
406   }
407 }
408
409 /** Check if collection is already isolated.
410  *
411  * A collection is isolated if all its parents and children are "visible".
412  * All the other collections must be "invisible".
413  *
414  * Note: We could/should boost performance by iterating over the tree twice.
415  * First tagging all the children/parent collections, then getting their values and comparing.
416  * To run BKE_collection_has_collection() so many times is silly and slow.
417  */
418 static bool outliner_collection_is_isolated(Scene *scene,
419                                             const LayerCollection *layer_collection_cmp,
420                                             const Collection *collection_cmp,
421                                             const bool value_cmp,
422                                             const PropertyRNA *layer_or_collection_prop,
423                                             LayerCollection *layer_collection,
424                                             Collection *collection)
425 {
426   PointerRNA ptr;
427   outliner_layer_or_collection_pointer_create(scene, layer_collection, collection, &ptr);
428   const bool value = RNA_property_boolean_get(&ptr, (PropertyRNA *)layer_or_collection_prop);
429   Collection *collection_ensure = collection ? collection : layer_collection->collection;
430   const Collection *collection_ensure_cmp = collection_cmp ? collection_cmp :
431                                                              layer_collection_cmp->collection;
432
433   if (collection_ensure->flag & COLLECTION_IS_MASTER) {
434   }
435   else if (collection_ensure == collection_ensure_cmp) {
436   }
437   else if (BKE_collection_has_collection(collection_ensure, (Collection *)collection_ensure_cmp) ||
438            BKE_collection_has_collection((Collection *)collection_ensure_cmp, collection_ensure)) {
439     /* This collection is either a parent or a child of the collection.
440      * We expect it to be set "visble" already. */
441     if (value != value_cmp) {
442       return false;
443     }
444   }
445   else {
446     /* This collection is neither a parent nor a child of the collection.
447      * We expect it to be "invisble". */
448     if (value == value_cmp) {
449       return false;
450     }
451   }
452
453   /* Keep going recursively. */
454   ListBase *lb = (layer_collection ? &layer_collection->layer_collections : &collection->children);
455   for (Link *link = lb->first; link; link = link->next) {
456     LayerCollection *layer_collection_iter = layer_collection ? (LayerCollection *)link : NULL;
457     Collection *collection_iter = layer_collection ?
458                                       (collection ? layer_collection_iter->collection : NULL) :
459                                       ((CollectionChild *)link)->collection;
460     if (layer_collection_iter && layer_collection_iter->flag & LAYER_COLLECTION_EXCLUDE) {
461       continue;
462     }
463     if (!outliner_collection_is_isolated(scene,
464                                          layer_collection_cmp,
465                                          collection_cmp,
466                                          value_cmp,
467                                          layer_or_collection_prop,
468                                          layer_collection_iter,
469                                          collection_iter)) {
470       return false;
471     }
472   }
473
474   return true;
475 }
476
477 void outliner_collection_isolate_flag(Scene *scene,
478                                       ViewLayer *view_layer,
479                                       LayerCollection *layer_collection,
480                                       Collection *collection,
481                                       PropertyRNA *layer_or_collection_prop,
482                                       const char *propname,
483                                       const bool value)
484 {
485   PointerRNA ptr;
486   const bool is_hide = strstr(propname, "hide_") != NULL;
487
488   LayerCollection *top_layer_collection = layer_collection ? view_layer->layer_collections.first :
489                                                              NULL;
490   Collection *top_collection = collection ? scene->master_collection : NULL;
491
492   bool was_isolated = (value == is_hide);
493   was_isolated &= outliner_collection_is_isolated(scene,
494                                                   layer_collection,
495                                                   collection,
496                                                   !is_hide,
497                                                   layer_or_collection_prop,
498                                                   top_layer_collection,
499                                                   top_collection);
500
501   if (was_isolated) {
502     const bool default_value = RNA_property_boolean_get_default(NULL, layer_or_collection_prop);
503     /* Make every collection go back to its default "visibility" state. */
504     outliner_collection_set_flag_recursive(scene,
505                                            view_layer,
506                                            top_layer_collection,
507                                            top_collection,
508                                            layer_or_collection_prop,
509                                            NULL,
510                                            default_value);
511     return;
512   }
513
514   /* Make every collection "invisible". */
515   outliner_collection_set_flag_recursive(scene,
516                                          view_layer,
517                                          top_layer_collection,
518                                          top_collection,
519                                          layer_or_collection_prop,
520                                          NULL,
521                                          is_hide);
522
523   /* Make this collection and its children collections the only "visible". */
524   outliner_collection_set_flag_recursive(
525       scene, view_layer, layer_collection, collection, layer_or_collection_prop, NULL, !is_hide);
526
527   /* Make this collection direct parents also "visible". */
528   if (layer_collection) {
529     LayerCollection *lc_parent = layer_collection;
530     for (LayerCollection *lc_iter = top_layer_collection->layer_collections.first; lc_iter;
531          lc_iter = lc_iter->next) {
532       if (BKE_layer_collection_has_layer_collection(lc_iter, layer_collection)) {
533         lc_parent = lc_iter;
534         break;
535       }
536     }
537
538     while (lc_parent != layer_collection) {
539       outliner_layer_or_collection_pointer_create(
540           scene, lc_parent, collection ? lc_parent->collection : NULL, &ptr);
541       RNA_property_boolean_set(&ptr, layer_or_collection_prop, !is_hide);
542
543       for (LayerCollection *lc_iter = lc_parent->layer_collections.first; lc_iter;
544            lc_iter = lc_iter->next) {
545         if (BKE_layer_collection_has_layer_collection(lc_iter, layer_collection)) {
546           lc_parent = lc_iter;
547           break;
548         }
549       }
550     }
551   }
552   else {
553     CollectionParent *parent;
554     Collection *child = collection;
555     while ((parent = child->parents.first)) {
556       if (parent->collection->flag & COLLECTION_IS_MASTER) {
557         break;
558       }
559       RNA_id_pointer_create(&parent->collection->id, &ptr);
560       RNA_property_boolean_set(&ptr, layer_or_collection_prop, !is_hide);
561       child = parent->collection;
562     }
563   }
564 }
565
566 static void outliner_collection_set_flag_recursive_cb(bContext *C,
567                                                       LayerCollection *layer_collection,
568                                                       Collection *collection,
569                                                       const char *propname)
570 {
571   Main *bmain = CTX_data_main(C);
572   wmWindow *win = CTX_wm_window(C);
573   Scene *scene = CTX_data_scene(C);
574   ViewLayer *view_layer = CTX_data_view_layer(C);
575   PointerRNA ptr;
576
577   bool do_isolate = (win->eventstate->ctrl != 0);
578   bool extend = (win->eventstate->shift != 0);
579
580   if (!ELEM(true, do_isolate, extend)) {
581     return;
582   }
583
584   /* Create PointerRNA and PropertyRNA for either Collection or LayerCollection. */
585   ID *id = collection ? &collection->id : &scene->id;
586   StructRNA *struct_rna = collection ? &RNA_Collection : &RNA_LayerCollection;
587   void *data = collection ? (void *)collection : (void *)layer_collection;
588
589   RNA_pointer_create(id, struct_rna, data, &ptr);
590   outliner_layer_or_collection_pointer_create(scene, layer_collection, collection, &ptr);
591   PropertyRNA *layer_or_collection_prop = RNA_struct_type_find_property(struct_rna, propname);
592   const bool value = RNA_property_boolean_get(&ptr, layer_or_collection_prop);
593
594   PropertyRNA *base_or_object_prop = NULL;
595   if (layer_collection != NULL) {
596     /* If we are toggling Layer collections we still want to change the properties of the base
597      * or the objects. If we have a matching property, toggle it as well, it can be NULL. */
598     struct_rna = collection ? &RNA_Object : &RNA_ObjectBase;
599     base_or_object_prop = RNA_struct_type_find_property(struct_rna, propname);
600   }
601
602   if (extend) {
603     outliner_collection_set_flag_recursive(scene,
604                                            view_layer,
605                                            layer_collection,
606                                            collection,
607                                            layer_or_collection_prop,
608                                            base_or_object_prop,
609                                            value);
610   }
611   else {
612     outliner_collection_isolate_flag(scene,
613                                      view_layer,
614                                      layer_collection,
615                                      collection,
616                                      layer_or_collection_prop,
617                                      propname,
618                                      value);
619   }
620
621   /* We don't call RNA_property_update() due to performance, so we batch update them. */
622   BKE_main_collection_sync_remap(bmain);
623   DEG_relations_tag_update(bmain);
624 }
625
626 /**
627  * Layer collection properties called from the ViewLayer mode.
628  * Change the (non-excluded) collection children, and the objects nested to them all.
629  */
630 static void view_layer__layer_collection_set_flag_recursive_cb(bContext *C,
631                                                                void *poin,
632                                                                void *poin2)
633 {
634   LayerCollection *layer_collection = poin;
635   char *propname = poin2;
636   outliner_collection_set_flag_recursive_cb(C, layer_collection, NULL, propname);
637 }
638
639 /**
640  * Collection properties called from the ViewLayer mode.
641  * Change the (non-excluded) collection children, and the objects nested to them all.
642  */
643 static void view_layer__collection_set_flag_recursive_cb(bContext *C, void *poin, void *poin2)
644 {
645   LayerCollection *layer_collection = poin;
646   char *propname = poin2;
647   outliner_collection_set_flag_recursive_cb(
648       C, layer_collection, layer_collection->collection, propname);
649 }
650
651 /**
652  * Collection properties called from the Scenes mode.
653  * Change the collection children but no objects.
654  */
655 static void scenes__collection_set_flag_recursive_cb(bContext *C, void *poin, void *poin2)
656 {
657   Collection *collection = poin;
658   char *propname = poin2;
659   outliner_collection_set_flag_recursive_cb(C, NULL, collection, propname);
660 }
661
662 static void namebutton_cb(bContext *C, void *tsep, char *oldname)
663 {
664   Main *bmain = CTX_data_main(C);
665   SpaceOutliner *soops = CTX_wm_space_outliner(C);
666   Object *obedit = CTX_data_edit_object(C);
667   BLI_mempool *ts = soops->treestore;
668   TreeStoreElem *tselem = tsep;
669
670   if (ts && tselem) {
671     TreeElement *te = outliner_find_tree_element(&soops->tree, tselem);
672
673     if (tselem->type == 0) {
674       BLI_libblock_ensure_unique_name(bmain, tselem->id->name);
675
676       switch (GS(tselem->id->name)) {
677         case ID_MA:
678           WM_event_add_notifier(C, NC_MATERIAL, NULL);
679           break;
680         case ID_TE:
681           WM_event_add_notifier(C, NC_TEXTURE, NULL);
682           break;
683         case ID_IM:
684           WM_event_add_notifier(C, NC_IMAGE, NULL);
685           break;
686         case ID_SCE:
687           WM_event_add_notifier(C, NC_SCENE, NULL);
688           break;
689         case ID_OB: {
690           Object *ob = (Object *)tselem->id;
691           if (ob->type == OB_MBALL) {
692             DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
693           }
694           DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
695           WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL);
696           break;
697         }
698         default:
699           WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL);
700           break;
701       }
702       /* Check the library target exists */
703       if (te->idcode == ID_LI) {
704         Library *lib = (Library *)tselem->id;
705         char expanded[FILE_MAX];
706
707         BKE_library_filepath_set(bmain, lib, lib->name);
708
709         BLI_strncpy(expanded, lib->name, sizeof(expanded));
710         BLI_path_abs(expanded, BKE_main_blendfile_path(bmain));
711         if (!BLI_exists(expanded)) {
712           BKE_reportf(CTX_wm_reports(C),
713                       RPT_ERROR,
714                       "Library path '%s' does not exist, correct this before saving",
715                       expanded);
716         }
717         else if (lib->id.tag & LIB_TAG_MISSING) {
718           BKE_reportf(CTX_wm_reports(C),
719                       RPT_INFO,
720                       "Library path '%s' is now valid, please reload the library",
721                       expanded);
722           lib->id.tag &= ~LIB_TAG_MISSING;
723         }
724       }
725     }
726     else {
727       switch (tselem->type) {
728         case TSE_DEFGROUP:
729           defgroup_unique_name(te->directdata, (Object *)tselem->id);  //  id = object
730           break;
731         case TSE_NLA_ACTION:
732           BLI_libblock_ensure_unique_name(bmain, tselem->id->name);
733           break;
734         case TSE_EBONE: {
735           bArmature *arm = (bArmature *)tselem->id;
736           if (arm->edbo) {
737             EditBone *ebone = te->directdata;
738             char newname[sizeof(ebone->name)];
739
740             /* restore bone name */
741             BLI_strncpy(newname, ebone->name, sizeof(ebone->name));
742             BLI_strncpy(ebone->name, oldname, sizeof(ebone->name));
743             ED_armature_bone_rename(bmain, obedit->data, oldname, newname);
744             WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
745           }
746           break;
747         }
748
749         case TSE_BONE: {
750           ViewLayer *view_layer = CTX_data_view_layer(C);
751           Scene *scene = CTX_data_scene(C);
752           bArmature *arm = (bArmature *)tselem->id;
753           Bone *bone = te->directdata;
754           char newname[sizeof(bone->name)];
755
756           /* always make current object active */
757           tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, true);
758
759           /* restore bone name */
760           BLI_strncpy(newname, bone->name, sizeof(bone->name));
761           BLI_strncpy(bone->name, oldname, sizeof(bone->name));
762           ED_armature_bone_rename(bmain, arm, oldname, newname);
763           WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
764           break;
765         }
766         case TSE_POSE_CHANNEL: {
767           Scene *scene = CTX_data_scene(C);
768           ViewLayer *view_layer = CTX_data_view_layer(C);
769           Object *ob = (Object *)tselem->id;
770           bPoseChannel *pchan = te->directdata;
771           char newname[sizeof(pchan->name)];
772
773           /* always make current pose-bone active */
774           tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, true);
775
776           BLI_assert(ob->type == OB_ARMATURE);
777
778           /* restore bone name */
779           BLI_strncpy(newname, pchan->name, sizeof(pchan->name));
780           BLI_strncpy(pchan->name, oldname, sizeof(pchan->name));
781           ED_armature_bone_rename(bmain, ob->data, oldname, newname);
782           WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
783           break;
784         }
785         case TSE_POSEGRP: {
786           Object *ob = (Object *)tselem->id;  // id = object
787           bActionGroup *grp = te->directdata;
788
789           BLI_uniquename(&ob->pose->agroups,
790                          grp,
791                          CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "Group"),
792                          '.',
793                          offsetof(bActionGroup, name),
794                          sizeof(grp->name));
795           WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
796           break;
797         }
798         case TSE_GP_LAYER: {
799           bGPdata *gpd = (bGPdata *)tselem->id; /* id = GP Datablock */
800           bGPDlayer *gpl = te->directdata;
801
802           /* always make layer active */
803           BKE_gpencil_layer_setactive(gpd, gpl);
804
805           // XXX: name needs translation stuff
806           BLI_uniquename(
807               &gpd->layers, gpl, "GP Layer", '.', offsetof(bGPDlayer, info), sizeof(gpl->info));
808
809           WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, gpd);
810           break;
811         }
812         case TSE_R_LAYER: {
813           Scene *scene = (Scene *)tselem->id;
814           ViewLayer *view_layer = te->directdata;
815
816           /* Restore old name. */
817           char newname[sizeof(view_layer->name)];
818           BLI_strncpy(newname, view_layer->name, sizeof(view_layer->name));
819           BLI_strncpy(view_layer->name, oldname, sizeof(view_layer->name));
820
821           /* Rename, preserving animation and compositing data. */
822           BKE_view_layer_rename(bmain, scene, view_layer, newname);
823           WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL);
824           break;
825         }
826         case TSE_LAYER_COLLECTION: {
827           BLI_libblock_ensure_unique_name(bmain, tselem->id->name);
828           WM_event_add_notifier(C, NC_ID | NA_RENAME, NULL);
829           break;
830         }
831       }
832     }
833     tselem->flag &= ~TSE_TEXTBUT;
834   }
835 }
836
837 typedef struct RestrictProperties {
838   bool initialized;
839
840   PropertyRNA *object_hide_viewport, *object_hide_select, *object_hide_render;
841   PropertyRNA *base_hide_viewport;
842   PropertyRNA *collection_hide_viewport, *collection_hide_select, *collection_hide_render;
843   PropertyRNA *layer_collection_holdout, *layer_collection_indirect_only,
844       *layer_collection_hide_viewport;
845   PropertyRNA *modifier_show_viewport, *modifier_show_render;
846 } RestrictProperties;
847
848 /* We don't care about the value of the property
849  * but whether the property should be active or grayed out. */
850 typedef struct RestrictPropertiesActive {
851   bool object_hide_viewport;
852   bool object_hide_select;
853   bool object_hide_render;
854   bool base_hide_viewport;
855   bool collection_hide_viewport;
856   bool collection_hide_select;
857   bool collection_hide_render;
858   bool layer_collection_holdout;
859   bool layer_collection_indirect_only;
860   bool layer_collection_hide_viewport;
861   bool modifier_show_viewport;
862   bool modifier_show_render;
863 } RestrictPropertiesActive;
864
865 static void outliner_restrict_properties_enable_collection_set(
866     PointerRNA *collection_ptr, RestrictProperties *props, RestrictPropertiesActive *props_active)
867 {
868   if (props_active->collection_hide_render) {
869     props_active->collection_hide_render = !RNA_property_boolean_get(
870         collection_ptr, props->collection_hide_render);
871     if (!props_active->collection_hide_render) {
872       props_active->layer_collection_holdout = false;
873       props_active->layer_collection_indirect_only = false;
874       props_active->object_hide_render = false;
875       props_active->modifier_show_render = false;
876     }
877   }
878
879   if (props_active->collection_hide_viewport) {
880     props_active->collection_hide_viewport = !RNA_property_boolean_get(
881         collection_ptr, props->collection_hide_viewport);
882     if (!props_active->collection_hide_viewport) {
883       props_active->collection_hide_select = false;
884       props_active->object_hide_select = false;
885       props_active->layer_collection_hide_viewport = false;
886       props_active->object_hide_viewport = false;
887       props_active->base_hide_viewport = false;
888       props_active->modifier_show_viewport = false;
889     }
890   }
891
892   if (props_active->collection_hide_select) {
893     props_active->collection_hide_select = !RNA_property_boolean_get(
894         collection_ptr, props->collection_hide_select);
895     if (!props_active->collection_hide_select) {
896       props_active->object_hide_select = false;
897     }
898   }
899 }
900
901 static void outliner_restrict_properties_enable_layer_collection_set(
902     PointerRNA *layer_collection_ptr,
903     PointerRNA *collection_ptr,
904     RestrictProperties *props,
905     RestrictPropertiesActive *props_active)
906 {
907   outliner_restrict_properties_enable_collection_set(collection_ptr, props, props_active);
908
909   if (props_active->layer_collection_holdout) {
910     props_active->layer_collection_holdout = RNA_property_boolean_get(
911         layer_collection_ptr, props->layer_collection_holdout);
912   }
913
914   if (props_active->layer_collection_indirect_only) {
915     props_active->layer_collection_indirect_only = RNA_property_boolean_get(
916         layer_collection_ptr, props->layer_collection_indirect_only);
917   }
918
919   if (props_active->layer_collection_hide_viewport) {
920     props_active->layer_collection_hide_viewport = !RNA_property_boolean_get(
921         layer_collection_ptr, props->layer_collection_hide_viewport);
922
923     if (!props_active->layer_collection_hide_viewport) {
924       props_active->base_hide_viewport = false;
925       props_active->collection_hide_select = false;
926       props_active->object_hide_select = false;
927     }
928   }
929 }
930
931 static void outliner_draw_restrictbuts(uiBlock *block,
932                                        Scene *scene,
933                                        ViewLayer *view_layer,
934                                        ARegion *ar,
935                                        SpaceOutliner *soops,
936                                        ListBase *lb,
937                                        RestrictPropertiesActive props_active_parent)
938 {
939   /* Get RNA properties (once for speed). */
940   static RestrictProperties props = {false};
941   if (!props.initialized) {
942     props.object_hide_viewport = RNA_struct_type_find_property(&RNA_Object, "hide_viewport");
943     props.object_hide_select = RNA_struct_type_find_property(&RNA_Object, "hide_select");
944     props.object_hide_render = RNA_struct_type_find_property(&RNA_Object, "hide_render");
945     props.base_hide_viewport = RNA_struct_type_find_property(&RNA_ObjectBase, "hide_viewport");
946     props.collection_hide_viewport = RNA_struct_type_find_property(&RNA_Collection,
947                                                                    "hide_viewport");
948     props.collection_hide_select = RNA_struct_type_find_property(&RNA_Collection, "hide_select");
949     props.collection_hide_render = RNA_struct_type_find_property(&RNA_Collection, "hide_render");
950     props.layer_collection_holdout = RNA_struct_type_find_property(&RNA_LayerCollection,
951                                                                    "holdout");
952     props.layer_collection_indirect_only = RNA_struct_type_find_property(&RNA_LayerCollection,
953                                                                          "indirect_only");
954     props.layer_collection_hide_viewport = RNA_struct_type_find_property(&RNA_LayerCollection,
955                                                                          "hide_viewport");
956     props.modifier_show_viewport = RNA_struct_type_find_property(&RNA_Modifier, "show_viewport");
957     props.modifier_show_render = RNA_struct_type_find_property(&RNA_Modifier, "show_render");
958
959     props.initialized = true;
960   }
961
962   struct {
963     int select;
964     int hide;
965     int viewport;
966     int render;
967     int indirect_only;
968     int holdout;
969   } restrict_offsets = {0};
970   int restrict_column_offset = 0;
971
972   /* This will determine the order of drawing from RIGHT to LEFT. */
973   if (soops->outlinevis == SO_VIEW_LAYER) {
974     if (soops->show_restrict_flags & SO_RESTRICT_INDIRECT_ONLY) {
975       restrict_offsets.indirect_only = (++restrict_column_offset) * UI_UNIT_X + V2D_SCROLL_WIDTH;
976     }
977     if (soops->show_restrict_flags & SO_RESTRICT_HOLDOUT) {
978       restrict_offsets.holdout = (++restrict_column_offset) * UI_UNIT_X + V2D_SCROLL_WIDTH;
979     }
980   }
981   if (soops->show_restrict_flags & SO_RESTRICT_RENDER) {
982     restrict_offsets.render = (++restrict_column_offset) * UI_UNIT_X + V2D_SCROLL_WIDTH;
983   }
984   if (soops->show_restrict_flags & SO_RESTRICT_VIEWPORT) {
985     restrict_offsets.viewport = (++restrict_column_offset) * UI_UNIT_X + V2D_SCROLL_WIDTH;
986   }
987   if (soops->show_restrict_flags & SO_RESTRICT_HIDE) {
988     restrict_offsets.hide = (++restrict_column_offset) * UI_UNIT_X + V2D_SCROLL_WIDTH;
989   }
990   if (soops->show_restrict_flags & SO_RESTRICT_SELECT) {
991     restrict_offsets.select = (++restrict_column_offset) * UI_UNIT_X + V2D_SCROLL_WIDTH;
992   }
993   BLI_assert((restrict_column_offset * UI_UNIT_X + V2D_SCROLL_WIDTH) ==
994              outliner_restrict_columns_width(soops));
995
996   /* Create buttons. */
997   uiBut *bt;
998
999   for (TreeElement *te = lb->first; te; te = te->next) {
1000     TreeStoreElem *tselem = TREESTORE(te);
1001     RestrictPropertiesActive props_active = props_active_parent;
1002
1003     if (te->ys + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && te->ys <= ar->v2d.cur.ymax) {
1004       if (tselem->type == TSE_R_LAYER && (soops->outlinevis == SO_SCENES)) {
1005         if (soops->show_restrict_flags & SO_RESTRICT_RENDER) {
1006           /* View layer render toggle. */
1007           ViewLayer *layer = te->directdata;
1008
1009           bt = uiDefIconButBitS(block,
1010                                 UI_BTYPE_ICON_TOGGLE_N,
1011                                 VIEW_LAYER_RENDER,
1012                                 0,
1013                                 ICON_RESTRICT_RENDER_OFF,
1014                                 (int)(ar->v2d.cur.xmax - restrict_offsets.render),
1015                                 te->ys,
1016                                 UI_UNIT_X,
1017                                 UI_UNIT_Y,
1018                                 &layer->flag,
1019                                 0,
1020                                 0,
1021                                 0,
1022                                 0,
1023                                 TIP_("Use view layer for rendering"));
1024           UI_but_func_set(bt, restrictbutton_r_lay_cb, tselem->id, NULL);
1025           UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1026           UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
1027         }
1028       }
1029       else if ((tselem->type == 0 && te->idcode == ID_OB) &&
1030                (te->flag & TE_CHILD_NOT_IN_COLLECTION)) {
1031         /* Don't show restrict columns for children that are not directly inside the collection. */
1032       }
1033       else if (tselem->type == 0 && te->idcode == ID_OB) {
1034         PointerRNA ptr;
1035         Object *ob = (Object *)tselem->id;
1036         RNA_id_pointer_create(&ob->id, &ptr);
1037
1038         if (soops->show_restrict_flags & SO_RESTRICT_HIDE) {
1039           Base *base = (te->directdata) ? (Base *)te->directdata :
1040                                           BKE_view_layer_base_find(view_layer, ob);
1041           if (base) {
1042             PointerRNA base_ptr;
1043             RNA_pointer_create(&ob->id, &RNA_ObjectBase, base, &base_ptr);
1044             bt = uiDefIconButR_prop(block,
1045                                     UI_BTYPE_ICON_TOGGLE,
1046                                     0,
1047                                     0,
1048                                     (int)(ar->v2d.cur.xmax - restrict_offsets.hide),
1049                                     te->ys,
1050                                     UI_UNIT_X,
1051                                     UI_UNIT_Y,
1052                                     &base_ptr,
1053                                     props.base_hide_viewport,
1054                                     -1,
1055                                     0,
1056                                     0,
1057                                     0,
1058                                     0,
1059                                     TIP_("Temporarly hide in viewport\n"
1060                                          "* Shift to set children"));
1061             UI_but_func_set(
1062                 bt, outliner__base_set_flag_recursive_cb, base, (void *)"hide_viewport");
1063             UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1064             if (!props_active.base_hide_viewport) {
1065               UI_but_flag_enable(bt, UI_BUT_INACTIVE);
1066             }
1067           }
1068         }
1069
1070         if (soops->show_restrict_flags & SO_RESTRICT_SELECT) {
1071           bt = uiDefIconButR_prop(block,
1072                                   UI_BTYPE_ICON_TOGGLE,
1073                                   0,
1074                                   0,
1075                                   (int)(ar->v2d.cur.xmax - restrict_offsets.select),
1076                                   te->ys,
1077                                   UI_UNIT_X,
1078                                   UI_UNIT_Y,
1079                                   &ptr,
1080                                   props.object_hide_select,
1081                                   -1,
1082                                   0,
1083                                   0,
1084                                   -1,
1085                                   -1,
1086                                   TIP_("Disable selection in viewport\n"
1087                                        "* Shift to set children"));
1088           UI_but_func_set(bt, outliner__object_set_flag_recursive_cb, ob, (char *)"hide_select");
1089           UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1090           if (!props_active.object_hide_select) {
1091             UI_but_flag_enable(bt, UI_BUT_INACTIVE);
1092           }
1093         }
1094
1095         if (soops->show_restrict_flags & SO_RESTRICT_VIEWPORT) {
1096           bt = uiDefIconButR_prop(block,
1097                                   UI_BTYPE_ICON_TOGGLE,
1098                                   0,
1099                                   0,
1100                                   (int)(ar->v2d.cur.xmax - restrict_offsets.viewport),
1101                                   te->ys,
1102                                   UI_UNIT_X,
1103                                   UI_UNIT_Y,
1104                                   &ptr,
1105                                   props.object_hide_viewport,
1106                                   -1,
1107                                   0,
1108                                   0,
1109                                   -1,
1110                                   -1,
1111                                   TIP_("Globally disable in viewports\n"
1112                                        "* Shift to set children"));
1113           UI_but_func_set(bt, outliner__object_set_flag_recursive_cb, ob, (void *)"hide_viewport");
1114           UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1115           if (!props_active.object_hide_viewport) {
1116             UI_but_flag_enable(bt, UI_BUT_INACTIVE);
1117           }
1118         }
1119
1120         if (soops->show_restrict_flags & SO_RESTRICT_RENDER) {
1121           bt = uiDefIconButR_prop(block,
1122                                   UI_BTYPE_ICON_TOGGLE,
1123                                   0,
1124                                   0,
1125                                   (int)(ar->v2d.cur.xmax - restrict_offsets.render),
1126                                   te->ys,
1127                                   UI_UNIT_X,
1128                                   UI_UNIT_Y,
1129                                   &ptr,
1130                                   props.object_hide_render,
1131                                   -1,
1132                                   0,
1133                                   0,
1134                                   -1,
1135                                   -1,
1136                                   TIP_("Globally disable in renders\n"
1137                                        "* Shift to set children"));
1138           UI_but_func_set(bt, outliner__object_set_flag_recursive_cb, ob, (char *)"hide_render");
1139           UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1140           if (!props_active.object_hide_render) {
1141             UI_but_flag_enable(bt, UI_BUT_INACTIVE);
1142           }
1143         }
1144       }
1145       else if (tselem->type == TSE_MODIFIER) {
1146         ModifierData *md = (ModifierData *)te->directdata;
1147
1148         PointerRNA ptr;
1149         RNA_pointer_create(tselem->id, &RNA_Modifier, md, &ptr);
1150
1151         if (soops->show_restrict_flags & SO_RESTRICT_VIEWPORT) {
1152           bt = uiDefIconButR_prop(block,
1153                                   UI_BTYPE_ICON_TOGGLE,
1154                                   0,
1155                                   0,
1156                                   (int)(ar->v2d.cur.xmax - restrict_offsets.viewport),
1157                                   te->ys,
1158                                   UI_UNIT_X,
1159                                   UI_UNIT_Y,
1160                                   &ptr,
1161                                   props.modifier_show_viewport,
1162                                   -1,
1163                                   0,
1164                                   0,
1165                                   -1,
1166                                   -1,
1167                                   NULL);
1168           UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1169           if (!props_active.modifier_show_viewport) {
1170             UI_but_flag_enable(bt, UI_BUT_INACTIVE);
1171           }
1172         }
1173
1174         if (soops->show_restrict_flags & SO_RESTRICT_RENDER) {
1175           bt = uiDefIconButR_prop(block,
1176                                   UI_BTYPE_ICON_TOGGLE,
1177                                   0,
1178                                   0,
1179                                   (int)(ar->v2d.cur.xmax - restrict_offsets.render),
1180                                   te->ys,
1181                                   UI_UNIT_X,
1182                                   UI_UNIT_Y,
1183                                   &ptr,
1184                                   props.modifier_show_render,
1185                                   -1,
1186                                   0,
1187                                   0,
1188                                   -1,
1189                                   -1,
1190                                   NULL);
1191           UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1192           if (!props_active.modifier_show_render) {
1193             UI_but_flag_enable(bt, UI_BUT_INACTIVE);
1194           }
1195         }
1196       }
1197       else if (tselem->type == TSE_POSE_CHANNEL) {
1198         bPoseChannel *pchan = (bPoseChannel *)te->directdata;
1199         Bone *bone = pchan->bone;
1200         Object *ob = (Object *)tselem->id;
1201
1202         if (soops->show_restrict_flags & SO_RESTRICT_VIEWPORT) {
1203           bt = uiDefIconButBitI(block,
1204                                 UI_BTYPE_ICON_TOGGLE,
1205                                 BONE_HIDDEN_P,
1206                                 0,
1207                                 ICON_HIDE_OFF,
1208                                 (int)(ar->v2d.cur.xmax - restrict_offsets.viewport),
1209                                 te->ys,
1210                                 UI_UNIT_X,
1211                                 UI_UNIT_Y,
1212                                 &(bone->flag),
1213                                 0,
1214                                 0,
1215                                 0,
1216                                 0,
1217                                 TIP_("Restrict visibility in the 3D View"));
1218           UI_but_func_set(bt, restrictbutton_bone_visibility_cb, ob->data, bone);
1219           UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1220           UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
1221         }
1222
1223         if (soops->show_restrict_flags & SO_RESTRICT_SELECT) {
1224           bt = uiDefIconButBitI(block,
1225                                 UI_BTYPE_ICON_TOGGLE,
1226                                 BONE_UNSELECTABLE,
1227                                 0,
1228                                 ICON_RESTRICT_SELECT_OFF,
1229                                 (int)(ar->v2d.cur.xmax - restrict_offsets.select),
1230                                 te->ys,
1231                                 UI_UNIT_X,
1232                                 UI_UNIT_Y,
1233                                 &(bone->flag),
1234                                 0,
1235                                 0,
1236                                 0,
1237                                 0,
1238                                 TIP_("Restrict selection in the 3D View"));
1239           UI_but_func_set(bt, restrictbutton_bone_select_cb, ob->data, bone);
1240           UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1241           UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
1242         }
1243       }
1244       else if (tselem->type == TSE_EBONE) {
1245         EditBone *ebone = (EditBone *)te->directdata;
1246
1247         if (soops->show_restrict_flags & SO_RESTRICT_VIEWPORT) {
1248           bt = uiDefIconButBitI(block,
1249                                 UI_BTYPE_ICON_TOGGLE,
1250                                 BONE_HIDDEN_A,
1251                                 0,
1252                                 ICON_RESTRICT_VIEW_OFF,
1253                                 (int)(ar->v2d.cur.xmax - restrict_offsets.viewport),
1254                                 te->ys,
1255                                 UI_UNIT_X,
1256                                 UI_UNIT_Y,
1257                                 &(ebone->flag),
1258                                 0,
1259                                 0,
1260                                 0,
1261                                 0,
1262                                 TIP_("Restrict visibility in the 3D View"));
1263           UI_but_func_set(bt, restrictbutton_ebone_visibility_cb, NULL, ebone);
1264           UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1265           UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
1266         }
1267
1268         if (soops->show_restrict_flags & SO_RESTRICT_SELECT) {
1269           bt = uiDefIconButBitI(block,
1270                                 UI_BTYPE_ICON_TOGGLE,
1271                                 BONE_UNSELECTABLE,
1272                                 0,
1273                                 ICON_RESTRICT_SELECT_OFF,
1274                                 (int)(ar->v2d.cur.xmax - restrict_offsets.select),
1275                                 te->ys,
1276                                 UI_UNIT_X,
1277                                 UI_UNIT_Y,
1278                                 &(ebone->flag),
1279                                 0,
1280                                 0,
1281                                 0,
1282                                 0,
1283                                 TIP_("Restrict selection in the 3D View"));
1284           UI_but_func_set(bt, restrictbutton_ebone_select_cb, NULL, ebone);
1285           UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1286           UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
1287         }
1288       }
1289       else if (tselem->type == TSE_GP_LAYER) {
1290         ID *id = tselem->id;
1291         bGPDlayer *gpl = (bGPDlayer *)te->directdata;
1292
1293         if (soops->show_restrict_flags & SO_RESTRICT_VIEWPORT) {
1294           bt = uiDefIconButBitS(block,
1295                                 UI_BTYPE_ICON_TOGGLE,
1296                                 GP_LAYER_HIDE,
1297                                 0,
1298                                 ICON_HIDE_OFF,
1299                                 (int)(ar->v2d.cur.xmax - restrict_offsets.viewport),
1300                                 te->ys,
1301                                 UI_UNIT_X,
1302                                 UI_UNIT_Y,
1303                                 &gpl->flag,
1304                                 0,
1305                                 0,
1306                                 0,
1307                                 0,
1308                                 TIP_("Restrict visibility in the 3D View"));
1309           UI_but_func_set(bt, restrictbutton_gp_layer_flag_cb, id, gpl);
1310           UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1311           UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
1312         }
1313
1314         if (soops->show_restrict_flags & SO_RESTRICT_SELECT) {
1315           bt = uiDefIconButBitS(block,
1316                                 UI_BTYPE_ICON_TOGGLE,
1317                                 GP_LAYER_LOCKED,
1318                                 0,
1319                                 ICON_UNLOCKED,
1320                                 (int)(ar->v2d.cur.xmax - restrict_offsets.select),
1321                                 te->ys,
1322                                 UI_UNIT_X,
1323                                 UI_UNIT_Y,
1324                                 &gpl->flag,
1325                                 0,
1326                                 0,
1327                                 0,
1328                                 0,
1329                                 TIP_("Restrict editing of strokes and keyframes in this layer"));
1330           UI_but_func_set(bt, restrictbutton_gp_layer_flag_cb, id, gpl);
1331           UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1332         }
1333       }
1334       else if (outliner_is_collection_tree_element(te)) {
1335         LayerCollection *layer_collection = (tselem->type == TSE_LAYER_COLLECTION) ?
1336                                                 te->directdata :
1337                                                 NULL;
1338         Collection *collection = outliner_collection_from_tree_element(te);
1339         if ((!layer_collection || !(layer_collection->flag & LAYER_COLLECTION_EXCLUDE)) &&
1340             !(collection->flag & COLLECTION_IS_MASTER)) {
1341
1342           PointerRNA collection_ptr;
1343           PointerRNA layer_collection_ptr;
1344           RNA_id_pointer_create(&collection->id, &collection_ptr);
1345           if (layer_collection != NULL) {
1346             RNA_pointer_create(
1347                 &scene->id, &RNA_LayerCollection, layer_collection, &layer_collection_ptr);
1348           }
1349
1350           /* Update the restriction column values for the collection children. */
1351           if (layer_collection) {
1352             outliner_restrict_properties_enable_layer_collection_set(
1353                 &layer_collection_ptr, &collection_ptr, &props, &props_active);
1354           }
1355           else {
1356             outliner_restrict_properties_enable_collection_set(
1357                 &collection_ptr, &props, &props_active);
1358           }
1359
1360           if (layer_collection != NULL) {
1361             if (soops->show_restrict_flags & SO_RESTRICT_HIDE) {
1362               bt = uiDefIconButR_prop(block,
1363                                       UI_BTYPE_ICON_TOGGLE,
1364                                       0,
1365                                       0,
1366                                       (int)(ar->v2d.cur.xmax - restrict_offsets.hide),
1367                                       te->ys,
1368                                       UI_UNIT_X,
1369                                       UI_UNIT_Y,
1370                                       &layer_collection_ptr,
1371                                       props.layer_collection_hide_viewport,
1372                                       -1,
1373                                       0,
1374                                       0,
1375                                       0,
1376                                       0,
1377                                       TIP_("Temporarily hide in viewport\n"
1378                                            "* Ctrl to isolate collection\n"
1379                                            "* Shift to set inside collections and objects"));
1380               UI_but_func_set(bt,
1381                               view_layer__layer_collection_set_flag_recursive_cb,
1382                               layer_collection,
1383                               (char *)"hide_viewport");
1384               UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1385               if (!props_active.layer_collection_hide_viewport) {
1386                 UI_but_flag_enable(bt, UI_BUT_INACTIVE);
1387               }
1388             }
1389
1390             if (soops->show_restrict_flags & SO_RESTRICT_HOLDOUT) {
1391               bt = uiDefIconButR_prop(block,
1392                                       UI_BTYPE_ICON_TOGGLE,
1393                                       0,
1394                                       0,
1395                                       (int)(ar->v2d.cur.xmax - restrict_offsets.holdout),
1396                                       te->ys,
1397                                       UI_UNIT_X,
1398                                       UI_UNIT_Y,
1399                                       &layer_collection_ptr,
1400                                       props.layer_collection_holdout,
1401                                       -1,
1402                                       0,
1403                                       0,
1404                                       0,
1405                                       0,
1406                                       TIP_("Mask out objects in collection from view layer\n"
1407                                            "* Ctrl to isolate collection\n"
1408                                            "* Shift to set inside collections"));
1409               UI_but_func_set(bt,
1410                               view_layer__layer_collection_set_flag_recursive_cb,
1411                               layer_collection,
1412                               (char *)"holdout");
1413               UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1414               if (!props_active.layer_collection_holdout) {
1415                 UI_but_flag_enable(bt, UI_BUT_INACTIVE);
1416               }
1417             }
1418
1419             if (soops->show_restrict_flags & SO_RESTRICT_INDIRECT_ONLY) {
1420               bt = uiDefIconButR_prop(
1421                   block,
1422                   UI_BTYPE_ICON_TOGGLE,
1423                   0,
1424                   0,
1425                   (int)(ar->v2d.cur.xmax - restrict_offsets.indirect_only),
1426                   te->ys,
1427                   UI_UNIT_X,
1428                   UI_UNIT_Y,
1429                   &layer_collection_ptr,
1430                   props.layer_collection_indirect_only,
1431                   -1,
1432                   0,
1433                   0,
1434                   0,
1435                   0,
1436                   TIP_("Objects in collection only contribute indirectly (through shadows and "
1437                        "reflections) in the view layer\n"
1438                        "* Ctrl to isolate collection\n"
1439                        "* Shift to set inside collections"));
1440               UI_but_func_set(bt,
1441                               view_layer__layer_collection_set_flag_recursive_cb,
1442                               layer_collection,
1443                               (char *)"indirect_only");
1444               UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1445               if (!props_active.layer_collection_indirect_only) {
1446                 UI_but_flag_enable(bt, UI_BUT_INACTIVE);
1447               }
1448             }
1449           }
1450
1451           if (soops->show_restrict_flags & SO_RESTRICT_VIEWPORT) {
1452             bt = uiDefIconButR_prop(block,
1453                                     UI_BTYPE_ICON_TOGGLE,
1454                                     0,
1455                                     0,
1456                                     (int)(ar->v2d.cur.xmax - restrict_offsets.viewport),
1457                                     te->ys,
1458                                     UI_UNIT_X,
1459                                     UI_UNIT_Y,
1460                                     &collection_ptr,
1461                                     props.collection_hide_viewport,
1462                                     -1,
1463                                     0,
1464                                     0,
1465                                     0,
1466                                     0,
1467                                     TIP_("Globally disable in viewports\n"
1468                                          "* Ctrl to isolate collection\n"
1469                                          "* Shift to set inside collections and objects"));
1470             if (layer_collection != NULL) {
1471               UI_but_func_set(bt,
1472                               view_layer__collection_set_flag_recursive_cb,
1473                               layer_collection,
1474                               (char *)"hide_viewport");
1475             }
1476             else {
1477               UI_but_func_set(bt,
1478                               scenes__collection_set_flag_recursive_cb,
1479                               collection,
1480                               (char *)"hide_viewport");
1481             }
1482             UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1483             if (!props_active.collection_hide_viewport) {
1484               UI_but_flag_enable(bt, UI_BUT_INACTIVE);
1485             }
1486           }
1487
1488           if (soops->show_restrict_flags & SO_RESTRICT_RENDER) {
1489             bt = uiDefIconButR_prop(block,
1490                                     UI_BTYPE_ICON_TOGGLE,
1491                                     0,
1492                                     0,
1493                                     (int)(ar->v2d.cur.xmax - restrict_offsets.render),
1494                                     te->ys,
1495                                     UI_UNIT_X,
1496                                     UI_UNIT_Y,
1497                                     &collection_ptr,
1498                                     props.collection_hide_render,
1499                                     -1,
1500                                     0,
1501                                     0,
1502                                     0,
1503                                     0,
1504                                     TIP_("Globally disable in renders\n"
1505                                          "* Ctrl to isolate collection\n"
1506                                          "* Shift to set inside collections and objects"));
1507             if (layer_collection != NULL) {
1508               UI_but_func_set(bt,
1509                               view_layer__collection_set_flag_recursive_cb,
1510                               layer_collection,
1511                               (char *)"hide_render");
1512             }
1513             else {
1514               UI_but_func_set(
1515                   bt, scenes__collection_set_flag_recursive_cb, collection, (char *)"hide_render");
1516             }
1517             UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1518             if (!props_active.collection_hide_render) {
1519               UI_but_flag_enable(bt, UI_BUT_INACTIVE);
1520             }
1521           }
1522
1523           if (soops->show_restrict_flags & SO_RESTRICT_SELECT) {
1524             bt = uiDefIconButR_prop(block,
1525                                     UI_BTYPE_ICON_TOGGLE,
1526                                     0,
1527                                     0,
1528                                     (int)(ar->v2d.cur.xmax - restrict_offsets.select),
1529                                     te->ys,
1530                                     UI_UNIT_X,
1531                                     UI_UNIT_Y,
1532                                     &collection_ptr,
1533                                     props.collection_hide_select,
1534                                     -1,
1535                                     0,
1536                                     0,
1537                                     0,
1538                                     0,
1539                                     TIP_("Disable selection in viewport\n"
1540                                          "* Ctrl to isolate collection\n"
1541                                          "* Shift to set inside collections and objects"));
1542             if (layer_collection != NULL) {
1543               UI_but_func_set(bt,
1544                               view_layer__collection_set_flag_recursive_cb,
1545                               layer_collection,
1546                               (char *)"hide_select");
1547             }
1548             else {
1549               UI_but_func_set(
1550                   bt, scenes__collection_set_flag_recursive_cb, collection, (char *)"hide_select");
1551             }
1552             UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1553             if (!props_active.collection_hide_select) {
1554               UI_but_flag_enable(bt, UI_BUT_INACTIVE);
1555             }
1556           }
1557         }
1558       }
1559     }
1560
1561     if (TSELEM_OPEN(tselem, soops)) {
1562       outliner_draw_restrictbuts(block, scene, view_layer, ar, soops, &te->subtree, props_active);
1563     }
1564   }
1565 }
1566
1567 static void outliner_draw_userbuts(uiBlock *block, ARegion *ar, SpaceOutliner *soops, ListBase *lb)
1568 {
1569
1570   for (TreeElement *te = lb->first; te; te = te->next) {
1571     TreeStoreElem *tselem = TREESTORE(te);
1572     if (te->ys + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && te->ys <= ar->v2d.cur.ymax) {
1573       if (tselem->type == 0) {
1574         uiBut *bt;
1575         ID *id = tselem->id;
1576         const char *tip = NULL;
1577         int icon = ICON_NONE;
1578         char buf[16] = "";
1579         int but_flag = UI_BUT_DRAG_LOCK;
1580
1581         if (ID_IS_LINKED(id)) {
1582           but_flag |= UI_BUT_DISABLED;
1583         }
1584
1585         BLI_str_format_int_grouped(buf, id->us);
1586         bt = uiDefBut(block,
1587                       UI_BTYPE_BUT,
1588                       1,
1589                       buf,
1590                       (int)(ar->v2d.cur.xmax - OL_TOG_USER_BUTS_USERS),
1591                       te->ys,
1592                       UI_UNIT_X,
1593                       UI_UNIT_Y,
1594                       NULL,
1595                       0.0,
1596                       0.0,
1597                       0,
1598                       0,
1599                       TIP_("Number of users of this data-block"));
1600         UI_but_flag_enable(bt, but_flag);
1601
1602         if (id->flag & LIB_FAKEUSER) {
1603           icon = ICON_FILE_TICK;
1604           tip = TIP_("Data-block will be retained using a fake user");
1605         }
1606         else {
1607           icon = ICON_X;
1608           tip = TIP_("Data-block has no users and will be deleted");
1609         }
1610         bt = uiDefIconButBitS(block,
1611                               UI_BTYPE_ICON_TOGGLE,
1612                               LIB_FAKEUSER,
1613                               1,
1614                               icon,
1615                               (int)(ar->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS),
1616                               te->ys,
1617                               UI_UNIT_X,
1618                               UI_UNIT_Y,
1619                               &id->flag,
1620                               0,
1621                               0,
1622                               0,
1623                               0,
1624                               tip);
1625         UI_but_func_set(bt, restrictbutton_id_user_toggle, id, NULL);
1626         UI_but_flag_enable(bt, but_flag);
1627
1628         bt = uiDefButBitS(block,
1629                           UI_BTYPE_ICON_TOGGLE,
1630                           LIB_FAKEUSER,
1631                           1,
1632                           (id->flag & LIB_FAKEUSER) ? "F" : " ",
1633                           (int)(ar->v2d.cur.xmax - OL_TOG_USER_BUTS_FAKEUSER),
1634                           te->ys,
1635                           UI_UNIT_X,
1636                           UI_UNIT_Y,
1637                           &id->flag,
1638                           0,
1639                           0,
1640                           0,
1641                           0,
1642                           TIP_("Data-block has a 'fake' user which will keep it in the file "
1643                                "even if nothing else uses it"));
1644         UI_but_func_set(bt, restrictbutton_id_user_toggle, id, NULL);
1645         UI_but_flag_enable(bt, but_flag);
1646       }
1647     }
1648
1649     if (TSELEM_OPEN(tselem, soops)) {
1650       outliner_draw_userbuts(block, ar, soops, &te->subtree);
1651     }
1652   }
1653 }
1654
1655 static void outliner_draw_rnacols(ARegion *ar, int sizex)
1656 {
1657   View2D *v2d = &ar->v2d;
1658
1659   float miny = v2d->cur.ymin;
1660   if (miny < v2d->tot.ymin) {
1661     miny = v2d->tot.ymin;
1662   }
1663
1664   GPU_line_width(1.0f);
1665
1666   uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
1667   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
1668   immUniformThemeColorShadeAlpha(TH_BACK, -15, -200);
1669
1670   immBegin(GPU_PRIM_LINES, 4);
1671
1672   immVertex2f(pos, sizex, v2d->cur.ymax);
1673   immVertex2f(pos, sizex, miny);
1674
1675   immVertex2f(pos, sizex + OL_RNA_COL_SIZEX, v2d->cur.ymax);
1676   immVertex2f(pos, sizex + OL_RNA_COL_SIZEX, miny);
1677
1678   immEnd();
1679
1680   immUnbindProgram();
1681 }
1682
1683 static void outliner_draw_rnabuts(
1684     uiBlock *block, ARegion *ar, SpaceOutliner *soops, int sizex, ListBase *lb)
1685 {
1686   PointerRNA *ptr;
1687   PropertyRNA *prop;
1688
1689   for (TreeElement *te = lb->first; te; te = te->next) {
1690     TreeStoreElem *tselem = TREESTORE(te);
1691     if (te->ys + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && te->ys <= ar->v2d.cur.ymax) {
1692       if (tselem->type == TSE_RNA_PROPERTY) {
1693         ptr = &te->rnaptr;
1694         prop = te->directdata;
1695
1696         if (!TSELEM_OPEN(tselem, soops)) {
1697           if (RNA_property_type(prop) == PROP_POINTER) {
1698             uiBut *but = uiDefAutoButR(block,
1699                                        ptr,
1700                                        prop,
1701                                        -1,
1702                                        "",
1703                                        ICON_NONE,
1704                                        sizex,
1705                                        te->ys,
1706                                        OL_RNA_COL_SIZEX,
1707                                        UI_UNIT_Y - 1);
1708             UI_but_flag_enable(but, UI_BUT_DISABLED);
1709           }
1710           else if (RNA_property_type(prop) == PROP_ENUM) {
1711             uiDefAutoButR(block,
1712                           ptr,
1713                           prop,
1714                           -1,
1715                           NULL,
1716                           ICON_NONE,
1717                           sizex,
1718                           te->ys,
1719                           OL_RNA_COL_SIZEX,
1720                           UI_UNIT_Y - 1);
1721           }
1722           else {
1723             uiDefAutoButR(block,
1724                           ptr,
1725                           prop,
1726                           -1,
1727                           "",
1728                           ICON_NONE,
1729                           sizex,
1730                           te->ys,
1731                           OL_RNA_COL_SIZEX,
1732                           UI_UNIT_Y - 1);
1733           }
1734         }
1735       }
1736       else if (tselem->type == TSE_RNA_ARRAY_ELEM) {
1737         ptr = &te->rnaptr;
1738         prop = te->directdata;
1739
1740         uiDefAutoButR(block,
1741                       ptr,
1742                       prop,
1743                       te->index,
1744                       "",
1745                       ICON_NONE,
1746                       sizex,
1747                       te->ys,
1748                       OL_RNA_COL_SIZEX,
1749                       UI_UNIT_Y - 1);
1750       }
1751     }
1752
1753     if (TSELEM_OPEN(tselem, soops)) {
1754       outliner_draw_rnabuts(block, ar, soops, sizex, &te->subtree);
1755     }
1756   }
1757 }
1758
1759 static void outliner_buttons(const bContext *C,
1760                              uiBlock *block,
1761                              ARegion *ar,
1762                              const float restrict_column_width,
1763                              TreeElement *te)
1764 {
1765   SpaceOutliner *soops = CTX_wm_space_outliner(C);
1766   uiBut *bt;
1767   TreeStoreElem *tselem;
1768   int spx, dx, len;
1769
1770   tselem = TREESTORE(te);
1771
1772   BLI_assert(tselem->flag & TSE_TEXTBUT);
1773   /* If we add support to rename Sequence.
1774    * need change this.
1775    */
1776
1777   if (tselem->type == TSE_EBONE) {
1778     len = sizeof(((EditBone *)0)->name);
1779   }
1780   else if (tselem->type == TSE_MODIFIER) {
1781     len = sizeof(((ModifierData *)0)->name);
1782   }
1783   else if (tselem->id && GS(tselem->id->name) == ID_LI) {
1784     len = sizeof(((Library *)0)->name);
1785   }
1786   else {
1787     len = MAX_ID_NAME - 2;
1788   }
1789
1790   spx = te->xs + 1.8f * UI_UNIT_X;
1791   if ((tselem->type == TSE_LAYER_COLLECTION) &&
1792       (soops->show_restrict_flags & SO_RESTRICT_ENABLE)) {
1793     spx += UI_UNIT_X;
1794   }
1795   dx = ar->v2d.cur.xmax - (spx + restrict_column_width + 0.2f * UI_UNIT_X);
1796
1797   bt = uiDefBut(block,
1798                 UI_BTYPE_TEXT,
1799                 OL_NAMEBUTTON,
1800                 "",
1801                 spx,
1802                 te->ys,
1803                 dx,
1804                 UI_UNIT_Y - 1,
1805                 (void *)te->name,
1806                 1.0,
1807                 (float)len,
1808                 0,
1809                 0,
1810                 "");
1811   UI_but_func_rename_set(bt, namebutton_cb, tselem);
1812
1813   /* returns false if button got removed */
1814   if (false == UI_but_active_only(C, ar, block, bt)) {
1815     tselem->flag &= ~TSE_TEXTBUT;
1816
1817     /* bad! (notifier within draw) without this, we don't get a refresh */
1818     WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, NULL);
1819   }
1820 }
1821
1822 /* ****************************************************** */
1823 /* Normal Drawing... */
1824
1825 TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
1826 {
1827   TreeElementIcon data = {0};
1828
1829   if (tselem->type) {
1830     switch (tselem->type) {
1831       case TSE_ANIM_DATA:
1832         data.icon = ICON_ANIM_DATA; /* XXX */
1833         break;
1834       case TSE_NLA:
1835         data.icon = ICON_NLA;
1836         break;
1837       case TSE_NLA_TRACK:
1838         data.icon = ICON_NLA; /* XXX */
1839         break;
1840       case TSE_NLA_ACTION:
1841         data.icon = ICON_ACTION;
1842         break;
1843       case TSE_DRIVER_BASE:
1844         data.icon = ICON_DRIVER;
1845         break;
1846       case TSE_DEFGROUP_BASE:
1847         data.icon = ICON_GROUP_VERTEX;
1848         break;
1849       case TSE_BONE:
1850       case TSE_EBONE:
1851         data.icon = ICON_BONE_DATA;
1852         break;
1853       case TSE_CONSTRAINT_BASE:
1854         data.icon = ICON_CONSTRAINT;
1855         break;
1856       case TSE_MODIFIER_BASE:
1857         data.icon = ICON_MODIFIER_DATA;
1858         break;
1859       case TSE_LINKED_OB:
1860         data.icon = ICON_OBJECT_DATA;
1861         break;
1862       case TSE_LINKED_PSYS:
1863         data.icon = ICON_PARTICLES;
1864         break;
1865       case TSE_MODIFIER: {
1866         Object *ob = (Object *)tselem->id;
1867         if (ob->type != OB_GPENCIL) {
1868           ModifierData *md = BLI_findlink(&ob->modifiers, tselem->nr);
1869           switch ((ModifierType)md->type) {
1870             case eModifierType_Subsurf:
1871               data.icon = ICON_MOD_SUBSURF;
1872               break;
1873             case eModifierType_Armature:
1874               data.icon = ICON_MOD_ARMATURE;
1875               break;
1876             case eModifierType_Lattice:
1877               data.icon = ICON_MOD_LATTICE;
1878               break;
1879             case eModifierType_Curve:
1880               data.icon = ICON_MOD_CURVE;
1881               break;
1882             case eModifierType_Build:
1883               data.icon = ICON_MOD_BUILD;
1884               break;
1885             case eModifierType_Mirror:
1886               data.icon = ICON_MOD_MIRROR;
1887               break;
1888             case eModifierType_Decimate:
1889               data.icon = ICON_MOD_DECIM;
1890               break;
1891             case eModifierType_Wave:
1892               data.icon = ICON_MOD_WAVE;
1893               break;
1894             case eModifierType_Hook:
1895               data.icon = ICON_HOOK;
1896               break;
1897             case eModifierType_Softbody:
1898               data.icon = ICON_MOD_SOFT;
1899               break;
1900             case eModifierType_Boolean:
1901               data.icon = ICON_MOD_BOOLEAN;
1902               break;
1903             case eModifierType_ParticleSystem:
1904               data.icon = ICON_MOD_PARTICLES;
1905               break;
1906             case eModifierType_ParticleInstance:
1907               data.icon = ICON_MOD_PARTICLES;
1908               break;
1909             case eModifierType_EdgeSplit:
1910               data.icon = ICON_MOD_EDGESPLIT;
1911               break;
1912             case eModifierType_Array:
1913               data.icon = ICON_MOD_ARRAY;
1914               break;
1915             case eModifierType_UVProject:
1916             case eModifierType_UVWarp: /* TODO, get own icon */
1917               data.icon = ICON_MOD_UVPROJECT;
1918               break;
1919             case eModifierType_Displace:
1920               data.icon = ICON_MOD_DISPLACE;
1921               break;
1922             case eModifierType_Shrinkwrap:
1923               data.icon = ICON_MOD_SHRINKWRAP;
1924               break;
1925             case eModifierType_Cast:
1926               data.icon = ICON_MOD_CAST;
1927               break;
1928             case eModifierType_MeshDeform:
1929             case eModifierType_SurfaceDeform:
1930               data.icon = ICON_MOD_MESHDEFORM;
1931               break;
1932             case eModifierType_Bevel:
1933               data.icon = ICON_MOD_BEVEL;
1934               break;
1935             case eModifierType_Smooth:
1936             case eModifierType_LaplacianSmooth:
1937             case eModifierType_CorrectiveSmooth:
1938               data.icon = ICON_MOD_SMOOTH;
1939               break;
1940             case eModifierType_SimpleDeform:
1941               data.icon = ICON_MOD_SIMPLEDEFORM;
1942               break;
1943             case eModifierType_Mask:
1944               data.icon = ICON_MOD_MASK;
1945               break;
1946             case eModifierType_Cloth:
1947               data.icon = ICON_MOD_CLOTH;
1948               break;
1949             case eModifierType_Explode:
1950               data.icon = ICON_MOD_EXPLODE;
1951               break;
1952             case eModifierType_Collision:
1953             case eModifierType_Surface:
1954               data.icon = ICON_MOD_PHYSICS;
1955               break;
1956             case eModifierType_Fluidsim:
1957               data.icon = ICON_MOD_FLUIDSIM;
1958               break;
1959             case eModifierType_Multires:
1960               data.icon = ICON_MOD_MULTIRES;
1961               break;
1962             case eModifierType_Smoke:
1963               data.icon = ICON_MOD_SMOKE;
1964               break;
1965             case eModifierType_Solidify:
1966               data.icon = ICON_MOD_SOLIDIFY;
1967               break;
1968             case eModifierType_Screw:
1969               data.icon = ICON_MOD_SCREW;
1970               break;
1971             case eModifierType_Remesh:
1972               data.icon = ICON_MOD_REMESH;
1973               break;
1974             case eModifierType_WeightVGEdit:
1975             case eModifierType_WeightVGMix:
1976             case eModifierType_WeightVGProximity:
1977               data.icon = ICON_MOD_VERTEX_WEIGHT;
1978               break;
1979             case eModifierType_DynamicPaint:
1980               data.icon = ICON_MOD_DYNAMICPAINT;
1981               break;
1982             case eModifierType_Ocean:
1983               data.icon = ICON_MOD_OCEAN;
1984               break;
1985             case eModifierType_Warp:
1986               data.icon = ICON_MOD_WARP;
1987               break;
1988             case eModifierType_Skin:
1989               data.icon = ICON_MOD_SKIN;
1990               break;
1991             case eModifierType_Triangulate:
1992               data.icon = ICON_MOD_TRIANGULATE;
1993               break;
1994             case eModifierType_MeshCache:
1995               data.icon = ICON_MOD_MESHDEFORM; /* XXX, needs own icon */
1996               break;
1997             case eModifierType_MeshSequenceCache:
1998               data.icon = ICON_MOD_MESHDEFORM; /* XXX, needs own icon */
1999               break;
2000             case eModifierType_Wireframe:
2001               data.icon = ICON_MOD_WIREFRAME;
2002               break;
2003             case eModifierType_LaplacianDeform:
2004               data.icon = ICON_MOD_MESHDEFORM; /* XXX, needs own icon */
2005               break;
2006             case eModifierType_DataTransfer:
2007               data.icon = ICON_MOD_DATA_TRANSFER;
2008               break;
2009             case eModifierType_NormalEdit:
2010             case eModifierType_WeightedNormal:
2011               data.icon = ICON_MOD_NORMALEDIT;
2012               break;
2013               /* Default */
2014             case eModifierType_None:
2015             case eModifierType_ShapeKey:
2016
2017             case NUM_MODIFIER_TYPES:
2018               data.icon = ICON_DOT;
2019               break;
2020           }
2021         }
2022         else {
2023           /* grease pencil modifiers */
2024           GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, tselem->nr);
2025           switch ((GpencilModifierType)md->type) {
2026             case eGpencilModifierType_Noise:
2027               data.icon = ICON_RNDCURVE;
2028               break;
2029             case eGpencilModifierType_Subdiv:
2030               data.icon = ICON_MOD_SUBSURF;
2031               break;
2032             case eGpencilModifierType_Thick:
2033               data.icon = ICON_MOD_THICKNESS;
2034               break;
2035             case eGpencilModifierType_Tint:
2036               data.icon = ICON_MOD_TINT;
2037               break;
2038             case eGpencilModifierType_Array:
2039               data.icon = ICON_MOD_ARRAY;
2040               break;
2041             case eGpencilModifierType_Build:
2042               data.icon = ICON_MOD_BUILD;
2043               break;
2044             case eGpencilModifierType_Opacity:
2045               data.icon = ICON_MOD_MASK;
2046               break;
2047             case eGpencilModifierType_Color:
2048               data.icon = ICON_MOD_HUE_SATURATION;
2049               break;
2050             case eGpencilModifierType_Lattice:
2051               data.icon = ICON_MOD_LATTICE;
2052               break;
2053             case eGpencilModifierType_Mirror:
2054               data.icon = ICON_MOD_MIRROR;
2055               break;
2056             case eGpencilModifierType_Simplify:
2057               data.icon = ICON_MOD_SIMPLIFY;
2058               break;
2059             case eGpencilModifierType_Smooth:
2060               data.icon = ICON_MOD_SMOOTH;
2061               break;
2062             case eGpencilModifierType_Hook:
2063               data.icon = ICON_HOOK;
2064               break;
2065             case eGpencilModifierType_Offset:
2066               data.icon = ICON_MOD_OFFSET;
2067               break;
2068             case eGpencilModifierType_Armature:
2069               data.icon = ICON_MOD_ARMATURE;
2070               break;
2071
2072               /* Default */
2073             default:
2074               data.icon = ICON_DOT;
2075               break;
2076           }
2077         }
2078         break;
2079       }
2080       case TSE_POSE_BASE:
2081         data.icon = ICON_ARMATURE_DATA;
2082         break;
2083       case TSE_POSE_CHANNEL:
2084         data.icon = ICON_BONE_DATA;
2085         break;
2086       case TSE_PROXY:
2087         data.icon = ICON_GHOST_ENABLED;
2088         break;
2089       case TSE_R_LAYER_BASE:
2090         data.icon = ICON_RENDERLAYERS;
2091         break;
2092       case TSE_SCENE_OBJECTS_BASE:
2093         data.icon = ICON_OUTLINER_OB_GROUP_INSTANCE;
2094         break;
2095       case TSE_R_LAYER:
2096         data.icon = ICON_RENDER_RESULT;
2097         break;
2098       case TSE_LINKED_LAMP:
2099         data.icon = ICON_LIGHT_DATA;
2100         break;
2101       case TSE_LINKED_MAT:
2102         data.icon = ICON_MATERIAL_DATA;
2103         break;
2104       case TSE_POSEGRP_BASE:
2105         data.icon = ICON_GROUP_BONE;
2106         break;
2107       case TSE_SEQUENCE:
2108         if (te->idcode == SEQ_TYPE_MOVIE) {
2109           data.icon = ICON_SEQUENCE;
2110         }
2111         else if (te->idcode == SEQ_TYPE_META) {
2112           data.icon = ICON_DOT;
2113         }
2114         else if (te->idcode == SEQ_TYPE_SCENE) {
2115           data.icon = ICON_SCENE;
2116         }
2117         else if (te->idcode == SEQ_TYPE_SOUND_RAM) {
2118           data.icon = ICON_SOUND;
2119         }
2120         else if (te->idcode == SEQ_TYPE_IMAGE) {
2121           data.icon = ICON_IMAGE;
2122         }
2123         else {
2124           data.icon = ICON_PARTICLES;
2125         }
2126         break;
2127       case TSE_SEQ_STRIP:
2128         data.icon = ICON_LIBRARY_DATA_DIRECT;
2129         break;
2130       case TSE_SEQUENCE_DUP:
2131         data.icon = ICON_OBJECT_DATA;
2132         break;
2133       case TSE_RNA_STRUCT:
2134         if (RNA_struct_is_ID(te->rnaptr.type)) {
2135           data.drag_id = (ID *)te->rnaptr.data;
2136           data.icon = RNA_struct_ui_icon(te->rnaptr.type);
2137         }
2138         else {
2139           data.icon = RNA_struct_ui_icon(te->rnaptr.type);
2140         }
2141         break;
2142       case TSE_LAYER_COLLECTION:
2143       case TSE_SCENE_COLLECTION_BASE:
2144       case TSE_VIEW_COLLECTION_BASE: {
2145         Collection *collection = outliner_collection_from_tree_element(te);
2146         if (collection && !(collection->flag & COLLECTION_IS_MASTER)) {
2147           data.drag_id = tselem->id;
2148           data.drag_parent = (data.drag_id && te->parent) ? TREESTORE(te->parent)->id : NULL;
2149         }
2150
2151         data.icon = ICON_GROUP;
2152         break;
2153       }
2154       case TSE_GP_LAYER: {
2155         data.icon = ICON_OUTLINER_DATA_GP_LAYER;
2156         break;
2157       }
2158       default:
2159         data.icon = ICON_DOT;
2160         break;
2161     }
2162   }
2163   else if (tselem->id) {
2164     data.drag_id = tselem->id;
2165     data.drag_parent = (data.drag_id && te->parent) ? TREESTORE(te->parent)->id : NULL;
2166
2167     if (GS(tselem->id->name) == ID_OB) {
2168       Object *ob = (Object *)tselem->id;
2169       switch (ob->type) {
2170         case OB_LAMP:
2171           data.icon = ICON_OUTLINER_OB_LIGHT;
2172           break;
2173         case OB_MESH:
2174           data.icon = ICON_OUTLINER_OB_MESH;
2175           break;
2176         case OB_CAMERA:
2177           data.icon = ICON_OUTLINER_OB_CAMERA;
2178           break;
2179         case OB_CURVE:
2180           data.icon = ICON_OUTLINER_OB_CURVE;
2181           break;
2182         case OB_MBALL:
2183           data.icon = ICON_OUTLINER_OB_META;
2184           break;
2185         case OB_LATTICE:
2186           data.icon = ICON_OUTLINER_OB_LATTICE;
2187           break;
2188         case OB_ARMATURE:
2189           data.icon = ICON_OUTLINER_OB_ARMATURE;
2190           break;
2191         case OB_FONT:
2192           data.icon = ICON_OUTLINER_OB_FONT;
2193           break;
2194         case OB_SURF:
2195           data.icon = ICON_OUTLINER_OB_SURFACE;
2196           break;
2197         case OB_SPEAKER:
2198           data.icon = ICON_OUTLINER_OB_SPEAKER;
2199           break;
2200         case OB_LIGHTPROBE:
2201           data.icon = ICON_OUTLINER_OB_LIGHTPROBE;
2202           break;
2203         case OB_EMPTY:
2204           if (ob->instance_collection) {
2205             data.icon = ICON_OUTLINER_OB_GROUP_INSTANCE;
2206           }
2207           else if (ob->empty_drawtype == OB_EMPTY_IMAGE) {
2208             data.icon = ICON_OUTLINER_OB_IMAGE;
2209           }
2210           else {
2211             data.icon = ICON_OUTLINER_OB_EMPTY;
2212           }
2213           break;
2214         case OB_GPENCIL:
2215           data.icon = ICON_OUTLINER_OB_GREASEPENCIL;
2216           break;
2217           break;
2218       }
2219     }
2220     else {
2221       /* TODO(sergey): Casting to short here just to handle ID_NLA which is
2222        * NOT inside of IDType enum.
2223        */
2224       switch ((short)GS(tselem->id->name)) {
2225         case ID_SCE:
2226           data.icon = ICON_SCENE_DATA;
2227           break;
2228         case ID_ME:
2229           data.icon = ICON_OUTLINER_DATA_MESH;
2230           break;
2231         case ID_CU:
2232           data.icon = ICON_OUTLINER_DATA_CURVE;
2233           break;
2234         case ID_MB:
2235           data.icon = ICON_OUTLINER_DATA_META;
2236           break;
2237         case ID_LT:
2238           data.icon = ICON_OUTLINER_DATA_LATTICE;
2239           break;
2240         case ID_LA: {
2241           Light *la = (Light *)tselem->id;
2242           switch (la->type) {
2243             case LA_LOCAL:
2244               data.icon = ICON_LIGHT_POINT;
2245               break;
2246             case LA_SUN:
2247               data.icon = ICON_LIGHT_SUN;
2248               break;
2249             case LA_SPOT:
2250               data.icon = ICON_LIGHT_SPOT;
2251               break;
2252             case LA_AREA:
2253               data.icon = ICON_LIGHT_AREA;
2254               break;
2255             default:
2256               data.icon = ICON_OUTLINER_DATA_LIGHT;
2257               break;
2258           }
2259           break;
2260         }
2261         case ID_MA:
2262           data.icon = ICON_MATERIAL_DATA;
2263           break;
2264         case ID_TE:
2265           data.icon = ICON_TEXTURE_DATA;
2266           break;
2267         case ID_IM:
2268           data.icon = ICON_IMAGE_DATA;
2269           break;
2270         case ID_SPK:
2271         case ID_SO:
2272           data.icon = ICON_OUTLINER_DATA_SPEAKER;
2273           break;
2274         case ID_AR:
2275           data.icon = ICON_OUTLINER_DATA_ARMATURE;
2276           break;
2277         case ID_CA:
2278           data.icon = ICON_OUTLINER_DATA_CAMERA;
2279           break;
2280         case ID_KE:
2281           data.icon = ICON_SHAPEKEY_DATA;
2282           break;
2283         case ID_WO:
2284           data.icon = ICON_WORLD_DATA;
2285           break;
2286         case ID_AC:
2287           data.icon = ICON_ACTION;
2288           break;
2289         case ID_NLA:
2290           data.icon = ICON_NLA;
2291           break;
2292         case ID_TXT:
2293           data.icon = ICON_SCRIPT;
2294           break;
2295         case ID_GR:
2296           data.icon = ICON_GROUP;
2297           break;
2298         case ID_LI:
2299           if (tselem->id->tag & LIB_TAG_MISSING) {
2300             data.icon = ICON_LIBRARY_DATA_BROKEN;
2301           }
2302           else if (((Library *)tselem->id)->parent) {
2303             data.icon = ICON_LIBRARY_DATA_INDIRECT;
2304           }
2305           else {
2306             data.icon = ICON_LIBRARY_DATA_DIRECT;
2307           }
2308           break;
2309         case ID_LS:
2310           data.icon = ICON_LINE_DATA;
2311           break;
2312         case ID_GD:
2313           data.icon = ICON_OUTLINER_DATA_GREASEPENCIL;
2314           break;
2315         case ID_LP: {
2316           LightProbe *lp = (LightProbe *)tselem->id;
2317           switch (lp->type) {
2318             case LIGHTPROBE_TYPE_CUBE:
2319               data.icon = ICON_LIGHTPROBE_CUBEMAP;
2320               break;
2321             case LIGHTPROBE_TYPE_PLANAR:
2322               data.icon = ICON_LIGHTPROBE_PLANAR;
2323               break;
2324             case LIGHTPROBE_TYPE_GRID:
2325               data.icon = ICON_LIGHTPROBE_GRID;
2326               break;
2327             default:
2328               data.icon = ICON_LIGHTPROBE_CUBEMAP;
2329               break;
2330           }
2331           break;
2332         }
2333         case ID_BR:
2334           data.icon = ICON_BRUSH_DATA;
2335           break;
2336         case ID_SCR:
2337         case ID_WS:
2338           data.icon = ICON_WORKSPACE;
2339           break;
2340         case ID_MSK:
2341           data.icon = ICON_MOD_MASK;
2342           break;
2343         case ID_MC:
2344           data.icon = ICON_SEQUENCE;
2345           break;
2346         case ID_PC:
2347           data.icon = ICON_CURVE_BEZCURVE;
2348           break;
2349         default:
2350           break;
2351       }
2352     }
2353   }
2354
2355   return data;
2356 }
2357
2358 static void tselem_draw_layer_collection_enable_icon(
2359     Scene *scene, uiBlock *block, int xmax, float x, float y, TreeElement *te, float alpha)
2360 {
2361   /* Get RNA property (once for speed). */
2362   static PropertyRNA *exclude_prop = NULL;
2363   if (exclude_prop == NULL) {
2364     exclude_prop = RNA_struct_type_find_property(&RNA_LayerCollection, "exclude");
2365   }
2366
2367   if (x >= xmax) {
2368     /* Placement of icons, copied from interface_widgets.c. */
2369     float aspect = (0.8f * UI_UNIT_Y) / ICON_DEFAULT_HEIGHT;
2370     x += 2.0f * aspect;
2371     y += 2.0f * aspect;
2372
2373     /* restrict column clip... it has been coded by simply overdrawing,
2374      * doesn't work for buttons */
2375     char color[4];
2376     int icon = RNA_property_ui_icon(exclude_prop);
2377     if (UI_icon_get_theme_color(icon, (uchar *)color)) {
2378       UI_icon_draw_ex(x, y, icon, U.inv_dpi_fac, alpha, 0.0f, color, true);
2379     }
2380     else {
2381       UI_icon_draw_ex(x, y, icon, U.inv_dpi_fac, alpha, 0.0f, NULL, false);
2382     }
2383   }
2384   else {
2385     LayerCollection *layer_collection = te->directdata;
2386     PointerRNA layer_collection_ptr;
2387     RNA_pointer_create(&scene->id, &RNA_LayerCollection, layer_collection, &layer_collection_ptr);
2388
2389     char emboss = UI_block_emboss_get(block);
2390     UI_block_emboss_set(block, UI_EMBOSS_NONE);
2391     uiBut *bt = uiDefIconButR_prop(block,
2392                                    UI_BTYPE_ICON_TOGGLE,
2393                                    0,
2394                                    0,
2395                                    x,
2396                                    y,
2397                                    UI_UNIT_X,
2398                                    UI_UNIT_Y,
2399                                    &layer_collection_ptr,
2400                                    exclude_prop,
2401                                    -1,
2402                                    0,
2403                                    0,
2404                                    0,
2405                                    0,
2406                                    NULL);
2407     UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
2408     UI_block_emboss_set(block, emboss);
2409   }
2410 }
2411
2412 static void tselem_draw_icon(uiBlock *block,
2413                              int xmax,
2414                              float x,
2415                              float y,
2416                              TreeStoreElem *tselem,
2417                              TreeElement *te,
2418                              float alpha,
2419                              const bool is_clickable)
2420 {
2421   TreeElementIcon data = tree_element_get_icon(tselem, te);
2422
2423   if (data.icon == 0) {
2424     return;
2425   }
2426
2427   if (!is_clickable || x >= xmax) {
2428     /* placement of icons, copied from interface_widgets.c */
2429     float aspect = (0.8f * UI_UNIT_Y) / ICON_DEFAULT_HEIGHT;
2430     x += 2.0f * aspect;
2431     y += 2.0f * aspect;
2432
2433     /* restrict column clip... it has been coded by simply overdrawing,
2434      * doesn't work for buttons */
2435     char color[4];
2436     if (UI_icon_get_theme_color(data.icon, (uchar *)color)) {
2437       UI_icon_draw_ex(x, y, data.icon, U.inv_dpi_fac, alpha, 0.0f, color, true);
2438     }
2439     else {
2440       UI_icon_draw_ex(x, y, data.icon, U.inv_dpi_fac, alpha, 0.0f, NULL, false);
2441     }
2442   }
2443   else {
2444     uiDefIconBut(block,
2445                  UI_BTYPE_LABEL,
2446                  0,
2447                  data.icon,
2448                  x,
2449                  y,
2450                  UI_UNIT_X,
2451                  UI_UNIT_Y,
2452                  NULL,
2453                  0.0,
2454                  0.0,
2455                  1.0,
2456                  alpha,
2457                  (data.drag_id && ID_IS_LINKED(data.drag_id)) ? data.drag_id->lib->name : "");
2458   }
2459 }
2460
2461 /**
2462  * For icon-only children of a collapsed tree,
2463  * Draw small number over the icon to show how many items of this type are displayed.
2464  */
2465 static void outliner_draw_iconrow_number(const uiFontStyle *fstyle,
2466                                          int offsx,
2467                                          int ys,
2468                                          const int num_elements)
2469 {
2470   float color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
2471   float ufac = 0.25f * UI_UNIT_X;
2472   float offset_x = (float)offsx + UI_UNIT_X * 0.35f;
2473
2474   UI_draw_roundbox_corner_set(UI_CNR_ALL);
2475   UI_draw_roundbox_aa(true,
2476                       offset_x + ufac,
2477                       (float)ys - UI_UNIT_Y * 0.2f + ufac,
2478                       offset_x + UI_UNIT_X - ufac,
2479                       (float)ys - UI_UNIT_Y * 0.2f + UI_UNIT_Y - ufac,
2480                       (float)UI_UNIT_Y / 2.0f - ufac,
2481                       color);
2482
2483   /* Now the numbers. */
2484   unsigned char text_col[4];
2485
2486   UI_GetThemeColor4ubv(TH_TEXT_HI, text_col);
2487   text_col[3] = 255;
2488
2489   uiFontStyle fstyle_small = *fstyle;
2490   fstyle_small.points *= 0.8f;
2491
2492   /* We treat +99 as 4 digits to make sure the (eyeballed) alignment looks nice. */
2493   int num_digits = 4;
2494   char number_text[4] = "+99\0";
2495   if (num_elements < 100) {
2496     BLI_snprintf(number_text, sizeof(number_text), "%d", num_elements);
2497     num_digits = num_elements < 10 ? 1 : 2;
2498   }
2499   UI_fontstyle_draw_simple(&fstyle_small,
2500                            (offset_x + ufac + UI_UNIT_X * (2 - num_digits) * 0.12f),
2501                            (float)ys - UI_UNIT_Y * 0.095f + ufac,
2502                            number_text,
2503                            text_col);
2504   UI_fontstyle_set(fstyle);
2505   GPU_blend(true); /* Roundbox and text drawing disables. */
2506 }
2507
2508 static void outliner_icon_background_colors(float icon_color[4], float icon_border[4])
2509 {
2510   float text[4];
2511   UI_GetThemeColor4fv(TH_TEXT, text);
2512
2513   copy_v3_v3(icon_color, text);
2514   icon_color[3] = 0.4f;
2515   copy_v3_v3(icon_border, text);
2516   icon_border[3] = 0.2f;
2517 }
2518
2519 static void outliner_draw_iconrow_doit(uiBlock *block,
2520                                        TreeElement *te,
2521                                        const uiFontStyle *fstyle,
2522                                        int xmax,
2523                                        int *offsx,
2524                                        int ys,
2525                                        float alpha_fac,
2526                                        const eOLDrawState active,
2527                                        const int num_elements)
2528 {
2529   TreeStoreElem *tselem = TREESTORE(te);
2530
2531   if (active != OL_DRAWSEL_NONE) {
2532     float ufac = UI_UNIT_X / 20.0f;
2533     float icon_color[4], icon_border[4];
2534     outliner_icon_background_colors(icon_color, icon_border);
2535     icon_color[3] *= alpha_fac;
2536     if (active == OL_DRAWSEL_ACTIVE) {
2537       UI_GetThemeColor4fv(TH_EDITED_OBJECT, icon_color);
2538       icon_border[3] = 0.3f;
2539     }
2540     UI_draw_roundbox_corner_set(UI_CNR_ALL);
2541
2542     UI_draw_roundbox_aa(true,
2543                         (float)*offsx,
2544                         (float)ys + ufac,
2545                         (float)*offsx + UI_UNIT_X,
2546                         (float)ys + UI_UNIT_Y - ufac,
2547                         (float)UI_UNIT_Y / 4.0f,
2548                         icon_color);
2549     /* border around it */
2550     UI_draw_roundbox_aa(false,
2551                         (float)*offsx,
2552                         (float)ys + ufac,
2553                         (float)*offsx + UI_UNIT_X,
2554                         (float)ys + UI_UNIT_Y - ufac,
2555                         (float)UI_UNIT_Y / 4.0f,
2556                         icon_border);
2557     GPU_blend(true); /* Roundbox disables. */
2558   }
2559
2560   tselem_draw_icon(block, xmax, (float)*offsx, (float)ys, tselem, te, alpha_fac, false);
2561   te->xs = *offsx;
2562   te->ys = ys;
2563   te->xend = (short)*offsx + UI_UNIT_X;
2564
2565   if (num_elements > 1) {
2566     outliner_draw_iconrow_number(fstyle, *offsx, ys, num_elements);
2567   }
2568   (*offsx) += UI_UNIT_X;
2569 }
2570
2571 /**
2572  * Return the index to use based on the TreeElement ID and object type
2573  *
2574  * We use a continuum of indices until we get to the object data-blocks
2575  * and we then make room for the object types.
2576  */
2577 static int tree_element_id_type_to_index(TreeElement *te)
2578 {
2579   TreeStoreElem *tselem = TREESTORE(te);
2580
2581   const int id_index = tselem->type == 0 ? BKE_idcode_to_index(te->idcode) : INDEX_ID_GR;
2582   if (id_index < INDEX_ID_OB) {
2583     return id_index;
2584   }
2585   else if (id_index == INDEX_ID_OB) {
2586     const Object *ob = (Object *)tselem->id;
2587     return INDEX_ID_OB + ob->type;
2588   }
2589   else {
2590     return id_index + OB_TYPE_MAX;
2591   }
2592 }
2593
2594 typedef struct MergedIconRow {
2595   eOLDrawState active[INDEX_ID_MAX + OB_TYPE_MAX];
2596   int num_elements[INDEX_ID_MAX + OB_TYPE_MAX];
2597   TreeElement *tree_element[INDEX_ID_MAX + OB_TYPE_MAX];
2598 } MergedIconRow;
2599
2600 static void outliner_draw_iconrow(bContext *C,
2601                                   uiBlock *block,
2602                                   const uiFontStyle *fstyle,
2603                                   Scene *scene,
2604                                   ViewLayer *view_layer,
2605                                   SpaceOutliner *soops,
2606                                   ListBase *lb,
2607                                   int level,
2608                                   int xmax,
2609                                   int *offsx,
2610                                   int ys,
2611                                   float alpha_fac,
2612                                   MergedIconRow *merged)
2613 {
2614   eOLDrawState active = OL_DRAWSEL_NONE;
2615   const Object *obact = OBACT(view_layer);
2616
2617   for (TreeElement *te = lb->first; te; te = te->next) {
2618     TreeStoreElem *tselem = TREESTORE(te);
2619
2620     /* object hierarchy always, further constrained on level */
2621     if (level < 1 || (tselem->type == 0 && te->idcode == ID_OB)) {
2622       /* active blocks get white circle */
2623       if (tselem->type == 0) {
2624         if (te->idcode == ID_OB) {
2625           active = (OBACT(view_layer) == (Object *)tselem->id) ? OL_DRAWSEL_NORMAL :
2626                                                                  OL_DRAWSEL_NONE;
2627         }
2628         else if (is_object_data_in_editmode(tselem->id, obact)) {
2629           active = OL_DRAWSEL_ACTIVE;
2630         }
2631         else {
2632           active = tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NONE, false);
2633         }
2634       }
2635       else if (tselem->type == TSE_GP_LAYER) {
2636         bGPDlayer *gpl = te->directdata;
2637         active = (gpl->flag & GP_LAYER_ACTIVE) ? OL_DRAWSEL_ACTIVE : OL_DRAWSEL_NONE;
2638       }
2639       else {
2640         active = tree_element_type_active(
2641             C, scene, view_layer, soops, te, tselem, OL_SETSEL_NONE, false);
2642       }
2643
2644       if (!ELEM(tselem->type, 0, TSE_LAYER_COLLECTION, TSE_R_LAYER, TSE_GP_LAYER)) {
2645         outliner_draw_iconrow_doit(block, te, fstyle, xmax, offsx, ys, alpha_fac, active, 1);
2646       }
2647       else {
2648         const int index = tree_element_id_type_to_index(te);
2649         merged->num_elements[index]++;
2650         if ((merged->tree_element[index] == NULL) || (active > merged->active[index])) {
2651           merged->tree_element[index] = te;
2652         }
2653         merged->active[index] = MAX2(active, merged->active[index]);
2654       }
2655     }
2656
2657     /* this tree element always has same amount of branches, so don't draw */
2658     if (tselem->type != TSE_R_LAYER) {
2659       outliner_draw_iconrow(C,
2660                             block,
2661                             fstyle,
2662                             scene,
2663                             view_layer,
2664                             soops,
2665                             &te->subtree,
2666                             level + 1,
2667                             xmax,
2668                             offsx,
2669                             ys,
2670                             alpha_fac,
2671                             merged);
2672     }
2673   }
2674
2675   if (level == 0) {
2676     for (int i = 0; i < INDEX_ID_MAX; i++) {
2677       const int num_subtypes = (i == INDEX_ID_OB) ? OB_TYPE_MAX : 1;
2678       /* See tree_element_id_type_to_index for the index logic. */
2679       int index_base = i;
2680       if (i > INDEX_ID_OB) {
2681         index_base += OB_TYPE_MAX;
2682       }
2683       for (int j = 0; j < num_subtypes; j++) {
2684         const int index = index_base + j;
2685         if (merged->num_elements[index] != 0) {
2686           outliner_draw_iconrow_doit(block,
2687                                      merged->tree_element[index],
2688                                      fstyle,
2689                                      xmax,
2690                                      offsx,
2691                                      ys,
2692                                      alpha_fac,
2693                                      merged->active[index],
2694                                      merged->num_elements[index]);
2695         }
2696       }
2697     }
2698   }
2699 }
2700
2701 /* closed tree element */
2702 static void outliner_set_coord_tree_element(TreeElement *te, int startx, int starty)
2703 {
2704   TreeElement *ten;
2705
2706   /* closed items may be displayed in row of parent, don't change their coordinate! */
2707   if ((te->flag & TE_ICONROW) == 0) {
2708     /* store coord and continue, we need coordinates for elements outside view too */
2709     te->xs = startx;
2710     te->ys = starty;
2711   }
2712
2713   for (ten = te->subtree.first; ten; ten = ten->next) {
2714     outliner_set_coord_tree_element(ten, startx + UI_UNIT_X, starty);
2715   }
2716 }
2717
2718 static void outliner_draw_tree_element(bContext *C,
2719                                        uiBlock *block,
2720                                        const uiFontStyle *fstyle,
2721                                        Scene *scene,
2722                                        ViewLayer *view_layer,
2723                                        ARegion *ar,
2724                                        SpaceOutliner *soops,
2725                                        TreeElement *te,
2726                                        bool draw_grayed_out,
2727                                        int startx,
2728                                        int *starty,
2729                                        const float restrict_column_width,
2730                                        TreeElement **te_edit)
2731 {
2732   TreeStoreElem *tselem = TREESTORE(te);
2733   float ufac = UI_UNIT_X / 20.0f;
2734   int offsx = 0;
2735   eOLDrawState active = OL_DRAWSEL_NONE;
2736   unsigned char text_color[4];
2737   UI_GetThemeColor4ubv(TH_TEXT, text_color);
2738   float icon_bgcolor[4], icon_border[4];
2739   outliner_icon_background_colors(icon_bgcolor, icon_border);
2740
2741   if (*starty + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && *starty <= ar->v2d.cur.ymax) {
2742     const float alpha_fac = ((te->flag & TE_DISABLED) || (te->flag & TE_CHILD_NOT_IN_COLLECTION) ||
2743                              draw_grayed_out) ?
2744                                 0.5f :
2745                                 1.0f;
2746     int xmax = ar->v2d.cur.xmax;
2747
2748     if ((tselem->flag & TSE_TEXTBUT) && (*te_edit == NULL)) {
2749       *te_edit = te;
2750     }
2751
2752     /* icons can be ui buts, we don't want it to overlap with restrict */
2753     if (restrict_column_width > 0) {
2754       xmax -= restrict_column_width + UI_UNIT_X;
2755     }
2756
2757     GPU_blend(true);
2758
2759     /* colors for active/selected data */
2760     if (tselem->type == 0) {
2761       const Object *obact = OBACT(view_layer);
2762       if (te->idcode == ID_SCE) {
2763         if (tselem->id == (ID *)scene) {
2764           /* active scene */
2765           icon_bgcolor[3] = 0.2f;
2766           active = OL_DRAWSEL_ACTIVE;
2767         }
2768       }
2769       else if (te->idcode == ID_OB) {
2770         Object *ob = (Object *)tselem->id;
2771         Base *base = (te->directdata) ? (Base *)te->directdata :
2772                                         BKE_view_layer_base_find(view_layer, ob);
2773         const bool is_selected = (base != NULL) && ((base->flag & BASE_SELECTED) != 0);
2774
2775         if (ob == obact) {
2776           active = OL_DRAWSEL_ACTIVE;
2777         }
2778
2779         if (is_selected) {
2780           if (ob == obact) {
2781             /* active selected object */
2782             UI_GetThemeColor3ubv(TH_ACTIVE_OBJECT, text_color);
2783             text_color[3] = 255;
2784           }
2785           else {
2786             /* other selected objects */
2787             UI_GetThemeColor3ubv(TH_SELECTED_OBJECT, text_color);
2788             text_color[3] = 255;
2789           }
2790         }
2791       }
2792       else if (is_object_data_in_editmode(tselem->id, obact)) {
2793         /* objects being edited */
2794         UI_GetThemeColor4fv(TH_EDITED_OBJECT, icon_bgcolor);
2795         icon_border[3] = 0.3f;
2796         active = OL_DRAWSEL_ACTIVE;
2797       }
2798       else {
2799         if (tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NONE, false)) {
2800           /* active items like camera or material */
2801           icon_bgcolor[3] = 0.2f;
2802           active = OL_DRAWSEL_ACTIVE;
2803         }
2804       }
2805     }
2806     else if (tselem->type == TSE_GP_LAYER) {
2807       /* Active grease pencil layer. */
2808       if (((bGPDlayer *)te->directdata)->flag & GP_LAYER_ACTIVE) {
2809         icon_bgcolor[3] = 0.2f;
2810         active = OL_DRAWSEL_ACTIVE;
2811       }
2812     }
2813     else {
2814       active = tree_element_type_active(
2815           C, scene, view_layer, soops, te, tselem, OL_SETSEL_NONE, false);
2816       /* active collection*/
2817       icon_bgcolor[3] = 0.2f;
2818     }
2819
2820     /* Checkbox to enable collections. */
2821     if ((tselem->type == TSE_LAYER_COLLECTION) &&
2822         (soops->show_restrict_flags & SO_RESTRICT_ENABLE)) {
2823       tselem_draw_layer_collection_enable_icon(
2824           scene, block, xmax, (float)startx + offsx + UI_UNIT_X, (float)*starty, te, 0.8f);
2825       offsx += UI_UNIT_X;
2826     }
2827
2828     /* active circle */
2829     if (active != OL_DRAWSEL_NONE) {
2830       UI_draw_roundbox_corner_set(UI_CNR_ALL);
2831       UI_draw_roundbox_aa(true,
2832                           (float)startx + offsx + UI_UNIT_X,
2833                           (float)*starty + ufac,
2834                           (float)startx + offsx + 2.0f * UI_UNIT_X,
2835                           (float)*starty + UI_UNIT_Y - ufac,
2836                           UI_UNIT_Y / 4.0f,
2837                           icon_bgcolor);
2838       /* border around it */
2839       UI_draw_roundbox_aa(false,
2840                           (float)startx + offsx + UI_UNIT_X,
2841                           (float)*starty + ufac,
2842                           (float)startx + offsx + 2.0f * UI_UNIT_X,
2843                           (float)*starty + UI_UNIT_Y - ufac,
2844                           UI_UNIT_Y / 4.0f,
2845                           icon_border);
2846       GPU_blend(true); /* roundbox disables it */
2847
2848       te->flag |= TE_ACTIVE;  // for lookup in display hierarchies
2849     }
2850
2851     if (tselem->type == TSE_VIEW_COLLECTION_BASE) {
2852       /* Scene collection in view layer can't expand/collapse. */
2853     }
2854     else if (te->subtree.first || (tselem->type == 0 && te->idcode == ID_SCE) ||
2855              (te->flag & TE_LAZY_CLOSED)) {
2856       /* open/close icon, only when sublevels, except for scene */
2857       int icon_x = startx;
2858
2859       // icons a bit higher
2860       if (TSELEM_OPEN(tselem, soops)) {
2861         UI_icon_draw_alpha((float)icon_x + 2 * ufac,
2862                            (float)*starty + 1 * ufac,
2863                            ICON_DISCLOSURE_TRI_DOWN,
2864                            alpha_fac);
2865       }
2866       else {
2867         UI_icon_draw_alpha((float)icon_x + 2 * ufac,
2868                            (float)*starty + 1 * ufac,
2869                            ICON_DISCLOSURE_TRI_RIGHT,
2870                            alpha_fac);
2871       }
2872     }
2873     offsx += UI_UNIT_X;
2874
2875     /* datatype icon */
2876     if (!(ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE))) {
2877       tselem_draw_icon(
2878           block, xmax, (float)startx + offsx, (float)*starty, tselem, te, alpha_fac, true);
2879       offsx += UI_UNIT_X + 4 * ufac;
2880     }
2881     else {
2882       offsx += 2 * ufac;
2883     }
2884
2885     if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION) && ID_IS_LINKED(tselem->id)) {
2886       if (tselem->id->tag & LIB_TAG_MISSING) {
2887         UI_icon_draw_alpha((float)startx + offsx + 2 * ufac,
2888                            (float)*starty + 2 * ufac,
2889                            ICON_LIBRARY_DATA_BROKEN,
2890                            alpha_fac);
2891       }
2892       else if (tselem->id->tag & LIB_TAG_INDIRECT) {
2893         UI_icon_draw_alpha((float)startx + offsx + 2 * ufac,
2894                            (float)*starty + 2 * ufac,
2895                            ICON_LIBRARY_DATA_INDIRECT,
2896                            alpha_fac);
2897       }
2898       else {
2899         UI_icon_draw_alpha((float)startx + offsx + 2 * ufac,
2900                            (float)*starty + 2 * ufac,
2901                            ICON_LIBRARY_DATA_DIRECT,
2902                            alpha_fac);
2903       }
2904       offsx += UI_UNIT_X + 4 * ufac;
2905     }
2906     else if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION) && ID_IS_OVERRIDE_LIBRARY(tselem->id)) {
2907       UI_icon_draw_alpha((float)startx + offsx + 2 * ufac,
2908                          (float)*starty + 2 * ufac,
2909                          ICON_LIBRARY_DATA_OVERRIDE,
2910                          alpha_fac);
2911       offsx += UI_UNIT_X + 4 * ufac;
2912     }
2913     GPU_blend(false);
2914
2915     /* name */
2916     if ((tselem->flag & TSE_TEXTBUT) == 0) {
2917       if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) {
2918         UI_GetThemeColorBlend3ubv(TH_BACK, TH_TEXT, 0.75f, text_color);
2919         text_color[3] = 255;
2920       }
2921       text_color[3] *= alpha_fac;
2922       UI_fontstyle_draw_simple(fstyle, startx + offsx, *starty + 5 * ufac, te->name, text_color);
2923     }
2924
2925     offsx += (int)(UI_UNIT_X + UI_fontstyle_string_width(fstyle, te->name));
2926
2927     /* closed item, we draw the icons, not when it's a scene, or master-server list though */
2928     if (!TSELEM_OPEN(tselem, soops)) {
2929       if (te->subtree.first) {
2930         if (tselem->type == 0 && te->idcode == ID_SCE) {
2931           /* pass */
2932         }
2933         /* this tree element always has same amount of branches, so don't draw */
2934         else if (tselem->type != TSE_R_LAYER) {
2935           int tempx = startx + offsx;
2936
2937           GPU_blend(true);
2938
2939           MergedIconRow merged = {{0}};
2940           outliner_draw_iconrow(C,
2941                                 block,
2942                                 fstyle,
2943                                 scene,
2944                                 view_layer,
2945                                 soops,
2946                                 &te->subtree,
2947                                 0,
2948                                 xmax,
2949                                 &tempx,
2950                                 *starty,
2951                                 alpha_fac,
2952                                 &merged);
2953
2954           GPU_blend(false);
2955         }
2956       }
2957     }
2958   }
2959   /* store coord and continue, we need coordinates for elements outside view too */
2960   te->xs = startx;
2961   te->ys = *starty;
2962   te->xend = startx + offsx;
2963
2964   if (TSELEM_OPEN(tselem, soops)) {
2965     *starty -= UI_UNIT_Y;
2966
2967     for (TreeElement *ten = te->subtree.first; ten; ten = ten->next) {
2968       /* check if element needs to be drawn grayed out, but also gray out
2969        * childs of a grayed out parent (pass on draw_grayed_out to childs) */
2970       bool draw_childs_grayed_out = draw_grayed_out || (ten->flag & TE_DRAGGING);
2971       outliner_draw_tree_element(C,
2972                                  block,
2973                                  fstyle,
2974                                  scene,
2975                                  view_layer,
2976                                  ar,
2977                                  soops,
2978                                  ten,
2979                                  draw_childs_grayed_out,
2980                                  startx + UI_UNIT_X,
2981                                  starty,
2982                                  restrict_column_width,
2983                                  te_edit);
2984     }
2985   }
2986   else {
2987     for (TreeElement *ten = te->subtree.first; ten; ten = ten->next) {
2988       outliner_set_coord_tree_element(ten, startx, *starty);
2989     }
2990
2991     *starty -= UI_UNIT_Y;
2992   }
2993 }
2994
2995 static void outliner_draw_hierarchy_lines_recursive(unsigned pos,
2996                                                     SpaceOutliner *soops,
2997                                                     ListBase *lb,
2998                                                     int startx,
2999                                                     const unsigned char col[4],
3000                                                     bool draw_grayed_out,
3001                                                     int *starty)
3002 {
3003   TreeElement *te, *te_vertical_line_last = NULL, *te_vertical_line_last_dashed = NULL;
3004   int y1, y2, y1_dashed, y2_dashed;
3005
3006   if (BLI_listbase_is_empty(lb)) {
3007     return;
3008   }
3009
3010   struct {
3011     int steps_num;
3012     int step_len;
3013     int gap_len;
3014   } dash = {
3015       .steps_num = 4,
3016   };
3017
3018   dash.step_len = UI_UNIT_X / dash.steps_num;
3019   dash.gap_len = dash.step_len / 2;
3020
3021   const unsigned char grayed_alpha = col[3] / 2;
3022
3023   /* For vertical lines between objects. */
3024   y1 = y2 = y1_dashed = y2_dashed = *starty;
3025   for (te = lb->first; te; te = te->next) {
3026     bool draw_childs_grayed_out = draw_grayed_out || (te->flag & TE_DRAGGING);
3027     TreeStoreElem *tselem = TREESTORE(te);
3028
3029     if (draw_childs_grayed_out) {
3030       immUniformColor3ubvAlpha(col, grayed_alpha);
3031     }
3032     else {
3033       immUniformColor4ubv(col);
3034     }
3035
3036     if ((te->flag & TE_CHILD_NOT_IN_COLLECTION) == 0) {
3037       /* Horizontal Line? */
3038       if (tselem->type == 0 && (te->idcode == ID_OB || te->idcode == ID_SCE)) {
3039         immRecti(pos, startx, *starty, startx + UI_UNIT_X, *starty - U.pixelsize);
3040
3041         /* Vertical Line? */
3042         if (te->idcode == ID_OB) {
3043           te_vertical_line_last = te;
3044           y2 = *starty;
3045         }
3046         y1_dashed = *starty - UI_UNIT_Y;
3047       }
3048     }
3049     else {
3050       BLI_assert(te->idcode == ID_OB);
3051       /* Horizontal line - dashed. */
3052       int start = startx;
3053       for (int i = 0; i < dash.steps_num; i++) {
3054         immRecti(pos, start, *starty, start + dash.step_len - dash.gap_len, *starty - U.pixelsize);
3055         start += dash.step_len;
3056       }
3057
3058       te_vertical_line_last_dashed = te;
3059       y2_dashed = *starty;
3060     }
3061
3062     *starty -= UI_UNIT_Y;
3063
3064     if (TSELEM_OPEN(tselem, soops)) {
3065       outliner_draw_hierarchy_lines_recursive(
3066           pos, soops, &te->subtree, startx + UI_UNIT_X, col, draw_childs_grayed_out, starty);
3067     }
3068   }
3069
3070   if (draw_grayed_out) {
3071     immUniformColor3ubvAlpha(col, grayed_alpha);
3072   }
3073   else {
3074     immUniformColor4ubv(col);
3075   }
3076
3077   /* Vertical line. */
3078   te = te_vertical_line_last;
3079   if ((te != NULL) && (te->parent || lb->first != lb->last)) {
3080     immRecti(pos, startx, y1 + UI_UNIT_Y, startx + U.pixelsize, y2);
3081   }
3082
3083   /* Children that are not in the collection are always in the end of the subtree.
3084    * This way we can draw their own dashed vertical lines. */
3085   te = te_vertical_line_last_dashed;
3086   if ((te != NULL) && (te->parent || lb->first != lb->last)) {
3087     const int steps_num = ((y1_dashed + UI_UNIT_Y) - y2_dashed) / dash.step_len;
3088     int start = y1_dashed + UI_UNIT_Y;
3089     for (int i = 0; i < steps_num; i++) {
3090       immRecti(pos, startx, start, startx + U.pixelsize, start - dash.step_len + dash.gap_len);
3091       start -= dash.step_len;
3092     }
3093   }
3094 }
3095
3096 static void outliner_draw_hierarchy_lines(SpaceOutliner *soops,
3097                                           ListBase *lb,
3098                                           int startx,
3099                                           int *starty)
3100 {
3101   GPUVertFormat *format = immVertexFormat();
3102   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
3103   unsigned char col[4];
3104
3105   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3106   UI_GetThemeColorBlend3ubv(TH_BACK, TH_TEXT, 0.4f, col);
3107   col[3] = 255;
3108
3109   GPU_blend(true);
3110   outliner_draw_hierarchy_lines_recursive(pos, soops, lb, startx, col, false, starty);
3111   GPU_blend(false);
3112
3113   immUnbindProgram();
3114 }
3115
3116 static void outliner_draw_struct_marks(ARegion *ar,
3117                                        SpaceOutliner *soops,
3118                                        ListBase *lb,