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