Blender V5.0
anim_channels_edit.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 Blender Authors, Joshua Leung. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <cstdio>
11#include <cstdlib>
12#include <cstring>
13
14#include "MEM_guardedalloc.h"
15
16#include "BLI_listbase.h"
17#include "BLI_math_base.h"
18#include "BLI_span.hh"
19#include "BLI_string_utf8.h"
20#include "BLI_utildefines.h"
21
22#include "DNA_anim_types.h"
24#include "DNA_key_types.h"
25#include "DNA_mask_types.h"
26#include "DNA_object_types.h"
27#include "DNA_scene_types.h"
28
29#include "RNA_access.hh"
30#include "RNA_define.hh"
31#include "RNA_path.hh"
32
33#include "BKE_action.hh"
34#include "BKE_anim_data.hh"
35#include "BKE_context.hh"
36#include "BKE_fcurve.hh"
37#include "BKE_global.hh"
38#include "BKE_gpencil_legacy.h"
39#include "BKE_grease_pencil.hh"
40#include "BKE_layer.hh"
41#include "BKE_lib_id.hh"
42#include "BKE_library.hh"
43#include "BKE_mask.h"
44#include "BKE_nla.hh"
45#include "BKE_report.hh"
46#include "BKE_scene.hh"
47#include "BKE_screen.hh"
48#include "BKE_workspace.hh"
49
50#include "ANIM_action.hh"
51#include "ANIM_action_legacy.hh"
52
53#include "DEG_depsgraph.hh"
55
56#include "UI_interface.hh"
57#include "UI_view2d.hh"
58
59#include "ED_armature.hh"
60#include "ED_keyframes_edit.hh" /* XXX move the select modes out of there! */
61#include "ED_markers.hh"
62#include "ED_object.hh"
63#include "ED_screen.hh"
64#include "ED_select_utils.hh"
65
66#include "ANIM_animdata.hh"
67#include "ANIM_fcurve.hh"
68
69#include "WM_api.hh"
70#include "WM_message.hh"
71#include "WM_types.hh"
72
73#include "BLT_translation.hh"
74
75/* -------------------------------------------------------------------- */
78
80 SpaceLink *space_link,
81 Scene *scene,
82 ID *id,
83 const bool include_handles,
84 const float range[2],
85 rctf *r_bounds)
86{
87 const bool fcu_selection_only = false;
88 const bool found_bounds = BKE_fcurve_calc_bounds(
89 fcu, fcu_selection_only, include_handles, range, r_bounds);
90
91 if (!found_bounds) {
92 return false;
93 }
94
95 const short mapping_flag = ANIM_get_normalization_flags(space_link);
96
97 float offset;
98 const float unit_fac = ANIM_unit_mapping_get_factor(scene, id, fcu, mapping_flag, &offset);
99
100 r_bounds->ymin = (r_bounds->ymin + offset) * unit_fac;
101 r_bounds->ymax = (r_bounds->ymax + offset) * unit_fac;
102
103 const float min_height = 0.01f;
104 const float height = BLI_rctf_size_y(r_bounds);
105 if (height < min_height) {
106 r_bounds->ymin -= (min_height - height) / 2;
107 r_bounds->ymax += (min_height - height) / 2;
108 }
109
110 return true;
111}
112
113static bool get_gpencil_bounds(bGPDlayer *gpl, const float range[2], rctf *r_bounds)
114{
115 bool found_start = false;
116 int start_frame = 0;
117 int end_frame = 1;
118 LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
119 if (gpf->framenum < range[0]) {
120 continue;
121 }
122 if (gpf->framenum > range[1]) {
123 break;
124 }
125 if (!found_start) {
126 start_frame = gpf->framenum;
127 found_start = true;
128 }
129 end_frame = gpf->framenum;
130 }
131 r_bounds->xmin = start_frame;
132 r_bounds->xmax = end_frame;
133 r_bounds->ymin = 0;
134 r_bounds->ymax = 1;
135
136 return found_start;
137}
138
140 const float range[2],
141 rctf *r_bounds)
142{
143 using namespace blender::bke::greasepencil;
144 const Layer &layer = gplayer->wrap();
145
146 bool found_start = false;
147 int start_frame = 0;
148 int end_frame = 1;
149
150 for (const FramesMapKeyT key : layer.sorted_keys()) {
151 if (key < range[0]) {
152 continue;
153 }
154 if (key > range[1]) {
155 break;
156 }
157
158 if (!found_start) {
159 start_frame = key;
160 found_start = true;
161 }
162 end_frame = key;
163 }
164 r_bounds->xmin = start_frame;
165 r_bounds->xmax = end_frame;
166 r_bounds->ymin = 0;
167 r_bounds->ymax = 1;
168
169 return found_start;
170}
171
173 bAnimListElem *ale,
174 const float range[2],
175 const bool include_handles,
176 rctf *r_bounds)
177{
178 bool found_bounds = false;
179 switch (ale->datatype) {
180 case ALE_GPFRAME: {
181 bGPDlayer *gpl = static_cast<bGPDlayer *>(ale->data);
182 found_bounds = get_gpencil_bounds(gpl, range, r_bounds);
183 break;
184 }
186 found_bounds = get_grease_pencil_layer_bounds(
187 static_cast<const GreasePencilLayer *>(ale->data), range, r_bounds);
188 break;
189
190 case ALE_FCURVE: {
191 FCurve *fcu = static_cast<FCurve *>(ale->key_data);
192 found_bounds = get_normalized_fcurve_bounds(
193 fcu, ac->sl, ac->scene, ale->id, include_handles, range, r_bounds);
194 if (found_bounds) {
195 r_bounds->xmin = ANIM_nla_tweakedit_remap(ale, r_bounds->xmin, NLATIME_CONVERT_MAP);
196 r_bounds->xmax = ANIM_nla_tweakedit_remap(ale, r_bounds->xmax, NLATIME_CONVERT_MAP);
197 }
198 break;
199 }
200 case ALE_NONE:
201 case ALE_MASKLAY:
202 case ALE_NLASTRIP:
203 case ALE_ALL:
204 case ALE_SCE:
205 case ALE_OB:
206 case ALE_ACT:
207 case ALE_GROUP:
209 case ALE_ACTION_SLOT:
212 return false;
213 }
214 return found_bounds;
215}
216
217/* Pad the given rctf with regions that could block the view.
218 * For example Markers and Time Scrubbing. */
220{
221 BLI_rctf_scale(bounds, 1.1f);
222
223 const float pad_top = UI_TIME_SCRUB_MARGIN_Y;
224 const float pad_bottom = BLI_listbase_is_empty(ED_context_get_markers(C)) ?
227 BLI_rctf_pad_y(bounds, region->winy, pad_bottom, pad_top);
228}
229
231
232/* -------------------------------------------------------------------- */
235
237 void *data,
238 eAnimCont_Types datatype,
240 void *channel_data,
241 eAnim_ChannelType channel_type)
242{
243 /* TODO: extend for animdata types. */
244
245 ListBase anim_data = {nullptr, nullptr};
246 bAnimListElem *ale;
247
248 /* try to build list of filtered items */
249 ANIM_animdata_filter(ac, &anim_data, filter, data, datatype);
250 if (BLI_listbase_is_empty(&anim_data)) {
251 return;
252 }
253
254 /* only clear the 'active' flag for the channels of the same type */
255 for (ale = static_cast<bAnimListElem *>(anim_data.first); ale; ale = ale->next) {
256 /* skip if types don't match */
257 if (channel_type != ale->type) {
258 continue;
259 }
260
261 /* flag to set depends on type */
262 switch (ale->type) {
263 case ANIMTYPE_GROUP: {
264 bActionGroup *agrp = static_cast<bActionGroup *>(ale->data);
265
267 break;
268 }
269 case ANIMTYPE_FCURVE:
270 case ANIMTYPE_NLACURVE: {
271 FCurve *fcu = static_cast<FCurve *>(ale->data);
272
274 break;
275 }
276 case ANIMTYPE_NLATRACK: {
277 NlaTrack *nlt = static_cast<NlaTrack *>(ale->data);
278
280 break;
281 }
282 case ANIMTYPE_FILLACTD: /* Action Expander */
283 case ANIMTYPE_FILLACT_LAYERED: /* Animation Expander */
284 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
285 case ANIMTYPE_DSLAM:
286 case ANIMTYPE_DSCAM:
288 case ANIMTYPE_DSCUR:
289 case ANIMTYPE_DSSKEY:
290 case ANIMTYPE_DSWOR:
291 case ANIMTYPE_DSPART:
292 case ANIMTYPE_DSMBALL:
293 case ANIMTYPE_DSARM:
294 case ANIMTYPE_DSMESH:
295 case ANIMTYPE_DSTEX:
296 case ANIMTYPE_DSLAT:
298 case ANIMTYPE_DSSPK:
300 case ANIMTYPE_DSMCLIP:
301 case ANIMTYPE_DSHAIR:
305 case ANIMTYPE_NLAACTION: {
306 /* need to verify that this data is valid for now */
307 if (ale->adt) {
309 }
310 break;
311 }
312 case ANIMTYPE_GPLAYER: {
313 bGPDlayer *gpl = static_cast<bGPDlayer *>(ale->data);
314
316 break;
317 }
318 case ANIMTYPE_NONE:
321 case ANIMTYPE_SUMMARY:
322 case ANIMTYPE_SCENE:
323 case ANIMTYPE_OBJECT:
327 case ANIMTYPE_DSNTREE:
334 case ANIMTYPE_PALETTE:
336 break;
337 }
338 }
339
340 /* set active flag */
341 if (channel_data) {
342 switch (channel_type) {
343 case ANIMTYPE_GROUP: {
344 bActionGroup *agrp = static_cast<bActionGroup *>(channel_data);
345 agrp->flag |= AGRP_ACTIVE;
346 break;
347 }
348 case ANIMTYPE_FCURVE:
349 case ANIMTYPE_NLACURVE: {
350 FCurve *fcu = static_cast<FCurve *>(channel_data);
351 fcu->flag |= FCURVE_ACTIVE;
352 break;
353 }
354 case ANIMTYPE_NLATRACK: {
355 NlaTrack *nlt = static_cast<NlaTrack *>(channel_data);
356 nlt->flag |= NLATRACK_ACTIVE;
357 break;
358 }
360 /* ANIMTYPE_ACTION_SLOT is not supported by this function (because the to-be-activated
361 * bAnimListElement is not passed here, only sub-fields of it), just call
362 * Action::slot_active_set() directly. */
363 break;
364 case ANIMTYPE_FILLACTD: /* Action Expander */
365 case ANIMTYPE_FILLACT_LAYERED: /* Animation Expander */
366 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
367 case ANIMTYPE_DSLAM:
368 case ANIMTYPE_DSCAM:
370 case ANIMTYPE_DSCUR:
371 case ANIMTYPE_DSSKEY:
372 case ANIMTYPE_DSWOR:
373 case ANIMTYPE_DSPART:
374 case ANIMTYPE_DSMBALL:
375 case ANIMTYPE_DSARM:
376 case ANIMTYPE_DSMESH:
377 case ANIMTYPE_DSLAT:
379 case ANIMTYPE_DSSPK:
380 case ANIMTYPE_DSNTREE:
381 case ANIMTYPE_DSTEX:
383 case ANIMTYPE_DSMCLIP:
384 case ANIMTYPE_DSHAIR:
388 case ANIMTYPE_NLAACTION: {
389 /* need to verify that this data is valid for now */
390 if (ale && ale->adt) {
391 ale->adt->flag |= ADT_UI_ACTIVE;
392 }
393 break;
394 }
395
396 case ANIMTYPE_GPLAYER: {
397 bGPDlayer *gpl = static_cast<bGPDlayer *>(channel_data);
398 gpl->flag |= GP_LAYER_ACTIVE;
399 break;
400 }
401 /* unhandled currently, but may be interesting */
404 break;
405
406 /* other types */
407 default:
408 break;
409 }
410 }
411
412 /* clean up */
413 ANIM_animdata_freelist(&anim_data);
414}
415
417{
418 using namespace blender;
419
420 switch (ale->type) {
421 case ANIMTYPE_FILLACTD: /* Action Expander */
422 case ANIMTYPE_FILLACT_LAYERED: /* Animation Expander */
423 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
424 case ANIMTYPE_DSLAM:
425 case ANIMTYPE_DSCAM:
427 case ANIMTYPE_DSCUR:
428 case ANIMTYPE_DSSKEY:
429 case ANIMTYPE_DSWOR:
430 case ANIMTYPE_DSPART:
431 case ANIMTYPE_DSMBALL:
432 case ANIMTYPE_DSARM:
433 case ANIMTYPE_DSMESH:
434 case ANIMTYPE_DSNTREE:
435 case ANIMTYPE_DSTEX:
436 case ANIMTYPE_DSLAT:
438 case ANIMTYPE_DSSPK:
440 case ANIMTYPE_DSMCLIP:
441 case ANIMTYPE_DSHAIR:
445 case ANIMTYPE_NLAACTION: {
446 return ale->adt && (ale->adt->flag & ADT_UI_ACTIVE);
447 }
448 case ANIMTYPE_GROUP: {
449 bActionGroup *argp = static_cast<bActionGroup *>(ale->data);
450 return argp->flag & AGRP_ACTIVE;
451 }
452 case ANIMTYPE_FCURVE:
453 case ANIMTYPE_NLACURVE: {
454 FCurve *fcu = static_cast<FCurve *>(ale->data);
455 return fcu->flag & FCURVE_ACTIVE;
456 }
457 case ANIMTYPE_GPLAYER: {
458 bGPDlayer *gpl = static_cast<bGPDlayer *>(ale->data);
459 return gpl->flag & GP_LAYER_ACTIVE;
460 }
462 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ale->id);
463 return grease_pencil->is_layer_active(
464 static_cast<blender::bke::greasepencil::Layer *>(ale->data));
465 }
467 animrig::Slot *slot = reinterpret_cast<animrig::Slot *>(ale->data);
468 return slot->is_active();
469 }
470 /* These channel types do not have active flags. */
471 case ANIMTYPE_NONE:
474 case ANIMTYPE_SUMMARY:
475 case ANIMTYPE_SCENE:
476 case ANIMTYPE_OBJECT:
485 case ANIMTYPE_PALETTE:
487 break;
488 }
489 return false;
490}
491
492/* change_active determines whether to change the active bone of the armature when selecting pose
493 * channels. It is false during range selection otherwise true. */
495 bActionGroup *agrp,
496 bAnimListElem *ale,
497 const bool change_active)
498{
499 /* Armatures-Specific Feature:
500 * See mouse_anim_channels() -> ANIMTYPE_GROUP case for more details (#38737)
501 */
502 if ((ac->filters.flag & ADS_FILTER_ONLYSEL) == 0) {
503 if ((ale->id) && (GS(ale->id->name) == ID_OB)) {
504 Object *ob = reinterpret_cast<Object *>(ale->id);
505 if (ob->type == OB_ARMATURE) {
506 /* Assume for now that any group with corresponding name is what we want
507 * (i.e. for an armature whose location is animated, things would break
508 * if the user were to add a bone named "Location").
509 *
510 * TODO: check the first F-Curve or so to be sure...
511 */
513
514 if (agrp->flag & AGRP_SELECTED) {
515 ED_pose_bone_select(ob, pchan, true, change_active);
516 }
517 else {
518 ED_pose_bone_select(ob, pchan, false, change_active);
519 }
520 }
521 }
522 }
523}
524
526{
527 ListBase anim_data = {nullptr, nullptr};
528
529 /* filter data */
530 /* NOTE: no list visible, otherwise, we get dangling */
533 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
534
535 return anim_data;
536}
537
539{
540 /* See if we should be selecting or deselecting. */
541 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
542 switch (ale->type) {
543 case ANIMTYPE_SCENE:
544 if (ale->flag & SCE_DS_SELECTED) {
546 }
547 break;
548 case ANIMTYPE_OBJECT:
549#if 0 /* for now, do not take object selection into account, since it gets too annoying */
550 if (ale->flag & SELECT) {
552 }
553#endif
554 break;
555 case ANIMTYPE_GROUP:
556 if (ale->flag & AGRP_SELECTED) {
558 }
559 break;
560 case ANIMTYPE_FCURVE:
562 if (ale->flag & FCURVE_SELECTED) {
564 }
565 break;
567 if (ale->flag & KEYBLOCK_SEL) {
569 }
570 break;
572 if (ale->flag & NLATRACK_SELECTED) {
574 }
575 break;
577 using namespace blender::animrig;
578 if (static_cast<Slot *>(ale->data)->is_selected()) {
580 }
581 break;
582 }
583 case ANIMTYPE_FILLACTD: /* Action Expander */
584 case ANIMTYPE_FILLACT_LAYERED: /* Animation Expander */
585 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
586 case ANIMTYPE_DSLAM:
587 case ANIMTYPE_DSCAM:
589 case ANIMTYPE_DSCUR:
590 case ANIMTYPE_DSSKEY:
591 case ANIMTYPE_DSWOR:
592 case ANIMTYPE_DSPART:
593 case ANIMTYPE_DSMBALL:
594 case ANIMTYPE_DSARM:
595 case ANIMTYPE_DSMESH:
596 case ANIMTYPE_DSNTREE:
597 case ANIMTYPE_DSTEX:
598 case ANIMTYPE_DSLAT:
600 case ANIMTYPE_DSSPK:
602 case ANIMTYPE_DSMCLIP:
603 case ANIMTYPE_DSHAIR:
607 case ANIMTYPE_NLAACTION: {
608 if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED)) {
610 }
611 break;
612 }
613 case ANIMTYPE_GPLAYER:
614 if (ale->flag & GP_LAYER_SELECT) {
616 }
617 break;
619 if (ale->flag & MASK_LAYERFLAG_SELECT) {
621 }
622 break;
623 case ANIMTYPE_NONE:
626 case ANIMTYPE_SUMMARY:
633 case ANIMTYPE_PALETTE:
635 break;
636 }
637 }
638
640}
641
652template<typename T>
653static void templated_selection_state_update(T &selectable_thing,
654 const eAnimChannels_SetFlag selectmode)
655{
656 switch (selectmode) {
658 selectable_thing.set_selected(!selectable_thing.is_selected());
659 break;
661 selectable_thing.set_selected(true);
662 break;
663 /* You would probably expect "extend range" to select rather than deselect,
664 * and "toggle" to behave the same as "invert", because that's what a sane
665 * system would do. However, this function is used in the same places as the
666 * `ACHANNEL_SET_FLAG` macro, and therefore reproduces its logic. Note that
667 * in the "extend range" case this is actually functionally important,
668 * because `anim_channels_select_set()` below uses that case to *deselect
669 * everything* before `animchannel_select_range()` later does the actual
670 * selection of the channels in the range. */
674 selectable_thing.set_selected(false);
675 break;
676 }
677}
678
680 const ListBase anim_data,
682{
683 using namespace blender;
684
685 /* Boolean to keep active channel status during range selection. */
686 const bool change_active = (sel != ACHANNEL_SETFLAG_EXTEND_RANGE);
687
688 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
689 switch (ale->type) {
690 case ANIMTYPE_SCENE: {
691 if (change_active) {
692 break;
693 }
694 Scene *scene = static_cast<Scene *>(ale->data);
695
697
698 if (scene->adt) {
700 }
701 break;
702 }
703 case ANIMTYPE_OBJECT: {
704#if 0 /* for now, do not take object selection into account, since it gets too annoying */
705 Base *base = (Base *)ale->data;
706 Object *ob = base->object;
707
708 ACHANNEL_SET_FLAG(base, sel, SELECT);
709 ACHANNEL_SET_FLAG(ob, sel, SELECT);
710
711 if (ob->adt) {
713 }
714#endif
715 break;
716 }
717 case ANIMTYPE_GROUP: {
718 bActionGroup *agrp = static_cast<bActionGroup *>(ale->data);
720 select_pchan_for_action_group(ac, agrp, ale, change_active);
721 if (change_active) {
722 agrp->flag &= ~AGRP_ACTIVE;
723 }
724 break;
725 }
726 case ANIMTYPE_FCURVE:
727 case ANIMTYPE_NLACURVE: {
728 FCurve *fcu = static_cast<FCurve *>(ale->data);
729
731 if (!(fcu->flag & FCURVE_SELECTED) && change_active) {
732 /* Only erase the ACTIVE flag when deselecting. This ensures that "select all curves"
733 * retains the currently active curve. */
734 fcu->flag &= ~FCURVE_ACTIVE;
735 }
736 break;
737 }
738 case ANIMTYPE_SHAPEKEY: {
739 KeyBlock *kb = static_cast<KeyBlock *>(ale->data);
740
742 break;
743 }
744 case ANIMTYPE_NLATRACK: {
745 NlaTrack *nlt = static_cast<NlaTrack *>(ale->data);
746
748 nlt->flag &= ~NLATRACK_ACTIVE;
749 break;
750 }
752 animrig::Slot *slot = static_cast<animrig::Slot *>(ale->data);
754 break;
755 }
756 case ANIMTYPE_FILLACTD: /* Action Expander */
757 case ANIMTYPE_FILLACT_LAYERED: /* Animation Expander */
758 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
759 case ANIMTYPE_DSLAM:
760 case ANIMTYPE_DSCAM:
762 case ANIMTYPE_DSCUR:
763 case ANIMTYPE_DSSKEY:
764 case ANIMTYPE_DSWOR:
765 case ANIMTYPE_DSPART:
766 case ANIMTYPE_DSMBALL:
767 case ANIMTYPE_DSARM:
768 case ANIMTYPE_DSMESH:
769 case ANIMTYPE_DSNTREE:
770 case ANIMTYPE_DSTEX:
771 case ANIMTYPE_DSLAT:
773 case ANIMTYPE_DSSPK:
775 case ANIMTYPE_DSMCLIP:
776 case ANIMTYPE_DSHAIR:
780 case ANIMTYPE_NLAACTION: {
781 /* need to verify that this data is valid for now */
782 if (ale->adt) {
783 ACHANNEL_SET_FLAG(ale->adt, sel, ADT_UI_SELECTED);
784 if (change_active) {
785 ale->adt->flag &= ~ADT_UI_ACTIVE;
786 }
787 }
788 break;
789 }
791 using namespace blender::bke::greasepencil;
792 Layer *layer = static_cast<Layer *>(ale->data);
794 break;
795 }
796 case ANIMTYPE_GPLAYER: {
797 bGPDlayer *gpl = static_cast<bGPDlayer *>(ale->data);
798
800 break;
801 }
802 case ANIMTYPE_MASKLAYER: {
803 MaskLayer *masklay = static_cast<MaskLayer *>(ale->data);
804
806 break;
807 }
808 case ANIMTYPE_NONE:
811 case ANIMTYPE_SUMMARY:
817 case ANIMTYPE_PALETTE:
819 break;
820 }
821 }
822}
823
830
838
840
841/* -------------------------------------------------------------------- */
844
845/* Copy a certain channel setting to parents of the modified channel. */
847 const eAnimChannel_Settings setting,
848 const eAnimChannels_SetFlag mode,
849 bAnimListElem *const match,
850 const int matchLevel)
851{
852 /* flush up?
853 *
854 * For Visibility:
855 * - only flush up if the current state is now enabled (positive 'on' state is default)
856 * (otherwise, it's too much work to force the parents to be inactive too)
857 *
858 * For everything else:
859 * - only flush up if the current state is now disabled (negative 'off' state is default)
860 * (otherwise, it's too much work to force the parents to be active too)
861 */
862 if (setting == ACHANNEL_SETTING_VISIBLE) {
863 if (mode == ACHANNEL_SETFLAG_CLEAR) {
864 return;
865 }
866 }
867 else {
868 if (mode != ACHANNEL_SETFLAG_CLEAR) {
869 return;
870 }
871 }
872
873 /* Go backwards in the list, until the highest-ranking element
874 * (by indentation has been covered). */
875 int prevLevel = matchLevel;
876 for (bAnimListElem *ale = match->prev; ale; ale = ale->prev) {
878
879 /* if no channel info was found, skip, since this type might not have any useful info */
880 if (acf == nullptr) {
881 continue;
882 }
883
884 /* Get the level of the current channel traversed
885 * - we define the level as simply being the offset for the start of the channel
886 */
887 const int level = (acf->get_offset) ? acf->get_offset(ac, ale) : 0;
888
889 if (level == prevLevel) {
890 /* Don't influence siblings. */
891 continue;
892 }
893
894 if (level > prevLevel) {
895 /* If previous level was a base-level (i.e. 0 offset / root of one hierarchy), stop here. */
896 if (prevLevel == 0) {
897 return;
898 }
899
900 /* Otherwise, this level weaves into another sibling hierarchy to the previous one just
901 * finished, so skip until we get to the parent of this level. */
902 continue;
903 }
904
905 /* The level is 'less than' (i.e. more important) the level we're matching but also 'less
906 * than' the level just tried (i.e. only the 1st group above grouped F-Curves, when toggling
907 * visibility of F-Curves, gets flushed, which should happen if we don't let prevLevel get
908 * updated below once the first 1st group is found). */
909 ANIM_channel_setting_set(ac, ale, setting, mode);
910
911 /* store this level as the 'old' level now */
912 prevLevel = level;
913 }
914}
915
916/* Copy a certain channel setting to children of the modified channel. */
918 const eAnimChannel_Settings setting,
919 const eAnimChannels_SetFlag mode,
920 bAnimListElem *const match,
921 const int matchLevel)
922{
923 /* go forwards in the list, until the lowest-ranking element (by indentation has been covered) */
924 for (bAnimListElem *ale = match->next; ale; ale = ale->next) {
926
927 /* if no channel info was found, skip, since this type might not have any useful info */
928 if (acf == nullptr) {
929 continue;
930 }
931
932 /* get the level of the current channel traversed
933 * - we define the level as simply being the offset for the start of the channel
934 */
935 const int level = (acf->get_offset) ? acf->get_offset(ac, ale) : 0;
936
937 /* if the level is 'greater than' (i.e. less important) the channel that was changed,
938 * flush the new status...
939 */
940 if (level > matchLevel) {
941 ANIM_channel_setting_set(ac, ale, setting, mode);
942 /* however, if the level is 'less than or equal to' the channel that was changed,
943 * (i.e. the current channel is as important if not more important than the changed
944 * channel) then we should stop, since we've found the last one of the children we should
945 * flush
946 */
947 }
948 else {
949 break;
950 }
951 }
952}
953
955 ListBase *anim_data,
956 bAnimListElem *ale_setting,
957 eAnimChannel_Settings setting,
959{
960 bAnimListElem *match = nullptr;
961 int matchLevel = 0;
962
963 /* sanity check */
964 if (ELEM(nullptr, anim_data, anim_data->first)) {
965 return;
966 }
967
968 if (setting == ACHANNEL_SETTING_ALWAYS_VISIBLE) {
969 return;
970 }
971
972 /* find the channel that got changed */
973 LISTBASE_FOREACH (bAnimListElem *, ale, anim_data) {
974 /* compare data, and type as main way of identifying the channel */
975 if ((ale->data == ale_setting->data) && (ale->type == ale_setting->type)) {
976 /* We also have to check the ID, this is assigned to,
977 * since a block may have multiple users. */
978 /* TODO: is the owner-data more revealing? */
979 if (ale->id == ale_setting->id) {
980 match = ale;
981 break;
982 }
983 }
984 }
985 if (match == nullptr) {
986 printf("ERROR: no channel matching the one changed was found\n");
987 return;
988 }
989
990 {
991 const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale_setting);
992 if (acf == nullptr) {
993 printf("ERROR: no channel info for the changed channel\n");
994 return;
995 }
996
997 /* get the level of the channel that was affected
998 * - we define the level as simply being the offset for the start of the channel
999 */
1000 matchLevel = (acf->get_offset) ? acf->get_offset(ac, ale_setting) : 0;
1001 }
1002
1003 anim_flush_channel_setting_up(ac, setting, mode, match, matchLevel);
1004 anim_flush_channel_setting_down(ac, setting, mode, match, matchLevel);
1005}
1006
1008{
1009
1011
1012 if (!window_region) {
1013 return;
1014 }
1015
1016 ListBase anim_data = {nullptr, nullptr};
1020 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
1021
1022 rctf bounds{};
1023 bounds.xmin = FLT_MAX;
1024 bounds.xmax = -FLT_MAX;
1025 bounds.ymin = FLT_MAX;
1026 bounds.ymax = -FLT_MAX;
1027 const bool include_handles = false;
1028 float frame_range[2] = {window_region->v2d.cur.xmin, window_region->v2d.cur.xmax};
1029 if (ac->scene->r.flag & SCER_PRV_RANGE) {
1030 frame_range[0] = ac->scene->r.psfra;
1031 frame_range[1] = ac->scene->r.pefra;
1032 }
1033
1034 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
1035 rctf channel_bounds;
1036 const bool found_bounds = get_channel_bounds(
1037 ac, ale, frame_range, include_handles, &channel_bounds);
1038 if (found_bounds) {
1039 BLI_rctf_union(&bounds, &channel_bounds);
1040 }
1041 }
1042
1043 if (!BLI_rctf_is_valid(&bounds)) {
1044 ANIM_animdata_freelist(&anim_data);
1045 return;
1046 }
1047
1048 add_region_padding(C, window_region, &bounds);
1049
1050 window_region->v2d.cur.ymin = bounds.ymin;
1051 window_region->v2d.cur.ymax = bounds.ymax;
1052
1053 ANIM_animdata_freelist(&anim_data);
1054}
1055
1057
1058/* -------------------------------------------------------------------- */
1061
1062/* poll callback for being in an Animation Editor channels list region */
1064{
1065 ScrArea *area = CTX_wm_area(C);
1066
1067 /* channels region test */
1068 /* TODO: could enhance with actually testing if channels region? */
1069 if (ELEM(nullptr, area, CTX_wm_region(C))) {
1070 return false;
1071 }
1072 /* animation editor test */
1073 if (ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH, SPACE_NLA) == 0) {
1074 return false;
1075 }
1076
1077 return true;
1078}
1079
1080/* Poll callback for Animation Editor channels list region + not in NLA-tweak-mode for NLA. */
1082{
1083 ScrArea *area = CTX_wm_area(C);
1084 Scene *scene = CTX_data_scene(C);
1085
1086 /* channels region test */
1087 /* TODO: could enhance with actually testing if channels region? */
1088 if (ELEM(nullptr, area, CTX_wm_region(C))) {
1089 return false;
1090 }
1091 /* animation editor test */
1092 if (ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH, SPACE_NLA) == 0) {
1093 return false;
1094 }
1095
1096 /* NLA tweak-mode test. */
1097 if (area->spacetype == SPACE_NLA) {
1098 if ((scene == nullptr) || (scene->flag & SCE_NLA_EDIT_ON)) {
1099 return false;
1100 }
1101 }
1102
1103 return true;
1104}
1105
1107
1108/* -------------------------------------------------------------------- */
1111
1112/* constants for channel rearranging */
1113/* WARNING: don't change existing ones without modifying rearrange func accordingly */
1120
1121/* defines for rearranging channels */
1123 {REARRANGE_ANIMCHAN_TOP, "TOP", 0, "To Top", ""},
1124 {REARRANGE_ANIMCHAN_UP, "UP", 0, "Up", ""},
1125 {REARRANGE_ANIMCHAN_DOWN, "DOWN", 0, "Down", ""},
1126 {REARRANGE_ANIMCHAN_BOTTOM, "BOTTOM", 0, "To Bottom", ""},
1127 {0, nullptr, 0, nullptr, nullptr},
1128};
1129
1130/* Reordering "Islands" Defines ----------------------------------- */
1131
1132/* Island definition - just a listbase container */
1135
1136 ListBase channels; /* channels within this region with the same state */
1137 int flag; /* eReorderIslandFlag */
1138};
1139
1140/* flags for channel reordering islands */
1142 REORDER_ISLAND_SELECTED = (1 << 0), /* island is selected */
1143 REORDER_ISLAND_UNTOUCHABLE = (1 << 1), /* island should be ignored */
1144 REORDER_ISLAND_MOVED = (1 << 2), /* island has already been moved */
1145 REORDER_ISLAND_HIDDEN = (1 << 3), /* island is not visible */
1146};
1147
1148/* Rearrange Methods --------------------------------------------- */
1149
1151{
1152 /* island must not be untouchable */
1153 if (island->flag & REORDER_ISLAND_UNTOUCHABLE) {
1154 return false;
1155 }
1156
1157 /* island should be selected to be moved */
1158 return (island->flag & REORDER_ISLAND_SELECTED) && !(island->flag & REORDER_ISLAND_MOVED);
1159}
1160
1161/* ............................. */
1162
1164{
1165 if (rearrange_island_ok(island)) {
1166 /* remove from current position */
1167 BLI_remlink(list, island);
1168
1169 /* make it first element */
1170 BLI_insertlinkbefore(list, list->first, island);
1171
1172 return true;
1173 }
1174
1175 return false;
1176}
1177
1179{
1180 if (rearrange_island_ok(island)) {
1181 /* moving up = moving before the previous island, otherwise we're in the same place */
1182 tReorderChannelIsland *prev = island->prev;
1183
1184 /* Skip hidden islands! */
1185 while (prev && prev->flag & REORDER_ISLAND_HIDDEN) {
1186 prev = prev->prev;
1187 }
1188
1189 if (prev) {
1190 /* remove from current position */
1191 BLI_remlink(list, island);
1192
1193 /* push it up */
1194 BLI_insertlinkbefore(list, prev, island);
1195
1196 return true;
1197 }
1198 }
1199
1200 return false;
1201}
1202
1204{
1205 if (rearrange_island_ok(island)) {
1206 /* moving down = moving after the next island, otherwise we're in the same place */
1207 tReorderChannelIsland *next = island->next;
1208
1209 /* Skip hidden islands! */
1210 while (next && next->flag & REORDER_ISLAND_HIDDEN) {
1211 next = next->next;
1212 }
1213
1214 if (next) {
1215 /* can only move past if next is not untouchable (i.e. nothing can go after it) */
1216 if ((next->flag & REORDER_ISLAND_UNTOUCHABLE) == 0) {
1217 /* remove from current position */
1218 BLI_remlink(list, island);
1219
1220 /* push it down */
1221 BLI_insertlinkafter(list, next, island);
1222
1223 return true;
1224 }
1225 }
1226 /* else: no next channel, so we're at the bottom already, so can't move */
1227 }
1228
1229 return false;
1230}
1231
1233{
1234 if (rearrange_island_ok(island)) {
1235 tReorderChannelIsland *last = static_cast<tReorderChannelIsland *>(list->last);
1236
1237 /* remove island from current position */
1238 BLI_remlink(list, island);
1239
1240 /* add before or after the last channel? */
1241 if ((last->flag & REORDER_ISLAND_UNTOUCHABLE) == 0) {
1242 /* can add after it */
1243 BLI_addtail(list, island);
1244 }
1245 else {
1246 /* can at most go just before it, since last cannot be moved */
1247 BLI_insertlinkbefore(list, last, island);
1248 }
1249
1250 return true;
1251 }
1252
1253 return false;
1254}
1255
1256/* ............................. */
1257
1265using AnimChanRearrangeFp = bool (*)(ListBase *list, tReorderChannelIsland *island);
1266
1267/* get rearranging function, given 'rearrange' mode */
1269{
1270 switch (mode) {
1272 return rearrange_island_top;
1274 return rearrange_island_up;
1276 return rearrange_island_down;
1279 default:
1280 return nullptr;
1281 }
1282}
1283
1284/* get rearranging function, given 'rearrange' mode (grease pencil is inverted) */
1286{
1287 switch (mode) {
1291 return rearrange_island_down;
1293 return rearrange_island_up;
1295 return rearrange_island_top;
1296 default:
1297 return nullptr;
1298 }
1299}
1300
1301/* Rearrange Islands Generics ------------------------------------- */
1302
1303/* add channel into list of islands */
1305 ListBase *srcList,
1306 Link *channel,
1307 eAnim_ChannelType type,
1308 const bool is_hidden)
1309{
1310 /* always try to add to last island if possible */
1311 tReorderChannelIsland *island = static_cast<tReorderChannelIsland *>(islands->last);
1312 bool is_sel = false, is_untouchable = false;
1313
1314 /* get flags - selected and untouchable from the channel */
1315 switch (type) {
1316 case ANIMTYPE_GROUP: {
1317 bActionGroup *agrp = reinterpret_cast<bActionGroup *>(channel);
1318
1319 is_sel = SEL_AGRP(agrp);
1320 is_untouchable = (agrp->flag & AGRP_TEMP) != 0;
1321 break;
1322 }
1323 case ANIMTYPE_FCURVE:
1324 case ANIMTYPE_NLACURVE: {
1325 FCurve *fcu = reinterpret_cast<FCurve *>(channel);
1326
1327 is_sel = SEL_FCU(fcu);
1328 break;
1329 }
1330 case ANIMTYPE_NLATRACK: {
1331 NlaTrack *nlt = reinterpret_cast<NlaTrack *>(channel);
1332
1333 is_sel = SEL_NLT(nlt);
1334 break;
1335 }
1336 case ANIMTYPE_GPLAYER: {
1337 bGPDlayer *gpl = reinterpret_cast<bGPDlayer *>(channel);
1338
1339 is_sel = SEL_GPL(gpl);
1340 break;
1341 }
1342 default:
1343 printf(
1344 "rearrange_animchannel_add_to_islands(): don't know how to handle channels of type %d\n",
1345 type);
1346 return;
1347 }
1348
1349 /* do we need to add to a new island? */
1350 if (/* 1) no islands yet */
1351 (island == nullptr) ||
1352 /* 2) unselected islands have single channels only - to allow up/down movement */
1353 ((island->flag & REORDER_ISLAND_SELECTED) == 0) ||
1354 /* 3) if channel is unselected, stop existing island
1355 * (it was either wrong sel status, or full already) */
1356 (is_sel == 0) ||
1357 /* 4) hidden status changes */
1358 (bool(island->flag & REORDER_ISLAND_HIDDEN) != is_hidden))
1359 {
1360 /* create a new island now */
1361 island = MEM_callocN<tReorderChannelIsland>("tReorderChannelIsland");
1362 BLI_addtail(islands, island);
1363
1364 if (is_sel) {
1365 island->flag |= REORDER_ISLAND_SELECTED;
1366 }
1367 if (is_untouchable) {
1369 }
1370 if (is_hidden) {
1371 island->flag |= REORDER_ISLAND_HIDDEN;
1372 }
1373 }
1374
1375 /* add channel to island - need to remove it from its existing list first though */
1376 BLI_remlink(srcList, channel);
1377 BLI_addtail(&island->channels, channel);
1378}
1379
1380/* flatten islands out into a single list again */
1382{
1383 tReorderChannelIsland *island, *isn = nullptr;
1384
1385 /* make sure srcList is empty now */
1387
1388 /* go through merging islands */
1389 for (island = static_cast<tReorderChannelIsland *>(islands->first); island; island = isn) {
1390 isn = island->next;
1391
1392 /* merge island channels back to main list, then delete the island */
1393 BLI_movelisttolist(srcList, &island->channels);
1394 BLI_freelinkN(islands, island);
1395 }
1396}
1397
1398/* ............................. */
1399
1400/* get a list of all bAnimListElem's of a certain type which are currently visible */
1402 ListBase *anim_data_visible,
1403 bAnimContext *ac,
1404 const eAnim_ChannelType type,
1405 const eAnimFilter_Flags additional_filters = eAnimFilter_Flags(0))
1406{
1407 ListBase anim_data = {nullptr, nullptr};
1409 ANIMFILTER_LIST_CHANNELS | additional_filters);
1410
1411 /* get all visible channels */
1412 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, eAnimCont_Types(ac->datatype));
1413
1414 /* now, only keep the ones that are of the types we are interested in */
1415 LISTBASE_FOREACH_MUTABLE (bAnimListElem *, ale, &anim_data) {
1416 if (ale->type != type) {
1417 BLI_freelinkN(&anim_data, ale);
1418 continue;
1419 }
1420
1421 if (type == ANIMTYPE_NLATRACK) {
1422 NlaTrack *nlt = static_cast<NlaTrack *>(ale->data);
1423
1424 if (BKE_nlatrack_is_nonlocal_in_liboverride(ale->id, nlt)) {
1425 /* No re-arrangement of non-local tracks of override data. */
1426 BLI_freelinkN(&anim_data, ale);
1427 continue;
1428 }
1429 }
1430 }
1431
1432 /* return cleaned up list */
1433 *anim_data_visible = anim_data;
1434}
1435
1436/* performing rearranging of channels using islands */
1438 AnimChanRearrangeFp rearrange_func,
1440 eAnim_ChannelType type,
1441 ListBase *anim_data_visible)
1442{
1443 ListBase islands = {nullptr, nullptr};
1444 Link *channel, *chanNext = nullptr;
1445 bool done = false;
1446
1447 /* don't waste effort on an empty list */
1448 if (BLI_listbase_is_empty(list)) {
1449 return false;
1450 }
1451
1452 /* group channels into islands */
1453 for (channel = static_cast<Link *>(list->first); channel; channel = chanNext) {
1454 /* find out whether this channel is present in anim_data_visible or not! */
1455 const bool is_hidden =
1456 (BLI_findptr(anim_data_visible, channel, offsetof(bAnimListElem, data)) == nullptr);
1457 chanNext = channel->next;
1458 rearrange_animchannel_add_to_islands(&islands, list, channel, type, is_hidden);
1459 }
1460
1461 /* Perform moving of selected islands now, but only if there is more than one of them
1462 * so that something will happen:
1463 *
1464 * - Scanning of the list is performed in the opposite direction
1465 * to the direction we're moving things,
1466 * so that we shouldn't need to encounter items we've moved already.
1467 */
1468 if (islands.first != islands.last) {
1469 tReorderChannelIsland *first = static_cast<tReorderChannelIsland *>(
1470 (mode > 0) ? islands.last : islands.first);
1471 tReorderChannelIsland *island, *isn = nullptr;
1472
1473 for (island = first; island; island = isn) {
1474 isn = (mode > 0) ? island->prev : island->next;
1475
1476 /* perform rearranging */
1477 if (rearrange_func(&islands, island)) {
1478 island->flag |= REORDER_ISLAND_MOVED;
1479 done = true;
1480 }
1481 }
1482 }
1483
1484 /* ungroup islands */
1486
1487 /* did we do anything? */
1488 return done;
1489}
1490
1491/* NLA Specific Stuff ----------------------------------------------------- */
1492
1493/* Change the order NLA Tracks within NLA Stack
1494 * ! NLA tracks are displayed in opposite order, so directions need care
1495 * mode: REARRANGE_ANIMCHAN_*
1496 */
1498{
1499 AnimChanRearrangeFp rearrange_func;
1500 ListBase anim_data_visible = {nullptr, nullptr};
1501 const bool is_liboverride = (ac->obact != nullptr) ? ID_IS_OVERRIDE_LIBRARY(ac->obact) : false;
1502
1503 /* hack: invert mode so that functions will work in right order */
1504 mode = eRearrangeAnimChan_Mode(int(mode) * -1);
1505
1506 /* get rearranging function */
1507 rearrange_func = rearrange_get_mode_func(mode);
1508 if (rearrange_func == nullptr) {
1509 return;
1510 }
1511
1512 /* In liboverride case, we need to extract non-local NLA tracks from current anim data before we
1513 * can perform the move, and add then back afterwards. It's the only way to prevent them from
1514 * being affected by the reordering.
1515 *
1516 * Note that both override apply code for NLA tracks collection, and NLA editing code, are
1517 * responsible to ensure that non-local tracks always remain first in the list. */
1518 ListBase extracted_nonlocal_nla_tracks = {nullptr, nullptr};
1519 if (is_liboverride) {
1520 NlaTrack *nla_track;
1521 for (nla_track = static_cast<NlaTrack *>(adt->nla_tracks.first); nla_track != nullptr;
1522 nla_track = nla_track->next)
1523 {
1524 if (!BKE_nlatrack_is_nonlocal_in_liboverride(&ac->obact->id, nla_track)) {
1525 break;
1526 }
1527 }
1528 if (nla_track != nullptr && nla_track->prev != nullptr) {
1529 extracted_nonlocal_nla_tracks.first = adt->nla_tracks.first;
1530 extracted_nonlocal_nla_tracks.last = nla_track->prev;
1531 adt->nla_tracks.first = nla_track;
1532 nla_track->prev->next = nullptr;
1533 nla_track->prev = nullptr;
1534 }
1535 }
1536
1537 /* Filter visible data. */
1539
1540 /* perform rearranging on tracks list */
1542 &adt->nla_tracks, rearrange_func, mode, ANIMTYPE_NLATRACK, &anim_data_visible);
1543
1544 /* Add back non-local NLA tracks at the beginning of the animation data's list. */
1545 if (!BLI_listbase_is_empty(&extracted_nonlocal_nla_tracks)) {
1546 BLI_assert(is_liboverride);
1547 static_cast<NlaTrack *>(extracted_nonlocal_nla_tracks.last)->next = static_cast<NlaTrack *>(
1548 adt->nla_tracks.first);
1549 static_cast<NlaTrack *>(adt->nla_tracks.first)->prev = static_cast<NlaTrack *>(
1550 extracted_nonlocal_nla_tracks.last);
1551 adt->nla_tracks.first = extracted_nonlocal_nla_tracks.first;
1552 }
1553
1554 /* free temp data */
1555 BLI_freelistN(&anim_data_visible);
1556}
1557
1558/* Drivers Specific Stuff ------------------------------------------------- */
1559
1560/* Change the order drivers within AnimData block
1561 * mode: REARRANGE_ANIMCHAN_*
1562 */
1564 AnimData *adt,
1566{
1567 /* get rearranging function */
1568 AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
1569 ListBase anim_data_visible = {nullptr, nullptr};
1570
1571 if (rearrange_func == nullptr) {
1572 return;
1573 }
1574
1575 /* only consider drivers if they're accessible */
1576 if (EXPANDED_DRVD(adt) == 0) {
1577 return;
1578 }
1579
1580 /* Filter visible data. */
1582
1583 /* perform rearranging on drivers list (drivers are really just F-Curves) */
1585 &adt->drivers, rearrange_func, mode, ANIMTYPE_FCURVE, &anim_data_visible);
1586
1587 /* free temp data */
1588 BLI_freelistN(&anim_data_visible);
1589}
1590
1591/* Action Specific Stuff ------------------------------------------------- */
1592
1593/* make sure all action-channels belong to a group (and clear action's list) */
1595{
1596 FCurve *fcu;
1597
1598 if (act == nullptr) {
1599 return;
1600 }
1601
1602 BLI_assert(act->wrap().is_action_legacy());
1603
1604 /* Separate F-Curves into lists per group */
1605 LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) {
1606 FCurve *const group_fcurves_first = static_cast<FCurve *>(agrp->channels.first);
1607 FCurve *const group_fcurves_last = static_cast<FCurve *>(agrp->channels.last);
1608 if (group_fcurves_first == nullptr) {
1609 /* Empty group. */
1610 continue;
1611 }
1612
1613 if (group_fcurves_first == act->curves.first) {
1614 /* First of the action curves, update the start of the action curves. */
1615 BLI_assert(group_fcurves_first->prev == nullptr);
1616 act->curves.first = group_fcurves_last->next;
1617 }
1618 else {
1619 group_fcurves_first->prev->next = group_fcurves_last->next;
1620 }
1621
1622 if (group_fcurves_last == act->curves.last) {
1623 /* Last of the action curves, update the end of the action curves. */
1624 BLI_assert(group_fcurves_last->next == nullptr);
1625 act->curves.last = group_fcurves_first->prev;
1626 }
1627 else {
1628 group_fcurves_last->next->prev = group_fcurves_first->prev;
1629 }
1630
1631 /* Clear links pointing outside the per-group list. */
1632 group_fcurves_first->prev = group_fcurves_last->next = nullptr;
1633 }
1634
1635 /* Initialize memory for temp-group */
1636 *tgrp = bActionGroup{};
1637 tgrp->cs = ThemeWireColor{};
1639 STRNCPY_UTF8(tgrp->name, "#TempGroup");
1640
1641 /* Move any action-channels not already moved, to the temp group */
1642 if (act->curves.first) {
1643 /* start of list */
1644 fcu = static_cast<FCurve *>(act->curves.first);
1645 fcu->prev = nullptr;
1646 tgrp->channels.first = fcu;
1647 act->curves.first = nullptr;
1648
1649 /* end of list */
1650 fcu = static_cast<FCurve *>(act->curves.last);
1651 fcu->next = nullptr;
1652 tgrp->channels.last = fcu;
1653 act->curves.last = nullptr;
1654
1655 /* ensure that all of these get their group set to this temp group
1656 * (so that visibility filtering works)
1657 */
1658 LISTBASE_FOREACH (FCurve *, fcu, &tgrp->channels) {
1659 fcu->grp = tgrp;
1660 }
1661 }
1662
1663 /* Add temp-group to list */
1664 BLI_addtail(&act->groups, tgrp);
1665}
1666
1667/* link lists of channels that groups have */
1669{
1670 LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) {
1671 /* add list of channels to action's channels */
1672 const ListBase group_channels = agrp->channels;
1673 BLI_movelisttolist(&act->curves, &agrp->channels);
1674 agrp->channels = group_channels;
1675
1676 /* clear moved flag */
1677 agrp->flag &= ~AGRP_MOVED;
1678
1679 /* if group was temporary one:
1680 * - unassign all FCurves which were temporarily added to it
1681 * - remove from list (but don't free as it's on the stack!)
1682 */
1683 if (agrp->flag & AGRP_TEMP) {
1684 LISTBASE_FOREACH (FCurve *, fcu, &agrp->channels) {
1685 fcu->grp = nullptr;
1686 }
1687
1688 BLI_remlink(&act->groups, agrp);
1689 break;
1690 }
1691 }
1692}
1693
1700{
1701 /* TODO: the general structure of this function is basically the same as
1702 * `rearrange_layered_action_channel_groups()` and
1703 * `rearrange_layered_action_fcurves()`. It would be nice to DRY them at some
1704 * point if we can. */
1705
1706 ListBase anim_data_selected_visible = {nullptr, nullptr};
1708 &anim_data_selected_visible, ac, ANIMTYPE_ACTION_SLOT, ANIMFILTER_SEL);
1709
1710 int total_moved = 0;
1711
1712 switch (mode) {
1713 case REARRANGE_ANIMCHAN_UP: {
1714 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data_selected_visible) {
1715 BLI_assert(ale->type == ANIMTYPE_ACTION_SLOT);
1716 blender::animrig::Slot &slot = static_cast<ActionSlot *>(ale->data)->wrap();
1717 blender::animrig::Action &action =
1718 reinterpret_cast<bAction *>(ale->fcurve_owner_id)->wrap();
1719
1720 const int current_index = action.slots().first_index_try(&slot);
1721 const int to_index = current_index - 1;
1722 BLI_assert(current_index >= 0);
1723
1724 /* We skip moving when the destination is also selected because that
1725 * would swap two selected slots rather than moving them all in the
1726 * same direction. This happens when multiple selected slots are
1727 * already packed together at the top. */
1728 if (to_index < 0 || action.slot(to_index)->is_selected()) {
1729 continue;
1730 }
1731
1732 action.slot_move_to_index(slot, to_index);
1733 total_moved++;
1734 }
1735 break;
1736 }
1737
1739 LISTBASE_FOREACH_BACKWARD (bAnimListElem *, ale, &anim_data_selected_visible) {
1740 BLI_assert(ale->type == ANIMTYPE_ACTION_SLOT);
1741 blender::animrig::Slot &slot = static_cast<ActionSlot *>(ale->data)->wrap();
1742 blender::animrig::Action &action =
1743 reinterpret_cast<bAction *>(ale->fcurve_owner_id)->wrap();
1744
1745 const int current_index = action.slots().first_index_try(&slot);
1746 const int to_index = 0;
1747 if (current_index != to_index) {
1748 action.slot_move_to_index(slot, to_index);
1749 total_moved++;
1750 }
1751 }
1752 break;
1753 }
1754
1756 LISTBASE_FOREACH_BACKWARD (bAnimListElem *, ale, &anim_data_selected_visible) {
1757 BLI_assert(ale->type == ANIMTYPE_ACTION_SLOT);
1758 blender::animrig::Slot &slot = static_cast<ActionSlot *>(ale->data)->wrap();
1759 blender::animrig::Action &action =
1760 reinterpret_cast<bAction *>(ale->fcurve_owner_id)->wrap();
1761
1762 const int current_index = action.slots().first_index_try(&slot);
1763 const int to_index = current_index + 1;
1764 BLI_assert(current_index >= 0);
1765
1766 /* We skip moving when the destination is also selected because that
1767 * would swap two selected slots rather than moving them all in the
1768 * same direction. This happens when multiple selected slots are
1769 * already packed together at the bottom. */
1770 if (to_index >= action.slots().size() || action.slot(to_index)->is_selected()) {
1771 continue;
1772 }
1773
1774 action.slot_move_to_index(slot, to_index);
1775 total_moved++;
1776 }
1777 break;
1778 }
1779
1781 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data_selected_visible) {
1782 BLI_assert(ale->type == ANIMTYPE_ACTION_SLOT);
1783 blender::animrig::Slot &slot = static_cast<ActionSlot *>(ale->data)->wrap();
1784 blender::animrig::Action &action =
1785 reinterpret_cast<bAction *>(ale->fcurve_owner_id)->wrap();
1786
1787 const int current_index = action.slots().first_index_try(&slot);
1788 const int to_index = action.slots().size() - 1;
1789 if (current_index != to_index) {
1790 action.slot_move_to_index(slot, to_index);
1791 total_moved++;
1792 }
1793 }
1794 break;
1795 }
1796 }
1797
1798 BLI_freelistN(&anim_data_selected_visible);
1799
1800 return total_moved > 0;
1801}
1802
1823 const eRearrangeAnimChan_Mode mode)
1824{
1825 ListBase anim_data_visible = {nullptr, nullptr};
1826
1827 /* We don't use `ANIMFILTER_SEL` here, and instead individually check on each
1828 * element whether it's selected or not in the code further below. This is
1829 * because it's what the legacy code does (see for example
1830 * `rearrange_animchannel_add_to_islands()`), and we're avoiding diverging
1831 * unnecessarily from that in case there was a reason for it. */
1833
1834 switch (mode) {
1835 case REARRANGE_ANIMCHAN_UP: {
1836 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data_visible) {
1837 if (ale->adt && &ale->adt->action->wrap() != &action) {
1838 continue;
1839 }
1840 BLI_assert(ale->type == ANIMTYPE_GROUP);
1841 bActionGroup *group = static_cast<bActionGroup *>(ale->data);
1842 if (!SEL_AGRP(group)) {
1843 continue;
1844 }
1845 blender::animrig::Channelbag &bag = group->channelbag->wrap();
1846 const int group_index = bag.channel_groups().first_index_try(group);
1847 const int to_index = group_index - 1;
1848 BLI_assert(group_index >= 0);
1849
1850 /* We skip moving when the destination is also selected because that
1851 * would swap two selected groups rather than moving them all in the
1852 * same direction. This happens when multiple selected groups are
1853 * already packed together at the top. */
1854 if (to_index < 0 || SEL_AGRP(bag.channel_group(to_index))) {
1855 continue;
1856 }
1857
1858 bag.channel_group_move_to_index(*group, to_index);
1859 }
1860 break;
1861 }
1862
1864 LISTBASE_FOREACH_BACKWARD (bAnimListElem *, ale, &anim_data_visible) {
1865 if (ale->adt && &ale->adt->action->wrap() != &action) {
1866 continue;
1867 }
1868 BLI_assert(ale->type == ANIMTYPE_GROUP);
1869 bActionGroup *group = static_cast<bActionGroup *>(ale->data);
1870 if (!SEL_AGRP(group)) {
1871 continue;
1872 }
1873 blender::animrig::Channelbag &bag = group->channelbag->wrap();
1874 bag.channel_group_move_to_index(*group, 0);
1875 }
1876 break;
1877 }
1878
1880 LISTBASE_FOREACH_BACKWARD (bAnimListElem *, ale, &anim_data_visible) {
1881 if (ale->adt && &ale->adt->action->wrap() != &action) {
1882 continue;
1883 }
1884 BLI_assert(ale->type == ANIMTYPE_GROUP);
1885 bActionGroup *group = static_cast<bActionGroup *>(ale->data);
1886 if (!SEL_AGRP(group)) {
1887 continue;
1888 }
1889 blender::animrig::Channelbag &bag = group->channelbag->wrap();
1890 const int group_index = bag.channel_groups().first_index_try(group);
1891 const int to_index = group_index + 1;
1892 BLI_assert(group_index >= 0);
1893
1894 /* We skip moving when the destination is also selected because that
1895 * would swap two selected groups rather than moving them all in the
1896 * same direction. This happens when multiple selected groups are
1897 * already packed together at the bottom. */
1898 if (to_index >= bag.channel_groups().size() || SEL_AGRP(bag.channel_group(to_index))) {
1899 continue;
1900 }
1901
1902 bag.channel_group_move_to_index(*group, to_index);
1903 }
1904 break;
1905 }
1906
1908 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data_visible) {
1909 if (ale->adt && &ale->adt->action->wrap() != &action) {
1910 continue;
1911 }
1912 BLI_assert(ale->type == ANIMTYPE_GROUP);
1913 bActionGroup *group = static_cast<bActionGroup *>(ale->data);
1914 if (!SEL_AGRP(group)) {
1915 continue;
1916 }
1917 blender::animrig::Channelbag &bag = group->channelbag->wrap();
1918 bag.channel_group_move_to_index(*group, bag.channel_groups().size() - 1);
1919 }
1920 break;
1921 }
1922 }
1923
1924 BLI_freelistN(&anim_data_visible);
1925}
1926
1946 const eRearrangeAnimChan_Mode mode)
1947{
1948 ListBase anim_data_visible = {nullptr, nullptr};
1949
1950 /* We don't use `ANIMFILTER_SEL` here, and instead individually check on each
1951 * element whether it's selected or not in the code further below. This is
1952 * because it's what the legacy code does (see for example
1953 * `rearrange_animchannel_add_to_islands()`), and we're avoiding diverging
1954 * unnecessarily from that in case there was a reason for it. */
1956
1957 /* Lambda to either fetch an fcurve's group if it has one, or otherwise
1958 * construct a fake one representing the ungrouped range at the end of the
1959 * fcurve array. This lets the code further below be much less of a special-case,
1960 * in exchange for a little data copying.
1961 *
1962 * NOTE: this returns a *copy* of the group, rather a pointer or reference, to
1963 * make it possible to return a fake group when needed. */
1964 auto get_group_or_make_fake = [&action](bAnimListElem *fcurve_ale) -> bActionGroup {
1965 FCurve *fcurve = static_cast<FCurve *>(fcurve_ale->data);
1966 if (fcurve->grp) {
1967 return *fcurve->grp;
1968 }
1969
1971 fcurve_ale->slot_handle);
1972 BLI_assert(bag != nullptr);
1973
1974 bActionGroup group = {};
1975 group.channelbag = bag;
1976 group.fcurve_range_start = 0;
1977 if (!bag->channel_groups().is_empty()) {
1978 bActionGroup *last_group = bag->channel_groups().last();
1979 group.fcurve_range_start = last_group->fcurve_range_start + last_group->fcurve_range_length;
1980 }
1981 group.fcurve_range_length = bag->fcurves().size() - group.fcurve_range_start;
1982
1983 return group;
1984 };
1985
1986 /* Lambda to determine whether an fcurve should be skipped, given both the
1987 * fcurve and the group it belongs to. */
1988 auto should_skip = [](FCurve &fcurve, bActionGroup &group) {
1989 /* If the curve itself isn't selected, then it shouldn't be operated on. If
1990 * its group is selected then the group was moved so we don't move the
1991 * fcurve individually. */
1992 return !SEL_FCU(&fcurve) || SEL_AGRP(&group);
1993 };
1994
1995 switch (mode) {
1996 case REARRANGE_ANIMCHAN_UP: {
1997 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data_visible) {
1998 if (ale->adt && &ale->adt->action->wrap() != &action) {
1999 continue;
2000 }
2001 BLI_assert(ale->type == ANIMTYPE_FCURVE);
2002 FCurve *fcurve = static_cast<FCurve *>(ale->data);
2003 bActionGroup group = get_group_or_make_fake(ale);
2004
2005 if (should_skip(*fcurve, group)) {
2006 continue;
2007 }
2008
2009 blender::animrig::Channelbag &bag = group.channelbag->wrap();
2010 const int fcurve_index = bag.fcurves().first_index_try(fcurve);
2011 const int to_index = fcurve_index - 1;
2012
2013 /* We skip moving when the destination is also selected because that
2014 * would swap two selected fcurves rather than moving them all in the
2015 * same direction. This happens when multiple selected fcurves are
2016 * already packed together at the top. */
2017 if (to_index < group.fcurve_range_start || SEL_FCU(bag.fcurve(to_index))) {
2018 continue;
2019 }
2020
2021 bag.fcurve_move_to_index(*fcurve, to_index);
2022 }
2023 break;
2024 }
2025
2027 LISTBASE_FOREACH_BACKWARD (bAnimListElem *, ale, &anim_data_visible) {
2028 if (ale->adt && &ale->adt->action->wrap() != &action) {
2029 continue;
2030 }
2031 BLI_assert(ale->type == ANIMTYPE_FCURVE);
2032 FCurve *fcurve = static_cast<FCurve *>(ale->data);
2033 bActionGroup group = get_group_or_make_fake(ale);
2034
2035 if (should_skip(*fcurve, group)) {
2036 continue;
2037 }
2038
2039 blender::animrig::Channelbag &bag = group.channelbag->wrap();
2040 bag.fcurve_move_to_index(*fcurve, group.fcurve_range_start);
2041 }
2042 break;
2043 }
2044
2046 LISTBASE_FOREACH_BACKWARD (bAnimListElem *, ale, &anim_data_visible) {
2047 if (ale->adt && &ale->adt->action->wrap() != &action) {
2048 continue;
2049 }
2050 BLI_assert(ale->type == ANIMTYPE_FCURVE);
2051 FCurve *fcurve = static_cast<FCurve *>(ale->data);
2052 bActionGroup group = get_group_or_make_fake(ale);
2053
2054 if (should_skip(*fcurve, group)) {
2055 continue;
2056 }
2057
2058 blender::animrig::Channelbag &bag = group.channelbag->wrap();
2059 const int fcurve_index = bag.fcurves().first_index_try(fcurve);
2060 const int to_index = fcurve_index + 1;
2061
2062 /* We skip moving when the destination is also selected because that
2063 * would swap two selected fcurves rather than moving them all in the
2064 * same direction. This happens when multiple selected fcurves are
2065 * already packed together at the bottom. */
2066 if (to_index >= group.fcurve_range_start + group.fcurve_range_length ||
2067 SEL_FCU(bag.fcurve(to_index)))
2068 {
2069 continue;
2070 }
2071
2072 bag.fcurve_move_to_index(*fcurve, to_index);
2073 }
2074 break;
2075 }
2076
2078 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data_visible) {
2079 if (ale->adt && &ale->adt->action->wrap() != &action) {
2080 continue;
2081 }
2082 BLI_assert(ale->type == ANIMTYPE_FCURVE);
2083 FCurve *fcurve = static_cast<FCurve *>(ale->data);
2084 bActionGroup group = get_group_or_make_fake(ale);
2085
2086 if (should_skip(*fcurve, group)) {
2087 continue;
2088 }
2089
2090 blender::animrig::Channelbag &bag = group.channelbag->wrap();
2091 bag.fcurve_move_to_index(*fcurve,
2092 group.fcurve_range_start + group.fcurve_range_length - 1);
2093 }
2094 break;
2095 }
2096 }
2097 BLI_freelistN(&anim_data_visible);
2098}
2099
2100/* Change the order of anim-channels within action
2101 * mode: REARRANGE_ANIMCHAN_*
2102 */
2104{
2105 BLI_assert(act != nullptr);
2106
2107 /* Layered actions. */
2109 if (rearrange_layered_action_slots(ac, mode)) {
2110 /* Only rearrange other channels if no slot rearranging happened. */
2111 return;
2112 }
2113 rearrange_layered_action_channel_groups(ac, act->wrap(), mode);
2114 rearrange_layered_action_fcurves(ac, act->wrap(), mode);
2115 return;
2116 }
2117
2118 /* Legacy actions. */
2119 bActionGroup tgrp;
2120 ListBase anim_data_visible = {nullptr, nullptr};
2121 bool do_channels;
2122
2123 /* get rearranging function */
2124 AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
2125
2126 if (rearrange_func == nullptr) {
2127 return;
2128 }
2129
2130 /* make sure we're only operating with groups (vs a mixture of groups+curves) */
2131 split_groups_action_temp(act, &tgrp);
2132
2133 /* Filter visible data. */
2135
2136 /* Rearrange groups first:
2137 * - The group's channels will only get considered
2138 * if nothing happened when rearranging the groups
2139 * i.e. the rearrange function returned 0.
2140 */
2141 do_channels = (rearrange_animchannel_islands(
2142 &act->groups, rearrange_func, mode, ANIMTYPE_GROUP, &anim_data_visible) == 0);
2143
2144 /* free temp data */
2145 BLI_freelistN(&anim_data_visible);
2146
2147 if (do_channels) {
2148 /* Filter visible data. */
2150
2151 LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) {
2152 /* only consider F-Curves if they're visible (group expanded) */
2153 if (EXPANDED_AGRP(ac, agrp)) {
2155 &agrp->channels, rearrange_func, mode, ANIMTYPE_FCURVE, &anim_data_visible);
2156 }
2157 }
2158
2159 /* free temp data */
2160 BLI_freelistN(&anim_data_visible);
2161 }
2162
2163 /* assemble lists into one list (and clear moved tags) */
2165}
2166
2167/* ------------------- */
2168
2170 AnimData *adt,
2172{
2173 ListBase anim_data_visible = {nullptr, nullptr};
2174
2175 /* get rearranging function */
2176 AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode);
2177
2178 if (rearrange_func == nullptr) {
2179 return;
2180 }
2181
2182 /* skip if these curves aren't being shown */
2183 if (adt->flag & ADT_NLA_SKEYS_COLLAPSED) {
2184 return;
2185 }
2186
2187 /* Filter visible data. */
2189
2190 /* we cannot rearrange between strips, but within each strip, we can rearrange those curves */
2191 LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
2192 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
2194 &strip->fcurves, rearrange_func, mode, ANIMTYPE_NLACURVE, &anim_data_visible);
2195 }
2196 }
2197
2198 /* free temp data */
2199 BLI_freelistN(&anim_data_visible);
2200}
2201
2202/* ------------------- */
2203
2205{
2206 using namespace blender::bke::greasepencil;
2207 ListBase anim_data = {nullptr, nullptr};
2208 blender::Vector<Layer *> layer_list;
2210
2212 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
2213
2214 if (mode == REARRANGE_ANIMCHAN_TOP) {
2215 LISTBASE_FOREACH_BACKWARD (bAnimListElem *, ale, &anim_data) {
2216 GreasePencil &grease_pencil = *reinterpret_cast<GreasePencil *>(ale->id);
2217 Layer *layer = static_cast<Layer *>(ale->data);
2218 if (layer->is_selected()) {
2219 grease_pencil.move_node_top(layer->as_node());
2220 }
2221 }
2222 }
2223 else {
2224 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2225 GreasePencil &grease_pencil = *reinterpret_cast<GreasePencil *>(ale->id);
2226 Layer *layer = static_cast<Layer *>(ale->data);
2227
2228 switch (mode) {
2229 case REARRANGE_ANIMCHAN_UP: {
2230 if (layer->is_selected()) {
2231 grease_pencil.move_node_up(layer->as_node());
2232 }
2233 break;
2234 }
2236 if (layer->is_selected()) {
2237 grease_pencil.move_node_down(layer->as_node());
2238 }
2239 break;
2240 }
2242 if (layer->is_selected()) {
2243 grease_pencil.move_node_bottom(layer->as_node());
2244 }
2245 break;
2246 }
2248 /* Handled separately before the switch case. */
2249 break;
2250 }
2251 }
2252 }
2253
2254 BLI_freelistN(&anim_data);
2255}
2256
2258{
2259 ListBase anim_data = {nullptr, nullptr};
2260 int filter;
2261
2262 /* get rearranging function */
2264
2265 if (rearrange_func == nullptr) {
2266 return;
2267 }
2268
2269 /* get Grease Pencil datablocks */
2273 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
2274
2275 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2276 /* only consider grease pencil container channels */
2277 if (ale->type != ANIMTYPE_DSGPENCIL) {
2278 continue;
2279 }
2280
2281 ListBase anim_data_visible = {nullptr, nullptr};
2282 bGPdata *gpd = static_cast<bGPdata *>(ale->data);
2283
2284 /* only consider layers if this datablock is open */
2285 if ((gpd->flag & GP_DATA_EXPAND) == 0) {
2286 continue;
2287 }
2288
2289 /* Filter visible data. */
2291
2292 /* Rearrange data-block's layers. */
2294 &gpd->layers, rearrange_func, mode, ANIMTYPE_GPLAYER, &anim_data_visible);
2295
2296 /* free visible layers data */
2297 BLI_freelistN(&anim_data_visible);
2298
2299 /* Tag to recalc geometry */
2301 }
2302
2303 /* free GPD channel data */
2304 ANIM_animdata_freelist(&anim_data);
2305
2307}
2308
2309/* ------------------- */
2310
2312{
2313 bAnimContext ac;
2315
2316 /* get editor data */
2317 if (ANIM_animdata_get_context(C, &ac) == 0) {
2318 return OPERATOR_CANCELLED;
2319 }
2320
2321 /* get mode */
2322 mode = eRearrangeAnimChan_Mode(RNA_enum_get(op->ptr, "direction"));
2323
2324 /* method to move channels depends on the editor */
2325 if (ac.datatype == ANIMCONT_GPENCIL) {
2326 /* Grease Pencil channels */
2328 }
2329 else if (ac.datatype == ANIMCONT_MASK) {
2330 /* Grease Pencil channels */
2331 printf("Mask does not supported for moving yet\n");
2332 }
2333 else if (ac.datatype == ANIMCONT_ACTION) {
2334 /* Directly rearrange action's channels */
2335 rearrange_action_channels(&ac, static_cast<bAction *>(ac.data), mode);
2336 }
2337 else {
2338 ListBase anim_data = {nullptr, nullptr};
2339 int filter;
2340
2342 rearrange_gpencil_channels(&ac, mode);
2343 }
2344
2345 /* get animdata blocks */
2349 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
2350
2351 /* Rearranging an Action should only happen once, as that inspects all the
2352 * selected & visible channels of that Action anyway. */
2353 blender::Set<bAction *> visited_actions;
2354
2355 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2356 AnimData *adt = static_cast<AnimData *>(ale->data);
2357
2358 switch (ac.datatype) {
2359 case ANIMCONT_NLA: /* NLA-tracks only */
2360 rearrange_nla_tracks(&ac, adt, mode);
2362 break;
2363
2364 case ANIMCONT_DRIVERS: /* Drivers list only */
2365 rearrange_driver_channels(&ac, adt, mode);
2366 break;
2367
2368 case ANIMCONT_ACTION: /* Single Action only... */
2369 case ANIMCONT_SHAPEKEY: /* DOUBLE CHECK ME... */
2370 {
2371 if (adt->action) {
2372 if (visited_actions.add(adt->action)) {
2373 rearrange_action_channels(&ac, adt->action, mode);
2374 }
2375 }
2376 else if (G.debug & G_DEBUG) {
2377 printf("Animdata has no action\n");
2378 }
2379 break;
2380 }
2381
2382 default: /* DopeSheet/Graph Editor - Some Actions + NLA Control Curves */
2383 {
2384 /* NLA Control Curves */
2385 if (adt->nla_tracks.first) {
2386 rearrange_nla_control_channels(&ac, adt, mode);
2387 }
2388
2389 /* Action */
2390 if (adt->action) {
2391 if (visited_actions.add(adt->action)) {
2392 rearrange_action_channels(&ac, adt->action, mode);
2393 }
2394 }
2395 else if (G.debug & G_DEBUG) {
2396 printf("Animdata has no action\n");
2397 }
2398 break;
2399 }
2400 }
2401 }
2402
2403 /* free temp data */
2404 ANIM_animdata_freelist(&anim_data);
2405 }
2406
2407 /* send notifier that things have changed */
2410
2411 return OPERATOR_FINISHED;
2412}
2413
2415{
2416 /* identifiers */
2417 ot->name = "Move Channels";
2418 ot->idname = "ANIM_OT_channels_move";
2419 ot->description = "Rearrange selected animation channels";
2420
2421 /* API callbacks. */
2424
2425 /* flags */
2426 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2427
2428 /* props */
2429 ot->prop = RNA_def_enum(ot->srna,
2430 "direction",
2433 "Direction",
2434 "");
2435}
2436
2438
2439/* -------------------------------------------------------------------- */
2442
2444{
2445 ScrArea *area = CTX_wm_area(C);
2446 SpaceLink *sl;
2447
2448 /* channels region test */
2449 /* TODO: could enhance with actually testing if channels region? */
2450 if (ELEM(nullptr, area, CTX_wm_region(C))) {
2451 return false;
2452 }
2453
2454 /* animation editor test - must be suitable modes only */
2455 sl = CTX_wm_space_data(C);
2456
2457 switch (area->spacetype) {
2458 /* supported... */
2459 case SPACE_ACTION: {
2460 SpaceAction *saction = reinterpret_cast<SpaceAction *>(sl);
2461
2462 /* Dope-sheet and action only - all others are for other data-types or have no groups. */
2463 if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_DOPESHEET) == 0) {
2464 return false;
2465 }
2466
2467 break;
2468 }
2469 case SPACE_GRAPH: {
2470 SpaceGraph *sipo = reinterpret_cast<SpaceGraph *>(sl);
2471
2472 /* drivers can't have groups... */
2473 if (sipo->mode != SIPO_MODE_ANIMATION) {
2474 return false;
2475 }
2476
2477 break;
2478 }
2479 /* unsupported... */
2480 default:
2481 return false;
2482 }
2483
2484 return true;
2485}
2486
2487/* ----------------------------------------------------------- */
2488
2490 bAnimListElem *adt_ref,
2491 const char name[])
2492{
2493 AnimData *adt = adt_ref->adt;
2494 bAction *act = adt->action;
2495
2496 if (act == nullptr) {
2497 return;
2498 }
2499
2500 /* Get list of selected F-Curves to re-group. */
2501 ListBase anim_data = {nullptr, nullptr};
2504 ANIM_animdata_filter(ac, &anim_data, filter, adt_ref, ANIMCONT_CHANNEL);
2505
2506 if (anim_data.first == nullptr) {
2507 return;
2508 }
2509
2510 /* Legacy actions. */
2512 bActionGroup *agrp;
2513
2514 /* create new group, which should now be part of the action */
2515 agrp = action_groups_add_new(act, name);
2516 BLI_assert(agrp != nullptr);
2517
2518 /* Transfer selected F-Curves across to new group. */
2519 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2520 FCurve *fcu = static_cast<FCurve *>(ale->data);
2521 bActionGroup *grp = fcu->grp;
2522
2523 /* remove F-Curve from group, then group too if it is now empty */
2525
2526 if ((grp) && BLI_listbase_is_empty(&grp->channels)) {
2527 BLI_freelinkN(&act->groups, grp);
2528 }
2529
2530 /* add F-Curve to group */
2531 action_groups_add_channel(act, agrp, fcu);
2532 }
2533
2534 /* cleanup */
2535 ANIM_animdata_freelist(&anim_data);
2536
2537 return;
2538 }
2539
2540 /* Layered action.
2541 *
2542 * The anim-list doesn't explicitly group the channels by channel bag, so we
2543 * have to get a little clever here. We take advantage of the fact that the
2544 * fcurves are at least listed in order, and so all fcurves in the same
2545 * channel bag will be next to each other. So we keep track of the channel bag
2546 * from the last fcurve, and check it against the current fcurve to see if
2547 * we've progressed into a new channel bag, and then we create the new group
2548 * for that channel bag.
2549 *
2550 * It's a little messy, and also has quadratic performance due to handling
2551 * each fcurve individually (each of which is an O(N) operation), but it's
2552 * also the simplest thing we can do given the data we have. In the future we
2553 * can do something smarter, particularly if it becomes a performance issue. */
2554 blender::animrig::Channelbag *last_channelbag = nullptr;
2555 bActionGroup *group = nullptr;
2556 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2557 FCurve *fcu = static_cast<FCurve *>(ale->data);
2559 ale->slot_handle);
2560
2561 if (channelbag != last_channelbag) {
2562 last_channelbag = channelbag;
2563 group = &channelbag->channel_group_create(name);
2564 }
2565
2566 channelbag->fcurve_assign_to_channel_group(*fcu, *group);
2567 }
2568
2569 /* Cleanup. */
2570 ANIM_animdata_freelist(&anim_data);
2571}
2572
2574{
2575 bAnimContext ac;
2576 char name[MAX_NAME];
2577
2578 /* get editor data */
2579 if (ANIM_animdata_get_context(C, &ac) == 0) {
2580 return OPERATOR_CANCELLED;
2581 }
2582
2583 /* get name for new group */
2584 RNA_string_get(op->ptr, "name", name);
2585
2586 /* XXX: name for group should never be empty... */
2587 if (name[0]) {
2588 ListBase anim_data = {nullptr, nullptr};
2589 int filter;
2590
2591 /* Handle each animdata block separately, so that the regrouping doesn't flow into blocks. */
2595 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
2596
2597 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2599 }
2600
2601 /* free temp data */
2602 ANIM_animdata_freelist(&anim_data);
2603
2604 /* Updates. */
2606 }
2607
2608 return OPERATOR_FINISHED;
2609}
2610
2612{
2613 /* identifiers */
2614 ot->name = "Group Channels";
2615 ot->idname = "ANIM_OT_channels_group";
2616 ot->description = "Add selected F-Curves to a new group";
2617
2618 /* callbacks */
2619 ot->invoke = WM_operator_props_popup;
2622
2623 /* flags */
2624 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2625
2626 /* props */
2627 ot->prop = RNA_def_string(ot->srna,
2628 "name",
2629 "New Group",
2630 sizeof(bActionGroup::name),
2631 "Name",
2632 "Name of newly created group");
2633 /* XXX: still not too sure about this - keeping same text is confusing... */
2634 // RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
2635}
2636
2638
2639/* -------------------------------------------------------------------- */
2642
2644{
2645 bAnimContext ac;
2646
2647 ListBase anim_data = {nullptr, nullptr};
2648 int filter;
2649
2650 /* get editor data */
2651 if (ANIM_animdata_get_context(C, &ac) == 0) {
2652 return OPERATOR_CANCELLED;
2653 }
2654
2655 /* just selected F-Curves... */
2659 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
2660
2661 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2662
2663 FCurve *fcu = static_cast<FCurve *>(ale->data);
2664
2665 /* Already ungrouped, so skip. */
2666 if (fcu->grp == nullptr) {
2667 continue;
2668 }
2669
2670 /* find action for this F-Curve... */
2671 if (!ale->adt || !ale->adt->action) {
2672 continue;
2673 }
2674 bAction *act = ale->adt->action;
2675
2676 /* Legacy actions. */
2678 bActionGroup *agrp = fcu->grp;
2679
2680 /* remove F-Curve from group and add at tail (ungrouped) */
2682 BLI_addtail(&act->curves, fcu);
2683
2684 /* delete group if it is now empty */
2685 if (BLI_listbase_is_empty(&agrp->channels)) {
2686 BLI_freelinkN(&act->groups, agrp);
2687 }
2688 continue;
2689 }
2690
2691 /* Layered action. */
2692 fcu->grp->channelbag->wrap().fcurve_ungroup(*fcu);
2693 }
2694
2695 /* cleanup */
2696 ANIM_animdata_freelist(&anim_data);
2697
2698 /* updates */
2700
2701 return OPERATOR_FINISHED;
2702}
2703
2705{
2706 /* identifiers */
2707 ot->name = "Ungroup Channels";
2708 ot->idname = "ANIM_OT_channels_ungroup";
2709 ot->description = "Remove selected F-Curves from their current groups";
2710
2711 /* callbacks */
2714
2715 /* flags */
2716 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2717}
2718
2720
2721/* -------------------------------------------------------------------- */
2724
2726{
2727 ID *id = ale->id;
2728 AnimData *adt = BKE_animdata_from_id(id);
2729 /* TODO(sergey): Technically, if the animation element is being deleted
2730 * from a driver we don't have to tag action. This is something we can check
2731 * for in the future. For now just do most reliable tag which was always happening. */
2732 if (adt != nullptr) {
2734 if (adt->action != nullptr) {
2736 }
2737 }
2738 /* Deals with NLA and drivers.
2739 * Doesn't cause overhead for action updates, since object will receive
2740 * animation update after dependency graph flushes update from action to
2741 * all its users. */
2743}
2744
2755{
2759 ListBase anim_data = {nullptr, nullptr};
2760 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2761
2762 bool must_skip_groups = false;
2763 bool has_skipped_group = false;
2764
2765 /* Delete selected container-like channels and their underlying data. */
2766 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2767 switch (ale->type) {
2768 case ANIMTYPE_ACTION_SLOT: {
2769 BLI_assert(ale->fcurve_owner_id);
2770 BLI_assert(ale->data);
2771 BLI_assert_msg(GS(ale->fcurve_owner_id->name) == ID_AC,
2772 "fcurve_owner_id should be an Action");
2773
2774 blender::animrig::Action &action =
2775 reinterpret_cast<bAction *>(ale->fcurve_owner_id)->wrap();
2776 blender::animrig::Slot &slot_to_remove = static_cast<ActionSlot *>(ale->data)->wrap();
2777
2778 action.slot_remove(slot_to_remove);
2779
2781
2782 /* Subsequent groups should be skipped, and their deletion kept for
2783 * another run (if they even exist after this slot was deleted). */
2784 must_skip_groups = true;
2785 break;
2786 }
2787
2788 case ANIMTYPE_GROUP: {
2789 if (must_skip_groups) {
2790 /* Another run of this function is needed to see if this group still
2791 * exists, and thus still needs deleting. */
2792 has_skipped_group = true;
2793 break;
2794 }
2795
2796 bActionGroup *agrp = static_cast<bActionGroup *>(ale->data);
2797 AnimData *adt = ale->adt;
2798 FCurve *fcu, *fcn;
2799
2800 /* Groups should always be part of an action. */
2801 if (adt == nullptr || adt->action == nullptr) {
2803 continue;
2804 }
2805
2806 blender::animrig::Action &action = adt->action->wrap();
2807
2808 /* Legacy actions */
2809 if (!action.is_action_layered()) {
2810 /* delete all of the Group's F-Curves, but no others */
2811 for (fcu = static_cast<FCurve *>(agrp->channels.first); fcu && fcu->grp == agrp;
2812 fcu = fcn)
2813 {
2814 fcn = fcu->next;
2815
2816 /* remove from group and action, then free */
2818 BKE_fcurve_free(fcu);
2819 }
2820
2821 /* free the group itself */
2822 BLI_freelinkN(&adt->action->groups, agrp);
2824
2825 break;
2826 }
2827
2828 /* Layered actions.
2829 *
2830 * Note that the behavior here is different from deleting groups via
2831 * the Python API: in the Python API the fcurves that belonged to the
2832 * group remain, and just get ungrouped, whereas here they are deleted
2833 * along with the group. This difference in behavior is replicated
2834 * from legacy actions. */
2835
2836 blender::animrig::Channelbag &channelbag = agrp->channelbag->wrap();
2837
2838 /* Remove all the fcurves in the group, which also automatically
2839 * deletes the group when the last fcurve is deleted. Since the group
2840 * is automatically deleted, we store the fcurve range ahead of time
2841 * so we don't have to worry about the memory disappearing out from
2842 * under us. */
2843 const int fcurve_range_start = agrp->fcurve_range_start;
2844 const int fcurve_range_length = agrp->fcurve_range_length;
2845 for (int i = 0; i < fcurve_range_length; i++) {
2846 channelbag.fcurve_remove(*channelbag.fcurve(fcurve_range_start));
2847 }
2848
2850
2851 break;
2852 }
2853
2854 case ANIMTYPE_NONE:
2855 case ANIMTYPE_ANIMDATA:
2857 case ANIMTYPE_SUMMARY:
2858 case ANIMTYPE_SCENE:
2859 case ANIMTYPE_OBJECT:
2860 case ANIMTYPE_FCURVE:
2862 case ANIMTYPE_NLACURVE:
2864 case ANIMTYPE_FILLACTD:
2866 case ANIMTYPE_DSMAT:
2867 case ANIMTYPE_DSLAM:
2868 case ANIMTYPE_DSCAM:
2870 case ANIMTYPE_DSCUR:
2871 case ANIMTYPE_DSSKEY:
2872 case ANIMTYPE_DSWOR:
2873 case ANIMTYPE_DSNTREE:
2874 case ANIMTYPE_DSPART:
2875 case ANIMTYPE_DSMBALL:
2876 case ANIMTYPE_DSARM:
2877 case ANIMTYPE_DSMESH:
2878 case ANIMTYPE_DSTEX:
2879 case ANIMTYPE_DSLAT:
2881 case ANIMTYPE_DSSPK:
2882 case ANIMTYPE_DSGPENCIL:
2883 case ANIMTYPE_DSMCLIP:
2884 case ANIMTYPE_DSHAIR:
2886 case ANIMTYPE_DSVOLUME:
2888 case ANIMTYPE_SHAPEKEY:
2889 case ANIMTYPE_GPLAYER:
2894 case ANIMTYPE_MASKLAYER:
2895 case ANIMTYPE_NLATRACK:
2896 case ANIMTYPE_NLAACTION:
2897 case ANIMTYPE_PALETTE:
2898 case ANIMTYPE_NUM_TYPES:
2899 break;
2900 }
2901 }
2902
2903 ANIM_animdata_freelist(&anim_data);
2904
2905 return has_skipped_group;
2906}
2907
2909{
2912
2913 switch (ale.type) {
2914 case ANIMTYPE_FCURVE: {
2915 AnimData *adt = ale.adt;
2916 FCurve *fcu = static_cast<FCurve *>(ale.data);
2917
2918 BLI_assert_msg((fcu->driver != nullptr) == (ac.datatype == ANIMCONT_DRIVERS),
2919 "Expecting only driver F-Curves in the drivers editor");
2920
2921 if (ale.fcurve_owner_id && GS(ale.fcurve_owner_id->name) == ID_AC) {
2922 /* F-Curves can be owned by Actions assigned to NLA strips, which
2923 * `animrig::animdata_fcurve_delete()` (below) cannot handle. */
2924 BLI_assert_msg(!fcu->driver, "Drivers are not expected to be owned by Actions");
2925 blender::animrig::Action &action =
2926 reinterpret_cast<bAction *>(ale.fcurve_owner_id)->wrap();
2927 BLI_assert(!action.is_action_legacy());
2928 action_fcurve_remove(action, *fcu);
2929 }
2930 else if (fcu->driver || adt->action) {
2931 /* This function only works for drivers & directly-assigned Actions: */
2933 }
2934 else {
2936 }
2937 break;
2938 }
2939 case ANIMTYPE_NLACURVE: {
2940 /* NLA Control Curve. */
2941 NlaStrip *strip = static_cast<NlaStrip *>(ale.owner);
2942 FCurve *fcu = static_cast<FCurve *>(ale.data);
2943 if (!BKE_nlastrip_controlcurve_remove(strip, fcu)) {
2944 printf("ERROR: Trying to delete NLA Control Curve for unknown property '%s'\n",
2945 fcu->rna_path);
2946 }
2947 break;
2948 }
2949
2950 default:
2952 }
2953
2955}
2956
2958{
2959 bAnimContext ac;
2960 ListBase anim_data = {nullptr, nullptr};
2961 int filter;
2962
2963 /* get editor data */
2964 if (ANIM_animdata_get_context(C, &ac) == 0) {
2965 return OPERATOR_CANCELLED;
2966 }
2967
2968 /* cannot delete in shapekey */
2969 if (ac.datatype == ANIMCONT_SHAPEKEY) {
2970 return OPERATOR_CANCELLED;
2971 }
2972
2973 /* Do groups and other "summary/expander" types first (unless in Drivers mode, where there are
2974 * none), because the following loop will not find those channels. Also deleting an entire group
2975 * or slot will delete the channels they contain as well, so better avoid looping over those in
2976 * the same loop. */
2977 if (ac.datatype != ANIMCONT_DRIVERS) {
2978 /* Keep deleting container-like channels until there are no more to delete. */
2979 while (animchannels_delete_containers(C, &ac)) {
2980 /* Pass. */
2981 }
2982 }
2983
2984 /* filter data */
2988 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
2989
2990 /* delete selected data channels */
2991 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
2992 switch (ale->type) {
2993 case ANIMTYPE_FCURVE:
2994 case ANIMTYPE_NLACURVE:
2995 ED_anim_ale_fcurve_delete(ac, *ale);
2996 break;
2997
2998 case ANIMTYPE_GPLAYER: {
2999 /* Grease Pencil layer */
3000 bGPdata *gpd = reinterpret_cast<bGPdata *>(ale->id);
3001 bGPDlayer *gpl = static_cast<bGPDlayer *>(ale->data);
3002
3003 /* try to delete the layer's data and the layer itself */
3004 BKE_gpencil_layer_delete(gpd, gpl);
3005 ale->update = ANIM_UPDATE_DEPS;
3006
3007 /* Free Grease Pencil data block when last annotation layer is removed, see: #112683. */
3008 if (gpd->flag & GP_DATA_ANNOTATIONS && gpd->layers.first == nullptr) {
3009 BKE_gpencil_free_data(gpd, true);
3010
3011 Scene *scene = CTX_data_scene(C);
3012 scene->gpd = nullptr;
3013
3014 Main *bmain = CTX_data_main(C);
3015 BKE_id_free_us(bmain, gpd);
3016 }
3017 break;
3018 }
3020 using namespace blender::bke::greasepencil;
3021 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ale->id);
3022 Layer *layer = static_cast<Layer *>(ale->data);
3023 grease_pencil->remove_layer(*layer);
3024 DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
3025 break;
3026 }
3027 case ANIMTYPE_MASKLAYER: {
3028 /* Mask layer */
3029 Mask *mask = reinterpret_cast<Mask *>(ale->id);
3030 MaskLayer *masklay = static_cast<MaskLayer *>(ale->data);
3031
3032 /* try to delete the layer's data and the layer itself */
3033 BKE_mask_layer_remove(mask, masklay);
3034 break;
3035 }
3036 case ANIMTYPE_NONE:
3037 case ANIMTYPE_ANIMDATA:
3039 case ANIMTYPE_SUMMARY:
3040 case ANIMTYPE_SCENE:
3041 case ANIMTYPE_OBJECT:
3042 case ANIMTYPE_GROUP:
3046 case ANIMTYPE_FILLACTD:
3048 case ANIMTYPE_DSMAT:
3049 case ANIMTYPE_DSLAM:
3050 case ANIMTYPE_DSCAM:
3052 case ANIMTYPE_DSCUR:
3053 case ANIMTYPE_DSSKEY:
3054 case ANIMTYPE_DSWOR:
3055 case ANIMTYPE_DSNTREE:
3056 case ANIMTYPE_DSPART:
3057 case ANIMTYPE_DSMBALL:
3058 case ANIMTYPE_DSARM:
3059 case ANIMTYPE_DSMESH:
3060 case ANIMTYPE_DSTEX:
3061 case ANIMTYPE_DSLAT:
3063 case ANIMTYPE_DSSPK:
3064 case ANIMTYPE_DSGPENCIL:
3065 case ANIMTYPE_DSMCLIP:
3066 case ANIMTYPE_DSHAIR:
3068 case ANIMTYPE_DSVOLUME:
3070 case ANIMTYPE_SHAPEKEY:
3074 case ANIMTYPE_NLATRACK:
3075 case ANIMTYPE_NLAACTION:
3076 case ANIMTYPE_PALETTE:
3077 case ANIMTYPE_NUM_TYPES:
3078 break;
3079 }
3080 }
3081
3082 ANIM_animdata_update(&ac, &anim_data);
3083 ANIM_animdata_freelist(&anim_data);
3084
3085 /* send notifier that things have changed */
3089
3090 return OPERATOR_FINISHED;
3091}
3092
3094{
3095 /* identifiers */
3096 ot->name = "Delete Channels";
3097 ot->idname = "ANIM_OT_channels_delete";
3098 ot->description = "Delete all selected animation channels";
3099
3100 /* API callbacks. */
3103
3104 /* flags */
3105 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3106}
3107
3109
3110/* -------------------------------------------------------------------- */
3113
3114/* defines for setting animation-channel flags */
3116 {ACHANNEL_SETFLAG_TOGGLE, "TOGGLE", 0, "Toggle", ""},
3117 {ACHANNEL_SETFLAG_CLEAR, "DISABLE", 0, "Disable", ""},
3118 {ACHANNEL_SETFLAG_ADD, "ENABLE", 0, "Enable", ""},
3119 {ACHANNEL_SETFLAG_INVERT, "INVERT", 0, "Invert", ""},
3120 {0, nullptr, 0, nullptr, nullptr},
3121};
3122
3123/* defines for set animation-channel settings */
3124/* TODO: could add some more types, but those are really quite dependent on the mode... */
3126 {ACHANNEL_SETTING_PROTECT, "PROTECT", 0, "Protect", ""},
3127 {ACHANNEL_SETTING_MUTE, "MUTE", 0, "Mute", ""},
3128 {0, nullptr, 0, nullptr, nullptr},
3129};
3130
3131/* ------------------- */
3132
3142 eAnimChannel_Settings setting,
3144 bool onlysel,
3145 bool flush)
3146{
3147 ListBase anim_data = {nullptr, nullptr};
3148 ListBase all_data = {nullptr, nullptr};
3149 int filter;
3150
3151 /* filter data that we need if flush is on */
3152 if (flush) {
3153 /* get list of all channels that selection may need to be flushed to
3154 * - hierarchy visibility needs to be ignored so that settings can get flushed
3155 * "down" inside closed containers
3156 */
3159 ac, &all_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
3160 }
3161
3162 /* filter data that we're working on
3163 * - hierarchy matters if we're doing this from the channels region
3164 * since we only want to apply this to channels we can "see",
3165 * and have these affect their relatives
3166 * - but for Graph Editor, this gets used also from main region
3167 * where hierarchy doesn't apply #21276.
3168 */
3169 if ((ac->spacetype == SPACE_GRAPH) && (ac->regiontype != RGN_TYPE_CHANNELS)) {
3170 /* graph editor (case 2) */
3173 }
3174 else {
3175 /* standard case */
3178 }
3179 if (onlysel) {
3181 }
3183 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
3184
3185 /* if toggling, check if disable or enable */
3186 if (mode == ACHANNEL_SETFLAG_TOGGLE) {
3187 /* default to turn all on, unless we encounter one that's on... */
3188 mode = ACHANNEL_SETFLAG_ADD;
3189
3190 /* see if we should turn off instead... */
3191 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
3192 /* set the setting in the appropriate way (if available) */
3193 if (ANIM_channel_setting_get(ac, ale, setting) > 0) {
3195 break;
3196 }
3197 }
3198 }
3199
3200 /* apply the setting */
3201 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
3202 /* skip channel if setting is not available */
3203 if (ANIM_channel_setting_get(ac, ale, setting) == -1) {
3204 continue;
3205 }
3206
3207 /* set the setting in the appropriate way */
3208 ANIM_channel_setting_set(ac, ale, setting, mode);
3210
3211 /* if flush status... */
3212 if (flush) {
3213 ANIM_flush_setting_anim_channels(ac, &all_data, ale, setting, mode);
3214 }
3215 }
3216
3217 ANIM_animdata_freelist(&anim_data);
3218 BLI_freelistN(&all_data);
3219}
3220
3221/* ------------------- */
3222
3224{
3225 bAnimContext ac;
3226 eAnimChannel_Settings setting;
3228 bool flush = true;
3229
3230 /* get editor data */
3231 if (ANIM_animdata_get_context(C, &ac) == 0) {
3232 return OPERATOR_CANCELLED;
3233 }
3234
3235 /* mode (eAnimChannels_SetFlag), setting (eAnimChannel_Settings) */
3236 mode = eAnimChannels_SetFlag(RNA_enum_get(op->ptr, "mode"));
3237 setting = eAnimChannel_Settings(RNA_enum_get(op->ptr, "type"));
3238
3239 /* check if setting is flushable */
3240 if (setting == ACHANNEL_SETTING_EXPAND) {
3241 flush = false;
3242 }
3243
3244 /* modify setting
3245 * - only selected channels are affected
3246 */
3247 setflag_anim_channels(&ac, setting, mode, true, flush);
3248
3249 /* send notifier that things have changed */
3251
3252 return OPERATOR_FINISHED;
3253}
3254
3255/* Duplicate of `ANIM_OT_channels_setting_toggle` for menu title only, weak! */
3257{
3258 PropertyRNA *prop;
3259
3260 /* identifiers */
3261 ot->name = "Enable Channel Setting";
3262 ot->idname = "ANIM_OT_channels_setting_enable";
3263 ot->description = "Enable specified setting on all selected animation channels";
3264
3265 /* API callbacks. */
3266 ot->invoke = WM_menu_invoke;
3269
3270 /* flags */
3271 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3272
3273 /* props */
3274 /* flag-setting mode */
3275 prop = RNA_def_enum(
3276 ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_ADD, "Mode", "");
3278 /* setting to set */
3279 ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
3280}
3281/* Duplicate of `ANIM_OT_channels_setting_toggle` for menu title only, weak! */
3283{
3284 PropertyRNA *prop;
3285
3286 /* identifiers */
3287 ot->name = "Disable Channel Setting";
3288 ot->idname = "ANIM_OT_channels_setting_disable";
3289 ot->description = "Disable specified setting on all selected animation channels";
3290
3291 /* API callbacks. */
3292 ot->invoke = WM_menu_invoke;
3295
3296 /* flags */
3297 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3298
3299 /* props */
3300 /* flag-setting mode */
3301 prop = RNA_def_enum(
3302 ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_CLEAR, "Mode", "");
3303 RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
3304 /* setting to set */
3305 ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
3306}
3307
3309{
3310 PropertyRNA *prop;
3311
3312 /* identifiers */
3313 ot->name = "Toggle Channel Setting";
3314 ot->idname = "ANIM_OT_channels_setting_toggle";
3315 ot->description = "Toggle specified setting on all selected animation channels";
3316
3317 /* API callbacks. */
3318 ot->invoke = WM_menu_invoke;
3321
3322 /* flags */
3323 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3324
3325 /* props */
3326 /* flag-setting mode */
3327 prop = RNA_def_enum(
3328 ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
3329 RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
3330 /* setting to set */
3331 ot->prop = RNA_def_enum(ot->srna, "type", prop_animchannel_settings_types, 0, "Type", "");
3332}
3333
3335{
3336 PropertyRNA *prop;
3337
3338 /* identifiers */
3339 ot->name = "Toggle Channel Editability";
3340 ot->idname = "ANIM_OT_channels_editable_toggle";
3341 ot->description = "Toggle editability of selected channels";
3342
3343 /* API callbacks. */
3346
3347 /* flags */
3348 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3349
3350 /* props */
3351 /* flag-setting mode */
3353 ot->srna, "mode", prop_animchannel_setflag_types, ACHANNEL_SETFLAG_TOGGLE, "Mode", "");
3354 /* setting to set */
3355 prop = RNA_def_enum(
3356 ot->srna, "type", prop_animchannel_settings_types, ACHANNEL_SETTING_PROTECT, "Type", "");
3357 RNA_def_property_flag(prop, PROP_HIDDEN); /* internal hack - don't expose */
3358}
3359
3361
3362/* -------------------------------------------------------------------- */
3365
3367{
3368 bAnimContext ac;
3369 bool onlysel = true;
3370
3371 /* get editor data */
3372 if (ANIM_animdata_get_context(C, &ac) == 0) {
3373 return OPERATOR_CANCELLED;
3374 }
3375
3376 /* only affect selected channels? */
3377 if (RNA_boolean_get(op->ptr, "all")) {
3378 onlysel = false;
3379 }
3380
3381 /* modify setting */
3383
3384 /* send notifier that things have changed */
3386
3387 return OPERATOR_FINISHED;
3388}
3389
3391{
3392 /* identifiers */
3393 ot->name = "Expand Channels";
3394 ot->idname = "ANIM_OT_channels_expand";
3395 ot->description = "Expand (open) all selected expandable animation channels";
3396
3397 /* API callbacks. */
3400
3401 /* flags */
3402 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3403
3404 /* props */
3405 ot->prop = RNA_def_boolean(
3406 ot->srna, "all", true, "All", "Expand all channels (not just selected ones)");
3407}
3408
3410
3411/* -------------------------------------------------------------------- */
3414
3416{
3417 bAnimContext ac;
3418 bool onlysel = true;
3419
3420 /* get editor data */
3421 if (ANIM_animdata_get_context(C, &ac) == 0) {
3422 return OPERATOR_CANCELLED;
3423 }
3424
3425 /* only affect selected channels? */
3426 if (RNA_boolean_get(op->ptr, "all")) {
3427 onlysel = false;
3428 }
3429
3430 /* modify setting */
3432
3433 /* send notifier that things have changed */
3435
3436 return OPERATOR_FINISHED;
3437}
3438
3440{
3441 /* identifiers */
3442 ot->name = "Collapse Channels";
3443 ot->idname = "ANIM_OT_channels_collapse";
3444 ot->description = "Collapse (close) all selected expandable animation channels";
3445
3446 /* API callbacks. */
3449
3450 /* flags */
3451 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3452
3453 /* props */
3454 ot->prop = RNA_def_boolean(
3455 ot->srna, "all", true, "All", "Collapse all channels (not just selected ones)");
3456}
3457
3459
3460/* -------------------------------------------------------------------- */
3474
3476{
3477 bAnimContext ac;
3478
3479 ListBase anim_data = {nullptr, nullptr};
3480 int filter;
3481
3482 /* get editor data */
3483 if (ANIM_animdata_get_context(C, &ac) == 0) {
3484 return OPERATOR_CANCELLED;
3485 }
3486
3487 /* get animdata blocks */
3491 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
3492
3493 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
3494 ID *id = ale->id;
3495 AnimData *adt = static_cast<AnimData *>(ale->data);
3496
3497 bool action_empty;
3498 bool nla_empty = false;
3499 bool drivers_empty = false;
3500
3501 /* sanity checks */
3502 BLI_assert((id != nullptr) && (adt != nullptr));
3503
3504 /* check if this is "empty" and can be deleted */
3505 /* (For now, there are only these 3 criteria) */
3506
3507 /* 1) Assigned Action is empty, at least when it comes to this data-block. */
3508 if (adt->action) {
3509 using namespace blender::animrig;
3510 const Action &action = adt->action->wrap();
3511 /* This should not be using action.is_empty(), as this operator is not about cleaning up the
3512 * Action itself, but rather disassociating it from the animated ID when that ID is not being
3513 * animated by it. */
3514 action_empty = fcurves_for_action_slot(action, adt->slot_handle).is_empty();
3515 }
3516 else {
3517 action_empty = true;
3518 }
3519
3520 /* 2) No NLA Tracks and/or NLA Strips */
3521 if (adt->nla_tracks.first == nullptr) {
3522 nla_empty = true;
3523 }
3524 else {
3525 /* empty tracks? */
3526 LISTBASE_FOREACH (NlaTrack *, nlt, &adt->nla_tracks) {
3527 if (nlt->strips.first) {
3528 /* stop searching, as we found one that actually had stuff we don't want lost
3529 * NOTE: nla_empty gets reset to false, as a previous track may have been empty
3530 */
3531 nla_empty = false;
3532 break;
3533 }
3534 if (nlt->strips.first == nullptr) {
3535 /* this track is empty, but another one may still have stuff in it, so can't break yet */
3536 nla_empty = true;
3537 }
3538 }
3539 }
3540
3541 /* 3) Drivers */
3542 drivers_empty = (adt->drivers.first == nullptr);
3543
3544 /* remove AnimData? */
3545 if (action_empty && nla_empty && drivers_empty) {
3546 BKE_animdata_free(id, true);
3547 }
3548 }
3549
3550 /* free temp data */
3551 ANIM_animdata_freelist(&anim_data);
3552
3553 /* send notifier that things have changed */
3556
3557 return OPERATOR_FINISHED;
3558}
3559
3561{
3562 /* identifiers */
3563 ot->name = "Remove Empty Animation Data";
3564 ot->idname = "ANIM_OT_channels_clean_empty";
3565 ot->description = "Delete all empty animation data containers from visible data-blocks";
3566
3567 /* API callbacks. */
3570
3571 /* flags */
3572 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3573}
3574
3576
3577/* -------------------------------------------------------------------- */
3580
3582{
3583 ScrArea *area = CTX_wm_area(C);
3584
3585 /* channels region test */
3586 /* TODO: could enhance with actually testing if channels region? */
3587 if (ELEM(nullptr, area, CTX_wm_region(C))) {
3588 return false;
3589 }
3590
3591 /* animation editor test - Action/Dope-sheet/etc. and Graph only */
3592 if (ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH) == 0) {
3593 return false;
3594 }
3595
3596 return true;
3597}
3598
3600{
3601 bAnimContext ac;
3602
3603 ListBase anim_data = {nullptr, nullptr};
3604 int filter;
3605
3606 /* get editor data */
3607 if (ANIM_animdata_get_context(C, &ac) == 0) {
3608 return OPERATOR_CANCELLED;
3609 }
3610
3611 /* filter data */
3614 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
3615
3616 /* loop through filtered data and clean curves */
3617 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
3618 FCurve *fcu = static_cast<FCurve *>(ale->data);
3619
3620 /* remove disabled flags from F-Curves */
3621 fcu->flag &= ~FCURVE_DISABLED;
3622
3623 /* for drivers, let's do the same too */
3624 if (fcu->driver) {
3626 }
3627
3628 /* tag everything for updates - in particular, this is needed to get drivers working again */
3629 ale->update |= ANIM_UPDATE_DEPS;
3630 }
3631
3632 ANIM_animdata_update(&ac, &anim_data);
3633 ANIM_animdata_freelist(&anim_data);
3634
3635 /* send notifier that things have changed */
3637
3638 return OPERATOR_FINISHED;
3639}
3640
3642{
3643 /* identifiers */
3644 ot->name = "Revive Disabled F-Curves";
3645 ot->idname = "ANIM_OT_channels_fcurves_enable";
3646 ot->description = "Clear 'disabled' tag from all F-Curves to get broken F-Curves working again";
3647
3648 /* API callbacks. */
3651
3652 /* flags */
3653 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3654}
3655
3657
3658/* -------------------------------------------------------------------- */
3661
3662/* XXX: make this generic? */
3664{
3665 ScrArea *area = CTX_wm_area(C);
3666
3667 if (area == nullptr) {
3668 return false;
3669 }
3670
3671 /* animation editor with dope-sheet */
3673}
3674
3676 wmOperator *op,
3677 const wmEvent * /*event*/)
3678{
3679 ScrArea *area = CTX_wm_area(C);
3680 ARegion *region_ctx = CTX_wm_region(C);
3681 ARegion *region_channels = BKE_area_find_region_type(area, RGN_TYPE_CHANNELS);
3682
3683 CTX_wm_region_set(C, region_channels);
3684
3685 /* Show the channel region if it's hidden. This means that direct activation of the input field
3686 * is impossible, as it may not exist yet. For that reason, the actual activation is deferred to
3687 * the modal callback function; by the time it runs, the screen has been redrawn and the UI
3688 * element is there to activate. */
3689 if (region_channels->flag & RGN_FLAG_HIDDEN) {
3690 ED_region_toggle_hidden(C, region_channels);
3691 ED_region_tag_redraw(region_channels);
3692 }
3693
3695
3696 CTX_wm_region_set(C, region_ctx);
3698}
3699
3701 wmOperator * /*op*/,
3702 const wmEvent * /*event*/)
3703{
3704 bAnimContext ac;
3705 if (ANIM_animdata_get_context(C, &ac) == 0) {
3706 return OPERATOR_CANCELLED;
3707 }
3708
3709 ARegion *region = CTX_wm_region(C);
3710 if (UI_textbutton_activate_rna(C, region, ac.ads, "filter_text")) {
3711 /* Redraw to make sure it shows the cursor after activating */
3713 }
3714
3715 return OPERATOR_FINISHED;
3716}
3717
3719{
3720 /* identifiers */
3721 ot->name = "Filter Channels";
3722 ot->idname = "ANIM_OT_channels_select_filter";
3723 ot->description =
3724 "Start entering text which filters the set of channels shown to only include those with "
3725 "matching names";
3726
3727 /* callbacks */
3731}
3732
3734
3735/* -------------------------------------------------------------------- */
3738
3740{
3741 bAnimContext ac;
3742
3743 /* get editor data */
3744 if (ANIM_animdata_get_context(C, &ac) == 0) {
3745 return OPERATOR_CANCELLED;
3746 }
3747
3748 /* 'standard' behavior - check if selected, then apply relevant selection */
3749 const int action = RNA_enum_get(op->ptr, "action");
3750 switch (action) {
3751 case SEL_TOGGLE:
3753 break;
3754 case SEL_SELECT:
3756 break;
3757 case SEL_DESELECT:
3759 break;
3760 case SEL_INVERT:
3762 break;
3763 default:
3764 BLI_assert(0);
3765 break;
3766 }
3767
3768 /* send notifier that things have changed */
3770
3771 return OPERATOR_FINISHED;
3772}
3773
3775{
3776 /* identifiers */
3777 ot->name = "Select All";
3778 ot->idname = "ANIM_OT_channels_select_all";
3779 ot->description = "Toggle selection of all animation channels";
3780
3781 /* API callbacks. */
3784
3785 /* flags */
3786 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3787
3788 /* properties */
3790}
3791
3793
3794/* -------------------------------------------------------------------- */
3797
3798static void box_select_anim_channels(bAnimContext *ac, const rcti &rect, short selectmode)
3799{
3800 ListBase anim_data = {nullptr, nullptr};
3801 int filter;
3802
3803 SpaceNla *snla = reinterpret_cast<SpaceNla *>(ac->sl);
3804 View2D *v2d = &ac->region->v2d;
3805 rctf rectf;
3806
3807 /* convert border-region to view coordinates */
3808 UI_view2d_region_to_view(v2d, rect.xmin, rect.ymin + 2, &rectf.xmin, &rectf.ymin);
3809 UI_view2d_region_to_view(v2d, rect.xmax, rect.ymax - 2, &rectf.xmax, &rectf.ymax);
3810
3811 /* filter data */
3813
3815 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
3816
3817 float ymax;
3818 if (ac->datatype == ANIMCONT_NLA) {
3819 ymax = NLATRACK_FIRST_TOP(ac);
3820 }
3821 else {
3823 }
3824
3825 /* loop over data, doing box select */
3826 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
3827 float ymin;
3828
3829 if (ac->datatype == ANIMCONT_NLA) {
3830 ymin = ymax - NLATRACK_STEP(snla);
3831 }
3832 else {
3833 ymin = ymax - ANIM_UI_get_channel_step();
3834 }
3835
3836 /* if channel is within border-select region, alter it */
3837 if (ymax >= rectf.ymin && ymin <= rectf.ymax) {
3838 /* set selection flags only */
3840 ac, ale, ACHANNEL_SETTING_SELECT, eAnimChannels_SetFlag(selectmode));
3841
3842 /* type specific actions */
3843 switch (ale->type) {
3844 case ANIMTYPE_GROUP: {
3845 bActionGroup *agrp = static_cast<bActionGroup *>(ale->data);
3846 select_pchan_for_action_group(ac, agrp, ale, true);
3847 /* always clear active flag after doing this */
3848 agrp->flag &= ~AGRP_ACTIVE;
3849 break;
3850 }
3851 case ANIMTYPE_NLATRACK: {
3852 NlaTrack *nlt = static_cast<NlaTrack *>(ale->data);
3853
3854 /* for now, it's easier just to do this here manually, as defining a new type
3855 * currently adds complications when doing other stuff
3856 */
3857 ACHANNEL_SET_FLAG(nlt, selectmode, NLATRACK_SELECTED);
3858 break;
3859 }
3860 case ANIMTYPE_ACTION_SLOT: {
3861 using namespace blender::animrig;
3862 Slot *slot = static_cast<Slot *>(ale->data);
3864 break;
3865 }
3866 case ANIMTYPE_NONE:
3867 case ANIMTYPE_ANIMDATA:
3869 case ANIMTYPE_SUMMARY:
3870 case ANIMTYPE_SCENE:
3871 case ANIMTYPE_OBJECT:
3872 case ANIMTYPE_FCURVE:
3874 case ANIMTYPE_NLACURVE:
3876 case ANIMTYPE_FILLACTD:
3878 case ANIMTYPE_DSMAT:
3879 case ANIMTYPE_DSLAM:
3880 case ANIMTYPE_DSCAM:
3882 case ANIMTYPE_DSCUR:
3883 case ANIMTYPE_DSSKEY:
3884 case ANIMTYPE_DSWOR:
3885 case ANIMTYPE_DSNTREE:
3886 case ANIMTYPE_DSPART:
3887 case ANIMTYPE_DSMBALL:
3888 case ANIMTYPE_DSARM:
3889 case ANIMTYPE_DSMESH:
3890 case ANIMTYPE_DSTEX:
3891 case ANIMTYPE_DSLAT:
3893 case ANIMTYPE_DSSPK:
3894 case ANIMTYPE_DSGPENCIL:
3895 case ANIMTYPE_DSMCLIP:
3896 case ANIMTYPE_DSHAIR:
3898 case ANIMTYPE_DSVOLUME:
3900 case ANIMTYPE_SHAPEKEY:
3901 case ANIMTYPE_GPLAYER:
3906 case ANIMTYPE_MASKLAYER:
3907 case ANIMTYPE_NLAACTION:
3908 case ANIMTYPE_PALETTE:
3909 case ANIMTYPE_NUM_TYPES:
3910 break;
3911 }
3912 }
3913
3914 /* set minimum extent to be the maximum of the next channel */
3915 ymax = ymin;
3916 }
3917
3918 /* cleanup */
3919 ANIM_animdata_freelist(&anim_data);
3920}
3921
3923{
3924 bAnimContext ac;
3925 rcti rect;
3926 short selectmode = 0;
3927 const bool select = !RNA_boolean_get(op->ptr, "deselect");
3928 const bool extend = RNA_boolean_get(op->ptr, "extend");
3929
3930 /* get editor data */
3931 if (ANIM_animdata_get_context(C, &ac) == 0) {
3932 return OPERATOR_CANCELLED;
3933 }
3934
3935 /* get settings from operator */
3937
3938 if (!extend) {
3940 }
3941
3942 if (select) {
3943 selectmode = ACHANNEL_SETFLAG_ADD;
3944 }
3945 else {
3946 selectmode = ACHANNEL_SETFLAG_CLEAR;
3947 }
3948
3949 /* apply box_select animation channels */
3950 box_select_anim_channels(&ac, rect, selectmode);
3951
3952 /* send notifier that things have changed */
3954
3955 return OPERATOR_FINISHED;
3956}
3957
3959{
3960 /* identifiers */
3961 ot->name = "Box Select";
3962 ot->idname = "ANIM_OT_channels_select_box";
3963 ot->description = "Select all animation channels within the specified region";
3964
3965 /* API callbacks. */
3966 ot->invoke = WM_gesture_box_invoke;
3968 ot->modal = WM_gesture_box_modal;
3969 ot->cancel = WM_gesture_box_cancel;
3970
3972
3973 /* flags */
3974 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3975
3976 /* rna */
3978}
3979
3981
3982/* -------------------------------------------------------------------- */
3987
3988static bool rename_anim_channels(bAnimContext *ac, int channel_index)
3989{
3990 ListBase anim_data = {nullptr, nullptr};
3991 const bAnimChannelType *acf;
3992 bAnimListElem *ale;
3993 int filter;
3994 bool success = false;
3995
3996 /* Filter relevant channels (note that grease-pencil/annotations are not displayed in Graph
3997 * Editor). */
4001 }
4003 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
4004
4005 /* Get channel that was clicked on from index. */
4006 ale = static_cast<bAnimListElem *>(BLI_findlink(&anim_data, channel_index));
4007 if (ale == nullptr) {
4008 /* channel not found */
4009 if (G.debug & G_DEBUG) {
4010 printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n",
4011 channel_index);
4012 }
4013
4014 ANIM_animdata_freelist(&anim_data);
4015 return false;
4016 }
4017
4018 /* Don't allow renaming linked/liboverride channels. */
4019 if (ale->fcurve_owner_id != nullptr &&
4021 {
4022 ANIM_animdata_freelist(&anim_data);
4023 return false;
4024 }
4025 if (ale->id != nullptr) {
4026 if (!ID_IS_EDITABLE(ale->id)) {
4027 ANIM_animdata_freelist(&anim_data);
4028 return false;
4029 }
4030 /* There is one exception to not allowing renaming on liboverride channels: locally-inserted
4031 * NLA tracks. */
4032 if (ID_IS_OVERRIDE_LIBRARY(ale->id)) {
4033 switch (ale->type) {
4034 case ANIMTYPE_NLATRACK: {
4035 NlaTrack *nlt = static_cast<NlaTrack *>(ale->data);
4036 if ((nlt->flag & NLATRACK_OVERRIDELIBRARY_LOCAL) == 0) {
4037 ANIM_animdata_freelist(&anim_data);
4038 return false;
4039 }
4040 break;
4041 }
4042 default:
4043 ANIM_animdata_freelist(&anim_data);
4044 return false;
4045 }
4046 }
4047 }
4048
4049 /* check that channel can be renamed */
4050 acf = ANIM_channel_get_typeinfo(ale);
4051 if (acf && acf->name_prop) {
4053 PropertyRNA *prop;
4054
4055 /* ok if we can get name property to edit from this channel */
4056 if (acf->name_prop(ale, &ptr, &prop)) {
4057 /* Actually showing the rename text-field is done on redraw,
4058 * so here we just store the index of this channel in the
4059 * dope-sheet data, which will get utilized when drawing the channel.
4060 *
4061 * +1 factor is for backwards compatibility issues. */
4062 if (ac->ads) {
4063 ac->ads->renameIndex = channel_index + 1;
4064 success = true;
4065 }
4066 }
4067 }
4068
4069 /* free temp data and tag for refresh */
4070 ANIM_animdata_freelist(&anim_data);
4072 return success;
4073}
4074
4075static int animchannels_channel_get(bAnimContext *ac, const int mval[2])
4076{
4077 ARegion *region;
4078 View2D *v2d;
4079 int channel_index;
4080 float x, y;
4081
4082 /* get useful pointers from animation context data */
4083 region = ac->region;
4084 v2d = &region->v2d;
4085
4086 /* Figure out which channel user clicked in. */
4087 UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
4088
4089 if (ac->datatype == ANIMCONT_NLA) {
4090 SpaceNla *snla = reinterpret_cast<SpaceNla *>(ac->sl);
4092 NLATRACK_STEP(snla),
4093 0,
4095 x,
4096 y,
4097 nullptr,
4098 &channel_index);
4099 }
4100 else {
4103 0,
4105 x,
4106 y,
4107 nullptr,
4108 &channel_index);
4109 }
4110
4111 return channel_index;
4112}
4113
4115 wmOperator * /*op*/,
4116 const wmEvent *event)
4117{
4118 bAnimContext ac;
4119 int channel_index;
4120
4121 /* get editor data */
4122 if (ANIM_animdata_get_context(C, &ac) == 0) {
4123 return OPERATOR_CANCELLED;
4124 }
4125
4126 channel_index = animchannels_channel_get(&ac, event->mval);
4127
4128 /* handle click */
4129 if (rename_anim_channels(&ac, channel_index)) {
4131 return OPERATOR_FINISHED;
4132 }
4133
4134 /* allow event to be handled by selectall operator */
4135 return OPERATOR_PASS_THROUGH;
4136}
4137
4139{
4140 /* identifiers */
4141 ot->name = "Rename Channel";
4142 ot->idname = "ANIM_OT_channels_rename";
4143 ot->description = "Rename animation channel under mouse";
4144
4145 /* API callbacks. */
4148}
4149
4151
4152/* -------------------------------------------------------------------- */
4155
4156/* Handle selection changes due to clicking on channels. Settings will get caught by UI code... */
4157
4159 const short /* eEditKeyframes_Select or -1 */ selectmode)
4160{
4161 Scene *sce = static_cast<Scene *>(ale->data);
4162 AnimData *adt = sce->adt;
4163
4164 /* set selection status */
4165 if (selectmode == SELECT_INVERT) {
4166 /* swap select */
4167 sce->flag ^= SCE_DS_SELECTED;
4168 if (adt) {
4169 adt->flag ^= ADT_UI_SELECTED;
4170 }
4171 }
4172 else {
4173 sce->flag |= SCE_DS_SELECTED;
4174 if (adt) {
4175 adt->flag |= ADT_UI_SELECTED;
4176 }
4177 }
4178 return (ND_ANIMCHAN | NA_SELECTED);
4179}
4180
4181/* Return whether active channel of given type is present. */
4183{
4184 ListBase anim_data = anim_channels_for_selection(ac);
4185 bool is_active_found = false;
4186
4187 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
4188 if (ale->type != type) {
4189 continue;
4190 }
4191 is_active_found = ANIM_is_active_channel(ale);
4192 if (is_active_found) {
4193 break;
4194 }
4195 }
4196
4197 ANIM_animdata_freelist(&anim_data);
4198 return is_active_found;
4199}
4200
4201/* Select channels that lies between active channel and cursor_elem. */
4203{
4204 ListBase anim_data = anim_channels_for_selection(ac);
4205 bool in_selection_range = false;
4206
4207 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
4208
4209 /* Allow selection when active channel and `cursor_elem` are of same type. */
4210 if (ale->type != cursor_elem->type) {
4211 continue;
4212 }
4213
4214 const bool is_cursor_elem = (ale->data == cursor_elem->data);
4215 const bool is_active_elem = ANIM_is_active_channel(ale);
4216
4217 /* Restrict selection when active element is not found and group-channels are excluded from the
4218 * selection. */
4219 if (is_active_elem || is_cursor_elem) {
4220 /* Select first and last element from the range. Reverse selection status on extremes. */
4222 in_selection_range = !in_selection_range;
4223 if (ale->type == ANIMTYPE_GROUP) {
4224 select_pchan_for_action_group(ac, static_cast<bActionGroup *>(ale->data), ale, false);
4225 }
4226 }
4227 else if (in_selection_range) {
4228 /* Select elements between the range. */
4230 if (ale->type == ANIMTYPE_GROUP) {
4231 select_pchan_for_action_group(ac, static_cast<bActionGroup *>(ale->data), ale, false);
4232 }
4233 }
4234
4235 if (is_active_elem && is_cursor_elem) {
4236 /* Selection range is only one element when active channel and clicked channel are same. So
4237 * exit out of the loop when this condition is hit. */
4238 break;
4239 }
4240 }
4241
4242 ANIM_animdata_freelist(&anim_data);
4243}
4244
4246 bAnimContext *ac,
4247 bAnimListElem *ale,
4248 const short /* eEditKeyframes_Select or -1 */ selectmode)
4249{
4250 using namespace blender::ed;
4251 Scene *scene = ac->scene;
4252 ViewLayer *view_layer = ac->view_layer;
4253 Base *base = static_cast<Base *>(ale->data);
4254 Object *ob = base->object;
4255 AnimData *adt = ob->adt;
4256
4257 if ((base->flag & BASE_SELECTABLE) == 0) {
4258 return 0;
4259 }
4260
4261 if (selectmode == SELECT_INVERT) {
4262 /* swap select */
4264
4265 if (adt) {
4266 adt->flag ^= ADT_UI_SELECTED;
4267 }
4268 }
4269 else if (selectmode == SELECT_EXTEND_RANGE) {
4271 animchannel_select_range(ac, ale);
4272 }
4273 else {
4274 /* deselect all */
4276 BKE_view_layer_synced_ensure(scene, view_layer);
4277 /* TODO: should this deselect all other types of channels too? */
4280 if (b->object->adt) {
4281 b->object->adt->flag &= ~(ADT_UI_SELECTED | ADT_UI_ACTIVE);
4282 }
4283 }
4284
4285 /* select object now */
4287 if (adt) {
4288 adt->flag |= ADT_UI_SELECTED;
4289 }
4290 }
4291
4292 /* Change active object - regardless of whether it is now selected, see: #37883.
4293 *
4294 * Ensure we exit edit-mode on whatever object was active before
4295 * to avoid getting stuck there, see: #48747. */
4296 object::base_activate_with_mode_exit_if_needed(C, base); /* adds notifier */
4297
4298 /* Similar to outliner, do not change active element when selecting elements in range. */
4299 if ((adt) && (adt->flag & ADT_UI_SELECTED) && (selectmode != SELECT_EXTEND_RANGE)) {
4300 adt->flag |= ADT_UI_ACTIVE;
4301 }
4302
4303 return (ND_ANIMCHAN | NA_SELECTED);
4304}
4305
4307 bAnimListElem *ale,
4308 const short /* eEditKeyframes_Select or -1 */ selectmode)
4309{
4310 if (ale->adt == nullptr) {
4311 return 0;
4312 }
4313
4314 /* select/deselect */
4315 if (selectmode == SELECT_INVERT) {
4316 /* inverse selection status of this AnimData block only */
4317 ale->adt->flag ^= ADT_UI_SELECTED;
4318 }
4319 else if (selectmode == SELECT_EXTEND_RANGE) {
4321 animchannel_select_range(ac, ale);
4322 }
4323 else {
4324 /* select AnimData block by itself */
4326 ale->adt->flag |= ADT_UI_SELECTED;
4327 }
4328
4329 /* Similar to outliner, do not change active element when selecting elements in range. */
4330 if ((ale->adt->flag & ADT_UI_SELECTED) && (selectmode != SELECT_EXTEND_RANGE)) {
4331 ale->adt->flag |= ADT_UI_ACTIVE;
4332 }
4333
4334 return (ND_ANIMCHAN | NA_SELECTED);
4335}
4336
4338 bAnimListElem *ale,
4339 const short /* eEditKeyframes_Select or -1 */ selectmode,
4340 const int filter)
4341{
4342 bActionGroup *agrp = static_cast<bActionGroup *>(ale->data);
4343 Object *ob = nullptr;
4344 bPoseChannel *pchan = nullptr;
4345
4346 /* Armatures-Specific Feature:
4347 * Since groups are used to collect F-Curves of the same Bone by default
4348 * (via Keying Sets) so that they can be managed better, we try to make
4349 * things here easier for animators by mapping group selection to bone
4350 * selection.
4351 *
4352 * Only do this if "Only Selected" dope-sheet filter is not active, or else it
4353 * becomes too unpredictable/tricky to manage
4354 */
4355 if ((ac->filters.flag & ADS_FILTER_ONLYSEL) == 0) {
4356 if ((ale->id) && (GS(ale->id->name) == ID_OB)) {
4357 ob = reinterpret_cast<Object *>(ale->id);
4358
4359 if (ob->type == OB_ARMATURE) {
4360 /* Assume for now that any group with corresponding name is what we want
4361 * (i.e. for an armature whose location is animated, things would break
4362 * if the user were to add a bone named "Location").
4363 *
4364 * TODO: check the first F-Curve or so to be sure...
4365 */
4366 pchan = BKE_pose_channel_find_name(ob->pose, agrp->name);
4367 }
4368 }
4369 }
4370
4371 /* select/deselect group */
4372 if (selectmode == SELECT_INVERT) {
4373 /* inverse selection status of this group only */
4374 agrp->flag ^= AGRP_SELECTED;
4375 }
4376 else if (selectmode == SELECT_EXTEND_RANGE) {
4378 animchannel_select_range(ac, ale);
4379 }
4380 else if (selectmode == -1) {
4381 /* select all in group (and deselect everything else) */
4382 FCurve *fcu;
4383
4384 /* deselect all other channels */
4386 if (pchan) {
4388 }
4389
4390 /* only select channels in group and group itself */
4391 for (fcu = static_cast<FCurve *>(agrp->channels.first); fcu && fcu->grp == agrp;
4392 fcu = fcu->next)
4393 {
4394 fcu->flag |= FCURVE_SELECTED;
4395 }
4396 agrp->flag |= AGRP_SELECTED;
4397 }
4398 else {
4399 /* select group by itself */
4401 if (pchan) {
4403 }
4404
4405 agrp->flag |= AGRP_SELECTED;
4406 }
4407
4408 /* if group is selected now, make group the 'active' one in the visible list.
4409 * Similar to outliner, do not change active element when selecting elements in range. */
4410 if (agrp->flag & AGRP_SELECTED) {
4411 if (selectmode != SELECT_EXTEND_RANGE) {
4413 ac->data,
4416 agrp,
4418 if (pchan) {
4419 ED_pose_bone_select(ob, pchan, true, true);
4420 }
4421 }
4422 }
4423 else {
4424 if (selectmode != SELECT_EXTEND_RANGE) {
4426 ac->data,
4429 nullptr,
4431 if (pchan) {
4432 ED_pose_bone_select(ob, pchan, false, true);
4433 }
4434 }
4435 }
4436
4437 return (ND_ANIMCHAN | NA_SELECTED);
4438}
4439
4441 bAnimListElem *ale,
4442 const short /* eEditKeyframes_Select or -1 */ selectmode,
4443 const int filter)
4444{
4445 FCurve *fcu = static_cast<FCurve *>(ale->data);
4446
4447 /* select/deselect */
4448 if (selectmode == SELECT_INVERT) {
4449 /* inverse selection status of this F-Curve only */
4450 fcu->flag ^= FCURVE_SELECTED;
4451 }
4452 else if (selectmode == SELECT_EXTEND_RANGE) {
4454 animchannel_select_range(ac, ale);
4455 }
4456 else {
4457 /* select F-Curve by itself */
4459 fcu->flag |= FCURVE_SELECTED;
4460 }
4461
4462 /* if F-Curve is selected now, make F-Curve the 'active' one in the visible list.
4463 * Similar to outliner, do not change active element when selecting elements in range. */
4464 if ((fcu->flag & FCURVE_SELECTED) && (selectmode != SELECT_EXTEND_RANGE)) {
4466 ac->data,
4469 fcu,
4470 eAnim_ChannelType(ale->type));
4471 }
4472
4473 return (ND_ANIMCHAN | NA_SELECTED);
4474}
4476 bAnimListElem *ale,
4477 short /* eEditKeyframes_Select or -1 */ selectmode)
4478{
4479 using namespace blender;
4480
4482 "fcurve_owner_id of an Action Slot should be an Action");
4483 animrig::Action *action = reinterpret_cast<animrig::Action *>(ale->fcurve_owner_id);
4484 animrig::Slot *slot = static_cast<animrig::Slot *>(ale->data);
4485
4486 if (selectmode == SELECT_INVERT) {
4487 selectmode = slot->is_selected() ? SELECT_SUBTRACT : SELECT_ADD;
4488 }
4489
4490 switch (selectmode) {
4491 case SELECT_REPLACE:
4494 case SELECT_ADD:
4495 slot->set_selected(true);
4496 action->slot_active_set(slot->handle);
4497 break;
4498 case SELECT_SUBTRACT:
4499 slot->set_selected(false);
4500 break;
4503 animchannel_select_range(ac, ale);
4504 break;
4505 case SELECT_INVERT:
4507 break;
4508 }
4509
4510 return (ND_ANIMCHAN | NA_SELECTED);
4511}
4512
4514 bAnimListElem *ale,
4515 const short /* eEditKeyframes_Select or -1 */ selectmode)
4516{
4517 KeyBlock *kb = static_cast<KeyBlock *>(ale->data);
4518 Key *key = reinterpret_cast<Key *>(ale->id);
4519 Object &ob = *ac->obact;
4520
4521 ob.shapenr = BLI_findindex(&key->block, kb) + 1;
4522
4523 /* select/deselect */
4524 if (selectmode == SELECT_INVERT) {
4525 /* inverse selection status of this ShapeKey only */
4526 kb->flag ^= KEYBLOCK_SEL;
4527 }
4528 else {
4529 /* select ShapeKey by itself */
4531 kb->flag |= KEYBLOCK_SEL;
4532 }
4533
4534 return (ND_ANIMCHAN | NA_SELECTED);
4535}
4536
4538{
4539 AnimData *adt = static_cast<AnimData *>(ale->data);
4540
4541 /* Toggle expand:
4542 * - Although the triangle widget already allows this,
4543 * since there's nothing else that can be done here now,
4544 * let's just use it for easier expand/collapse for now.
4545 */
4547
4548 return (ND_ANIMCHAN | NA_EDITED);
4549}
4550
4552 bAnimContext *ac,
4553 bAnimListElem *ale,
4554 const short /* eEditKeyframes_Select or -1 */ selectmode,
4555 const int filter)
4556{
4557 bGPdata *gpd = reinterpret_cast<bGPdata *>(ale->id);
4558 bGPDlayer *gpl = static_cast<bGPDlayer *>(ale->data);
4559
4560 /* select/deselect */
4561 if (selectmode == SELECT_INVERT) {
4562 /* invert selection status of this layer only */
4563 gpl->flag ^= GP_LAYER_SELECT;
4564 }
4565 else if (selectmode == SELECT_EXTEND_RANGE) {
4567 animchannel_select_range(ac, ale);
4568 }
4569 else {
4570 /* select layer by itself */
4572 gpl->flag |= GP_LAYER_SELECT;
4573 }
4574
4575 /* change active layer, if this is selected (since we must always have an active layer).
4576 * Similar to outliner, do not change active element when selecting elements in range. */
4577 if ((gpl->flag & GP_LAYER_SELECT) && (selectmode != SELECT_EXTEND_RANGE)) {
4579 ac->data,
4582 gpl,
4584 /* update other layer status */
4587 }
4588
4589 /* Grease Pencil updates */
4591 return (ND_ANIMCHAN | NA_EDITED); /* Animation Editors updates */
4592}
4593
4595{
4596 GreasePencil *grease_pencil = static_cast<GreasePencil *>(ale->data);
4597
4598 /* Toggle expand:
4599 * - Although the triangle widget already allows this,
4600 * the whole channel can also be used for this purpose.
4601 */
4602 grease_pencil->flag ^= GREASE_PENCIL_ANIM_CHANNEL_EXPANDED;
4603
4604 return (ND_ANIMCHAN | NA_EDITED);
4605}
4606
4608{
4609 using namespace blender::bke::greasepencil;
4610 LayerGroup &layer_group = static_cast<GreasePencilLayerTreeGroup *>(ale->data)->wrap();
4611
4612 /* Toggle expand:
4613 * - Although the triangle widget already allows this,
4614 * the whole channel can also be used for this purpose.
4615 */
4616 layer_group.set_expanded(!layer_group.is_expanded());
4619 return (ND_ANIMCHAN | NA_EDITED);
4620}
4621
4623 bAnimContext *ac,
4624 bAnimListElem *ale,
4625 const short selectmode,
4626 const int /*filter*/)
4627{
4628 using namespace blender::bke::greasepencil;
4629 Layer *layer = static_cast<Layer *>(ale->data);
4630 GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(ale->id);
4631
4632 if (selectmode == SELECT_INVERT) {
4633 layer->set_selected(!layer->is_selected());
4634 }
4635 else if (selectmode == SELECT_EXTEND_RANGE) {
4637 animchannel_select_range(ac, ale);
4638 }
4639 else {
4641 layer->set_selected(true);
4642 }
4643
4644 /* Active channel is not changed during range select. */
4645 if (layer->is_selected() && (selectmode != SELECT_EXTEND_RANGE)) {
4646 grease_pencil->set_active_layer(layer);
4648 CTX_wm_message_bus(C), &grease_pencil->id, grease_pencil, GreasePencilv3Layers, active);
4649 DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY);
4650 }
4651
4653 return (ND_ANIMCHAN | NA_EDITED);
4654}
4655
4657{
4658 Mask *mask = static_cast<Mask *>(ale->data);
4659
4660 /* Toggle expand
4661 * - Although the triangle widget already allows this,
4662 * the whole channel can also be used for this purpose.
4663 */
4664 mask->flag ^= MASK_ANIMF_EXPAND;
4665
4666 return (ND_ANIMCHAN | NA_EDITED);
4667}
4668
4670 bAnimListElem *ale,
4671 const short /* eEditKeyframes_Select or -1 */ selectmode)
4672{
4673 MaskLayer *masklay = static_cast<MaskLayer *>(ale->data);
4674
4675 /* select/deselect */
4676 if (selectmode == SELECT_INVERT) {
4677 /* invert selection status of this layer only */
4678 masklay->flag ^= MASK_LAYERFLAG_SELECT;
4679 }
4680 else {
4681 /* select layer by itself */
4683 masklay->flag |= MASK_LAYERFLAG_SELECT;
4684 }
4685
4686 return (ND_ANIMCHAN | NA_EDITED);
4687}
4688
4690 bAnimContext *ac,
4691 const int channel_index,
4692 short /* eEditKeyframes_Select or -1 */ selectmode)
4693{
4694 ListBase anim_data = {nullptr, nullptr};
4695 bAnimListElem *ale;
4696 int filter;
4697 int notifierFlags = 0;
4698 ScrArea *area = CTX_wm_area(C);
4699
4700 /* get the channel that was clicked on */
4701 /* filter channels */
4703 if (ELEM(area->spacetype, SPACE_NLA, SPACE_GRAPH)) {
4705 }
4707 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
4708
4709 /* get channel from index */
4710 ale = static_cast<bAnimListElem *>(BLI_findlink(&anim_data, channel_index));
4711 if (ale == nullptr) {
4712 /* channel not found */
4713 if (G.debug & G_DEBUG) {
4714 printf("Error: animation channel (index = %d) not found in mouse_anim_channels()\n",
4715 channel_index);
4716 }
4717
4718 ANIM_animdata_freelist(&anim_data);
4719 return 0;
4720 }
4721
4722 /* selectmode -1 is a special case for ActionGroups only,
4723 * which selects all of the channels underneath it only. */
4724 /* TODO: should this feature be extended to work with other channel types too? */
4725 if ((selectmode == -1) && (ale->type != ANIMTYPE_GROUP)) {
4726 /* normal channels should not behave normally in this case */
4727 ANIM_animdata_freelist(&anim_data);
4728 return 0;
4729 }
4730
4731 /* Change selection mode to single when no active element is found. */
4732 if ((selectmode == SELECT_EXTEND_RANGE) &&
4734 {
4735 selectmode = SELECT_INVERT;
4736 }
4737
4738 /* action to take depends on what channel we've got */
4739 /* WARNING: must keep this in sync with the equivalent function in `nla_tracks.cc`. */
4740 switch (ale->type) {
4741 case ANIMTYPE_SCENE:
4742 notifierFlags |= click_select_channel_scene(ale, selectmode);
4743 break;
4744 case ANIMTYPE_OBJECT:
4745 notifierFlags |= click_select_channel_object(C, ac, ale, selectmode);
4746 break;
4747 case ANIMTYPE_FILLACTD: /* Action Expander */
4748 case ANIMTYPE_FILLACT_LAYERED: /* Animation Expander */
4749 case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
4750 case ANIMTYPE_DSLAM:
4751 case ANIMTYPE_DSCAM:
4753 case ANIMTYPE_DSCUR:
4754 case ANIMTYPE_DSSKEY:
4755 case ANIMTYPE_DSWOR:
4756 case ANIMTYPE_DSPART:
4757 case ANIMTYPE_DSMBALL:
4758 case ANIMTYPE_DSARM:
4759 case ANIMTYPE_DSMESH:
4760 case ANIMTYPE_DSNTREE:
4761 case ANIMTYPE_DSTEX:
4762 case ANIMTYPE_DSLAT:
4764 case ANIMTYPE_DSSPK:
4765 case ANIMTYPE_DSGPENCIL:
4766 case ANIMTYPE_DSMCLIP:
4767 case ANIMTYPE_DSHAIR:
4769 case ANIMTYPE_DSVOLUME:
4771 notifierFlags |= click_select_channel_dummy(ac, ale, selectmode);
4772 break;
4773 case ANIMTYPE_GROUP:
4774 notifierFlags |= click_select_channel_group(ac, ale, selectmode, filter);
4775 break;
4776 case ANIMTYPE_FCURVE:
4777 case ANIMTYPE_NLACURVE:
4778 notifierFlags |= click_select_channel_fcurve(ac, ale, selectmode, filter);
4779 break;
4781 notifierFlags |= click_select_channel_action_slot(ac, ale, selectmode);
4782 break;
4783 case ANIMTYPE_SHAPEKEY:
4784 notifierFlags |= click_select_channel_shapekey(ac, ale, selectmode);
4785 break;
4787 notifierFlags |= click_select_channel_nlacontrols(ale);
4788 break;
4789 case ANIMTYPE_GPLAYER:
4790 notifierFlags |= click_select_channel_gplayer(C, ac, ale, selectmode, filter);
4791 break;
4794 break;
4797 break;
4799 notifierFlags |= click_select_channel_grease_pencil_layer(C, ac, ale, selectmode, filter);
4800 break;
4802 notifierFlags |= click_select_channel_maskdatablock(ale);
4803 break;
4804 case ANIMTYPE_MASKLAYER:
4805 notifierFlags |= click_select_channel_masklayer(ac, ale, selectmode);
4806 break;
4807 default:
4808 if (G.debug & G_DEBUG) {
4809 printf("Error: Invalid channel type in mouse_anim_channels()\n");
4810 }
4811 break;
4812 }
4813
4814 /* free channels */
4815 ANIM_animdata_freelist(&anim_data);
4816
4817 /* return notifier flags */
4818 return notifierFlags;
4819}
4820
4822
4823/* -------------------------------------------------------------------- */
4826
4829 wmOperator *op,
4830 const wmEvent *event)
4831{
4832 bAnimContext ac;
4833 ARegion *region;
4834 View2D *v2d;
4835 int channel_index;
4836 int notifierFlags = 0;
4837 short selectmode;
4838 float x, y;
4839
4840 /* get editor data */
4841 if (ANIM_animdata_get_context(C, &ac) == 0) {
4842 return OPERATOR_CANCELLED;
4843 }
4844
4845 /* get useful pointers from animation context data */
4846 region = ac.region;
4847 v2d = &region->v2d;
4848
4849 /* select mode is either replace (deselect all, then add) or add/extend */
4850 if (RNA_boolean_get(op->ptr, "extend")) {
4851 selectmode = SELECT_INVERT;
4852 }
4853 else if (RNA_boolean_get(op->ptr, "extend_range")) {
4854 selectmode = SELECT_EXTEND_RANGE;
4855 }
4856 else if (RNA_boolean_get(op->ptr, "children_only")) {
4857 /* this is a bit of a special case for ActionGroups only...
4858 * should it be removed or extended to all instead? */
4859 selectmode = -1;
4860 }
4861 else {
4862 selectmode = SELECT_REPLACE;
4863 }
4864
4865 /* figure out which channel user clicked in */
4866 UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
4869 0,
4871 x,
4872 y,
4873 nullptr,
4874 &channel_index);
4875
4876 /* handle mouse-click in the relevant channel then */
4877 notifierFlags = mouse_anim_channels(C, &ac, channel_index, selectmode);
4878
4879 /* set notifier that things have changed */
4880 WM_event_add_notifier(C, NC_ANIMATION | notifierFlags, nullptr);
4881
4883 event);
4884}
4885
4887{
4888 PropertyRNA *prop;
4889
4890 /* identifiers */
4891 ot->name = "Mouse Click on Channels";
4892 ot->idname = "ANIM_OT_channels_click";
4893 ot->description = "Handle mouse clicks over animation channels";
4894
4895 /* API callbacks. */
4898
4899 /* flags */
4900 ot->flag = OPTYPE_UNDO;
4901
4902 /* properties */
4903 /* NOTE: don't save settings, otherwise, can end up with some weird behavior (sticky extend)
4904 *
4905 * Key-map: Enable with `Shift`. */
4906 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend Select", "");
4908
4909 prop = RNA_def_boolean(ot->srna,
4910 "extend_range",
4911 false,
4912 "Extend Range",
4913 "Selection of active channel to clicked channel");
4915
4916 /* Key-map: Enable with `Ctrl-Shift`. */
4917 prop = RNA_def_boolean(ot->srna, "children_only", false, "Select Children Only", "");
4919}
4920
4921static bool select_anim_channel_keys(bAnimContext *ac, int channel_index, bool extend)
4922{
4923 ListBase anim_data = {nullptr, nullptr};
4924 bAnimListElem *ale;
4925 int filter;
4926 bool success = false;
4927 FCurve *fcu;
4928 int i;
4929
4930 /* get the channel that was clicked on */
4931 /* filter channels */
4935 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
4936
4937 /* get channel from index */
4938 ale = static_cast<bAnimListElem *>(BLI_findlink(&anim_data, channel_index));
4939 if (ale == nullptr) {
4940 /* channel not found */
4941 if (G.debug & G_DEBUG) {
4942 printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n",
4943 channel_index);
4944 }
4945
4946 ANIM_animdata_freelist(&anim_data);
4947 return false;
4948 }
4949
4950 /* Only FCuves can have their keys selected. */
4951 if (ale->datatype != ALE_FCURVE) {
4952 ANIM_animdata_freelist(&anim_data);
4953 return false;
4954 }
4955
4956 fcu = static_cast<FCurve *>(ale->key_data);
4957 success = (fcu != nullptr);
4958
4959 ANIM_animdata_freelist(&anim_data);
4960
4961 /* F-Curve may not have any keyframes */
4962 if (fcu && fcu->bezt) {
4963 BezTriple *bezt;
4964
4965 if (!extend) {
4968 ac, &anim_data, eAnimFilter_Flags(filter), ac->data, eAnimCont_Types(ac->datatype));
4969 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
4970 FCurve *fcu_inner = static_cast<FCurve *>(ale->key_data);
4971
4972 if (fcu_inner != nullptr && fcu_inner->bezt != nullptr) {
4973 for (i = 0, bezt = fcu_inner->bezt; i < fcu_inner->totvert; i++, bezt++) {
4974 bezt->f2 = bezt->f1 = bezt->f3 = 0;
4975 }
4976 }
4977 }
4978
4979 ANIM_animdata_freelist(&anim_data);
4980 }
4981
4982 for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
4983 bezt->f2 = bezt->f1 = bezt->f3 = SELECT;
4984 }
4985 }
4986
4987 /* free temp data and tag for refresh */
4989 return success;
4990}
4991
4993 wmOperator *op,
4994 const wmEvent *event)
4995{
4996 bAnimContext ac;
4997 int channel_index;
4998 bool extend = RNA_boolean_get(op->ptr, "extend");
4999
5000 /* get editor data */
5001 if (ANIM_animdata_get_context(C, &ac) == 0) {
5002 return OPERATOR_CANCELLED;
5003 }
5004
5005 channel_index = animchannels_channel_get(&ac, event->mval);
5006
5007 /* handle click */
5008 if (select_anim_channel_keys(&ac, channel_index, extend)) {
5010 return OPERATOR_FINISHED;
5011 }
5012
5013 /* allow event to be handled by selectall operator */
5014 return OPERATOR_PASS_THROUGH;
5015}
5016
5018{
5019 PropertyRNA *prop;
5020
5021 /* identifiers */
5022 ot->name = "Select Channel Keyframes";
5023 ot->idname = "ANIM_OT_channel_select_keys";
5024 ot->description = "Select all keyframes of channel under mouse";
5025
5026 /* API callbacks. */
5029
5030 ot->flag = OPTYPE_UNDO;
5031
5032 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection");
5034}
5035
5037
5038/* -------------------------------------------------------------------- */
5041
5042static void get_view_range(Scene *scene, const bool use_preview_range, float r_range[2])
5043{
5044 if (use_preview_range && scene->r.flag & SCER_PRV_RANGE) {
5045 r_range[0] = scene->r.psfra;
5046 r_range[1] = scene->r.pefra;
5047 }
5048 else {
5049 r_range[0] = scene->r.sfra;
5050 r_range[1] = scene->r.efra;
5051 }
5052}
5053
5055{
5056 bAnimContext ac;
5057
5058 /* Get editor data. */
5059 if (ANIM_animdata_get_context(C, &ac) == 0) {
5060 return OPERATOR_CANCELLED;
5061 }
5063
5064 if (!window_region) {
5065 return OPERATOR_CANCELLED;
5066 }
5067
5068 ListBase anim_data = {nullptr, nullptr};
5071 size_t anim_data_length = ANIM_animdata_filter(
5072 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
5073
5074 if (anim_data_length == 0) {
5075 BKE_report(op->reports, RPT_WARNING, "No channels to operate on");
5076 return OPERATOR_CANCELLED;
5077 }
5078
5079 float range[2];
5080 const bool use_preview_range = RNA_boolean_get(op->ptr, "use_preview_range");
5081 get_view_range(ac.scene, use_preview_range, range);
5082
5083 rctf bounds{};
5084 bounds.xmin = FLT_MAX;
5085 bounds.xmax = -FLT_MAX;
5086 bounds.ymin = FLT_MAX;
5087 bounds.ymax = -FLT_MAX;
5088
5089 const bool include_handles = RNA_boolean_get(op->ptr, "include_handles");
5090
5091 bool valid_bounds = false;
5092 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
5093 rctf channel_bounds;
5094 const bool found_bounds = get_channel_bounds(
5095 &ac, ale, range, include_handles, &channel_bounds);
5096 if (found_bounds) {
5097 BLI_rctf_union(&bounds, &channel_bounds);
5098 valid_bounds = true;
5099 }
5100 }
5101
5102 if (!valid_bounds) {
5103 ANIM_animdata_freelist(&anim_data);
5104 BKE_report(op->reports, RPT_WARNING, "No keyframes to focus on");
5105 return OPERATOR_CANCELLED;
5106 }
5107
5108 add_region_padding(C, window_region, &bounds);
5109
5110 if (ac.spacetype == SPACE_ACTION) {
5111 bounds.ymin = window_region->v2d.cur.ymin;
5112 bounds.ymax = window_region->v2d.cur.ymax;
5113 }
5114
5115 const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
5116 UI_view2d_smooth_view(C, window_region, &bounds, smooth_viewtx);
5117
5118 ANIM_animdata_freelist(&anim_data);
5119
5120 return OPERATOR_FINISHED;
5121}
5122
5127
5129{
5130 /* Identifiers */
5131 ot->name = "Frame Selected Channels";
5132 ot->idname = "ANIM_OT_channels_view_selected";
5133 ot->description = "Reset viewable area to show the selected channels";
5134
5135 /* API callbacks */
5137 ot->poll = channel_view_poll;
5138
5139 ot->flag = 0;
5140
5141 ot->prop = RNA_def_boolean(ot->srna,
5142 "include_handles",
5143 true,
5144 "Include Handles",
5145 "Include handles of keyframes when calculating extents");
5146
5147 ot->prop = RNA_def_boolean(ot->srna,
5148 "use_preview_range",
5149 true,
5150 "Use Preview Range",
5151 "Ignore frames outside of the preview range");
5152}
5153
5155 wmOperator *op,
5156 const wmEvent *event)
5157{
5158 bAnimContext ac;
5159
5160 if (ANIM_animdata_get_context(C, &ac) == 0) {
5161 return OPERATOR_CANCELLED;
5162 }
5163
5165
5166 if (!window_region) {
5167 return OPERATOR_CANCELLED;
5168 }
5169
5170 ListBase anim_data = {nullptr, nullptr};
5174 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
5175
5176 bAnimListElem *ale;
5177 const int channel_index = animchannels_channel_get(&ac, event->mval);
5178 ale = static_cast<bAnimListElem *>(BLI_findlink(&anim_data, channel_index));
5179 if (ale == nullptr) {
5180 ANIM_animdata_freelist(&anim_data);
5181 return OPERATOR_CANCELLED;
5182 }
5183
5184 float range[2];
5185 const bool use_preview_range = RNA_boolean_get(op->ptr, "use_preview_range");
5186
5187 get_view_range(ac.scene, use_preview_range, range);
5188
5189 rctf bounds;
5190 const bool include_handles = RNA_boolean_get(op->ptr, "include_handles");
5191 const bool found_bounds = get_channel_bounds(&ac, ale, range, include_handles, &bounds);
5192
5193 if (!found_bounds) {
5194 ANIM_animdata_freelist(&anim_data);
5195 BKE_report(op->reports, RPT_WARNING, "No keyframes to focus on");
5196 return OPERATOR_CANCELLED;
5197 }
5198
5199 add_region_padding(C, window_region, &bounds);
5200
5201 if (ac.spacetype == SPACE_ACTION) {
5202 bounds.ymin = window_region->v2d.cur.ymin;
5203 bounds.ymax = window_region->v2d.cur.ymax;
5204 }
5205
5206 const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
5207 UI_view2d_smooth_view(C, window_region, &bounds, smooth_viewtx);
5208
5209 ANIM_animdata_freelist(&anim_data);
5210
5211 return OPERATOR_FINISHED;
5212}
5213
5215{
5216 /* Identifiers */
5217 ot->name = "Frame Channel Under Cursor";
5218 ot->idname = "ANIM_OT_channel_view_pick";
5219 ot->description = "Reset viewable area to show the channel under the cursor";
5220
5221 /* API callbacks */
5223 ot->poll = channel_view_poll;
5224
5225 ot->flag = 0;
5226
5227 ot->prop = RNA_def_boolean(ot->srna,
5228 "include_handles",
5229 true,
5230 "Include Handles",
5231 "Include handles of keyframes when calculating extents");
5232
5233 ot->prop = RNA_def_boolean(ot->srna,
5234 "use_preview_range",
5235 true,
5236 "Use Preview Range",
5237 "Ignore frames outside of the preview range");
5238}
5239
5241 {BEZT_IPO_BEZ, "BEZIER", 0, "Bézier", "New keys will be Bézier"},
5242 {BEZT_IPO_LIN, "LIN", 0, "Linear", "New keys will be linear"},
5243 {BEZT_IPO_CONST, "CONST", 0, "Constant", "New keys will be constant"},
5244 {0, nullptr, 0, nullptr, nullptr},
5245};
5246
5248{
5249 using namespace blender::animrig;
5250 bAnimContext ac;
5251
5252 /* Get editor data. */
5253 if (ANIM_animdata_get_context(C, &ac) == 0) {
5254 return OPERATOR_CANCELLED;
5255 }
5256
5257 ListBase anim_data = {nullptr, nullptr};
5260 size_t anim_data_length = ANIM_animdata_filter(
5261 &ac, &anim_data, eAnimFilter_Flags(filter), ac.data, eAnimCont_Types(ac.datatype));
5262
5263 if (anim_data_length == 0) {
5264 BKE_report(op->reports, RPT_WARNING, "No channels to operate on");
5265 return OPERATOR_CANCELLED;
5266 }
5267
5268 Scene *scene = CTX_data_scene(C);
5269
5270 /* The range will default to the scene or preview range, but only if it hasn't been set before.
5271 * If a range is set here, the redo panel wouldn't work properly because the range would
5272 * constantly be overridden. */
5273 blender::int2 frame_range;
5274 RNA_int_get_array(op->ptr, "range", frame_range);
5275 frame_range[1] = std::max(frame_range[1], frame_range[0]);
5276 const float step = RNA_float_get(op->ptr, "step");
5277 if (frame_range[0] == 0 && frame_range[1] == 0) {
5278 if (scene->r.flag & SCER_PRV_RANGE) {
5279 frame_range = {scene->r.psfra, scene->r.pefra};
5280 }
5281 else {
5282 frame_range = {scene->r.sfra, scene->r.efra};
5283 }
5284 RNA_int_set_array(op->ptr, "range", frame_range);
5285 }
5286
5287 const bool remove_outside_range = RNA_boolean_get(op->ptr, "remove_outside_range");
5288 const BakeCurveRemove remove_existing = remove_outside_range ? BakeCurveRemove::ALL :
5290 const int interpolation_type = RNA_enum_get(op->ptr, "interpolation_type");
5291 const bool bake_modifiers = RNA_boolean_get(op->ptr, "bake_modifiers");
5292
5293 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
5294 FCurve *fcu = static_cast<FCurve *>(ale->data);
5295 if (!fcu->bezt) {
5296 continue;
5297 }
5298 blender::int2 nla_mapped_range = {
5299 int(ANIM_nla_tweakedit_remap(ale, frame_range[0], NLATIME_CONVERT_UNMAP)),
5300 int(ANIM_nla_tweakedit_remap(ale, frame_range[1], NLATIME_CONVERT_UNMAP)),
5301 };
5302 /* Save current state of modifier flags so they can be reapplied after baking. */
5303 blender::Vector<short> modifier_flags;
5304 if (!bake_modifiers) {
5306 modifier_flags.append(modifier->flag);
5308 }
5309 }
5310
5311 bool replace;
5312 const int last_index = BKE_fcurve_bezt_binarysearch_index(
5313 fcu->bezt, nla_mapped_range[1], fcu->totvert, &replace);
5314
5315 /* Since the interpolation of a key defines the curve following it, the last key in the baked
5316 * segment needs to keep the interpolation mode that existed previously so the curve isn't
5317 * changed. */
5318 const char segment_end_interpolation = fcu->bezt[min_ii(last_index, fcu->totvert - 1)].ipo;
5319
5320 bake_fcurve(fcu, nla_mapped_range, step, remove_existing);
5321
5322 if (bake_modifiers) {
5324 }
5325 else {
5326 int modifier_index = 0;
5328 modifier->flag = modifier_flags[modifier_index];
5329 modifier_index++;
5330 }
5331 }
5332
5333 for (int i = 0; i < fcu->totvert; i++) {
5334 BezTriple *key = &fcu->bezt[i];
5335 if (key->vec[1][0] < nla_mapped_range[0]) {
5336 continue;
5337 }
5338 if (key->vec[1][0] > nla_mapped_range[1]) {
5339 fcu->bezt[max_ii(i - 1, 0)].ipo = segment_end_interpolation;
5340 break;
5341 }
5342 key->ipo = interpolation_type;
5343 }
5344 }
5345
5346 ANIM_animdata_freelist(&anim_data);
5348
5349 return OPERATOR_FINISHED;
5350}
5351
5353{
5354 /* Identifiers */
5355 ot->name = "Bake Channels";
5356 ot->idname = "ANIM_OT_channels_bake";
5357 ot->description =
5358 "Create keyframes following the current shape of F-Curves of selected channels";
5359
5360 /* API callbacks */
5361 ot->exec = channels_bake_exec;
5362 ot->poll = channel_view_poll;
5363
5364 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5365 RNA_def_int_array(ot->srna,
5366 "range",
5367 2,
5368 nullptr,
5369 INT_MIN,
5370 INT_MAX,
5371 "Frame Range",
5372 "The range in which to create new keys",
5373 0,
5374 INT_MAX);
5375
5376 RNA_def_float(ot->srna,
5377 "step",
5378 1.0f,
5379 0.01f,
5380 FLT_MAX,
5381 "Frame Step",
5382 "At which interval to add keys",
5383 1.0f,
5384 16.0f);
5385
5386 RNA_def_boolean(ot->srna,
5387 "remove_outside_range",
5388 false,
5389 "Remove Outside Range",
5390 "Removes keys outside the given range, leaving only the newly baked");
5391
5392 RNA_def_enum(ot->srna,
5393 "interpolation_type",
5396 "Interpolation Type",
5397 "Choose the interpolation type with which new keys will be added");
5398
5399 RNA_def_boolean(ot->srna,
5400 "bake_modifiers",
5401 true,
5402 "Bake Modifiers",
5403 "Bake Modifiers into keyframes and delete them after");
5404}
5405
5407{
5408 using namespace blender::animrig;
5409 bAnimContext ac;
5410
5411 /* Get editor data. */
5412 if (ANIM_animdata_get_context(C, &ac) == 0) {
5413 return OPERATOR_CANCELLED;
5414 }
5415
5416 ListBase anim_data = {nullptr, nullptr};
5419
5420 size_t anim_data_length = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
5421
5422 if (anim_data_length == 0) {
5423 BKE_report(op->reports, RPT_WARNING, "No channels to operate on");
5424 return OPERATOR_CANCELLED;
5425 }
5426
5428 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
5429 if (ale->type != ANIMTYPE_ACTION_SLOT) {
5430 continue;
5431 }
5432 BLI_assert(GS(ale->fcurve_owner_id->name) == ID_AC);
5433 bAction *owning_action = reinterpret_cast<bAction *>(ale->fcurve_owner_id);
5434 slots.append({reinterpret_cast<Slot *>(ale->data), owning_action});
5435 }
5436 ANIM_animdata_freelist(&anim_data);
5437
5438 if (slots.size() == 0) {
5439 BKE_report(op->reports, RPT_WARNING, "None of the selected channels is an Action Slot");
5440 return OPERATOR_CANCELLED;
5441 }
5442
5443 /* If multiple slots are selected they are moved to the new action together. In that case it is
5444 * hard to determine a name, so a constant default is used. */
5445 Action *target_action;
5446 Main *bmain = CTX_data_main(C);
5447 if (slots.size() == 1) {
5448 char actname[MAX_ID_NAME - 2];
5449 SNPRINTF_UTF8(actname, DATA_("%sAction"), slots[0].first->identifier + 2);
5450 target_action = &action_add(*bmain, actname);
5451 }
5452 else {
5453 target_action = &action_add(*bmain, DATA_("CombinedAction"));
5454 }
5455
5456 Layer &layer = target_action->layer_add(std::nullopt);
5457 layer.strip_add(*target_action, Strip::Type::Keyframe);
5458
5459 for (std::pair<Slot *, bAction *> &slot_data : slots) {
5460 Action &source_action = slot_data.second->wrap();
5461 move_slot(*bmain, *slot_data.first, source_action, *target_action);
5463 }
5464
5468
5469 return OPERATOR_FINISHED;
5470}
5471
5473{
5474 Scene *scene = CTX_data_scene(C);
5475 ViewLayer *view_layer = CTX_data_view_layer(C);
5476 ScrArea *area = CTX_wm_area(C);
5477 bAction *action = ANIM_active_action_from_area(scene, view_layer, area);
5478
5479 if (!action) {
5480 CTX_wm_operator_poll_msg_set(C, "No active action to operate on");
5481 return false;
5482 }
5483 if (!action->wrap().is_action_layered()) {
5484 CTX_wm_operator_poll_msg_set(C, "Active action is not layered");
5485 return false;
5486 }
5487 return true;
5488}
5489
5491{
5492 ot->name = "Move Slots to new Action";
5493 ot->idname = "ANIM_OT_slot_channels_move_to_new_action";
5494 ot->description = "Move the selected slots into a newly created action";
5495
5498
5499 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5500}
5501
5503{
5504 using namespace blender::animrig;
5505 Object *active_object = CTX_data_active_object(C);
5506 /* Checked by the poll function. */
5507 BLI_assert(active_object != nullptr);
5508
5509 Action *action = get_action(active_object->id);
5510 /* Also checked by the poll function. */
5511 BLI_assert(action != nullptr);
5512
5513 Main *bmain = CTX_data_main(C);
5514 int created_actions = 0;
5515 while (action->slot_array_num) {
5516 Slot *slot = action->slot(action->slot_array_num - 1);
5517 char actname[MAX_ID_NAME - 2];
5518 SNPRINTF_UTF8(actname, DATA_("%sAction"), slot->identifier + 2);
5519 Action &target_action = action_add(*bmain, actname);
5520 created_actions++;
5521 Layer &layer = target_action.layer_add(std::nullopt);
5522 layer.strip_add(target_action, Strip::Type::Keyframe);
5523 move_slot(*bmain, *slot, *action, target_action);
5525 }
5526
5527 BKE_reportf(op->reports,
5528 RPT_INFO,
5529 "Separated %s into %i new actions",
5530 action->id.name + 2,
5531 created_actions);
5532
5536
5537 return OPERATOR_FINISHED;
5538}
5539
5541{
5542 Object *active_object = CTX_data_active_object(C);
5543 if (!active_object) {
5544 CTX_wm_operator_poll_msg_set(C, "No active object");
5545 return false;
5546 }
5547
5549 if (!action) {
5550 CTX_wm_operator_poll_msg_set(C, "Active object isn't animated");
5551 return false;
5552 }
5553 if (!action->is_action_layered()) {
5554 return false;
5555 }
5556 return true;
5557}
5558
5560{
5561 ot->name = "Separate Slots";
5562 ot->idname = "ANIM_OT_separate_slots";
5563 ot->description =
5564 "Move all slots of the action on the active object into newly created, separate actions. "
5565 "All users of those slots will be reassigned to the new actions. The current action won't "
5566 "be deleted but will be empty and might end up having zero users";
5567
5568 ot->exec = separate_slots_exec;
5569 ot->poll = separate_slots_poll;
5570
5571 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5572}
5573
5578 wmWindow **r_win,
5579 ScrArea **r_area,
5580 ARegion **r_region)
5581{
5582 LISTBASE_FOREACH (wmWindow *, win, &CTX_wm_manager(C)->windows) {
5583 bScreen *screen = WM_window_get_active_screen(win);
5584
5585 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
5586 if (area->spacetype != SPACE_GRAPH) {
5587 continue;
5588 }
5590 if (!region) {
5591 continue;
5592 }
5593
5594 *r_win = win;
5595 *r_area = area;
5596 *r_region = region;
5597 return true;
5598 }
5599 }
5600 return false;
5601}
5602
5603static void deselect_all_fcurves(bAnimContext *ac, const bool hide)
5604{
5605 ListBase anim_data = {nullptr, nullptr};
5608 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, eAnimCont_Types(ac->datatype));
5609
5610 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
5611 FCurve *fcu = static_cast<FCurve *>(ale->key_data);
5612 fcu->flag &= ~FCURVE_SELECTED;
5613 fcu->flag &= ~FCURVE_ACTIVE;
5614 if (hide) {
5615 fcu->flag &= ~FCURVE_VISIBLE;
5616 }
5617 }
5618
5619 ANIM_animdata_freelist(&anim_data);
5620}
5621
5623{
5624 ListBase anim_data = {nullptr, nullptr};
5625 if (ac->sl->spacetype != SPACE_GRAPH) {
5626 return 0;
5627 }
5628 SpaceGraph *sipo = reinterpret_cast<SpaceGraph *>(ac->sl);
5630 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, eAnimCont_Types(ac->datatype));
5631
5632 /* Adding FCurves to a map for quicker lookup times. */
5633 blender::Map<FCurve *, bool> filtered_fcurves;
5634 LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
5635 FCurve *fcu = static_cast<FCurve *>(ale->key_data);
5636 filtered_fcurves.add(fcu, true);
5637 }
5638
5639 int hidden_fcurve_count = fcurves.size();
5640 for (FCurve *fcu : fcurves) {
5641 if (filtered_fcurves.contains(fcu)) {
5642 hidden_fcurve_count--;
5643 }
5644 }
5645 ANIM_animdata_freelist(&anim_data);
5646 return hidden_fcurve_count;
5647}
5648
5650 ID *id, PointerRNA *ptr, PropertyRNA *prop, const bool whole_array, const int index)
5651{
5652 using namespace blender;
5653
5654 AnimData *anim_data = BKE_animdata_from_id(id);
5655 if (anim_data == nullptr) {
5656 return Vector<FCurve *>();
5657 }
5658
5659 const std::optional<std::string> path = RNA_path_from_ID_to_property(ptr, prop);
5660
5662 if (RNA_property_array_check(prop) && whole_array) {
5663 const int length = RNA_property_array_length(ptr, prop);
5664 for (int i = 0; i < length; i++) {
5666 anim_data, path->c_str(), i, nullptr, nullptr);
5667 if (fcurve != nullptr) {
5668 fcurves.append(fcurve);
5669 }
5670 }
5671 }
5672 else {
5674 anim_data, path->c_str(), index, nullptr, nullptr);
5675 if (fcurve != nullptr) {
5676 fcurves.append(fcurve);
5677 }
5678 }
5679 return fcurves;
5680}
5681
5683 Scene *scene,
5684 ID *id,
5685 const blender::Span<FCurve *> fcurves)
5686{
5687 rctf bounds;
5688 bounds.xmin = INFINITY;
5689 bounds.xmax = -INFINITY;
5690 bounds.ymin = INFINITY;
5691 bounds.ymax = -INFINITY;
5692
5693 if (space_link->spacetype != SPACE_GRAPH) {
5694 return bounds;
5695 }
5696
5697 AnimData *anim_data = BKE_animdata_from_id(id);
5698 if (anim_data == nullptr) {
5699 return bounds;
5700 }
5701
5702 float frame_range[2];
5703 get_view_range(scene, true, frame_range);
5704 float mapped_frame_range[2];
5705 mapped_frame_range[0] = BKE_nla_tweakedit_remap(
5706 anim_data, frame_range[0], NLATIME_CONVERT_UNMAP);
5707 mapped_frame_range[1] = BKE_nla_tweakedit_remap(
5708 anim_data, frame_range[1], NLATIME_CONVERT_UNMAP);
5709
5710 const bool include_handles = false;
5711
5712 for (FCurve *fcurve : fcurves) {
5713 fcurve->flag |= (FCURVE_SELECTED | FCURVE_VISIBLE);
5714 rctf fcu_bounds;
5716 fcurve, space_link, scene, id, include_handles, mapped_frame_range, &fcu_bounds);
5717
5718 if (BLI_rctf_is_valid(&fcu_bounds)) {
5719 BLI_rctf_union(&bounds, &fcu_bounds);
5720 }
5721 }
5722
5723 return bounds;
5724}
5725
5727 blender::Span<PointerRNA> selection,
5728 PropertyRNA *prop,
5729 const blender::StringRefNull id_to_prop_path,
5730 const int index,
5731 const bool whole_array,
5732 int *r_filtered_fcurve_count)
5733{
5734 rctf bounds;
5735 bounds.xmin = INFINITY;
5736 bounds.xmax = -INFINITY;
5737 bounds.ymin = INFINITY;
5738 bounds.ymax = -INFINITY;
5739
5740 for (const PointerRNA &selected : selection) {
5741 ID *selected_id = selected.owner_id;
5742 if (!BKE_animdata_id_is_animated(selected_id)) {
5743 continue;
5744 }
5745 PointerRNA resolved_ptr;
5746 PropertyRNA *resolved_prop;
5747 if (!id_to_prop_path.is_empty()) {
5748 const bool resolved = RNA_path_resolve_property(
5749 &selected, id_to_prop_path.c_str(), &resolved_ptr, &resolved_prop);
5750 if (!resolved) {
5751 continue;
5752 }
5753 }
5754 else {
5755 resolved_ptr = selected;
5756 resolved_prop = prop;
5757 }
5759 selected_id, &resolved_ptr, resolved_prop, whole_array, index);
5760 *r_filtered_fcurve_count += count_fcurves_hidden_by_filter(ac, fcurves);
5761 rctf fcu_bounds = calculate_fcurve_bounds_and_unhide(ac->sl, ac->scene, selected_id, fcurves);
5762 if (BLI_rctf_is_valid(&fcu_bounds)) {
5763 BLI_rctf_union(&bounds, &fcu_bounds);
5764 }
5765 }
5766
5767 return bounds;
5768}
5769
5771{
5772 PointerRNA button_ptr = {};
5773 PropertyRNA *button_prop = nullptr;
5774 uiBut *but;
5775 int index;
5776
5777 if (!(but = UI_context_active_but_prop_get(C, &button_ptr, &button_prop, &index))) {
5778 /* Pass event on if no active button found. */
5780 }
5781
5783
5785
5786 struct {
5787 wmWindow *win;
5788 ScrArea *area;
5789 ARegion *region;
5790 } wm_context_prev = {nullptr}, wm_context_temp = {nullptr};
5791
5792 bool path_from_id;
5793 std::optional<std::string> id_to_prop_path;
5794 const bool selected_list_success = UI_context_copy_to_selected_list(
5795 C, &button_ptr, button_prop, &selection, &path_from_id, &id_to_prop_path);
5796
5798 C, &wm_context_temp.win, &wm_context_temp.area, &wm_context_temp.region))
5799 {
5800 BKE_report(op->reports, RPT_WARNING, "No open Graph Editor window found");
5801 retval = OPERATOR_CANCELLED;
5802 }
5803 else {
5804 wm_context_prev.win = CTX_wm_window(C);
5805 wm_context_prev.area = CTX_wm_area(C);
5806 wm_context_prev.region = CTX_wm_region(C);
5807
5808 CTX_wm_window_set(C, wm_context_temp.win);
5809 CTX_wm_area_set(C, wm_context_temp.area);
5810 CTX_wm_region_set(C, wm_context_temp.region);
5811
5812 bAnimContext ac;
5813 if (!ANIM_animdata_get_context(C, &ac)) {
5814 /* This might never be called since we are manually setting the Graph Editor just before. */
5815 BKE_report(op->reports, RPT_ERROR, "Cannot create the Animation Context");
5816 retval = OPERATOR_CANCELLED;
5817 }
5818 else {
5819 const bool isolate = RNA_boolean_get(op->ptr, "isolate");
5820 /* The index can be less than 0 e.g. on color properties. */
5821 const bool whole_array = RNA_boolean_get(op->ptr, "all") || index < 0;
5822
5823 deselect_all_fcurves(&ac, isolate);
5824
5825 rctf bounds;
5826 bounds.xmin = INFINITY;
5827 bounds.xmax = -INFINITY;
5828 bounds.ymin = INFINITY;
5829 bounds.ymax = -INFINITY;
5830 int filtered_fcurve_count = 0;
5831 if (selected_list_success && !selection.is_empty()) {
5832 rctf selection_bounds = calculate_selection_fcurve_bounds(&ac,
5833 selection,
5834 button_prop,
5835 id_to_prop_path.value_or(""),
5836 index,
5837 whole_array,
5838 &filtered_fcurve_count);
5839 if (BLI_rctf_is_valid(&selection_bounds)) {
5840 BLI_rctf_union(&bounds, &selection_bounds);
5841 }
5842 }
5843
5844 /* The object to which the button belongs might not be selected, or selectable. */
5846 button_ptr.owner_id, &button_ptr, button_prop, whole_array, index);
5847 filtered_fcurve_count += count_fcurves_hidden_by_filter(&ac, button_fcurves);
5849 ac.sl, ac.scene, button_ptr.owner_id, button_fcurves);
5850 if (BLI_rctf_is_valid(&button_bounds)) {
5851 BLI_rctf_union(&bounds, &button_bounds);
5852 }
5853
5854 if (filtered_fcurve_count > 0) {
5855 BKE_report(op->reports,
5857 "One or more F-Curves are not visible due to filter settings");
5858 }
5859
5860 if (!BLI_rctf_is_valid(&bounds)) {
5861 BKE_report(op->reports, RPT_ERROR, "F-Curves have no valid size");
5862 retval = OPERATOR_CANCELLED;
5863 }
5864 else {
5865 ARegion *region = wm_context_temp.region;
5866 ScrArea *area = wm_context_temp.area;
5867 add_region_padding(C, region, &bounds);
5868
5869 const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
5870 UI_view2d_smooth_view(C, region, &bounds, smooth_viewtx);
5871
5872 /* This ensures the channel list updates. */
5873 ED_area_tag_redraw(area);
5874 }
5875 }
5876
5877 CTX_wm_window_set(C, wm_context_prev.win);
5878 CTX_wm_area_set(C, wm_context_prev.area);
5879 CTX_wm_region_set(C, wm_context_prev.region);
5880 }
5881
5882 return retval;
5883}
5884
5886{
5887 /* Identifiers */
5888 ot->name = "View In Graph Editor";
5889 ot->idname = "ANIM_OT_view_curve_in_graph_editor";
5890 ot->description = "Frame the property under the cursor in the Graph Editor";
5891
5892 /* API callbacks */
5894
5895 RNA_def_boolean(ot->srna,
5896 "all",
5897 false,
5898 "Show All",
5899 "Frame the whole array property instead of only the index under the cursor");
5900
5901 RNA_def_boolean(ot->srna,
5902 "isolate",
5903 false,
5904 "Isolate",
5905 "Hides all F-Curves other than the ones being framed");
5906}
5907
5909
5910/* -------------------------------------------------------------------- */
5913
5915{
5918
5922
5924
5928
5932
5934
5935 /* XXX does this need to be a separate operator? */
5937
5939
5942
5944
5946
5949
5951
5954}
5955
5957{
5958 /* TODO: check on a poll callback for this, to get hotkeys into menus. */
5959
5960 WM_keymap_ensure(keyconf, "Animation Channels", SPACE_EMPTY, RGN_TYPE_WINDOW);
5961}
5962
Functions and classes to work with Actions.
Functions for backward compatibility with the legacy Action API.
Functions to work with AnimData.
Functions to modify FCurves.
Blender kernel action and pose functionality.
void action_groups_add_channel(bAction *act, bActionGroup *agrp, FCurve *fcurve)
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
void action_groups_remove_channel(bAction *act, FCurve *fcu)
bActionGroup * action_groups_add_new(bAction *act, const char name[])
bool BKE_animdata_id_is_animated(const ID *id)
Definition anim_data.cc:238
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:83
void BKE_animdata_free(ID *id, bool do_id_user)
Definition anim_data.cc:188
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
ScrArea * CTX_wm_area(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
SpaceLink * CTX_wm_space_data(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
void CTX_wm_window_set(bContext *C, wmWindow *win)
Main * CTX_data_main(const bContext *C)
void CTX_wm_area_set(bContext *C, ScrArea *area)
void CTX_wm_region_set(bContext *C, ARegion *region)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
wmMsgBus * CTX_wm_message_bus(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
int BKE_fcurve_bezt_binarysearch_index(const BezTriple array[], float frame, int arraylen, bool *r_replace)
FCurve * BKE_animadata_fcurve_find_by_rna_path(AnimData *animdata, const char *rna_path, const int rna_index, bAction **r_action, bool *r_driven)
void free_fmodifiers(ListBase *modifiers)
void BKE_fcurve_free(FCurve *fcu)
bool BKE_fcurve_calc_bounds(const FCurve *fcu, bool selected_keys_only, bool include_handles, const float frame_range[2], rctf *r_bounds)
@ G_DEBUG
void BKE_gpencil_layer_active_set(struct bGPdata *gpd, struct bGPDlayer *active)
void BKE_gpencil_free_data(struct bGPdata *gpd, bool free_all)
void BKE_gpencil_layer_delete(struct bGPdata *gpd, struct bGPDlayer *gpl)
Low-level operations for grease pencil.
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
ListBase * BKE_view_layer_object_bases_get(ViewLayer *view_layer)
void BKE_id_free_us(Main *bmain, void *idv) ATTR_NONNULL()
void BKE_mask_layer_remove(struct Mask *mask, struct MaskLayer *masklay)
bool BKE_nlastrip_controlcurve_remove(NlaStrip *strip, FCurve *fcurve)
@ NLATIME_CONVERT_MAP
Definition BKE_nla.hh:552
@ NLATIME_CONVERT_UNMAP
Definition BKE_nla.hh:549
float BKE_nla_tweakedit_remap(AnimData *adt, float cframe, eNlaTime_ConvertModes mode)
bool BKE_nlatrack_is_nonlocal_in_liboverride(const ID *id, const NlaTrack *nlt)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
ARegion * BKE_area_find_region_type(const ScrArea *area, int region_type)
Definition screen.cc:846
#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
#define ATTR_FALLTHROUGH
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
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 void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_insertlinkafter(ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:332
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void * BLI_findptr(const struct ListBase *listbase, const void *ptr, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void BLI_insertlinkbefore(ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:371
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
void BLI_rctf_union(struct rctf *rct_a, const struct rctf *rct_b)
bool BLI_rctf_is_valid(const struct rctf *rect)
void BLI_rctf_pad_y(struct rctf *rect, float boundary_size, float pad_min, float pad_max)
Definition rct.cc:689
void BLI_rctf_scale(rctf *rect, float scale)
Definition rct.cc:677
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
#define SNPRINTF_UTF8(dst, format,...)
#define STRNCPY_UTF8(dst, src)
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define DATA_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1054
@ ID_RECALC_ANIMATION
Definition DNA_ID.h:1077
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ ID_RECALC_ANIMATION_NO_FLUSH
Definition DNA_ID.h:1176
#define MAX_ID_NAME
Definition DNA_ID.h:373
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:705
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:730
@ ID_AC
@ ID_OB
@ ADS_FILTER_ONLYSEL
@ AGRP_TEMP
@ AGRP_ACTIVE
@ AGRP_SELECTED
@ AGRP_EXPANDED_G
@ AGRP_EXPANDED
@ AGRP_MOVED
@ SACTCONT_ACTION
@ SACTCONT_DOPESHEET
@ ADT_NLA_SKEYS_COLLAPSED
@ ADT_UI_ACTIVE
@ ADT_UI_SELECTED
@ FMODIFIER_FLAG_MUTED
@ DRIVER_FLAG_INVALID
@ FCURVE_DISABLED
@ FCURVE_ACTIVE
@ FCURVE_SELECTED
@ FCURVE_VISIBLE
@ NLATRACK_ACTIVE
@ NLATRACK_SELECTED
@ NLATRACK_OVERRIDELIBRARY_LOCAL
@ BEZT_IPO_CONST
@ BEZT_IPO_BEZ
@ BEZT_IPO_LIN
#define MAX_NAME
Definition DNA_defs.h:50
@ GP_LAYER_TREE_NODE_SELECT
@ GREASE_PENCIL_ANIM_CHANNEL_EXPANDED
@ KEYBLOCK_SEL
@ MASK_ANIMF_EXPAND
@ MASK_LAYERFLAG_SELECT
Object is a sort of wrapper for general info.
@ OB_ARMATURE
@ SCER_PRV_RANGE
@ SCE_DS_SELECTED
@ SCE_NLA_EDIT_ON
#define BASE_SELECTABLE(v3d, base)
@ RGN_TYPE_CHANNELS
@ RGN_TYPE_WINDOW
@ RGN_FLAG_HIDDEN
@ SPACE_ACTION
@ SPACE_NLA
@ SPACE_EMPTY
@ SPACE_GRAPH
@ SIPO_MODE_ANIMATION
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
eAnimChannels_SetFlag
@ ACHANNEL_SETFLAG_TOGGLE
@ ACHANNEL_SETFLAG_EXTEND_RANGE
@ ACHANNEL_SETFLAG_ADD
@ ACHANNEL_SETFLAG_INVERT
@ ACHANNEL_SETFLAG_CLEAR
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 SEL_AGRP(agrp)
#define SEL_GPL(gpl)
#define NLATRACK_FIRST_TOP(ac)
#define EXPANDED_AGRP(ac, agrp)
@ 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 NLATRACK_STEP(snla)
@ ANIM_UPDATE_DEPS
eAnimCont_Types
@ ANIMCONT_DRIVERS
@ ANIMCONT_FCURVES
@ ANIMCONT_NLA
@ ANIMCONT_MASK
@ ANIMCONT_SHAPEKEY
@ ANIMCONT_TIMELINE
@ ANIMCONT_DOPESHEET
@ ANIMCONT_ACTION
@ ANIMCONT_GPENCIL
@ ANIMCONT_CHANNEL
#define SEL_FCU(fcu)
eAnimChannel_Settings
@ ACHANNEL_SETTING_ALWAYS_VISIBLE
@ ACHANNEL_SETTING_MUTE
@ ACHANNEL_SETTING_PROTECT
@ ACHANNEL_SETTING_VISIBLE
@ ACHANNEL_SETTING_EXPAND
@ ACHANNEL_SETTING_SELECT
#define ACHANNEL_SET_FLAG(channel, smode, sflag)
#define EXPANDED_DRVD(adt)
#define SEL_NLT(nlt)
#define NLATRACK_NAMEWIDTH
eAnimFilter_Flags
@ ANIMFILTER_FOREDIT
@ ANIMFILTER_ANIMDATA
@ ANIMFILTER_DATA_VISIBLE
@ ANIMFILTER_CURVE_VISIBLE
@ ANIMFILTER_LIST_VISIBLE
@ ANIMFILTER_LIST_CHANNELS
@ ANIMFILTER_NODUPLIS
@ ANIMFILTER_FCURVESONLY
@ ANIMFILTER_SEL
@ SELECT_INVERT
@ SELECT_EXTEND_RANGE
@ SELECT_SUBTRACT
@ SELECT_REPLACE
@ SELECT_ADD
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:693
bool ED_operator_graphedit_active(bContext *C)
bool ED_operator_action_active(bContext *C)
void ED_region_toggle_hidden(bContext *C, ARegion *region)
Definition area.cc:2373
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:618
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
bool UI_textbutton_activate_rna(const bContext *C, ARegion *region, const void *rna_poin_data, const char *rna_prop_id)
uiBut * UI_context_active_but_prop_get(const bContext *C, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
bool UI_context_copy_to_selected_list(bContext *C, PointerRNA *ptr, PropertyRNA *prop, blender::Vector< PointerRNA > *r_lb, bool *r_use_path_from_id, std::optional< std::string > *r_path)
void UI_view2d_smooth_view(const bContext *C, ARegion *region, const rctf *cur, int smooth_viewtx)
#define UI_MARKER_MARGIN_Y
Definition UI_view2d.hh:478
#define V2D_SCROLL_HANDLE_HEIGHT
Definition UI_view2d.hh:67
#define UI_TIME_SCRUB_MARGIN_Y
Definition UI_view2d.hh:479
void UI_view2d_listview_view_to_cell(float columnwidth, float rowheight, float startx, float starty, float viewx, float viewy, int *r_column, int *r_row)
Definition view2d.cc:1621
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1668
#define ND_NLA_ACTCHANGE
Definition WM_types.hh:498
#define ND_DATA
Definition WM_types.hh:509
#define NC_ANIMATION
Definition WM_types.hh:388
#define ND_SPACE_PROPERTIES
Definition WM_types.hh:529
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NA_EDITED
Definition WM_types.hh:584
#define NA_REMOVED
Definition WM_types.hh:587
#define ND_NLA_ORDER
Definition WM_types.hh:500
#define NC_GPENCIL
Definition WM_types.hh:399
#define ND_NLA
Definition WM_types.hh:497
#define NA_RENAME
Definition WM_types.hh:588
#define ND_KEYFRAME
Definition WM_types.hh:494
#define ND_ANIMCHAN
Definition WM_types.hh:496
#define NC_SPACE
Definition WM_types.hh:392
#define NA_SELECTED
Definition WM_types.hh:589
void ANIM_channel_setting_set(bAnimContext *ac, bAnimListElem *ale, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode)
float ANIM_UI_get_channel_name_width()
float ANIM_UI_get_channel_step()
float ANIM_UI_get_first_channel_top(View2D *v2d)
short ANIM_channel_setting_get(bAnimContext *ac, bAnimListElem *ale, eAnimChannel_Settings setting)
const bAnimChannelType * ANIM_channel_get_typeinfo(const bAnimListElem *ale)
static bool slot_channels_move_to_new_action_poll(bContext *C)
static bool get_gpencil_bounds(bGPDlayer *gpl, const float range[2], rctf *r_bounds)
static wmOperatorStatus animchannels_select_filter_invoke(bContext *C, wmOperator *op, const wmEvent *)
static int click_select_channel_grease_pencil_datablock(bAnimListElem *ale)
static void rearrange_gpencil_channels(bAnimContext *ac, eRearrangeAnimChan_Mode mode)
static void anim_flush_channel_setting_down(bAnimContext *ac, const eAnimChannel_Settings setting, const eAnimChannels_SetFlag mode, bAnimListElem *const match, const int matchLevel)
void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode)
static void box_select_anim_channels(bAnimContext *ac, const rcti &rect, short selectmode)
void ANIM_anim_channels_select_set(bAnimContext *ac, eAnimChannels_SetFlag sel)
static void add_region_padding(bContext *C, ARegion *region, rctf *bounds)
static bool animchannels_enable_poll(bContext *C)
static void join_groups_action_temp(bAction *act)
static void tag_update_animation_element(bAnimListElem *ale)
static void ANIM_OT_channel_select_keys(wmOperatorType *ot)
static wmOperatorStatus animchannels_ungroup_exec(bContext *C, wmOperator *)
@ REORDER_ISLAND_UNTOUCHABLE
@ REORDER_ISLAND_MOVED
@ REORDER_ISLAND_SELECTED
@ REORDER_ISLAND_HIDDEN
static bool animchannel_has_active_of_type(bAnimContext *ac, const eAnim_ChannelType type)
static bool get_normalized_fcurve_bounds(FCurve *fcu, SpaceLink *space_link, Scene *scene, ID *id, const bool include_handles, const float range[2], rctf *r_bounds)
void ED_keymap_animchannels(wmKeyConfig *keyconf)
static void split_groups_action_temp(bAction *act, bActionGroup *tgrp)
static bool animchannels_select_filter_poll(bContext *C)
static const EnumPropertyItem prop_animchannel_rearrange_types[]
static bool animchannels_delete_containers(const bContext *C, bAnimContext *ac)
static void setflag_anim_channels(bAnimContext *ac, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode, bool onlysel, bool flush)
static void get_view_range(Scene *scene, const bool use_preview_range, float r_range[2])
static void rearrange_animchannels_filter_visible(ListBase *anim_data_visible, bAnimContext *ac, const eAnim_ChannelType type, const eAnimFilter_Flags additional_filters=eAnimFilter_Flags(0))
static void ANIM_OT_channels_clean_empty(wmOperatorType *ot)
static bool rearrange_island_bottom(ListBase *list, tReorderChannelIsland *island)
static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *srcList, Link *channel, eAnim_ChannelType type, const bool is_hidden)
static wmOperatorStatus animchannels_delete_exec(bContext *C, wmOperator *)
static void select_pchan_for_action_group(bAnimContext *ac, bActionGroup *agrp, bAnimListElem *ale, const bool change_active)
static bool rearrange_island_up(ListBase *list, tReorderChannelIsland *island)
bool(*)(ListBase *list, tReorderChannelIsland *island) AnimChanRearrangeFp
static void rearrange_nla_tracks(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode)
static bool select_anim_channel_keys(bAnimContext *ac, int channel_index, bool extend)
static bool get_channel_bounds(bAnimContext *ac, bAnimListElem *ale, const float range[2], const bool include_handles, rctf *r_bounds)
void ED_anim_ale_fcurve_delete(bAnimContext &ac, bAnimListElem &ale)
static wmOperatorStatus animchannels_channel_select_keys_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus animchannels_box_select_exec(bContext *C, wmOperator *op)
static wmOperatorStatus view_curve_in_graph_editor_exec(bContext *C, wmOperator *op)
static wmOperatorStatus animchannels_setflag_exec(bContext *C, wmOperator *op)
static wmOperatorStatus animchannels_rearrange_exec(bContext *C, wmOperator *op)
static int click_select_channel_maskdatablock(bAnimListElem *ale)
static int click_select_channel_object(bContext *C, bAnimContext *ac, bAnimListElem *ale, const short selectmode)
static void anim_flush_channel_setting_up(bAnimContext *ac, const eAnimChannel_Settings setting, const eAnimChannels_SetFlag mode, bAnimListElem *const match, const int matchLevel)
static void ANIM_OT_channels_delete(wmOperatorType *ot)
static int animchannels_channel_get(bAnimContext *ac, const int mval[2])
static rctf calculate_fcurve_bounds_and_unhide(SpaceLink *space_link, Scene *scene, ID *id, const blender::Span< FCurve * > fcurves)
void ANIM_frame_channel_y_extents(bContext *C, bAnimContext *ac)
static wmOperatorStatus animchannels_select_filter_modal(bContext *C, wmOperator *, const wmEvent *)
static int click_select_channel_action_slot(bAnimContext *ac, bAnimListElem *ale, short selectmode)
static int click_select_channel_group(bAnimContext *ac, bAnimListElem *ale, const short selectmode, const int filter)
static void rearrange_driver_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode)
static int click_select_channel_grease_pencil_layer_group(bContext *C, bAnimListElem *ale)
static void rearrange_animchannel_flatten_islands(ListBase *islands, ListBase *srcList)
static const EnumPropertyItem prop_animchannel_settings_types[]
static void ANIM_OT_slot_channels_move_to_new_action(wmOperatorType *ot)
bool ANIM_is_active_channel(bAnimListElem *ale)
static bool animedit_poll_channels_active(bContext *C)
static void ANIM_OT_separate_slots(wmOperatorType *ot)
static bool rearrange_layered_action_slots(bAnimContext *ac, const eRearrangeAnimChan_Mode mode)
static ListBase anim_channels_for_selection(bAnimContext *ac)
static wmOperatorStatus animchannels_group_exec(bContext *C, wmOperator *op)
static void ANIM_OT_channels_expand(wmOperatorType *ot)
static wmOperatorStatus separate_slots_exec(bContext *C, wmOperator *op)
static wmOperatorStatus animchannels_selectall_exec(bContext *C, wmOperator *op)
static void ANIM_OT_channels_rename(wmOperatorType *ot)
static void deselect_all_fcurves(bAnimContext *ac, const bool hide)
static int click_select_channel_shapekey(bAnimContext *ac, bAnimListElem *ale, const short selectmode)
static void ANIM_OT_view_curve_in_graph_editor(wmOperatorType *ot)
static bool animedit_poll_channels_nla_tweakmode_off(bContext *C)
void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datatype, eAnimFilter_Flags filter, void *channel_data, eAnim_ChannelType channel_type)
static wmOperatorStatus animchannels_clean_empty_exec(bContext *C, wmOperator *)
static bool separate_slots_poll(bContext *C)
static void animchannel_select_range(bAnimContext *ac, bAnimListElem *cursor_elem)
static wmOperatorStatus graphkeys_channel_view_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void animchannels_group_channels(bAnimContext *ac, bAnimListElem *adt_ref, const char name[])
static wmOperatorStatus slot_channels_move_to_new_action_exec(bContext *C, wmOperator *op)
static bool rename_anim_channels(bAnimContext *ac, int channel_index)
static int click_select_channel_nlacontrols(bAnimListElem *ale)
static bool rearrange_island_down(ListBase *list, tReorderChannelIsland *island)
static void ANIM_OT_channels_select_box(wmOperatorType *ot)
static void ANIM_OT_channels_editable_toggle(wmOperatorType *ot)
static blender::Vector< FCurve * > get_fcurves_of_property(ID *id, PointerRNA *ptr, PropertyRNA *prop, const bool whole_array, const int index)
static wmOperatorStatus channels_bake_exec(bContext *C, wmOperator *op)
static bool rearrange_island_top(ListBase *list, tReorderChannelIsland *island)
static wmOperatorStatus animchannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void ANIM_OT_channels_fcurves_enable(wmOperatorType *ot)
static void ANIM_OT_channels_ungroup(wmOperatorType *ot)
static void rearrange_action_channels(bAnimContext *ac, bAction *act, eRearrangeAnimChan_Mode mode)
static void ANIM_OT_channels_view_selected(wmOperatorType *ot)
static int click_select_channel_gplayer(bContext *C, bAnimContext *ac, bAnimListElem *ale, const short selectmode, const int filter)
static bool rearrange_animchannel_islands(ListBase *list, AnimChanRearrangeFp rearrange_func, eRearrangeAnimChan_Mode mode, eAnim_ChannelType type, ListBase *anim_data_visible)
static void ANIM_OT_channels_setting_enable(wmOperatorType *ot)
static int click_select_channel_grease_pencil_layer(bContext *C, bAnimContext *ac, bAnimListElem *ale, const short selectmode, const int)
static const EnumPropertyItem channel_bake_key_options[]
static int mouse_anim_channels(bContext *C, bAnimContext *ac, const int channel_index, short selectmode)
static void ANIM_OT_channels_setting_toggle(wmOperatorType *ot)
static bool context_find_graph_editor(bContext *C, wmWindow **r_win, ScrArea **r_area, ARegion **r_region)
static bool animchannels_grouping_poll(bContext *C)
static void rearrange_layered_action_channel_groups(bAnimContext *ac, blender::animrig::Action &action, const eRearrangeAnimChan_Mode mode)
static wmOperatorStatus graphkeys_view_selected_channels_exec(bContext *C, wmOperator *op)
static void ANIM_OT_channels_move(wmOperatorType *ot)
static AnimChanRearrangeFp rearrange_get_mode_func(eRearrangeAnimChan_Mode mode)
static bool channel_view_poll(bContext *C)
void ANIM_anim_channels_select_toggle(bAnimContext *ac)
static void rearrange_nla_control_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode)
static AnimChanRearrangeFp rearrange_gpencil_get_mode_func(eRearrangeAnimChan_Mode mode)
static rctf calculate_selection_fcurve_bounds(bAnimContext *ac, blender::Span< PointerRNA > selection, PropertyRNA *prop, const blender::StringRefNull id_to_prop_path, const int index, const bool whole_array, int *r_filtered_fcurve_count)
static bool rearrange_island_ok(tReorderChannelIsland *island)
static void ANIM_OT_channels_bake(wmOperatorType *ot)
static void ANIM_OT_channels_group(wmOperatorType *ot)
static void rearrange_layered_action_fcurves(bAnimContext *ac, blender::animrig::Action &action, const eRearrangeAnimChan_Mode mode)
static void ANIM_OT_channels_select_filter(wmOperatorType *ot)
static int click_select_channel_dummy(bAnimContext *ac, bAnimListElem *ale, const short selectmode)
static wmOperatorStatus animchannels_expand_exec(bContext *C, wmOperator *op)
static bool get_grease_pencil_layer_bounds(const GreasePencilLayer *gplayer, const float range[2], rctf *r_bounds)
static void anim_channels_select_set(bAnimContext *ac, const ListBase anim_data, eAnimChannels_SetFlag sel)
static int count_fcurves_hidden_by_filter(bAnimContext *ac, const blender::Span< FCurve * > fcurves)
static void templated_selection_state_update(T &selectable_thing, const eAnimChannels_SetFlag selectmode)
static void ANIM_OT_channels_setting_disable(wmOperatorType *ot)
static void ANIM_OT_channels_click(wmOperatorType *ot)
static wmOperatorStatus animchannels_collapse_exec(bContext *C, wmOperator *op)
static eAnimChannels_SetFlag anim_channels_selection_flag_for_toggle(const ListBase anim_data)
static wmOperatorStatus animchannels_enable_exec(bContext *C, wmOperator *)
static int click_select_channel_scene(bAnimListElem *ale, const short selectmode)
static wmOperatorStatus animchannels_rename_invoke(bContext *C, wmOperator *, const wmEvent *event)
static const EnumPropertyItem prop_animchannel_setflag_types[]
void ED_operatortypes_animchannels()
eRearrangeAnimChan_Mode
@ REARRANGE_ANIMCHAN_DOWN
@ REARRANGE_ANIMCHAN_UP
@ REARRANGE_ANIMCHAN_BOTTOM
@ REARRANGE_ANIMCHAN_TOP
static int click_select_channel_masklayer(bAnimContext *ac, bAnimListElem *ale, const short selectmode)
static int click_select_channel_fcurve(bAnimContext *ac, bAnimListElem *ale, const short selectmode, const int filter)
static void ANIM_OT_channel_view_pick(wmOperatorType *ot)
static void rearrange_grease_pencil_channels(bAnimContext *ac, eRearrangeAnimChan_Mode mode)
static void ANIM_OT_channels_collapse(wmOperatorType *ot)
static void ANIM_OT_channels_select_all(wmOperatorType *ot)
void ANIM_animdata_freelist(ListBase *anim_data)
Definition anim_deps.cc:463
void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data)
Definition anim_deps.cc:356
short ANIM_get_normalization_flags(SpaceLink *space_link)
Definition anim_draw.cc:415
float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short flag, float *r_offset)
Definition anim_draw.cc:624
float ANIM_nla_tweakedit_remap(bAnimListElem *ale, const float cframe, const eNlaTime_ConvertModes mode)
Definition anim_draw.cc:324
bAction * ANIM_active_action_from_area(Scene *scene, ViewLayer *view_layer, const ScrArea *area, ID **r_action_user)
bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac)
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode, void *data, const eAnimCont_Types datatype)
ListBase * ED_context_get_markers(const bContext *C)
BMesh const char void * data
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
const TreeNode & as_node() const
Span< FramesMapKeyT > sorted_keys() const
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
bool contains(const Key &key) const
Definition BLI_map.hh:353
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
constexpr const char * c_str() const
int64_t size() const
void append(const T &value)
bool is_empty() const
void slot_active_set(slot_handle_t slot_handle)
const Slot * slot(int64_t index) const
void slot_move_to_index(Slot &slot, int to_slot_index)
blender::Span< const Slot * > slots() const
bool slot_remove(Slot &slot_to_remove)
Layer & layer_add(std::optional< StringRefNull > name)
const FCurve * fcurve(int64_t index) const
void fcurve_move_to_index(FCurve &fcurve, int to_fcurve_index)
bool fcurve_assign_to_channel_group(FCurve &fcurve, bActionGroup &to_group)
bActionGroup & channel_group_create(StringRefNull name)
bool fcurve_remove(FCurve &fcurve_to_remove)
const bActionGroup * channel_group(int64_t index) const
blender::Span< const FCurve * > fcurves() const
void channel_group_move_to_index(bActionGroup &group, int to_group_index)
blender::Span< const bActionGroup * > channel_groups() const
#define SELECT
#define offsetof(t, d)
#define GS(x)
#define active
#define filter
#define printf(...)
#define select(A, B, C)
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
float length(VecOp< float, D >) RET
DEG_id_tag_update_ex(cb_data->bmain, cb_data->owner_id, ID_RECALC_TAG_FOR_UNDO|ID_RECALC_SYNC_TO_EVAL)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
static ulong * next
#define T
#define G(x, y, z)
bool action_treat_as_legacy(const bAction &action)
bool action_fcurve_remove(Action &action, FCurve &fcu)
void animdata_fcurve_delete(AnimData *adt, FCurve *fcu)
Definition animdata.cc:251
Action & action_add(Main &bmain, StringRefNull name)
const animrig::Channelbag * channelbag_for_action_slot(const Action &action, slot_handle_t slot_handle)
void bake_fcurve(FCurve *fcu, blender::int2 range, float step, BakeCurveRemove remove_existing)
Span< FCurve * > fcurves_for_action_slot(Action &action, slot_handle_t slot_handle)
Action * get_action(ID &animated_id)
void move_slot(Main &bmain, Slot &slot, Action &from_action, Action &to_action)
void base_select(Base *base, eObjectSelect_Mode mode)
void base_activate_with_mode_exit_if_needed(bContext *C, Base *base)
VecBase< int32_t, 2 > int2
float wrap(float value, float max, float min)
Definition node_math.h:103
bool ED_pose_deselect_all(Object *ob, int select_mode, const bool ignore_visibility)
void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select, bool change_active)
const char * name
void RNA_int_set_array(PointerRNA *ptr, const char *name, const int *values)
bool RNA_property_array_check(PropertyRNA *prop)
void RNA_int_get_array(PointerRNA *ptr, const char *name, int *values)
float RNA_float_get(PointerRNA *ptr, const char *name)
std::string RNA_string_get(PointerRNA *ptr, const char *name)
int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_int_array(StructOrFunctionRNA *cont_, const char *identifier, const int len, const int *default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
std::optional< std::string > RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop)
Definition rna_path.cc:1173
bool RNA_path_resolve_property(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
Definition rna_path.cc:560
#define FLT_MAX
Definition stdcycles.h:14
char identifier[258]
bAction * action
int32_t slot_handle
ListBase drivers
ListBase nla_tracks
short flag
struct Object * object
float vec[3][3]
struct FCurve * next
bActionGroup * grp
char * rna_path
ChannelDriver * driver
BezTriple * bezt
struct FCurve * prev
unsigned int totvert
ListBase modifiers
GreasePencilLayerTreeNode base
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
ListBase block
void * last
void * first
ListBase strips
struct NlaTrack * next
struct NlaTrack * prev
struct bPose * pose
struct AnimData * adt
ID * owner_id
Definition RNA_types.hh:51
struct bGPdata * gpd
struct RenderData r
struct AnimData * adt
struct bDopeSheet * ads
ThemeWireColor cs
struct ActionChannelbag * channelbag
ListBase curves
ListBase groups
short(* get_offset)(bAnimContext *ac, bAnimListElem *ale)
bool(* name_prop)(bAnimListElem *ale, PointerRNA *r_ptr, PropertyRNA **r_prop)
eDopeSheet_FilterFlag flag
SpaceLink * sl
eAnimCont_Types datatype
bDopeSheet * ads
eSpace_Type spacetype
ViewLayer * view_layer
ARegion * region
Object * obact
eRegion_Type regiontype
struct bAnimContext::@242276144047124000362312251357014355232301013033 filters
ScrArea * area
AnimData * adt
bAnimListElem * next
eAnim_ChannelType type
eAnim_KeyType datatype
bAnimListElem * prev
ListBase areabase
float xmax
float xmin
float ymax
float ymin
int ymin
int ymax
int xmin
int xmax
tReorderChannelIsland * prev
tReorderChannelIsland * next
int mval[2]
Definition WM_types.hh:763
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_main_add_notifier(uint type, void *reference)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
wmKeyMap * WM_keymap_ensure(wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid)
Definition wm_keymap.cc:895
#define WM_msg_publish_rna_prop(mbus, id_, data_, type_, prop_)
void WM_operator_properties_border_to_rcti(wmOperator *op, rcti *r_rect)
void WM_operator_properties_gesture_box_select(wmOperatorType *ot)
void WM_operator_properties_select_all(wmOperatorType *ot)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
wmOperatorStatus WM_operator_flag_only_pass_through_on_press(wmOperatorStatus retval, const wmEvent *event)
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
int WM_operator_smooth_viewtx_get(const wmOperator *op)
wmOperatorStatus WM_operator_props_popup(bContext *C, wmOperator *op, const wmEvent *)
bScreen * WM_window_get_active_screen(const wmWindow *win)