Blender V4.5
action_data.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2015 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cfloat>
10#include <cstdlib>
11#include <cstring>
12
13#include "BLI_listbase.h"
14#include "BLI_utildefines.h"
15
16#include "BLT_translation.hh"
17
18#include "DNA_anim_types.h"
19#include "DNA_key_types.h"
20#include "DNA_object_types.h"
21#include "DNA_scene_types.h"
22
23#include "RNA_access.hh"
24#include "RNA_define.hh"
25#include "RNA_prototypes.hh"
26
27#include "BKE_action.hh"
28#include "BKE_context.hh"
29#include "BKE_key.hh"
30#include "BKE_lib_id.hh"
31#include "BKE_nla.hh"
32#include "BKE_report.hh"
33#include "BKE_scene.hh"
34
35#include "ANIM_action.hh"
36
37#include "ED_anim_api.hh"
38#include "ED_screen.hh"
39
40#include "DEG_depsgraph.hh"
41
42#include "WM_api.hh"
43#include "WM_types.hh"
44
45#include "UI_interface.hh"
46#include "UI_interface_c.hh"
47
48#include "action_intern.hh"
49
50/* -------------------------------------------------------------------- */
53
55{
56 { /* Support use from the layout.template_action() UI template. */
57 PointerRNA ptr = {};
58 PropertyRNA *prop = nullptr;
60 /* template_action() sets a RNA_AnimData pointer, whereas other code may set
61 * other pointer types. This code here only deals with the former. */
62 if (prop && ptr.type == &RNA_AnimData) {
63 if (!RNA_property_editable(&ptr, prop)) {
64 return nullptr;
65 }
66 if (r_adt_id_owner) {
67 *r_adt_id_owner = ptr.owner_id;
68 }
69 AnimData *adt = static_cast<AnimData *>(ptr.data);
70 return adt;
71 }
72 }
73
74 SpaceLink *space_data = CTX_wm_space_data(C);
75 if (!space_data || space_data->spacetype != SPACE_ACTION) {
76 return nullptr;
77 }
78
79 SpaceAction *saction = (SpaceAction *)space_data;
81 AnimData *adt = nullptr;
82
83 /* Get AnimData block to use */
84 if (saction->mode == SACTCONT_ACTION) {
85 /* Currently, "Action Editor" means object-level only... */
86 if (ob) {
87 adt = ob->adt;
88 if (r_adt_id_owner) {
89 *r_adt_id_owner = &ob->id;
90 }
91 }
92 }
93 else if (saction->mode == SACTCONT_SHAPEKEY) {
94 Key *key = BKE_key_from_object(ob);
95 if (key) {
96 adt = key->adt;
97 if (r_adt_id_owner) {
98 *r_adt_id_owner = &key->id;
99 }
100 }
101 }
102
103 return adt;
104}
105
107
108/* -------------------------------------------------------------------- */
111
113{
114 bAction *action;
115
116 /* create action - the way to do this depends on whether we've got an
117 * existing one there already, in which case we make a copy of it
118 * (which is useful for "versioning" actions within the same file)
119 */
120 if (oldact && GS(oldact->id.name) == ID_AC) {
121 /* make a copy of the existing action */
122 action = (bAction *)BKE_id_copy(CTX_data_main(C), &oldact->id);
123 }
124 else {
125 /* just make a new (empty) action */
126 action = BKE_action_add(CTX_data_main(C), DATA_("Action"));
127 }
128
129 /* when creating new ID blocks, there is already 1 user (as for all new datablocks),
130 * but the RNA pointer code will assign all the proper users instead, so we compensate
131 * for that here
132 */
133 BLI_assert(action->id.us == 1);
134 id_us_min(&action->id);
135
136 return action;
137}
138
139/* Change the active action used by the action editor */
141{
142 bScreen *screen = CTX_wm_screen(C);
144
145 PropertyRNA *prop;
146
147 /* create RNA pointers and get the property */
148 PointerRNA ptr = RNA_pointer_create_discrete(&screen->id, &RNA_SpaceDopeSheetEditor, saction);
149 prop = RNA_struct_find_property(&ptr, "action");
150
151 /* NOTE: act may be nullptr here, so better to just use a cast here */
152 PointerRNA idptr = RNA_id_pointer_create((ID *)act);
153
154 /* set the new pointer, and force a refresh */
155 RNA_property_pointer_set(&ptr, prop, idptr, nullptr);
156 RNA_property_update(C, &ptr, prop);
157}
158
160
161/* -------------------------------------------------------------------- */
170
172{
173 { /* Support use from the layout.template_action() UI template. */
174 PointerRNA ptr = {};
175 PropertyRNA *prop = nullptr;
177 if (prop) {
178 return RNA_property_editable(&ptr, prop);
179 }
180 }
181
182 Scene *scene = CTX_data_scene(C);
183
184 /* Check tweak-mode is off (as you don't want to be tampering with the action in that case) */
185 /* NOTE: unlike for pushdown,
186 * this operator needs to be run when creating an action from nothing... */
190
191 /* For now, actions are only for the active object, and on object and shape-key levels... */
192 if (saction->mode == SACTCONT_ACTION) {
193 /* XXX: This assumes that actions are assigned to the active object in this mode */
194 if (ob) {
195 if ((ob->adt == nullptr) || (ob->adt->flag & ADT_NLA_EDIT_ON) == 0) {
196 return true;
197 }
198 }
199 }
200 else if (saction->mode == SACTCONT_SHAPEKEY) {
201 Key *key = BKE_key_from_object(ob);
202 if (key) {
203 if ((key->adt == nullptr) || (key->adt->flag & ADT_NLA_EDIT_ON) == 0) {
204 return true;
205 }
206 }
207 }
208 }
209 else if (ED_operator_nla_active(C)) {
210 if (!(scene->flag & SCE_NLA_EDIT_ON)) {
211 return true;
212 }
213 }
214
215 /* something failed... */
216 return false;
217}
218
220{
222 PropertyRNA *prop;
223
224 bAction *oldact = nullptr;
225 AnimData *adt = nullptr;
226 ID *adt_id_owner = nullptr;
227 /* hook into UI */
229
230 if (prop) {
231 /* The operator was called from a button. */
232 PointerRNA oldptr;
233
234 oldptr = RNA_property_pointer_get(&ptr, prop);
235 oldact = (bAction *)oldptr.owner_id;
236
237 /* stash the old action to prevent it from being lost */
238 if (ptr.type == &RNA_AnimData) {
239 adt = static_cast<AnimData *>(ptr.data);
240 adt_id_owner = ptr.owner_id;
241 }
242 else if (ptr.type == &RNA_SpaceDopeSheetEditor) {
243 adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
244 }
245 }
246 else {
247 adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
248 oldact = adt->action;
249 }
250 {
251 bAction *action = nullptr;
252
253 /* Perform stashing operation - But only if there is an action */
254 if (adt && oldact) {
255 BLI_assert(adt_id_owner != nullptr);
256 /* stash the action */
257 if (BKE_nla_action_stash({*adt_id_owner, *adt}, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) {
258 /* The stash operation will remove the user already
259 * (and unlink the action from the AnimData action slot).
260 * Hence, we must unset the ref to the action in the
261 * action editor too (if this is where we're being called from)
262 * first before setting the new action once it is created,
263 * or else the user gets decremented twice!
264 */
265 if (ptr.type == &RNA_SpaceDopeSheetEditor) {
266 SpaceAction *saction = static_cast<SpaceAction *>(ptr.data);
267 saction->action = nullptr;
268 }
269 }
270 else {
271#if 0
272 printf("WARNING: Failed to stash %s. It may already exist in the NLA stack though\n",
273 oldact->id.name);
274#endif
275 }
276 }
277
278 /* create action */
279 action = action_create_new(C, oldact);
280
281 if (prop) {
282 /* set this new action
283 * NOTE: we can't use actedit_change_action, as this function is also called from the NLA
284 */
285 PointerRNA idptr = RNA_id_pointer_create(&action->id);
286 RNA_property_pointer_set(&ptr, prop, idptr, nullptr);
287 RNA_property_update(C, &ptr, prop);
288 }
289 }
290
291 /* set notifier that keyframes have changed */
293
294 return OPERATOR_FINISHED;
295}
296
298{
299 /* identifiers */
300 ot->name = "New Action";
301 ot->idname = "ACTION_OT_new";
302 ot->description = "Create new action";
303
304 /* API callbacks. */
305 ot->exec = action_new_exec;
306 ot->poll = action_new_poll;
307
308 /* flags */
310}
311
313
314/* -------------------------------------------------------------------- */
322
324{
326 return false;
327 }
328
331
332 if (!adt || !saction->action) {
333 return false;
334 }
335
336 /* NOTE: We check this for the AnimData block in question and not the global flag,
337 * as the global flag may be left dirty by some of the browsing ops here.
338 */
339 return (adt->flag & ADT_NLA_EDIT_ON) == 0;
340}
341
343{
345 ID *adt_id_owner = nullptr;
346 AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
347
348 /* Do the deed... */
349 if (adt && adt->action) {
350 blender::animrig::Action &action = adt->action->wrap();
351
352 /* action can be safely added */
353 BKE_nla_action_pushdown({*adt_id_owner, *adt}, ID_IS_OVERRIDE_LIBRARY(adt_id_owner));
354
355 Main *bmain = CTX_data_main(C);
356 DEG_id_tag_update_ex(bmain, adt_id_owner, ID_RECALC_ANIMATION);
357
358 /* The action needs updating too, as FCurve modifiers are to be reevaluated. They won't extend
359 * beyond the NLA strip after pushing down to the NLA. */
361
362 /* Stop displaying this action in this editor
363 * NOTE: The editor itself doesn't set a user...
364 */
365 saction->action = nullptr;
366 }
367
368 /* Send notifiers that stuff has changed */
370 return OPERATOR_FINISHED;
371}
372
374{
375 /* identifiers */
376 ot->name = "Push Down Action";
377 ot->idname = "ACTION_OT_push_down";
378 ot->description = "Push action down on to the NLA stack as a new strip";
379
380 /* callbacks */
381 ot->exec = action_pushdown_exec;
382 ot->poll = action_pushdown_poll;
383
384 /* flags */
386}
387
389
390/* -------------------------------------------------------------------- */
393
395{
397 ID *adt_id_owner = nullptr;
398 AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
399
400 /* Perform stashing operation */
401 if (adt) {
402 /* stash the action */
403 if (BKE_nla_action_stash({*adt_id_owner, *adt}, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) {
404 /* The stash operation will remove the user already,
405 * so the flushing step later shouldn't double up
406 * the user-count fixes. Hence, we must unset this ref
407 * first before setting the new action.
408 */
409 saction->action = nullptr;
410 }
411 else {
412 /* action has already been added - simply warn about this, and clear */
413 BKE_report(op->reports, RPT_ERROR, "Action+Slot has already been stashed");
414 }
415
416 /* clear action refs from editor, and then also the backing data (not necessary) */
417 actedit_change_action(C, nullptr);
418 }
419
420 /* Send notifiers that stuff has changed */
422 return OPERATOR_FINISHED;
423}
424
426{
427 /* identifiers */
428 ot->name = "Stash Action";
429 ot->idname = "ACTION_OT_stash";
430 ot->description = "Store this action in the NLA stack as a non-contributing strip for later use";
431
432 /* callbacks */
433 ot->exec = action_stash_exec;
434 ot->poll = action_pushdown_poll;
435
436 /* flags */
438
439 /* properties */
440 ot->prop = RNA_def_boolean(ot->srna,
441 "create_new",
442 true,
443 "Create New Action",
444 "Create a new action once the existing one has been safely stored");
445}
446
448
449/* -------------------------------------------------------------------- */
456
458{
461
462 /* Check tweak-mode is off (as you don't want to be tampering with the action in that case) */
463 /* NOTE: unlike for pushdown,
464 * this operator needs to be run when creating an action from nothing... */
465 if (adt) {
466 if (!(adt->flag & ADT_NLA_EDIT_ON)) {
467 return true;
468 }
469 }
470 else {
471 /* There may not be any action/animdata yet, so, just fall back to the global setting
472 * (which may not be totally valid yet if the action editor was used and things are
473 * now in an inconsistent state)
474 */
476 Scene *scene = CTX_data_scene(C);
477
478 if (!(scene->flag & SCE_NLA_EDIT_ON)) {
479 /* For now, actions are only for the active object, and on object and shape-key levels...
480 */
481 return ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY);
482 }
483 }
484 }
485
486 /* something failed... */
487 return false;
488}
489
491{
493 ID *adt_id_owner = nullptr;
494 AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
495
496 /* Check for no action... */
497 if (saction->action == nullptr) {
498 /* just create a new action */
499 bAction *action = action_create_new(C, nullptr);
500 actedit_change_action(C, action);
501 }
502 else if (adt) {
503 /* Perform stashing operation */
504 if (BKE_nla_action_stash({*adt_id_owner, *adt}, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) {
505 bAction *new_action = nullptr;
506
507 /* Create new action not based on the old one
508 * (since the "new" operator already does that). */
509 new_action = action_create_new(C, nullptr);
510
511 /* The stash operation will remove the user already,
512 * so the flushing step later shouldn't double up
513 * the user-count fixes. Hence, we must unset this ref
514 * first before setting the new action.
515 */
516 saction->action = nullptr;
517 actedit_change_action(C, new_action);
518 }
519 else {
520 /* action has already been added - simply warn about this, and clear */
521 BKE_report(op->reports, RPT_ERROR, "Action+Slot has already been stashed");
522 actedit_change_action(C, nullptr);
523 }
524 }
525
526 /* Send notifiers that stuff has changed */
528 return OPERATOR_FINISHED;
529}
530
532{
533 /* identifiers */
534 ot->name = "Stash Action";
535 ot->idname = "ACTION_OT_stash_and_create";
536 ot->description =
537 "Store this action in the NLA stack as a non-contributing strip for later use, and create a "
538 "new action";
539
540 /* callbacks */
543
544 /* flags */
546}
547
549
550/* -------------------------------------------------------------------- */
560
562 bContext *C, ID *id, AnimData *adt, bAction *act, ReportList *reports, bool force_delete)
563{
564 BLI_assert(id);
565 ScrArea *area = CTX_wm_area(C);
566
567 /* If the old action only has a single user (that it's about to lose),
568 * warn user about it
569 *
570 * TODO: Maybe we should just save it for them? But then, there's the problem of
571 * trying to get rid of stuff that's actually unwanted!
572 */
573 if (act->id.us == 1) {
576 "Action '%s' will not be saved, create Fake User or Stash in NLA Stack to retain",
577 act->id.name + 2);
578 }
579
580 /* Clear Fake User and remove action stashing strip (if present) */
581 if (force_delete) {
582 /* Remove stashed strip binding this action to this datablock */
583 /* XXX: we cannot unlink it from *OTHER* datablocks that may also be stashing it,
584 * but GE users only seem to use/care about single-object binding for now so this
585 * should be fine
586 */
587 if (adt) {
588 NlaTrack *nlt, *nlt_next;
589 NlaStrip *strip, *nstrip;
590
591 for (nlt = static_cast<NlaTrack *>(adt->nla_tracks.first); nlt; nlt = nlt_next) {
592 nlt_next = nlt->next;
593
594 if (strstr(nlt->name, DATA_("[Action Stash]"))) {
595 for (strip = static_cast<NlaStrip *>(nlt->strips.first); strip; strip = nstrip) {
596 nstrip = strip->next;
597
598 if (strip->act == act) {
599 /* Remove this strip, and the track too if it doesn't have anything else */
600 BKE_nlastrip_remove_and_free(&nlt->strips, strip, true);
601
602 if (nlt->strips.first == nullptr) {
603 BLI_assert(nstrip == nullptr);
605 }
606 }
607 }
608 }
609 }
610 }
611
612 /* Clear Fake User */
613 id_fake_user_clear(&act->id);
614 }
615
616 /* If in Tweak Mode, don't unlink. Instead, this becomes a shortcut to exit Tweak Mode. */
617 if ((adt) && (adt->flag & ADT_NLA_EDIT_ON)) {
618 BKE_nla_tweakmode_exit({*id, *adt});
619
620 Scene *scene = CTX_data_scene(C);
621 if (scene != nullptr) {
622 scene->flag &= ~SCE_NLA_EDIT_ON;
623 }
624 }
625 else {
626 /* Clear AnimData -> action via RNA, so that it triggers message bus updates. */
627 PointerRNA ptr = RNA_pointer_create_discrete(id, &RNA_AnimData, adt);
628 PropertyRNA *prop = RNA_struct_find_property(&ptr, "action");
629
631 RNA_property_update(C, &ptr, prop);
632
633 /* Also update the Action editor legacy Action pointer. */
634 if (area->spacetype == SPACE_ACTION) {
635 actedit_change_action(C, nullptr);
636 }
637 }
638}
639
640/* -------------------------- */
641
643{
644 {
645 ID *animated_id = nullptr;
646 AnimData *adt = ED_actedit_animdata_from_context(C, &animated_id);
647 if (animated_id) {
648 if (!BKE_id_is_editable(CTX_data_main(C), animated_id)) {
649 return false;
650 }
651 if (!adt) {
652 return false;
653 }
654 return adt->action != nullptr;
655 }
656 }
657
661
662 /* Only when there's an active action, in the right modes... */
663 if (saction->action && adt) {
664 return true;
665 }
666 }
667
668 /* something failed... */
669 return false;
670}
671
673{
674 ID *animated_id = nullptr;
675 AnimData *adt = ED_actedit_animdata_from_context(C, &animated_id);
676 bool force_delete = RNA_boolean_get(op->ptr, "force_delete");
677
678 if (adt && adt->action) {
679 ED_animedit_unlink_action(C, animated_id, adt, adt->action, op->reports, force_delete);
680 }
681
682 /* Unlink is also abused to exit NLA tweak mode. */
684
685 return OPERATOR_FINISHED;
686}
687
689{
690 /* NOTE: this is hardcoded to match the behavior for the unlink button
691 * (in `interface_templates.cc`). */
692 RNA_boolean_set(op->ptr, "force_delete", event->modifier & KM_SHIFT);
693 return action_unlink_exec(C, op);
694}
695
697{
698 PropertyRNA *prop;
699
700 /* identifiers */
701 ot->name = "Unlink Action";
702 ot->idname = "ACTION_OT_unlink";
703 ot->description = "Unlink this action from the active action slot (and/or exit Tweak Mode)";
704
705 /* callbacks */
706 ot->invoke = action_unlink_invoke;
707 ot->exec = action_unlink_exec;
708 ot->poll = action_unlink_poll;
709
710 /* properties */
711 prop = RNA_def_boolean(ot->srna,
712 "force_delete",
713 false,
714 "Force Delete",
715 "Clear Fake User and remove "
716 "copy stashed in this data-block's NLA stack");
718
719 /* flags */
721}
722
724
725/* -------------------------------------------------------------------- */
728
729/* Try to find NLA Strip to use for action layer up/down tool */
730static NlaStrip *action_layer_get_nlastrip(ListBase *strips, float ctime)
731{
732 LISTBASE_FOREACH (NlaStrip *, strip, strips) {
733 /* Can we use this? */
734 if (IN_RANGE_INCL(ctime, strip->start, strip->end)) {
735 /* in range - use this one */
736 return strip;
737 }
738 if ((ctime < strip->start) && (strip->prev == nullptr)) {
739 /* before first - use this one */
740 return strip;
741 }
742 if ((ctime > strip->end) && (strip->next == nullptr)) {
743 /* after last - use this one */
744 return strip;
745 }
746 }
747
748 /* nothing suitable found... */
749 return nullptr;
750}
751
752/* Switch NLA Strips/Actions. */
753static void action_layer_switch_strip(const OwnedAnimData owned_adt,
754 NlaTrack *old_track,
755 NlaStrip *old_strip,
756 NlaTrack *nlt,
757 NlaStrip *strip)
758{
759 AnimData *adt = &owned_adt.adt;
760
761 /* Exit tweak-mode on old strip
762 * NOTE: We need to manually clear this stuff ourselves, as tweak-mode exit doesn't do it
763 */
764 BKE_nla_tweakmode_exit(owned_adt);
765
766 if (old_strip) {
768 }
769 if (old_track) {
770 old_track->flag &= ~(NLATRACK_ACTIVE | NLATRACK_SELECTED);
771 }
772
773 /* Make this one the active one instead */
775 nlt->flag |= NLATRACK_ACTIVE;
776
777 /* Copy over "solo" flag - This is useful for stashed actions... */
778 if (old_track) {
779 if (old_track->flag & NLATRACK_SOLO) {
780 old_track->flag &= ~NLATRACK_SOLO;
781 nlt->flag |= NLATRACK_SOLO;
782 }
783 }
784 else {
785 /* NLA muting <==> Solo Tracks */
786 if (adt->flag & ADT_NLA_EVAL_OFF) {
787 /* disable NLA muting */
788 adt->flag &= ~ADT_NLA_EVAL_OFF;
789
790 /* mark this track as being solo */
791 adt->flag |= ADT_NLA_SOLO_TRACK;
792 nlt->flag |= NLATRACK_SOLO;
793
794 /* TODO: Needs rest-pose flushing (when we get reference track) */
795 }
796 }
797
798 /* Enter tweak-mode again - hopefully we're now "it" */
799 BKE_nla_tweakmode_enter(owned_adt);
800 BLI_assert(adt->actstrip == strip);
801}
802
804
805/* -------------------------------------------------------------------- */
808
810{
811 /* Action Editor's action editing modes only */
814 if (adt) {
815 /* only allow if we're in tweak-mode, and there's something above us... */
816 if (adt->flag & ADT_NLA_EDIT_ON) {
817 /* We need to check if there are any tracks above the active one
818 * since the track the action comes from is not stored in AnimData
819 */
820 if (adt->nla_tracks.last) {
821 NlaTrack *nlt = (NlaTrack *)adt->nla_tracks.last;
822
823 if (nlt->flag & NLATRACK_DISABLED) {
824 /* A disabled track will either be the track itself,
825 * or one of the ones above it.
826 *
827 * If this is the top-most one, there is the possibility
828 * that there is no active action. For now, we let this
829 * case return true too, so that there is a natural way
830 * to "move to an empty layer", even though this means
831 * that we won't actually have an action.
832 */
833 // return (adt->tmpact != nullptr);
834 return true;
835 }
836 }
837 }
838 }
839 }
840
841 /* something failed... */
842 return false;
843}
844
846{
847 ID *animated_id = nullptr;
848 AnimData *adt = ED_actedit_animdata_from_context(C, &animated_id);
849 const OwnedAnimData owned_adt{*animated_id, *adt};
850
851 Scene *scene = CTX_data_scene(C);
852 float ctime = BKE_scene_ctime_get(scene);
853
854 /* Get active track */
855 NlaTrack *act_track = BKE_nlatrack_find_tweaked(adt);
856
857 if (act_track == nullptr) {
858 BKE_report(op->reports, RPT_ERROR, "Could not find current NLA Track");
859 return OPERATOR_CANCELLED;
860 }
861
862 /* Find next action, and hook it up */
863 if (act_track->next) {
864 NlaTrack *nlt;
865
866 /* Find next action to use */
867 for (nlt = act_track->next; nlt; nlt = nlt->next) {
868 NlaStrip *strip = action_layer_get_nlastrip(&nlt->strips, ctime);
869
870 if (strip) {
871 action_layer_switch_strip(owned_adt, act_track, adt->actstrip, nlt, strip);
872 break;
873 }
874 }
875 }
876 else {
877 /* No more actions (strips) - Go back to editing the original active action
878 * NOTE: This will mean exiting tweak-mode...
879 */
880 BKE_nla_tweakmode_exit(owned_adt);
881
882 /* Deal with solo flags...
883 * Assume: Solo Track == NLA Muting
884 */
885 if (adt->flag & ADT_NLA_SOLO_TRACK) {
886 /* turn off solo flags on tracks */
887 act_track->flag &= ~NLATRACK_SOLO;
889
890 /* turn on NLA muting (to keep same effect) */
891 adt->flag |= ADT_NLA_EVAL_OFF;
892
893 /* TODO: Needs rest-pose flushing (when we get reference track) */
894 }
895 }
896
897 /* Update the action that this editor now uses
898 * NOTE: The calls above have already handled the user-count/anim-data side of things. */
900 return OPERATOR_FINISHED;
901}
902
904{
905 /* identifiers */
906 ot->name = "Next Layer";
907 ot->idname = "ACTION_OT_layer_next";
908 ot->description =
909 "Switch to editing action in animation layer above the current action in the NLA Stack";
910
911 /* callbacks */
914
915 /* flags */
917}
918
920
921/* -------------------------------------------------------------------- */
924
926{
927 /* Action Editor's action editing modes only */
930 if (adt) {
931 if (adt->flag & ADT_NLA_EDIT_ON) {
932 /* Tweak Mode: We need to check if there are any tracks below the active one
933 * that we can move to */
934 if (adt->nla_tracks.first) {
935 NlaTrack *nlt = (NlaTrack *)adt->nla_tracks.first;
936
937 /* Since the first disabled track is the track being tweaked/edited,
938 * we can simplify things by only checking the first track:
939 * - If it is disabled, this is the track being tweaked,
940 * so there can't be anything below it
941 * - Otherwise, there is at least 1 track below the tweaking
942 * track that we can descend to
943 */
944 if ((nlt->flag & NLATRACK_DISABLED) == 0) {
945 /* not disabled = there are actions below the one being tweaked */
946 return true;
947 }
948 }
949 }
950 else {
951 /* Normal Mode: If there are any tracks, we can try moving to those */
952 return (adt->nla_tracks.first != nullptr);
953 }
954 }
955 }
956
957 /* something failed... */
958 return false;
959}
960
962{
963 ID *animated_id = nullptr;
964 AnimData *adt = ED_actedit_animdata_from_context(C, &animated_id);
965 NlaTrack *act_track;
966 NlaTrack *nlt;
967
968 Scene *scene = CTX_data_scene(C);
969 float ctime = BKE_scene_ctime_get(scene);
970
971 /* Sanity Check */
972 if (adt == nullptr) {
974 op->reports, RPT_ERROR, "Internal Error: Could not find Animation Data/NLA Stack to use");
975 return OPERATOR_CANCELLED;
976 }
977
978 /* Get active track */
979 act_track = BKE_nlatrack_find_tweaked(adt);
980
981 /* If there is no active track, that means we are using the active action... */
982 if (act_track) {
983 /* Active Track - Start from the one below it */
984 nlt = act_track->prev;
985 }
986 else {
987 /* Active Action - Use the top-most track */
988 nlt = static_cast<NlaTrack *>(adt->nla_tracks.last);
989 }
990
991 /* Find previous action and hook it up */
992 for (; nlt; nlt = nlt->prev) {
993 NlaStrip *strip = action_layer_get_nlastrip(&nlt->strips, ctime);
994
995 if (strip) {
996 action_layer_switch_strip({*animated_id, *adt}, act_track, adt->actstrip, nlt, strip);
997 break;
998 }
999 }
1000
1001 /* Update the action that this editor now uses
1002 * NOTE: The calls above have already handled the user-count/animdata side of things. */
1004 return OPERATOR_FINISHED;
1005}
1006
1008{
1009 /* identifiers */
1010 ot->name = "Previous Layer";
1011 ot->idname = "ACTION_OT_layer_prev";
1012 ot->description =
1013 "Switch to editing action in animation layer below the current action in the NLA Stack";
1014
1015 /* callbacks */
1016 ot->exec = action_layer_prev_exec;
1017 ot->poll = action_layer_prev_poll;
1018
1019 /* flags */
1020 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1021}
1022
Functions and classes to work with Actions.
Blender kernel action and pose functionality.
bAction * BKE_action_add(Main *bmain, const char name[])
bScreen * CTX_wm_screen(const bContext *C)
ScrArea * CTX_wm_area(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)
Main * CTX_data_main(const bContext *C)
Key * BKE_key_from_object(Object *ob)
Definition key.cc:1824
bool BKE_id_is_editable(const Main *bmain, const ID *id)
Definition lib_id.cc:2503
void id_fake_user_clear(ID *id)
Definition lib_id.cc:399
ID * BKE_id_copy(Main *bmain, const ID *id)
Definition lib_id.cc:772
void id_us_min(ID *id)
Definition lib_id.cc:361
void BKE_nlastrip_remove_and_free(ListBase *strips, NlaStrip *strip, const bool do_id_user)
void BKE_nla_action_pushdown(OwnedAnimData owned_adt, bool is_liboverride)
void BKE_nla_tweakmode_exit(OwnedAnimData owned_adt)
NlaTrack * BKE_nlatrack_find_tweaked(AnimData *adt)
bool BKE_nla_action_stash(OwnedAnimData owned_adt, bool is_liboverride)
bool BKE_nla_tweakmode_enter(OwnedAnimData owned_adt)
void BKE_nlatrack_remove_and_free(ListBase *tracks, NlaTrack *nlt, bool do_id_user)
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
float BKE_scene_ctime_get(const Scene *scene)
Definition scene.cc:2367
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
#define ELEM(...)
#define IN_RANGE_INCL(a, b, c)
#define DATA_(msgid)
@ ID_RECALC_ANIMATION
Definition DNA_ID.h:985
@ ID_AC
@ SACTCONT_ACTION
@ SACTCONT_SHAPEKEY
@ NLASTRIP_FLAG_ACTIVE
@ NLASTRIP_FLAG_SELECT
@ ADT_NLA_SOLO_TRACK
@ ADT_NLA_EVAL_OFF
@ ADT_NLA_EDIT_ON
@ NLATRACK_SOLO
@ NLATRACK_ACTIVE
@ NLATRACK_DISABLED
@ NLATRACK_SELECTED
Object is a sort of wrapper for general info.
@ SCE_NLA_EDIT_ON
@ SPACE_ACTION
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
bool ED_operator_action_active(bContext *C)
bool ED_operator_nla_active(bContext *C)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
#define C
Definition RandGen.cpp:29
void UI_context_active_but_prop_get_templateID(const bContext *C, PointerRNA *r_ptr, PropertyRNA **r_prop)
#define ND_NLA_ACTCHANGE
Definition WM_types.hh:495
@ KM_SHIFT
Definition WM_types.hh:275
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NC_ANIMATION
Definition WM_types.hh:385
#define NA_ADDED
Definition WM_types.hh:583
ReportList * reports
Definition WM_types.hh:1025
#define ND_KEYFRAME
Definition WM_types.hh:491
static bool action_unlink_poll(bContext *C)
static wmOperatorStatus action_stash_exec(bContext *C, wmOperator *op)
static void action_layer_switch_strip(const OwnedAnimData owned_adt, NlaTrack *old_track, NlaStrip *old_strip, NlaTrack *nlt, NlaStrip *strip)
AnimData * ED_actedit_animdata_from_context(const bContext *C, ID **r_adt_id_owner)
static wmOperatorStatus action_layer_next_exec(bContext *C, wmOperator *op)
void ACTION_OT_layer_prev(wmOperatorType *ot)
void ACTION_OT_push_down(wmOperatorType *ot)
static bAction * action_create_new(bContext *C, bAction *oldact)
static bool action_new_poll(bContext *C)
static void actedit_change_action(bContext *C, bAction *act)
void ACTION_OT_unlink(wmOperatorType *ot)
static bool action_layer_prev_poll(bContext *C)
static wmOperatorStatus action_unlink_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool action_stash_create_poll(bContext *C)
static wmOperatorStatus action_stash_create_exec(bContext *C, wmOperator *op)
void ED_animedit_unlink_action(bContext *C, ID *id, AnimData *adt, bAction *act, ReportList *reports, bool force_delete)
static bool action_pushdown_poll(bContext *C)
void ACTION_OT_stash_and_create(wmOperatorType *ot)
static wmOperatorStatus action_unlink_exec(bContext *C, wmOperator *op)
void ACTION_OT_stash(wmOperatorType *ot)
static wmOperatorStatus action_pushdown_exec(bContext *C, wmOperator *)
static NlaStrip * action_layer_get_nlastrip(ListBase *strips, float ctime)
static wmOperatorStatus action_new_exec(bContext *C, wmOperator *)
void ACTION_OT_new(wmOperatorType *ot)
void ACTION_OT_layer_next(wmOperatorType *ot)
static wmOperatorStatus action_layer_prev_exec(bContext *C, wmOperator *op)
static bool action_layer_next_poll(bContext *C)
#define printf(...)
#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)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_property_pointer_set(PointerRNA *ptr, PropertyRNA *prop, PointerRNA ptr_value, ReportList *reports)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
const PointerRNA PointerRNA_NULL
PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
bool RNA_property_editable(const PointerRNA *ptr, PropertyRNA *prop)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
PointerRNA RNA_id_pointer_create(ID *id)
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)
bAction * action
NlaStrip * actstrip
ListBase nla_tracks
Definition DNA_ID.h:404
int us
Definition DNA_ID.h:425
char name[66]
Definition DNA_ID.h:415
struct AnimData * adt
void * last
void * first
struct NlaStrip * next
bAction * act
ListBase strips
struct NlaTrack * next
char name[64]
struct NlaTrack * prev
struct AnimData * adt
AnimData & adt
ID * owner_id
Definition RNA_types.hh:51
wmEventModifierFlag modifier
Definition WM_types.hh:771
struct ReportList * reports
struct PointerRNA * ptr
void WM_main_add_notifier(uint type, void *reference)
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