Blender  V3.3
outliner_sync.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2004 Blender Foundation. All rights reserved. */
3 
8 #include <cstdio>
9 
10 #include "DNA_armature_types.h"
11 #include "DNA_layer_types.h"
12 #include "DNA_outliner_types.h"
13 #include "DNA_screen_types.h"
14 #include "DNA_sequence_types.h"
15 #include "DNA_space_types.h"
16 
17 #include "BLI_compiler_compat.h"
18 #include "BLI_ghash.h"
19 #include "BLI_listbase.h"
20 
21 #include "BKE_armature.h"
22 #include "BKE_context.h"
23 #include "BKE_layer.h"
24 #include "BKE_main.h"
25 
26 #include "DEG_depsgraph.h"
27 
28 #include "ED_armature.h"
29 #include "ED_object.h"
30 #include "ED_outliner.h"
31 
32 #include "SEQ_select.h"
33 
34 #include "WM_api.h"
35 #include "WM_types.h"
36 
37 #include "outliner_intern.hh"
38 
40 {
43 }
44 
46 {
49 }
50 
52 {
55 }
56 
58 {
61 }
62 
64 {
67 }
68 
70 {
73 }
74 
76 {
77  Main *bmain = CTX_data_main(C);
79 
80  for (bScreen *screen = reinterpret_cast<bScreen *>(bmain->screens.first); screen;
81  screen = reinterpret_cast<bScreen *>(screen->id.next)) {
82  LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
83  LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
84  if (sl->spacetype == SPACE_OUTLINER) {
85  SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
86 
87  space_outliner->sync_select_dirty |= wm->outliner_sync_select_dirty;
88  }
89  }
90  }
91  }
92 
93  /* Clear global sync flag */
95 }
96 
103  bool object;
104  bool edit_bone;
105  bool pose_bone;
106  bool sequence;
107 };
108 
114  SpaceOutliner *space_outliner,
115  SyncSelectTypes *sync_types)
116 {
117  TreeViewContext tvc;
119 
120  const bool sequence_view = space_outliner->outlinevis == SO_SEQUENCE;
121 
122  sync_types->object = !sequence_view;
123  sync_types->edit_bone = !sequence_view && (tvc.ob_edit && tvc.ob_edit->type == OB_ARMATURE);
124  sync_types->pose_bone = !sequence_view && (tvc.ob_pose && tvc.ob_pose->mode == OB_MODE_POSE);
125  sync_types->sequence = sequence_view;
126 }
127 
134  SpaceOutliner *space_outliner,
135  SyncSelectTypes *sync_types)
136 {
137  TreeViewContext tvc;
139 
140  const bool sequence_view = space_outliner->outlinevis == SO_SEQUENCE;
141 
142  sync_types->object = !sequence_view &&
144  sync_types->edit_bone = !sequence_view && (tvc.ob_edit && tvc.ob_edit->type == OB_ARMATURE) &&
145  (space_outliner->sync_select_dirty &
147  sync_types->pose_bone = !sequence_view && (tvc.ob_pose && tvc.ob_pose->mode == OB_MODE_POSE) &&
148  (space_outliner->sync_select_dirty &
150  sync_types->sequence = sequence_view && (space_outliner->sync_select_dirty &
152 
153  return sync_types->object || sync_types->edit_bone || sync_types->pose_bone ||
154  sync_types->sequence;
155 }
156 
165 };
166 
167 static void selected_items_init(SelectedItems *selected_items)
168 {
169  selected_items->objects = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
172 }
173 
174 static void selected_items_free(SelectedItems *selected_items)
175 {
176  BLI_gset_free(selected_items->objects, nullptr);
177  BLI_gset_free(selected_items->edit_bones, nullptr);
178  BLI_gset_free(selected_items->pose_bones, nullptr);
179 }
180 
181 /* Check if an instance of this object been selected by the sync */
182 static bool is_object_selected(GSet *selected_objects, Base *base)
183 {
184  return BLI_gset_haskey(selected_objects, base);
185 }
186 
187 /* Check if an instance of this edit bone been selected by the sync */
188 static bool is_edit_bone_selected(GSet *selected_ebones, EditBone *ebone)
189 {
190  return BLI_gset_haskey(selected_ebones, ebone);
191 }
192 
193 /* Check if an instance of this pose bone been selected by the sync */
194 static bool is_pose_bone_selected(GSet *selected_pbones, bPoseChannel *pchan)
195 {
196  return BLI_gset_haskey(selected_pbones, pchan);
197 }
198 
199 /* Add element's data to selected item set */
200 static void add_selected_item(GSet *selected, void *data)
201 {
202  BLI_gset_add(selected, data);
203 }
204 
206  TreeElement *te,
207  TreeStoreElem *tselem,
208  GSet *selected_objects)
209 {
210  Object *ob = (Object *)tselem->id;
211  Base *base = (te->directdata) ? (Base *)te->directdata :
212  BKE_view_layer_base_find(view_layer, ob);
213 
214  if (base && (base->flag & BASE_SELECTABLE)) {
215  if (tselem->flag & TSE_SELECTED) {
217 
218  add_selected_item(selected_objects, base);
219  }
220  else if (!is_object_selected(selected_objects, base)) {
222  }
223  }
224 }
225 
227  TreeElement *te,
228  TreeStoreElem *tselem,
229  GSet *selected_ebones)
230 {
231  bArmature *arm = (bArmature *)tselem->id;
232  EditBone *ebone = (EditBone *)te->directdata;
233 
234  short bone_flag = ebone->flag;
235 
236  if (EBONE_SELECTABLE(arm, ebone)) {
237  if (tselem->flag & TSE_SELECTED) {
238  ED_armature_ebone_select_set(ebone, true);
239  add_selected_item(selected_ebones, ebone);
240  }
241  else if (!is_edit_bone_selected(selected_ebones, ebone)) {
242  /* Don't flush to parent bone tip, synced selection is iterating the whole tree so
243  * deselecting potential children with `ED_armature_ebone_select_set(ebone, false)`
244  * would leave own tip deselected. */
245  ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
246  }
247  }
248 
249  /* Tag if selection changed */
250  if (bone_flag != ebone->flag) {
251  Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
254  }
255 }
256 
258  TreeStoreElem *tselem,
259  GSet *selected_pbones)
260 {
261  Object *ob = (Object *)tselem->id;
262  bArmature *arm = reinterpret_cast<bArmature *>(ob->data);
263  bPoseChannel *pchan = (bPoseChannel *)te->directdata;
264 
265  short bone_flag = pchan->bone->flag;
266 
267  if (PBONE_SELECTABLE(arm, pchan->bone)) {
268  if (tselem->flag & TSE_SELECTED) {
269  pchan->bone->flag |= BONE_SELECTED;
270 
271  add_selected_item(selected_pbones, pchan);
272  }
273  else if (!is_pose_bone_selected(selected_pbones, pchan)) {
274  pchan->bone->flag &= ~BONE_SELECTED;
275  }
276  }
277 
278  /* Tag if selection changed */
279  if (bone_flag != pchan->bone->flag) {
282  }
283 }
284 
286 {
287  Sequence *seq = (Sequence *)tselem->id;
288 
289  if (tselem->flag & TSE_ACTIVE) {
291  }
292 
293  if (tselem->flag & TSE_SELECTED) {
294  seq->flag |= SELECT;
295  }
296  else {
297  seq->flag &= ~SELECT;
298  }
299 }
300 
303  ViewLayer *view_layer,
304  ListBase *tree,
305  const SyncSelectTypes *sync_types,
306  SelectedItems *selected_items)
307 {
308 
310  TreeStoreElem *tselem = TREESTORE(te);
311 
312  if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) {
313  if (sync_types->object) {
314  outliner_select_sync_to_object(view_layer, te, tselem, selected_items->objects);
315  }
316  }
317  else if (tselem->type == TSE_EBONE) {
318  if (sync_types->edit_bone) {
319  outliner_select_sync_to_edit_bone(view_layer, te, tselem, selected_items->edit_bones);
320  }
321  }
322  else if (tselem->type == TSE_POSE_CHANNEL) {
323  if (sync_types->pose_bone) {
324  outliner_select_sync_to_pose_bone(te, tselem, selected_items->pose_bones);
325  }
326  }
327  else if (tselem->type == TSE_SEQUENCE) {
328  if (sync_types->sequence) {
330  }
331  }
332 
334  scene, view_layer, &te->subtree, sync_types, selected_items);
335  }
336 }
337 
339 {
340  /* Don't sync if not checked or in certain outliner display modes */
341  if (!(space_outliner->flag & SO_SYNC_SELECT) || ELEM(space_outliner->outlinevis,
342  SO_LIBRARIES,
344  SO_DATA_API,
345  SO_ID_ORPHANS)) {
346  return;
347  }
348 
350  ViewLayer *view_layer = CTX_data_view_layer(C);
351 
352  SyncSelectTypes sync_types;
353  outliner_sync_select_from_outliner_set_types(C, space_outliner, &sync_types);
354 
355  /* To store elements that have been selected to prevent linked object sync errors */
356  SelectedItems selected_items;
357 
358  selected_items_init(&selected_items);
359 
361  scene, view_layer, &space_outliner->tree, &sync_types, &selected_items);
362 
363  selected_items_free(&selected_items);
364 
365  /* Tag for updates and clear dirty flag to prevent a sync to the outliner on draw. */
366  if (sync_types.object) {
370  }
371  else if (sync_types.edit_bone) {
373  }
374  else if (sync_types.pose_bone) {
376  }
377  if (sync_types.sequence) {
380  }
381 }
382 
384  Object *obact,
385  TreeElement *te,
386  TreeStoreElem *tselem)
387 {
388  Object *ob = (Object *)tselem->id;
389  Base *base = (te->directdata) ? (Base *)te->directdata :
390  BKE_view_layer_base_find(view_layer, ob);
391  const bool is_selected = (base != nullptr) && ((base->flag & BASE_SELECTED) != 0);
392 
393  if (base && (ob == obact)) {
394  tselem->flag |= TSE_ACTIVE;
395  }
396  else {
397  tselem->flag &= ~TSE_ACTIVE;
398  }
399 
400  if (is_selected) {
401  tselem->flag |= TSE_SELECTED;
402  }
403  else {
404  tselem->flag &= ~TSE_SELECTED;
405  }
406 }
407 
409  TreeElement *te,
410  TreeStoreElem *tselem)
411 {
412  EditBone *ebone = (EditBone *)te->directdata;
413 
414  if (ebone == ebone_active) {
415  tselem->flag |= TSE_ACTIVE;
416  }
417  else {
418  tselem->flag &= ~TSE_ACTIVE;
419  }
420 
421  if (ebone->flag & BONE_SELECTED) {
422  tselem->flag |= TSE_SELECTED;
423  }
424  else {
425  tselem->flag &= ~TSE_SELECTED;
426  }
427 }
428 
430  TreeElement *te,
431  TreeStoreElem *tselem)
432 {
433  bPoseChannel *pchan = (bPoseChannel *)te->directdata;
434  Bone *bone = pchan->bone;
435 
436  if (pchan == pchan_active) {
437  tselem->flag |= TSE_ACTIVE;
438  }
439  else {
440  tselem->flag &= ~TSE_ACTIVE;
441  }
442 
443  if (bone->flag & BONE_SELECTED) {
444  tselem->flag |= TSE_SELECTED;
445  }
446  else {
447  tselem->flag &= ~TSE_SELECTED;
448  }
449 }
450 
451 static void outliner_select_sync_from_sequence(Sequence *sequence_active, TreeStoreElem *tselem)
452 {
453  Sequence *seq = (Sequence *)tselem->id;
454 
455  if (seq == sequence_active) {
456  tselem->flag |= TSE_ACTIVE;
457  }
458  else {
459  tselem->flag &= ~TSE_ACTIVE;
460  }
461 
462  if (seq->flag & SELECT) {
463  tselem->flag |= TSE_SELECTED;
464  }
465  else {
466  tselem->flag &= ~TSE_SELECTED;
467  }
468 }
469 
479 };
480 
483  SpaceOutliner *space_outliner,
484  ListBase *tree,
485  SyncSelectActiveData *active_data,
486  const SyncSelectTypes *sync_types)
487 {
489  TreeStoreElem *tselem = TREESTORE(te);
490 
491  if ((tselem->type == TSE_SOME_ID) && te->idcode == ID_OB) {
492  if (sync_types->object) {
493  outliner_select_sync_from_object(view_layer, active_data->object, te, tselem);
494  }
495  }
496  else if (tselem->type == TSE_EBONE) {
497  if (sync_types->edit_bone) {
498  outliner_select_sync_from_edit_bone(active_data->edit_bone, te, tselem);
499  }
500  }
501  else if (tselem->type == TSE_POSE_CHANNEL) {
502  if (sync_types->pose_bone) {
503  outliner_select_sync_from_pose_bone(active_data->pose_channel, te, tselem);
504  }
505  }
506  else if (tselem->type == TSE_SEQUENCE) {
507  if (sync_types->sequence) {
508  outliner_select_sync_from_sequence(active_data->sequence, tselem);
509  }
510  }
511  else {
512  tselem->flag &= ~(TSE_SELECTED | TSE_ACTIVE);
513  }
514 
515  /* Sync subtree elements */
517  view_layer, space_outliner, &te->subtree, active_data, sync_types);
518  }
519 }
520 
521 /* Get active data from context */
523 {
525  ViewLayer *view_layer = CTX_data_view_layer(C);
526  active_data->object = OBACT(view_layer);
527  active_data->edit_bone = CTX_data_active_bone(C);
528  active_data->pose_channel = CTX_data_active_pose_bone(C);
529  active_data->sequence = SEQ_select_active_get(scene);
530 }
531 
532 void outliner_sync_selection(const bContext *C, SpaceOutliner *space_outliner)
533 {
534  /* Set which types of data to sync from sync dirty flag and outliner display mode */
535  SyncSelectTypes sync_types;
536  const bool sync_required = outliner_sync_select_to_outliner_set_types(
537  C, space_outliner, &sync_types);
538 
539  if (sync_required) {
540  ViewLayer *view_layer = CTX_data_view_layer(C);
541 
542  /* Store active object, bones, and sequence */
543  SyncSelectActiveData active_data;
544  get_sync_select_active_data(C, &active_data);
545 
547  view_layer, space_outliner, &space_outliner->tree, &active_data, &sync_types);
548 
549  /* Keep any un-synced data in the dirty flag. */
550  if (sync_types.object) {
552  }
553  if (sync_types.edit_bone) {
555  }
556  if (sync_types.pose_bone) {
558  }
559  if (sync_types.sequence) {
561  }
562  }
563 }
#define PBONE_SELECTABLE(arm, bone)
Definition: BKE_armature.h:554
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1090
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:713
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1100
struct EditBone * CTX_data_active_bone(const bContext *C)
Definition: context.c:1395
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1074
struct bPoseChannel * CTX_data_active_pose_bone(const bContext *C)
Definition: context.c:1425
struct Base * BKE_view_layer_base_find(struct ViewLayer *view_layer, struct Object *ob)
Definition: layer.c:379
struct GSet GSet
Definition: BLI_ghash.h:340
bool BLI_gset_haskey(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:1007
unsigned int BLI_ghashutil_ptrhash(const void *key)
GSet * BLI_gset_new(GSetHashFP hashfp, GSetCmpFP cmpfp, const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:947
bool BLI_ghashutil_ptrcmp(const void *a, const void *b)
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition: BLI_ghash.c:1037
bool BLI_gset_add(GSet *gs, void *key)
Definition: BLI_ghash.c:969
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
#define ELEM(...)
void DEG_id_tag_update(struct ID *id, int flag)
@ ID_RECALC_SELECT
Definition: DNA_ID.h:818
@ ID_OB
Definition: DNA_ID_enums.h:47
@ BONE_ROOTSEL
@ BONE_SELECTED
@ BONE_TIPSEL
@ BASE_SELECTABLE
@ BASE_SELECTED
@ OB_MODE_POSE
@ OB_ARMATURE
@ TSE_POSE_CHANNEL
@ TSE_SEQUENCE
@ TSE_EBONE
@ TSE_SOME_ID
@ TSE_SELECTED
@ TSE_ACTIVE
#define OBEDIT_FROM_VIEW_LAYER(view_layer)
#define OBACT(_view_layer)
@ SPACE_OUTLINER
@ SO_SYNC_SELECT
@ SO_OVERRIDES_LIBRARY
@ SO_SEQUENCE
@ SO_DATA_API
@ SO_LIBRARIES
@ SO_ID_ORPHANS
@ WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE
@ WM_OUTLINER_SYNC_SELECT_FROM_OBJECT
@ WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE
@ WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE
#define WM_OUTLINER_SYNC_SELECT_FROM_ALL
#define EBONE_SELECTABLE(arm, ebone)
Definition: ED_armature.h:52
void ED_object_base_select(struct Base *base, eObjectSelect_Mode mode)
Definition: object_select.c:76
@ BA_DESELECT
Definition: ED_object.h:154
@ BA_SELECT
Definition: ED_object.h:155
#define C
Definition: RandGen.cpp:25
#define ND_SEQUENCER
Definition: WM_types.h:385
#define ND_OB_SELECT
Definition: WM_types.h:390
#define NC_SCENE
Definition: WM_types.h:328
#define ND_BONE_SELECT
Definition: WM_types.h:409
#define NC_OBJECT
Definition: WM_types.h:329
#define NA_SELECTED
Definition: WM_types.h:528
void ED_armature_ebone_select_set(EditBone *ebone, bool select)
#define SELECT
Scene scene
void * tree
static void area(int d1, int d2, int e1, int e2, float weights[2])
void outliner_viewcontext_init(const struct bContext *C, TreeViewContext *tvc)
#define TREESTORE(a)
static void selected_items_free(SelectedItems *selected_items)
void outliner_sync_selection(const bContext *C, SpaceOutliner *space_outliner)
void ED_outliner_select_sync_from_object_tag(bContext *C)
void ED_outliner_select_sync_from_edit_bone_tag(bContext *C)
static void outliner_sync_selection_from_outliner(Scene *scene, ViewLayer *view_layer, ListBase *tree, const SyncSelectTypes *sync_types, SelectedItems *selected_items)
static bool is_object_selected(GSet *selected_objects, Base *base)
static void outliner_select_sync_to_object(ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, GSet *selected_objects)
static bool is_pose_bone_selected(GSet *selected_pbones, bPoseChannel *pchan)
void ED_outliner_select_sync_from_pose_bone_tag(bContext *C)
static void add_selected_item(GSet *selected, void *data)
static void outliner_select_sync_from_object(ViewLayer *view_layer, Object *obact, TreeElement *te, TreeStoreElem *tselem)
void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *space_outliner)
static void outliner_sync_select_from_outliner_set_types(bContext *C, SpaceOutliner *space_outliner, SyncSelectTypes *sync_types)
static bool is_edit_bone_selected(GSet *selected_ebones, EditBone *ebone)
static bool outliner_sync_select_to_outliner_set_types(const bContext *C, SpaceOutliner *space_outliner, SyncSelectTypes *sync_types)
static void outliner_sync_selection_to_outliner(ViewLayer *view_layer, SpaceOutliner *space_outliner, ListBase *tree, SyncSelectActiveData *active_data, const SyncSelectTypes *sync_types)
static void outliner_select_sync_from_pose_bone(bPoseChannel *pchan_active, TreeElement *te, TreeStoreElem *tselem)
void ED_outliner_select_sync_from_all_tag(bContext *C)
static void outliner_select_sync_from_sequence(Sequence *sequence_active, TreeStoreElem *tselem)
void ED_outliner_select_sync_from_sequence_tag(bContext *C)
static void get_sync_select_active_data(const bContext *C, SyncSelectActiveData *active_data)
static void outliner_select_sync_from_edit_bone(EditBone *ebone_active, TreeElement *te, TreeStoreElem *tselem)
bool ED_outliner_select_sync_is_dirty(const bContext *C)
static void selected_items_init(SelectedItems *selected_items)
static void outliner_select_sync_to_pose_bone(TreeElement *te, TreeStoreElem *tselem, GSet *selected_pbones)
static void outliner_select_sync_to_sequence(Scene *scene, TreeStoreElem *tselem)
static void outliner_select_sync_to_edit_bone(ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, GSet *selected_ebones)
void ED_outliner_select_sync_flag_outliners(const bContext *C)
Sequence * SEQ_select_active_get(Scene *scene)
Definition: strip_select.c:18
void SEQ_select_active_set(Scene *scene, Sequence *seq)
Definition: strip_select.c:29
void * first
Definition: DNA_listBase.h:31
Definition: BKE_main.h:121
ListBase screens
Definition: BKE_main.h:183
void * data
bPoseChannel * pose_channel
struct Bone * bone
void WM_main_add_notifier(unsigned int type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)