Blender V4.5
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
16#include "BLI_listbase.h"
17
18#include "BKE_armature.hh"
19#include "BKE_context.hh"
20#include "BKE_layer.hh"
21#include "BKE_main.hh"
22
23#include "DEG_depsgraph.hh"
24
25#include "ED_armature.hh"
26#include "ED_object.hh"
27#include "ED_outliner.hh"
28
29#include "SEQ_select.hh"
30
31#include "WM_api.hh"
32#include "WM_types.hh"
33
34#include "ANIM_armature.hh"
35
37
38#include "outliner_intern.hh"
39
45
51
57
63
69
75
77{
78 Main *bmain = CTX_data_main(C);
80
81 for (bScreen *screen = static_cast<bScreen *>(bmain->screens.first); screen;
82 screen = static_cast<bScreen *>(screen->id.next))
83 {
84 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
85 LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
86 if (sl->spacetype == SPACE_OUTLINER) {
87 SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
88
89 space_outliner->sync_select_dirty |= wm->outliner_sync_select_dirty;
90 }
91 }
92 }
93 }
94
95 /* Clear global sync flag */
97}
98
99namespace blender::ed::outliner {
100
112
118 SpaceOutliner *space_outliner,
119 SyncSelectTypes *sync_types)
120{
121 TreeViewContext tvc;
123
124 const bool sequence_view = space_outliner->outlinevis == SO_SEQUENCE;
125
126 sync_types->object = !sequence_view;
127 sync_types->edit_bone = !sequence_view && (tvc.ob_edit && tvc.ob_edit->type == OB_ARMATURE);
128 sync_types->pose_bone = !sequence_view && (tvc.ob_pose && tvc.ob_pose->mode == OB_MODE_POSE);
129 sync_types->seq_strip = sequence_view;
130}
131
138 SpaceOutliner *space_outliner,
139 SyncSelectTypes *sync_types)
140{
141 const bool sequence_view = space_outliner->outlinevis == SO_SEQUENCE;
142
143 sync_types->object = !sequence_view &&
145 sync_types->edit_bone = !sequence_view && (tvc.ob_edit && tvc.ob_edit->type == OB_ARMATURE) &&
146 (space_outliner->sync_select_dirty &
148 sync_types->pose_bone = !sequence_view && (tvc.ob_pose && tvc.ob_pose->mode == OB_MODE_POSE) &&
149 (space_outliner->sync_select_dirty &
151 sync_types->seq_strip = sequence_view && (space_outliner->sync_select_dirty &
153
154 return sync_types->object || sync_types->edit_bone || sync_types->pose_bone ||
155 sync_types->seq_strip;
156}
157
167
169 TreeElement *te,
170 TreeStoreElem *tselem,
171 Set<Base *> &selected_objects)
172{
173 Object *ob = (Object *)tselem->id;
174 Base *base = (te->directdata) ? (Base *)te->directdata :
175 BKE_view_layer_base_find(view_layer, ob);
176
177 if (base && (base->flag & BASE_SELECTABLE)) {
178 if (tselem->flag & TSE_SELECTED) {
180 selected_objects.add(base);
181 }
182 else if (!selected_objects.contains(base)) {
184 }
185 }
186}
187
189 ViewLayer *view_layer,
190 TreeElement *te,
191 TreeStoreElem *tselem,
192 Set<EditBone *> &selected_ebones)
193{
194 bArmature *arm = (bArmature *)tselem->id;
195 EditBone *ebone = (EditBone *)te->directdata;
196
197 short bone_flag = ebone->flag;
198
199 if (EBONE_SELECTABLE(arm, ebone)) {
200 if (tselem->flag & TSE_SELECTED) {
201 ED_armature_ebone_select_set(ebone, true);
202 selected_ebones.add(ebone);
203 }
204 else if (!selected_ebones.contains(ebone)) {
205 /* Don't flush to parent bone tip, synced selection is iterating the whole tree so
206 * deselecting potential children with `ED_armature_ebone_select_set(ebone, false)`
207 * would leave its own tip deselected. */
209 }
210 }
211
212 /* Tag if selection changed */
213 if (bone_flag != ebone->flag) {
214 BKE_view_layer_synced_ensure(scene, view_layer);
215 Object *obedit = BKE_view_layer_edit_object_get(view_layer);
218 }
219}
220
222 TreeStoreElem *tselem,
223 Set<bPoseChannel *> &selected_pbones)
224{
225 Object *ob = (Object *)tselem->id;
226 bArmature *arm = static_cast<bArmature *>(ob->data);
227 bPoseChannel *pchan = (bPoseChannel *)te->directdata;
228
229 short bone_flag = pchan->bone->flag;
230
231 if (PBONE_SELECTABLE(arm, pchan->bone)) {
232 if (tselem->flag & TSE_SELECTED) {
233 pchan->bone->flag |= BONE_SELECTED;
234
235 selected_pbones.add(pchan);
236 }
237 else if (!selected_pbones.contains(pchan)) {
238 pchan->bone->flag &= ~BONE_SELECTED;
239 }
240 }
241
242 /* Tag if selection changed */
243 if (bone_flag != pchan->bone->flag) {
246 }
247}
248
249static void outliner_select_sync_to_strip(Scene *scene, const TreeElement *te)
250{
251 const TreeStoreElem *tselem = TREESTORE(te);
252
254 Strip *strip = &te_strip->get_strip();
255
256 if (tselem->flag & TSE_ACTIVE) {
257 seq::select_active_set(scene, strip);
258 }
259
260 if (tselem->flag & TSE_SELECTED) {
261 strip->flag |= SELECT;
262 }
263 else {
264 strip->flag &= ~SELECT;
265 }
266}
267
270 ViewLayer *view_layer,
271 ListBase *tree,
272 const SyncSelectTypes *sync_types,
273 SelectedItems *selected_items)
274{
275
277 TreeStoreElem *tselem = TREESTORE(te);
278
279 if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) {
280 if (sync_types->object) {
281 outliner_select_sync_to_object(view_layer, te, tselem, selected_items->objects);
282 }
283 }
284 else if (tselem->type == TSE_EBONE) {
285 if (sync_types->edit_bone) {
287 scene, view_layer, te, tselem, selected_items->edit_bones);
288 }
289 }
290 else if (tselem->type == TSE_POSE_CHANNEL) {
291 if (sync_types->pose_bone) {
292 outliner_select_sync_to_pose_bone(te, tselem, selected_items->pose_bones);
293 }
294 }
295 else if (tselem->type == TSE_STRIP) {
296 if (sync_types->seq_strip) {
298 }
299 }
300
302 scene, view_layer, &te->subtree, sync_types, selected_items);
303 }
304}
305
306} // namespace blender::ed::outliner
307
309{
310 using namespace blender::ed::outliner;
311
312 /* Don't sync if not checked or in certain outliner display modes */
313 if (!(space_outliner->flag & SO_SYNC_SELECT) || ELEM(space_outliner->outlinevis,
318 {
319 return;
320 }
321
322 Scene *scene = CTX_data_scene(C);
323 ViewLayer *view_layer = CTX_data_view_layer(C);
324
325 SyncSelectTypes sync_types;
326 outliner_sync_select_from_outliner_set_types(C, space_outliner, &sync_types);
327
328 /* To store elements that have been selected to prevent linked object sync errors */
329 SelectedItems selected_items;
331 scene, view_layer, &space_outliner->tree, &sync_types, &selected_items);
332
333 /* Tag for updates and clear dirty flag to prevent a sync to the outliner on draw. */
334 if (sync_types.object) {
338 }
339 else if (sync_types.edit_bone) {
341 }
342 else if (sync_types.pose_bone) {
344 }
345 if (sync_types.seq_strip) {
348 }
349}
350
351namespace blender::ed::outliner {
352
354 ViewLayer *view_layer,
355 Object *obact,
356 TreeElement *te,
357 TreeStoreElem *tselem)
358{
359 Object *ob = (Object *)tselem->id;
360 BKE_view_layer_synced_ensure(scene, view_layer);
361 Base *base = (te->directdata) ? (Base *)te->directdata :
362 BKE_view_layer_base_find(view_layer, ob);
363 const bool is_selected = (base != nullptr) && ((base->flag & BASE_SELECTED) != 0);
364
365 if (base && (ob == obact)) {
366 tselem->flag |= TSE_ACTIVE;
367 }
368 else {
369 tselem->flag &= ~TSE_ACTIVE;
370 }
371
372 if (is_selected) {
373 tselem->flag |= TSE_SELECTED;
374 }
375 else {
376 tselem->flag &= ~TSE_SELECTED;
377 }
378}
379
381 TreeElement *te,
382 TreeStoreElem *tselem)
383{
384 EditBone *ebone = (EditBone *)te->directdata;
385
386 if (ebone == ebone_active) {
387 tselem->flag |= TSE_ACTIVE;
388 }
389 else {
390 tselem->flag &= ~TSE_ACTIVE;
391 }
392
393 if (ebone->flag & BONE_SELECTED) {
394 tselem->flag |= TSE_SELECTED;
395 }
396 else {
397 tselem->flag &= ~TSE_SELECTED;
398 }
399}
400
402 TreeElement *te,
403 TreeStoreElem *tselem)
404{
405 bPoseChannel *pchan = (bPoseChannel *)te->directdata;
406 Bone *bone = pchan->bone;
407
408 if (pchan == pchan_active) {
409 tselem->flag |= TSE_ACTIVE;
410 }
411 else {
412 tselem->flag &= ~TSE_ACTIVE;
413 }
414
415 if (bone->flag & BONE_SELECTED) {
416 tselem->flag |= TSE_SELECTED;
417 }
418 else {
419 tselem->flag &= ~TSE_SELECTED;
420 }
421}
422
423static void outliner_select_sync_from_strip(Strip *strip_active, const TreeElement *te)
424{
425 TreeStoreElem *tselem = TREESTORE(te);
426
428 const Strip *strip = &te_strip->get_strip();
429
430 if (strip == strip_active) {
431 tselem->flag |= TSE_ACTIVE;
432 }
433 else {
434 tselem->flag &= ~TSE_ACTIVE;
435 }
436
437 if (strip->flag & SELECT) {
438 tselem->flag |= TSE_SELECTED;
439 }
440 else {
441 tselem->flag &= ~TSE_SELECTED;
442 }
443}
444
455
458 ViewLayer *view_layer,
459 SpaceOutliner *space_outliner,
460 ListBase *tree,
461 SyncSelectActiveData *active_data,
462 const SyncSelectTypes *sync_types)
463{
465 TreeStoreElem *tselem = TREESTORE(te);
466
467 if ((tselem->type == TSE_SOME_ID) && te->idcode == ID_OB) {
468 if (sync_types->object) {
469 outliner_select_sync_from_object(scene, view_layer, active_data->object, te, tselem);
470 }
471 }
472 else if (tselem->type == TSE_EBONE) {
473 if (sync_types->edit_bone) {
474 outliner_select_sync_from_edit_bone(active_data->edit_bone, te, tselem);
475 }
476 }
477 else if (tselem->type == TSE_POSE_CHANNEL) {
478 if (sync_types->pose_bone) {
479 outliner_select_sync_from_pose_bone(active_data->pose_channel, te, tselem);
480 }
481 }
482 else if (tselem->type == TSE_STRIP) {
483 if (sync_types->seq_strip) {
484 outliner_select_sync_from_strip(active_data->strip, te);
485 }
486 }
487 else {
488 tselem->flag &= ~(TSE_SELECTED | TSE_ACTIVE);
489 }
490
491 /* Sync subtree elements */
493 scene, view_layer, space_outliner, &te->subtree, active_data, sync_types);
494 }
495}
496
497/* Get active data from context */
499{
500 Scene *scene = CTX_data_scene(C);
501 ViewLayer *view_layer = CTX_data_view_layer(C);
502 BKE_view_layer_synced_ensure(scene, view_layer);
503 active_data->object = BKE_view_layer_active_object_get(view_layer);
504 active_data->edit_bone = CTX_data_active_bone(C);
506 active_data->strip = seq::select_active_get(scene);
507}
508
510 const TreeViewContext &tvc,
511 SpaceOutliner *space_outliner)
512{
513 /* Set which types of data to sync from sync dirty flag and outliner display mode */
514 SyncSelectTypes sync_types;
515 const bool sync_required = outliner_sync_select_to_outliner_set_types(
516 tvc, space_outliner, &sync_types);
517
518 if (sync_required) {
519 /* Store active object, bones, and strip */
520 SyncSelectActiveData active_data;
521 get_sync_select_active_data(C, &active_data);
522
524 tvc.view_layer,
525 space_outliner,
526 &space_outliner->tree,
527 &active_data,
528 &sync_types);
529
530 /* Keep any un-synced data in the dirty flag. */
531 if (sync_types.object) {
533 }
534 if (sync_types.edit_bone) {
536 }
537 if (sync_types.pose_bone) {
539 }
540 if (sync_types.seq_strip) {
542 }
543 }
544}
545
546} // namespace blender::ed::outliner
Functions to deal with Armatures.
#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)
#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:1009
@ ID_OB
@ 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:434
#define ND_OB_SELECT
Definition WM_types.hh:439
#define NC_SCENE
Definition WM_types.hh:375
#define ND_BONE_SELECT
Definition WM_types.hh:457
#define NC_OBJECT
Definition WM_types.hh:376
#define NA_SELECTED
Definition WM_types.hh:586
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
void base_select(Base *base, eObjectSelect_Mode mode)
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_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_strip(Scene *scene, const TreeElement *te)
static void outliner_sync_selection_from_outliner(Scene *scene, ViewLayer *view_layer, ListBase *tree, const SyncSelectTypes *sync_types, SelectedItems *selected_items)
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:261
struct Bone * bone
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)