Blender V5.0
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_c.hh"
46
47#include "action_intern.hh"
48
49/* -------------------------------------------------------------------- */
52
54{
55 { /* Support use from the layout.template_action() UI template. */
56 PointerRNA ptr = {};
57 PropertyRNA *prop = nullptr;
59 /* template_action() sets a RNA_AnimData pointer, whereas other code may set
60 * other pointer types. This code here only deals with the former. */
61 if (prop && ptr.type == &RNA_AnimData) {
62 if (!RNA_property_editable(&ptr, prop)) {
63 return nullptr;
64 }
65 if (r_adt_id_owner) {
66 *r_adt_id_owner = ptr.owner_id;
67 }
68 AnimData *adt = static_cast<AnimData *>(ptr.data);
69 return adt;
70 }
71 }
72
73 SpaceLink *space_data = CTX_wm_space_data(C);
74 if (!space_data || space_data->spacetype != SPACE_ACTION) {
75 return nullptr;
76 }
77
78 SpaceAction *saction = (SpaceAction *)space_data;
80 AnimData *adt = nullptr;
81
82 /* Get AnimData block to use */
83 if (saction->mode == SACTCONT_ACTION) {
84 /* Currently, "Action Editor" means object-level only... */
85 if (ob) {
86 adt = ob->adt;
87 if (r_adt_id_owner) {
88 *r_adt_id_owner = &ob->id;
89 }
90 }
91 }
92 else if (saction->mode == SACTCONT_SHAPEKEY) {
93 Key *key = BKE_key_from_object(ob);
94 if (key) {
95 adt = key->adt;
96 if (r_adt_id_owner) {
97 *r_adt_id_owner = &key->id;
98 }
99 }
100 }
101
102 return adt;
103}
104
106
107/* -------------------------------------------------------------------- */
110
112{
113 bAction *action;
114
115 /* create action - the way to do this depends on whether we've got an
116 * existing one there already, in which case we make a copy of it
117 * (which is useful for "versioning" actions within the same file)
118 */
119 if (oldact && GS(oldact->id.name) == ID_AC) {
120 /* make a copy of the existing action */
121 action = (bAction *)BKE_id_copy(CTX_data_main(C), &oldact->id);
122 }
123 else {
124 /* just make a new (empty) action */
125 action = BKE_action_add(CTX_data_main(C), DATA_("Action"));
126 }
127
128 /* when creating new ID blocks, there is already 1 user (as for all new datablocks),
129 * but the RNA pointer code will assign all the proper users instead, so we compensate
130 * for that here
131 */
132 BLI_assert(action->id.us == 1);
133 id_us_min(&action->id);
134
135 return action;
136}
137
139
140/* -------------------------------------------------------------------- */
149
151{
152 { /* Support use from the layout.template_action() UI template. */
153 PointerRNA ptr = {};
154 PropertyRNA *prop = nullptr;
156 if (prop) {
157 return RNA_property_editable(&ptr, prop);
158 }
159 }
160
161 Scene *scene = CTX_data_scene(C);
162
163 /* Check tweak-mode is off (as you don't want to be tampering with the action in that case) */
164 /* NOTE: unlike for pushdown,
165 * this operator needs to be run when creating an action from nothing... */
169
170 /* For now, actions are only for the active object, and on object and shape-key levels... */
171 if (saction->mode == SACTCONT_ACTION) {
172 /* XXX: This assumes that actions are assigned to the active object in this mode */
173 if (ob) {
174 if ((ob->adt == nullptr) || (ob->adt->flag & ADT_NLA_EDIT_ON) == 0) {
175 return true;
176 }
177 }
178 }
179 else if (saction->mode == SACTCONT_SHAPEKEY) {
180 Key *key = BKE_key_from_object(ob);
181 if (key) {
182 if ((key->adt == nullptr) || (key->adt->flag & ADT_NLA_EDIT_ON) == 0) {
183 return true;
184 }
185 }
186 }
187 }
188 else if (ED_operator_nla_active(C)) {
189 if (!(scene->flag & SCE_NLA_EDIT_ON)) {
190 return true;
191 }
192 }
193
194 /* something failed... */
195 return false;
196}
197
199{
201 PropertyRNA *prop;
202
203 bAction *oldact = nullptr;
204 AnimData *adt = nullptr;
205 ID *adt_id_owner = nullptr;
206 /* hook into UI */
208
209 if (prop) {
210 /* The operator was called from a button. */
211 PointerRNA oldptr;
212
213 oldptr = RNA_property_pointer_get(&ptr, prop);
214 oldact = (bAction *)oldptr.owner_id;
215
216 /* stash the old action to prevent it from being lost */
217 if (ptr.type == &RNA_AnimData) {
218 adt = static_cast<AnimData *>(ptr.data);
219 adt_id_owner = ptr.owner_id;
220 }
221 else if (ptr.type == &RNA_SpaceDopeSheetEditor) {
222 adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
223 }
224 }
225 else {
226 adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
227 oldact = adt->action;
228 }
229 {
230 bAction *action = nullptr;
231
232 /* Perform stashing operation - But only if there is an action */
233 if (adt && oldact) {
234 BLI_assert(adt_id_owner != nullptr);
235 /* stash the action */
236 if (!BKE_nla_action_stash({*adt_id_owner, *adt}, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) {
237#if 0
238 printf("WARNING: Failed to stash %s. It may already exist in the NLA stack though\n",
239 oldact->id.name);
240#endif
241 }
242 }
243
244 /* create action */
245 action = action_create_new(C, oldact);
246
247 if (prop) {
248 /* set this new action */
249 PointerRNA idptr = RNA_id_pointer_create(&action->id);
250 RNA_property_pointer_set(&ptr, prop, idptr, nullptr);
251 RNA_property_update(C, &ptr, prop);
252 }
253 }
254
255 /* set notifier that keyframes have changed */
257
258 return OPERATOR_FINISHED;
259}
260
262{
263 /* identifiers */
264 ot->name = "New Action";
265 ot->idname = "ACTION_OT_new";
266 ot->description = "Create new action";
267
268 /* API callbacks. */
269 ot->exec = action_new_exec;
270 ot->poll = action_new_poll;
271
272 /* flags */
274}
275
277
278/* -------------------------------------------------------------------- */
286
288{
290 return false;
291 }
292
294 if (!adt || !adt->action) {
295 return false;
296 }
297
298 /* NOTE: We check this for the AnimData block in question and not the global flag,
299 * as the global flag may be left dirty by some of the browsing ops here.
300 */
301 return (adt->flag & ADT_NLA_EDIT_ON) == 0;
302}
303
305{
306 ID *adt_id_owner = nullptr;
307 AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
308
309 /* Do the deed... */
310 if (adt && adt->action) {
311 blender::animrig::Action &action = adt->action->wrap();
312
313 /* action can be safely added */
314 BKE_nla_action_pushdown({*adt_id_owner, *adt}, ID_IS_OVERRIDE_LIBRARY(adt_id_owner));
315
316 Main *bmain = CTX_data_main(C);
317 DEG_id_tag_update_ex(bmain, adt_id_owner, ID_RECALC_ANIMATION);
318
319 /* The action needs updating too, as FCurve modifiers are to be reevaluated. They won't extend
320 * beyond the NLA strip after pushing down to the NLA. */
322 }
323
324 /* Send notifiers that stuff has changed */
326 return OPERATOR_FINISHED;
327}
328
330{
331 /* identifiers */
332 ot->name = "Push Down Action";
333 ot->idname = "ACTION_OT_push_down";
334 ot->description = "Push action down on to the NLA stack as a new strip";
335
336 /* callbacks */
337 ot->exec = action_pushdown_exec;
338 ot->poll = action_pushdown_poll;
339
340 /* flags */
342}
343
345
346/* -------------------------------------------------------------------- */
349
351{
352 ID *adt_id_owner = nullptr;
353 AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
354
355 /* Perform stashing operation */
356 if (adt) {
357 /* stash the action */
358 if (!BKE_nla_action_stash({*adt_id_owner, *adt}, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) {
359 /* action has already been added - simply warn about this, and clear */
360 BKE_report(op->reports, RPT_ERROR, "Action+Slot has already been stashed");
361 }
362
363 if (!blender::animrig::unassign_action({*adt_id_owner, *adt})) {
364 BKE_report(op->reports, RPT_ERROR, "Could not unassign the active Action");
365 }
366 }
367
368 /* Send notifiers that stuff has changed */
370 return OPERATOR_FINISHED;
371}
372
374{
375 /* identifiers */
376 ot->name = "Stash Action";
377 ot->idname = "ACTION_OT_stash";
378 ot->description = "Store this action in the NLA stack as a non-contributing strip for later use";
379
380 /* callbacks */
381 ot->exec = action_stash_exec;
382 ot->poll = action_pushdown_poll;
383
384 /* flags */
386
387 /* properties */
388 ot->prop = RNA_def_boolean(ot->srna,
389 "create_new",
390 true,
391 "Create New Action",
392 "Create a new action once the existing one has been safely stored");
393}
394
396
397/* -------------------------------------------------------------------- */
404
406{
409
410 /* Check tweak-mode is off (as you don't want to be tampering with the action in that case) */
411 /* NOTE: unlike for pushdown,
412 * this operator needs to be run when creating an action from nothing... */
413 if (adt) {
414 if (!(adt->flag & ADT_NLA_EDIT_ON)) {
415 return true;
416 }
417 }
418 else {
419 /* There may not be any action/animdata yet, so, just fall back to the global setting
420 * (which may not be totally valid yet if the action editor was used and things are
421 * now in an inconsistent state)
422 */
424 Scene *scene = CTX_data_scene(C);
425
426 if (!(scene->flag & SCE_NLA_EDIT_ON)) {
427 /* For now, actions are only for the active object, and on object and shape-key levels...
428 */
429 return ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY);
430 }
431 }
432 }
433
434 /* something failed... */
435 return false;
436}
437
439{
440 ID *adt_id_owner = nullptr;
441 AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner);
442
443 /* Check for no action... */
444 if (adt->action == nullptr) {
445 /* just create a new action */
446 bAction *action = action_create_new(C, nullptr);
447 if (!blender::animrig::assign_action(action, {*adt_id_owner, *adt})) {
449 op->reports, RPT_ERROR, "Could not assign a new Action to %s", adt_id_owner->name + 2);
450 }
451 }
452 else if (adt) {
453 /* Perform stashing operation */
454 if (BKE_nla_action_stash({*adt_id_owner, *adt}, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) {
455 bAction *new_action = nullptr;
456
457 /* Create new action not based on the old one
458 * (since the "new" operator already does that). */
459 new_action = action_create_new(C, nullptr);
460 if (!blender::animrig::assign_action(new_action, {*adt_id_owner, *adt})) {
462 op->reports, RPT_ERROR, "Could not assign a new Action to %s", adt_id_owner->name + 2);
463 }
464 }
465 else {
466 /* action has already been added - simply warn about this, and clear */
467 BKE_report(op->reports, RPT_ERROR, "Action+Slot has already been stashed");
468 if (!blender::animrig::unassign_action({*adt_id_owner, *adt})) {
470 op->reports, RPT_ERROR, "Could not un-assign Action from %s", adt_id_owner->name + 2);
471 }
472 }
473 }
474
475 /* Send notifiers that stuff has changed */
477 return OPERATOR_FINISHED;
478}
479
481{
482 /* identifiers */
483 ot->name = "Stash Action";
484 ot->idname = "ACTION_OT_stash_and_create";
485 ot->description =
486 "Store this action in the NLA stack as a non-contributing strip for later use, and create a "
487 "new action";
488
489 /* callbacks */
492
493 /* flags */
495}
496
498
499/* -------------------------------------------------------------------- */
509
511 bContext *C, ID *id, AnimData *adt, bAction *act, ReportList *reports, bool force_delete)
512{
513 BLI_assert(id);
514
515 /* If the old action only has a single user (that it's about to lose),
516 * warn user about it
517 *
518 * TODO: Maybe we should just save it for them? But then, there's the problem of
519 * trying to get rid of stuff that's actually unwanted!
520 */
521 if (act->id.us == 1) {
522 BKE_reportf(reports,
524 "Action '%s' will not be saved, create Fake User or Stash in NLA Stack to retain",
525 act->id.name + 2);
526 }
527
528 /* Clear Fake User and remove action stashing strip (if present) */
529 if (force_delete) {
530 /* Remove stashed strip binding this action to this datablock */
531 /* XXX: we cannot unlink it from *OTHER* datablocks that may also be stashing it,
532 * but GE users only seem to use/care about single-object binding for now so this
533 * should be fine
534 */
535 if (adt) {
536 NlaTrack *nlt, *nlt_next;
537 NlaStrip *strip, *nstrip;
538
539 for (nlt = static_cast<NlaTrack *>(adt->nla_tracks.first); nlt; nlt = nlt_next) {
540 nlt_next = nlt->next;
541
542 if (strstr(nlt->name, DATA_("[Action Stash]"))) {
543 for (strip = static_cast<NlaStrip *>(nlt->strips.first); strip; strip = nstrip) {
544 nstrip = strip->next;
545
546 if (strip->act == act) {
547 /* Remove this strip, and the track too if it doesn't have anything else */
548 BKE_nlastrip_remove_and_free(&nlt->strips, strip, true);
549
550 if (nlt->strips.first == nullptr) {
551 BLI_assert(nstrip == nullptr);
553 }
554 }
555 }
556 }
557 }
558 }
559
560 /* Clear Fake User */
561 id_fake_user_clear(&act->id);
562 }
563
564 /* If in Tweak Mode, don't unlink. Instead, this becomes a shortcut to exit Tweak Mode. */
565 if ((adt) && (adt->flag & ADT_NLA_EDIT_ON)) {
566 BKE_nla_tweakmode_exit({*id, *adt});
567
568 Scene *scene = CTX_data_scene(C);
569 if (scene != nullptr) {
570 scene->flag &= ~SCE_NLA_EDIT_ON;
571 }
572 }
573 else {
574 /* Clear AnimData -> action via RNA, so that it triggers message bus updates. */
575 PointerRNA ptr = RNA_pointer_create_discrete(id, &RNA_AnimData, adt);
576 PropertyRNA *prop = RNA_struct_find_property(&ptr, "action");
577
579 RNA_property_update(C, &ptr, prop);
580 }
581}
582
583/* -------------------------- */
584
586{
587 ID *animated_id = nullptr;
588 AnimData *adt = ED_actedit_animdata_from_context(C, &animated_id);
589 if (!animated_id) {
590 return false;
591 }
592 if (!BKE_id_is_editable(CTX_data_main(C), animated_id)) {
593 return false;
594 }
595 return adt && adt->action;
596}
597
599{
600 ID *animated_id = nullptr;
601 AnimData *adt = ED_actedit_animdata_from_context(C, &animated_id);
602 bool force_delete = RNA_boolean_get(op->ptr, "force_delete");
603
604 if (adt && adt->action) {
605 ED_animedit_unlink_action(C, animated_id, adt, adt->action, op->reports, force_delete);
606 }
607
608 /* Unlink is also abused to exit NLA tweak mode. */
610
611 return OPERATOR_FINISHED;
612}
613
615{
616 /* NOTE: this is hardcoded to match the behavior for the unlink button
617 * (in `interface_templates.cc`). */
618 RNA_boolean_set(op->ptr, "force_delete", event->modifier & KM_SHIFT);
619 return action_unlink_exec(C, op);
620}
621
623{
624 PropertyRNA *prop;
625
626 /* identifiers */
627 ot->name = "Unlink Action";
628 ot->idname = "ACTION_OT_unlink";
629 ot->description = "Unlink this action from the active action slot (and/or exit Tweak Mode)";
630
631 /* callbacks */
632 ot->invoke = action_unlink_invoke;
633 ot->exec = action_unlink_exec;
634 ot->poll = action_unlink_poll;
635
636 /* properties */
637 prop = RNA_def_boolean(ot->srna,
638 "force_delete",
639 false,
640 "Force Delete",
641 "Clear Fake User and remove "
642 "copy stashed in this data-block's NLA stack");
644
645 /* flags */
647}
648
Functions and classes to work with Actions.
Blender kernel action and pose functionality.
bAction * BKE_action_add(Main *bmain, const char name[])
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:1791
bool BKE_id_is_editable(const Main *bmain, const ID *id)
Definition lib_id.cc:2523
void id_fake_user_clear(ID *id)
Definition lib_id.cc:404
ID * BKE_id_copy(Main *bmain, const ID *id)
Definition lib_id.cc:782
void id_us_min(ID *id)
Definition lib_id.cc:366
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)
bool BKE_nla_action_stash(OwnedAnimData owned_adt, bool is_liboverride)
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
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ELEM(...)
#define DATA_(msgid)
@ ID_RECALC_ANIMATION
Definition DNA_ID.h:1077
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:730
@ ID_AC
@ SACTCONT_ACTION
@ SACTCONT_SHAPEKEY
@ ADT_NLA_EDIT_ON
Object is a sort of wrapper for general info.
@ SCE_NLA_EDIT_ON
@ SPACE_ACTION
@ OPERATOR_FINISHED
bool ED_operator_action_active(bContext *C)
bool ED_operator_nla_active(bContext *C)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
#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:498
@ KM_SHIFT
Definition WM_types.hh:278
#define NC_ANIMATION
Definition WM_types.hh:388
#define NA_ADDED
Definition WM_types.hh:586
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_KEYFRAME
Definition WM_types.hh:494
static bool action_unlink_poll(bContext *C)
static wmOperatorStatus action_stash_exec(bContext *C, wmOperator *op)
AnimData * ED_actedit_animdata_from_context(const bContext *C, ID **r_adt_id_owner)
void ACTION_OT_push_down(wmOperatorType *ot)
static bAction * action_create_new(bContext *C, bAction *oldact)
static bool action_new_poll(bContext *C)
void ACTION_OT_unlink(wmOperatorType *ot)
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 wmOperatorStatus action_new_exec(bContext *C, wmOperator *)
void ACTION_OT_new(wmOperatorType *ot)
#define GS(x)
#define printf(...)
DEG_id_tag_update_ex(cb_data->bmain, cb_data->owner_id, ID_RECALC_TAG_FOR_UNDO|ID_RECALC_SYNC_TO_EVAL)
bool unassign_action(ID &animated_id)
bool assign_action(bAction *action, ID &animated_id)
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
ListBase nla_tracks
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
int us
Definition DNA_ID.h:443
struct AnimData * adt
void * first
struct NlaStrip * next
bAction * act
ListBase strips
struct NlaTrack * next
char name[64]
struct AnimData * adt
ID * owner_id
Definition RNA_types.hh:51
wmEventModifierFlag modifier
Definition WM_types.hh:774
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:4238
wmOperatorType * ot
Definition wm_files.cc:4237