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