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 <stdio.h>
25 
26 #include "DNA_armature_types.h"
27 #include "DNA_layer_types.h"
28 #include "DNA_outliner_types.h"
29 #include "DNA_screen_types.h"
30 #include "DNA_sequence_types.h"
31 #include "DNA_space_types.h"
32 
33 #include "BLI_compiler_compat.h"
34 #include "BLI_ghash.h"
35 #include "BLI_listbase.h"
36 
37 #include "BKE_armature.h"
38 #include "BKE_context.h"
39 #include "BKE_layer.h"
40 #include "BKE_main.h"
41 #include "BKE_object.h"
42 #include "BKE_sequencer.h"
43 
44 #include "DEG_depsgraph.h"
45 
46 #include "ED_armature.h"
47 #include "ED_object.h"
48 #include "ED_outliner.h"
49 
50 #include "WM_api.h"
51 #include "WM_types.h"
52 
53 #include "outliner_intern.h"
54 
55 /* Functions for tagging outliner selection syncing is dirty from operators */
ED_outliner_select_sync_from_object_tag(bContext * C)56 void ED_outliner_select_sync_from_object_tag(bContext *C)
57 {
58   wmWindowManager *wm = CTX_wm_manager(C);
59   wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_OBJECT;
60 }
61 
ED_outliner_select_sync_from_edit_bone_tag(bContext * C)62 void ED_outliner_select_sync_from_edit_bone_tag(bContext *C)
63 {
64   wmWindowManager *wm = CTX_wm_manager(C);
65   wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE;
66 }
67 
ED_outliner_select_sync_from_pose_bone_tag(bContext * C)68 void ED_outliner_select_sync_from_pose_bone_tag(bContext *C)
69 {
70   wmWindowManager *wm = CTX_wm_manager(C);
71   wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE;
72 }
73 
ED_outliner_select_sync_from_sequence_tag(bContext * C)74 void ED_outliner_select_sync_from_sequence_tag(bContext *C)
75 {
76   wmWindowManager *wm = CTX_wm_manager(C);
77   wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE;
78 }
79 
ED_outliner_select_sync_from_all_tag(bContext * C)80 void ED_outliner_select_sync_from_all_tag(bContext *C)
81 {
82   wmWindowManager *wm = CTX_wm_manager(C);
83   wm->outliner_sync_select_dirty |= WM_OUTLINER_SYNC_SELECT_FROM_ALL;
84 }
85 
ED_outliner_select_sync_is_dirty(const bContext * C)86 bool ED_outliner_select_sync_is_dirty(const bContext *C)
87 {
88   wmWindowManager *wm = CTX_wm_manager(C);
89   return wm->outliner_sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_ALL;
90 }
91 
92 /* Copy sync select dirty flag from window manager to all outliners to be synced lazily on draw */
ED_outliner_select_sync_flag_outliners(const bContext * C)93 void ED_outliner_select_sync_flag_outliners(const bContext *C)
94 {
95   Main *bmain = CTX_data_main(C);
96   wmWindowManager *wm = CTX_wm_manager(C);
97 
98   for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
99     LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
100       LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
101         if (sl->spacetype == SPACE_OUTLINER) {
102           SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
103 
104           space_outliner->sync_select_dirty |= wm->outliner_sync_select_dirty;
105         }
106       }
107     }
108   }
109 
110   /* Clear global sync flag */
111   wm->outliner_sync_select_dirty = 0;
112 }
113 
114 /**
115  * Outliner sync select dirty flags are not enough to determine which types to sync,
116  * outliner display mode also needs to be considered. This stores the types of data
117  * to sync to increase code clarity.
118  */
119 typedef struct SyncSelectTypes {
120   bool object;
121   bool edit_bone;
122   bool pose_bone;
123   bool sequence;
124 } SyncSelectTypes;
125 
126 /**
127  * Set which types of data to sync when syncing selection from the outliner based on object
128  * interaction mode and outliner display mode
129  */
outliner_sync_select_from_outliner_set_types(bContext * C,SpaceOutliner * space_outliner,SyncSelectTypes * sync_types)130 static void outliner_sync_select_from_outliner_set_types(bContext *C,
131                                                          SpaceOutliner *space_outliner,
132                                                          SyncSelectTypes *sync_types)
133 {
134   TreeViewContext tvc;
135   outliner_viewcontext_init(C, &tvc);
136 
137   const bool sequence_view = space_outliner->outlinevis == SO_SEQUENCE;
138 
139   sync_types->object = !sequence_view;
140   sync_types->edit_bone = !sequence_view && (tvc.ob_edit && tvc.ob_edit->type == OB_ARMATURE);
141   sync_types->pose_bone = !sequence_view && (tvc.ob_pose && tvc.ob_pose->mode == OB_MODE_POSE);
142   sync_types->sequence = sequence_view;
143 }
144 
145 /**
146  * Current dirty flags and outliner display mode determine which type of syncing should occur.
147  * This is to ensure sync flag data is not lost on sync in the wrong display mode.
148  * Returns true if a sync is needed.
149  */
outliner_sync_select_to_outliner_set_types(const bContext * C,SpaceOutliner * space_outliner,SyncSelectTypes * sync_types)150 static bool outliner_sync_select_to_outliner_set_types(const bContext *C,
151                                                        SpaceOutliner *space_outliner,
152                                                        SyncSelectTypes *sync_types)
153 {
154   TreeViewContext tvc;
155   outliner_viewcontext_init(C, &tvc);
156 
157   const bool sequence_view = space_outliner->outlinevis == SO_SEQUENCE;
158 
159   sync_types->object = !sequence_view &&
160                        (space_outliner->sync_select_dirty & WM_OUTLINER_SYNC_SELECT_FROM_OBJECT);
161   sync_types->edit_bone = !sequence_view && (tvc.ob_edit && tvc.ob_edit->type == OB_ARMATURE) &&
162                           (space_outliner->sync_select_dirty &
163                            WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE);
164   sync_types->pose_bone = !sequence_view && (tvc.ob_pose && tvc.ob_pose->mode == OB_MODE_POSE) &&
165                           (space_outliner->sync_select_dirty &
166                            WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE);
167   sync_types->sequence = sequence_view && (space_outliner->sync_select_dirty &
168                                            WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE);
169 
170   return sync_types->object || sync_types->edit_bone || sync_types->pose_bone ||
171          sync_types->sequence;
172 }
173 
174 /**
175  * Stores items selected from a sync from the outliner. Prevents syncing the selection
176  * state of the last instance of an object linked in multiple collections.
177  */
178 typedef struct SelectedItems {
179   GSet *objects;
180   GSet *edit_bones;
181   GSet *pose_bones;
182 } SelectedItems;
183 
selected_items_init(SelectedItems * selected_items)184 static void selected_items_init(SelectedItems *selected_items)
185 {
186   selected_items->objects = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
187   selected_items->edit_bones = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
188   selected_items->pose_bones = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
189 }
190 
selected_items_free(SelectedItems * selected_items)191 static void selected_items_free(SelectedItems *selected_items)
192 {
193   BLI_gset_free(selected_items->objects, NULL);
194   BLI_gset_free(selected_items->edit_bones, NULL);
195   BLI_gset_free(selected_items->pose_bones, NULL);
196 }
197 
198 /* Check if an instance of this object been selected by the sync */
is_object_selected(GSet * selected_objects,Base * base)199 static bool is_object_selected(GSet *selected_objects, Base *base)
200 {
201   return BLI_gset_haskey(selected_objects, base);
202 }
203 
204 /* Check if an instance of this edit bone been selected by the sync */
is_edit_bone_selected(GSet * selected_ebones,EditBone * ebone)205 static bool is_edit_bone_selected(GSet *selected_ebones, EditBone *ebone)
206 {
207   return BLI_gset_haskey(selected_ebones, ebone);
208 }
209 
210 /* Check if an instance of this pose bone been selected by the sync */
is_pose_bone_selected(GSet * selected_pbones,bPoseChannel * pchan)211 static bool is_pose_bone_selected(GSet *selected_pbones, bPoseChannel *pchan)
212 {
213   return BLI_gset_haskey(selected_pbones, pchan);
214 }
215 
216 /* Add element's data to selected item set */
add_selected_item(GSet * selected,void * data)217 static void add_selected_item(GSet *selected, void *data)
218 {
219   BLI_gset_add(selected, data);
220 }
221 
outliner_select_sync_to_object(ViewLayer * view_layer,TreeElement * te,TreeStoreElem * tselem,GSet * selected_objects)222 static void outliner_select_sync_to_object(ViewLayer *view_layer,
223                                            TreeElement *te,
224                                            TreeStoreElem *tselem,
225                                            GSet *selected_objects)
226 {
227   Object *ob = (Object *)tselem->id;
228   Base *base = (te->directdata) ? (Base *)te->directdata :
229                                   BKE_view_layer_base_find(view_layer, ob);
230 
231   if (base && (base->flag & BASE_SELECTABLE)) {
232     if (tselem->flag & TSE_SELECTED) {
233       ED_object_base_select(base, BA_SELECT);
234 
235       add_selected_item(selected_objects, base);
236     }
237     else if (!is_object_selected(selected_objects, base)) {
238       ED_object_base_select(base, BA_DESELECT);
239     }
240   }
241 }
242 
outliner_select_sync_to_edit_bone(ViewLayer * view_layer,TreeElement * te,TreeStoreElem * tselem,GSet * selected_ebones)243 static void outliner_select_sync_to_edit_bone(ViewLayer *view_layer,
244                                               TreeElement *te,
245                                               TreeStoreElem *tselem,
246                                               GSet *selected_ebones)
247 {
248   bArmature *arm = (bArmature *)tselem->id;
249   EditBone *ebone = (EditBone *)te->directdata;
250 
251   short bone_flag = ebone->flag;
252 
253   if (EBONE_SELECTABLE(arm, ebone)) {
254     if (tselem->flag & TSE_SELECTED) {
255       ED_armature_ebone_select_set(ebone, true);
256       add_selected_item(selected_ebones, ebone);
257     }
258     else if (!is_edit_bone_selected(selected_ebones, ebone)) {
259       /* Dont flush to parent bone tip, synced selection is iterating the whole tree so deselecting
260        * potential children with 'ED_armature_ebone_select_set(ebone, false)' would leave own tip
261        * deselected. */
262       ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
263     }
264   }
265 
266   /* Tag if selection changed */
267   if (bone_flag != ebone->flag) {
268     Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
269     DEG_id_tag_update(&arm->id, ID_RECALC_SELECT);
270     WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, obedit);
271   }
272 }
273 
outliner_select_sync_to_pose_bone(TreeElement * te,TreeStoreElem * tselem,GSet * selected_pbones)274 static void outliner_select_sync_to_pose_bone(TreeElement *te,
275                                               TreeStoreElem *tselem,
276                                               GSet *selected_pbones)
277 {
278   Object *ob = (Object *)tselem->id;
279   bArmature *arm = ob->data;
280   bPoseChannel *pchan = (bPoseChannel *)te->directdata;
281 
282   short bone_flag = pchan->bone->flag;
283 
284   if (PBONE_SELECTABLE(arm, pchan->bone)) {
285     if (tselem->flag & TSE_SELECTED) {
286       pchan->bone->flag |= BONE_SELECTED;
287 
288       add_selected_item(selected_pbones, pchan);
289     }
290     else if (!is_pose_bone_selected(selected_pbones, pchan)) {
291       pchan->bone->flag &= ~BONE_SELECTED;
292     }
293   }
294 
295   /* Tag if selection changed */
296   if (bone_flag != pchan->bone->flag) {
297     DEG_id_tag_update(&arm->id, ID_RECALC_SELECT);
298     WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, ob);
299   }
300 }
301 
outliner_select_sync_to_sequence(Scene * scene,TreeStoreElem * tselem)302 static void outliner_select_sync_to_sequence(Scene *scene, TreeStoreElem *tselem)
303 {
304   Sequence *seq = (Sequence *)tselem->id;
305 
306   if (tselem->flag & TSE_ACTIVE) {
307     BKE_sequencer_active_set(scene, seq);
308   }
309 
310   if (tselem->flag & TSE_SELECTED) {
311     seq->flag |= SELECT;
312   }
313   else {
314     seq->flag &= ~SELECT;
315   }
316 }
317 
318 /** Sync select and active flags from outliner to active view layer, bones, and sequencer. */
outliner_sync_selection_from_outliner(Scene * scene,ViewLayer * view_layer,ListBase * tree,const SyncSelectTypes * sync_types,SelectedItems * selected_items)319 static void outliner_sync_selection_from_outliner(Scene *scene,
320                                                   ViewLayer *view_layer,
321                                                   ListBase *tree,
322                                                   const SyncSelectTypes *sync_types,
323                                                   SelectedItems *selected_items)
324 {
325 
326   LISTBASE_FOREACH (TreeElement *, te, tree) {
327     TreeStoreElem *tselem = TREESTORE(te);
328 
329     if (tselem->type == 0 && te->idcode == ID_OB) {
330       if (sync_types->object) {
331         outliner_select_sync_to_object(view_layer, te, tselem, selected_items->objects);
332       }
333     }
334     else if (tselem->type == TSE_EBONE) {
335       if (sync_types->edit_bone) {
336         outliner_select_sync_to_edit_bone(view_layer, te, tselem, selected_items->edit_bones);
337       }
338     }
339     else if (tselem->type == TSE_POSE_CHANNEL) {
340       if (sync_types->pose_bone) {
341         outliner_select_sync_to_pose_bone(te, tselem, selected_items->pose_bones);
342       }
343     }
344     else if (tselem->type == TSE_SEQUENCE) {
345       if (sync_types->sequence) {
346         outliner_select_sync_to_sequence(scene, tselem);
347       }
348     }
349 
350     outliner_sync_selection_from_outliner(
351         scene, view_layer, &te->subtree, sync_types, selected_items);
352   }
353 }
354 
355 /* Set clean outliner and mark other outliners for syncing */
ED_outliner_select_sync_from_outliner(bContext * C,SpaceOutliner * space_outliner)356 void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *space_outliner)
357 {
358   /* Don't sync if not checked or in certain outliner display modes */
359   if (!(space_outliner->flag & SO_SYNC_SELECT) ||
360       ELEM(space_outliner->outlinevis, SO_LIBRARIES, SO_DATA_API, SO_ID_ORPHANS)) {
361     return;
362   }
363 
364   Scene *scene = CTX_data_scene(C);
365   ViewLayer *view_layer = CTX_data_view_layer(C);
366 
367   SyncSelectTypes sync_types;
368   outliner_sync_select_from_outliner_set_types(C, space_outliner, &sync_types);
369 
370   /* To store elements that have been selected to prevent linked object sync errors */
371   SelectedItems selected_items;
372 
373   selected_items_init(&selected_items);
374 
375   outliner_sync_selection_from_outliner(
376       scene, view_layer, &space_outliner->tree, &sync_types, &selected_items);
377 
378   selected_items_free(&selected_items);
379 
380   /* Tag for updates and clear dirty flag toprevent a sync to the outliner on draw */
381   if (sync_types.object) {
382     space_outliner->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_OBJECT;
383     DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
384     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
385   }
386   else if (sync_types.edit_bone) {
387     space_outliner->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE;
388   }
389   else if (sync_types.pose_bone) {
390     space_outliner->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE;
391   }
392   if (sync_types.sequence) {
393     space_outliner->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE;
394     WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene);
395   }
396 }
397 
outliner_select_sync_from_object(ViewLayer * view_layer,Object * obact,TreeElement * te,TreeStoreElem * tselem)398 static void outliner_select_sync_from_object(ViewLayer *view_layer,
399                                              Object *obact,
400                                              TreeElement *te,
401                                              TreeStoreElem *tselem)
402 {
403   Object *ob = (Object *)tselem->id;
404   Base *base = (te->directdata) ? (Base *)te->directdata :
405                                   BKE_view_layer_base_find(view_layer, ob);
406   const bool is_selected = (base != NULL) && ((base->flag & BASE_SELECTED) != 0);
407 
408   if (base && (ob == obact)) {
409     tselem->flag |= TSE_ACTIVE;
410   }
411   else {
412     tselem->flag &= ~TSE_ACTIVE;
413   }
414 
415   if (is_selected) {
416     tselem->flag |= TSE_SELECTED;
417   }
418   else {
419     tselem->flag &= ~TSE_SELECTED;
420   }
421 }
422 
outliner_select_sync_from_edit_bone(EditBone * ebone_active,TreeElement * te,TreeStoreElem * tselem)423 static void outliner_select_sync_from_edit_bone(EditBone *ebone_active,
424                                                 TreeElement *te,
425                                                 TreeStoreElem *tselem)
426 {
427   EditBone *ebone = (EditBone *)te->directdata;
428 
429   if (ebone == ebone_active) {
430     tselem->flag |= TSE_ACTIVE;
431   }
432   else {
433     tselem->flag &= ~TSE_ACTIVE;
434   }
435 
436   if (ebone->flag & BONE_SELECTED) {
437     tselem->flag |= TSE_SELECTED;
438   }
439   else {
440     tselem->flag &= ~TSE_SELECTED;
441   }
442 }
443 
outliner_select_sync_from_pose_bone(bPoseChannel * pchan_active,TreeElement * te,TreeStoreElem * tselem)444 static void outliner_select_sync_from_pose_bone(bPoseChannel *pchan_active,
445                                                 TreeElement *te,
446                                                 TreeStoreElem *tselem)
447 {
448   bPoseChannel *pchan = (bPoseChannel *)te->directdata;
449   Bone *bone = pchan->bone;
450 
451   if (pchan == pchan_active) {
452     tselem->flag |= TSE_ACTIVE;
453   }
454   else {
455     tselem->flag &= ~TSE_ACTIVE;
456   }
457 
458   if (bone->flag & BONE_SELECTED) {
459     tselem->flag |= TSE_SELECTED;
460   }
461   else {
462     tselem->flag &= ~TSE_SELECTED;
463   }
464 }
465 
outliner_select_sync_from_sequence(Sequence * sequence_active,TreeStoreElem * tselem)466 static void outliner_select_sync_from_sequence(Sequence *sequence_active, TreeStoreElem *tselem)
467 {
468   Sequence *seq = (Sequence *)tselem->id;
469 
470   if (seq == sequence_active) {
471     tselem->flag |= TSE_ACTIVE;
472   }
473   else {
474     tselem->flag &= ~TSE_ACTIVE;
475   }
476 
477   if (seq->flag & SELECT) {
478     tselem->flag |= TSE_SELECTED;
479   }
480   else {
481     tselem->flag &= ~TSE_SELECTED;
482   }
483 }
484 
485 /**
486  * Contains active object, bones, and sequence for syncing to prevent getting active data
487  * repeatedly throughout syncing to the outliner.
488  */
489 typedef struct SyncSelectActiveData {
490   Object *object;
491   EditBone *edit_bone;
492   bPoseChannel *pose_channel;
493   Sequence *sequence;
494 } SyncSelectActiveData;
495 
496 /** Sync select and active flags from active view layer, bones, and sequences to the outliner. */
outliner_sync_selection_to_outliner(ViewLayer * view_layer,SpaceOutliner * space_outliner,ListBase * tree,SyncSelectActiveData * active_data,const SyncSelectTypes * sync_types)497 static void outliner_sync_selection_to_outliner(ViewLayer *view_layer,
498                                                 SpaceOutliner *space_outliner,
499                                                 ListBase *tree,
500                                                 SyncSelectActiveData *active_data,
501                                                 const SyncSelectTypes *sync_types)
502 {
503   LISTBASE_FOREACH (TreeElement *, te, tree) {
504     TreeStoreElem *tselem = TREESTORE(te);
505 
506     if (tselem->type == 0 && te->idcode == ID_OB) {
507       if (sync_types->object) {
508         outliner_select_sync_from_object(view_layer, active_data->object, te, tselem);
509       }
510     }
511     else if (tselem->type == TSE_EBONE) {
512       if (sync_types->edit_bone) {
513         outliner_select_sync_from_edit_bone(active_data->edit_bone, te, tselem);
514       }
515     }
516     else if (tselem->type == TSE_POSE_CHANNEL) {
517       if (sync_types->pose_bone) {
518         outliner_select_sync_from_pose_bone(active_data->pose_channel, te, tselem);
519       }
520     }
521     else if (tselem->type == TSE_SEQUENCE) {
522       if (sync_types->sequence) {
523         outliner_select_sync_from_sequence(active_data->sequence, tselem);
524       }
525     }
526     else {
527       tselem->flag &= ~(TSE_SELECTED | TSE_ACTIVE);
528     }
529 
530     /* Sync subtree elements */
531     outliner_sync_selection_to_outliner(
532         view_layer, space_outliner, &te->subtree, active_data, sync_types);
533   }
534 }
535 
536 /* Get active data from context */
get_sync_select_active_data(const bContext * C,SyncSelectActiveData * active_data)537 static void get_sync_select_active_data(const bContext *C, SyncSelectActiveData *active_data)
538 {
539   Scene *scene = CTX_data_scene(C);
540   ViewLayer *view_layer = CTX_data_view_layer(C);
541   active_data->object = OBACT(view_layer);
542   active_data->edit_bone = CTX_data_active_bone(C);
543   active_data->pose_channel = CTX_data_active_pose_bone(C);
544   active_data->sequence = BKE_sequencer_active_get(scene);
545 }
546 
547 /* If outliner is dirty sync selection from view layer and sequwncer */
outliner_sync_selection(const bContext * C,SpaceOutliner * space_outliner)548 void outliner_sync_selection(const bContext *C, SpaceOutliner *space_outliner)
549 {
550   /* Set which types of data to sync from sync dirty flag and outliner display mode */
551   SyncSelectTypes sync_types;
552   const bool sync_required = outliner_sync_select_to_outliner_set_types(
553       C, space_outliner, &sync_types);
554 
555   if (sync_required) {
556     ViewLayer *view_layer = CTX_data_view_layer(C);
557 
558     /* Store active object, bones, and sequence */
559     SyncSelectActiveData active_data;
560     get_sync_select_active_data(C, &active_data);
561 
562     outliner_sync_selection_to_outliner(
563         view_layer, space_outliner, &space_outliner->tree, &active_data, &sync_types);
564 
565     /* Keep any unsynced data in the dirty flag */
566     if (sync_types.object) {
567       space_outliner->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_OBJECT;
568     }
569     if (sync_types.edit_bone) {
570       space_outliner->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE;
571     }
572     if (sync_types.pose_bone) {
573       space_outliner->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE;
574     }
575     if (sync_types.sequence) {
576       space_outliner->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE;
577     }
578   }
579 }
580