Outliner Tooltips Cleanup: Retrict/Allow > Restrict ; set/unset > set
[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 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 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 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 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 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 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 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 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 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(block,
1231                                 UI_BTYPE_ICON_TOGGLE,
1232                                 GP_LAYER_LOCKED,
1233                                 0,
1234                                 ICON_UNLOCKED,
1235                                 (int)(ar->v2d.cur.xmax - restrict_offsets.select),
1236                                 te->ys,
1237                                 UI_UNIT_X,
1238                                 UI_UNIT_Y,
1239                                 &gpl->flag,
1240                                 0,
1241                                 0,
1242                                 0,
1243                                 0,
1244                                 TIP_("Restrict editing of strokes and keyframes in this layer"));
1245           UI_but_func_set(bt, restrictbutton_gp_layer_flag_cb, id, gpl);
1246           UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1247         }
1248       }
1249       else if (outliner_is_collection_tree_element(te)) {
1250         LayerCollection *layer_collection = (tselem->type == TSE_LAYER_COLLECTION) ?
1251                                                 te->directdata :
1252                                                 NULL;
1253         Collection *collection = outliner_collection_from_tree_element(te);
1254
1255         if ((!layer_collection || !(layer_collection->flag & LAYER_COLLECTION_EXCLUDE)) &&
1256             !(collection->flag & COLLECTION_IS_MASTER)) {
1257           PointerRNA collection_ptr;
1258           RNA_id_pointer_create(&collection->id, &collection_ptr);
1259
1260           if (layer_collection != NULL) {
1261             PointerRNA layer_collection_ptr;
1262             RNA_pointer_create(
1263                 &scene->id, &RNA_LayerCollection, layer_collection, &layer_collection_ptr);
1264
1265             if (soops->show_restrict_flags & SO_RESTRICT_HIDE) {
1266               bt = uiDefIconButR_prop(block,
1267                                       UI_BTYPE_ICON_TOGGLE,
1268                                       0,
1269                                       0,
1270                                       (int)(ar->v2d.cur.xmax - restrict_offsets.hide),
1271                                       te->ys,
1272                                       UI_UNIT_X,
1273                                       UI_UNIT_Y,
1274                                       &layer_collection_ptr,
1275                                       props.layer_collection_hide_viewport,
1276                                       -1,
1277                                       0,
1278                                       0,
1279                                       0,
1280                                       0,
1281                                       TIP_("Temporarily hide in viewport\n"
1282                                            "* Ctrl to isolate collection\n"
1283                                            "* Shift to set inside collections and objects"));
1284               UI_but_func_set(bt,
1285                               view_layer__layer_collection_set_flag_recursive_cb,
1286                               layer_collection,
1287                               (char *)"hide_viewport");
1288               UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1289             }
1290
1291             if (soops->show_restrict_flags & SO_RESTRICT_HOLDOUT) {
1292               bt = uiDefIconButR_prop(block,
1293                                       UI_BTYPE_ICON_TOGGLE,
1294                                       0,
1295                                       0,
1296                                       (int)(ar->v2d.cur.xmax - restrict_offsets.holdout),
1297                                       te->ys,
1298                                       UI_UNIT_X,
1299                                       UI_UNIT_Y,
1300                                       &layer_collection_ptr,
1301                                       props.layer_collection_holdout,
1302                                       -1,
1303                                       0,
1304                                       0,
1305                                       0,
1306                                       0,
1307                                       TIP_("Mask out objects in collection from view layer\n"
1308                                            "* Ctrl to isolate collection\n"
1309                                            "* Shift to set inside collections"));
1310               UI_but_func_set(bt,
1311                               view_layer__layer_collection_set_flag_recursive_cb,
1312                               layer_collection,
1313                               (char *)"holdout");
1314               UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1315             }
1316
1317             if (soops->show_restrict_flags & SO_RESTRICT_INDIRECT_ONLY) {
1318               bt = uiDefIconButR_prop(
1319                   block,
1320                   UI_BTYPE_ICON_TOGGLE,
1321                   0,
1322                   (layer_collection->flag & LAYER_COLLECTION_INDIRECT_ONLY) != 0 ? 0 : ICON_REMOVE,
1323                   (int)(ar->v2d.cur.xmax - restrict_offsets.indirect_only),
1324                   te->ys,
1325                   UI_UNIT_X,
1326                   UI_UNIT_Y,
1327                   &layer_collection_ptr,
1328                   props.layer_collection_indirect_only,
1329                   -1,
1330                   0,
1331                   0,
1332                   0,
1333                   0,
1334                   TIP_("Objects in collection only contribute indirectly (through shadows and "
1335                        "reflections) in the view layer\n"
1336                        "* Ctrl to isolate collection\n"
1337                        "* Shift to set inside collections"));
1338               UI_but_func_set(bt,
1339                               view_layer__layer_collection_set_flag_recursive_cb,
1340                               layer_collection,
1341                               (char *)"indirect_only");
1342               UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1343             }
1344           }
1345
1346           if (soops->show_restrict_flags & SO_RESTRICT_VIEWPORT) {
1347             bt = uiDefIconButR_prop(block,
1348                                     UI_BTYPE_ICON_TOGGLE,
1349                                     0,
1350                                     0,
1351                                     (int)(ar->v2d.cur.xmax - restrict_offsets.viewport),
1352                                     te->ys,
1353                                     UI_UNIT_X,
1354                                     UI_UNIT_Y,
1355                                     &collection_ptr,
1356                                     props.collection_hide_viewport,
1357                                     -1,
1358                                     0,
1359                                     0,
1360                                     0,
1361                                     0,
1362                                     TIP_("Globally disable in viewports\n"
1363                                          "* Ctrl to isolate collection\n"
1364                                          "* Shift to set inside collections and objects"));
1365             if (layer_collection != NULL) {
1366               UI_but_func_set(bt,
1367                               view_layer__collection_set_flag_recursive_cb,
1368                               layer_collection,
1369                               (char *)"hide_viewport");
1370             }
1371             else {
1372               UI_but_func_set(bt,
1373                               scenes__collection_set_flag_recursive_cb,
1374                               collection,
1375                               (char *)"hide_viewport");
1376             }
1377             UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1378           }
1379
1380           if (soops->show_restrict_flags & SO_RESTRICT_RENDER) {
1381             bt = uiDefIconButR_prop(block,
1382                                     UI_BTYPE_ICON_TOGGLE,
1383                                     0,
1384                                     0,
1385                                     (int)(ar->v2d.cur.xmax - restrict_offsets.render),
1386                                     te->ys,
1387                                     UI_UNIT_X,
1388                                     UI_UNIT_Y,
1389                                     &collection_ptr,
1390                                     props.collection_hide_render,
1391                                     -1,
1392                                     0,
1393                                     0,
1394                                     0,
1395                                     0,
1396                                     TIP_("Globally disable in renders\n"
1397                                          "* Ctrl to isolate collection\n"
1398                                          "* Shift to set inside collections and objects"));
1399             if (layer_collection != NULL) {
1400               UI_but_func_set(bt,
1401                               view_layer__collection_set_flag_recursive_cb,
1402                               layer_collection,
1403                               (char *)"hide_render");
1404             }
1405             else {
1406               UI_but_func_set(
1407                   bt, scenes__collection_set_flag_recursive_cb, collection, (char *)"hide_render");
1408             }
1409             UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1410           }
1411
1412           if (soops->show_restrict_flags & SO_RESTRICT_SELECT) {
1413             bt = uiDefIconButR_prop(block,
1414                                     UI_BTYPE_ICON_TOGGLE,
1415                                     0,
1416                                     0,
1417                                     (int)(ar->v2d.cur.xmax - restrict_offsets.select),
1418                                     te->ys,
1419                                     UI_UNIT_X,
1420                                     UI_UNIT_Y,
1421                                     &collection_ptr,
1422                                     props.collection_hide_select,
1423                                     -1,
1424                                     0,
1425                                     0,
1426                                     0,
1427                                     0,
1428                                     TIP_("Disable selection in viewport\n"
1429                                          "* Ctrl to isolate collection\n"
1430                                          "* Shift to set inside collections and objects"));
1431             if (layer_collection != NULL) {
1432               UI_but_func_set(bt,
1433                               view_layer__collection_set_flag_recursive_cb,
1434                               layer_collection,
1435                               (char *)"hide_select");
1436             }
1437             else {
1438               UI_but_func_set(
1439                   bt, scenes__collection_set_flag_recursive_cb, collection, (char *)"hide_select");
1440             }
1441             UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
1442           }
1443         }
1444       }
1445     }
1446
1447     if (TSELEM_OPEN(tselem, soops)) {
1448       outliner_draw_restrictbuts(block, scene, view_layer, ar, soops, &te->subtree);
1449     }
1450   }
1451 }
1452
1453 static void outliner_draw_userbuts(uiBlock *block, ARegion *ar, SpaceOutliner *soops, ListBase *lb)
1454 {
1455
1456   for (TreeElement *te = lb->first; te; te = te->next) {
1457     TreeStoreElem *tselem = TREESTORE(te);
1458     if (te->ys + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && te->ys <= ar->v2d.cur.ymax) {
1459       if (tselem->type == 0) {
1460         uiBut *bt;
1461         ID *id = tselem->id;
1462         const char *tip = NULL;
1463         int icon = ICON_NONE;
1464         char buf[16] = "";
1465         int but_flag = UI_BUT_DRAG_LOCK;
1466
1467         if (ID_IS_LINKED(id)) {
1468           but_flag |= UI_BUT_DISABLED;
1469         }
1470
1471         BLI_str_format_int_grouped(buf, id->us);
1472         bt = uiDefBut(block,
1473                       UI_BTYPE_BUT,
1474                       1,
1475                       buf,
1476                       (int)(ar->v2d.cur.xmax - OL_TOG_USER_BUTS_USERS),
1477                       te->ys,
1478                       UI_UNIT_X,
1479                       UI_UNIT_Y,
1480                       NULL,
1481                       0.0,
1482                       0.0,
1483                       0,
1484                       0,
1485                       TIP_("Number of users of this data-block"));
1486         UI_but_flag_enable(bt, but_flag);
1487
1488         if (id->flag & LIB_FAKEUSER) {
1489           icon = ICON_FILE_TICK;
1490           tip = TIP_("Data-block will be retained using a fake user");
1491         }
1492         else {
1493           icon = ICON_X;
1494           tip = TIP_("Data-block has no users and will be deleted");
1495         }
1496         bt = uiDefIconButBitS(block,
1497                               UI_BTYPE_ICON_TOGGLE,
1498                               LIB_FAKEUSER,
1499                               1,
1500                               icon,
1501                               (int)(ar->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS),
1502                               te->ys,
1503                               UI_UNIT_X,
1504                               UI_UNIT_Y,
1505                               &id->flag,
1506                               0,
1507                               0,
1508                               0,
1509                               0,
1510                               tip);
1511         UI_but_func_set(bt, restrictbutton_id_user_toggle, id, NULL);
1512         UI_but_flag_enable(bt, but_flag);
1513
1514         bt = uiDefButBitS(block,
1515                           UI_BTYPE_ICON_TOGGLE,
1516                           LIB_FAKEUSER,
1517                           1,
1518                           (id->flag & LIB_FAKEUSER) ? "F" : " ",
1519                           (int)(ar->v2d.cur.xmax - OL_TOG_USER_BUTS_FAKEUSER),
1520                           te->ys,
1521                           UI_UNIT_X,
1522                           UI_UNIT_Y,
1523                           &id->flag,
1524                           0,
1525                           0,
1526                           0,
1527                           0,
1528                           TIP_("Data-block has a 'fake' user which will keep it in the file "
1529                                "even if nothing else uses it"));
1530         UI_but_func_set(bt, restrictbutton_id_user_toggle, id, NULL);
1531         UI_but_flag_enable(bt, but_flag);
1532       }
1533     }
1534
1535     if (TSELEM_OPEN(tselem, soops)) {
1536       outliner_draw_userbuts(block, ar, soops, &te->subtree);
1537     }
1538   }
1539 }
1540
1541 static void outliner_draw_rnacols(ARegion *ar, int sizex)
1542 {
1543   View2D *v2d = &ar->v2d;
1544
1545   float miny = v2d->cur.ymin;
1546   if (miny < v2d->tot.ymin) {
1547     miny = v2d->tot.ymin;
1548   }
1549
1550   GPU_line_width(1.0f);
1551
1552   uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
1553   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
1554   immUniformThemeColorShadeAlpha(TH_BACK, -15, -200);
1555
1556   immBegin(GPU_PRIM_LINES, 4);
1557
1558   immVertex2f(pos, sizex, v2d->cur.ymax);
1559   immVertex2f(pos, sizex, miny);
1560
1561   immVertex2f(pos, sizex + OL_RNA_COL_SIZEX, v2d->cur.ymax);
1562   immVertex2f(pos, sizex + OL_RNA_COL_SIZEX, miny);
1563
1564   immEnd();
1565
1566   immUnbindProgram();
1567 }
1568
1569 static void outliner_draw_rnabuts(
1570     uiBlock *block, ARegion *ar, SpaceOutliner *soops, int sizex, ListBase *lb)
1571 {
1572   PointerRNA *ptr;
1573   PropertyRNA *prop;
1574
1575   for (TreeElement *te = lb->first; te; te = te->next) {
1576     TreeStoreElem *tselem = TREESTORE(te);
1577     if (te->ys + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && te->ys <= ar->v2d.cur.ymax) {
1578       if (tselem->type == TSE_RNA_PROPERTY) {
1579         ptr = &te->rnaptr;
1580         prop = te->directdata;
1581
1582         if (!TSELEM_OPEN(tselem, soops)) {
1583           if (RNA_property_type(prop) == PROP_POINTER) {
1584             uiBut *but = uiDefAutoButR(block,
1585                                        ptr,
1586                                        prop,
1587                                        -1,
1588                                        "",
1589                                        ICON_NONE,
1590                                        sizex,
1591                                        te->ys,
1592                                        OL_RNA_COL_SIZEX,
1593                                        UI_UNIT_Y - 1);
1594             UI_but_flag_enable(but, UI_BUT_DISABLED);
1595           }
1596           else if (RNA_property_type(prop) == PROP_ENUM) {
1597             uiDefAutoButR(block,
1598                           ptr,
1599                           prop,
1600                           -1,
1601                           NULL,
1602                           ICON_NONE,
1603                           sizex,
1604                           te->ys,
1605                           OL_RNA_COL_SIZEX,
1606                           UI_UNIT_Y - 1);
1607           }
1608           else {
1609             uiDefAutoButR(block,
1610                           ptr,
1611                           prop,
1612                           -1,
1613                           "",
1614                           ICON_NONE,
1615                           sizex,
1616                           te->ys,
1617                           OL_RNA_COL_SIZEX,
1618                           UI_UNIT_Y - 1);
1619           }
1620         }
1621       }
1622       else if (tselem->type == TSE_RNA_ARRAY_ELEM) {
1623         ptr = &te->rnaptr;
1624         prop = te->directdata;
1625
1626         uiDefAutoButR(block,
1627                       ptr,
1628                       prop,
1629                       te->index,
1630                       "",
1631                       ICON_NONE,
1632                       sizex,
1633                       te->ys,
1634                       OL_RNA_COL_SIZEX,
1635                       UI_UNIT_Y - 1);
1636       }
1637     }
1638
1639     if (TSELEM_OPEN(tselem, soops)) {
1640       outliner_draw_rnabuts(block, ar, soops, sizex, &te->subtree);
1641     }
1642   }
1643 }
1644
1645 static void outliner_buttons(const bContext *C, uiBlock *block, ARegion *ar, TreeElement *te)
1646 {
1647   uiBut *bt;
1648   TreeStoreElem *tselem;
1649   int spx, dx, len;
1650
1651   tselem = TREESTORE(te);
1652
1653   BLI_assert(tselem->flag & TSE_TEXTBUT);
1654   /* If we add support to rename Sequence.
1655    * need change this.
1656    */
1657
1658   if (tselem->type == TSE_EBONE) {
1659     len = sizeof(((EditBone *)0)->name);
1660   }
1661   else if (tselem->type == TSE_MODIFIER) {
1662     len = sizeof(((ModifierData *)0)->name);
1663   }
1664   else if (tselem->id && GS(tselem->id->name) == ID_LI) {
1665     len = sizeof(((Library *)0)->name);
1666   }
1667   else {
1668     len = MAX_ID_NAME - 2;
1669   }
1670
1671   spx = te->xs + 1.8f * UI_UNIT_X;
1672   dx = ar->v2d.cur.xmax - (spx + 3.2f * UI_UNIT_X);
1673
1674   bt = uiDefBut(block,
1675                 UI_BTYPE_TEXT,
1676                 OL_NAMEBUTTON,
1677                 "",
1678                 spx,
1679                 te->ys,
1680                 dx,
1681                 UI_UNIT_Y - 1,
1682                 (void *)te->name,
1683                 1.0,
1684                 (float)len,
1685                 0,
1686                 0,
1687                 "");
1688   UI_but_func_rename_set(bt, namebutton_cb, tselem);
1689
1690   /* returns false if button got removed */
1691   if (false == UI_but_active_only(C, ar, block, bt)) {
1692     tselem->flag &= ~TSE_TEXTBUT;
1693
1694     /* bad! (notifier within draw) without this, we don't get a refresh */
1695     WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, NULL);
1696   }
1697 }
1698
1699 /* ****************************************************** */
1700 /* Normal Drawing... */
1701
1702 TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
1703 {
1704   TreeElementIcon data = {0};
1705
1706   if (tselem->type) {
1707     switch (tselem->type) {
1708       case TSE_ANIM_DATA:
1709         data.icon = ICON_ANIM_DATA; /* XXX */
1710         break;
1711       case TSE_NLA:
1712         data.icon = ICON_NLA;
1713         break;
1714       case TSE_NLA_TRACK:
1715         data.icon = ICON_NLA; /* XXX */
1716         break;
1717       case TSE_NLA_ACTION:
1718         data.icon = ICON_ACTION;
1719         break;
1720       case TSE_DRIVER_BASE:
1721         data.icon = ICON_DRIVER;
1722         break;
1723       case TSE_DEFGROUP_BASE:
1724         data.icon = ICON_GROUP_VERTEX;
1725         break;
1726       case TSE_BONE:
1727       case TSE_EBONE:
1728         data.icon = ICON_BONE_DATA;
1729         break;
1730       case TSE_CONSTRAINT_BASE:
1731         data.icon = ICON_CONSTRAINT;
1732         break;
1733       case TSE_MODIFIER_BASE:
1734         data.icon = ICON_MODIFIER_DATA;
1735         break;
1736       case TSE_LINKED_OB:
1737         data.icon = ICON_OBJECT_DATA;
1738         break;
1739       case TSE_LINKED_PSYS:
1740         data.icon = ICON_PARTICLES;
1741         break;
1742       case TSE_MODIFIER: {
1743         Object *ob = (Object *)tselem->id;
1744         if (ob->type != OB_GPENCIL) {
1745           ModifierData *md = BLI_findlink(&ob->modifiers, tselem->nr);
1746           switch ((ModifierType)md->type) {
1747             case eModifierType_Subsurf:
1748               data.icon = ICON_MOD_SUBSURF;
1749               break;
1750             case eModifierType_Armature:
1751               data.icon = ICON_MOD_ARMATURE;
1752               break;
1753             case eModifierType_Lattice:
1754               data.icon = ICON_MOD_LATTICE;
1755               break;
1756             case eModifierType_Curve:
1757               data.icon = ICON_MOD_CURVE;
1758               break;
1759             case eModifierType_Build:
1760               data.icon = ICON_MOD_BUILD;
1761               break;
1762             case eModifierType_Mirror:
1763               data.icon = ICON_MOD_MIRROR;
1764               break;
1765             case eModifierType_Decimate:
1766               data.icon = ICON_MOD_DECIM;
1767               break;
1768             case eModifierType_Wave:
1769               data.icon = ICON_MOD_WAVE;
1770               break;
1771             case eModifierType_Hook:
1772               data.icon = ICON_HOOK;
1773               break;
1774             case eModifierType_Softbody:
1775               data.icon = ICON_MOD_SOFT;
1776               break;
1777             case eModifierType_Boolean:
1778               data.icon = ICON_MOD_BOOLEAN;
1779               break;
1780             case eModifierType_ParticleSystem:
1781               data.icon = ICON_MOD_PARTICLES;
1782               break;
1783             case eModifierType_ParticleInstance:
1784               data.icon = ICON_MOD_PARTICLES;
1785               break;
1786             case eModifierType_EdgeSplit:
1787               data.icon = ICON_MOD_EDGESPLIT;
1788               break;
1789             case eModifierType_Array:
1790               data.icon = ICON_MOD_ARRAY;
1791               break;
1792             case eModifierType_UVProject:
1793             case eModifierType_UVWarp: /* TODO, get own icon */
1794               data.icon = ICON_MOD_UVPROJECT;
1795               break;
1796             case eModifierType_Displace:
1797               data.icon = ICON_MOD_DISPLACE;
1798               break;
1799             case eModifierType_Shrinkwrap:
1800               data.icon = ICON_MOD_SHRINKWRAP;
1801               break;
1802             case eModifierType_Cast:
1803               data.icon = ICON_MOD_CAST;
1804               break;
1805             case eModifierType_MeshDeform:
1806             case eModifierType_SurfaceDeform:
1807               data.icon = ICON_MOD_MESHDEFORM;
1808               break;
1809             case eModifierType_Bevel:
1810               data.icon = ICON_MOD_BEVEL;
1811               break;
1812             case eModifierType_Smooth:
1813             case eModifierType_LaplacianSmooth:
1814             case eModifierType_CorrectiveSmooth:
1815               data.icon = ICON_MOD_SMOOTH;
1816               break;
1817             case eModifierType_SimpleDeform:
1818               data.icon = ICON_MOD_SIMPLEDEFORM;
1819               break;
1820             case eModifierType_Mask:
1821               data.icon = ICON_MOD_MASK;
1822               break;
1823             case eModifierType_Cloth:
1824               data.icon = ICON_MOD_CLOTH;
1825               break;
1826             case eModifierType_Explode:
1827               data.icon = ICON_MOD_EXPLODE;
1828               break;
1829             case eModifierType_Collision:
1830             case eModifierType_Surface:
1831               data.icon = ICON_MOD_PHYSICS;
1832               break;
1833             case eModifierType_Fluidsim:
1834               data.icon = ICON_MOD_FLUIDSIM;
1835               break;
1836             case eModifierType_Multires:
1837               data.icon = ICON_MOD_MULTIRES;
1838               break;
1839             case eModifierType_Smoke:
1840               data.icon = ICON_MOD_SMOKE;
1841               break;
1842             case eModifierType_Solidify:
1843               data.icon = ICON_MOD_SOLIDIFY;
1844               break;
1845             case eModifierType_Screw:
1846               data.icon = ICON_MOD_SCREW;
1847               break;
1848             case eModifierType_Remesh:
1849               data.icon = ICON_MOD_REMESH;
1850               break;
1851             case eModifierType_WeightVGEdit:
1852             case eModifierType_WeightVGMix:
1853             case eModifierType_WeightVGProximity:
1854               data.icon = ICON_MOD_VERTEX_WEIGHT;
1855               break;
1856             case eModifierType_DynamicPaint:
1857               data.icon = ICON_MOD_DYNAMICPAINT;
1858               break;
1859             case eModifierType_Ocean:
1860               data.icon = ICON_MOD_OCEAN;
1861               break;
1862             case eModifierType_Warp:
1863               data.icon = ICON_MOD_WARP;
1864               break;
1865             case eModifierType_Skin:
1866               data.icon = ICON_MOD_SKIN;
1867               break;
1868             case eModifierType_Triangulate:
1869               data.icon = ICON_MOD_TRIANGULATE;
1870               break;
1871             case eModifierType_MeshCache:
1872               data.icon = ICON_MOD_MESHDEFORM; /* XXX, needs own icon */
1873               break;
1874             case eModifierType_MeshSequenceCache:
1875               data.icon = ICON_MOD_MESHDEFORM; /* XXX, needs own icon */
1876               break;
1877             case eModifierType_Wireframe:
1878               data.icon = ICON_MOD_WIREFRAME;
1879               break;
1880             case eModifierType_LaplacianDeform:
1881               data.icon = ICON_MOD_MESHDEFORM; /* XXX, needs own icon */
1882               break;
1883             case eModifierType_DataTransfer:
1884               data.icon = ICON_MOD_DATA_TRANSFER;
1885               break;
1886             case eModifierType_NormalEdit:
1887             case eModifierType_WeightedNormal:
1888               data.icon = ICON_MOD_NORMALEDIT;
1889               break;
1890               /* Default */
1891             case eModifierType_None:
1892             case eModifierType_ShapeKey:
1893
1894             case NUM_MODIFIER_TYPES:
1895               data.icon = ICON_DOT;
1896               break;
1897           }
1898         }
1899         else {
1900           /* grease pencil modifiers */
1901           GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, tselem->nr);
1902           switch ((GpencilModifierType)md->type) {
1903             case eGpencilModifierType_Noise:
1904               data.icon = ICON_RNDCURVE;
1905               break;
1906             case eGpencilModifierType_Subdiv:
1907               data.icon = ICON_MOD_SUBSURF;
1908               break;
1909             case eGpencilModifierType_Thick:
1910               data.icon = ICON_MOD_THICKNESS;
1911               break;
1912             case eGpencilModifierType_Tint:
1913               data.icon = ICON_MOD_TINT;
1914               break;
1915             case eGpencilModifierType_Array:
1916               data.icon = ICON_MOD_ARRAY;
1917               break;
1918             case eGpencilModifierType_Build:
1919               data.icon = ICON_MOD_BUILD;
1920               break;
1921             case eGpencilModifierType_Opacity:
1922               data.icon = ICON_MOD_MASK;
1923               break;
1924             case eGpencilModifierType_Color:
1925               data.icon = ICON_MOD_HUE_SATURATION;
1926               break;
1927             case eGpencilModifierType_Lattice:
1928               data.icon = ICON_MOD_LATTICE;
1929               break;
1930             case eGpencilModifierType_Mirror:
1931               data.icon = ICON_MOD_MIRROR;
1932               break;
1933             case eGpencilModifierType_Simplify:
1934               data.icon = ICON_MOD_SIMPLIFY;
1935               break;
1936             case eGpencilModifierType_Smooth:
1937               data.icon = ICON_MOD_SMOOTH;
1938               break;
1939             case eGpencilModifierType_Hook:
1940               data.icon = ICON_HOOK;
1941               break;
1942             case eGpencilModifierType_Offset:
1943               data.icon = ICON_MOD_OFFSET;
1944               break;
1945             case eGpencilModifierType_Armature:
1946               data.icon = ICON_MOD_ARMATURE;
1947               break;
1948
1949               /* Default */
1950             default:
1951               data.icon = ICON_DOT;
1952               break;
1953           }
1954         }
1955         break;
1956       }
1957       case TSE_POSE_BASE:
1958         data.icon = ICON_ARMATURE_DATA;
1959         break;
1960       case TSE_POSE_CHANNEL:
1961         data.icon = ICON_BONE_DATA;
1962         break;
1963       case TSE_PROXY:
1964         data.icon = ICON_GHOST_ENABLED;
1965         break;
1966       case TSE_R_LAYER_BASE:
1967         data.icon = ICON_RENDERLAYERS;
1968         break;
1969       case TSE_SCENE_OBJECTS_BASE:
1970         data.icon = ICON_OUTLINER_OB_GROUP_INSTANCE;
1971         break;
1972       case TSE_R_LAYER:
1973         data.icon = ICON_RENDER_RESULT;
1974         break;
1975       case TSE_LINKED_LAMP:
1976         data.icon = ICON_LIGHT_DATA;
1977         break;
1978       case TSE_LINKED_MAT:
1979         data.icon = ICON_MATERIAL_DATA;
1980         break;
1981       case TSE_POSEGRP_BASE:
1982         data.icon = ICON_GROUP_BONE;
1983         break;
1984       case TSE_SEQUENCE:
1985         if (te->idcode == SEQ_TYPE_MOVIE) {
1986           data.icon = ICON_SEQUENCE;
1987         }
1988         else if (te->idcode == SEQ_TYPE_META) {
1989           data.icon = ICON_DOT;
1990         }
1991         else if (te->idcode == SEQ_TYPE_SCENE) {
1992           data.icon = ICON_SCENE;
1993         }
1994         else if (te->idcode == SEQ_TYPE_SOUND_RAM) {
1995           data.icon = ICON_SOUND;
1996         }
1997         else if (te->idcode == SEQ_TYPE_IMAGE) {
1998           data.icon = ICON_IMAGE;
1999         }
2000         else {
2001           data.icon = ICON_PARTICLES;
2002         }
2003         break;
2004       case TSE_SEQ_STRIP:
2005         data.icon = ICON_LIBRARY_DATA_DIRECT;
2006         break;
2007       case TSE_SEQUENCE_DUP:
2008         data.icon = ICON_OBJECT_DATA;
2009         break;
2010       case TSE_RNA_STRUCT:
2011         if (RNA_struct_is_ID(te->rnaptr.type)) {
2012           data.drag_id = (ID *)te->rnaptr.data;
2013           data.icon = RNA_struct_ui_icon(te->rnaptr.type);
2014         }
2015         else {
2016           data.icon = RNA_struct_ui_icon(te->rnaptr.type);
2017         }
2018         break;
2019       case TSE_LAYER_COLLECTION:
2020       case TSE_SCENE_COLLECTION_BASE:
2021       case TSE_VIEW_COLLECTION_BASE: {
2022         Collection *collection = outliner_collection_from_tree_element(te);
2023         if (collection && !(collection->flag & COLLECTION_IS_MASTER)) {
2024           data.drag_id = tselem->id;
2025           data.drag_parent = (data.drag_id && te->parent) ? TREESTORE(te->parent)->id : NULL;
2026         }
2027
2028         data.icon = ICON_GROUP;
2029         break;
2030       }
2031       /* Removed the icons from outliner.
2032        * Need a better structure with Layers, Palettes and Colors. */
2033       case TSE_GP_LAYER: {
2034         /* indicate whether layer is active */
2035         bGPDlayer *gpl = te->directdata;
2036         if (gpl->flag & GP_LAYER_ACTIVE) {
2037           data.icon = ICON_GREASEPENCIL;
2038         }
2039         else {
2040           data.icon = ICON_DOT;
2041         }
2042         break;
2043       }
2044       default:
2045         data.icon = ICON_DOT;
2046         break;
2047     }
2048   }
2049   else if (tselem->id) {
2050     data.drag_id = tselem->id;
2051     data.drag_parent = (data.drag_id && te->parent) ? TREESTORE(te->parent)->id : NULL;
2052
2053     if (GS(tselem->id->name) == ID_OB) {
2054       Object *ob = (Object *)tselem->id;
2055       switch (ob->type) {
2056         case OB_LAMP:
2057           data.icon = ICON_OUTLINER_OB_LIGHT;
2058           break;
2059         case OB_MESH:
2060           data.icon = ICON_OUTLINER_OB_MESH;
2061           break;
2062         case OB_CAMERA:
2063           data.icon = ICON_OUTLINER_OB_CAMERA;
2064           break;
2065         case OB_CURVE:
2066           data.icon = ICON_OUTLINER_OB_CURVE;
2067           break;
2068         case OB_MBALL:
2069           data.icon = ICON_OUTLINER_OB_META;
2070           break;
2071         case OB_LATTICE:
2072           data.icon = ICON_OUTLINER_OB_LATTICE;
2073           break;
2074         case OB_ARMATURE:
2075           data.icon = ICON_OUTLINER_OB_ARMATURE;
2076           break;
2077         case OB_FONT:
2078           data.icon = ICON_OUTLINER_OB_FONT;
2079           break;
2080         case OB_SURF:
2081           data.icon = ICON_OUTLINER_OB_SURFACE;
2082           break;
2083         case OB_SPEAKER:
2084           data.icon = ICON_OUTLINER_OB_SPEAKER;
2085           break;
2086         case OB_LIGHTPROBE:
2087           data.icon = ICON_OUTLINER_OB_LIGHTPROBE;
2088           break;
2089         case OB_EMPTY:
2090           if (ob->instance_collection) {
2091             data.icon = ICON_OUTLINER_OB_GROUP_INSTANCE;
2092           }
2093           else if (ob->empty_drawtype == OB_EMPTY_IMAGE) {
2094             data.icon = ICON_OUTLINER_OB_IMAGE;
2095           }
2096           else {
2097             data.icon = ICON_OUTLINER_OB_EMPTY;
2098           }
2099           break;
2100         case OB_GPENCIL:
2101           data.icon = ICON_OUTLINER_OB_GREASEPENCIL;
2102           break;
2103           break;
2104       }
2105     }
2106     else {
2107       /* TODO(sergey): Casting to short here just to handle ID_NLA which is
2108        * NOT inside of IDType enum.
2109        */
2110       switch ((short)GS(tselem->id->name)) {
2111         case ID_SCE:
2112           data.icon = ICON_SCENE_DATA;
2113           break;
2114         case ID_ME:
2115           data.icon = ICON_OUTLINER_DATA_MESH;
2116           break;
2117         case ID_CU:
2118           data.icon = ICON_OUTLINER_DATA_CURVE;
2119           break;
2120         case ID_MB:
2121           data.icon = ICON_OUTLINER_DATA_META;
2122           break;
2123         case ID_LT:
2124           data.icon = ICON_OUTLINER_DATA_LATTICE;
2125           break;
2126         case ID_LA: {
2127           Light *la = (Light *)tselem->id;
2128           switch (la->type) {
2129             case LA_LOCAL:
2130               data.icon = ICON_LIGHT_POINT;
2131               break;
2132             case LA_SUN:
2133               data.icon = ICON_LIGHT_SUN;
2134               break;
2135             case LA_SPOT:
2136               data.icon = ICON_LIGHT_SPOT;
2137               break;
2138             case LA_AREA:
2139               data.icon = ICON_LIGHT_AREA;
2140               break;
2141             default:
2142               data.icon = ICON_OUTLINER_DATA_LIGHT;
2143               break;
2144           }
2145           break;
2146         }
2147         case ID_MA:
2148           data.icon = ICON_MATERIAL_DATA;
2149           break;
2150         case ID_TE:
2151           data.icon = ICON_TEXTURE_DATA;
2152           break;
2153         case ID_IM:
2154           data.icon = ICON_IMAGE_DATA;
2155           break;
2156         case ID_SPK:
2157         case ID_SO:
2158           data.icon = ICON_OUTLINER_DATA_SPEAKER;
2159           break;
2160         case ID_AR:
2161           data.icon = ICON_OUTLINER_DATA_ARMATURE;
2162           break;
2163         case ID_CA:
2164           data.icon = ICON_OUTLINER_DATA_CAMERA;
2165           break;
2166         case ID_KE:
2167           data.icon = ICON_SHAPEKEY_DATA;
2168           break;
2169         case ID_WO:
2170           data.icon = ICON_WORLD_DATA;
2171           break;
2172         case ID_AC:
2173           data.icon = ICON_ACTION;
2174           break;
2175         case ID_NLA:
2176           data.icon = ICON_NLA;
2177           break;
2178         case ID_TXT:
2179           data.icon = ICON_SCRIPT;
2180           break;
2181         case ID_GR:
2182           data.icon = ICON_GROUP;
2183           break;
2184         case ID_LI:
2185           if (tselem->id->tag & LIB_TAG_MISSING) {
2186             data.icon = ICON_LIBRARY_DATA_BROKEN;
2187           }
2188           else if (((Library *)tselem->id)->parent) {
2189             data.icon = ICON_LIBRARY_DATA_INDIRECT;
2190           }
2191           else {
2192             data.icon = ICON_LIBRARY_DATA_DIRECT;
2193           }
2194           break;
2195         case ID_LS:
2196           data.icon = ICON_LINE_DATA;
2197           break;
2198         case ID_GD:
2199           data.icon = ICON_OUTLINER_DATA_GREASEPENCIL;
2200           break;
2201         case ID_LP: {
2202           LightProbe *lp = (LightProbe *)tselem->id;
2203           switch (lp->type) {
2204             case LIGHTPROBE_TYPE_CUBE:
2205               data.icon = ICON_LIGHTPROBE_CUBEMAP;
2206               break;
2207             case LIGHTPROBE_TYPE_PLANAR:
2208               data.icon = ICON_LIGHTPROBE_PLANAR;
2209               break;
2210             case LIGHTPROBE_TYPE_GRID:
2211               data.icon = ICON_LIGHTPROBE_GRID;
2212               break;
2213             default:
2214               data.icon = ICON_LIGHTPROBE_CUBEMAP;
2215               break;
2216           }
2217           break;
2218         }
2219         case ID_BR:
2220           data.icon = ICON_BRUSH_DATA;
2221           break;
2222         case ID_SCR:
2223         case ID_WS:
2224           data.icon = ICON_WORKSPACE;
2225           break;
2226         case ID_MSK:
2227           data.icon = ICON_MOD_MASK;
2228           break;
2229         case ID_MC:
2230           data.icon = ICON_SEQUENCE;
2231           break;
2232         case ID_PC:
2233           data.icon = ICON_CURVE_BEZCURVE;
2234           break;
2235         default:
2236           break;
2237       }
2238     }
2239   }
2240
2241   return data;
2242 }
2243
2244 static void tselem_draw_layer_collection_enable_icon(
2245     Scene *scene, uiBlock *block, int xmax, float x, float y, TreeElement *te, float alpha)
2246 {
2247   /* Get RNA property (once for speed). */
2248   static PropertyRNA *exclude_prop = NULL;
2249   if (exclude_prop == NULL) {
2250     exclude_prop = RNA_struct_type_find_property(&RNA_LayerCollection, "exclude");
2251   }
2252
2253   if (x >= xmax) {
2254     /* Placement of icons, copied from interface_widgets.c. */
2255     float aspect = (0.8f * UI_UNIT_Y) / ICON_DEFAULT_HEIGHT;
2256     x += 2.0f * aspect;
2257     y += 2.0f * aspect;
2258
2259     /* restrict column clip... it has been coded by simply overdrawing,
2260      * doesn't work for buttons */
2261     char color[4];
2262     int icon = RNA_property_ui_icon(exclude_prop);
2263     if (UI_icon_get_theme_color(icon, (uchar *)color)) {
2264       UI_icon_draw_ex(x, y, icon, U.inv_dpi_fac, alpha, 0.0f, color, true);
2265     }
2266     else {
2267       UI_icon_draw_ex(x, y, icon, U.inv_dpi_fac, alpha, 0.0f, NULL, false);
2268     }
2269   }
2270   else {
2271     LayerCollection *layer_collection = te->directdata;
2272     PointerRNA layer_collection_ptr;
2273     RNA_pointer_create(&scene->id, &RNA_LayerCollection, layer_collection, &layer_collection_ptr);
2274
2275     char emboss = UI_block_emboss_get(block);
2276     UI_block_emboss_set(block, UI_EMBOSS_NONE);
2277     uiBut *bt = uiDefIconButR_prop(block,
2278                                    UI_BTYPE_ICON_TOGGLE,
2279                                    0,
2280                                    0,
2281                                    x,
2282                                    y,
2283                                    UI_UNIT_X,
2284                                    UI_UNIT_Y,
2285                                    &layer_collection_ptr,
2286                                    exclude_prop,
2287                                    -1,
2288                                    0,
2289                                    0,
2290                                    0,
2291                                    0,
2292                                    NULL);
2293     UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
2294     UI_block_emboss_set(block, emboss);
2295   }
2296 }
2297
2298 static void tselem_draw_icon(uiBlock *block,
2299                              int xmax,
2300                              float x,
2301                              float y,
2302                              TreeStoreElem *tselem,
2303                              TreeElement *te,
2304                              float alpha,
2305                              const bool is_clickable)
2306 {
2307   TreeElementIcon data = tree_element_get_icon(tselem, te);
2308
2309   if (data.icon == 0) {
2310     return;
2311   }
2312
2313   if (!is_clickable || x >= xmax) {
2314     /* placement of icons, copied from interface_widgets.c */
2315     float aspect = (0.8f * UI_UNIT_Y) / ICON_DEFAULT_HEIGHT;
2316     x += 2.0f * aspect;
2317     y += 2.0f * aspect;
2318
2319     /* restrict column clip... it has been coded by simply overdrawing,
2320      * doesn't work for buttons */
2321     char color[4];
2322     if (UI_icon_get_theme_color(data.icon, (uchar *)color)) {
2323       UI_icon_draw_ex(x, y, data.icon, U.inv_dpi_fac, alpha, 0.0f, color, true);
2324     }
2325     else {
2326       UI_icon_draw_ex(x, y, data.icon, U.inv_dpi_fac, alpha, 0.0f, NULL, false);
2327     }
2328   }
2329   else {
2330     uiDefIconBut(block,
2331                  UI_BTYPE_LABEL,
2332                  0,
2333                  data.icon,
2334                  x,
2335                  y,
2336                  UI_UNIT_X,
2337                  UI_UNIT_Y,
2338                  NULL,
2339                  0.0,
2340                  0.0,
2341                  1.0,
2342                  alpha,
2343                  (data.drag_id && ID_IS_LINKED(data.drag_id)) ? data.drag_id->lib->name : "");
2344   }
2345 }
2346
2347 /**
2348  * For icon-only children of a collapsed tree,
2349  * Draw small number over the icon to show how many items of this type are displayed.
2350  */
2351 static void outliner_draw_iconrow_number(const uiFontStyle *fstyle,
2352                                          int offsx,
2353                                          int ys,
2354                                          const int num_elements)
2355 {
2356   float color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
2357   float ufac = 0.25f * UI_UNIT_X;
2358   float offset_x = (float)offsx + UI_UNIT_X * 0.35f;
2359
2360   UI_draw_roundbox_corner_set(UI_CNR_ALL);
2361   UI_draw_roundbox_aa(true,
2362                       offset_x + ufac,
2363                       (float)ys - UI_UNIT_Y * 0.2f + ufac,
2364                       offset_x + UI_UNIT_X - ufac,
2365                       (float)ys - UI_UNIT_Y * 0.2f + UI_UNIT_Y - ufac,
2366                       (float)UI_UNIT_Y / 2.0f - ufac,
2367                       color);
2368
2369   /* Now the numbers. */
2370   unsigned char text_col[4];
2371
2372   UI_GetThemeColor4ubv(TH_TEXT_HI, text_col);
2373   text_col[3] = 255;
2374
2375   uiFontStyle fstyle_small = *fstyle;
2376   fstyle_small.points *= 0.8f;
2377
2378   /* We treat +99 as 4 digits to make sure the (eyeballed) alignment looks nice. */
2379   int num_digits = 4;
2380   char number_text[4] = "+99\0";
2381   if (num_elements < 100) {
2382     BLI_snprintf(number_text, sizeof(number_text), "%d", num_elements);
2383     num_digits = num_elements < 10 ? 1 : 2;
2384   }
2385   UI_fontstyle_draw_simple(&fstyle_small,
2386                            (offset_x + ufac + UI_UNIT_X * (2 - num_digits) * 0.12f),
2387                            (float)ys - UI_UNIT_Y * 0.095f + ufac,
2388                            number_text,
2389                            text_col);
2390   UI_fontstyle_set(fstyle);
2391   GPU_blend(true); /* Roundbox and text drawing disables. */
2392 }
2393
2394 static void outliner_draw_iconrow_doit(uiBlock *block,
2395                                        TreeElement *te,
2396                                        const uiFontStyle *fstyle,
2397                                        int xmax,
2398                                        int *offsx,
2399                                        int ys,
2400                                        float alpha_fac,
2401                                        const eOLDrawState active,
2402                                        const int num_elements)
2403 {
2404   TreeStoreElem *tselem = TREESTORE(te);
2405
2406   if (active != OL_DRAWSEL_NONE) {
2407     float ufac = UI_UNIT_X / 20.0f;
2408     float color[4] = {1.0f, 1.0f, 1.0f, 0.2f};
2409
2410     UI_draw_roundbox_corner_set(UI_CNR_ALL);
2411     color[3] *= alpha_fac;
2412
2413     UI_draw_roundbox_aa(true,
2414                         (float)*offsx + 1.0f * ufac,
2415                         (float)ys + 1.0f * ufac,
2416                         (float)*offsx + UI_UNIT_X - 1.0f * ufac,
2417                         (float)ys + UI_UNIT_Y - ufac,
2418                         (float)UI_UNIT_Y / 2.0f - ufac,
2419                         color);
2420     GPU_blend(true); /* Roundbox disables. */
2421   }
2422
2423   /* No inlined icon should be clickable. */
2424   tselem_draw_icon(block, xmax, (float)*offsx, (float)ys, tselem, te, 0.8f * alpha_fac, false);
2425   te->xs = *offsx;
2426   te->ys = ys;
2427   te->xend = (short)*offsx + UI_UNIT_X;
2428
2429   if (num_elements > 1) {
2430     outliner_draw_iconrow_number(fstyle, *offsx, ys, num_elements);
2431   }
2432   (*offsx) += UI_UNIT_X;
2433 }
2434
2435 /**
2436  * Return the index to use based on the TreeElement ID and object type
2437  *
2438  * We use a continuum of indices until we get to the object datablocks
2439  * and we then make room for the object types.
2440  */
2441 static int tree_element_id_type_to_index(TreeElement *te)
2442 {
2443   TreeStoreElem *tselem = TREESTORE(te);
2444
2445   const int id_index = tselem->type == 0 ? BKE_idcode_to_index(te->idcode) : INDEX_ID_GR;
2446   if (id_index < INDEX_ID_OB) {
2447     return id_index;
2448   }
2449   else if (id_index == INDEX_ID_OB) {
2450     const Object *ob = (Object *)tselem->id;
2451     return INDEX_ID_OB + ob->type;
2452   }
2453   else {
2454     return id_index + OB_TYPE_MAX;
2455   }
2456 }
2457
2458 typedef struct MergedIconRow {
2459   eOLDrawState active[INDEX_ID_MAX + OB_TYPE_MAX];
2460   int num_elements[INDEX_ID_MAX + OB_TYPE_MAX];
2461   TreeElement *tree_element[INDEX_ID_MAX + OB_TYPE_MAX];
2462 } MergedIconRow;
2463
2464 static void outliner_draw_iconrow(bContext *C,
2465                                   uiBlock *block,
2466                                   const uiFontStyle *fstyle,
2467                                   Scene *scene,
2468                                   ViewLayer *view_layer,
2469                                   SpaceOutliner *soops,
2470                                   ListBase *lb,
2471                                   int level,
2472                                   int xmax,
2473                                   int *offsx,
2474                                   int ys,
2475                                   float alpha_fac,
2476                                   MergedIconRow *merged)
2477 {
2478   eOLDrawState active;
2479   const Object *obact = OBACT(view_layer);
2480
2481   for (TreeElement *te = lb->first; te; te = te->next) {
2482     /* exit drawing early */
2483     if ((*offsx) - UI_UNIT_X > xmax) {
2484       break;
2485     }
2486
2487     TreeStoreElem *tselem = TREESTORE(te);
2488
2489     /* object hierarchy always, further constrained on level */
2490     if (level < 1 || (tselem->type == 0 && te->idcode == ID_OB)) {
2491       /* active blocks get white circle */
2492       if (tselem->type == 0) {
2493         if (te->idcode == ID_OB) {
2494           active = (OBACT(view_layer) == (Object *)tselem->id) ? OL_DRAWSEL_NORMAL :
2495                                                                  OL_DRAWSEL_NONE;
2496         }
2497         else if (is_object_data_in_editmode(tselem->id, obact)) {
2498           active = OL_DRAWSEL_NORMAL;
2499         }
2500         else {
2501           active = tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NONE, false);
2502         }
2503       }
2504       else {
2505         active = tree_element_type_active(
2506             C, scene, view_layer, soops, te, tselem, OL_SETSEL_NONE, false);
2507       }
2508
2509       if (!ELEM(tselem->type, 0, TSE_LAYER_COLLECTION, TSE_R_LAYER)) {
2510         outliner_draw_iconrow_doit(block, te, fstyle, xmax, offsx, ys, alpha_fac, active, 1);
2511       }
2512       else {
2513         const int index = tree_element_id_type_to_index(te);
2514         merged->num_elements[index]++;
2515         if ((merged->tree_element[index] == NULL) || (active > merged->active[index])) {
2516           merged->tree_element[index] = te;
2517         }
2518         merged->active[index] = MAX2(active, merged->active[index]);
2519       }
2520     }
2521
2522     /* this tree element always has same amount of branches, so don't draw */
2523     if (tselem->type != TSE_R_LAYER) {
2524       outliner_draw_iconrow(C,
2525                             block,
2526                             fstyle,
2527                             scene,
2528                             view_layer,
2529                             soops,
2530                             &te->subtree,
2531                             level + 1,
2532                             xmax,
2533                             offsx,
2534                             ys,
2535                             alpha_fac,
2536                             merged);
2537     }
2538   }
2539
2540   if (level == 0) {
2541     for (int i = 0; i < INDEX_ID_MAX; i++) {
2542       const int num_subtypes = (i == INDEX_ID_OB) ? OB_TYPE_MAX : 1;
2543       /* See tree_element_id_type_to_index for the index logic. */
2544       int index_base = i;
2545       if (i > INDEX_ID_OB) {
2546         index_base += OB_TYPE_MAX;
2547       }
2548       for (int j = 0; j < num_subtypes; j++) {
2549         const int index = index_base + j;
2550         if (merged->num_elements[index] != 0) {
2551           outliner_draw_iconrow_doit(block,
2552                                      merged->tree_element[index],
2553                                      fstyle,
2554                                      xmax,
2555                                      offsx,
2556                                      ys,
2557                                      alpha_fac,
2558                                      merged->active[index],
2559                                      merged->num_elements[index]);
2560         }
2561       }
2562     }
2563   }
2564 }
2565
2566 /* closed tree element */
2567 static void outliner_set_coord_tree_element(TreeElement *te, int startx, int starty)
2568 {
2569   TreeElement *ten;
2570
2571   /* closed items may be displayed in row of parent, don't change their coordinate! */
2572   if ((te->flag & TE_ICONROW) == 0) {
2573     /* store coord and continue, we need coordinates for elements outside view too */
2574     te->xs = startx;
2575     te->ys = starty;
2576   }
2577
2578   for (ten = te->subtree.first; ten; ten = ten->next) {
2579     outliner_set_coord_tree_element(ten, startx + UI_UNIT_X, starty);
2580   }
2581 }
2582
2583 static void outliner_draw_tree_element(bContext *C,
2584                                        uiBlock *block,
2585                                        const uiFontStyle *fstyle,
2586                                        Scene *scene,
2587                                        ViewLayer *view_layer,
2588                                        ARegion *ar,
2589                                        SpaceOutliner *soops,
2590                                        TreeElement *te,
2591                                        bool draw_grayed_out,
2592                                        int startx,
2593                                        int *starty,
2594                                        const float restrict_column_width,
2595                                        TreeElement **te_edit)
2596 {
2597   TreeStoreElem *tselem;
2598   float ufac = UI_UNIT_X / 20.0f;
2599   int offsx = 0;
2600   eOLDrawState active = OL_DRAWSEL_NONE;
2601   float color[4];
2602   tselem = TREESTORE(te);
2603
2604   if (*starty + 2 * UI_UNIT_Y >= ar->v2d.cur.ymin && *starty <= ar->v2d.cur.ymax) {
2605     const float alpha_fac = ((te->flag & TE_DISABLED) || (te->flag & TE_CHILD_NOT_IN_COLLECTION) ||
2606                              draw_grayed_out) ?
2607                                 0.5f :
2608                                 1.0f;
2609     const float alpha = 0.5f * alpha_fac;
2610     int xmax = ar->v2d.cur.xmax;
2611
2612     if ((tselem->flag & TSE_TEXTBUT) && (*te_edit == NULL)) {
2613       *te_edit = te;
2614     }
2615
2616     /* icons can be ui buts, we don't want it to overlap with restrict */
2617     if (restrict_column_width > 0) {
2618       xmax -= restrict_column_width + UI_UNIT_X;
2619     }
2620
2621     GPU_blend(true);
2622
2623     /* colors for active/selected data */
2624     if (tselem->type == 0) {
2625       const Object *obact = OBACT(view_layer);
2626       if (te->idcode == ID_SCE) {
2627         if (tselem->id == (ID *)scene) {
2628           rgba_float_args_set(color, 1.0f, 1.0f, 1.0f, alpha);
2629           active = OL_DRAWSEL_ACTIVE;
2630         }
2631       }
2632       else if (te->idcode == ID_OB) {
2633         Object *ob = (Object *)tselem->id;
2634         Base *base = (te->directdata) ? (Base *)te->directdata :
2635                                         BKE_view_layer_base_find(view_layer, ob);
2636         const bool is_selected = (base != NULL) && ((base->flag & BASE_SELECTED) != 0);
2637
2638         if (ob == obact || is_selected) {
2639           uchar col[4] = {0, 0, 0, 0};
2640
2641           /* outliner active ob: always white text, circle color now similar to view3d */
2642
2643           active = OL_DRAWSEL_ACTIVE;
2644           if (ob == obact) {
2645             if (is_selected) {
2646               UI_GetThemeColorType4ubv(TH_ACTIVE, SPACE_VIEW3D, col);
2647               col[3] = alpha;
2648             }
2649
2650             active = OL_DRAWSEL_NORMAL;
2651           }
2652           else if (is_selected) {
2653             UI_GetThemeColorType4ubv(TH_SELECT, SPACE_VIEW3D, col);
2654             col[3] = alpha;
2655           }
2656           rgba_float_args_set(
2657               color, (float)col[0] / 255, (float)col[1] / 255, (float)col[2] / 255, alpha);
2658         }
2659       }
2660       else if (is_object_data_in_editmode(tselem->id, obact)) {
2661         rgba_float_args_set(color, 1.0f, 1.0f, 1.0f, alpha);
2662         active = OL_DRAWSEL_ACTIVE;
2663       }
2664       else {
2665         if (tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NONE, false)) {
2666           rgba_float_args_set(color, 0.85f, 0.85f, 1.0f, alpha);
2667           active = OL_DRAWSEL_ACTIVE;
2668         }
2669       }
2670     }
2671     else {
2672       active = tree_element_type_active(
2673           C, scene, view_layer, soops, te, tselem, OL_SETSEL_NONE, false);
2674       rgba_float_args_set(color, 0.85f, 0.85f, 1.0f, alpha);
2675     }
2676
2677     /* Checkbox to enable collections. */
2678     if ((tselem->type == TSE_LAYER_COLLECTION) &&
2679         (soops->show_restrict_flags & SO_RESTRICT_ENABLE)) {
2680       tselem_draw_layer_collection_enable_icon(
2681           scene, block, xmax, (float)startx + offsx + UI_UNIT_X, (float)*starty, te, 0.8f);
2682       offsx += UI_UNIT_X;
2683     }
2684
2685     /* active circle */
2686     if (active != OL_DRAWSEL_NONE) {
2687       UI_draw_roundbox_corner_set(UI_CNR_ALL);
2688       UI_draw_roundbox_aa(true,
2689                           (float)startx + offsx + UI_UNIT_X + 1.0f * ufac,
2690                           (float)*starty + 1.0f * ufac,
2691                           (float)startx + offsx + 2.0f * UI_UNIT_X - 1.0f * ufac,
2692                           (float)*starty + UI_UNIT_Y - 1.0f * ufac,
2693                           UI_UNIT_Y / 2.0f - 1.0f * ufac,
2694                           color);
2695       GPU_blend(true); /* roundbox disables it */
2696
2697       te->flag |= TE_ACTIVE;  // for lookup in display hierarchies
2698     }
2699
2700     if (tselem->type == TSE_VIEW_COLLECTION_BASE) {
2701       /* Scene collection in view layer can't expand/collapse. */
2702     }
2703     else if (te->subtree.first || (tselem->type == 0 && te->idcode == ID_SCE) ||
2704              (te->flag & TE_LAZY_CLOSED)) {
2705       /* open/close icon, only when sublevels, except for scene */
2706       int icon_x = startx;
2707
2708       // icons a bit higher
2709       if (TSELEM_OPEN(tselem, soops)) {
2710         UI_icon_draw_alpha((float)icon_x + 2 * ufac,
2711                            (float)*starty + 1 * ufac,
2712                            ICON_DISCLOSURE_TRI_DOWN,
2713                            alpha_fac);
2714       }
2715       else {
2716         UI_icon_draw_alpha((float)icon_x + 2 * ufac,
2717                            (float)*starty + 1 * ufac,
2718                            ICON_DISCLOSURE_TRI_RIGHT,
2719                            alpha_fac);
2720       }
2721     }
2722     offsx += UI_UNIT_X;
2723
2724     /* datatype icon */
2725     if (!(ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE))) {
2726       tselem_draw_icon(
2727           block, xmax, (float)startx + offsx, (float)*starty, tselem, te, alpha_fac, true);
2728       offsx += UI_UNIT_X + 4 * ufac;
2729     }
2730     else {
2731       offsx += 2 * ufac;
2732     }
2733
2734     if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION) && ID_IS_LINKED(tselem->id)) {
2735       if (tselem->id->tag & LIB_TAG_MISSING) {
2736         UI_icon_draw_alpha((float)startx + offsx + 2 * ufac,
2737                            (float)*starty + 2 * ufac,
2738                            ICON_LIBRARY_DATA_BROKEN,
2739                            alpha_fac);
2740       }
2741       else if (tselem->id->tag & LIB_TAG_INDIRECT) {
2742         UI_icon_draw_alpha((float)startx + offsx + 2 * ufac,
2743                            (float)*starty + 2 * ufac,
2744                            ICON_LIBRARY_DATA_INDIRECT,
2745                            alpha_fac);
2746       }
2747       else {
2748         UI_icon_draw_alpha((float)startx + offsx + 2 * ufac,
2749                            (float)*starty + 2 * ufac,
2750                            ICON_LIBRARY_DATA_DIRECT,
2751                            alpha_fac);
2752       }
2753       offsx += UI_UNIT_X + 4 * ufac;
2754     }
2755     else if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION) && ID_IS_STATIC_OVERRIDE(tselem->id)) {
2756       UI_icon_draw_alpha((float)startx + offsx + 2 * ufac,
2757                          (float)*starty + 2 * ufac,
2758                          ICON_LIBRARY_DATA_OVERRIDE,
2759                          alpha_fac);
2760       offsx += UI_UNIT_X + 4 * ufac;
2761     }
2762     GPU_blend(false);
2763
2764     /* name */
2765     if ((tselem->flag & TSE_TEXTBUT) == 0) {
2766       unsigned char text_col[4];
2767
2768       if (active == OL_DRAWSEL_NORMAL) {
2769         UI_GetThemeColor4ubv(TH_TEXT_HI, text_col);
2770       }
2771       else if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) {
2772         UI_GetThemeColorBlend3ubv(TH_BACK, TH_TEXT, 0.75f, text_col);
2773         text_col[3] = 255;
2774       }
2775       else {
2776         UI_GetThemeColor4ubv(TH_TEXT, text_col);
2777       }
2778       text_col[3] *= alpha_fac;
2779
2780       UI_fontstyle_draw_simple(fstyle, startx + offsx, *starty + 5 * ufac, te->name, text_col);
2781     }
2782
2783     offsx += (int)(UI_UNIT_X + UI_fontstyle_string_width(fstyle, te->name));
2784
2785     /* closed item, we draw the icons, not when it's a scene, or master-server list though */
2786     if (!TSELEM_OPEN(tselem, soops)) {
2787       if (te->subtree.first) {
2788         if (tselem->type == 0 && te->idcode == ID_SCE) {
2789           /* pass */
2790         }
2791         /* this tree element always has same amount of branches, so don't draw */
2792         else if (tselem->type != TSE_R_LAYER) {
2793           int tempx = startx + offsx;
2794
2795           GPU_blend(true);
2796
2797           MergedIconRow merged = {{0}};
2798           outliner_draw_iconrow(C,
2799                                 block,
2800                                 fstyle,
2801                                 scene,
2802                                 view_layer,
2803                                 soops,
2804                                 &te->subtree,
2805                                 0,
2806                                 xmax,
2807                                 &tempx,
2808                                 *starty,
2809                                 alpha_fac,
2810                                 &merged);
2811
2812           GPU_blend(false);
2813         }
2814       }
2815     }
2816   }
2817   /* store coord and continue, we need coordinates for elements outside view too */
2818   te->xs = startx;
2819   te->ys = *starty;
2820   te->xend = startx + offsx;
2821
2822   if (TSELEM_OPEN(tselem, soops)) {
2823     *starty -= UI_UNIT_Y;
2824
2825     for (TreeElement *ten = te->subtree.first; ten; ten = ten->next) {
2826       /* check if element needs to be drawn grayed out, but also gray out
2827        * childs of a grayed out parent (pass on draw_grayed_out to childs) */
2828       bool draw_childs_grayed_out = draw_grayed_out || (ten->flag & TE_DRAGGING);
2829       outliner_draw_tree_element(C,
2830                                  block,
2831                                  fstyle,
2832                                  scene,
2833                                  view_layer,
2834                                  ar,
2835                                  soops,
2836                                  ten,
2837                                  draw_childs_grayed_out,
2838                                  startx + UI_UNIT_X,
2839                                  starty,
2840                                  restrict_column_width,
2841                                  te_edit);
2842     }
2843   }
2844   else {
2845     for (TreeElement *ten = te->subtree.first; ten; ten = ten->next) {
2846       outliner_set_coord_tree_element(ten, startx, *starty);
2847     }
2848
2849     *starty -= UI_UNIT_Y;
2850   }
2851 }
2852
2853 static void outliner_draw_hierarchy_lines_recursive(unsigned pos,
2854                                                     SpaceOutliner *soops,
2855                                                     ListBase *lb,
2856                                                     int startx,
2857                                                     const unsigned char col[4],
2858                                                     bool draw_grayed_out,
2859                                                     int *starty)
2860 {
2861   TreeElement *te, *te_vertical_line_last = NULL, *te_vertical_line_last_dashed = NULL;
2862   int y1, y2, y1_dashed, y2_dashed;
2863
2864   if (BLI_listbase_is_empty(lb)) {
2865     return;
2866   }
2867
2868   struct {
2869     int steps_num;
2870     int step_len;
2871     int gap_len;
2872   } dash = {
2873       .steps_num = 4,
2874   };
2875
2876   dash.step_len = UI_UNIT_X / dash.steps_num;
2877   dash.gap_len = dash.step_len / 2;
2878
2879   const unsigned char grayed_alpha = col[3] / 2;
2880
2881   /* For vertical lines between objects. */
2882   y1 = y2 = y1_dashed = y2_dashed = *starty;
2883   for (te = lb->first; te; te = te->next) {
2884     bool draw_childs_grayed_out = draw_grayed_out || (te->flag & TE_DRAGGING);
2885     TreeStoreElem *tselem = TREESTORE(te);
2886
2887     if (draw_childs_grayed_out) {
2888       immUniformColor3ubvAlpha(col, grayed_alpha);
2889     }
2890     else {
2891       immUniformColor4ubv(col);
2892     }
2893
2894     if ((te->flag & TE_CHILD_NOT_IN_COLLECTION) == 0) {
2895       /* Horizontal Line? */
2896       if (tselem->type == 0 && (te->idcode == ID_OB || te->idcode == ID_SCE)) {
2897         immRecti(pos, startx, *starty, startx + UI_UNIT_X, *starty - U.pixelsize);
2898
2899         /* Vertical Line? */
2900         if (te->idcode == ID_OB) {
2901           te_vertical_line_last = te;
2902           y2 = *starty;
2903         }
2904         y1_dashed = *starty - UI_UNIT_Y;
2905       }
2906     }
2907     else {
2908       BLI_assert(te->idcode == ID_OB);
2909       /* Horizontal line - dashed. */
2910       int start = startx;
2911       for (int i = 0; i < dash.steps_num; i++) {
2912         immRecti(pos, start, *starty, start + dash.step_len - dash.gap_len, *starty - U.pixelsize);
2913         start += dash.step_len;
2914       }
2915
2916       te_vertical_line_last_dashed = te;
2917       y2_dashed = *starty;
2918     }
2919
2920     *starty -= UI_UNIT_Y;
2921
2922     if (TSELEM_OPEN(tselem, soops)) {
2923       outliner_draw_hierarchy_lines_recursive(
2924           pos, soops, &te->subtree, startx + UI_UNIT_X, col, draw_childs_grayed_out, starty);
2925     }
2926   }
2927
2928   if (draw_grayed_out) {
2929     immUniformColor3ubvAlpha(col, grayed_alpha);
2930   }
2931   else {
2932     immUniformColor4ubv(col);
2933   }
2934
2935   /* Vertical line. */
2936   te = te_vertical_line_last;
2937   if ((te != NULL) && (te->parent || lb->first != lb->last)) {
2938     immRecti(pos, startx, y1 + UI_UNIT_Y, startx + U.pixelsize, y2);
2939   }
2940
2941   /* Children that are not in the collection are always in the end of the subtree.
2942    * This way we can draw their own dashed vertical lines. */
2943   te = te_vertical_line_last_dashed;
2944   if ((te != NULL) && (te->parent || lb->first != lb->last)) {
2945     const int steps_num = ((y1_dashed + UI_UNIT_Y) - y2_dashed) / dash.step_len;
2946     int start = y1_dashed + UI_UNIT_Y;
2947     for (int i = 0; i < steps_num; i++) {
2948       immRecti(pos, startx, start, startx + U.pixelsize, start - dash.step_len + dash.gap_len);
2949       start -= dash.step_len;
2950     }
2951   }
2952 }
2953
2954 static void outliner_draw_hierarchy_lines(SpaceOutliner *soops,
2955                                           ListBase *lb,
2956                                           int startx,
2957                                           int *starty)
2958 {
2959   GPUVertFormat *format = immVertexFormat();
2960   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
2961   unsigned char col[4];
2962
2963   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2964   UI_GetThemeColorBlend3ubv(TH_BACK, TH_TEXT, 0.4f, col);
2965   col[3] = 255;
2966
2967   GPU_blend(true);
2968   outliner_draw_hierarchy_lines_recursive(pos, soops, lb, startx, col, false, starty);
2969   GPU_blend(false);
2970
2971   immUnbindProgram();
2972 }
2973
2974 static void outliner_draw_struct_marks(ARegion *ar,
2975                                        SpaceOutliner *soops,
2976                                        ListBase *lb,
2977                                        int *starty)
2978 {
2979   for (TreeElement *te = lb->first; te; te = te->next) {
2980     TreeStoreElem *tselem = TREESTORE(te);
2981
2982     /* selection status */
2983     if (TSELEM_OPEN(tselem, soops)) {
2984       if (tselem->type == TSE_RNA_STRUCT) {
2985         GPUVertFormat *format = immVertexFormat();
2986         uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
2987         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2988         immThemeColorShadeAlpha(TH_BACK, -15, -200);
2989         immRecti(pos, 0, *starty + 1, (int)ar->v2d.cur.xmax, *starty + UI_UNIT_Y - 1);
2990         immUnbindProgram();
2991       }
2992     }
2993
2994     *starty -= UI_UNIT_Y;
2995     if (TSELEM_OPEN(tselem, soops)) {
2996       outliner_draw_struct_marks(ar, soops, &te->subtree, starty);
2997       if (tselem->type == TSE_RNA_STRUCT) {
2998         GPUVertFormat *format = immVertexFormat();
2999         uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
3000         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3001         immThemeColorShadeAlpha(TH_BACK, -15, -200);
3002
3003         immBegin(GPU_PRIM_LINES, 2);
3004         immVertex2f(pos, 0, (float)*starty + UI_UNIT_Y);
3005         immVertex2f(pos, ar->v2d.cur.xmax, (float)*starty + UI_UNIT_Y);
3006         immEnd();
3007
3008         immUnbindProgram();
3009       }
3010     }
3011   }
3012 }
3013
3014 static void outliner_draw_highlights_recursive(unsigned pos,
3015                                                const ARegion *ar,
3016                                                const SpaceOutliner *soops,
3017                                                const ListBase *lb,
3018                                                const float col_selection[4],
3019                                                const float col_highlight[4],
3020                                                const float col_searchmatch[4],
3021                                                int start_x,
3022                                                int *io_start_y)
3023 {
3024   const bool is_searching = (SEARCHING_OUTLINER(soops) ||
3025                              (soops->outlinevis == SO_DATA_API && soops->search_string[0] != 0));
3026
3027   for (TreeElement *te = lb->first; te; te = te->next) {
3028     const TreeStoreElem *tselem = TREESTORE(te);
3029     const int start_y = *io_start_y;
3030
3031     /* selection status */
3032     if (tselem->flag & TSE_SELECTED) {
3033       immUniformColor4fv(col_selection);
3034       immRecti(pos, 0, start_y, (int)ar->v2d.cur.xmax, start_y + UI_UNIT_Y);
3035     }
3036
3037     /* highlights */
3038     if (tselem->flag & (TSE_DRAG_ANY | TSE_HIGHLIGHTED | TSE_SEARCHMATCH)) {
3039       const int end_x = (int)ar->v2d.cur.xmax;
3040
3041       if (tselem->flag & TSE_DRAG_ANY) {
3042         /* drag and drop highlight */
3043         float col[4];
3044         UI_GetThemeColorShade4fv(TH_BACK, -40, col);
3045
3046         if (tselem->flag & TSE_DRAG_BEFORE) {
3047           immUniformColor4fv(col);
3048           immRecti(pos,
3049                    start_x,
3050                    start_y + UI_UNIT_Y - U.pixelsize,
3051                    end_x,
3052                    start_y + UI_UNIT_Y + U.pixelsize);
3053         }
3054         else if (tselem->flag & TSE_DRAG_AFTER) {
3055           immUniformColor4fv(col);
3056           immRecti(pos, start_x, start_y - U.pixelsize, end_x, start_y + U.pixelsize);
3057         }
3058         else {
3059           immUniformColor3fvAlpha(col, col[3] * 0.5f);
3060           immRecti(pos, start_x, start_y, end_x, start_y + UI_UNIT_Y);
3061         }
3062       }
3063       else {
3064         if (is_searching && (tselem->flag & TSE_SEARCHMATCH)) {
3065           /* search match highlights
3066            *   we don't expand items when searching in the datablocks but we
3067            *   still want to highlight any filter matches. */
3068           immUniformColor4fv(col_searchmatch);
3069           immRecti(pos, start_x, start_y, end_x, start_y + UI_UNIT_Y);
3070         }
3071         else if (tselem->flag & TSE_HIGHLIGHTED) {
3072           /* mouse hover highlight */
3073           immUniformColor4fv(col_highlight);
3074           immRecti(pos, 0, start_y, end_x, start_y + UI_UNIT_Y);
3075         }
3076       }
3077     }
3078
3079     *io_start_y -= UI_UNIT_Y;
3080     if (TSELEM_OPEN(tselem, soops)) {
3081       outliner_draw_highlights_recursive(pos,
3082                                          ar,
3083                                          soops,
3084                                          &te->subtree,
3085                                          col_selection,
3086                                          col_highlight,
3087                                          col_searchmatch,
3088                                          start_x + UI_UNIT_X,
3089                                          io_start_y);
3090     }
3091   }
3092 }
3093
3094 static void outliner_draw_highlights(ARegion *ar, SpaceOutliner *soops, int startx, int *starty)
3095 {
3096   const float col_highlight[4] = {1.0f, 1.0f, 1.0f, 0.13f};
3097   float col_selection[4], col_searchmatch[4];
3098
3099   UI_GetThemeColor3fv(TH_SELECT_HIGHLIGHT, col_selection);
3100   col_selection[3] = 1.0f; /* no alpha */
3101   UI_GetThemeColor4fv(TH_MATCH, col_searchmatch);
3102   col_searchmatch[3] = 0.5f;
3103
3104   GPU_blend(true);
3105   GPUVertFormat *format = immVertexFormat();
3106   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
3107   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
3108   outliner_draw_highlights_recursive(
3109       pos, ar, soops, &soops->tree, col_selection, col_highlight, col_searchmatch, startx, starty);
3110   immUnbindProgram();
3111   GPU_blend(false);
3112 }
3113
3114 static void outliner_draw_tree(bContext *C,