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