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