Blender V5.0
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
8
10#include "DNA_layer_types.h"
11#include "DNA_outliner_types.h"
12#include "DNA_screen_types.h"
13#include "DNA_sequence_types.h"
14#include "DNA_space_types.h"
15#include "DNA_workspace_types.h"
16
17#include "BLI_listbase.h"
18
19#include "BKE_armature.hh"
20#include "BKE_context.hh"
21#include "BKE_layer.hh"
22#include "BKE_main.hh"
23
24#include "DEG_depsgraph.hh"
25
26#include "ED_armature.hh"
27#include "ED_object.hh"
28#include "ED_outliner.hh"
29
30#include "SEQ_select.hh"
31
32#include "WM_api.hh"
33#include "WM_types.hh"
34
35#include "ANIM_armature.hh"
36
38
39#include "outliner_intern.hh"
40
46
52
58
64
70
76
78{
79 Main *bmain = CTX_data_main(C);
81
82 for (bScreen *screen = static_cast<bScreen *>(bmain->screens.first); screen;
83 screen = static_cast<bScreen *>(screen->id.next))
84 {
85 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
86 LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
87 if (sl->spacetype == SPACE_OUTLINER) {
88 SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
89
90 space_outliner->sync_select_dirty |= wm->outliner_sync_select_dirty;
91 }
92 }
93 }
94 }
95
96 /* Clear global sync flag */
98}
99
100namespace blender::ed::outliner {
101
113
119 SpaceOutliner *space_outliner,
120 SyncSelectTypes *sync_types)
121{
122 TreeViewContext tvc;
124
125 const bool sequence_view = space_outliner->outlinevis == SO_SEQUENCE;
126
127 sync_types->object = !sequence_view;
128 sync_types->edit_bone = !sequence_view && (tvc.ob_edit && tvc.ob_edit->type == OB_ARMATURE);
129 sync_types->pose_bone = !sequence_view && (tvc.ob_pose && tvc.ob_pose->mode == OB_MODE_POSE);
130 sync_types->seq_strip = sequence_view;
131}
132
139 SpaceOutliner *space_outliner,
140 SyncSelectTypes *sync_types)
141{
142 const bool sequence_view = space_outliner->outlinevis == SO_SEQUENCE;
143
144 sync_types->object = !sequence_view &&
146 sync_types->edit_bone = !sequence_view && (tvc.ob_edit && tvc.ob_edit->type == OB_ARMATURE) &&
147 (space_outliner->sync_select_dirty &
149 sync_types->pose_bone = !sequence_view && (tvc.ob_pose && tvc.ob_pose->mode == OB_MODE_POSE) &&
150 (space_outliner->sync_select_dirty &
152 sync_types->seq_strip = sequence_view && (space_outliner->sync_select_dirty &
154
155 return sync_types->object || sync_types->edit_bone || sync_types->pose_bone ||
156 sync_types->seq_strip;
157}
158
168
170 TreeElement *te,
171 TreeStoreElem *tselem,
172 Set<Base *> &selected_objects)
173{
174 Object *ob = (Object *)tselem->id;
175 Base *base = (te->directdata) ? (Base *)te->directdata :
176 BKE_view_layer_base_find(view_layer, ob);
177
178 if (base && (base->flag & BASE_SELECTABLE)) {
179 if (tselem->flag & TSE_SELECTED) {
181 selected_objects.add(base);
182 }
183 else if (!selected_objects.contains(base)) {
185 }
186 }
187}
188
190 ViewLayer *view_layer,
191 TreeElement *te,
192 TreeStoreElem *tselem,
193 Set<EditBone *> &selected_ebones)
194{
195 bArmature *arm = (bArmature *)tselem->id;
196 EditBone *ebone = (EditBone *)te->directdata;
197
198 short bone_flag = ebone->flag;
199
200 if (EBONE_SELECTABLE(arm, ebone)) {
201 if (tselem->flag & TSE_SELECTED) {
202 ED_armature_ebone_select_set(ebone, true);
203 selected_ebones.add(ebone);
204 }
205 else if (!selected_ebones.contains(ebone)) {
206 /* Don't flush to parent bone tip, synced selection is iterating the whole tree so
207 * deselecting potential children with `ED_armature_ebone_select_set(ebone, false)`
208 * would leave its own tip deselected. */
210 }
211 }
212
213 /* Tag if selection changed */
214 if (bone_flag != ebone->flag) {
215 BKE_view_layer_synced_ensure(scene, view_layer);
216 Object *obedit = BKE_view_layer_edit_object_get(view_layer);
219 }
220}
221
223 TreeStoreElem *tselem,
224 Set<bPoseChannel *> &selected_pbones)
225{
226 Object *ob = (Object *)tselem->id;
227 bArmature *arm = static_cast<bArmature *>(ob->data);
228 bPoseChannel *pchan = (bPoseChannel *)te->directdata;
229
230 short bone_flag = pchan->flag;
231
232 if (blender::animrig::bone_is_selectable(arm, pchan)) {
233 if (tselem->flag & TSE_SELECTED) {
234 pchan->flag |= POSE_SELECTED;
235
236 selected_pbones.add(pchan);
237 }
238 else if (!selected_pbones.contains(pchan)) {
239 pchan->flag &= ~POSE_SELECTED;
240 }
241 }
242
243 /* Tag if selection changed */
244 if (bone_flag != pchan->flag) {
247 }
248}
249
250static void outliner_select_sync_to_strip(WorkSpace *workspace, const TreeElement *te)
251{
252 Scene *sequencer_scene = workspace->sequencer_scene;
253 if (!sequencer_scene) {
254 return;
255 }
256 const TreeStoreElem *tselem = TREESTORE(te);
257
259 Strip *strip = &te_strip->get_strip();
260
261 if (tselem->flag & TSE_ACTIVE) {
262 seq::select_active_set(sequencer_scene, strip);
263 }
264
265 if (tselem->flag & TSE_SELECTED) {
266 strip->flag |= SELECT;
267 }
268 else {
269 strip->flag &= ~SELECT;
270 }
271}
272
275 Scene *scene,
276 ViewLayer *view_layer,
277 ListBase *tree,
278 const SyncSelectTypes *sync_types,
279 SelectedItems *selected_items)
280{
281
283 TreeStoreElem *tselem = TREESTORE(te);
284
285 if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) {
286 if (sync_types->object) {
287 outliner_select_sync_to_object(view_layer, te, tselem, selected_items->objects);
288 }
289 }
290 else if (tselem->type == TSE_EBONE) {
291 if (sync_types->edit_bone) {
293 scene, view_layer, te, tselem, selected_items->edit_bones);
294 }
295 }
296 else if (tselem->type == TSE_POSE_CHANNEL) {
297 if (sync_types->pose_bone) {
298 outliner_select_sync_to_pose_bone(te, tselem, selected_items->pose_bones);
299 }
300 }
301 else if (tselem->type == TSE_STRIP) {
302 if (sync_types->seq_strip) {
303 outliner_select_sync_to_strip(workspace, te);
304 }
305 }
306
308 workspace, scene, view_layer, &te->subtree, sync_types, selected_items);
309 }
310}
311
312} // namespace blender::ed::outliner
313
315{
316 using namespace blender::ed::outliner;
317
318 /* Don't sync if not checked or in certain outliner display modes */
319 if (!(space_outliner->flag & SO_SYNC_SELECT) || ELEM(space_outliner->outlinevis,
324 {
325 return;
326 }
327
328 Scene *scene = CTX_data_scene(C);
329 ViewLayer *view_layer = CTX_data_view_layer(C);
330
331 SyncSelectTypes sync_types;
332 outliner_sync_select_from_outliner_set_types(C, space_outliner, &sync_types);
333
334 /* To store elements that have been selected to prevent linked object sync errors */
335 SelectedItems selected_items;
337 CTX_wm_workspace(C), scene, view_layer, &space_outliner->tree, &sync_types, &selected_items);
338
339 /* Tag for updates and clear dirty flag to prevent a sync to the outliner on draw. */
340 if (sync_types.object) {
344 }
345 else if (sync_types.edit_bone) {
347 }
348 else if (sync_types.pose_bone) {
350 }
351 if (sync_types.seq_strip) {
354 }
355}
356
357namespace blender::ed::outliner {
358
360 ViewLayer *view_layer,
361 Object *obact,
362 TreeElement *te,
363 TreeStoreElem *tselem)
364{
365 Object *ob = (Object *)tselem->id;
366 BKE_view_layer_synced_ensure(scene, view_layer);
367 Base *base = (te->directdata) ? (Base *)te->directdata :
368 BKE_view_layer_base_find(view_layer, ob);
369 const bool is_selected = (base != nullptr) && ((base->flag & BASE_SELECTED) != 0);
370
371 if (base && (ob == obact)) {
372 tselem->flag |= TSE_ACTIVE;
373 }
374 else {
375 tselem->flag &= ~TSE_ACTIVE;
376 }
377
378 if (is_selected) {
379 tselem->flag |= TSE_SELECTED;
380 }
381 else {
382 tselem->flag &= ~TSE_SELECTED;
383 }
384}
385
387 TreeElement *te,
388 TreeStoreElem *tselem)
389{
390 EditBone *ebone = (EditBone *)te->directdata;
391
392 if (ebone == ebone_active) {
393 tselem->flag |= TSE_ACTIVE;
394 }
395 else {
396 tselem->flag &= ~TSE_ACTIVE;
397 }
398
399 if (ebone->flag & BONE_SELECTED) {
400 tselem->flag |= TSE_SELECTED;
401 }
402 else {
403 tselem->flag &= ~TSE_SELECTED;
404 }
405}
406
408 TreeElement *te,
409 TreeStoreElem *tselem)
410{
411 bPoseChannel *pchan = (bPoseChannel *)te->directdata;
412
413 if (pchan == pchan_active) {
414 tselem->flag |= TSE_ACTIVE;
415 }
416 else {
417 tselem->flag &= ~TSE_ACTIVE;
418 }
419
420 if (pchan->flag & POSE_SELECTED) {
421 tselem->flag |= TSE_SELECTED;
422 }
423 else {
424 tselem->flag &= ~TSE_SELECTED;
425 }
426}
427
428static void outliner_select_sync_from_strip(Strip *strip_active, const TreeElement *te)
429{
430 TreeStoreElem *tselem = TREESTORE(te);
431
433 const Strip *strip = &te_strip->get_strip();
434
435 if (strip == strip_active) {
436 tselem->flag |= TSE_ACTIVE;
437 }
438 else {
439 tselem->flag &= ~TSE_ACTIVE;
440 }
441
442 if (strip->flag & SELECT) {
443 tselem->flag |= TSE_SELECTED;
444 }
445 else {
446 tselem->flag &= ~TSE_SELECTED;
447 }
448}
449
460
463 ViewLayer *view_layer,
464 SpaceOutliner *space_outliner,
465 ListBase *tree,
466 SyncSelectActiveData *active_data,
467 const SyncSelectTypes *sync_types)
468{
470 TreeStoreElem *tselem = TREESTORE(te);
471
472 if ((tselem->type == TSE_SOME_ID) && te->idcode == ID_OB) {
473 if (sync_types->object) {
474 outliner_select_sync_from_object(scene, view_layer, active_data->object, te, tselem);
475 }
476 }
477 else if (tselem->type == TSE_EBONE) {
478 if (sync_types->edit_bone) {
479 outliner_select_sync_from_edit_bone(active_data->edit_bone, te, tselem);
480 }
481 }
482 else if (tselem->type == TSE_POSE_CHANNEL) {
483 if (sync_types->pose_bone) {
484 outliner_select_sync_from_pose_bone(active_data->pose_channel, te, tselem);
485 }
486 }
487 else if (tselem->type == TSE_STRIP) {
488 if (sync_types->seq_strip) {
489 outliner_select_sync_from_strip(active_data->strip, te);
490 }
491 }
492 else {
493 tselem->flag &= ~(TSE_SELECTED | TSE_ACTIVE);
494 }
495
496 /* Sync subtree elements */
498 scene, view_layer, space_outliner, &te->subtree, active_data, sync_types);
499 }
500}
501
502/* Get active data from context */
504{
505 Scene *scene = CTX_data_scene(C);
506 Scene *sequencer_scene = CTX_data_sequencer_scene(C);
507 ViewLayer *view_layer = CTX_data_view_layer(C);
508 BKE_view_layer_synced_ensure(scene, view_layer);
509 active_data->object = BKE_view_layer_active_object_get(view_layer);
510 active_data->edit_bone = CTX_data_active_bone(C);
512 active_data->strip = sequencer_scene ? seq::select_active_get(sequencer_scene) : nullptr;
513}
514
516 const TreeViewContext &tvc,
517 SpaceOutliner *space_outliner)
518{
519 /* Set which types of data to sync from sync dirty flag and outliner display mode */
520 SyncSelectTypes sync_types;
521 const bool sync_required = outliner_sync_select_to_outliner_set_types(
522 tvc, space_outliner, &sync_types);
523
524 if (sync_required) {
525 /* Store active object, bones, and strip */
526 SyncSelectActiveData active_data;
527 get_sync_select_active_data(C, &active_data);
528
530 tvc.view_layer,
531 space_outliner,
532 &space_outliner->tree,
533 &active_data,
534 &sync_types);
535
536 /* Keep any un-synced data in the dirty flag. */
537 if (sync_types.object) {
539 }
540 if (sync_types.edit_bone) {
542 }
543 if (sync_types.pose_bone) {
545 }
546 if (sync_types.seq_strip) {
548 }
549 }
550}
551
552} // namespace blender::ed::outliner
Functions to deal with Armatures.
WorkSpace * CTX_wm_workspace(const bContext *C)
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)
Scene * CTX_data_sequencer_scene(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)
#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:1101
@ ID_OB
@ POSE_SELECTED
@ BONE_ROOTSEL
@ BONE_SELECTED
@ BONE_TIPSEL
@ OB_MODE_POSE
@ OB_ARMATURE
@ TSE_POSE_CHANNEL
@ TSE_STRIP
@ 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 C
Definition RandGen.cpp:29
#define ND_SEQUENCER
Definition WM_types.hh:437
#define ND_OB_SELECT
Definition WM_types.hh:442
#define NC_SCENE
Definition WM_types.hh:378
#define ND_BONE_SELECT
Definition WM_types.hh:460
#define NC_OBJECT
Definition WM_types.hh:379
#define NA_SELECTED
Definition WM_types.hh:589
void ED_armature_ebone_select_set(EditBone *ebone, bool select)
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool add(const Key &key)
Definition BLI_set.hh:248
#define SELECT
KDTree_3d * tree
bool bone_is_selectable(const bArmature *armature, const bPoseChannel *pchan)
void base_select(Base *base, eObjectSelect_Mode mode)
static void outliner_sync_selection_from_outliner(WorkSpace *workspace, Scene *scene, ViewLayer *view_layer, ListBase *tree, const SyncSelectTypes *sync_types, SelectedItems *selected_items)
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_object(const Scene *scene, ViewLayer *view_layer, Object *obact, TreeElement *te, TreeStoreElem *tselem)
static void outliner_select_sync_to_edit_bone(const Scene *scene, ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, Set< EditBone * > &selected_ebones)
static void outliner_select_sync_to_strip(WorkSpace *workspace, const TreeElement *te)
static void outliner_select_sync_from_edit_bone(EditBone *ebone_active, TreeElement *te, TreeStoreElem *tselem)
static void outliner_select_sync_from_strip(Strip *strip_active, const TreeElement *te)
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)
static bool outliner_sync_select_to_outliner_set_types(const TreeViewContext &tvc, SpaceOutliner *space_outliner, SyncSelectTypes *sync_types)
static void outliner_select_sync_to_pose_bone(TreeElement *te, TreeStoreElem *tselem, Set< bPoseChannel * > &selected_pbones)
void outliner_viewcontext_init(const bContext *C, TreeViewContext *tvc)
TreeElementT * tree_element_cast(const TreeElement *te)
void outliner_sync_selection(const bContext *C, const TreeViewContext &tvc, SpaceOutliner *space_outliner)
static void outliner_select_sync_to_object(ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, Set< Base * > &selected_objects)
Strip * select_active_get(const Scene *scene)
void select_active_set(Scene *scene, Strip *strip)
#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_sequence_tag(const bContext *C)
void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *space_outliner)
void ED_outliner_select_sync_from_all_tag(bContext *C)
bool ED_outliner_select_sync_is_dirty(const bContext *C)
void ED_outliner_select_sync_flag_outliners(const bContext *C)
short flag
void * first
ListBase screens
Definition BKE_main.hh:292
struct Scene * sequencer_scene
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)