Blender V4.3
outliner_sync.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2004 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cstdio>
10
11#include "DNA_armature_types.h"
12#include "DNA_layer_types.h"
13#include "DNA_outliner_types.h"
14#include "DNA_screen_types.h"
15#include "DNA_sequence_types.h"
16#include "DNA_space_types.h"
17
18#include "BLI_compiler_compat.h"
19#include "BLI_ghash.h"
20#include "BLI_listbase.h"
21
22#include "BKE_armature.hh"
23#include "BKE_context.hh"
24#include "BKE_layer.hh"
25#include "BKE_main.hh"
26
27#include "DEG_depsgraph.hh"
28
29#include "ED_armature.hh"
30#include "ED_object.hh"
31#include "ED_outliner.hh"
32
33#include "SEQ_select.hh"
34
35#include "WM_api.hh"
36#include "WM_types.hh"
37
39
41
42#include "outliner_intern.hh"
43
49
55
61
67
73
79
81{
82 Main *bmain = CTX_data_main(C);
84
85 for (bScreen *screen = static_cast<bScreen *>(bmain->screens.first); screen;
86 screen = static_cast<bScreen *>(screen->id.next))
87 {
88 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
89 LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
90 if (sl->spacetype == SPACE_OUTLINER) {
91 SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
92
93 space_outliner->sync_select_dirty |= wm->outliner_sync_select_dirty;
94 }
95 }
96 }
97 }
98
99 /* Clear global sync flag */
101}
102
103namespace blender::ed::outliner {
104
116
122 SpaceOutliner *space_outliner,
123 SyncSelectTypes *sync_types)
124{
125 TreeViewContext tvc;
127
128 const bool sequence_view = space_outliner->outlinevis == SO_SEQUENCE;
129
130 sync_types->object = !sequence_view;
131 sync_types->edit_bone = !sequence_view && (tvc.ob_edit && tvc.ob_edit->type == OB_ARMATURE);
132 sync_types->pose_bone = !sequence_view && (tvc.ob_pose && tvc.ob_pose->mode == OB_MODE_POSE);
133 sync_types->sequence = sequence_view;
134}
135
142 SpaceOutliner *space_outliner,
143 SyncSelectTypes *sync_types)
144{
145 TreeViewContext tvc;
147
148 const bool sequence_view = space_outliner->outlinevis == SO_SEQUENCE;
149
150 sync_types->object = !sequence_view &&
152 sync_types->edit_bone = !sequence_view && (tvc.ob_edit && tvc.ob_edit->type == OB_ARMATURE) &&
153 (space_outliner->sync_select_dirty &
155 sync_types->pose_bone = !sequence_view && (tvc.ob_pose && tvc.ob_pose->mode == OB_MODE_POSE) &&
156 (space_outliner->sync_select_dirty &
158 sync_types->sequence = sequence_view && (space_outliner->sync_select_dirty &
160
161 return sync_types->object || sync_types->edit_bone || sync_types->pose_bone ||
162 sync_types->sequence;
163}
164
174
175static void selected_items_init(SelectedItems *selected_items)
176{
180}
181
182static void selected_items_free(SelectedItems *selected_items)
183{
184 BLI_gset_free(selected_items->objects, nullptr);
185 BLI_gset_free(selected_items->edit_bones, nullptr);
186 BLI_gset_free(selected_items->pose_bones, nullptr);
187}
188
189/* Check if an instance of this object been selected by the sync */
190static bool is_object_selected(GSet *selected_objects, Base *base)
191{
192 return BLI_gset_haskey(selected_objects, base);
193}
194
195/* Check if an instance of this edit bone been selected by the sync */
196static bool is_edit_bone_selected(GSet *selected_ebones, EditBone *ebone)
197{
198 return BLI_gset_haskey(selected_ebones, ebone);
199}
200
201/* Check if an instance of this pose bone been selected by the sync */
202static bool is_pose_bone_selected(GSet *selected_pbones, bPoseChannel *pchan)
203{
204 return BLI_gset_haskey(selected_pbones, pchan);
205}
206
207/* Add element's data to selected item set */
208static void add_selected_item(GSet *selected, void *data)
209{
210 BLI_gset_add(selected, data);
211}
212
214 TreeElement *te,
215 TreeStoreElem *tselem,
216 GSet *selected_objects)
217{
218 Object *ob = (Object *)tselem->id;
219 Base *base = (te->directdata) ? (Base *)te->directdata :
220 BKE_view_layer_base_find(view_layer, ob);
221
222 if (base && (base->flag & BASE_SELECTABLE)) {
223 if (tselem->flag & TSE_SELECTED) {
225
226 add_selected_item(selected_objects, base);
227 }
228 else if (!is_object_selected(selected_objects, base)) {
230 }
231 }
232}
233
235 ViewLayer *view_layer,
236 TreeElement *te,
237 TreeStoreElem *tselem,
238 GSet *selected_ebones)
239{
240 bArmature *arm = (bArmature *)tselem->id;
241 EditBone *ebone = (EditBone *)te->directdata;
242
243 short bone_flag = ebone->flag;
244
245 if (EBONE_SELECTABLE(arm, ebone)) {
246 if (tselem->flag & TSE_SELECTED) {
247 ED_armature_ebone_select_set(ebone, true);
248 add_selected_item(selected_ebones, ebone);
249 }
250 else if (!is_edit_bone_selected(selected_ebones, ebone)) {
251 /* Don't flush to parent bone tip, synced selection is iterating the whole tree so
252 * deselecting potential children with `ED_armature_ebone_select_set(ebone, false)`
253 * would leave its own tip deselected. */
255 }
256 }
257
258 /* Tag if selection changed */
259 if (bone_flag != ebone->flag) {
260 BKE_view_layer_synced_ensure(scene, view_layer);
261 Object *obedit = BKE_view_layer_edit_object_get(view_layer);
264 }
265}
266
268 TreeStoreElem *tselem,
269 GSet *selected_pbones)
270{
271 Object *ob = (Object *)tselem->id;
272 bArmature *arm = static_cast<bArmature *>(ob->data);
273 bPoseChannel *pchan = (bPoseChannel *)te->directdata;
274
275 short bone_flag = pchan->bone->flag;
276
277 if (PBONE_SELECTABLE(arm, pchan->bone)) {
278 if (tselem->flag & TSE_SELECTED) {
279 pchan->bone->flag |= BONE_SELECTED;
280
281 add_selected_item(selected_pbones, pchan);
282 }
283 else if (!is_pose_bone_selected(selected_pbones, pchan)) {
284 pchan->bone->flag &= ~BONE_SELECTED;
285 }
286 }
287
288 /* Tag if selection changed */
289 if (bone_flag != pchan->bone->flag) {
292 }
293}
294
296{
297 const TreeStoreElem *tselem = TREESTORE(te);
298
300 Sequence *seq = &te_sequence->get_sequence();
301
302 if (tselem->flag & TSE_ACTIVE) {
303 SEQ_select_active_set(scene, seq);
304 }
305
306 if (tselem->flag & TSE_SELECTED) {
307 seq->flag |= SELECT;
308 }
309 else {
310 seq->flag &= ~SELECT;
311 }
312}
313
316 ViewLayer *view_layer,
317 ListBase *tree,
318 const SyncSelectTypes *sync_types,
319 SelectedItems *selected_items)
320{
321
323 TreeStoreElem *tselem = TREESTORE(te);
324
325 if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) {
326 if (sync_types->object) {
327 outliner_select_sync_to_object(view_layer, te, tselem, selected_items->objects);
328 }
329 }
330 else if (tselem->type == TSE_EBONE) {
331 if (sync_types->edit_bone) {
333 scene, view_layer, te, tselem, selected_items->edit_bones);
334 }
335 }
336 else if (tselem->type == TSE_POSE_CHANNEL) {
337 if (sync_types->pose_bone) {
338 outliner_select_sync_to_pose_bone(te, tselem, selected_items->pose_bones);
339 }
340 }
341 else if (tselem->type == TSE_SEQUENCE) {
342 if (sync_types->sequence) {
344 }
345 }
346
348 scene, view_layer, &te->subtree, sync_types, selected_items);
349 }
350}
351
352} // namespace blender::ed::outliner
353
355{
356 using namespace blender::ed::outliner;
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,
364 {
365 return;
366 }
367
368 Scene *scene = CTX_data_scene(C);
369 ViewLayer *view_layer = CTX_data_view_layer(C);
370
371 SyncSelectTypes sync_types;
372 outliner_sync_select_from_outliner_set_types(C, space_outliner, &sync_types);
373
374 /* To store elements that have been selected to prevent linked object sync errors */
375 SelectedItems selected_items;
376
377 selected_items_init(&selected_items);
378
379 outliner_sync_selection_from_outliner(
380 scene, view_layer, &space_outliner->tree, &sync_types, &selected_items);
381
382 selected_items_free(&selected_items);
383
384 /* Tag for updates and clear dirty flag to prevent a sync to the outliner on draw. */
385 if (sync_types.object) {
386 space_outliner->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_OBJECT;
389 }
390 else if (sync_types.edit_bone) {
391 space_outliner->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE;
392 }
393 else if (sync_types.pose_bone) {
394 space_outliner->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE;
395 }
396 if (sync_types.sequence) {
397 space_outliner->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE;
399 }
400}
401
402namespace blender::ed::outliner {
403
405 ViewLayer *view_layer,
406 Object *obact,
407 TreeElement *te,
408 TreeStoreElem *tselem)
409{
410 Object *ob = (Object *)tselem->id;
411 BKE_view_layer_synced_ensure(scene, view_layer);
412 Base *base = (te->directdata) ? (Base *)te->directdata :
413 BKE_view_layer_base_find(view_layer, ob);
414 const bool is_selected = (base != nullptr) && ((base->flag & BASE_SELECTED) != 0);
415
416 if (base && (ob == obact)) {
417 tselem->flag |= TSE_ACTIVE;
418 }
419 else {
420 tselem->flag &= ~TSE_ACTIVE;
421 }
422
423 if (is_selected) {
424 tselem->flag |= TSE_SELECTED;
425 }
426 else {
427 tselem->flag &= ~TSE_SELECTED;
428 }
429}
430
432 TreeElement *te,
433 TreeStoreElem *tselem)
434{
435 EditBone *ebone = (EditBone *)te->directdata;
436
437 if (ebone == ebone_active) {
438 tselem->flag |= TSE_ACTIVE;
439 }
440 else {
441 tselem->flag &= ~TSE_ACTIVE;
442 }
443
444 if (ebone->flag & BONE_SELECTED) {
445 tselem->flag |= TSE_SELECTED;
446 }
447 else {
448 tselem->flag &= ~TSE_SELECTED;
449 }
450}
451
453 TreeElement *te,
454 TreeStoreElem *tselem)
455{
456 bPoseChannel *pchan = (bPoseChannel *)te->directdata;
457 Bone *bone = pchan->bone;
458
459 if (pchan == pchan_active) {
460 tselem->flag |= TSE_ACTIVE;
461 }
462 else {
463 tselem->flag &= ~TSE_ACTIVE;
464 }
465
466 if (bone->flag & BONE_SELECTED) {
467 tselem->flag |= TSE_SELECTED;
468 }
469 else {
470 tselem->flag &= ~TSE_SELECTED;
471 }
472}
473
474static void outliner_select_sync_from_sequence(Sequence *sequence_active, const TreeElement *te)
475{
476 TreeStoreElem *tselem = TREESTORE(te);
477
479 const Sequence *seq = &te_sequence->get_sequence();
480
481 if (seq == sequence_active) {
482 tselem->flag |= TSE_ACTIVE;
483 }
484 else {
485 tselem->flag &= ~TSE_ACTIVE;
486 }
487
488 if (seq->flag & SELECT) {
489 tselem->flag |= TSE_SELECTED;
490 }
491 else {
492 tselem->flag &= ~TSE_SELECTED;
493 }
494}
495
506
509 ViewLayer *view_layer,
510 SpaceOutliner *space_outliner,
511 ListBase *tree,
512 SyncSelectActiveData *active_data,
513 const SyncSelectTypes *sync_types)
514{
516 TreeStoreElem *tselem = TREESTORE(te);
517
518 if ((tselem->type == TSE_SOME_ID) && te->idcode == ID_OB) {
519 if (sync_types->object) {
520 outliner_select_sync_from_object(scene, view_layer, active_data->object, te, tselem);
521 }
522 }
523 else if (tselem->type == TSE_EBONE) {
524 if (sync_types->edit_bone) {
525 outliner_select_sync_from_edit_bone(active_data->edit_bone, te, tselem);
526 }
527 }
528 else if (tselem->type == TSE_POSE_CHANNEL) {
529 if (sync_types->pose_bone) {
530 outliner_select_sync_from_pose_bone(active_data->pose_channel, te, tselem);
531 }
532 }
533 else if (tselem->type == TSE_SEQUENCE) {
534 if (sync_types->sequence) {
536 }
537 }
538 else {
539 tselem->flag &= ~(TSE_SELECTED | TSE_ACTIVE);
540 }
541
542 /* Sync subtree elements */
544 scene, view_layer, space_outliner, &te->subtree, active_data, sync_types);
545 }
546}
547
548/* Get active data from context */
550{
551 Scene *scene = CTX_data_scene(C);
552 ViewLayer *view_layer = CTX_data_view_layer(C);
553 BKE_view_layer_synced_ensure(scene, view_layer);
554 active_data->object = BKE_view_layer_active_object_get(view_layer);
555 active_data->edit_bone = CTX_data_active_bone(C);
556 active_data->pose_channel = CTX_data_active_pose_bone(C);
557 active_data->sequence = SEQ_select_active_get(scene);
558}
559
560void outliner_sync_selection(const bContext *C, SpaceOutliner *space_outliner)
561{
562 /* Set which types of data to sync from sync dirty flag and outliner display mode */
563 SyncSelectTypes sync_types;
564 const bool sync_required = outliner_sync_select_to_outliner_set_types(
565 C, space_outliner, &sync_types);
566
567 if (sync_required) {
568 const Scene *scene = CTX_data_scene(C);
569 ViewLayer *view_layer = CTX_data_view_layer(C);
570
571 /* Store active object, bones, and sequence */
572 SyncSelectActiveData active_data;
573 get_sync_select_active_data(C, &active_data);
574
576 scene, view_layer, space_outliner, &space_outliner->tree, &active_data, &sync_types);
577
578 /* Keep any un-synced data in the dirty flag. */
579 if (sync_types.object) {
580 space_outliner->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_OBJECT;
581 }
582 if (sync_types.edit_bone) {
583 space_outliner->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_EDIT_BONE;
584 }
585 if (sync_types.pose_bone) {
586 space_outliner->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_POSE_BONE;
587 }
588 if (sync_types.sequence) {
589 space_outliner->sync_select_dirty &= ~WM_OUTLINER_SYNC_SELECT_FROM_SEQUENCE;
590 }
591 }
592}
593
594} // namespace blender::ed::outliner
C++ functions to deal with Armature collections (i.e. the successor of bone layers).
#define PBONE_SELECTABLE(arm, bone)
bPoseChannel * CTX_data_active_pose_bone(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
EditBone * CTX_data_active_bone(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
Object * BKE_view_layer_edit_object_get(const ViewLayer *view_layer)
struct GSet GSet
Definition BLI_ghash.h:341
bool BLI_gset_haskey(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:1004
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:944
bool BLI_ghashutil_ptrcmp(const void *a, const void *b)
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.c:1034
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.c:966
#define LISTBASE_FOREACH(type, var, list)
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1068
@ ID_OB
@ BONE_ROOTSEL
@ BONE_SELECTED
@ BONE_TIPSEL
@ OB_MODE_POSE
@ OB_ARMATURE
@ TSE_POSE_CHANNEL
@ TSE_SEQUENCE
@ TSE_EBONE
@ TSE_SOME_ID
@ TSE_SELECTED
@ TSE_ACTIVE
#define BASE_SELECTED(v3d, base)
#define BASE_SELECTABLE(v3d, base)
@ 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)
#define ND_SEQUENCER
Definition WM_types.hh:404
#define ND_OB_SELECT
Definition WM_types.hh:409
#define NC_SCENE
Definition WM_types.hh:345
#define ND_BONE_SELECT
Definition WM_types.hh:427
#define NC_OBJECT
Definition WM_types.hh:346
#define NA_SELECTED
Definition WM_types.hh:555
void ED_armature_ebone_select_set(EditBone *ebone, bool select)
#define SELECT
KDTree_3d * tree
void base_select(Base *base, eObjectSelect_Mode mode)
static void add_selected_item(GSet *selected, void *data)
static bool is_edit_bone_selected(GSet *selected_ebones, EditBone *ebone)
static void outliner_select_sync_from_pose_bone(bPoseChannel *pchan_active, TreeElement *te, TreeStoreElem *tselem)
static void outliner_sync_select_from_outliner_set_types(bContext *C, SpaceOutliner *space_outliner, SyncSelectTypes *sync_types)
static void outliner_select_sync_from_sequence(Sequence *sequence_active, const TreeElement *te)
static void outliner_select_sync_to_sequence(Scene *scene, const TreeElement *te)
static void outliner_select_sync_from_object(const Scene *scene, ViewLayer *view_layer, Object *obact, TreeElement *te, TreeStoreElem *tselem)
static void selected_items_init(SelectedItems *selected_items)
static void outliner_select_sync_from_edit_bone(EditBone *ebone_active, TreeElement *te, TreeStoreElem *tselem)
static void outliner_select_sync_to_pose_bone(TreeElement *te, TreeStoreElem *tselem, GSet *selected_pbones)
static bool is_object_selected(GSet *selected_objects, Base *base)
static void get_sync_select_active_data(const bContext *C, SyncSelectActiveData *active_data)
static void outliner_sync_selection_to_outliner(const Scene *scene, ViewLayer *view_layer, SpaceOutliner *space_outliner, ListBase *tree, SyncSelectActiveData *active_data, const SyncSelectTypes *sync_types)
void outliner_viewcontext_init(const bContext *C, TreeViewContext *tvc)
static void selected_items_free(SelectedItems *selected_items)
static void outliner_select_sync_to_edit_bone(const Scene *scene, ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, GSet *selected_ebones)
void outliner_sync_selection(const bContext *C, SpaceOutliner *space_outliner)
TreeElementT * tree_element_cast(const TreeElement *te)
static void outliner_select_sync_to_object(ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, GSet *selected_objects)
static void outliner_sync_selection_from_outliner(Scene *scene, ViewLayer *view_layer, ListBase *tree, const SyncSelectTypes *sync_types, SelectedItems *selected_items)
static bool is_pose_bone_selected(GSet *selected_pbones, bPoseChannel *pchan)
static bool outliner_sync_select_to_outliner_set_types(const bContext *C, SpaceOutliner *space_outliner, SyncSelectTypes *sync_types)
#define TREESTORE(a)
void ED_outliner_select_sync_from_object_tag(bContext *C)
void ED_outliner_select_sync_from_edit_bone_tag(bContext *C)
void ED_outliner_select_sync_from_pose_bone_tag(bContext *C)
void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *space_outliner)
void ED_outliner_select_sync_from_all_tag(bContext *C)
void ED_outliner_select_sync_from_sequence_tag(bContext *C)
bool ED_outliner_select_sync_is_dirty(const bContext *C)
void ED_outliner_select_sync_flag_outliners(const bContext *C)
void SEQ_select_active_set(Scene *scene, Sequence *seq)
Sequence * SEQ_select_active_get(const Scene *scene)
short flag
void * first
ListBase screens
Definition BKE_main.hh:225
struct Bone * bone
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)