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