Blender  V2.93
outliner_sync.c
Go to the documentation of this file.
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 
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 
42 #include "DEG_depsgraph.h"
43 
44 #include "ED_armature.h"
45 #include "ED_object.h"
46 #include "ED_outliner.h"
47 
48 #include "SEQ_select.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 */
57 {
60 }
61 
63 {
66 }
67 
69 {
72 }
73 
75 {
78 }
79 
81 {
84 }
85 
87 {
90 }
91 
92 /* Copy sync select dirty flag from window manager to all outliners to be synced lazily on draw */
94 {
95  Main *bmain = CTX_data_main(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 */
112 }
113 
119 typedef struct SyncSelectTypes {
120  bool object;
121  bool edit_bone;
122  bool pose_bone;
123  bool sequence;
125 
131  SpaceOutliner *space_outliner,
132  SyncSelectTypes *sync_types)
133 {
134  TreeViewContext 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 
151  SpaceOutliner *space_outliner,
152  SyncSelectTypes *sync_types)
153 {
154  TreeViewContext tvc;
156 
157  const bool sequence_view = space_outliner->outlinevis == SO_SEQUENCE;
158 
159  sync_types->object = !sequence_view &&
161  sync_types->edit_bone = !sequence_view && (tvc.ob_edit && tvc.ob_edit->type == OB_ARMATURE) &&
162  (space_outliner->sync_select_dirty &
164  sync_types->pose_bone = !sequence_view && (tvc.ob_pose && tvc.ob_pose->mode == OB_MODE_POSE) &&
165  (space_outliner->sync_select_dirty &
167  sync_types->sequence = sequence_view && (space_outliner->sync_select_dirty &
169 
170  return sync_types->object || sync_types->edit_bone || sync_types->pose_bone ||
171  sync_types->sequence;
172 }
173 
178 typedef struct SelectedItems {
183 
184 static void selected_items_init(SelectedItems *selected_items)
185 {
186  selected_items->objects = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
189 }
190 
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 */
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 */
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 */
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 */
217 static void add_selected_item(GSet *selected, void *data)
218 {
219  BLI_gset_add(selected, data);
220 }
221 
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) {
234 
235  add_selected_item(selected_objects, base);
236  }
237  else if (!is_object_selected(selected_objects, base)) {
239  }
240  }
241 }
242 
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  /* Don't flush to parent bone tip, synced selection is iterating the whole tree so
260  * deselecting potential children with `ED_armature_ebone_select_set(ebone, false)`
261  * would leave own tip 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);
271  }
272 }
273 
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) {
299  }
300 }
301 
303 {
304  Sequence *seq = (Sequence *)tselem->id;
305 
306  if (tselem->flag & TSE_ACTIVE) {
308  }
309 
310  if (tselem->flag & TSE_SELECTED) {
311  seq->flag |= SELECT;
312  }
313  else {
314  seq->flag &= ~SELECT;
315  }
316 }
317 
320  ViewLayer *view_layer,
321  ListBase *tree,
322  const SyncSelectTypes *sync_types,
323  SelectedItems *selected_items)
324 {
325 
327  TreeStoreElem *tselem = TREESTORE(te);
328 
329  if ((tselem->type == TSE_SOME_ID) && (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) {
347  }
348  }
349 
351  scene, view_layer, &te->subtree, sync_types, selected_items);
352  }
353 }
354 
355 /* Set clean outliner and mark other outliners for syncing */
357 {
358  /* Don't sync if not checked or in certain outliner display modes */
359  if (!(space_outliner->flag & SO_SYNC_SELECT) || ELEM(space_outliner->outlinevis,
360  SO_LIBRARIES,
362  SO_DATA_API,
363  SO_ID_ORPHANS)) {
364  return;
365  }
366 
368  ViewLayer *view_layer = CTX_data_view_layer(C);
369 
370  SyncSelectTypes sync_types;
371  outliner_sync_select_from_outliner_set_types(C, space_outliner, &sync_types);
372 
373  /* To store elements that have been selected to prevent linked object sync errors */
374  SelectedItems selected_items;
375 
376  selected_items_init(&selected_items);
377 
379  scene, view_layer, &space_outliner->tree, &sync_types, &selected_items);
380 
381  selected_items_free(&selected_items);
382 
383  /* Tag for updates and clear dirty flag to prevent a sync to the outliner on draw. */
384  if (sync_types.object) {
388  }
389  else if (sync_types.edit_bone) {
391  }
392  else if (sync_types.pose_bone) {
394  }
395  if (sync_types.sequence) {
398  }
399 }
400 
402  Object *obact,
403  TreeElement *te,
404  TreeStoreElem *tselem)
405 {
406  Object *ob = (Object *)tselem->id;
407  Base *base = (te->directdata) ? (Base *)te->directdata :
408  BKE_view_layer_base_find(view_layer, ob);
409  const bool is_selected = (base != NULL) && ((base->flag & BASE_SELECTED) != 0);
410 
411  if (base && (ob == obact)) {
412  tselem->flag |= TSE_ACTIVE;
413  }
414  else {
415  tselem->flag &= ~TSE_ACTIVE;
416  }
417 
418  if (is_selected) {
419  tselem->flag |= TSE_SELECTED;
420  }
421  else {
422  tselem->flag &= ~TSE_SELECTED;
423  }
424 }
425 
427  TreeElement *te,
428  TreeStoreElem *tselem)
429 {
430  EditBone *ebone = (EditBone *)te->directdata;
431 
432  if (ebone == ebone_active) {
433  tselem->flag |= TSE_ACTIVE;
434  }
435  else {
436  tselem->flag &= ~TSE_ACTIVE;
437  }
438 
439  if (ebone->flag & BONE_SELECTED) {
440  tselem->flag |= TSE_SELECTED;
441  }
442  else {
443  tselem->flag &= ~TSE_SELECTED;
444  }
445 }
446 
448  TreeElement *te,
449  TreeStoreElem *tselem)
450 {
451  bPoseChannel *pchan = (bPoseChannel *)te->directdata;
452  Bone *bone = pchan->bone;
453 
454  if (pchan == pchan_active) {
455  tselem->flag |= TSE_ACTIVE;
456  }
457  else {
458  tselem->flag &= ~TSE_ACTIVE;
459  }
460 
461  if (bone->flag & BONE_SELECTED) {
462  tselem->flag |= TSE_SELECTED;
463  }
464  else {
465  tselem->flag &= ~TSE_SELECTED;
466  }
467 }
468 
469 static void outliner_select_sync_from_sequence(Sequence *sequence_active, TreeStoreElem *tselem)
470 {
471  Sequence *seq = (Sequence *)tselem->id;
472 
473  if (seq == sequence_active) {
474  tselem->flag |= TSE_ACTIVE;
475  }
476  else {
477  tselem->flag &= ~TSE_ACTIVE;
478  }
479 
480  if (seq->flag & SELECT) {
481  tselem->flag |= TSE_SELECTED;
482  }
483  else {
484  tselem->flag &= ~TSE_SELECTED;
485  }
486 }
487 
492 typedef struct SyncSelectActiveData {
498 
501  SpaceOutliner *space_outliner,
502  ListBase *tree,
503  SyncSelectActiveData *active_data,
504  const SyncSelectTypes *sync_types)
505 {
507  TreeStoreElem *tselem = TREESTORE(te);
508 
509  if ((tselem->type == TSE_SOME_ID) && te->idcode == ID_OB) {
510  if (sync_types->object) {
511  outliner_select_sync_from_object(view_layer, active_data->object, te, tselem);
512  }
513  }
514  else if (tselem->type == TSE_EBONE) {
515  if (sync_types->edit_bone) {
516  outliner_select_sync_from_edit_bone(active_data->edit_bone, te, tselem);
517  }
518  }
519  else if (tselem->type == TSE_POSE_CHANNEL) {
520  if (sync_types->pose_bone) {
521  outliner_select_sync_from_pose_bone(active_data->pose_channel, te, tselem);
522  }
523  }
524  else if (tselem->type == TSE_SEQUENCE) {
525  if (sync_types->sequence) {
526  outliner_select_sync_from_sequence(active_data->sequence, tselem);
527  }
528  }
529  else {
530  tselem->flag &= ~(TSE_SELECTED | TSE_ACTIVE);
531  }
532 
533  /* Sync subtree elements */
535  view_layer, space_outliner, &te->subtree, active_data, sync_types);
536  }
537 }
538 
539 /* Get active data from context */
541 {
543  ViewLayer *view_layer = CTX_data_view_layer(C);
544  active_data->object = OBACT(view_layer);
545  active_data->edit_bone = CTX_data_active_bone(C);
546  active_data->pose_channel = CTX_data_active_pose_bone(C);
547  active_data->sequence = SEQ_select_active_get(scene);
548 }
549 
550 /* If outliner is dirty sync selection from view layer and sequencer. */
551 void outliner_sync_selection(const bContext *C, SpaceOutliner *space_outliner)
552 {
553  /* Set which types of data to sync from sync dirty flag and outliner display mode */
554  SyncSelectTypes sync_types;
555  const bool sync_required = outliner_sync_select_to_outliner_set_types(
556  C, space_outliner, &sync_types);
557 
558  if (sync_required) {
559  ViewLayer *view_layer = CTX_data_view_layer(C);
560 
561  /* Store active object, bones, and sequence */
562  SyncSelectActiveData active_data;
563  get_sync_select_active_data(C, &active_data);
564 
566  view_layer, space_outliner, &space_outliner->tree, &active_data, &sync_types);
567 
568  /* Keep any unsynced data in the dirty flag */
569  if (sync_types.object) {
571  }
572  if (sync_types.edit_bone) {
574  }
575  if (sync_types.pose_bone) {
577  }
578  if (sync_types.sequence) {
580  }
581  }
582 }
#define PBONE_SELECTABLE(arm, bone)
Definition: BKE_armature.h:343
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1034
struct wmWindowManager * CTX_wm_manager(const bContext *C)
Definition: context.c:689
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1044
struct EditBone * CTX_data_active_bone(const bContext *C)
Definition: context.c:1321
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1018
struct bPoseChannel * CTX_data_active_pose_bone(const bContext *C)
Definition: context.c:1351
struct Base * BKE_view_layer_base_find(struct ViewLayer *view_layer, struct Object *ob)
Definition: layer.c:394
struct GSet GSet
Definition: BLI_ghash.h:189
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:1125
bool BLI_ghashutil_ptrcmp(const void *a, const void *b)
bool BLI_gset_haskey(GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:1216
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition: BLI_ghash.c:1253
bool BLI_gset_add(GSet *gs, void *key)
Definition: BLI_ghash.c:1160
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
#define ELEM(...)
void DEG_id_tag_update(struct ID *id, int flag)
@ ID_RECALC_SELECT
Definition: DNA_ID.h:638
@ ID_OB
Definition: DNA_ID_enums.h:59
@ 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:61
void ED_object_base_select(struct Base *base, eObjectSelect_Mode mode)
Definition: object_select.c:98
@ BA_DESELECT
Definition: ED_object.h:146
@ BA_SELECT
Definition: ED_object.h:147
#define C
Definition: RandGen.cpp:39
#define ND_SEQUENCER
Definition: WM_types.h:337
#define ND_OB_SELECT
Definition: WM_types.h:342
#define NC_SCENE
Definition: WM_types.h:279
#define ND_BONE_SELECT
Definition: WM_types.h:361
#define NC_OBJECT
Definition: WM_types.h:280
#define NA_SELECTED
Definition: WM_types.h:467
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)
Definition: outliner_sync.c:56
void ED_outliner_select_sync_from_edit_bone_tag(bContext *C)
Definition: outliner_sync.c:62
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)
Definition: outliner_sync.c:68
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)
struct SyncSelectTypes SyncSelectTypes
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)
struct SyncSelectActiveData SyncSelectActiveData
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)
Definition: outliner_sync.c:80
static void outliner_select_sync_from_sequence(Sequence *sequence_active, TreeStoreElem *tselem)
void ED_outliner_select_sync_from_sequence_tag(bContext *C)
Definition: outliner_sync.c:74
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)
Definition: outliner_sync.c:86
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)
struct SelectedItems SelectedItems
void ED_outliner_select_sync_flag_outliners(const bContext *C)
Definition: outliner_sync.c:93
Sequence * SEQ_select_active_get(Scene *scene)
Definition: strip_select.c:35
void SEQ_select_active_set(Scene *scene, Sequence *seq)
Definition: strip_select.c:46
void * first
Definition: DNA_listBase.h:47
Definition: BKE_main.h:116
ListBase screens
Definition: BKE_main.h:161
void * data
bPoseChannel * pose_channel
void * directdata
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)