Blender V5.0
anim_filter.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors, Joshua Leung. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9/* This file contains a system used to provide a layer of abstraction between sources
10 * of animation data and tools in Animation Editors. The method used here involves
11 * generating a list of edit structures which enable tools to naively perform the actions
12 * they require without all the boiler-plate associated with loops within loops and checking
13 * for cases to ignore.
14 *
15 * While this is primarily used for the Action/Dopesheet Editor (and its accessory modes),
16 * the Graph Editor also uses this for its channel list and for determining which curves
17 * are being edited. Likewise, the NLA Editor also uses this for its channel list and in
18 * its operators.
19 *
20 * NOTE: much of the original system this was based on was built before the creation of the RNA
21 * system. In future, it would be interesting to replace some parts of this code with RNA queries,
22 * however, RNA does not eliminate some of the boiler-plate reduction benefits presented by this
23 * system, so if any such work does occur, it should only be used for the internals used here...
24 *
25 * -- Joshua Leung, Dec 2008 (Last revision July 2009)
26 */
27
28#include <cstring>
29
30#include "DNA_anim_types.h"
31#include "DNA_armature_types.h"
32#include "DNA_cachefile_types.h"
33#include "DNA_camera_types.h"
34#include "DNA_curves_types.h"
37#include "DNA_key_types.h"
38#include "DNA_lattice_types.h"
39#include "DNA_layer_types.h"
40#include "DNA_light_types.h"
42#include "DNA_linestyle_types.h"
43#include "DNA_mask_types.h"
44#include "DNA_material_types.h"
45#include "DNA_mesh_types.h"
46#include "DNA_meta_types.h"
47#include "DNA_movieclip_types.h"
48#include "DNA_node_types.h"
50#include "DNA_object_types.h"
51#include "DNA_particle_types.h"
53#include "DNA_scene_types.h"
54#include "DNA_screen_types.h"
55#include "DNA_sequence_types.h"
56#include "DNA_space_types.h"
57#include "DNA_speaker_types.h"
58#include "DNA_userdef_types.h"
59#include "DNA_volume_types.h"
60#include "DNA_world_types.h"
61
62#include "MEM_guardedalloc.h"
63
64#include "BLI_alloca.h"
65#include "BLI_ghash.h"
66#include "BLI_listbase.h"
67#include "BLI_string.h"
68#include "BLI_utildefines.h"
69
70#include "BKE_action.hh"
71#include "BKE_anim_data.hh"
72#include "BKE_collection.hh"
73#include "BKE_context.hh"
74#include "BKE_fcurve.hh"
75#include "BKE_fcurve_driver.h"
76#include "BKE_global.hh"
77#include "BKE_grease_pencil.hh"
78#include "BKE_key.hh"
79#include "BKE_layer.hh"
80#include "BKE_library.hh"
81#include "BKE_main.hh"
82#include "BKE_mask.h"
83#include "BKE_material.hh"
84#include "BKE_modifier.hh"
85#include "BKE_node.hh"
86#include "BKE_node_runtime.hh"
87
88#include "ED_anim_api.hh"
89#include "ED_markers.hh"
90
91#include "SEQ_iterator.hh"
92#include "SEQ_modifier.hh"
93#include "SEQ_sequencer.hh"
94#include "SEQ_utils.hh"
95
96#include "ANIM_action.hh"
97#include "ANIM_armature.hh"
99
100#include "anim_intern.hh"
101
102using namespace blender;
103
104/* ************************************************************ */
105/* Blender Context <-> Animation Context mapping */
106
108 ViewLayer *view_layer,
109 const ScrArea *area,
110 ID **r_action_user)
111{
112 if (area->spacetype != SPACE_ACTION) {
113 return nullptr;
114 }
115
116 BKE_view_layer_synced_ensure(scene, view_layer);
118 if (!ob) {
119 return nullptr;
120 }
121
122 const SpaceAction *saction = static_cast<const SpaceAction *>(area->spacedata.first);
123 switch (eAnimEdit_Context(saction->mode)) {
124 case SACTCONT_ACTION: {
125 bAction *active_action = ob->adt ? ob->adt->action : nullptr;
126 if (r_action_user) {
127 *r_action_user = &ob->id;
128 }
129 return active_action;
130 }
131
132 case SACTCONT_SHAPEKEY: {
133 Key *active_key = BKE_key_from_object(ob);
134 bAction *active_action = (active_key && active_key->adt) ? active_key->adt->action : nullptr;
135 if (r_action_user) {
136 *r_action_user = &active_key->id;
137 }
138 return active_action;
139 }
140
141 case SACTCONT_GPENCIL:
143 case SACTCONT_MASK:
146 if (r_action_user) {
147 *r_action_user = nullptr;
148 }
149 return nullptr;
150 }
151
153 return nullptr;
154}
155
156/* ----------- Private Stuff - Action Editor ------------- */
157
158/* Get shapekey data being edited (for Action Editor -> ShapeKey mode) */
159/* NOTE: there's a similar function in `key.cc` #BKE_key_from_object. */
161{
162 Scene *scene = ac->scene;
163 ViewLayer *view_layer = ac->view_layer;
164 Object *ob;
165
166 BKE_view_layer_synced_ensure(scene, view_layer);
167 ob = BKE_view_layer_active_object_get(view_layer);
168 if (ob == nullptr) {
169 return nullptr;
170 }
171
172 /* XXX pinning is not available in 'ShapeKey' mode... */
173 // if (saction->pin) { return nullptr; }
174
175 /* shapekey data is stored with geometry data */
176 return BKE_key_from_object(ob);
177}
178
179/* Get data being edited in Action Editor (depending on current 'mode') */
181{
182 /* get dopesheet */
183 ac->ads = &saction->ads;
184 ac->dopesheet_mode = eAnimEdit_Context(saction->mode);
185
186 /* Set the default filters. These can be overridden later. */
189
191 ac->scene, ac->view_layer, ac->area, &ac->active_action_user);
192
193 /* sync settings with current view status, then return appropriate data */
194 switch (saction->mode) {
195 case SACTCONT_ACTION: /* 'Action Editor' */
197 ac->data = ac->active_action;
198
199 if (saction->flag & SACTION_POSEMARKERS_SHOW) {
200 ac->markers = &ac->active_action->markers;
201 }
202
203 return true;
204
205 case SACTCONT_SHAPEKEY: /* 'ShapeKey Editor' */
207 ac->data = actedit_get_shapekeys(ac);
208
209 if (saction->flag & SACTION_POSEMARKERS_SHOW) {
210 ac->markers = &ac->active_action->markers;
211 }
212
213 return true;
214
215 case SACTCONT_GPENCIL: /* Grease Pencil */ /* XXX review how this mode is handled... */
216 /* update scene-pointer (no need to check for pinning yet, as not implemented) */
217 saction->ads.source = reinterpret_cast<ID *>(ac->scene);
218
220 ac->data = &saction->ads;
221 return true;
222
223 case SACTCONT_CACHEFILE: /* Cache File */ /* XXX review how this mode is handled... */
224 /* update scene-pointer (no need to check for pinning yet, as not implemented) */
225 saction->ads.source = reinterpret_cast<ID *>(ac->scene);
226
228 ac->data = &saction->ads;
229 return true;
230
231 case SACTCONT_MASK: /* Mask */ /* XXX: review how this mode is handled. */
232 {
233 /* TODO: other methods to get the mask. */
234#if 0
235 Strip *strip = SEQ_select_active_get(ac->scene);
236 MovieClip *clip = ac->scene->clip;
237 struct Mask *mask = strip ? strip->mask : nullptr;
238#endif
239
240 /* update scene-pointer (no need to check for pinning yet, as not implemented) */
241 saction->ads.source = reinterpret_cast<ID *>(ac->scene);
242
244 ac->data = &saction->ads;
245 return true;
246 }
247
248 case SACTCONT_DOPESHEET: /* DopeSheet */
249 /* update scene-pointer (no need to check for pinning yet, as not implemented) */
250 saction->ads.source = reinterpret_cast<ID *>(ac->scene);
251
253 ac->data = &saction->ads;
254 return true;
255
256 case SACTCONT_TIMELINE: {
257 saction->ads.source = reinterpret_cast<ID *>(ac->scene);
258
260 ac->data = &saction->ads;
261
262 /* The 'only show selected' filter has to come from the scene flag, not the dopesheet filter.
263 * Most filter flags are hard-coded for the timeline. */
264 const eDopeSheet_FilterFlag flag_only_selected = (ac->scene->flag & SCE_KEYS_NO_SELONLY) ?
267 const eDopeSheet_FilterFlag flag_only_errors = eDopeSheet_FilterFlag(ac->ads->filterflag &
269 ac->filters.flag = flag_only_selected | flag_only_errors;
271 return true;
272 }
273 default: /* unhandled yet */
275 ac->data = nullptr;
276 return false;
277 }
278}
279
280/* ----------- Private Stuff - Graph Editor ------------- */
281
282/* Get data being edited in Graph Editor (depending on current 'mode') */
284{
285 /* init dopesheet data if non-existent (i.e. for old files) */
286 if (sipo->ads == nullptr) {
287 sipo->ads = MEM_callocN<bDopeSheet>("GraphEdit DopeSheet");
288 sipo->ads->source = reinterpret_cast<ID *>(ac->scene);
289 }
290 ac->ads = sipo->ads;
292
293 /* set settings for Graph Editor - "Selected = Editable" */
294 if (U.animation_flag & USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS) {
296 }
297 else {
299 }
300
301 bool ok;
302
303 /* sync settings with current view status, then return appropriate data */
304 switch (sipo->mode) {
305 case SIPO_MODE_ANIMATION: /* Animation F-Curve Editor */
306 /* update scene-pointer (no need to check for pinning yet, as not implemented) */
307 sipo->ads->source = reinterpret_cast<ID *>(ac->scene);
309
311 ac->data = sipo->ads;
312 ok = true;
313 break;
314
315 case SIPO_MODE_DRIVERS: /* Driver F-Curve Editor */
316 /* update scene-pointer (no need to check for pinning yet, as not implemented) */
317 sipo->ads->source = reinterpret_cast<ID *>(ac->scene);
319
321 ac->data = sipo->ads;
322 ok = true;
323 break;
324
325 default: /* unhandled yet */
327 ac->data = nullptr;
328 ok = false;
329 break;
330 }
331
334
335 return ok;
336}
337
338/* ----------- Private Stuff - NLA Editor ------------- */
339
340/* Get data being edited in Graph Editor (depending on current 'mode') */
342{
343 /* init dopesheet data if non-existent (i.e. for old files) */
344 if (snla->ads == nullptr) {
345 snla->ads = MEM_callocN<bDopeSheet>("NlaEdit DopeSheet");
346 }
347 ac->ads = snla->ads;
348
349 /* sync settings with current view status, then return appropriate data */
350 /* update scene-pointer (no need to check for pinning yet, as not implemented) */
351 snla->ads->source = reinterpret_cast<ID *>(ac->scene);
354
356 ac->data = snla->ads;
357
358 return true;
359}
360
361/* ----------- Public API --------------- */
362
364{
365 SpaceLink *sl = ac->sl;
366 bool ok = false;
367
368 /* context depends on editor we are currently in */
369 if (sl) {
370 switch (ac->spacetype) {
371 case SPACE_ACTION: {
372 SpaceAction *saction = reinterpret_cast<SpaceAction *>(sl);
373 ok = actedit_get_context(ac, saction);
374 break;
375 }
376 case SPACE_GRAPH: {
377 SpaceGraph *sipo = reinterpret_cast<SpaceGraph *>(sl);
378 ok = graphedit_get_context(ac, sipo);
379 break;
380 }
381 case SPACE_NLA: {
382 SpaceNla *snla = reinterpret_cast<SpaceNla *>(sl);
383 ok = nlaedit_get_context(ac, snla);
384 break;
385 }
386 case SPACE_EMPTY:
387 case SPACE_VIEW3D:
388 case SPACE_OUTLINER:
389 case SPACE_PROPERTIES:
390 case SPACE_FILE:
391 case SPACE_IMAGE:
392 case SPACE_INFO:
393 case SPACE_SEQ:
394 case SPACE_TEXT:
395 case SPACE_SCRIPT:
396 case SPACE_NODE:
397 case SPACE_CONSOLE:
398 case SPACE_USERPREF:
399 case SPACE_CLIP:
400 case SPACE_TOPBAR:
401 case SPACE_STATUSBAR:
403 break;
404 }
405 }
406
407 /* check if there's any valid data */
408 return (ok && ac->data);
409}
410
412{
413 Main *bmain = CTX_data_main(C);
414 ScrArea *area = CTX_wm_area(C);
415 ARegion *region = CTX_wm_region(C);
417 Scene *scene = CTX_data_scene(C);
418
419 /* clear old context info */
420 if (ac == nullptr) {
421 return false;
422 }
423 memset(ac, 0, sizeof(bAnimContext));
424
425 /* get useful default context settings from context */
426 ac->bmain = bmain;
427 ac->scene = scene;
429 if (scene) {
430 /* This may be overwritten by actedit_get_context() when pose markers should be shown. */
431 ac->markers = &scene->markers;
432 }
433 if (scene && ac->view_layer) {
436 }
438 ac->area = area;
439 ac->region = region;
440 ac->sl = sl;
441 ac->spacetype = eSpace_Type((area) ? area->spacetype : 0);
442 ac->regiontype = eRegion_Type((region) ? region->regiontype : 0);
443
444 /* get data context info */
445 /* XXX: if the below fails, try to grab this info from context instead...
446 * (to allow for scripting). */
448}
449
454
455/* ************************************************************ */
456/* Blender Data <-- Filter --> Channels to be operated on */
457
458/* macros to use before/after getting the sub-channels of some channel,
459 * to abstract away some of the tricky logic involved
460 *
461 * cases:
462 * 1) Graph Edit main area (just data) OR channels visible in Channel List
463 * 2) If not showing channels, we're only interested in the data (Action Editor's editing)
464 * 3) We don't care what data, we just care there is some (so that a collapsed
465 * channel can be kept around). No need to clear channels-flag in order to
466 * keep expander channels with no sub-data out, as those cases should get
467 * dealt with by the recursive detection idiom in place.
468 *
469 * Implementation NOTE:
470 * YES the _doSubChannels variable is NOT read anywhere. BUT, this is NOT an excuse
471 * to go steamrolling the logic into a single-line expression as from experience,
472 * those are notoriously difficult to read + debug when extending later on. The code
473 * below is purposefully laid out so that each case noted above corresponds clearly to
474 * one case below.
475 */
476#define BEGIN_ANIMFILTER_SUBCHANNELS(expanded_check) \
477 { \
478 const eAnimFilter_Flags _filter = filter_mode; \
479 short _doSubChannels = 0; \
480 if (!(filter_mode & ANIMFILTER_LIST_VISIBLE) || (expanded_check)) { \
481 _doSubChannels = 1; \
482 } \
483 else if (!(filter_mode & ANIMFILTER_LIST_CHANNELS)) { \
484 _doSubChannels = 2; \
485 } \
486 else { \
487 filter_mode |= ANIMFILTER_TMP_PEEK; \
488 } \
489\
490 { \
491 (void)_doSubChannels; \
492 }
493/* ... standard sub-channel filtering can go on here now ... */
494#define END_ANIMFILTER_SUBCHANNELS \
495 filter_mode = _filter; \
496 } \
497 (void)0
498
499/* ............................... */
500
501/* Test whether AnimData has a usable Action. */
502#define ANIMDATA_HAS_ACTION_LEGACY(id) \
503 ((id)->adt && (id)->adt->action && (id)->adt->action->wrap().is_action_legacy())
504
505#define ANIMDATA_HAS_ACTION_LAYERED(id) \
506 ((id)->adt && (id)->adt->action && (id)->adt->action->wrap().is_action_layered())
507
508/* quick macro to test if AnimData is usable for drivers */
509#define ANIMDATA_HAS_DRIVERS(id) ((id)->adt && (id)->adt->drivers.first)
510
511/* quick macro to test if AnimData is usable for NLA */
512#define ANIMDATA_HAS_NLA(id) ((id)->adt && (id)->adt->nla_tracks.first)
513
551#define ANIMDATA_FILTER_CASES( \
552 id, adtOk, nlaOk, driversOk, nlaKeysOk, legacyActionOk, layeredActionOk) \
553 { \
554 if ((id)->adt) { \
555 if (!(filter_mode & ANIMFILTER_CURVE_VISIBLE) || \
556 !((id)->adt->flag & ADT_CURVES_NOT_VISIBLE)) { \
557 if (filter_mode & ANIMFILTER_ANIMDATA) { \
558 adtOk \
559 } \
560 else if (ac->filters.flag & ADS_FILTER_ONLYNLA) { \
561 if (ANIMDATA_HAS_NLA(id)) { \
562 nlaOk \
563 } \
564 else if (!(ac->filters.flag & ADS_FILTER_NLA_NOACT) || ANIMDATA_HAS_ACTION_LAYERED(id)) \
565 { \
566 nlaOk \
567 } \
568 } \
569 else if (ac->filters.flag & ADS_FILTER_ONLYDRIVERS) { \
570 if (ANIMDATA_HAS_DRIVERS(id)) { \
571 driversOk \
572 } \
573 } \
574 else { \
575 if (ANIMDATA_HAS_NLA(id)) { \
576 nlaKeysOk \
577 } \
578 if (ANIMDATA_HAS_ACTION_LAYERED(id)) { \
579 layeredActionOk \
580 } \
581 else if (ANIMDATA_HAS_ACTION_LEGACY(id)) { \
582 legacyActionOk \
583 } \
584 } \
585 } \
586 } \
587 } \
588 (void)0
589
590/* ............................... */
591
598#define ANIMCHANNEL_NEW_CHANNEL_FULL( \
599 bmain, channel_data, channel_type, owner_id, fcurve_owner_id, ale_statement) \
600 if (filter_mode & ANIMFILTER_TMP_PEEK) { \
601 return 1; \
602 } \
603 { \
604 bAnimListElem *ale = make_new_animlistelem( \
605 bmain, channel_data, channel_type, (ID *)owner_id, fcurve_owner_id); \
606 if (ale) { \
607 BLI_addtail(anim_data, ale); \
608 items++; \
609 ale_statement \
610 } \
611 } \
612 (void)0
613
614#define ANIMCHANNEL_NEW_CHANNEL(bmain, channel_data, channel_type, owner_id, fcurve_owner_id) \
615 ANIMCHANNEL_NEW_CHANNEL_FULL(bmain, channel_data, channel_type, owner_id, fcurve_owner_id, {})
616
617/* ............................... */
618
619/* quick macro to test if an anim-channel representing an AnimData block is suitably active */
620#define ANIMCHANNEL_ACTIVEOK(ale) \
621 (!(filter_mode & ANIMFILTER_ACTIVE) || !(ale->adt) || (ale->adt->flag & ADT_UI_ACTIVE))
622
623/* Quick macro to test if an anim-channel (F-Curve, Group, etc.)
624 * is selected in an acceptable way. */
625#define ANIMCHANNEL_SELOK(test_func) \
626 (!(filter_mode & (ANIMFILTER_SEL | ANIMFILTER_UNSEL)) || \
627 ((filter_mode & ANIMFILTER_SEL) && test_func) || \
628 ((filter_mode & ANIMFILTER_UNSEL) && test_func == 0))
629
639#define ANIMCHANNEL_SELEDITOK(test_func) \
640 (!(filter_mode & ANIMFILTER_SELEDIT) || !(filter_mode & ANIMFILTER_FOREDIT) || (test_func))
641
642/* ----------- 'Private' Stuff --------------- */
643
649{
650 ale.adt = adt;
651
652 if (!adt || !adt->action) {
653 ale.key_data = nullptr;
654 ale.datatype = ALE_NONE;
655 return;
656 }
657
658 blender::animrig::Action &action = adt->action->wrap();
659 ale.key_data = &action;
661}
662
663/* this function allocates memory for a new bAnimListElem struct for the
664 * provided animation channel-data.
665 */
667 Main *bmain, void *data, const eAnim_ChannelType datatype, ID *owner_id, ID *fcurve_owner_id)
668{
669 /* Only allocate memory if there is data to convert. */
670 if (!data) {
671 return nullptr;
672 }
673
674 /* Allocate and set generic data. */
675 bAnimListElem *ale = MEM_callocN<bAnimListElem>("bAnimListElem");
676
677 ale->data = data;
678 ale->type = datatype;
679
680 ale->bmain = bmain;
681 ale->id = owner_id;
682 ale->adt = BKE_animdata_from_id(owner_id);
683 ale->fcurve_owner_id = fcurve_owner_id;
684
685 /* do specifics */
686 switch (datatype) {
687 case ANIMTYPE_SUMMARY: {
688 /* Nothing to include for now... this is just a dummy wrapper around
689 * all the other channels in the DopeSheet, and gets included at the start of the list. */
690 ale->key_data = nullptr;
691 ale->datatype = ALE_ALL;
692 break;
693 }
694 case ANIMTYPE_SCENE: {
695 Scene *sce = static_cast<Scene *>(data);
696
697 ale->flag = sce->flag;
698
699 ale->key_data = sce;
700 ale->datatype = ALE_SCE;
701
702 ale->adt = BKE_animdata_from_id(static_cast<ID *>(data));
703 break;
704 }
705 case ANIMTYPE_OBJECT: {
706 Base *base = static_cast<Base *>(data);
707 Object *ob = base->object;
708
709 ale->flag = ob->flag;
710
711 ale->key_data = ob;
712 ale->datatype = ALE_OB;
713
714 ale->adt = BKE_animdata_from_id(&ob->id);
715 break;
716 }
718 bAction *action = static_cast<bAction *>(data);
719
720 ale->flag = action->flag;
721
722 ale->key_data = action;
724 break;
725 }
727 animrig::Slot *slot = static_cast<animrig::Slot *>(data);
728 ale->flag = slot->slot_flags;
729
730 BLI_assert_msg(GS(fcurve_owner_id->name) == ID_AC, "fcurve_owner_id should be an Action");
731 /* ale->data = the slot itself, key_data = the Action. */
732 ale->key_data = fcurve_owner_id;
734 break;
735 }
736 case ANIMTYPE_FILLACTD: {
737 bAction *act = static_cast<bAction *>(data);
738
739 ale->flag = act->flag;
740
741 ale->key_data = act;
742 ale->datatype = ALE_ACT;
743 break;
744 }
746 AnimData *adt = static_cast<AnimData *>(data);
747
748 ale->flag = adt->flag;
749
750 /* XXX drivers don't show summary for now. */
751 ale->key_data = nullptr;
752 ale->datatype = ALE_NONE;
753 break;
754 }
755 case ANIMTYPE_DSMAT: {
756 Material *ma = static_cast<Material *>(data);
757 ale->flag = FILTER_MAT_OBJD(ma);
758 key_data_from_adt(*ale, ma->adt);
759 break;
760 }
761 case ANIMTYPE_DSLAM: {
762 Light *la = static_cast<Light *>(data);
763 ale->flag = FILTER_LAM_OBJD(la);
764 key_data_from_adt(*ale, la->adt);
765 break;
766 }
767 case ANIMTYPE_DSCAM: {
768 Camera *ca = static_cast<Camera *>(data);
769 ale->flag = FILTER_CAM_OBJD(ca);
770 key_data_from_adt(*ale, ca->adt);
771 break;
772 }
774 CacheFile *cache_file = static_cast<CacheFile *>(data);
775 ale->flag = FILTER_CACHEFILE_OBJD(cache_file);
776 key_data_from_adt(*ale, cache_file->adt);
777 break;
778 }
779 case ANIMTYPE_DSCUR: {
780 Curve *cu = static_cast<Curve *>(data);
781 ale->flag = FILTER_CUR_OBJD(cu);
782 key_data_from_adt(*ale, cu->adt);
783 break;
784 }
785 case ANIMTYPE_DSARM: {
786 bArmature *arm = static_cast<bArmature *>(data);
787 ale->flag = FILTER_ARM_OBJD(arm);
788 key_data_from_adt(*ale, arm->adt);
789 break;
790 }
791 case ANIMTYPE_DSMESH: {
792 Mesh *mesh = static_cast<Mesh *>(data);
793 ale->flag = FILTER_MESH_OBJD(mesh);
794 key_data_from_adt(*ale, mesh->adt);
795 break;
796 }
797 case ANIMTYPE_DSLAT: {
798 Lattice *lt = static_cast<Lattice *>(data);
799 ale->flag = FILTER_LATTICE_OBJD(lt);
800 key_data_from_adt(*ale, lt->adt);
801 break;
802 }
803 case ANIMTYPE_DSSPK: {
804 Speaker *spk = static_cast<Speaker *>(data);
805 ale->flag = FILTER_SPK_OBJD(spk);
806 key_data_from_adt(*ale, spk->adt);
807 break;
808 }
809 case ANIMTYPE_DSHAIR: {
810 Curves *curves = static_cast<Curves *>(data);
811 ale->flag = FILTER_CURVES_OBJD(curves);
812 key_data_from_adt(*ale, curves->adt);
813 break;
814 }
816 PointCloud *pointcloud = static_cast<PointCloud *>(data);
817 ale->flag = FILTER_POINTS_OBJD(pointcloud);
818 key_data_from_adt(*ale, pointcloud->adt);
819 break;
820 }
821 case ANIMTYPE_DSVOLUME: {
822 Volume *volume = static_cast<Volume *>(data);
823 ale->flag = FILTER_VOLUME_OBJD(volume);
824 key_data_from_adt(*ale, volume->adt);
825 break;
826 }
828 LightProbe *probe = static_cast<LightProbe *>(data);
829 ale->flag = FILTER_LIGHTPROBE_OBJD(probe);
830 key_data_from_adt(*ale, probe->adt);
831 break;
832 }
833 case ANIMTYPE_DSSKEY: {
834 Key *key = static_cast<Key *>(data);
835 ale->flag = FILTER_SKE_OBJD(key);
836 key_data_from_adt(*ale, key->adt);
837 break;
838 }
839 case ANIMTYPE_DSWOR: {
840 World *wo = static_cast<World *>(data);
841 ale->flag = FILTER_WOR_SCED(wo);
842 key_data_from_adt(*ale, wo->adt);
843 break;
844 }
845 case ANIMTYPE_DSNTREE: {
846 bNodeTree *ntree = static_cast<bNodeTree *>(data);
847 ale->flag = FILTER_NTREE_DATA(ntree);
848 key_data_from_adt(*ale, ntree->adt);
849 break;
850 }
852 FreestyleLineStyle *linestyle = static_cast<FreestyleLineStyle *>(data);
853 ale->flag = FILTER_LS_SCED(linestyle);
854 key_data_from_adt(*ale, linestyle->adt);
855 break;
856 }
857 case ANIMTYPE_DSPART: {
858 ParticleSettings *part = static_cast<ParticleSettings *>(ale->data);
859 ale->flag = FILTER_PART_OBJD(part);
860 key_data_from_adt(*ale, part->adt);
861 break;
862 }
863 case ANIMTYPE_DSTEX: {
864 Tex *tex = static_cast<Tex *>(data);
865 ale->flag = FILTER_TEX_DATA(tex);
866 key_data_from_adt(*ale, tex->adt);
867 break;
868 }
869 case ANIMTYPE_DSGPENCIL: {
870 bGPdata *gpd = static_cast<bGPdata *>(data);
871 /* NOTE: we just reuse the same expand filter for this case */
872 ale->flag = EXPANDED_GPD(gpd);
873
874 /* XXX: currently, this is only used for access to its animation data */
875 key_data_from_adt(*ale, gpd->adt);
876 break;
877 }
878 case ANIMTYPE_DSMCLIP: {
879 MovieClip *clip = static_cast<MovieClip *>(data);
880 ale->flag = EXPANDED_MCLIP(clip);
881 key_data_from_adt(*ale, clip->adt);
882 break;
883 }
885 AnimData *adt = static_cast<AnimData *>(data);
886
887 ale->flag = adt->flag;
888
889 ale->key_data = nullptr;
890 ale->datatype = ALE_NONE;
891 break;
892 }
893 case ANIMTYPE_GROUP: {
894 BLI_assert_msg(GS(fcurve_owner_id->name) == ID_AC, "fcurve_owner_id should be an Action");
895
896 bActionGroup *agrp = static_cast<bActionGroup *>(data);
897
898 ale->flag = agrp->flag;
899
900 ale->key_data = nullptr;
901 ale->datatype = ALE_GROUP;
902 break;
903 }
904 case ANIMTYPE_FCURVE:
905 case ANIMTYPE_NLACURVE: /* practically the same as ANIMTYPE_FCURVE.
906 * Differences are applied post-creation */
907 {
908 FCurve *fcu = static_cast<FCurve *>(data);
909
910 ale->flag = fcu->flag;
911
912 ale->key_data = fcu;
913 ale->datatype = ALE_FCURVE;
914 break;
915 }
916 case ANIMTYPE_SHAPEKEY: {
917 KeyBlock *kb = static_cast<KeyBlock *>(data);
918 Key *key = reinterpret_cast<Key *>(ale->id);
919
920 ale->flag = kb->flag;
921
922 /* whether we have keyframes depends on whether there is a Key block to find it from */
923 if (key) {
924 /* index of shapekey is defined by place in key's list */
925 ale->index = BLI_findindex(&key->block, kb);
926
927 /* the corresponding keyframes are from the animdata */
928 if (ale->adt && ale->adt->action) {
929 /* Try to find the F-Curve which corresponds to this exactly. */
930 if (std::optional<std::string> rna_path = BKE_keyblock_curval_rnapath_get(key, kb)) {
932 {*rna_path, 0});
933 }
934 }
935 ale->datatype = (ale->key_data) ? ALE_FCURVE : ALE_NONE;
936 }
937 break;
938 }
939 case ANIMTYPE_GPLAYER: {
940 bGPDlayer *gpl = static_cast<bGPDlayer *>(data);
941
942 ale->flag = gpl->flag;
943
944 ale->key_data = nullptr;
945 ale->datatype = ALE_GPFRAME;
946 break;
947 }
949 GreasePencilLayer *layer = static_cast<GreasePencilLayer *>(data);
950
951 ale->flag = layer->base.flag;
952
953 ale->key_data = nullptr;
955 break;
956 }
958 GreasePencilLayerTreeGroup *layer_group = static_cast<GreasePencilLayerTreeGroup *>(data);
959
960 ale->flag = layer_group->base.flag;
961
962 ale->key_data = nullptr;
964 break;
965 }
967 GreasePencil *grease_pencil = static_cast<GreasePencil *>(data);
968
969 ale->flag = grease_pencil->flag;
970
971 ale->key_data = nullptr;
973 break;
974 }
975 case ANIMTYPE_MASKLAYER: {
976 MaskLayer *masklay = static_cast<MaskLayer *>(data);
977
978 ale->flag = masklay->flag;
979
980 ale->key_data = nullptr;
981 ale->datatype = ALE_MASKLAY;
982 break;
983 }
984 case ANIMTYPE_NLATRACK: {
985 NlaTrack *nlt = static_cast<NlaTrack *>(data);
986
987 ale->flag = nlt->flag;
988
989 ale->key_data = &nlt->strips;
990 ale->datatype = ALE_NLASTRIP;
991 break;
992 }
993 case ANIMTYPE_NLAACTION: {
994 /* nothing to include for now... nothing editable from NLA-perspective here */
995 ale->key_data = nullptr;
996 ale->datatype = ALE_NONE;
997 break;
998 }
999
1000 case ANIMTYPE_NONE:
1001 case ANIMTYPE_ANIMDATA:
1003 case ANIMTYPE_DSMBALL:
1005 case ANIMTYPE_PALETTE:
1006 case ANIMTYPE_NUM_TYPES:
1007 break;
1008 }
1009
1010 return ale;
1011}
1012
1013/* ----------------------------------------- */
1014
1015/* 'Only Selected' selected data and/or 'Include Hidden' filtering
1016 * NOTE: when this function returns true, the F-Curve is to be skipped
1017 */
1019 FCurve *fcu,
1020 ID *owner_id,
1021 const eAnimFilter_Flags filter_mode)
1022{
1023 if (fcu->grp != nullptr && fcu->grp->flag & ADT_CURVES_ALWAYS_VISIBLE) {
1024 return false;
1025 }
1026 /* hidden items should be skipped if we only care about visible data,
1027 * but we aren't interested in hidden stuff */
1028 const bool skip_hidden = (filter_mode & ANIMFILTER_DATA_VISIBLE) &&
1030
1031 if (GS(owner_id->name) == ID_OB) {
1032 Object *ob = reinterpret_cast<Object *>(owner_id);
1033 bPoseChannel *pchan = nullptr;
1034 char bone_name[sizeof(pchan->name)];
1035
1036 /* Only consider if F-Curve involves `pose.bones`. */
1037 if (fcu->rna_path &&
1038 BLI_str_quoted_substr(fcu->rna_path, "pose.bones[", bone_name, sizeof(bone_name)))
1039 {
1040 /* Get bone-name, and check if this bone is selected. */
1041 pchan = BKE_pose_channel_find_name(ob->pose, bone_name);
1042
1043 /* check whether to continue or skip */
1044 if (pchan && pchan->bone) {
1045 /* If only visible channels,
1046 * skip if bone not visible unless user wants channels from hidden data too. */
1047 if (skip_hidden) {
1048 bArmature *arm = static_cast<bArmature *>(ob->data);
1049
1050 /* Skipping - is currently hidden. */
1051 if (!blender::animrig::bone_is_visible(arm, pchan)) {
1052 return true;
1053 }
1054 }
1055
1056 /* can only add this F-Curve if it is selected */
1057 if (ac->filters.flag & ADS_FILTER_ONLYSEL) {
1058 if ((pchan->flag & POSE_SELECTED) == 0) {
1059 return true;
1060 }
1061 }
1062 }
1063 }
1064 }
1065 else if (GS(owner_id->name) == ID_SCE) {
1066 Scene *scene = reinterpret_cast<Scene *>(owner_id);
1067 Strip *strip = nullptr;
1068 char strip_name[sizeof(strip->name)];
1069
1070 /* Only consider if F-Curve involves `sequence_editor.strips`. */
1071 if (fcu->rna_path &&
1072 BLI_str_quoted_substr(fcu->rna_path, "strips_all[", strip_name, sizeof(strip_name)))
1073 {
1074 /* Get strip name, and check if this strip is selected. */
1076 if (ed) {
1077 strip = blender::seq::get_strip_by_name(ed->current_strips(), strip_name, false);
1078 }
1079
1080 /* Can only add this F-Curve if it is selected. */
1081 if (ac->filters.flag & ADS_FILTER_ONLYSEL) {
1082
1083 /* NOTE(@ideasman42): The `strip == nullptr` check doesn't look right
1084 * (compared to other checks in this function which skip data that can't be found).
1085 *
1086 * This is done since the search for sequence strips doesn't use a global lookup:
1087 * - Nested meta-strips are excluded.
1088 * - When inside a meta-strip - strips outside the meta-strip excluded.
1089 *
1090 * Instead, only the strips directly visible to the user are considered for selection.
1091 * The nullptr check here means everything else is considered unselected and is not shown.
1092 *
1093 * There is a subtle difference between nodes, pose-bones ... etc
1094 * since data-paths that point to missing strips are not shown.
1095 * If this is an important difference, the nullptr case could perform a global lookup,
1096 * only returning `true` if the sequence strip exists elsewhere
1097 * (ignoring its selection state). */
1098 if (strip == nullptr) {
1099 return true;
1100 }
1101
1102 if ((strip->flag & SELECT) == 0) {
1103 return true;
1104 }
1105 }
1106 }
1107 }
1108 else if (GS(owner_id->name) == ID_NT) {
1109 bNodeTree *ntree = reinterpret_cast<bNodeTree *>(owner_id);
1110 bNode *node = nullptr;
1111 char node_name[sizeof(node->name)];
1112
1113 /* Check for selected nodes. */
1114 if (fcu->rna_path &&
1115 BLI_str_quoted_substr(fcu->rna_path, "nodes[", node_name, sizeof(node_name)))
1116 {
1117 /* Get strip name, and check if this strip is selected. */
1118 node = bke::node_find_node_by_name(*ntree, node_name);
1119
1120 /* Can only add this F-Curve if it is selected. */
1121 if (node) {
1122 if (ac->filters.flag & ADS_FILTER_ONLYSEL) {
1123 if ((node->flag & NODE_SELECT) == 0) {
1124 return true;
1125 }
1126 }
1127 }
1128 }
1129 }
1130
1131 return false;
1132}
1133
1134/* Helper for name-based filtering - Perform "partial/fuzzy matches" (as in 80a7efd) */
1135static bool name_matches_dopesheet_filter(const bDopeSheet *ads, const char *name)
1136{
1137 if (ads->flag & ADS_FLAG_FUZZY_NAMES) {
1138 /* full fuzzy, multi-word, case insensitive matches */
1139 const size_t str_len = strlen(ads->searchstr);
1140 const int words_max = BLI_string_max_possible_word_count(str_len);
1141
1142 int (*words)[2] = static_cast<int (*)[2]>(BLI_array_alloca(words, words_max));
1143 const int words_len = BLI_string_find_split_words(
1144 ads->searchstr, str_len, ' ', words, words_max);
1145 bool found = false;
1146
1147 /* match name against all search words */
1148 for (int index = 0; index < words_len; index++) {
1149 if (BLI_strncasestr(name, ads->searchstr + words[index][0], words[index][1])) {
1150 found = true;
1151 break;
1152 }
1153 }
1154
1155 /* if we have a match somewhere, this returns true */
1156 return ((ads->flag & ADS_FLAG_INVERT_FILTER) == 0) ? found : !found;
1157 }
1158 /* fallback/default - just case insensitive, but starts from start of word */
1159 bool found = BLI_strcasestr(name, ads->searchstr) != nullptr;
1160 return ((ads->flag & ADS_FLAG_INVERT_FILTER) == 0) ? found : !found;
1161}
1162
1163/* (Display-)Name-based F-Curve filtering
1164 * NOTE: when this function returns true, the F-Curve is to be skipped
1165 */
1167 bAnimContext *ac, FCurve *fcu, eAnim_ChannelType channel_type, void *owner, ID *owner_id)
1168{
1169 bAnimListElem ale_dummy = {nullptr};
1170 const bAnimChannelType *acf;
1171
1172 /* create a dummy wrapper for the F-Curve, so we can get typeinfo for it */
1173 ale_dummy.type = channel_type;
1174 ale_dummy.owner = owner;
1175 ale_dummy.id = owner_id;
1176 ale_dummy.data = fcu;
1177
1178 /* get type info for channel */
1179 acf = ANIM_channel_get_typeinfo(&ale_dummy);
1180 if (acf && acf->name) {
1182
1183 /* get name */
1184 acf->name(&ale_dummy, name);
1185
1186 /* check for partial match with the match string, assuming case insensitive filtering
1187 * if match, this channel shouldn't be ignored!
1188 */
1190 }
1191
1192 /* just let this go... */
1193 return true;
1194}
1195
1197{
1199 if (!acf) {
1201 /* Do not filter out stuff unless we know it can be filtered out. */
1202 return true;
1203 }
1204
1206 acf->name(&ale, name);
1207 return name_matches_dopesheet_filter(&ads, name);
1208}
1209
1215static bool fcurve_has_errors(bAnimContext *ac, const FCurve *fcu)
1216{
1217 /* F-Curve disabled (path evaluation error). */
1218 if (fcu->flag & FCURVE_DISABLED) {
1219 return true;
1220 }
1221
1222 /* driver? */
1223 if (fcu->driver) {
1224 const ChannelDriver *driver = fcu->driver;
1225
1226 /* error flag on driver usually means that there is an error
1227 * BUT this may not hold with PyDrivers as this flag gets cleared
1228 * if no critical errors prevent the driver from working...
1229 */
1230 if (driver->flag & DRIVER_FLAG_INVALID) {
1231 return true;
1232 }
1233
1234 /* check variables for other things that need linting... */
1235 /* TODO: maybe it would be more efficient just to have a quick flag for this? */
1236 LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
1238 if (dtar->flag & DTAR_FLAG_INVALID) {
1239 return true;
1240 }
1241
1242 if ((dtar->flag & DTAR_FLAG_FALLBACK_USED) &&
1244 {
1245 return true;
1246 }
1247 }
1249 }
1250 }
1251
1252 /* no errors found */
1253 return false;
1254}
1255
1256/* find the next F-Curve that is usable for inclusion */
1258 FCurve *first,
1259 eAnim_ChannelType channel_type,
1260 const eAnimFilter_Flags filter_mode,
1261 void *owner,
1262 ID *owner_id)
1263{
1264 bActionGroup *grp = (channel_type == ANIMTYPE_FCURVE) ? static_cast<bActionGroup *>(owner) :
1265 nullptr;
1266 FCurve *fcu = nullptr;
1267
1268 /* Loop over F-Curves - assume that the caller of this has already checked
1269 * that these should be included.
1270 * NOTE: we need to check if the F-Curves belong to the same group,
1271 * as this gets called for groups too...
1272 */
1273 for (fcu = first; ((fcu) && (fcu->grp == grp)); fcu = fcu->next) {
1274 /* special exception for Pose-Channel/Sequence-Strip/Node Based F-Curves:
1275 * - The 'Only Selected' and 'Include Hidden' data filters should be applied to sub-ID data
1276 * which can be independently selected/hidden, such as Pose-Channels, Sequence Strips,
1277 * and Nodes. Since these checks were traditionally done as first check for objects,
1278 * we do the same here.
1279 * - We currently use an 'approximate' method for getting these F-Curves that doesn't require
1280 * carefully checking the entire path.
1281 * - This will also affect things like Drivers, and also works for Bone Constraints.
1282 */
1283 if (ac->ads && owner_id) {
1284 if ((filter_mode & ANIMFILTER_TMP_IGNORE_ONLYSEL) == 0) {
1285 if ((ac->filters.flag & ADS_FILTER_ONLYSEL) ||
1286 (ac->filters.flag & ADS_FILTER_INCL_HIDDEN) == 0)
1287 {
1288 if (skip_fcurve_selected_data(ac, fcu, owner_id, filter_mode)) {
1289 continue;
1290 }
1291 }
1292 }
1293 }
1294
1295 /* only include if visible (Graph Editor check, not channels check) */
1296 if (!(filter_mode & ANIMFILTER_CURVE_VISIBLE) || (fcu->flag & FCURVE_VISIBLE)) {
1297 /* only work with this channel and its subchannels if it is editable */
1298 if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_FCU(fcu)) {
1299 /* Only include this curve if selected in a way consistent
1300 * with the filtering requirements. */
1302 /* only include if this curve is active */
1303 if (!(filter_mode & ANIMFILTER_ACTIVE) || (fcu->flag & FCURVE_ACTIVE)) {
1304 /* name based filtering... */
1305 if (((ac->ads) && (ac->ads->searchstr[0] != '\0')) && (owner_id)) {
1306 if (skip_fcurve_with_name(ac, fcu, channel_type, owner, owner_id)) {
1307 continue;
1308 }
1309 }
1310
1311 /* error-based filtering... */
1312 if ((ac->ads) && (ac->filters.flag & ADS_FILTER_ONLY_ERRORS)) {
1313 /* skip if no errors... */
1314 if (!fcurve_has_errors(ac, fcu)) {
1315 continue;
1316 }
1317 }
1318
1319 /* this F-Curve can be used, so return it */
1320 return fcu;
1321 }
1322 }
1323 }
1324 }
1325 }
1326
1327 /* no (more) F-Curves from the list are suitable... */
1328 return nullptr;
1329}
1330
1332 ListBase *anim_data,
1333 FCurve *first,
1334 eAnim_ChannelType fcurve_type,
1335 const eAnimFilter_Flags filter_mode,
1336 void *owner,
1337 ID *owner_id,
1338 ID *fcurve_owner_id)
1339{
1340 FCurve *fcu;
1341 size_t items = 0;
1342
1343 /* Loop over every F-Curve able to be included.
1344 *
1345 * This for-loop works like this:
1346 * 1) The starting F-Curve is assigned to the fcu pointer
1347 * so that we have a starting point to search from.
1348 * 2) The first valid F-Curve to start from (which may include the one given as 'first')
1349 * in the remaining list of F-Curves is found, and verified to be non-null.
1350 * 3) The F-Curve referenced by fcu pointer is added to the list
1351 * 4) The fcu pointer is set to the F-Curve after the one we just added,
1352 * so that we can keep going through the rest of the F-Curve list without an eternal loop.
1353 * Back to step 2 :)
1354 */
1355 for (fcu = first;
1356 (fcu = animfilter_fcurve_next(ac, fcu, fcurve_type, filter_mode, owner, owner_id));
1357 fcu = fcu->next)
1358 {
1359 if (UNLIKELY(fcurve_type == ANIMTYPE_NLACURVE)) {
1360 /* NLA Control Curve - Basically the same as normal F-Curves,
1361 * except we need to set some stuff differently */
1362 ANIMCHANNEL_NEW_CHANNEL_FULL(ac->bmain, fcu, ANIMTYPE_NLACURVE, owner_id, fcurve_owner_id, {
1363 ale->owner = owner; /* strip */
1364 /* Since #130440 landed, this should now in theory be something like
1365 * `ale->adt = BKE_animdata_from_id(owner_id)`, rather than a nullptr.
1366 * However, at the moment the nullptr doesn't hurt, and it helps us
1367 * catch bugs like #147803 via the assert in `fcurve_to_keylist()`. If
1368 * the nullptr does start to hurt at some point, please change it! */
1369 ale->adt = nullptr;
1370 });
1371 }
1372 else {
1373 /* Normal FCurve */
1374 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, fcu, ANIMTYPE_FCURVE, owner_id, fcurve_owner_id);
1375 }
1376 }
1377
1378 /* return the number of items added to the list */
1379 return items;
1380}
1381
1382static inline bool fcurve_span_selection_matters(const eAnimFilter_Flags filter_mode)
1383{
1384 /* This means that ANIMFILTER_SELEDIT only works if ANIMFILTER_FOREDIT is also set. Given the
1385 * description on ANIMFILTER_SELEDIT this seems reasonable. */
1386 if ((filter_mode & ANIMFILTER_FOREDIT) && (filter_mode & ANIMFILTER_SELEDIT)) {
1387 return true;
1388 }
1389 return filter_mode & (ANIMFILTER_SEL | ANIMFILTER_UNSEL);
1390}
1391
1392static inline bool fcurve_span_must_be_selected(const eAnimFilter_Flags filter_mode)
1393{
1394 if ((filter_mode & ANIMFILTER_FOREDIT) && (filter_mode & ANIMFILTER_SELEDIT)) {
1395 return true;
1396 }
1397 return filter_mode & ANIMFILTER_SEL;
1398}
1399
1416 ListBase * /*bAnimListElem*/ anim_data,
1417 Span<FCurve *> fcurves,
1418 const animrig::slot_handle_t slot_handle,
1419 const eAnimFilter_Flags filter_mode,
1420 ID *animated_id,
1421 ID *fcurve_owner_id)
1422{
1423 size_t num_items = 0;
1424 BLI_assert(animated_id);
1425
1426 const bool active_matters = filter_mode & ANIMFILTER_ACTIVE;
1427 const bool selection_matters = fcurve_span_selection_matters(filter_mode);
1428 const bool must_be_selected = fcurve_span_must_be_selected(filter_mode);
1429 const bool visibility_matters = filter_mode & ANIMFILTER_CURVE_VISIBLE;
1430 const bool editability_matters = filter_mode & ANIMFILTER_FOREDIT;
1431 const bool show_only_errors = ac->ads && (ac->filters.flag & ADS_FILTER_ONLY_ERRORS);
1432 const bool filter_by_name = ac->ads && (ac->ads->searchstr[0] != '\0');
1433
1434 for (FCurve *fcu : fcurves) {
1435 /* make_new_animlistelem will return nullptr when fcu == nullptr, and that's
1436 * going to cause problems. */
1437 BLI_assert(fcu);
1438
1439 if (editability_matters && (fcu->flag & FCURVE_PROTECTED)) {
1440 continue;
1441 }
1442
1443 if (selection_matters && bool(fcu->flag & FCURVE_SELECTED) != must_be_selected) {
1444 continue;
1445 }
1446 if (active_matters && !(fcu->flag & FCURVE_ACTIVE)) {
1447 continue;
1448 }
1449 if (visibility_matters && !(fcu->flag & FCURVE_VISIBLE)) {
1450 continue;
1451 }
1452 if (show_only_errors && !fcurve_has_errors(ac, fcu)) {
1453 continue;
1454 }
1455 if (skip_fcurve_selected_data(ac, fcu, animated_id, filter_mode)) {
1456 continue;
1457 }
1458
1460 ac->bmain, fcu, ANIMTYPE_FCURVE, animated_id, fcurve_owner_id);
1461
1462 /* Filtering by name needs a way to look up the name, which is easiest if
1463 * there is already an #bAnimListElem. */
1464 if (filter_by_name && !ale_name_matches_dopesheet_filter(*ac->ads, *ale)) {
1465 MEM_freeN(ale);
1466 continue;
1467 }
1468
1469 if (filter_mode & ANIMFILTER_TMP_PEEK) {
1470 /* Found an animation channel, which is good enough for the 'TMP_PEEK' mode. */
1471 MEM_freeN(ale);
1472 return 1;
1473 }
1474
1475 /* bAnimListElem::slot_handle is exposed as int32_t and not as slot_handle_t, so better
1476 * ensure that these are still equivalent.
1477 * TODO: move to another part of the code. */
1478 static_assert(
1479 std::is_same_v<decltype(ActionSlot::handle), decltype(bAnimListElem::slot_handle)>);
1480
1481 /* Note that this might not be the same as ale->adt->slot_handle. The reason this F-Curve is
1482 * shown could be because it's in the Action editor, showing ale->adt->action with _all_
1483 * slots, and this F-Curve could be from a different slot than what's used by the owner
1484 * of `ale->adt`. */
1485 ale->slot_handle = slot_handle;
1486
1487 BLI_addtail(anim_data, ale);
1488 num_items++;
1489 }
1490
1491 return num_items;
1492}
1493
1503 ListBase *anim_data,
1504 bAction *act,
1505 animrig::slot_handle_t slot_handle,
1506 bActionGroup *agrp,
1507 eAnimFilter_Flags filter_mode,
1508 ID *owner_id)
1509{
1510 BLI_assert(act != nullptr);
1511 BLI_assert(agrp != nullptr);
1512
1513 ListBase tmp_data = {nullptr, nullptr};
1514 size_t tmp_items = 0;
1515 size_t items = 0;
1516
1517 animrig::Action &action = act->wrap();
1518
1519 /* if we care about the selection status of the channels,
1520 * but the group isn't expanded (1)...
1521 * (1) this only matters if we actually care about the hierarchy though.
1522 * - Hierarchy matters: this hack should be applied
1523 * - Hierarchy ignored: cases like #21276 won't work properly, unless we skip this hack
1524 */
1525 if (
1526 /* Care about hierarchy but group isn't expanded. */
1527 ((filter_mode & ANIMFILTER_LIST_VISIBLE) && EXPANDED_AGRP(ac, agrp) == 0) &&
1528 /* Care about selection status. */
1529 (filter_mode & (ANIMFILTER_SEL | ANIMFILTER_UNSEL)))
1530 {
1531 /* If the group itself isn't selected appropriately,
1532 * we shouldn't consider its children either. */
1533 if (ANIMCHANNEL_SELOK(SEL_AGRP(agrp)) == 0) {
1534 return 0;
1535 }
1536
1537 /* if we're still here,
1538 * then the selection status of the curves within this group should not matter,
1539 * since this creates too much overhead for animators (i.e. making a slow workflow).
1540 *
1541 * Tools affected by this at time of coding (2010 Feb 09):
1542 * - Inserting keyframes on selected channels only.
1543 * - Pasting keyframes.
1544 * - Creating ghost curves in Graph Editor.
1545 */
1547 }
1548
1549 /* add grouped F-Curves */
1551 /* special filter so that we can get just the F-Curves within the active group */
1552 if (!(filter_mode & ANIMFILTER_ACTGROUPED) || (agrp->flag & AGRP_ACTIVE)) {
1553 /* for the Graph Editor, curves may be set to not be visible in the view to lessen
1554 * clutter, but to do this, we need to check that the group doesn't have its
1555 * not-visible flag set preventing all its sub-curves to be shown
1556 */
1557 if (!(filter_mode & ANIMFILTER_CURVE_VISIBLE) || !(agrp->flag & AGRP_NOTVISIBLE)) {
1558 /* group must be editable for its children to be editable (if we care about this) */
1559 if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_AGRP(agrp)) {
1560 /* Filter the fcurves in this group, adding them to the temporary
1561 * filter list. */
1562 if (action.is_action_legacy()) {
1563 /* get first F-Curve which can be used here */
1564 FCurve *first_fcu = animfilter_fcurve_next(ac,
1565 static_cast<FCurve *>(agrp->channels.first),
1567 filter_mode,
1568 agrp,
1569 owner_id);
1570
1571 /* filter list, starting from this F-Curve */
1572 tmp_items += animfilter_fcurves(
1573 ac, &tmp_data, first_fcu, ANIMTYPE_FCURVE, filter_mode, agrp, owner_id, &act->id);
1574 }
1575 else {
1576 BLI_assert(agrp->channelbag != nullptr);
1577 Span<FCurve *> fcurves = agrp->wrap().fcurves();
1578 tmp_items += animfilter_fcurves_span(
1579 ac, &tmp_data, fcurves, slot_handle, filter_mode, owner_id, &act->id);
1580 }
1581 }
1582 }
1583 }
1584 }
1586
1587 /* did we find anything? */
1588 if (tmp_items) {
1589 /* add this group as a channel first */
1590 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1591 /* filter selection of channel specially here again,
1592 * since may be open and not subject to previous test */
1593 if (ANIMCHANNEL_SELOK(SEL_AGRP(agrp))) {
1594 if (action.is_action_legacy()) {
1595 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, agrp, ANIMTYPE_GROUP, owner_id, &act->id);
1596 }
1597 else {
1598 ANIMCHANNEL_NEW_CHANNEL_FULL(ac->bmain, agrp, ANIMTYPE_GROUP, owner_id, &act->id, {
1599 ale->slot_handle = slot_handle;
1600 });
1601 }
1602 }
1603 }
1604
1605 /* now add the list of collected channels */
1606 BLI_movelisttolist(anim_data, &tmp_data);
1608 items += tmp_items;
1609 }
1610
1611 /* return the number of items added to the list */
1612 return items;
1613}
1614
1616 ListBase * /* bAnimListElem */ anim_data,
1617 animrig::Action &action,
1618 animrig::Slot &slot,
1619 const eAnimFilter_Flags filter_mode,
1620 ID *animated_id)
1621{
1622 BLI_assert(ac);
1623
1624 /* In some cases (see `ob_to_keylist()` and friends) fake bDopeSheet and fake bAnimContext are
1625 * created. These are mostly null-initialized, and so do not have a bmain. This means that
1626 * lookup of the animated ID is not possible, which can result in failure to look up the proper
1627 * F-Curve display name. For the `..._to_keylist` functions that doesn't matter, as those are
1628 * only interested in the key data anyway. So rather than trying to get a reliable `bmain`
1629 * through the maze, this code just treats it as optional (even though ideally it should always
1630 * be known). */
1631 ID *slot_user_id = nullptr;
1632 if (ac->bmain) {
1633 slot_user_id = animrig::action_slot_get_id_best_guess(*ac->bmain, slot, animated_id);
1634 }
1635 if (!slot_user_id) {
1636 BLI_assert(animated_id);
1637 /* At the time of writing this (PR #134922), downstream code (see e.g.
1638 * `animfilter_fcurves_span()`) assumes this is non-null, so we need to set
1639 * it to *something*. If it's not an actual user of the slot then channels
1640 * might not resolve to an actual property and thus be displayed oddly in
1641 * the channel list, but that's not technically a problem, it's just a
1642 * little strange for the end user. */
1643 slot_user_id = animated_id;
1644 }
1645
1646 /* Don't include anything from this animation if it is linked in from another
1647 * file, and we're getting stuff for editing... */
1648 if ((filter_mode & ANIMFILTER_FOREDIT) &&
1649 (ID_IS_LINKED(&action) || ID_IS_OVERRIDE_LIBRARY(&action)))
1650 {
1651 return 0;
1652 }
1653
1654 const bool selection_matters = filter_mode & (ANIMFILTER_SEL | ANIMFILTER_UNSEL);
1655 const bool must_be_selected = filter_mode & ANIMFILTER_SEL;
1656 const bool selection_ok_for_slot = !selection_matters || slot.is_selected() == must_be_selected;
1657
1658 int items = 0;
1659
1660 /* Add a list element for the Slot itself, but only if in Action mode. The Dopesheet mode
1661 * shouldn't display Slots, as F-Curves are always shown in the context of the animated ID
1662 * anyway. */
1663 const bool is_action_mode = (ac->spacetype == SPACE_ACTION &&
1665 const bool show_active_group_only = filter_mode & ANIMFILTER_ACTGROUPED;
1666 const bool include_summary_channels = (filter_mode & ANIMFILTER_LIST_CHANNELS);
1667 const bool show_slot_channel = (is_action_mode && selection_ok_for_slot &&
1668 include_summary_channels);
1669 if (show_slot_channel) {
1670 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, &slot, ANIMTYPE_ACTION_SLOT, slot_user_id, &action.id);
1671 items++;
1672 }
1673
1674 /* If the 'list visible' flag is used, the expansion state of the Slot
1675 * matters. Otherwise the sub-channels can always be listed. */
1676 const bool visible_only = (filter_mode & ANIMFILTER_LIST_VISIBLE);
1677 const bool expansion_is_ok = !visible_only || !show_slot_channel || slot.is_expanded();
1678
1680 if (channelbag == nullptr) {
1681 return items;
1682 }
1683
1684 if (!expansion_is_ok) {
1685 return items;
1686 }
1687
1688 /* Add channel groups and their member channels. */
1689 for (bActionGroup *group : channelbag->channel_groups()) {
1690 items += animfilter_act_group(
1691 ac, anim_data, &action, slot.handle, group, filter_mode, slot_user_id);
1692 }
1693
1694 /* Add ungrouped channels. */
1695 if (!show_active_group_only) {
1696 int first_ungrouped_fcurve_index = 0;
1697 if (!channelbag->channel_groups().is_empty()) {
1698 const bActionGroup *last_group = channelbag->channel_groups().last();
1699 first_ungrouped_fcurve_index = last_group->fcurve_range_start +
1700 last_group->fcurve_range_length;
1701 }
1702
1703 Span<FCurve *> fcurves = channelbag->fcurves().drop_front(first_ungrouped_fcurve_index);
1704 items += animfilter_fcurves_span(
1705 ac, anim_data, fcurves, slot.handle, filter_mode, slot_user_id, &action.id);
1706 }
1707
1708 return items;
1709}
1710
1712 ListBase *anim_data,
1713 animrig::Action &action,
1714 const eAnimFilter_Flags filter_mode,
1715 ID *owner_id)
1716{
1717 /* Don't include anything from this animation if it is linked in from another
1718 * file, and we're getting stuff for editing... */
1719 if ((filter_mode & ANIMFILTER_FOREDIT) &&
1720 (ID_IS_LINKED(&action) || ID_IS_OVERRIDE_LIBRARY(&action)))
1721 {
1722 return 0;
1723 }
1724
1725 int num_items = 0;
1726 for (animrig::Slot *slot : action.slots()) {
1727 BLI_assert(slot);
1728
1729 num_items += ANIM_animfilter_action_slot(ac, anim_data, action, *slot, filter_mode, owner_id);
1730 }
1731
1732 return num_items;
1733}
1734
1736 ListBase *anim_data,
1737 animrig::Action &action,
1738 const animrig::slot_handle_t slot_handle,
1739 const eAnimFilter_Flags filter_mode,
1740 ID *owner_id)
1741{
1742 FCurve *lastchan = nullptr;
1743 size_t items = 0;
1744
1745 if (action.is_empty()) {
1746 return 0;
1747 }
1748
1749 /* don't include anything from this action if it is linked in from another file,
1750 * and we're getting stuff for editing...
1751 */
1752 if ((filter_mode & ANIMFILTER_FOREDIT) &&
1753 (!ID_IS_EDITABLE(&action) || ID_IS_OVERRIDE_LIBRARY(&action)))
1754 {
1755 return 0;
1756 }
1757
1758 if (action.is_action_legacy()) {
1759 LISTBASE_FOREACH (bActionGroup *, agrp, &action.groups) {
1760 /* Store reference to last channel of group. */
1761 if (agrp->channels.last) {
1762 lastchan = static_cast<FCurve *>(agrp->channels.last);
1763 }
1764
1765 items += animfilter_act_group(
1766 ac, anim_data, &action, animrig::Slot::unassigned, agrp, filter_mode, owner_id);
1767 }
1768
1769 /* Un-grouped F-Curves (only if we're not only considering those channels in
1770 * the active group) */
1771 if (!(filter_mode & ANIMFILTER_ACTGROUPED)) {
1772 FCurve *firstfcu = (lastchan) ? (lastchan->next) :
1773 static_cast<FCurve *>(action.curves.first);
1774 items += animfilter_fcurves(
1775 ac, anim_data, firstfcu, ANIMTYPE_FCURVE, filter_mode, nullptr, owner_id, &action.id);
1776 }
1777
1778 return items;
1779 }
1780
1781 /* For now we don't show layers anywhere, just the contained F-Curves. */
1782
1783 /* Only show all Slots in Action editor mode. Otherwise the F-Curves ought to be displayed
1784 * underneath their animated ID anyway. */
1785 const bool is_action_mode = (ac->spacetype == SPACE_ACTION &&
1787 const bool show_active_only = (ac->filters.flag & ADS_FILTER_ONLY_SLOTS_OF_ACTIVE);
1788 if (is_action_mode && !show_active_only) {
1789 return animfilter_action_slots(ac, anim_data, action, filter_mode, owner_id);
1790 }
1791
1792 animrig::Slot *slot = action.slot_for_handle(slot_handle);
1793 if (!slot) {
1794 /* Can happen when an Action is assigned, but not a Slot. */
1795 return 0;
1796 }
1797 return ANIM_animfilter_action_slot(ac, anim_data, action, *slot, filter_mode, owner_id);
1798}
1799
1800/* Include NLA-Data for NLA-Editor:
1801 * - When ANIMFILTER_LIST_CHANNELS is used, that means we should be filtering the list for display
1802 * Although the evaluation order is from the first track to the last and then apply the
1803 * Action on top, we present this in the UI as the Active Action followed by the last track
1804 * to the first so that we get the evaluation order presented as per a stack.
1805 * - For normal filtering (i.e. for editing),
1806 * we only need the NLA-tracks but they can be in 'normal' evaluation order, i.e. first to last.
1807 * Otherwise, some tools may get screwed up.
1808 */
1810 ListBase *anim_data,
1811 AnimData *adt,
1812 const eAnimFilter_Flags filter_mode,
1813 ID *owner_id)
1814{
1815 NlaTrack *nlt;
1816 NlaTrack *first = nullptr, *next = nullptr;
1817 size_t items = 0;
1818
1819 /* if showing channels, include active action */
1820 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1821 /* if NLA action-line filtering is off, don't show unless there are keyframes,
1822 * in order to keep things more compact for doing transforms
1823 */
1824 if (!(ac->filters.flag & ADS_FILTER_NLA_NOACT) || (adt->action)) {
1825 /* there isn't really anything editable here, so skip if need editable */
1826 if ((filter_mode & ANIMFILTER_FOREDIT) == 0) {
1827 /* Just add the action track now (this MUST appear for drawing):
1828 * - As AnimData may not have an action,
1829 * we pass a dummy pointer just to get the list elem created,
1830 * then overwrite this with the real value - REVIEW THIS.
1831 */
1833 ac->bmain, (void *)(&adt->action), ANIMTYPE_NLAACTION, owner_id, nullptr, {
1834 ale->data = adt->action ? adt->action : nullptr;
1835 });
1836 }
1837 }
1838
1839 /* first track to include will be the last one if we're filtering by channels */
1840 first = static_cast<NlaTrack *>(adt->nla_tracks.last);
1841 }
1842 else {
1843 /* first track to include will the first one (as per normal) */
1844 first = static_cast<NlaTrack *>(adt->nla_tracks.first);
1845 }
1846
1847 /* loop over NLA Tracks -
1848 * assume that the caller of this has already checked that these should be included */
1849 for (nlt = first; nlt; nlt = next) {
1850 /* 'next' NLA-Track to use depends on whether we're filtering for drawing or not */
1851 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1852 next = nlt->prev;
1853 }
1854 else {
1855 next = nlt->next;
1856 }
1857
1858 /* only work with this channel and its subchannels if it is editable */
1859 if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_NLT(nlt)) {
1860 /* only include this track if selected in a way consistent with the filtering requirements */
1861 if (ANIMCHANNEL_SELOK(SEL_NLT(nlt))) {
1862 /* only include if this track is active */
1863 if (!(filter_mode & ANIMFILTER_ACTIVE) || (nlt->flag & NLATRACK_ACTIVE)) {
1864 /* name based filtering... */
1865 if (((ac->ads) && (ac->ads->searchstr[0] != '\0')) && (owner_id)) {
1866 bool track_ok = false, strip_ok = false;
1867
1868 /* check if the name of the track, or the strips it has are ok... */
1869 track_ok = name_matches_dopesheet_filter(ac->ads, nlt->name);
1870
1871 if (track_ok == false) {
1872 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
1873 if (name_matches_dopesheet_filter(ac->ads, strip->name)) {
1874 strip_ok = true;
1875 break;
1876 }
1877 }
1878 }
1879
1880 /* skip if both fail this test... */
1881 if (!track_ok && !strip_ok) {
1882 continue;
1883 }
1884 }
1885
1886 /* add the track now that it has passed all our tests */
1887 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, nlt, ANIMTYPE_NLATRACK, owner_id, nullptr);
1888 }
1889 }
1890 }
1891 }
1892
1893 /* return the number of items added to the list */
1894 return items;
1895}
1896
1897/* Include the control FCurves per NLA Strip in the channel list
1898 * NOTE: This is includes the expander too...
1899 */
1901 ListBase *anim_data,
1902 AnimData *adt,
1903 eAnimFilter_Flags filter_mode,
1904 ID *owner_id)
1905{
1906 ListBase tmp_data = {nullptr, nullptr};
1907 size_t tmp_items = 0;
1908 size_t items = 0;
1909
1910 /* add control curves from each NLA strip... */
1911 /* NOTE: ANIMTYPE_FCURVES are created here, to avoid duplicating the code needed */
1913 /* for now, we only go one level deep - so controls on grouped FCurves are not handled */
1915 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
1916 /* pass strip as the "owner",
1917 * so that the name lookups (used while filtering) will resolve */
1918 /* NLA tracks are coming from AnimData, so owner of f-curves
1919 * is the same as owner of animation data. */
1920 tmp_items += animfilter_fcurves(ac,
1921 &tmp_data,
1922 static_cast<FCurve *>(strip->fcurves.first),
1924 filter_mode,
1925 strip,
1926 owner_id,
1927 owner_id);
1928 }
1929 }
1930 }
1932
1933 /* did we find anything? */
1934 if (tmp_items) {
1935 /* add the expander as a channel first */
1936 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
1937 /* currently these channels cannot be selected, so they should be skipped */
1938 if ((filter_mode & (ANIMFILTER_SEL | ANIMFILTER_UNSEL)) == 0) {
1939 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, adt, ANIMTYPE_NLACONTROLS, owner_id, nullptr);
1940 }
1941 }
1942
1943 /* now add the list of collected channels */
1944 BLI_movelisttolist(anim_data, &tmp_data);
1946 items += tmp_items;
1947 }
1948
1949 /* return the number of items added to the list */
1950 return items;
1951}
1952
1953/* determine what animation data from AnimData block should get displayed */
1955 ListBase *anim_data,
1956 ID *id,
1957 const eAnimFilter_Flags filter_mode)
1958{
1960 size_t items = 0;
1961
1962 /* image object data-blocks have no anim-data so check for nullptr */
1963 if (adt) {
1964 IdAdtTemplate *iat = reinterpret_cast<IdAdtTemplate *>(id);
1965
1966 /* NOTE: this macro is used instead of inlining the logic here,
1967 * since this sort of filtering is still needed in a few places in the rest of the code still -
1968 * notably for the few cases where special mode-based
1969 * different types of data expanders are required.
1970 */
1972 iat,
1973 { /* AnimData */
1974 /* specifically filter animdata block */
1977 }
1978 },
1979 { /* NLA */
1980 items += animfilter_nla(ac, anim_data, adt, filter_mode, id);
1981 },
1982 { /* Drivers */
1983 items += animfilter_fcurves(ac,
1984 anim_data,
1985 static_cast<FCurve *>(adt->drivers.first),
1987 filter_mode,
1988 nullptr,
1989 id,
1990 id);
1991 },
1992 { /* NLA Control Keyframes */
1993 items += animfilter_nla_controls(ac, anim_data, adt, filter_mode, id);
1994 },
1995 { /* Keyframes from legacy Action. */
1996 items += animfilter_action(ac,
1997 anim_data,
1998 adt->action->wrap(),
2000 eAnimFilter_Flags(filter_mode),
2001 id);
2002 },
2003 { /* Keyframes from layered Action. */
2004 items += animfilter_action(ac,
2005 anim_data,
2006 adt->action->wrap(),
2008 eAnimFilter_Flags(filter_mode),
2009 id);
2010 });
2011 }
2012
2013 return items;
2014}
2015
2016/* Include ShapeKey Data for ShapeKey Editor */
2018 ListBase *anim_data,
2019 Key *key,
2020 const eAnimFilter_Flags filter_mode)
2021{
2022 using namespace blender::animrig;
2023
2024 size_t items = 0;
2025
2026 /* check if channels or only F-Curves */
2027 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2028 bDopeSheet *ads = ac->ads;
2029
2030 if (key->type == KEY_RELATIVE) {
2031 /* TODO: This currently doesn't take into account the animatable "Range Min/Max" keys on the
2032 * key-blocks. */
2033
2034 /* loop through the channels adding ShapeKeys as appropriate */
2035 LISTBASE_FOREACH (KeyBlock *, kb, &key->block) {
2036 /* skip the first one, since that's the non-animatable basis */
2037 if (kb == key->block.first) {
2038 continue;
2039 }
2040
2041 /* Skip shapekey if the name doesn't match the filter string. */
2042 if (ads != nullptr && ads->searchstr[0] != '\0' &&
2043 name_matches_dopesheet_filter(ads, kb->name) == false)
2044 {
2045 continue;
2046 }
2047
2048 /* only work with this channel and its subchannels if it is editable */
2049 if (!(filter_mode & ANIMFILTER_FOREDIT) || EDITABLE_SHAPEKEY(kb)) {
2050 /* Only include this track if selected in a way consistent
2051 * with the filtering requirements. */
2053 /* TODO: consider 'active' too? */
2054
2055 /* owner-id here must be key so that the F-Curve can be resolved... */
2056 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, kb, ANIMTYPE_SHAPEKEY, key, nullptr);
2057 }
2058 }
2059 }
2060 }
2061 else {
2062 /* key->type == KEY_NORMAL */
2063 if (!key->adt || !key->adt->action) {
2064 return 0;
2065 }
2066 Action &action = key->adt->action->wrap();
2067
2068 Vector<FCurve *> key_fcurves;
2069 for (FCurve *fcurve : fcurves_for_action_slot(action, key->adt->slot_handle)) {
2070 if (STREQ(fcurve->rna_path, "eval_time") ||
2071 BLI_str_endswith(fcurve->rna_path, ".interpolation"))
2072 {
2073 key_fcurves.append(fcurve);
2074 }
2075 }
2076
2077 items += animfilter_fcurves_span(
2078 ac, anim_data, key_fcurves, key->adt->slot_handle, filter_mode, &key->id, &action.id);
2079 }
2080 }
2081 else {
2082 /* just use the action associated with the shapekey */
2083 /* TODO: somehow manage to pass dopesheet info down here too? */
2084 if (key->adt) {
2085 if (filter_mode & ANIMFILTER_ANIMDATA) {
2086 if (ANIMCHANNEL_SELOK(SEL_ANIMDATA(key->adt))) {
2087 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, key->adt, ANIMTYPE_ANIMDATA, key, nullptr);
2088 }
2089 }
2090 else if (key->adt->action) {
2091 items = animfilter_action(ac,
2092 anim_data,
2093 key->adt->action->wrap(),
2094 key->adt->slot_handle,
2095 eAnimFilter_Flags(filter_mode),
2096 reinterpret_cast<ID *>(key));
2097 }
2098 }
2099 }
2100
2101 /* return the number of items added to the list */
2102 return items;
2103}
2104
2105/* Helper for Grease Pencil - layers within a data-block. */
2106
2108 ListBase *anim_data,
2109 GreasePencil *grease_pencil,
2111 const eAnimFilter_Flags filter_mode)
2112{
2113
2114 size_t items = 0;
2115
2116 /* Only if the layer is selected. */
2117 if (!ANIMCHANNEL_SELOK(layer.is_selected())) {
2118 return items;
2119 }
2120
2121 /* Only if the layer is editable. */
2122 if ((filter_mode & ANIMFILTER_FOREDIT) && layer.is_locked()) {
2123 return items;
2124 }
2125
2126 /* Only if the layer is active. */
2127 if ((filter_mode & ANIMFILTER_ACTIVE) && grease_pencil->is_layer_active(&layer)) {
2128 return items;
2129 }
2130
2131 /* Skip empty layers. */
2132 if (layer.is_empty()) {
2133 return items;
2134 }
2135
2136 /* Add layer channel. */
2138 static_cast<void *>(&layer),
2140 grease_pencil,
2141 nullptr);
2142
2143 return items;
2144}
2145
2147 bAnimContext *ac,
2148 ListBase *anim_data,
2149 GreasePencil *grease_pencil,
2151 eAnimFilter_Flags filter_mode)
2152{
2153 using namespace blender::bke::greasepencil;
2154 size_t items = 0;
2155
2156 /* Skip node if the name doesn't match the filter string. */
2157 const bool name_search = (ac->ads->searchstr[0] != '\0');
2158 const bool skip_node = name_search &&
2159 !name_matches_dopesheet_filter(ac->ads, node.name().c_str());
2160
2161 if (node.is_layer() && !skip_node) {
2163 ac, anim_data, grease_pencil, node.as_layer(), filter_mode);
2164 }
2165 else if (node.is_group()) {
2166 const LayerGroup &layer_group = node.as_group();
2167
2168 ListBase tmp_data = {nullptr, nullptr};
2169 size_t tmp_items = 0;
2170
2171 /* Add grease pencil layer channels. */
2175 ac, &tmp_data, grease_pencil, node_->wrap(), filter_mode);
2176 }
2177 }
2179
2180 if ((tmp_items == 0) && !name_search) {
2181 /* If no sub-channels, return early.
2182 * Except if the search by name is on, because we might want to display the layer group alone
2183 * in that case. */
2184 return items;
2185 }
2186
2187 if ((filter_mode & ANIMFILTER_LIST_CHANNELS) && !skip_node) {
2188 /* Add data block container (if for drawing, and it contains sub-channels). */
2190 static_cast<void *>(&node),
2192 grease_pencil,
2193 nullptr);
2194 }
2195
2196 /* Add the list of collected channels. */
2197 BLI_movelisttolist(anim_data, &tmp_data);
2199 items += tmp_items;
2200 }
2201 return items;
2202}
2203
2205 ListBase *anim_data,
2206 GreasePencil *grease_pencil,
2207 const eAnimFilter_Flags filter_mode)
2208{
2209 size_t items = 0;
2210
2212 GreasePencilLayerTreeNode *, node, &grease_pencil->root_group_ptr->children)
2213 {
2215 ac, anim_data, grease_pencil, node->wrap(), filter_mode);
2216 }
2217
2218 return items;
2219}
2220
2221/* Helper for Grease Pencil - layers within a data-block. */
2223 ListBase *anim_data,
2224 bGPdata *gpd,
2225 const eAnimFilter_Flags filter_mode)
2226{
2227 size_t items = 0;
2228
2229 /* loop over layers as the conditions are acceptable (top-Down order) */
2231 /* only if selected */
2232 if (!ANIMCHANNEL_SELOK(SEL_GPL(gpl))) {
2233 continue;
2234 }
2235
2236 /* only if editable */
2237 if ((filter_mode & ANIMFILTER_FOREDIT) && !EDITABLE_GPL(gpl)) {
2238 continue;
2239 }
2240
2241 /* active... */
2242 if ((filter_mode & ANIMFILTER_ACTIVE) && (gpl->flag & GP_LAYER_ACTIVE) == 0) {
2243 continue;
2244 }
2245
2246 /* skip layer if the name doesn't match the filter string */
2247 if (ac->ads != nullptr && ac->ads->searchstr[0] != '\0' &&
2248 name_matches_dopesheet_filter(ac->ads, gpl->info) == false)
2249 {
2250 continue;
2251 }
2252
2253 /* Skip empty layers. */
2254 if (BLI_listbase_is_empty(&gpl->frames)) {
2255 continue;
2256 }
2257
2258 /* add to list */
2259 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, gpl, ANIMTYPE_GPLAYER, gpd, nullptr);
2260 }
2261
2262 return items;
2263}
2264
2266 ListBase *anim_data,
2267 GreasePencil *grease_pencil,
2268 eAnimFilter_Flags filter_mode)
2269{
2270 using namespace blender;
2271
2272 size_t items = 0;
2273
2274 /* The Grease Pencil mode is not supposed to show channels for regular F-Curves from regular
2275 * Actions. At some point this might be desirable, but it would also require changing the
2276 * filtering flags for pretty much all operators running there. */
2277 const bool show_animdata = grease_pencil->adt && (ac->datatype != ANIMCONT_GPENCIL);
2278
2279 /* When asked from "AnimData" blocks (i.e. the top-level containers for normal animation),
2280 * for convenience, this will return grease pencil data-blocks instead.
2281 * This may cause issues down the track, but for now, this will do.
2282 */
2283 if (filter_mode & ANIMFILTER_ANIMDATA) {
2284 if (show_animdata) {
2285 items += animfilter_block_data(
2286 ac, anim_data, reinterpret_cast<ID *>(grease_pencil), filter_mode);
2287 }
2288 }
2289 else {
2290 ListBase tmp_data = {nullptr, nullptr};
2291 size_t tmp_items = 0;
2292
2293 /* Add grease pencil layer channels. */
2295 if (show_animdata) {
2296 tmp_items += animfilter_block_data(
2297 ac, &tmp_data, reinterpret_cast<ID *>(grease_pencil), filter_mode);
2298 }
2299
2300 if (!(filter_mode & ANIMFILTER_FCURVESONLY)) {
2302 ac, &tmp_data, grease_pencil, filter_mode);
2303 }
2304 }
2306
2307 if (tmp_items == 0) {
2308 /* If no sub-channels, return early. */
2309 return items;
2310 }
2311
2312 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2313 /* Add data block container (if for drawing, and it contains sub-channels). */
2315 ac->bmain, grease_pencil, ANIMTYPE_GREASE_PENCIL_DATABLOCK, grease_pencil, nullptr);
2316 }
2317
2318 /* Add the list of collected channels. */
2319 BLI_movelisttolist(anim_data, &tmp_data);
2321 items += tmp_items;
2322 }
2323
2324 return items;
2325}
2326
2328 ListBase *anim_data,
2329 const eAnimFilter_Flags filter_mode)
2330{
2331 size_t items = 0;
2332 Scene *scene = ac->scene;
2333 ViewLayer *view_layer = ac->view_layer;
2334 bDopeSheet *ads = ac->ads;
2335
2336 BKE_view_layer_synced_ensure(scene, view_layer);
2338 if (!base->object || (base->object->type != OB_GREASE_PENCIL)) {
2339 continue;
2340 }
2341 Object *ob = base->object;
2342
2343 if ((filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ac->filters.flag & ADS_FILTER_INCL_HIDDEN)) {
2344 /* Layer visibility - we check both object and base,
2345 * since these may not be in sync yet. */
2346 if ((base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) == 0 ||
2348 {
2349 continue;
2350 }
2351
2352 /* Outliner restrict-flag */
2354 continue;
2355 }
2356 }
2357
2358 /* Check selection and object type filters */
2359 if ((ac->filters.flag & ADS_FILTER_ONLYSEL) && !(base->flag & BASE_SELECTED)) {
2360 /* Only selected should be shown */
2361 continue;
2362 }
2363
2364 if (ads->filter_grp != nullptr) {
2366 continue;
2367 }
2368 }
2369
2371 ac, anim_data, static_cast<GreasePencil *>(ob->data), filter_mode);
2372 }
2373
2374 /* Return the number of items added to the list */
2375 return items;
2376}
2377
2378/* Helper for Grease Pencil data integrated with main DopeSheet */
2380 ListBase *anim_data,
2381 bGPdata *gpd,
2382 eAnimFilter_Flags filter_mode)
2383{
2384 ListBase tmp_data = {nullptr, nullptr};
2385 size_t tmp_items = 0;
2386 size_t items = 0;
2387
2388 /* add relevant animation channels for Grease Pencil */
2390 /* add animation channels */
2391 tmp_items += animfilter_block_data(ac, &tmp_data, &gpd->id, filter_mode);
2392
2393 /* add Grease Pencil layers */
2394 if (!(filter_mode & ANIMFILTER_FCURVESONLY)) {
2395 tmp_items += animdata_filter_gpencil_layers_data_legacy(ac, &tmp_data, gpd, filter_mode);
2396 }
2397
2398 /* TODO: do these need a separate expander?
2399 * XXX: what order should these go in? */
2400 }
2402
2403 /* did we find anything? */
2404 if (tmp_items) {
2405 /* include data-expand widget first */
2406 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2407 /* check if filtering by active status */
2408 /* XXX: active check here needs checking */
2409 if (ANIMCHANNEL_ACTIVEOK(gpd)) {
2410 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, gpd, ANIMTYPE_DSGPENCIL, gpd, nullptr);
2411 }
2412 }
2413
2414 /* now add the list of collected channels */
2415 BLI_movelisttolist(anim_data, &tmp_data);
2417 items += tmp_items;
2418 }
2419
2420 /* return the number of items added to the list */
2421 return items;
2422}
2423
2424/* Helper for Cache File data integrated with main DopeSheet */
2426 ListBase *anim_data,
2427 CacheFile *cache_file,
2428 eAnimFilter_Flags filter_mode)
2429{
2430 ListBase tmp_data = {nullptr, nullptr};
2431 size_t tmp_items = 0;
2432 size_t items = 0;
2433
2434 /* add relevant animation channels for Cache File */
2436 /* add animation channels */
2437 tmp_items += animfilter_block_data(ac, &tmp_data, &cache_file->id, filter_mode);
2438 }
2440
2441 /* did we find anything? */
2442 if (tmp_items) {
2443 /* include data-expand widget first */
2444 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2445 /* check if filtering by active status */
2446 /* XXX: active check here needs checking */
2447 if (ANIMCHANNEL_ACTIVEOK(cache_file)) {
2448 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, cache_file, ANIMTYPE_DSCACHEFILE, cache_file, nullptr);
2449 }
2450 }
2451
2452 /* now add the list of collected channels */
2453 BLI_movelisttolist(anim_data, &tmp_data);
2455 items += tmp_items;
2456 }
2457
2458 /* return the number of items added to the list */
2459 return items;
2460}
2461
2462/* Helper for Mask Editing - mask layers */
2464 ListBase *anim_data,
2465 Mask *mask,
2466 const eAnimFilter_Flags filter_mode)
2467{
2469 size_t items = 0;
2470
2471 LISTBASE_FOREACH (MaskLayer *, masklay, &mask->masklayers) {
2472 if (!ANIMCHANNEL_SELOK(SEL_MASKLAY(masklay))) {
2473 continue;
2474 }
2475
2476 if ((filter_mode & ANIMFILTER_FOREDIT) && !EDITABLE_MASK(masklay)) {
2477 continue;
2478 }
2479
2480 if ((filter_mode & ANIMFILTER_ACTIVE) & (masklay_act != masklay)) {
2481 continue;
2482 }
2483
2484 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, masklay, ANIMTYPE_MASKLAYER, mask, nullptr);
2485 }
2486
2487 return items;
2488}
2489
2490/* Grab all mask data */
2492 ListBase *anim_data,
2493 void * /*data*/,
2494 eAnimFilter_Flags filter_mode)
2495{
2496 size_t items = 0;
2497
2498 /* For now, grab mask data-blocks directly from main. */
2499 /* XXX: this is not good... */
2500 LISTBASE_FOREACH (Mask *, mask, &ac->bmain->masks) {
2501 ListBase tmp_data = {nullptr, nullptr};
2502 size_t tmp_items = 0;
2503
2504 /* only show if mask is used by something... */
2505 if (ID_REAL_USERS(mask) < 1) {
2506 continue;
2507 }
2508
2509 /* add mask animation channels */
2510 if (!(filter_mode & ANIMFILTER_FCURVESONLY)) {
2512 tmp_items += animdata_filter_mask_data(ac, &tmp_data, mask, filter_mode);
2513 }
2515 }
2516
2517 /* did we find anything? */
2518 if (!tmp_items) {
2519 continue;
2520 }
2521
2522 /* include data-expand widget first */
2523 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2524 /* add mask data-block as channel too (if for drawing, and it has layers) */
2526 }
2527
2528 /* now add the list of collected channels */
2529 BLI_movelisttolist(anim_data, &tmp_data);
2531 items += tmp_items;
2532 }
2533
2534 /* return the number of items added to the list */
2535 return items;
2536}
2537
2538/* NOTE: owner_id is scene, material, or texture block,
2539 * which is the direct owner of the node tree in question. */
2541 ListBase *anim_data,
2542 ID *owner_id,
2543 bNodeTree *ntree,
2544 eAnimFilter_Flags filter_mode)
2545{
2546 ListBase tmp_data = {nullptr, nullptr};
2547 size_t tmp_items = 0;
2548 size_t items = 0;
2549
2550 /* add nodetree animation channels */
2552 /* animation data filtering */
2553 tmp_items += animfilter_block_data(ac, &tmp_data, reinterpret_cast<ID *>(ntree), filter_mode);
2554 }
2556
2557 /* did we find anything? */
2558 if (tmp_items) {
2559 /* include data-expand widget first */
2560 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2561 /* check if filtering by active status */
2562 if (ANIMCHANNEL_ACTIVEOK(ntree)) {
2563 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, ntree, ANIMTYPE_DSNTREE, owner_id, nullptr);
2564 }
2565 }
2566
2567 /* now add the list of collected channels */
2568 BLI_movelisttolist(anim_data, &tmp_data);
2570 items += tmp_items;
2571 }
2572
2573 /* return the number of items added to the list */
2574 return items;
2575}
2576
2578 ListBase *anim_data,
2579 ID *owner_id,
2580 bNodeTree *ntree,
2581 const eAnimFilter_Flags filter_mode)
2582{
2583 size_t items = 0;
2584
2585 items += animdata_filter_ds_nodetree_group(ac, anim_data, owner_id, ntree, filter_mode);
2586
2587 for (bNode *node : ntree->all_nodes()) {
2588 if (node->is_group()) {
2589 if (node->id) {
2590 if ((ac->filters.flag & ADS_FILTER_ONLYSEL) && (node->flag & NODE_SELECT) == 0) {
2591 continue;
2592 }
2593 /* Recurse into the node group */
2594 items += animdata_filter_ds_nodetree(ac,
2595 anim_data,
2596 owner_id,
2597 reinterpret_cast<bNodeTree *>(node->id),
2598 filter_mode | ANIMFILTER_TMP_IGNORE_ONLYSEL);
2599 }
2600 }
2601 }
2602
2603 return items;
2604}
2605
2607 ListBase *anim_data,
2608 Scene *sce,
2609 eAnimFilter_Flags filter_mode)
2610{
2611 size_t items = 0;
2612
2613 LISTBASE_FOREACH (ViewLayer *, view_layer, &sce->view_layers) {
2614 LISTBASE_FOREACH (FreestyleLineSet *, lineset, &view_layer->freestyle_config.linesets) {
2615 if (lineset->linestyle) {
2616 lineset->linestyle->id.tag |= ID_TAG_DOIT;
2617 }
2618 }
2619 }
2620
2621 LISTBASE_FOREACH (ViewLayer *, view_layer, &sce->view_layers) {
2622 /* skip render layers without Freestyle enabled */
2623 if ((view_layer->flag & VIEW_LAYER_FREESTYLE) == 0) {
2624 continue;
2625 }
2626
2627 /* loop over linesets defined in the render layer */
2628 LISTBASE_FOREACH (FreestyleLineSet *, lineset, &view_layer->freestyle_config.linesets) {
2629 FreestyleLineStyle *linestyle = lineset->linestyle;
2630 ListBase tmp_data = {nullptr, nullptr};
2631 size_t tmp_items = 0;
2632
2633 if ((linestyle == nullptr) || !(linestyle->id.tag & ID_TAG_DOIT)) {
2634 continue;
2635 }
2636 linestyle->id.tag &= ~ID_TAG_DOIT;
2637
2638 /* add scene-level animation channels */
2640 /* animation data filtering */
2641 tmp_items += animfilter_block_data(
2642 ac, &tmp_data, reinterpret_cast<ID *>(linestyle), filter_mode);
2643 }
2645
2646 /* did we find anything? */
2647 if (tmp_items) {
2648 /* include anim-expand widget first */
2649 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2650 /* check if filtering by active status */
2651 if (ANIMCHANNEL_ACTIVEOK(linestyle)) {
2652 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, linestyle, ANIMTYPE_DSLINESTYLE, sce, nullptr);
2653 }
2654 }
2655
2656 /* now add the list of collected channels */
2657 BLI_movelisttolist(anim_data, &tmp_data);
2659 items += tmp_items;
2660 }
2661 }
2662 }
2663
2664 /* return the number of items added to the list */
2665 return items;
2666}
2667
2669 bAnimContext *ac, ListBase *anim_data, Tex *tex, ID *owner_id, eAnimFilter_Flags filter_mode)
2670{
2671 ListBase tmp_data = {nullptr, nullptr};
2672 size_t tmp_items = 0;
2673 size_t items = 0;
2674
2675 /* add texture's animation data to temp collection */
2677 /* texture animdata */
2678 tmp_items += animfilter_block_data(ac, &tmp_data, reinterpret_cast<ID *>(tex), filter_mode);
2679
2680 /* nodes */
2681 if ((tex->nodetree) && !(ac->filters.flag & ADS_FILTER_NONTREE)) {
2682 /* owner_id as id instead of texture,
2683 * since it'll otherwise be impossible to track the depth. */
2684
2685 /* FIXME: perhaps as a result, textures should NOT be included under materials,
2686 * but under their own section instead so that free-floating textures can also be animated.
2687 */
2688 tmp_items += animdata_filter_ds_nodetree(
2689 ac, &tmp_data, reinterpret_cast<ID *>(tex), tex->nodetree, filter_mode);
2690 }
2691 }
2693
2694 /* did we find anything? */
2695 if (tmp_items) {
2696 /* include texture-expand widget? */
2697 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2698 /* check if filtering by active status */
2699 if (ANIMCHANNEL_ACTIVEOK(tex)) {
2700 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, tex, ANIMTYPE_DSTEX, owner_id, nullptr);
2701 }
2702 }
2703
2704 /* now add the list of collected channels */
2705 BLI_movelisttolist(anim_data, &tmp_data);
2707 items += tmp_items;
2708 }
2709
2710 /* return the number of items added to the list */
2711 return items;
2712}
2713
2714/* NOTE: owner_id is the direct owner of the texture stack in question
2715 * It used to be Material/Light/World before the Blender Internal removal for 2.8
2716 */
2718 ListBase *anim_data,
2719 ID *owner_id,
2720 const eAnimFilter_Flags filter_mode)
2721{
2722 MTex **mtex = nullptr;
2723 size_t items = 0;
2724 int a = 0;
2725
2726 /* get datatype specific data first */
2727 if (owner_id == nullptr) {
2728 return 0;
2729 }
2730
2731 switch (GS(owner_id->name)) {
2732 case ID_PA: {
2733 ParticleSettings *part = reinterpret_cast<ParticleSettings *>(owner_id);
2734 mtex = reinterpret_cast<MTex **>(&part->mtex);
2735 break;
2736 }
2737 default: {
2738 /* invalid/unsupported option */
2739 if (G.debug & G_DEBUG) {
2740 printf("ERROR: Unsupported owner_id (i.e. texture stack) for filter textures - %s\n",
2741 owner_id->name);
2742 }
2743 return 0;
2744 }
2745 }
2746
2747 /* Firstly check that we actually have some textures,
2748 * by gathering all textures in a temp list. */
2749 for (a = 0; a < MAX_MTEX; a++) {
2750 Tex *tex = (mtex[a]) ? mtex[a]->tex : nullptr;
2751
2752 /* for now, if no texture returned, skip (this shouldn't confuse the user I hope) */
2753 if (tex == nullptr) {
2754 continue;
2755 }
2756
2757 /* add texture's anim channels */
2758 items += animdata_filter_ds_texture(ac, anim_data, tex, owner_id, filter_mode);
2759 }
2760
2761 /* return the number of items added to the list */
2762 return items;
2763}
2764
2766 ListBase *anim_data,
2767 Material *ma,
2768 eAnimFilter_Flags filter_mode)
2769{
2770 ListBase tmp_data = {nullptr, nullptr};
2771 size_t tmp_items = 0;
2772 size_t items = 0;
2773
2774 /* add material's animation data to temp collection */
2776 /* material's animation data */
2777 tmp_items += animfilter_block_data(ac, &tmp_data, reinterpret_cast<ID *>(ma), filter_mode);
2778
2779 /* nodes */
2780 if ((ma->nodetree) && !(ac->filters.flag & ADS_FILTER_NONTREE)) {
2781 tmp_items += animdata_filter_ds_nodetree(
2782 ac, &tmp_data, reinterpret_cast<ID *>(ma), ma->nodetree, filter_mode);
2783 }
2784 }
2786
2787 /* did we find anything? */
2788 if (tmp_items) {
2789 /* include material-expand widget first */
2790 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2791 /* check if filtering by active status */
2792 if (ANIMCHANNEL_ACTIVEOK(ma)) {
2793 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, ma, ANIMTYPE_DSMAT, ma, nullptr);
2794 }
2795 }
2796
2797 /* now add the list of collected channels */
2798 BLI_movelisttolist(anim_data, &tmp_data);
2800 items += tmp_items;
2801 }
2802
2803 return items;
2804}
2805
2807 ListBase *anim_data,
2808 Object *ob,
2809 const eAnimFilter_Flags filter_mode)
2810{
2811 size_t items = 0;
2812 int a = 0;
2813
2814 /* First pass: take the materials referenced via the Material slots of the object. */
2815 for (a = 1; a <= ob->totcol; a++) {
2816 Material *ma = BKE_object_material_get(ob, a);
2817
2818 /* if material is valid, try to add relevant contents from here */
2819 if (ma) {
2820 /* add channels */
2821 items += animdata_filter_ds_material(ac, anim_data, ma, filter_mode);
2822 }
2823 }
2824
2825 /* return the number of items added to the list */
2826 return items;
2827}
2828
2829/* ............ */
2830
2831/* Temporary context for modifier linked-data channel extraction */
2833 bAnimContext *ac; /* anim editor context */
2834 bDopeSheet *ads; /* TODO: Remove this pointer from the struct and just use ac->ads. */
2835
2836 ListBase tmp_data; /* list of channels created (but not yet added to the main list) */
2837 size_t items; /* number of channels created */
2838
2839 eAnimFilter_Flags filter_mode; /* flags for stuff we want to filter */
2840};
2841
2842/* dependency walker callback for modifier dependencies */
2843static void animfilter_modifier_idpoin_cb(void *afm_ptr,
2844 Object *ob,
2845 ID **idpoin,
2846 LibraryForeachIDCallbackFlag /*cb_flag*/)
2847{
2848 tAnimFilterModifiersContext *afm = static_cast<tAnimFilterModifiersContext *>(afm_ptr);
2849 ID *owner_id = &ob->id;
2850 ID *id = *idpoin;
2851
2852 /* NOTE: the walker only guarantees to give us all the ID-ptr *slots*,
2853 * not just the ones which are actually used, so be careful!
2854 */
2855 if (id == nullptr) {
2856 return;
2857 }
2858
2859 /* check if this is something we're interested in... */
2860 switch (GS(id->name)) {
2861 case ID_TE: /* Textures */
2862 {
2863 Tex *tex = reinterpret_cast<Tex *>(id);
2864 if (!(afm->ac->filters.flag & ADS_FILTER_NOTEX)) {
2865 BLI_assert(afm->ac->ads == afm->ads);
2867 afm->ac, &afm->tmp_data, tex, owner_id, afm->filter_mode);
2868 }
2869 break;
2870 }
2871 case ID_NT: {
2872 bNodeTree *node_tree = reinterpret_cast<bNodeTree *>(id);
2873 if (!(afm->ac->filters.flag & ADS_FILTER_NONTREE)) {
2874 BLI_assert(afm->ac->ads == afm->ads);
2876 afm->ac, &afm->tmp_data, owner_id, node_tree, afm->filter_mode);
2877 }
2878 }
2879
2880 /* TODO: images? */
2881 default:
2882 break;
2883 }
2884}
2885
2886/* animation linked to data used by modifiers
2887 * NOTE: strictly speaking, modifier animation is already included under Object level
2888 * but for some modifiers (e.g. Displace), there can be linked data that has settings
2889 * which would be nice to animate (i.e. texture parameters) but which are not actually
2890 * attached to any other objects/materials/etc. in the scene
2891 */
2892/* TODO: do we want an expander for this? */
2894 ListBase *anim_data,
2895 Object *ob,
2896 const eAnimFilter_Flags filter_mode)
2897{
2898 tAnimFilterModifiersContext afm = {nullptr};
2899 size_t items = 0;
2900
2901 /* 1) create a temporary "context" containing all the info we have here to pass to the callback
2902 * use to walk through the dependencies of the modifiers
2903 *
2904 * Assumes that all other unspecified values (i.e. accumulation buffers)
2905 * are zeroed out properly!
2906 */
2907 afm.ac = ac;
2908 afm.ads = ac->ads; /* TODO: Remove this pointer from the struct and just use afm.ac->ads. */
2909 afm.filter_mode = filter_mode;
2910
2911 /* 2) walk over dependencies */
2913
2914 /* 3) extract data from the context, merging it back into the standard list */
2915 if (afm.items) {
2916 /* now add the list of collected channels */
2917 BLI_movelisttolist(anim_data, &afm.tmp_data);
2919 items += afm.items;
2920 }
2921
2922 return items;
2923}
2924
2925/* ............ */
2926
2928 ListBase *anim_data,
2929 Object *ob,
2930 eAnimFilter_Flags filter_mode)
2931{
2932 size_t items = 0;
2933
2935 ListBase tmp_data = {nullptr, nullptr};
2936 size_t tmp_items = 0;
2937
2938 /* Note that when psys->part->adt is nullptr the textures can still be
2939 * animated. */
2940 if (psys->part == nullptr) {
2941 continue;
2942 }
2943
2944 /* add particle-system's animation data to temp collection */
2946 /* particle system's animation data */
2947 tmp_items += animfilter_block_data(
2948 ac, &tmp_data, reinterpret_cast<ID *>(psys->part), filter_mode);
2949
2950 /* textures */
2951 if (!(ac->filters.flag & ADS_FILTER_NOTEX)) {
2952 tmp_items += animdata_filter_ds_textures(
2953 ac, &tmp_data, reinterpret_cast<ID *>(psys->part), filter_mode);
2954 }
2955 }
2957
2958 /* did we find anything? */
2959 if (tmp_items) {
2960 /* include particle-expand widget first */
2961 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
2962 /* check if filtering by active status */
2963 if (ANIMCHANNEL_ACTIVEOK(psys->part)) {
2964 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, psys->part, ANIMTYPE_DSPART, psys->part, nullptr);
2965 }
2966 }
2967
2968 /* now add the list of collected channels */
2969 BLI_movelisttolist(anim_data, &tmp_data);
2971 items += tmp_items;
2972 }
2973 }
2974
2975 /* return the number of items added to the list */
2976 return items;
2977}
2978
2980 ListBase *anim_data,
2981 Object *ob,
2982 eAnimFilter_Flags filter_mode)
2983{
2984 ListBase tmp_data = {nullptr, nullptr};
2985 size_t tmp_items = 0;
2986 size_t items = 0;
2987
2988 IdAdtTemplate *iat = static_cast<IdAdtTemplate *>(ob->data);
2990 short expanded = 0;
2991 const eDopeSheet_FilterFlag ads_filterflag = ac->filters.flag;
2992 const eDopeSheet_FilterFlag2 ads_filterflag2 = ac->filters.flag2;
2993
2994 /* get settings based on data type */
2995 switch (ob->type) {
2996 case OB_CAMERA: /* ------- Camera ------------ */
2997 {
2998 Camera *ca = static_cast<Camera *>(ob->data);
2999
3000 if (ads_filterflag & ADS_FILTER_NOCAM) {
3001 return 0;
3002 }
3003
3004 type = ANIMTYPE_DSCAM;
3005 expanded = FILTER_CAM_OBJD(ca);
3006 break;
3007 }
3008 case OB_LAMP: /* ---------- Light ----------- */
3009 {
3010 Light *la = static_cast<Light *>(ob->data);
3011
3012 if (ads_filterflag & ADS_FILTER_NOLAM) {
3013 return 0;
3014 }
3015
3016 type = ANIMTYPE_DSLAM;
3017 expanded = FILTER_LAM_OBJD(la);
3018 break;
3019 }
3020 case OB_CURVES_LEGACY: /* ------- Curve ---------- */
3021 case OB_SURF: /* ------- Nurbs Surface ---------- */
3022 case OB_FONT: /* ------- Text Curve ---------- */
3023 {
3024 Curve *cu = static_cast<Curve *>(ob->data);
3025
3026 if (ads_filterflag & ADS_FILTER_NOCUR) {
3027 return 0;
3028 }
3029
3030 type = ANIMTYPE_DSCUR;
3031 expanded = FILTER_CUR_OBJD(cu);
3032 break;
3033 }
3034 case OB_MBALL: /* ------- MetaBall ---------- */
3035 {
3036 MetaBall *mb = static_cast<MetaBall *>(ob->data);
3037
3038 if (ads_filterflag & ADS_FILTER_NOMBA) {
3039 return 0;
3040 }
3041
3042 type = ANIMTYPE_DSMBALL;
3043 expanded = FILTER_MBALL_OBJD(mb);
3044 break;
3045 }
3046 case OB_ARMATURE: /* ------- Armature ---------- */
3047 {
3048 bArmature *arm = static_cast<bArmature *>(ob->data);
3049
3050 if (ads_filterflag & ADS_FILTER_NOARM) {
3051 return 0;
3052 }
3053
3054 type = ANIMTYPE_DSARM;
3055 expanded = FILTER_ARM_OBJD(arm);
3056 break;
3057 }
3058 case OB_MESH: /* ------- Mesh ---------- */
3059 {
3060 Mesh *mesh = static_cast<Mesh *>(ob->data);
3061
3062 if (ads_filterflag & ADS_FILTER_NOMESH) {
3063 return 0;
3064 }
3065
3066 type = ANIMTYPE_DSMESH;
3067 expanded = FILTER_MESH_OBJD(mesh);
3068 break;
3069 }
3070 case OB_LATTICE: /* ---- Lattice ---- */
3071 {
3072 Lattice *lt = static_cast<Lattice *>(ob->data);
3073
3074 if (ads_filterflag & ADS_FILTER_NOLAT) {
3075 return 0;
3076 }
3077
3078 type = ANIMTYPE_DSLAT;
3079 expanded = FILTER_LATTICE_OBJD(lt);
3080 break;
3081 }
3082 case OB_SPEAKER: /* ---------- Speaker ----------- */
3083 {
3084 Speaker *spk = static_cast<Speaker *>(ob->data);
3085
3086 type = ANIMTYPE_DSSPK;
3087 expanded = FILTER_SPK_OBJD(spk);
3088 break;
3089 }
3090 case OB_CURVES: /* ---------- Curves ----------- */
3091 {
3092 Curves *curves = static_cast<Curves *>(ob->data);
3093
3094 if (ads_filterflag2 & ADS_FILTER_NOHAIR) {
3095 return 0;
3096 }
3097
3098 type = ANIMTYPE_DSHAIR;
3099 expanded = FILTER_CURVES_OBJD(curves);
3100 break;
3101 }
3102 case OB_POINTCLOUD: /* ---------- PointCloud ----------- */
3103 {
3104 PointCloud *pointcloud = static_cast<PointCloud *>(ob->data);
3105
3106 if (ads_filterflag2 & ADS_FILTER_NOPOINTCLOUD) {
3107 return 0;
3108 }
3109
3110 type = ANIMTYPE_DSPOINTCLOUD;
3111 expanded = FILTER_POINTS_OBJD(pointcloud);
3112 break;
3113 }
3114 case OB_VOLUME: /* ---------- Volume ----------- */
3115 {
3116 Volume *volume = static_cast<Volume *>(ob->data);
3117
3118 if (ads_filterflag2 & ADS_FILTER_NOVOLUME) {
3119 return 0;
3120 }
3121
3122 type = ANIMTYPE_DSVOLUME;
3123 expanded = FILTER_VOLUME_OBJD(volume);
3124 break;
3125 }
3126 case OB_LIGHTPROBE: /* ---------- LightProbe ----------- */
3127 {
3128 LightProbe *probe = static_cast<LightProbe *>(ob->data);
3129
3130 if (ads_filterflag2 & ADS_FILTER_NOLIGHTPROBE) {
3131 return 0;
3132 }
3133
3134 type = ANIMTYPE_DSLIGHTPROBE;
3135 expanded = FILTER_LIGHTPROBE_OBJD(probe);
3136 break;
3137 }
3138 }
3139
3140 /* add object data animation channels */
3141 BEGIN_ANIMFILTER_SUBCHANNELS (expanded) {
3142 /* animation data filtering */
3143 tmp_items += animfilter_block_data(ac, &tmp_data, reinterpret_cast<ID *>(iat), filter_mode);
3144
3145 /* sub-data filtering... */
3146 switch (ob->type) {
3147 case OB_LAMP: /* light - textures + nodetree */
3148 {
3149 Light *la = static_cast<Light *>(ob->data);
3150 bNodeTree *ntree = la->nodetree;
3151
3152 /* nodetree */
3153 if ((ntree) && !(ads_filterflag & ADS_FILTER_NONTREE)) {
3154 tmp_items += animdata_filter_ds_nodetree(ac, &tmp_data, &la->id, ntree, filter_mode);
3155 }
3156 break;
3157 }
3158 }
3159 }
3161
3162 /* did we find anything? */
3163 if (tmp_items) {
3164 /* include data-expand widget first */
3165 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
3166 /* check if filtering by active status */
3167 if (ANIMCHANNEL_ACTIVEOK(iat)) {
3168 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, iat, type, iat, nullptr);
3169 }
3170 }
3171
3172 /* now add the list of collected channels */
3173 BLI_movelisttolist(anim_data, &tmp_data);
3175 items += tmp_items;
3176 }
3177
3178 /* return the number of items added to the list */
3179 return items;
3180}
3181
3182/* shapekey-level animation */
3184 bAnimContext *ac, ListBase *anim_data, Object *ob, Key *key, eAnimFilter_Flags filter_mode)
3185{
3186 ListBase tmp_data = {nullptr, nullptr};
3187 size_t tmp_items = 0;
3188 size_t items = 0;
3189
3190 /* add shapekey-level animation channels */
3192 /* animation data filtering */
3193 tmp_items += animfilter_block_data(ac, &tmp_data, reinterpret_cast<ID *>(key), filter_mode);
3194 }
3196
3197 /* did we find anything? */
3198 if (tmp_items) {
3199 /* include key-expand widget first */
3200 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
3201 if (ANIMCHANNEL_ACTIVEOK(key)) {
3202 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, key, ANIMTYPE_DSSKEY, ob, nullptr);
3203 }
3204 }
3205
3206 /* now add the list of collected channels */
3207 BLI_movelisttolist(anim_data, &tmp_data);
3209 items += tmp_items;
3210 }
3211
3212 /* return the number of items added to the list */
3213 return items;
3214}
3215
3216/* object-level animation */
3218 ListBase *anim_data,
3219 Object *ob,
3220 eAnimFilter_Flags filter_mode)
3221{
3222 ListBase tmp_data = {nullptr, nullptr};
3223 size_t tmp_items = 0;
3224 size_t items = 0;
3225
3226 AnimData *adt = ob->adt;
3228 short expanded = 1;
3229 void *cdata = nullptr;
3230
3231 /* determine the type of expander channels to use */
3232 /* this is the best way to do this for now... */
3234 ob, /* Some useless long comment to prevent wrapping by old clang-format versions... */
3235 {/* AnimData - no channel, but consider data */},
3236 {/* NLA - no channel, but consider data */},
3237 { /* Drivers */
3238 type = ANIMTYPE_FILLDRIVERS;
3239 cdata = adt;
3240 expanded = EXPANDED_DRVD(adt);
3241 },
3242 {/* NLA Strip Controls - no dedicated channel for now (XXX) */},
3243 { /* Keyframes from legacy Action. */
3244 type = ANIMTYPE_FILLACTD;
3245 cdata = adt->action;
3246 expanded = EXPANDED_ACTC(adt->action);
3247 },
3248 { /* Keyframes from layered action. */
3250 cdata = adt->action;
3251 expanded = EXPANDED_ADT(adt);
3252 });
3253
3254 /* add object-level animation channels */
3255 BEGIN_ANIMFILTER_SUBCHANNELS (expanded) {
3256 /* animation data filtering */
3257 tmp_items += animfilter_block_data(ac, &tmp_data, reinterpret_cast<ID *>(ob), filter_mode);
3258 }
3260
3261 /* did we find anything? */
3262 if (tmp_items) {
3263 /* include anim-expand widget first */
3264 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
3265 if (type != ANIMTYPE_NONE) {
3266 /* NOTE: active-status (and the associated checks) don't apply here... */
3267 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, cdata, type, ob, nullptr);
3268 }
3269 }
3270
3271 /* now add the list of collected channels */
3272 BLI_movelisttolist(anim_data, &tmp_data);
3274 items += tmp_items;
3275 }
3276
3277 /* return the number of items added to the list */
3278 return items;
3279}
3280
3281/* get animation channels from object2 */
3283 ListBase *anim_data,
3284 Base *base,
3285 eAnimFilter_Flags filter_mode)
3286{
3287 ListBase tmp_data = {nullptr, nullptr};
3288 Object *ob = base->object;
3289 size_t tmp_items = 0;
3290 size_t items = 0;
3291 const eDopeSheet_FilterFlag ads_filterflag = ac->filters.flag;
3292
3293 /* filter data contained under object first */
3295 Key *key = BKE_key_from_object(ob);
3296
3297 /* object-level animation */
3298 if ((ob->adt) && !(ads_filterflag & ADS_FILTER_NOOBJ)) {
3299 tmp_items += animdata_filter_ds_obanim(ac, &tmp_data, ob, filter_mode);
3300 }
3301
3302 /* particle deflector textures */
3303 if (ob->pd != nullptr && ob->pd->tex != nullptr && !(ads_filterflag & ADS_FILTER_NOTEX)) {
3304 tmp_items += animdata_filter_ds_texture(ac, &tmp_data, ob->pd->tex, &ob->id, filter_mode);
3305 }
3306
3307 /* shape-key */
3308 if ((key && key->adt) && !(ads_filterflag & ADS_FILTER_NOSHAPEKEYS)) {
3309 tmp_items += animdata_filter_ds_keyanim(ac, &tmp_data, ob, key, filter_mode);
3310 }
3311
3312 /* modifiers */
3313 if ((ob->modifiers.first) && !(ads_filterflag & ADS_FILTER_NOMODIFIERS)) {
3314 tmp_items += animdata_filter_ds_modifiers(ac, &tmp_data, ob, filter_mode);
3315 }
3316
3317 /* materials */
3318 if ((ob->totcol) && !(ads_filterflag & ADS_FILTER_NOMAT)) {
3319 tmp_items += animdata_filter_ds_materials(ac, &tmp_data, ob, filter_mode);
3320 }
3321
3322 /* object data */
3323 if (ob->data) {
3324 tmp_items += animdata_filter_ds_obdata(ac, &tmp_data, ob, filter_mode);
3325 }
3326
3327 /* particles */
3328 if ((ob->particlesystem.first) && !(ads_filterflag & ADS_FILTER_NOPART)) {
3329 tmp_items += animdata_filter_ds_particles(ac, &tmp_data, ob, filter_mode);
3330 }
3331
3332 /* grease pencil */
3333 if (ob->type == OB_GREASE_PENCIL && (ob->data) && !(ads_filterflag & ADS_FILTER_NOGPENCIL)) {
3335 ac, &tmp_data, static_cast<GreasePencil *>(ob->data), filter_mode);
3336 }
3337 }
3339
3340 /* if we collected some channels, add these to the new list... */
3341 if (tmp_items) {
3342 /* firstly add object expander if required */
3343 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
3344 /* check if filtering by selection */
3345 /* XXX: double-check on this -
3346 * most of the time, a lot of tools need to filter out these channels! */
3347 if (ANIMCHANNEL_SELOK((base->flag & BASE_SELECTED))) {
3348 /* check if filtering by active status */
3349 if (ANIMCHANNEL_ACTIVEOK(ob)) {
3350 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, base, ANIMTYPE_OBJECT, ob, nullptr);
3351 }
3352 }
3353 }
3354
3355 /* now add the list of collected channels */
3356 BLI_movelisttolist(anim_data, &tmp_data);
3358 items += tmp_items;
3359 }
3360
3361 /* return the number of items added */
3362 return items;
3363}
3364
3366 bAnimContext *ac, ListBase *anim_data, Scene *sce, World *wo, eAnimFilter_Flags filter_mode)
3367{
3368 ListBase tmp_data = {nullptr, nullptr};
3369 size_t tmp_items = 0;
3370 size_t items = 0;
3371
3372 /* add world animation channels */
3374 /* animation data filtering */
3375 tmp_items += animfilter_block_data(ac, &tmp_data, reinterpret_cast<ID *>(wo), filter_mode);
3376
3377 /* nodes */
3378 if ((wo->nodetree) && !(ac->filters.flag & ADS_FILTER_NONTREE)) {
3379 tmp_items += animdata_filter_ds_nodetree(
3380 ac, &tmp_data, reinterpret_cast<ID *>(wo), wo->nodetree, filter_mode);
3381 }
3382 }
3384
3385 /* did we find anything? */
3386 if (tmp_items) {
3387 /* include data-expand widget first */
3388 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
3389 /* check if filtering by active status */
3390 if (ANIMCHANNEL_ACTIVEOK(wo)) {
3391 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, wo, ANIMTYPE_DSWOR, sce, nullptr);
3392 }
3393 }
3394
3395 /* now add the list of collected channels */
3396 BLI_movelisttolist(anim_data, &tmp_data);
3398 items += tmp_items;
3399 }
3400
3401 /* return the number of items added to the list */
3402 return items;
3403}
3404
3406 ListBase *anim_data,
3407 Scene *sce,
3408 eAnimFilter_Flags filter_mode)
3409{
3410 ListBase tmp_data = {nullptr, nullptr};
3411 size_t tmp_items = 0;
3412 size_t items = 0;
3413
3414 AnimData *adt = sce->adt;
3416 short expanded = 1;
3417 void *cdata = nullptr;
3418
3419 /* determine the type of expander channels to use */
3420 /* this is the best way to do this for now... */
3422 sce, /* Some useless long comment to prevent wrapping by old clang-format versions... */
3423 {/* AnimData - no channel, but consider data */},
3424 {/* NLA - no channel, but consider data */},
3425 { /* Drivers */
3426 type = ANIMTYPE_FILLDRIVERS;
3427 cdata = adt;
3428 expanded = EXPANDED_DRVD(adt);
3429 },
3430 {/* NLA Strip Controls - no dedicated channel for now (XXX) */},
3431 { /* Keyframes from legacy Action. */
3432 type = ANIMTYPE_FILLACTD;
3433 cdata = adt->action;
3434 expanded = EXPANDED_ACTC(adt->action);
3435 },
3436 { /* Keyframes from layered Action. */
3438 cdata = adt->action;
3439 expanded = EXPANDED_ADT(adt);
3440 });
3441
3442 /* add scene-level animation channels */
3443 BEGIN_ANIMFILTER_SUBCHANNELS (expanded) {
3444 /* animation data filtering */
3445 tmp_items += animfilter_block_data(ac, &tmp_data, reinterpret_cast<ID *>(sce), filter_mode);
3446 }
3448
3449 /* did we find anything? */
3450 if (tmp_items) {
3451 /* include anim-expand widget first */
3452 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
3453 if (type != ANIMTYPE_NONE) {
3454 /* NOTE: active-status (and the associated checks) don't apply here... */
3455 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, cdata, type, sce, nullptr);
3456 }
3457 }
3458
3459 /* now add the list of collected channels */
3460 BLI_movelisttolist(anim_data, &tmp_data);
3462 items += tmp_items;
3463 }
3464
3465 /* return the number of items added to the list */
3466 return items;
3467}
3468
3470 ListBase *anim_data,
3471 Scene *sce,
3472 eAnimFilter_Flags filter_mode)
3473{
3474 ListBase tmp_data = {nullptr, nullptr};
3475 size_t tmp_items = 0;
3476 size_t items = 0;
3477
3478 /* filter data contained under object first */
3480 bNodeTree *ntree = sce->compositing_node_group;
3481 World *wo = sce->world;
3482 Editing *ed = sce->ed;
3483
3484 /* Action, Drivers, or NLA for Scene */
3485 if ((ac->filters.flag & ADS_FILTER_NOSCE) == 0) {
3486 tmp_items += animdata_filter_ds_scene(ac, &tmp_data, sce, filter_mode);
3487 }
3488
3489 /* world */
3490 if ((wo) && !(ac->filters.flag & ADS_FILTER_NOWOR)) {
3491 tmp_items += animdata_filter_ds_world(ac, &tmp_data, sce, wo, filter_mode);
3492 }
3493
3494 /* nodetree */
3495 if ((ntree) && !(ac->filters.flag & ADS_FILTER_NONTREE)) {
3496 tmp_items += animdata_filter_ds_nodetree(
3497 ac, &tmp_data, reinterpret_cast<ID *>(sce), ntree, filter_mode);
3498 }
3499
3500 /* Strip modifier node trees. */
3501 if (ed && !(ac->filters.flag & ADS_FILTER_NONTREE)) {
3502 VectorSet<ID *> node_trees;
3503 seq::foreach_strip(&ed->seqbase, [&](Strip *strip) {
3504 seq::foreach_strip_modifier_id(strip, [&](ID *id) {
3505 if (GS(id->name) == ID_NT) {
3506 node_trees.add(id);
3507 }
3508 });
3509 return true;
3510 });
3511 for (ID *node_tree : node_trees) {
3512 tmp_items += animdata_filter_ds_nodetree(
3513 ac, &tmp_data, &sce->id, reinterpret_cast<bNodeTree *>(node_tree), filter_mode);
3514 }
3515 }
3516
3517 /* line styles */
3518 if ((ac->filters.flag & ADS_FILTER_NOLINESTYLE) == 0) {
3519 tmp_items += animdata_filter_ds_linestyle(ac, &tmp_data, sce, filter_mode);
3520 }
3521
3522 /* TODO: one day, when sequencer becomes its own datatype,
3523 * perhaps it should be included here. */
3524 }
3526
3527 /* if we collected some channels, add these to the new list... */
3528 if (tmp_items) {
3529 /* firstly add object expander if required */
3530 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
3531 /* check if filtering by selection */
3532 if (ANIMCHANNEL_SELOK((sce->flag & SCE_DS_SELECTED))) {
3533 /* NOTE: active-status doesn't matter for this! */
3534 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, sce, ANIMTYPE_SCENE, sce, nullptr);
3535 }
3536 }
3537
3538 /* now add the list of collected channels */
3539 BLI_movelisttolist(anim_data, &tmp_data);
3541 items += tmp_items;
3542 }
3543
3544 /* return the number of items added */
3545 return items;
3546}
3547
3549 ListBase *anim_data,
3550 MovieClip *clip,
3551 eAnimFilter_Flags filter_mode)
3552{
3553 ListBase tmp_data = {nullptr, nullptr};
3554 size_t tmp_items = 0;
3555 size_t items = 0;
3556 /* add world animation channels */
3558 /* animation data filtering */
3559 tmp_items += animfilter_block_data(ac, &tmp_data, reinterpret_cast<ID *>(clip), filter_mode);
3560 }
3562 /* did we find anything? */
3563 if (tmp_items) {
3564 /* include data-expand widget first */
3565 if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
3566 /* check if filtering by active status */
3567 if (ANIMCHANNEL_ACTIVEOK(clip)) {
3568 ANIMCHANNEL_NEW_CHANNEL(ac->bmain, clip, ANIMTYPE_DSMCLIP, clip, nullptr);
3569 }
3570 }
3571 /* now add the list of collected channels */
3572 BLI_movelisttolist(anim_data, &tmp_data);
3574 items += tmp_items;
3575 }
3576 /* return the number of items added to the list */
3577 return items;
3578}
3579
3581 ListBase *anim_data,
3582 const eAnimFilter_Flags filter_mode)
3583{
3584 size_t items = 0;
3585 LISTBASE_FOREACH (MovieClip *, clip, &ac->bmain->movieclips) {
3586 /* only show if gpd is used by something... */
3587 if (ID_REAL_USERS(clip) < 1) {
3588 continue;
3589 }
3590 items += animdata_filter_ds_movieclip(ac, anim_data, clip, filter_mode);
3591 }
3592 /* return the number of items added to the list */
3593 return items;
3594}
3595
3596/* Helper for animdata_filter_dopesheet() - For checking if an object should be included or not */
3598 Base *base,
3599 const eObjectMode object_mode,
3600 const eAnimFilter_Flags filter_mode)
3601{
3602 Object *ob = base->object;
3603
3604 if (base->object == nullptr) {
3605 return false;
3606 }
3607
3608 /* firstly, check if object can be included, by the following factors:
3609 * - if only visible, must check for layer and also viewport visibility
3610 * --> while tools may demand only visible, user setting takes priority
3611 * as user option controls whether sets of channels get included while
3612 * tool-flag takes into account collapsed/open channels too
3613 * - if only selected, must check if object is selected
3614 * - there must be animation data to edit (this is done recursively as we
3615 * try to add the channels)
3616 */
3617 if ((filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ac->filters.flag & ADS_FILTER_INCL_HIDDEN)) {
3618 /* layer visibility - we check both object and base, since these may not be in sync yet */
3621 {
3622 return false;
3623 }
3624
3625 /* outliner restrict-flag */
3627 return false;
3628 }
3629 }
3630
3631 /* if only F-Curves with visible flags set can be shown, check that
3632 * data-block hasn't been set to invisible.
3633 */
3634 if (filter_mode & ANIMFILTER_CURVE_VISIBLE) {
3635 if ((ob->adt) && (ob->adt->flag & ADT_CURVES_NOT_VISIBLE)) {
3636 return false;
3637 }
3638 }
3639
3640 /* Pinned curves are visible regardless of selection flags. */
3641 if ((ob->adt) && (ob->adt->flag & ADT_CURVES_ALWAYS_VISIBLE)) {
3642 return true;
3643 }
3644
3645 /* Special case.
3646 * We don't do recursive checks for pin, but we need to deal with tricky
3647 * setup like animated camera lens without (pinned) animated camera location.
3648 * Without such special handle here we wouldn't be able to pin such
3649 * camera data animation to the editor.
3650 */
3651 if (ob->data != nullptr) {
3652 AnimData *data_adt = BKE_animdata_from_id(static_cast<ID *>(ob->data));
3653 if (data_adt != nullptr && (data_adt->flag & ADT_CURVES_ALWAYS_VISIBLE)) {
3654 return true;
3655 }
3656 }
3657
3658 /* check selection and object type filters */
3659 if (ac->filters.flag & ADS_FILTER_ONLYSEL) {
3660 if (object_mode & OB_MODE_POSE) {
3661 /* When in pose-mode handle all pose-mode objects.
3662 * This avoids problems with pose-mode where objects may be unselected,
3663 * where a selected bone of an unselected object would be hidden. see: #81922. */
3664 if (!(base->object->mode & object_mode)) {
3665 return false;
3666 }
3667 }
3668 else {
3669 /* only selected should be shown (ignore active) */
3670 if (!(base->flag & BASE_SELECTED)) {
3671 return false;
3672 }
3673 }
3674 }
3675
3676 /* check if object belongs to the filtering group if option to filter
3677 * objects by the grouped status is on
3678 * - used to ease the process of doing multiple-character choreographies
3679 */
3680 if (ac->ads->filter_grp != nullptr) {
3682 return false;
3683 }
3684 }
3685
3686 /* no reason to exclude this object... */
3687 return true;
3688}
3689
3690/* Helper for animdata_filter_ds_sorted_bases() - Comparison callback for two Base pointers... */
3691static int ds_base_sorting_cmp(const void *base1_ptr, const void *base2_ptr)
3692{
3693 const Base *b1 = *((const Base **)base1_ptr);
3694 const Base *b2 = *((const Base **)base2_ptr);
3695
3696 return BLI_strcasecmp_natural(b1->object->id.name + 2, b2->object->id.name + 2);
3697}
3698
3699/* Get a sorted list of all the bases - for inclusion in dopesheet (when drawing channels) */
3701 const Scene *scene,
3702 ViewLayer *view_layer,
3703 const eAnimFilter_Flags filter_mode,
3704 size_t *r_usable_bases)
3705{
3706 /* Create an array with space for all the bases, but only containing the usable ones */
3707 BKE_view_layer_synced_ensure(scene, view_layer);
3708 ListBase *object_bases = BKE_view_layer_object_bases_get(view_layer);
3709 size_t tot_bases = BLI_listbase_count(object_bases);
3710 size_t num_bases = 0;
3711
3712 Base **sorted_bases = MEM_calloc_arrayN<Base *>(tot_bases, "Dopesheet Usable Sorted Bases");
3713 LISTBASE_FOREACH (Base *, base, object_bases) {
3714 const eObjectMode object_mode = eObjectMode(base->object->mode);
3715 if (animdata_filter_base_is_ok(ac, base, object_mode, filter_mode)) {
3716 sorted_bases[num_bases++] = base;
3717 }
3718 }
3719
3720 /* Sort this list of pointers (based on the names) */
3721 qsort(sorted_bases, num_bases, sizeof(Base *), ds_base_sorting_cmp);
3722
3723 /* Return list of sorted bases */
3724 *r_usable_bases = num_bases;
3725 return sorted_bases;
3726}
3727
3728/* TODO: implement pinning...
3729 * (if and when pinning is done, what we need to do is to provide freeing mechanisms -
3730 * to protect against data that was deleted). */
3732 ListBase *anim_data,
3733 eAnimFilter_Flags filter_mode)
3734{
3735 bDopeSheet *ads = ac->ads;
3736 Scene *scene = reinterpret_cast<Scene *>(ads->source);
3737 ViewLayer *view_layer = ac->view_layer;
3738 size_t items = 0;
3739
3740 /* check that we do indeed have a scene */
3741 if ((ads->source == nullptr) || (GS(ads->source->name) != ID_SCE)) {
3742 printf("Dope Sheet Error: No scene!\n");
3743 if (G.debug & G_DEBUG) {
3744 printf("\tPointer = %p, Name = '%s'\n",
3745 (void *)ads->source,
3746 (ads->source) ? ads->source->name : nullptr);
3747 }
3748 return 0;
3749 }
3750
3751 /* augment the filter-flags with settings based on the dopesheet filterflags
3752 * so that some temp settings can get added automagically...
3753 */
3754 if (ac->filters.flag & ADS_FILTER_SELEDIT) {
3755 /* only selected F-Curves should get their keyframes considered for editability */
3756 filter_mode |= ANIMFILTER_SELEDIT;
3757 }
3758
3759 /* Cache files level animations (frame duration and such). */
3760 const bool use_only_selected = (ac->filters.flag & ADS_FILTER_ONLYSEL);
3761 if (!use_only_selected && !(ac->filters.flag2 & ADS_FILTER_NOCACHEFILES)) {
3762 LISTBASE_FOREACH (CacheFile *, cache_file, &ac->bmain->cachefiles) {
3763 items += animdata_filter_ds_cachefile(ac, anim_data, cache_file, filter_mode);
3764 }
3765 }
3766
3767 /* Annotations are always shown if "Only Show Selected" is disabled. */
3768 if (!use_only_selected && !(ac->filters.flag & ADS_FILTER_NOGPENCIL)) {
3769 LISTBASE_FOREACH (bGPdata *, gp_data, &ac->bmain->gpencils) {
3770 items += animdata_filter_ds_gpencil(ac, anim_data, gp_data, filter_mode);
3771 }
3772 }
3773
3774 /* movie clip's animation */
3775 if (!use_only_selected && !(ac->filters.flag2 & ADS_FILTER_NOMOVIECLIPS)) {
3776 items += animdata_filter_dopesheet_movieclips(ac, anim_data, filter_mode);
3777 }
3778
3779 /* Scene-linked animation - e.g. world, compositing nodes, scene anim
3780 * (including sequencer currently). */
3781 items += animdata_filter_dopesheet_scene(ac, anim_data, scene, filter_mode);
3782
3783 /* If filtering for channel drawing, we want the objects in alphabetical order,
3784 * to make it easier to predict where items are in the hierarchy
3785 * - This order only really matters
3786 * if we need to show all channels in the list (e.g. for drawing).
3787 * (XXX: What about lingering "active" flags? The order may now become unpredictable)
3788 * - Don't do this if this behavior has been turned off (i.e. due to it being too slow)
3789 * - Don't do this if there's just a single object
3790 */
3791 BKE_view_layer_synced_ensure(scene, view_layer);
3792 ListBase *object_bases = BKE_view_layer_object_bases_get(view_layer);
3793 if ((filter_mode & ANIMFILTER_LIST_CHANNELS) && !(ads->flag & ADS_FLAG_NO_DB_SORT) &&
3794 (object_bases->first != object_bases->last))
3795 {
3796 /* Filter list of bases (i.e. objects), sort them, then add their contents normally... */
3797 /* TODO: Cache the old sorted order - if the set of bases hasn't changed, don't re-sort... */
3798 Base **sorted_bases;
3799 size_t num_bases;
3800
3801 sorted_bases = animdata_filter_ds_sorted_bases(ac, scene, view_layer, filter_mode, &num_bases);
3802 if (sorted_bases) {
3803 /* Add the necessary channels for these bases... */
3804 for (size_t i = 0; i < num_bases; i++) {
3805 items += animdata_filter_dopesheet_ob(ac, anim_data, sorted_bases[i], filter_mode);
3806 }
3807
3808 /* TODO: store something to validate whether any changes are needed? */
3809
3810 /* free temporary data */
3811 MEM_freeN(sorted_bases);
3812 }
3813 }
3814 else {
3815 /* Filter and add contents of each base (i.e. object) without them sorting first
3816 * NOTE: This saves performance in cases where order doesn't matter
3817 */
3818 Object *obact = BKE_view_layer_active_object_get(view_layer);
3819 const eObjectMode object_mode = (obact != nullptr) ? eObjectMode(obact->mode) : OB_MODE_OBJECT;
3820 LISTBASE_FOREACH (Base *, base, object_bases) {
3821 if (animdata_filter_base_is_ok(ac, base, object_mode, filter_mode)) {
3822 /* since we're still here, this object should be usable */
3823 items += animdata_filter_dopesheet_ob(ac, anim_data, base, filter_mode);
3824 }
3825 }
3826 }
3827
3828 /* return the number of items in the list */
3829 return items;
3830}
3831
3832/* Summary track for DopeSheet/Action Editor
3833 * - return code is whether the summary lets the other channels get drawn
3834 */
3836 ListBase *anim_data,
3837 const eAnimFilter_Flags filter_mode,
3838 size_t *items)
3839{
3840 bDopeSheet *ads = nullptr;
3841
3842 /* get the DopeSheet information to use
3843 * - we should only need to deal with the DopeSheet/Action Editor,
3844 * since all the other Animation Editors won't have this concept
3845 * being applicable.
3846 */
3847 if ((ac && ac->sl) && (ac->spacetype == SPACE_ACTION)) {
3848 SpaceAction *saction = reinterpret_cast<SpaceAction *>(ac->sl);
3849 ads = &saction->ads;
3850 }
3851 else {
3852 /* invalid space type - skip this summary channels */
3853 return 1;
3854 }
3855
3856 if ((filter_mode & ANIMFILTER_LIST_CHANNELS) == 0) {
3857 /* Without ANIMFILTER_LIST_CHANNELS flag, summary channels should not be created.
3858 * Sub-channels of this summary should still be visited. */
3859 return 1;
3860 }
3861
3862 /* dopesheet summary
3863 * - only for drawing and/or selecting keyframes in channels, but not for real editing
3864 * - only useful for DopeSheet/Action/etc. editors where it is actually useful
3865 */
3866 const bool is_timeline = ac->dopesheet_mode == SACTCONT_TIMELINE;
3867 if (is_timeline || (ac->filters.flag & ADS_FILTER_SUMMARY)) {
3868 bAnimListElem *ale = make_new_animlistelem(ac->bmain, ac, ANIMTYPE_SUMMARY, nullptr, nullptr);
3869 if (ale) {
3870 BLI_addtail(anim_data, ale);
3871 (*items)++;
3872 }
3873
3874 /* If summary is collapsed, don't show other channels beneath this - this check is put inside
3875 * the summary check so that it doesn't interfere with normal operation.
3876 */
3877 if (ads->flag & ADS_FLAG_SUMMARY_COLLAPSED) {
3878 return 0;
3879 }
3880 }
3881
3882 /* the other channels beneath this can be shown */
3883 return 1;
3884}
3885
3886/* ......................... */
3887
3888/* filter data associated with a channel - usually for handling summary-channels in DopeSheet */
3890 ListBase *anim_data,
3891 bAnimListElem *channel,
3892 const eAnimFilter_Flags filter_mode)
3893{
3894 size_t items = 0;
3895
3896 /* data to filter depends on channel type */
3897 /* NOTE: only common channel-types have been handled for now. More can be added as necessary */
3898 switch (channel->type) {
3899 case ANIMTYPE_SUMMARY:
3900 items += animdata_filter_dopesheet(ac, anim_data, filter_mode);
3901 break;
3902
3903 case ANIMTYPE_SCENE:
3905 ac, anim_data, static_cast<Scene *>(channel->data), filter_mode);
3906 break;
3907
3908 case ANIMTYPE_OBJECT:
3910 ac, anim_data, static_cast<Base *>(channel->data), filter_mode);
3911 break;
3912
3915 ac, anim_data, static_cast<CacheFile *>(channel->data), filter_mode);
3916 break;
3917
3918 case ANIMTYPE_ANIMDATA:
3919 items += animfilter_block_data(ac, anim_data, channel->id, filter_mode);
3920 break;
3921
3922 default:
3923 printf("ERROR: Unsupported channel type (%d) in animdata_filter_animchan()\n",
3924 channel->type);
3925 break;
3926 }
3927
3928 return items;
3929}
3930
3931/* ----------- Cleanup API --------------- */
3932
3933/* Remove entries with invalid types in animation channel list */
3935{
3936 size_t items = 0;
3937
3938 /* only keep entries with valid types */
3939 LISTBASE_FOREACH_MUTABLE (bAnimListElem *, ale, anim_data) {
3940 if (ale->type == ANIMTYPE_NONE) {
3941 BLI_freelinkN(anim_data, ale);
3942 }
3943 else {
3944 items++;
3945 }
3946 }
3947
3948 return items;
3949}
3950
3951/* Remove duplicate entries in animation channel list */
3953{
3954 GSet *gs;
3955 size_t items = 0;
3956
3957 /* Build new hash-table to efficiently store and retrieve which entries have been
3958 * encountered already while searching. */
3959 gs = BLI_gset_ptr_new(__func__);
3960
3961 /* loop through items, removing them from the list if a similar item occurs already */
3962 LISTBASE_FOREACH_MUTABLE (bAnimListElem *, ale, anim_data) {
3963 /* check if hash has any record of an entry like this
3964 * - just use ale->data for now, though it would be nicer to involve
3965 * ale->type in combination too to capture corner cases
3966 * (where same data performs differently)
3967 */
3968 if (BLI_gset_add(gs, ale->data)) {
3969 /* this entry is 'unique' and can be kept */
3970 items++;
3971 }
3972 else {
3973 /* this entry isn't needed anymore */
3974 BLI_freelinkN(anim_data, ale);
3975 }
3976 }
3977
3978 /* free the hash... */
3979 BLI_gset_free(gs, nullptr);
3980
3981 /* return the number of items still in the list */
3982 return items;
3983}
3984
3985/* ----------- Public API --------------- */
3986
3988 ListBase *anim_data,
3989 const eAnimFilter_Flags filter_mode,
3990 void *data,
3991 const eAnimCont_Types datatype)
3992{
3993 if (!data || !anim_data) {
3994 return 0;
3995 }
3996
3997 size_t items = 0;
3998 switch (datatype) {
3999 /* Action-Editing Modes */
4000 case ANIMCONT_ACTION: /* 'Action Editor' */
4001 {
4002 Object *obact = ac->obact;
4003 SpaceAction *saction = reinterpret_cast<SpaceAction *>(ac->sl);
4004 bDopeSheet *ads = (saction) ? &saction->ads : nullptr;
4005 BLI_assert(ads == ac->ads);
4006 UNUSED_VARS_NDEBUG(ads);
4007
4008 /* specially check for AnimData filter, see #36687. */
4009 /* TODO: see how this interacts with the new layered Actions. */
4010 if (UNLIKELY(filter_mode & ANIMFILTER_ANIMDATA)) {
4011 /* all channels here are within the same AnimData block, hence this special case */
4012 if (LIKELY(obact->adt)) {
4014 ac->bmain, obact->adt, ANIMTYPE_ANIMDATA, reinterpret_cast<ID *>(obact), nullptr);
4015 }
4016 }
4017 else {
4018 /* The check for the DopeSheet summary is included here
4019 * since the summary works here too. */
4020 if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
4022 obact->adt && data == obact->adt->action,
4023 "This code assumes the Action editor shows the Action of the active object");
4024
4025 animrig::Action &action = static_cast<bAction *>(data)->wrap();
4026 const animrig::slot_handle_t slot_handle = obact->adt->slot_handle;
4027 items += animfilter_action(
4028 ac, anim_data, action, slot_handle, filter_mode, reinterpret_cast<ID *>(obact));
4029 }
4030 }
4031
4032 break;
4033 }
4034 case ANIMCONT_SHAPEKEY: /* 'ShapeKey Editor' */
4035 {
4036 Key *key = static_cast<Key *>(data);
4037
4038 /* specially check for AnimData filter, see #36687. */
4039 if (UNLIKELY(filter_mode & ANIMFILTER_ANIMDATA)) {
4040 /* all channels here are within the same AnimData block, hence this special case */
4041 if (LIKELY(key->adt)) {
4043 ac->bmain, key->adt, ANIMTYPE_ANIMDATA, reinterpret_cast<ID *>(key), nullptr);
4044 }
4045 }
4046 else {
4047 /* The check for the DopeSheet summary is included here
4048 * since the summary works here too. */
4049 if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
4050 items = animdata_filter_shapekey(ac, anim_data, key, filter_mode);
4051 }
4052 }
4053
4054 break;
4055 }
4056
4057 /* Modes for Specialty Data Types (i.e. not keyframes) */
4058 case ANIMCONT_GPENCIL: {
4059 if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
4060 items = animdata_filter_grease_pencil(ac, anim_data, filter_mode);
4061 }
4062 break;
4063 }
4064 case ANIMCONT_MASK: {
4065 if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
4066 items = animdata_filter_mask(ac, anim_data, data, filter_mode);
4067 }
4068 break;
4069 }
4070
4071 /* DopeSheet Based Modes */
4072 case ANIMCONT_DOPESHEET: /* 'DopeSheet Editor' */
4073 {
4074 /* Due to code in `actedit_get_context()`, the equation below holds. The `data` pointer is no
4075 * longer used here, in favor of always passing `ac` down the call chain. The called code
4076 * can access it via `ac->ads`. Because the anim filtering code is quite complex, I (Sybren)
4077 * want to keep this assertion in place. */
4078 BLI_assert_msg(ac->ads == data, "ANIMCONT_DOPESHEET");
4079
4080 /* the DopeSheet editor is the primary place where the DopeSheet summaries are useful */
4081 if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
4082 items += animdata_filter_dopesheet(ac, anim_data, filter_mode);
4083 }
4084 break;
4085 }
4086 case ANIMCONT_FCURVES: /* Graph Editor -> F-Curves/Animation Editing */
4087 case ANIMCONT_DRIVERS: /* Graph Editor -> Drivers Editing */
4088 case ANIMCONT_NLA: /* NLA Editor */
4089 {
4090 /* Due to code in `actedit_get_context()`, the equation below holds. The `data` pointer is no
4091 * longer used here, in favor of always passing `ac` down the call chain. The called code
4092 * can access it via `ac->ads`. Because the anim filtering code is quite complex, I (Sybren)
4093 * want to keep this assertion in place. */
4094 BLI_assert_msg(ac->ads == data, "ANIMCONT_FCURVES/DRIVERS/NLA");
4095
4096 /* all of these editors use the basic DopeSheet data for filtering options,
4097 * but don't have all the same features */
4098 items = animdata_filter_dopesheet(ac, anim_data, filter_mode);
4099 break;
4100 }
4101
4102 /* Timeline Mode - Basically the same as dopesheet,
4103 * except we only have the summary for now */
4104 case ANIMCONT_TIMELINE: {
4105 /* Due to code in `actedit_get_context()`, the equation below holds. The `data` pointer is no
4106 * longer used here, in favor of always passing `ac` down the call chain. The called code
4107 * can access it via `ac->ads`. Because the anim filtering code is quite complex, I (Sybren)
4108 * want to keep this assertion in place. */
4109 BLI_assert_msg(ac->ads == data, "ANIMCONT_TIMELINE");
4110 if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
4111 items += animdata_filter_dopesheet(ac, anim_data, filter_mode);
4112 }
4113 break;
4114 }
4115
4116 /* Special/Internal Use */
4117 case ANIMCONT_CHANNEL: /* animation channel */
4118 {
4119 /* based on the channel type, filter relevant data for this */
4121 ac, anim_data, static_cast<bAnimListElem *>(data), filter_mode);
4122 break;
4123 }
4124
4125 case ANIMCONT_NONE:
4126 printf("ANIM_animdata_filter() - Invalid datatype argument ANIMCONT_NONE\n");
4127 break;
4128 }
4129
4130 /* remove any 'weedy' entries */
4131 items = animdata_filter_remove_invalid(anim_data);
4132
4133 /* remove duplicates (if required) */
4134 if (filter_mode & ANIMFILTER_NODUPLIS) {
4135 items = animdata_filter_remove_duplis(anim_data);
4136 }
4137
4138 return items;
4139}
4140
4141/* ************************************************************ */
Functions and classes to work with Actions.
Functions to deal with Armatures.
C++ functions to deal with Armature collections (i.e. the successor of bone layers).
Blender kernel action and pose functionality.
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:83
bool BKE_collection_has_object_recursive(Collection *collection, Object *ob)
ScrArea * CTX_wm_area(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
SpaceLink * CTX_wm_space_data(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
#define DRIVER_TARGETS_USED_LOOPER_BEGIN(dvar)
#define DRIVER_TARGETS_LOOPER_END
@ G_DEBUG
Low-level operations for grease pencil.
std::optional< std::string > BKE_keyblock_curval_rnapath_get(const Key *key, const KeyBlock *kb)
Definition key.cc:1942
Key * BKE_key_from_object(Object *ob)
Definition key.cc:1791
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
ListBase * BKE_view_layer_object_bases_get(ViewLayer *view_layer)
LibraryForeachIDCallbackFlag
struct MaskLayer * BKE_mask_layer_active(struct Mask *mask)
General operations, lookup, etc. for materials.
Material * BKE_object_material_get(Object *ob, short act)
void void void BKE_modifiers_foreach_ID_link(Object *ob, IDWalkFunc walk, void *user_data)
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:18
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
struct GSet GSet
Definition BLI_ghash.h:337
GSet * BLI_gset_ptr_new(const char *info)
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.cc:966
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
void void void BLI_movelisttolist(ListBase *dst, ListBase *src) ATTR_NONNULL(1
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void BLI_freelinkN(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:270
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
int bool bool bool size_t size_t size_t int BLI_string_max_possible_word_count(int str_len) ATTR_WARN_UNUSED_RESULT
Definition string.cc:573
int char char * BLI_strncasestr(const char *s, const char *find, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
int char char int int int BLI_strcasecmp_natural(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
int bool bool BLI_str_endswith(const char *__restrict str, const char *__restrict end) ATTR_NONNULL(1
bool bool int BLI_string_find_split_words(const char *str, size_t str_maxlen, char delim, int r_words[][2], int words_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
int char * BLI_strcasestr(const char *s, const char *find) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
bool bool BLI_str_quoted_substr(const char *__restrict str, const char *__restrict prefix, char *result, size_t result_maxncpy)
Definition string.cc:521
#define UNUSED_VARS_NDEBUG(...)
#define UNLIKELY(x)
#define ELEM(...)
#define STREQ(a, b)
#define LIKELY(x)
@ ID_TAG_DOIT
Definition DNA_ID.h:1036
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:694
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:705
#define ID_REAL_USERS(id)
Definition DNA_ID.h:676
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:730
@ ID_TE
@ ID_NT
@ ID_SCE
@ ID_AC
@ ID_OB
@ ID_PA
eDopeSheet_FilterFlag2
@ ADS_FILTER_NOLIGHTPROBE
@ ADS_FILTER_DRIVER_FALLBACK_AS_ERROR
@ ADS_FILTER_NOMOVIECLIPS
@ ADS_FILTER_NOVOLUME
@ ADS_FILTER_NOHAIR
@ ADS_FILTER_NOCACHEFILES
@ ADS_FILTER_NOPOINTCLOUD
eDopeSheet_FilterFlag
@ ADS_FILTER_ONLYSEL
@ ADS_FILTER_NOARM
@ ADS_FILTER_NLA_NOACT
@ ADS_FILTER_NOMAT
@ ADS_FILTER_NONTREE
@ ADS_FILTER_NOCAM
@ ADS_FILTER_NOSHAPEKEYS
@ ADS_FILTER_NOTEX
@ ADS_FILTER_ONLYNLA
@ ADS_FILTER_ONLY_ERRORS
@ ADS_FILTER_ONLYDRIVERS
@ ADS_FILTER_SELEDIT
@ ADS_FILTER_NOSCE
@ ADS_FILTER_NOLAM
@ ADS_FILTER_ONLY_SLOTS_OF_ACTIVE
@ ADS_FILTER_NOMODIFIERS
@ ADS_FILTER_NOLINESTYLE
@ ADS_FILTER_SUMMARY
@ ADS_FILTER_NOGPENCIL
@ ADS_FILTER_NOOBJ
@ ADS_FILTER_NOCUR
@ ADS_FILTER_NOPART
@ ADS_FILTER_NOMESH
@ ADS_FILTER_NOWOR
@ ADS_FILTER_INCL_HIDDEN
@ ADS_FILTER_NOMBA
@ ADS_FILTER_NOLAT
@ AGRP_ACTIVE
@ AGRP_NOTVISIBLE
@ POSE_SELECTED
@ ADS_FLAG_SUMMARY_COLLAPSED
@ ADS_FLAG_INVERT_FILTER
@ ADS_FLAG_NO_DB_SORT
@ ADS_FLAG_FUZZY_NAMES
eAnimEdit_Context
@ SACTCONT_GPENCIL
@ SACTCONT_ACTION
@ SACTCONT_TIMELINE
@ SACTCONT_DOPESHEET
@ SACTCONT_SHAPEKEY
@ SACTCONT_MASK
@ SACTCONT_CACHEFILE
@ SACTION_POSEMARKERS_SHOW
@ ADT_CURVES_ALWAYS_VISIBLE
@ ADT_CURVES_NOT_VISIBLE
@ ADT_NLA_SKEYS_COLLAPSED
@ DTAR_FLAG_FALLBACK_USED
@ DTAR_FLAG_INVALID
@ DRIVER_FLAG_INVALID
@ FCURVE_DISABLED
@ FCURVE_ACTIVE
@ FCURVE_SELECTED
@ FCURVE_PROTECTED
@ FCURVE_VISIBLE
@ NLATRACK_ACTIVE
@ GREASE_PENCIL_ANIM_CHANNEL_EXPANDED
@ KEY_RELATIVE
@ BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT
@ BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT
@ VIEW_LAYER_FREESTYLE
@ NODE_SELECT
eObjectMode
@ OB_MODE_POSE
@ OB_MODE_OBJECT
Object is a sort of wrapper for general info.
@ OB_HIDE_VIEWPORT
@ OB_SPEAKER
@ OB_LATTICE
@ OB_MBALL
@ OB_SURF
@ OB_CAMERA
@ OB_FONT
@ OB_GREASE_PENCIL
@ OB_ARMATURE
@ OB_LAMP
@ OB_MESH
@ OB_POINTCLOUD
@ OB_VOLUME
@ OB_CURVES_LEGACY
@ OB_CURVES
@ OB_LIGHTPROBE
#define BASE_SELECTED(v3d, base)
@ SCE_KEYS_NO_SELONLY
@ SCE_DS_SELECTED
eRegion_Type
eSpace_Type
@ SPACE_TEXT
@ SPACE_CLIP
@ SPACE_ACTION
@ SPACE_CONSOLE
@ SPACE_OUTLINER
@ SPACE_STATUSBAR
@ SPACE_TOPBAR
@ SPACE_NODE
@ SPACE_SPREADSHEET
@ SPACE_USERPREF
@ SPACE_FILE
@ SPACE_PROPERTIES
@ SPACE_NLA
@ SPACE_SEQ
@ SPACE_EMPTY
@ SPACE_SCRIPT
@ SPACE_IMAGE
@ SPACE_GRAPH
@ SPACE_VIEW3D
@ SPACE_INFO
eGraphEdit_Mode
@ SIPO_MODE_DRIVERS
@ SIPO_MODE_ANIMATION
@ USER_ANIM_ONLY_SHOW_SELECTED_CURVE_KEYS
#define EXPANDED_ADT(adt)
#define FILTER_SPK_OBJD(spk)
#define FILTER_CACHEFILE_OBJD(cf)
#define SEL_SHAPEKEY(kb)
#define FILTER_LIGHTPROBE_OBJD(probe)
eAnim_ChannelType
@ ANIMTYPE_DSSPK
@ ANIMTYPE_DSTEX
@ ANIMTYPE_SUMMARY
@ ANIMTYPE_DSNTREE
@ ANIMTYPE_NLACURVE
@ ANIMTYPE_SHAPEKEY
@ ANIMTYPE_DSMBALL
@ ANIMTYPE_DSCAM
@ ANIMTYPE_DSLIGHTPROBE
@ ANIMTYPE_DSPOINTCLOUD
@ ANIMTYPE_FILLDRIVERS
@ ANIMTYPE_NONE
@ ANIMTYPE_DSPART
@ ANIMTYPE_DSLINESTYLE
@ ANIMTYPE_GROUP
@ ANIMTYPE_ACTION_SLOT
@ ANIMTYPE_SPECIALDATA__UNUSED
@ ANIMTYPE_GREASE_PENCIL_DATABLOCK
@ ANIMTYPE_DSCUR
@ ANIMTYPE_SCENE
@ ANIMTYPE_DSARM
@ ANIMTYPE_NLACONTROLS
@ ANIMTYPE_GPLAYER
@ ANIMTYPE_MASKDATABLOCK
@ ANIMTYPE_ANIMDATA
@ ANIMTYPE_MASKLAYER
@ ANIMTYPE_DSGPENCIL
@ ANIMTYPE_DSLAT
@ ANIMTYPE_NLAACTION
@ ANIMTYPE_DSMCLIP
@ ANIMTYPE_DSMAT
@ ANIMTYPE_NUM_TYPES
@ ANIMTYPE_DSCACHEFILE
@ ANIMTYPE_DSVOLUME
@ ANIMTYPE_FCURVE
@ ANIMTYPE_DSLAM
@ ANIMTYPE_PALETTE
@ ANIMTYPE_GREASE_PENCIL_LAYER
@ ANIMTYPE_FILLACT_LAYERED
@ ANIMTYPE_FILLACTD
@ ANIMTYPE_OBJECT
@ ANIMTYPE_DSMESH
@ ANIMTYPE_GREASE_PENCIL_LAYER_GROUP
@ ANIMTYPE_NLATRACK
@ ANIMTYPE_DSWOR
@ ANIMTYPE_DSSKEY
@ ANIMTYPE_DSHAIR
#define EXPANDED_MCLIP(clip)
#define FILTER_NTREE_DATA(ntree)
#define FILTER_SKE_OBJD(key)
#define FILTER_MESH_OBJD(me)
#define EDITABLE_GPL(gpl)
#define SEL_AGRP(agrp)
#define EXPANDED_SCEC(sce)
#define SEL_MASKLAY(masklay)
#define SEL_GPL(gpl)
#define FILTER_ARM_OBJD(arm)
#define EDITABLE_AGRP(agrp)
#define EXPANDED_AGRP(ac, agrp)
#define FILTER_CAM_OBJD(ca)
@ ALE_GREASE_PENCIL_GROUP
@ ALE_SCE
@ ALE_GREASE_PENCIL_CEL
@ ALE_GREASE_PENCIL_DATA
@ ALE_NONE
@ ALE_GPFRAME
@ ALE_FCURVE
@ ALE_NLASTRIP
@ ALE_ALL
@ ALE_ACT
@ ALE_ACTION_LAYERED
@ ALE_OB
@ ALE_GROUP
@ ALE_ACTION_SLOT
@ ALE_MASKLAY
#define FILTER_LATTICE_OBJD(lt)
#define FILTER_CURVES_OBJD(ha)
#define EXPANDED_ACTC(actc)
#define FILTER_LAM_OBJD(la)
eAnimCont_Types
@ ANIMCONT_DRIVERS
@ ANIMCONT_FCURVES
@ ANIMCONT_NLA
@ ANIMCONT_MASK
@ ANIMCONT_SHAPEKEY
@ ANIMCONT_TIMELINE
@ ANIMCONT_DOPESHEET
@ ANIMCONT_ACTION
@ ANIMCONT_NONE
@ ANIMCONT_GPENCIL
@ ANIMCONT_CHANNEL
#define EXPANDED_OBJC(ob)
#define SEL_FCU(fcu)
#define EDITABLE_SHAPEKEY(kb)
#define EDITABLE_FCU(fcu)
#define EDITABLE_MASK(masklay)
#define FILTER_CUR_OBJD(cu)
#define FILTER_MAT_OBJD(ma)
#define EDITABLE_NLT(nlt)
#define FILTER_WOR_SCED(wo)
#define FILTER_MBALL_OBJD(mb)
#define FILTER_LS_SCED(linestyle)
#define EXPANDED_DRVD(adt)
#define EXPANDED_MASK(mask)
#define SEL_ANIMDATA(adt)
#define FILTER_POINTS_OBJD(pt)
#define EXPANDED_GPD(gpd)
#define FILTER_TEX_DATA(tex)
#define FILTER_VOLUME_OBJD(vo)
#define SEL_NLT(nlt)
eAnimFilter_Flags
@ ANIMFILTER_TMP_PEEK
@ ANIMFILTER_ACTIVE
@ ANIMFILTER_UNSEL
@ ANIMFILTER_FOREDIT
@ ANIMFILTER_ANIMDATA
@ ANIMFILTER_DATA_VISIBLE
@ ANIMFILTER_CURVE_VISIBLE
@ ANIMFILTER_SELEDIT
@ ANIMFILTER_LIST_VISIBLE
@ ANIMFILTER_LIST_CHANNELS
@ ANIMFILTER_NODUPLIS
@ ANIMFILTER_TMP_IGNORE_ONLYSEL
@ ANIMFILTER_FCURVESONLY
@ ANIMFILTER_SEL
@ ANIMFILTER_ACTGROUPED
#define FILTER_PART_OBJD(part)
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
#define MAX_MTEX
Definition Stroke.h:31
const bAnimChannelType * ANIM_channel_get_typeinfo(const bAnimListElem *ale)
static size_t animdata_filter_ds_scene(bAnimContext *ac, ListBase *anim_data, Scene *sce, eAnimFilter_Flags filter_mode)
#define ANIMCHANNEL_NEW_CHANNEL(bmain, channel_data, channel_type, owner_id, fcurve_owner_id)
static size_t animdata_filter_dopesheet_ob(bAnimContext *ac, ListBase *anim_data, Base *base, eAnimFilter_Flags filter_mode)
static Key * actedit_get_shapekeys(bAnimContext *ac)
static size_t animfilter_block_data(bAnimContext *ac, ListBase *anim_data, ID *id, const eAnimFilter_Flags filter_mode)
bool ANIM_animdata_can_have_greasepencil(const eAnimCont_Types type)
static bool nlaedit_get_context(bAnimContext *ac, SpaceNla *snla)
static size_t animdata_filter_dopesheet_movieclips(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_grease_pencil_layers_data(bAnimContext *ac, ListBase *anim_data, GreasePencil *grease_pencil, const eAnimFilter_Flags filter_mode)
static bool animdata_filter_base_is_ok(bAnimContext *ac, Base *base, const eObjectMode object_mode, const eAnimFilter_Flags filter_mode)
bAction * ANIM_active_action_from_area(Scene *scene, ViewLayer *view_layer, const ScrArea *area, ID **r_action_user)
static size_t animdata_filter_ds_obanim(bAnimContext *ac, ListBase *anim_data, Object *ob, eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_movieclip(bAnimContext *ac, ListBase *anim_data, MovieClip *clip, eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_nodetree_group(bAnimContext *ac, ListBase *anim_data, ID *owner_id, bNodeTree *ntree, eAnimFilter_Flags filter_mode)
static bool name_matches_dopesheet_filter(const bDopeSheet *ads, const char *name)
static size_t animdata_filter_ds_particles(bAnimContext *ac, ListBase *anim_data, Object *ob, eAnimFilter_Flags filter_mode)
static size_t animfilter_fcurves_span(bAnimContext *ac, ListBase *anim_data, Span< FCurve * > fcurves, const animrig::slot_handle_t slot_handle, const eAnimFilter_Flags filter_mode, ID *animated_id, ID *fcurve_owner_id)
#define ANIMCHANNEL_SELOK(test_func)
static size_t animdata_filter_mask(bAnimContext *ac, ListBase *anim_data, void *, eAnimFilter_Flags filter_mode)
static bool fcurve_span_selection_matters(const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_gpencil(bAnimContext *ac, ListBase *anim_data, bGPdata *gpd, eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_materials(bAnimContext *ac, ListBase *anim_data, Object *ob, const eAnimFilter_Flags filter_mode)
static size_t animfilter_nla_controls(bAnimContext *ac, ListBase *anim_data, AnimData *adt, eAnimFilter_Flags filter_mode, ID *owner_id)
static size_t animdata_filter_grease_pencil(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_grease_pencil_layer(bAnimContext *ac, ListBase *anim_data, GreasePencil *grease_pencil, blender::bke::greasepencil::Layer &layer, const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_gpencil_layers_data_legacy(bAnimContext *ac, ListBase *anim_data, bGPdata *gpd, const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_textures(bAnimContext *ac, ListBase *anim_data, ID *owner_id, const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_modifiers(bAnimContext *ac, ListBase *anim_data, Object *ob, const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_grease_pencil_layer_node_recursive(bAnimContext *ac, ListBase *anim_data, GreasePencil *grease_pencil, blender::bke::greasepencil::TreeNode &node, eAnimFilter_Flags filter_mode)
bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac)
static void key_data_from_adt(bAnimListElem &ale, AnimData *adt)
#define ANIMCHANNEL_ACTIVEOK(ale)
static size_t animdata_filter_ds_material(bAnimContext *ac, ListBase *anim_data, Material *ma, eAnimFilter_Flags filter_mode)
static size_t animdata_filter_remove_invalid(ListBase *anim_data)
static size_t animdata_filter_ds_texture(bAnimContext *ac, ListBase *anim_data, Tex *tex, ID *owner_id, eAnimFilter_Flags filter_mode)
static short animdata_filter_dopesheet_summary(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode, size_t *items)
bool ANIM_animdata_context_getdata(bAnimContext *ac)
static void animfilter_modifier_idpoin_cb(void *afm_ptr, Object *ob, ID **idpoin, LibraryForeachIDCallbackFlag)
static size_t animdata_filter_grease_pencil_data(bAnimContext *ac, ListBase *anim_data, GreasePencil *grease_pencil, eAnimFilter_Flags filter_mode)
static size_t animdata_filter_shapekey(bAnimContext *ac, ListBase *anim_data, Key *key, const eAnimFilter_Flags filter_mode)
static size_t animfilter_fcurves(bAnimContext *ac, ListBase *anim_data, FCurve *first, eAnim_ChannelType fcurve_type, const eAnimFilter_Flags filter_mode, void *owner, ID *owner_id, ID *fcurve_owner_id)
static int ds_base_sorting_cmp(const void *base1_ptr, const void *base2_ptr)
static bool skip_fcurve_with_name(bAnimContext *ac, FCurve *fcu, eAnim_ChannelType channel_type, void *owner, ID *owner_id)
static size_t animdata_filter_ds_obdata(bAnimContext *ac, ListBase *anim_data, Object *ob, eAnimFilter_Flags filter_mode)
static bool fcurve_has_errors(bAnimContext *ac, const FCurve *fcu)
static size_t animdata_filter_ds_nodetree(bAnimContext *ac, ListBase *anim_data, ID *owner_id, bNodeTree *ntree, const eAnimFilter_Flags filter_mode)
#define END_ANIMFILTER_SUBCHANNELS
static bAnimListElem * make_new_animlistelem(Main *bmain, void *data, const eAnim_ChannelType datatype, ID *owner_id, ID *fcurve_owner_id)
static size_t animfilter_act_group(bAnimContext *ac, ListBase *anim_data, bAction *act, animrig::slot_handle_t slot_handle, bActionGroup *agrp, eAnimFilter_Flags filter_mode, ID *owner_id)
static bool fcurve_span_must_be_selected(const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_world(bAnimContext *ac, ListBase *anim_data, Scene *sce, World *wo, eAnimFilter_Flags filter_mode)
static bool skip_fcurve_selected_data(bAnimContext *ac, FCurve *fcu, ID *owner_id, const eAnimFilter_Flags filter_mode)
static size_t animdata_filter_remove_duplis(ListBase *anim_data)
static size_t animfilter_action_slots(bAnimContext *ac, ListBase *anim_data, animrig::Action &action, const eAnimFilter_Flags filter_mode, ID *owner_id)
static size_t animdata_filter_animchan(bAnimContext *ac, ListBase *anim_data, bAnimListElem *channel, const eAnimFilter_Flags filter_mode)
size_t ANIM_animfilter_action_slot(bAnimContext *ac, ListBase *anim_data, animrig::Action &action, animrig::Slot &slot, const eAnimFilter_Flags filter_mode, ID *animated_id)
static FCurve * animfilter_fcurve_next(bAnimContext *ac, FCurve *first, eAnim_ChannelType channel_type, const eAnimFilter_Flags filter_mode, void *owner, ID *owner_id)
static size_t animdata_filter_ds_cachefile(bAnimContext *ac, ListBase *anim_data, CacheFile *cache_file, eAnimFilter_Flags filter_mode)
static size_t animdata_filter_dopesheet(bAnimContext *ac, ListBase *anim_data, eAnimFilter_Flags filter_mode)
static size_t animdata_filter_ds_keyanim(bAnimContext *ac, ListBase *anim_data, Object *ob, Key *key, eAnimFilter_Flags filter_mode)
#define BEGIN_ANIMFILTER_SUBCHANNELS(expanded_check)
static size_t animfilter_nla(bAnimContext *ac, ListBase *anim_data, AnimData *adt, const eAnimFilter_Flags filter_mode, ID *owner_id)
static size_t animdata_filter_ds_linestyle(bAnimContext *ac, ListBase *anim_data, Scene *sce, eAnimFilter_Flags filter_mode)
static bool graphedit_get_context(bAnimContext *ac, SpaceGraph *sipo)
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode, void *data, const eAnimCont_Types datatype)
#define ANIMDATA_FILTER_CASES(id, adtOk, nlaOk, driversOk, nlaKeysOk, legacyActionOk, layeredActionOk)
#define ANIMCHANNEL_NEW_CHANNEL_FULL(bmain, channel_data, channel_type, owner_id, fcurve_owner_id, ale_statement)
static bool actedit_get_context(bAnimContext *ac, SpaceAction *saction)
static bool ale_name_matches_dopesheet_filter(const bDopeSheet &ads, bAnimListElem &ale)
static Base ** animdata_filter_ds_sorted_bases(bAnimContext *ac, const Scene *scene, ViewLayer *view_layer, const eAnimFilter_Flags filter_mode, size_t *r_usable_bases)
static size_t animfilter_action(bAnimContext *ac, ListBase *anim_data, animrig::Action &action, const animrig::slot_handle_t slot_handle, const eAnimFilter_Flags filter_mode, ID *owner_id)
static size_t animdata_filter_dopesheet_scene(bAnimContext *ac, ListBase *anim_data, Scene *sce, eAnimFilter_Flags filter_mode)
static size_t animdata_filter_mask_data(bAnimContext *ac, ListBase *anim_data, Mask *mask, const eAnimFilter_Flags filter_mode)
#define ANIMCHANNEL_SELEDITOK(test_func)
#define ANIM_CHAN_NAME_SIZE
#define U
BMesh const char void * data
void append(const T &value)
blender::Span< const Slot * > slots() const
Slot * slot_for_handle(slot_handle_t handle)
blender::Span< const FCurve * > fcurves() const
blender::Span< const bActionGroup * > channel_groups() const
static constexpr slot_handle_t unassigned
const LayerGroup & as_group() const
#define SELECT
#define GS(x)
#define printf(...)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
static ulong * next
#define G(x, y, z)
const animrig::Channelbag * channelbag_for_action_slot(const Action &action, slot_handle_t slot_handle)
bool bone_is_visible(const bArmature *armature, const Bone *bone)
ID * action_slot_get_id_best_guess(Main &bmain, Slot &slot, ID *primary_id)
decltype(::ActionSlot::handle) slot_handle_t
Span< FCurve * > fcurves_for_action_slot(Action &action, slot_handle_t slot_handle)
FCurve * fcurve_find_in_assigned_slot(AnimData &adt, const FCurveDescriptor &fcurve_descriptor)
bNode * node_find_node_by_name(bNodeTree &ntree, StringRefNull name)
Definition node.cc:3275
void foreach_strip(ListBase *seqbase, ForEachFunc callback, void *user_data)
Definition iterator.cc:59
Editing * editing_get(const Scene *scene)
Definition sequencer.cc:286
Strip * get_strip_by_name(ListBase *seqbase, const char *name, bool recursive)
float wrap(float value, float max, float min)
Definition node_math.h:103
const char * name
bAction * action
int32_t slot_handle
ListBase drivers
ListBase nla_tracks
short flag
struct Object * object
struct AnimData * adt
struct AnimData * adt
struct AnimData * adt
struct AnimData * adt
struct FCurve * next
bActionGroup * grp
char * rna_path
ChannelDriver * driver
struct AnimData * adt
GreasePencilLayerTreeNode base
GreasePencilLayerTreeNode base
GreasePencilLayerTreeGroup * root_group_ptr
struct AnimData * adt
Definition DNA_ID.h:414
int tag
Definition DNA_ID.h:442
char name[258]
Definition DNA_ID.h:432
struct AnimData * adt
char type
ListBase block
struct AnimData * adt
struct AnimData * adt
struct AnimData * adt
struct bNodeTree * nodetree
void * last
void * first
ListBase masks
Definition BKE_main.hh:312
ListBase movieclips
Definition BKE_main.hh:311
ListBase gpencils
Definition BKE_main.hh:309
ListBase cachefiles
Definition BKE_main.hh:314
struct AnimData * adt
int masklay_act
struct bNodeTree * nodetree
struct AnimData * adt
struct AnimData * adt
struct AnimData * adt
ListBase strips
ListBase strips
struct NlaTrack * next
char name[64]
struct NlaTrack * prev
ListBase particlesystem
struct bPose * pose
ListBase modifiers
struct PartDeflect * pd
short visibility_flag
struct AnimData * adt
struct AnimData * adt
struct MTex * mtex[18]
struct AnimData * adt
struct MovieClip * clip
struct Editing * ed
struct bNodeTree * compositing_node_group
ListBase view_layers
struct AnimData * adt
ListBase markers
struct World * world
ListBase spacedata
struct bDopeSheet * ads
struct bDopeSheet * ads
struct AnimData * adt
struct Mask * mask
char name[64]
struct AnimData * adt
struct bNodeTree * nodetree
struct AnimData * adt
struct bNodeTree * nodetree
struct AnimData * adt
struct ActionChannelbag * channelbag
ListBase curves
ListBase groups
ListBase markers
void(* name)(bAnimListElem *ale, char *name)
eDopeSheet_FilterFlag flag
eAnimEdit_Context dopesheet_mode
eGraphEdit_Mode grapheditor_mode
SpaceLink * sl
ListBase * markers
eAnimCont_Types datatype
bDopeSheet * ads
ID * active_action_user
bAction * active_action
eSpace_Type spacetype
ViewLayer * view_layer
ARegion * region
Depsgraph * depsgraph
Object * obact
eRegion_Type regiontype
struct bAnimContext::@242276144047124000362312251357014355232301013033 filters
ScrArea * area
eDopeSheet_FilterFlag2 flag2
int32_t slot_handle
AnimData * adt
eAnim_ChannelType type
eAnim_KeyType datatype
struct AnimData * adt
char searchstr[64]
struct Collection * filter_grp
struct AnimData * adt
struct AnimData * adt
char name[64]
struct Bone * bone
eAnimFilter_Flags filter_mode
i
Definition text_draw.cc:230