Blender V5.0
editors/animation/keyframing.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 <cstdio>
10
11#include <fmt/format.h>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_math_base.h"
16#include "BLI_string.h"
17
18#include "BLT_translation.hh"
19
20#include "DNA_ID.h"
21#include "DNA_anim_types.h"
22#include "DNA_armature_types.h"
23#include "DNA_scene_types.h"
24#include "DNA_sequence_types.h"
25
26#include "BKE_action.hh"
27#include "BKE_anim_data.hh"
28#include "BKE_animsys.h"
29#include "BKE_armature.hh"
30#include "BKE_context.hh"
31#include "BKE_fcurve.hh"
32#include "BKE_global.hh"
33#include "BKE_idtype.hh"
34#include "BKE_lib_id.hh"
35#include "BKE_nla.hh"
36#include "BKE_report.hh"
37#include "BKE_scene.hh"
38
39#include "DEG_depsgraph.hh"
41
42#include "ED_anim_api.hh"
43#include "ED_keyframing.hh"
44#include "ED_object.hh"
45#include "ED_screen.hh"
46#include "ED_sequencer.hh"
47
48#include "ANIM_action.hh"
50#include "ANIM_animdata.hh"
51#include "ANIM_armature.hh"
53#include "ANIM_driver.hh"
54#include "ANIM_fcurve.hh"
55#include "ANIM_keyframing.hh"
56#include "ANIM_keyingsets.hh"
57#include "ANIM_rna.hh"
58
59#include "SEQ_relations.hh"
60
61#include "UI_interface.hh"
63#include "UI_resources.hh"
64
65#include "WM_api.hh"
66#include "WM_types.hh"
67
68#include "RNA_access.hh"
69#include "RNA_define.hh"
70#include "RNA_enum_types.hh"
71#include "RNA_path.hh"
72#include "RNA_prototypes.hh"
73
74#include "anim_intern.hh"
75
77 PropertyRNA *prop,
78 Scene *scene);
79
81
82/* ******************************************* */
83/* Animation Data Validation */
84
86{
87 PointerRNA tmp_ptr;
88 PropertyRNA *prop;
89 int old_flag = fcu->flag;
90
91 if ((ptr->owner_id == nullptr) && (ptr->data == nullptr)) {
92 BKE_report(reports, RPT_ERROR, "No RNA pointer available to retrieve values for this F-curve");
93 return;
94 }
95
96 /* try to get property we should be affecting */
97 if (RNA_path_resolve_property(ptr, fcu->rna_path, &tmp_ptr, &prop) == false) {
98 /* property not found... */
99 const char *idname = (ptr->owner_id) ? ptr->owner_id->name : RPT_("<No ID pointer>");
100
101 BKE_reportf(reports,
102 RPT_ERROR,
103 "Could not update flags for this F-curve, as RNA path is invalid for the given ID "
104 "(ID = %s, path = %s)",
105 idname,
106 fcu->rna_path);
107 return;
108 }
109
110 /* update F-Curve flags */
112
113 if (old_flag != fcu->flag) {
114 /* Same as if keyframes had been changed */
116 }
117}
118
119/* ------------------------- Insert Key API ------------------------- */
120
121void ED_keyframes_add(FCurve *fcu, int num_keys_to_add)
122{
123 BLI_assert_msg(num_keys_to_add >= 0, "cannot remove keyframes with this function");
124
125 if (num_keys_to_add == 0) {
126 return;
127 }
128
129 fcu->bezt = static_cast<BezTriple *>(
130 MEM_recallocN(fcu->bezt, sizeof(BezTriple) * (fcu->totvert + num_keys_to_add)));
131 BezTriple *bezt = fcu->bezt + fcu->totvert; /* Pointer to the first new one. */
132
133 fcu->totvert += num_keys_to_add;
134
135 /* Iterate over the new keys to update their settings. */
136 while (num_keys_to_add--) {
137 /* Defaults, ignoring user-preference gives predictable results for API. */
138 bezt->f1 = bezt->f2 = bezt->f3 = SELECT;
139 bezt->ipo = BEZT_IPO_BEZ;
140 bezt->h1 = bezt->h2 = HD_AUTO_ANIM;
141 bezt++;
142 }
143}
144
145/* ******************************************* */
146/* KEYFRAME MODIFICATION */
147
148/* mode for commonkey_modifykey */
149enum {
152} /*eCommonModifyKey_Modes*/;
153
160{
161 ScrArea *area = CTX_wm_area(C);
162 Scene *scene = CTX_data_scene(C);
163
164 /* if no area or active scene */
165 if (ELEM(nullptr, area, scene)) {
166 return false;
167 }
168
169 /* should be fine */
170 return true;
171}
172
173/* Insert Key Operator ------------------------ */
174
176{
177 const bool is_sequencer = CTX_wm_space_seq(C) != nullptr;
178 Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C);
179 if (!scene) {
180 return OPERATOR_CANCELLED;
181 }
182 Object *obedit = CTX_data_edit_object(C);
183 bool ob_edit_mode = false;
184
185 const float cfra = BKE_scene_frame_get(scene);
186 const bool confirm = op->flag & OP_IS_INVOKE;
187 /* exit the edit mode to make sure that those object data properties that have been
188 * updated since the last switching to the edit mode will be keyframed correctly
189 */
190 if (obedit && blender::animrig::keyingset_find_id(ks, static_cast<ID *>(obedit->data))) {
192 ob_edit_mode = true;
193 }
194
195 /* try to insert keyframes for the channels specified by KeyingSet */
196 const int num_channels = blender::animrig::apply_keyingset(
197 C, nullptr, ks, blender::animrig::ModifyKeyMode::INSERT, cfra);
198 if (G.debug & G_DEBUG) {
200 RPT_INFO,
201 "Keying set '%s' - successfully added %d keyframes",
202 ks->name,
203 num_channels);
204 }
205
206 /* restore the edit mode if necessary */
207 if (ob_edit_mode) {
209 }
210
211 /* report failure or do updates? */
212 if (num_channels < 0) {
213 BKE_report(op->reports, RPT_ERROR, "No suitable context info for active keying set");
214 return OPERATOR_CANCELLED;
215 }
216
217 if (num_channels > 0) {
218 /* send notifiers that keyframes have been changed */
221 }
222
223 if (confirm) {
224 /* if called by invoke (from the UI), make a note that we've inserted keyframes */
225 if (num_channels > 0) {
227 RPT_INFO,
228 "Successfully added %d keyframes for keying set '%s'",
229 num_channels,
230 ks->name);
231 }
232 else {
233 BKE_report(op->reports, RPT_WARNING, "Keying set failed to insert any keyframes");
234 }
235 }
236
237 return OPERATOR_FINISHED;
238}
239
241{
242 eRotationModes rotation_mode;
244
245 if (ptr->type == &RNA_Strip || RNA_struct_is_a(ptr->type, &RNA_Strip)) {
246 eKeyInsertChannels insert_channel_flags = eKeyInsertChannels(U.key_insert_channels);
247 if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_LOCATION) {
248 paths.append({"transform.offset_x"});
249 paths.append({"transform.offset_y"});
250 }
251 if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_ROTATION) {
252 paths.append({"transform.rotation"});
253 }
254 if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_SCALE) {
255 paths.append({"transform.scale_x"});
256 paths.append({"transform.scale_y"});
257 }
258 if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_CUSTOM_PROPERTIES) {
260 }
261 return paths;
262 }
263
264 if (ptr->type == &RNA_PoseBone) {
265 bPoseChannel *pchan = static_cast<bPoseChannel *>(ptr->data);
266 rotation_mode = eRotationModes(pchan->rotmode);
267 }
268 else if (ptr->type == &RNA_Object) {
269 Object *ob = static_cast<Object *>(ptr->data);
270 rotation_mode = eRotationModes(ob->rotmode);
271 }
272 else {
273 /* Pointer type not supported. */
274 return paths;
275 }
276
277 eKeyInsertChannels insert_channel_flags = eKeyInsertChannels(U.key_insert_channels);
278 if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_LOCATION) {
279 paths.append({"location"});
280 }
281 if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_ROTATION) {
282 switch (rotation_mode) {
283 case ROT_MODE_QUAT:
284 paths.append({"rotation_quaternion"});
285 break;
287 paths.append({"rotation_axis_angle"});
288 break;
289 case ROT_MODE_XYZ:
290 case ROT_MODE_XZY:
291 case ROT_MODE_YXZ:
292 case ROT_MODE_YZX:
293 case ROT_MODE_ZXY:
294 case ROT_MODE_ZYX:
295 paths.append({"rotation_euler"});
296 default:
297 break;
298 }
299 }
300 if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_SCALE) {
301 paths.append({"scale"});
302 }
303 if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_ROTATION_MODE) {
304 paths.append({"rotation_mode"});
305 }
306
307 if (insert_channel_flags & USER_ANIM_KEY_CHANNEL_CUSTOM_PROPERTIES) {
309 }
310 return paths;
311}
312
313/* Fill the list with items depending on the mode of the context. */
315{
316 const eContextObjectMode context_mode = CTX_data_mode_enum(C);
317 ScrArea *area = CTX_wm_area(C);
318
319 if (area && area->spacetype == SPACE_SEQ) {
321 for (Strip *strip : strips) {
323 ptr = RNA_pointer_create_discrete(&CTX_data_scene(C)->id, &RNA_Strip, strip);
324 r_selection->append(ptr);
325 }
326 return true;
327 }
328
329 switch (context_mode) {
330 case CTX_MODE_OBJECT: {
331 CTX_data_selected_objects(C, r_selection);
332 break;
333 }
334 case CTX_MODE_POSE: {
335 CTX_data_selected_pose_bones(C, r_selection);
336 break;
337 }
338 default:
339 return false;
340 }
341
342 return true;
343}
344
346{
347 using namespace blender;
348
350 const bool found_selection = get_selection(C, &selection);
351 if (!found_selection) {
352 BKE_reportf(op->reports, RPT_ERROR, "Unsupported context mode");
353 return OPERATOR_CANCELLED;
354 }
355
356 if (selection.is_empty()) {
357 BKE_reportf(op->reports, RPT_WARNING, "Nothing selected to key");
358 return OPERATOR_CANCELLED;
359 }
360
361 Main *bmain = CTX_data_main(C);
362 Scene *scene = CTX_data_scene(C);
363 const float scene_frame = BKE_scene_frame_get(scene);
364
365 const eInsertKeyFlags insert_key_flags = animrig::get_keyframing_flags(scene);
371
372 animrig::CombinedKeyingResult combined_result;
374 for (PointerRNA &id_ptr : selection) {
375 ID *selected_id = id_ptr.owner_id;
376 ids.add(selected_id);
377 if (!id_can_have_animdata(selected_id)) {
379 RPT_ERROR,
380 "Could not insert keyframe, as this type does not support animation data (ID = "
381 "%s)",
382 selected_id->name);
383 continue;
384 }
385 if (!BKE_id_is_editable(bmain, selected_id)) {
386 BKE_reportf(op->reports, RPT_ERROR, "'%s' is not editable", selected_id->name + 2);
387 continue;
388 }
389 Vector<RNAPath> rna_paths = construct_rna_paths(&id_ptr);
390
391 combined_result.merge(animrig::insert_keyframes(bmain,
392 &id_ptr,
393 std::nullopt,
394 rna_paths.as_span(),
395 scene_frame,
396 anim_eval_context,
397 key_type,
398 insert_key_flags));
399 }
400
401 if (combined_result.get_count(animrig::SingleKeyingResult::SUCCESS) == 0) {
402 combined_result.generate_reports(op->reports);
403 }
404
405 for (ID *id : ids) {
407 }
408
411
412 return OPERATOR_FINISHED;
413}
414
416{
418
419 const bool is_sequencer = CTX_wm_space_seq(C) != nullptr;
420 Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C);
421 if (!scene) {
422 return OPERATOR_CANCELLED;
423 }
424 /* Use the active keying set if there is one. */
425 const int type = RNA_enum_get(op->ptr, "type");
427 if (ks) {
428 return insert_key_with_keyingset(C, op, ks);
429 }
430 return insert_key(C, op);
431}
432
434{
435 /* The depsgraph needs to be in an evaluated state to ensure the values we get from the
436 * properties are actually the values of the current frame. However we cannot do that in the exec
437 * function, as that would mean every call to the operator via python has to re-evaluate the
438 * depsgraph, causing performance regressions. */
440 return insert_key_exec(C, op);
441}
442
444{
445 /* identifiers */
446 ot->name = "Insert Keyframe";
447 ot->idname = "ANIM_OT_keyframe_insert";
448 ot->description =
449 "Insert keyframes on the current frame using either the active keying set, or the user "
450 "preferences if no keying set is active";
451
452 /* callbacks */
453 ot->invoke = insert_key_invoke;
454 ot->exec = insert_key_exec;
455 ot->poll = modify_key_op_poll;
456
457 /* flags */
459
460 /* Allows passing in a keying set when using the Python operator. */
462 ot->srna, "type", rna_enum_dummy_DEFAULT_items, 0, "Keying Set", "The Keying Set to use");
465 ot->prop = prop;
466}
467
469{
471
472 const bool is_sequencer = CTX_wm_space_seq(C) != nullptr;
473 Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C);
474 if (!scene) {
475 return OPERATOR_CANCELLED;
476 }
478 if (ks == nullptr) {
479 return OPERATOR_CANCELLED;
480 }
481 return insert_key_with_keyingset(C, op, ks);
482}
483
485{
486 PropertyRNA *prop;
487
488 /* identifiers */
489 ot->name = "Insert Keyframe (by name)";
490 ot->idname = "ANIM_OT_keyframe_insert_by_name";
491 ot->description = "Alternate access to 'Insert Keyframe' for keymaps to use";
492
493 /* callbacks */
495 ot->poll = modify_key_op_poll;
496
497 /* flags */
499
500 /* keyingset to use (idname) */
501 prop = RNA_def_string(
502 ot->srna, "type", nullptr, MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use");
506 ot->prop = prop;
507}
508
509/* Insert Key Operator (With Menu) ------------------------ */
510/* This operator checks if a menu should be shown for choosing the KeyingSet to use,
511 * then calls the menu if necessary before
512 */
513
515 wmOperator *op,
516 const wmEvent * /*event*/)
517{
518 const bool is_sequencer = CTX_wm_space_seq(C) != nullptr;
519 Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C);
520 if (!scene) {
521 return OPERATOR_CANCELLED;
522 }
523
524 /* When there is an active keying set and no request to prompt, keyframe immediately. */
525 if ((scene->active_keyingset != 0) && !RNA_boolean_get(op->ptr, "always_prompt")) {
526 /* Just call the exec() on the active keying-set. */
527 RNA_enum_set(op->ptr, "type", 0);
528 return op->type->exec(C, op);
529 }
530
531 /* Show a menu listing all keying-sets, the enum is expanded here to make use of the
532 * operator that accesses the keying-set by name. This is important for the ability
533 * to assign shortcuts to arbitrarily named keying sets. See #89560.
534 * These menu items perform the key-frame insertion (not this operator)
535 * hence the #OPERATOR_INTERFACE return. */
537 C, WM_operatortype_name(op->type, op->ptr).c_str(), ICON_NONE);
538 uiLayout *layout = UI_popup_menu_layout(pup);
539
540 /* Even though `ANIM_OT_keyframe_insert_menu` can show a menu in one line,
541 * prefer `ANIM_OT_keyframe_insert_by_name` so users can bind keys to specific
542 * keying sets by name in the key-map instead of the index which isn't stable. */
543 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "type");
544 const EnumPropertyItem *item_array = nullptr;
545 int totitem;
546 bool free;
547
548 RNA_property_enum_items_gettexted(C, op->ptr, prop, &item_array, &totitem, &free);
549
550 for (int i = 0; i < totitem; i++) {
551 const EnumPropertyItem *item = &item_array[i];
552 if (item->identifier[0] != '\0') {
553 PointerRNA op_ptr = layout->op("ANIM_OT_keyframe_insert_by_name", item->name, item->icon);
554 RNA_string_set(&op_ptr, "type", item->identifier);
555 }
556 else {
557 /* This enum shouldn't contain headings, assert there are none.
558 * NOTE: If in the future the enum includes them, additional layout code can be
559 * added to show them - although that doesn't seem likely. */
560 BLI_assert(item->name == nullptr);
561 layout->separator();
562 }
563 }
564
565 if (free) {
566 MEM_freeN(item_array);
567 }
568
569 UI_popup_menu_end(C, pup);
570
571 return OPERATOR_INTERFACE;
572}
573
575{
576 PropertyRNA *prop;
577
578 /* identifiers */
579 ot->name = "Insert Keyframe Menu";
580 ot->idname = "ANIM_OT_keyframe_insert_menu";
581 ot->description =
582 "Insert Keyframes for specified Keying Set, with menu of available Keying Sets if undefined";
583
584 /* callbacks */
585 ot->invoke = insert_key_menu_invoke;
588
589 /* flags */
591
592 /* keyingset to use (dynamic enum) */
593 prop = RNA_def_enum(
594 ot->srna, "type", rna_enum_dummy_DEFAULT_items, 0, "Keying Set", "The Keying Set to use");
597 ot->prop = prop;
598
599 /* whether the menu should always be shown
600 * - by default, the menu should only be shown when there is no active Keying Set (2.5 behavior),
601 * although in some cases it might be useful to always shown (pre 2.5 behavior)
602 */
603 prop = RNA_def_boolean(ot->srna, "always_prompt", false, "Always Show Menu", "");
605}
606
607/* Delete Key Operator ------------------------ */
608
610{
611 const bool is_sequencer = CTX_wm_space_seq(C) != nullptr;
612 Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C);
613 if (!scene) {
614 return OPERATOR_CANCELLED;
615 }
617 if (ks == nullptr) {
618 return OPERATOR_CANCELLED;
619 }
620
621 return delete_key_using_keying_set(C, op, ks);
622}
623
625{
626 const bool is_sequencer = CTX_wm_space_seq(C) != nullptr;
627 Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C);
628 if (!scene) {
629 return OPERATOR_CANCELLED;
630 }
631 float cfra = BKE_scene_frame_get(scene);
632 int num_channels;
633 const bool confirm = op->flag & OP_IS_INVOKE;
634
635 /* Try to delete keyframes for the channels specified by KeyingSet. */
638 if (G.debug & G_DEBUG) {
639 printf("KeyingSet '%s' - Successfully removed %d Keyframes\n", ks->name, num_channels);
640 }
641
642 /* Report failure or do updates? */
643 if (num_channels < 0) {
644 BKE_report(op->reports, RPT_ERROR, "No suitable context info for active keying set");
645 return OPERATOR_CANCELLED;
646 }
647
648 if (num_channels > 0) {
650
651 /* VSE notifiers. */
654 }
655
656 if (confirm) {
657 /* If called by invoke (from the UI), make a note that we've removed keyframes. */
658 if (num_channels > 0) {
660 RPT_INFO,
661 "Successfully removed %d keyframes for keying set '%s'",
662 num_channels,
663 ks->name);
664 }
665 else {
666 BKE_report(op->reports, RPT_WARNING, "Keying set failed to remove any keyframes");
667 }
668 }
669 return OPERATOR_FINISHED;
670}
671
673{
674 PropertyRNA *prop;
675
676 /* identifiers */
677 ot->name = "Delete Keying-Set Keyframe";
678 ot->idname = "ANIM_OT_keyframe_delete";
679 ot->description =
680 "Delete keyframes on the current frame for all properties in the specified Keying Set";
681
682 /* callbacks */
683 ot->exec = delete_key_exec;
684 ot->poll = modify_key_op_poll;
685
686 /* flags */
688
689 /* keyingset to use (dynamic enum) */
690 prop = RNA_def_enum(
691 ot->srna, "type", rna_enum_dummy_DEFAULT_items, 0, "Keying Set", "The Keying Set to use");
694 ot->prop = prop;
695}
696
698{
699 PropertyRNA *prop;
700
701 /* identifiers */
702 ot->name = "Delete Keying-Set Keyframe (by name)";
703 ot->idname = "ANIM_OT_keyframe_delete_by_name";
704 ot->description = "Alternate access to 'Delete Keyframe' for keymaps to use";
705
706 /* callbacks */
707 ot->exec = delete_key_exec;
708 ot->poll = modify_key_op_poll;
709
710 /* flags */
712
713 /* keyingset to use (idname) */
714 prop = RNA_def_string(
715 ot->srna, "type", nullptr, MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use");
719 ot->prop = prop;
720}
721
722/* Delete Key Operator ------------------------ */
723/* NOTE: Although this version is simpler than the more generic version for KeyingSets,
724 * it is more useful for animators working in the 3D view.
725 */
726
727/* While in pose mode, the selection of bones has to be considered. */
728static bool can_delete_fcurve(FCurve *fcu, Object *ob)
729{
730 bool can_delete = false;
731 /* in pose mode, only delete the F-Curve if it belongs to a selected bone */
732 if (ob->mode & OB_MODE_POSE) {
733 if (fcu->rna_path) {
734 /* Get bone-name, and check if this bone is selected. */
735 bPoseChannel *pchan = nullptr;
736 char bone_name[sizeof(pchan->name)];
737 if (BLI_str_quoted_substr(fcu->rna_path, "pose.bones[", bone_name, sizeof(bone_name))) {
738 pchan = BKE_pose_channel_find_name(ob->pose, bone_name);
739 /* Delete if bone is selected. */
740 if ((pchan) && (pchan->bone)) {
741 if (pchan->flag & POSE_SELECTED) {
742 can_delete = true;
743 }
744 }
745 }
746 }
747 }
748 else {
749 /* object mode - all of Object's F-Curves are affected */
750 /* TODO: this logic isn't solid. Only delete FCurves of the object, not of bones in this case.
751 */
752 can_delete = true;
753 }
754
755 return can_delete;
756}
757
759{
760 using namespace blender::animrig;
761 bool changed = false;
762
763 CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
764 /* just those in active action... */
765 if ((ob->adt) && (ob->adt->action)) {
766 AnimData *adt = ob->adt;
767 bAction *dna_action = adt->action;
768 FCurve *fcu, *fcn;
769
770 Action &action = dna_action->wrap();
771 if (action.is_action_layered()) {
772 blender::Vector<FCurve *> fcurves_to_delete;
773 foreach_fcurve_in_action_slot(action, adt->slot_handle, [&](FCurve &fcurve) {
774 if (can_delete_fcurve(&fcurve, ob)) {
775 fcurves_to_delete.append(&fcurve);
776 }
777 });
778 for (FCurve *fcurve : fcurves_to_delete) {
779 action_fcurve_remove(action, *fcurve);
781 changed = true;
782 }
783 DEG_id_tag_update(&ob->adt->action->id, ID_RECALC_ANIMATION_NO_FLUSH);
784 }
785 else {
786 for (fcu = static_cast<FCurve *>(dna_action->curves.first); fcu; fcu = fcn) {
787 fcn = fcu->next;
788 /* delete F-Curve completely */
789 if (can_delete_fcurve(fcu, ob)) {
792 changed = true;
793 }
794 }
795 }
796
797 /* Delete the action itself if it is empty. */
798 if (action.is_action_legacy() && blender::animrig::animdata_remove_empty_action(adt)) {
799 changed = true;
800 }
801 }
802 }
804
805 if (!changed) {
806 return OPERATOR_CANCELLED;
807 }
808
809 /* send updates */
811
812 return OPERATOR_FINISHED;
813}
814
816 wmOperator *op,
817 const wmEvent * /*event*/)
818{
819 if (RNA_boolean_get(op->ptr, "confirm")) {
821 op,
822 IFACE_("Remove animation from selected objects?"),
823 nullptr,
826 false);
827 }
828 return clear_anim_v3d_exec(C, op);
829}
830
832{
833 /* identifiers */
834 ot->name = "Remove Animation";
835 ot->description = "Remove all keyframe animation for selected objects";
836 ot->idname = "ANIM_OT_keyframe_clear_v3d";
837
838 /* callbacks */
839 ot->invoke = clear_anim_v3d_invoke;
840 ot->exec = clear_anim_v3d_exec;
841
843
844 /* flags */
847}
848
851{
852 blender::Vector<std::string> selected_strips_rna_paths;
853 for (PointerRNA &id_ptr : selection) {
854 if (RNA_struct_is_a(id_ptr.type, &RNA_Strip)) {
855 std::optional<std::string> rna_path = RNA_path_from_ID_to_struct(&id_ptr);
856 selected_strips_rna_paths.append(*rna_path);
857 }
858 }
859 return selected_strips_rna_paths;
860}
861
863{
864 for (PointerRNA &id_ptr : selection) {
865 if (RNA_struct_is_a(id_ptr.type, &RNA_Strip)) {
866 ::Strip *strip = static_cast<::Strip *>(id_ptr.data);
868 }
869 }
870}
871
872static bool fcurve_belongs_to_strip(const FCurve &fcurve, const std::string &strip_path)
873{
874 return fcurve.rna_path &&
875 std::strncmp(fcurve.rna_path, strip_path.c_str(), strip_path.length()) == 0;
876}
877
879{
880 using namespace blender::animrig;
881 bool changed = false;
882
884
886 blender::Vector<std::string> selected_strips_rna_paths;
887 get_selection(C, &selection);
888 selected_strips_rna_paths = get_selected_strips_rna_paths(selection);
889
890 if (selected_strips_rna_paths.is_empty()) {
891 BKE_reportf(op->reports, RPT_WARNING, "No strips selected");
892 return OPERATOR_CANCELLED;
893 }
894
895 if (!scene->adt || !scene->adt->action || (scene->adt->slot_handle == Slot::unassigned)) {
896 BKE_reportf(op->reports, RPT_ERROR, "Scene has no animation data or active action");
897 return OPERATOR_CANCELLED;
898 }
899
900 AnimData *adt = scene->adt;
901 bAction *dna_action = adt->action;
902
903 Action &action = dna_action->wrap();
904 blender::Vector<FCurve *> fcurves_to_delete;
905 foreach_fcurve_in_action_slot(action, adt->slot_handle, [&](FCurve &fcurve) {
906 for (const std::string &strip_path : selected_strips_rna_paths) {
907 if (fcurve_belongs_to_strip(fcurve, strip_path)) {
908 fcurves_to_delete.append(&fcurve);
909 break;
910 }
911 }
912 });
913 for (FCurve *fcurve : fcurves_to_delete) {
914 action_fcurve_remove(action, *fcurve);
915 changed = true;
916 }
917
918 if (!changed) {
919 return OPERATOR_CANCELLED;
920 }
921
922 if (scene->adt->action) {
923 DEG_id_tag_update(&scene->adt->action->id, ID_RECALC_ANIMATION_NO_FLUSH);
924 }
925 invalidate_strip_caches(selection, scene);
928
929 return OPERATOR_FINISHED;
930}
931
933 wmOperator *op,
934 const wmEvent * /*event*/)
935{
936 if (RNA_boolean_get(op->ptr, "confirm")) {
938 op,
939 IFACE_("Remove animation from selected strips?"),
940 nullptr,
943 false);
944 }
945 return clear_anim_vse_exec(C, op);
946}
948{
949 /* identifiers */
950 ot->name = "Remove Animation";
951 ot->description = "Remove all keyframe animation for selected strips";
952 ot->idname = "ANIM_OT_keyframe_clear_vse";
953
954 /* callbacks */
955 ot->invoke = clear_anim_vse_invoke;
956 ot->exec = clear_anim_vse_exec;
957
959
960 /* flags */
963}
964
965static bool can_delete_key(FCurve *fcu, Object *ob, ReportList *reports)
966{
967 /* don't touch protected F-Curves */
968 if (BKE_fcurve_is_protected(fcu)) {
969 BKE_reportf(reports,
971 "Not deleting keyframe for locked F-Curve '%s', object '%s'",
972 fcu->rna_path,
973 ob->id.name + 2);
974 return false;
975 }
976
977 /* Special exception for bones, as this makes this operator more convenient to use
978 * NOTE: This is only done in pose mode.
979 * In object mode, we're dealing with the entire object.
980 * TODO: While this means bone animation is not deleted of all bones while in pose mode. Running
981 * the code on the armature object WILL delete keys of all bones.
982 */
983 if (ob->mode & OB_MODE_POSE) {
984 bPoseChannel *pchan = nullptr;
985
986 /* Get bone-name, and check if this bone is selected. */
987 char bone_name[sizeof(pchan->name)];
988 if (!BLI_str_quoted_substr(fcu->rna_path, "pose.bones[", bone_name, sizeof(bone_name))) {
989 return false;
990 }
991 pchan = BKE_pose_channel_find_name(ob->pose, bone_name);
992
993 /* skip if bone is not selected */
994 if ((pchan) && (pchan->bone)) {
995 bArmature *arm = static_cast<bArmature *>(ob->data);
996
997 /* Only selected bones should be affected. */
998 if (!blender::animrig::bone_is_selected(arm, pchan)) {
999 return false;
1000 }
1001 }
1002 }
1003
1004 return true;
1005}
1006
1007static bool can_delete_scene_key(FCurve *fcu, Scene *scene, wmOperator *op)
1008{
1009 /* Don't touch protected F-Curves. */
1010 if (BKE_fcurve_is_protected(fcu)) {
1011 BKE_reportf(op->reports,
1013 "Not deleting keyframe for locked F-Curve '%s', scene '%s'",
1014 fcu->rna_path,
1015 scene->id.name + 2);
1016 return false;
1017 }
1018 return true;
1019}
1020
1022{
1023 using namespace blender::animrig;
1025 const float cfra = BKE_scene_frame_get(scene);
1026
1028 blender::Vector<std::string> selected_strips_rna_paths;
1029 get_selection(C, &selection);
1030 selected_strips_rna_paths = get_selected_strips_rna_paths(selection);
1031
1032 if (selected_strips_rna_paths.is_empty()) {
1033 BKE_reportf(op->reports, RPT_WARNING, "No strips selected");
1034 return OPERATOR_CANCELLED;
1035 }
1036
1037 const bool confirm = op->flag & OP_IS_INVOKE;
1038 if (!scene->adt || !scene->adt->action || (scene->adt->slot_handle == Slot::unassigned)) {
1039 BKE_reportf(op->reports, RPT_ERROR, "Scene has no animation data or active action");
1040 return OPERATOR_CANCELLED;
1041 }
1042
1043 AnimData *adt = scene->adt;
1044 bAction *act = adt->action;
1045 Action &action = act->wrap();
1046
1047 const float cfra_unmap = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
1048
1049 blender::VectorSet<std::string> modified_strips;
1050 blender::Vector<FCurve *> modified_fcurves;
1051
1052 foreach_fcurve_in_action_slot(action, adt->slot_handle, [&](FCurve &fcurve) {
1053 std::string changed_strip;
1054 for (const std::string &strip_path : selected_strips_rna_paths) {
1055 if (fcurve_belongs_to_strip(fcurve, strip_path)) {
1056 changed_strip = strip_path;
1057 break;
1058 }
1059 }
1060 if (!can_delete_scene_key(&fcurve, scene, op) || changed_strip.empty()) {
1061 return;
1062 }
1064 modified_fcurves.append(&fcurve);
1065 modified_strips.add(changed_strip);
1066 }
1067 });
1068
1069 for (FCurve *fcurve : modified_fcurves) {
1070 if (BKE_fcurve_is_empty(fcurve)) {
1071 action_fcurve_remove(action, *fcurve);
1072 }
1073 }
1074
1075 if (scene->adt->action) {
1076 /* The Action might have been unassigned, if it is legacy and the last
1077 * F-Curve was removed. */
1078 DEG_id_tag_update(&scene->adt->action->id, ID_RECALC_ANIMATION_NO_FLUSH);
1079 }
1080
1081 if (!modified_strips.is_empty()) {
1082 /* Key-frames on strips has been moved, so make sure related editors are informed. */
1085 }
1086
1087 invalidate_strip_caches(selection, scene);
1088
1089 if (confirm) {
1090 /* If called by invoke (from the UI), make a note that we've removed keyframes. */
1091 if (modified_strips.is_empty()) {
1092 const std::string msg = fmt::format(
1093 fmt::runtime(RPT_("No keyframes removed from {} strip(s)")),
1094 selected_strips_rna_paths.size());
1095 BKE_report(op->reports, RPT_WARNING, msg.c_str());
1096 return OPERATOR_CANCELLED;
1097 }
1098
1099 const std::string msg = fmt::format(
1100 fmt::runtime(RPT_("{} strip(s) successfully had {} keyframes removed")),
1101 modified_strips.size(),
1102 modified_fcurves.size());
1103 BKE_report(op->reports, RPT_INFO, msg.c_str());
1104 }
1105
1106 return OPERATOR_FINISHED;
1107}
1108
1110{
1113
1114 if (ks == nullptr) {
1116 }
1117
1118 return delete_key_using_keying_set(C, op, ks);
1119}
1120
1122 wmOperator *op,
1123 const wmEvent * /*event*/)
1124{
1125 if (RNA_boolean_get(op->ptr, "confirm")) {
1127 op,
1128 IFACE_("Delete keyframes from selected strips?"),
1129 nullptr,
1130 IFACE_("Delete"),
1132 false);
1133 }
1134 return delete_key_vse_exec(C, op);
1135}
1136
1138{
1139 /* identifiers */
1140 ot->name = "Delete Keyframe";
1141 ot->description = "Remove keyframes on current frame for selected strips";
1142 ot->idname = "ANIM_OT_keyframe_delete_vse";
1143
1144 /* callbacks */
1145 ot->invoke = delete_key_vse_invoke;
1146 ot->exec = delete_key_vse_exec;
1147
1149
1150 /* flags */
1151 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1153}
1154
1156{
1157 using namespace blender::animrig;
1158 Scene *scene = CTX_data_scene(C);
1159 const float cfra = BKE_scene_frame_get(scene);
1160
1161 int selected_objects_len = 0;
1162 int selected_objects_success_len = 0;
1163 int success_multi = 0;
1164
1165 const bool confirm = op->flag & OP_IS_INVOKE;
1166
1167 CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
1168 int success = 0;
1169
1170 selected_objects_len += 1;
1171
1172 /* just those in active action... */
1173 if ((ob->adt) && (ob->adt->action)) {
1174 AnimData *adt = ob->adt;
1175 bAction *act = adt->action;
1176 const float cfra_unmap = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
1177
1178 Action &action = act->wrap();
1179 if (action.is_action_layered()) {
1180 blender::Vector<FCurve *> modified_fcurves;
1181 foreach_fcurve_in_action_slot(action, adt->slot_handle, [&](FCurve &fcurve) {
1182 if (!can_delete_key(&fcurve, ob, op->reports)) {
1183 return;
1184 }
1186 modified_fcurves.append(&fcurve);
1187 }
1188 });
1189
1190 success += modified_fcurves.size();
1191 for (FCurve *fcurve : modified_fcurves) {
1192 if (BKE_fcurve_is_empty(fcurve)) {
1193 action_fcurve_remove(action, *fcurve);
1194 }
1195 }
1196 }
1197 else {
1198 FCurve *fcn;
1199 for (FCurve *fcu = static_cast<FCurve *>(act->curves.first); fcu; fcu = fcn) {
1200 fcn = fcu->next;
1201 if (!can_delete_key(fcu, ob, op->reports)) {
1202 continue;
1203 }
1204 /* Delete keyframes on current frame
1205 * WARNING: this can delete the next F-Curve, hence the "fcn" copying.
1206 */
1207 success += delete_keyframe_fcurve_legacy(adt, fcu, cfra_unmap);
1208 }
1209 }
1210
1211 if (ob->adt->action) {
1212 /* The Action might have been unassigned, if it is legacy and the last
1213 * F-Curve was removed. */
1214 DEG_id_tag_update(&ob->adt->action->id, ID_RECALC_ANIMATION_NO_FLUSH);
1215 }
1216 }
1217
1218 /* Only for reporting. */
1219 if (success) {
1220 selected_objects_success_len += 1;
1221 success_multi += success;
1222 }
1223
1225 }
1227
1228 if (selected_objects_success_len) {
1229 /* send updates */
1231 }
1232
1233 if (confirm) {
1234 /* if called by invoke (from the UI), make a note that we've removed keyframes */
1235 if (selected_objects_success_len) {
1236 BKE_reportf(op->reports,
1237 RPT_INFO,
1238 "%d object(s) successfully had %d keyframes removed",
1239 selected_objects_success_len,
1240 success_multi);
1241 }
1242 else {
1244 op->reports, RPT_ERROR, "No keyframes removed from %d object(s)", selected_objects_len);
1245 }
1246 }
1247 return OPERATOR_FINISHED;
1248}
1249
1251{
1252 Scene *scene = CTX_data_scene(C);
1254
1255 if (ks == nullptr) {
1257 }
1258
1259 return delete_key_using_keying_set(C, op, ks);
1260}
1261
1263 wmOperator *op,
1264 const wmEvent * /*event*/)
1265{
1266 if (RNA_boolean_get(op->ptr, "confirm")) {
1268 op,
1269 IFACE_("Delete keyframes from selected objects?"),
1270 nullptr,
1271 IFACE_("Delete"),
1273 false);
1274 }
1275 return delete_key_v3d_exec(C, op);
1276}
1277
1279{
1280 /* identifiers */
1281 ot->name = "Delete Keyframe";
1282 ot->description = "Remove keyframes on current frame for selected objects and bones";
1283 ot->idname = "ANIM_OT_keyframe_delete_v3d";
1284
1285 /* callbacks */
1286 ot->invoke = delete_key_v3d_invoke;
1287 ot->exec = delete_key_v3d_exec;
1288
1289 ot->poll = ED_operator_areaactive;
1290
1291 /* flags */
1292 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1294}
1295
1296/* Insert Key Button Operator ------------------------ */
1297
1299{
1300 using namespace blender::animrig;
1301 Main *bmain = CTX_data_main(C);
1302 const bool is_sequencer = CTX_wm_space_seq(C) != nullptr;
1303 Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C);
1304 if (!scene) {
1305 return OPERATOR_CANCELLED;
1306 }
1307 ToolSettings *ts = scene->toolsettings;
1308 PointerRNA ptr = {};
1309 PropertyRNA *prop = nullptr;
1310 uiBut *but;
1313 bool changed = false;
1314 int index;
1315 const bool all = RNA_boolean_get(op->ptr, "all");
1317
1318 flag = get_keyframing_flags(scene);
1319
1320 if (!(but = UI_context_active_but_prop_get(C, &ptr, &prop, &index))) {
1321 /* pass event on if no active button found */
1323 }
1324
1325 if ((ptr.owner_id && ptr.data && prop) && RNA_property_anim_editable(&ptr, prop)) {
1326 if (ptr.type == &RNA_NlaStrip) {
1327 /* Handle special properties for NLA Strips, whose F-Curves are stored on the
1328 * strips themselves. These are stored separately or else the properties will
1329 * not have any effect.
1330 */
1331 NlaStrip *strip = static_cast<NlaStrip *>(ptr.data);
1332 FCurve *fcu = BKE_fcurve_find(&strip->fcurves, RNA_property_identifier(prop), index);
1333
1334 if (fcu) {
1335 changed = insert_keyframe_direct(op->reports,
1336 ptr,
1337 prop,
1338 fcu,
1339 &anim_eval_context,
1341 nullptr,
1342 eInsertKeyFlags(0));
1343 }
1344 else {
1345 BKE_report(op->reports,
1346 RPT_ERROR,
1347 "This property cannot be animated as it will not get updated correctly");
1348 }
1349 }
1350 else if (UI_but_flag_is_set(but, UI_BUT_DRIVEN)) {
1351 /* Driven property - Find driver */
1352 FCurve *fcu;
1353 bool driven, special;
1354
1356 C, &ptr, prop, index, nullptr, nullptr, &driven, &special);
1357
1358 if (fcu && driven) {
1359 const float driver_frame = evaluate_driver_from_rna_pointer(
1360 &anim_eval_context, &ptr, prop, fcu);
1362 CTX_data_depsgraph_pointer(C), driver_frame);
1363 changed = insert_keyframe_direct(op->reports,
1364 ptr,
1365 prop,
1366 fcu,
1367 &remapped_context,
1369 nullptr,
1371 }
1372 }
1373 else {
1374 /* standard properties */
1375 if (const std::optional<std::string> path = RNA_path_from_ID_to_property(&ptr, prop)) {
1376 const char *identifier = RNA_property_identifier(prop);
1377 const std::optional<blender::StringRefNull> group = default_channel_group_for_path(
1378 &ptr, identifier);
1379
1381
1382 /* NOTE: `index == -1` is a magic number, meaning either "operate on all
1383 * elements" or "not an array property". */
1384 const std::optional<int> array_index = (all || index < 0) ? std::nullopt :
1385 std::optional(index);
1386 PointerRNA owner_ptr = RNA_id_pointer_create(ptr.owner_id);
1388 &owner_ptr,
1389 group,
1390 {{*path, {}, array_index}},
1391 std::nullopt,
1392 anim_eval_context,
1394 flag);
1395 changed = result.get_count(SingleKeyingResult::SUCCESS) != 0;
1396 }
1397 else {
1398 BKE_report(op->reports,
1400 "Failed to resolve path to property, "
1401 "try manually specifying this using a Keying Set instead");
1402 }
1403 }
1404 }
1405 else {
1406 if (prop && !RNA_property_anim_editable(&ptr, prop)) {
1407 BKE_reportf(op->reports,
1409 "\"%s\" property cannot be animated",
1411 }
1412 else {
1413 BKE_reportf(op->reports,
1415 "Button does not appear to have any property information attached (ptr.data = "
1416 "%p, prop = %p)",
1417 ptr.data,
1418 (void *)prop);
1419 }
1420 }
1421
1422 if (changed) {
1423 ID *id = ptr.owner_id;
1424 AnimData *adt = BKE_animdata_from_id(id);
1425 if (adt->action != nullptr) {
1427 }
1429
1430 /* send updates */
1432
1433 /* send notifiers that keyframes have been changed */
1435 }
1436
1437 return (changed) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1438}
1439
1441{
1442 /* identifiers */
1443 ot->name = "Insert Keyframe (Buttons)";
1444 ot->idname = "ANIM_OT_keyframe_insert_button";
1445 ot->description = "Insert a keyframe for current UI-active property";
1446
1447 /* callbacks */
1448 ot->exec = insert_key_button_exec;
1449 ot->poll = modify_key_op_poll;
1450
1451 /* flags */
1452 ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
1453
1454 /* properties */
1455 RNA_def_boolean(ot->srna, "all", true, "All", "Insert a keyframe for all element of the array");
1456}
1457
1458/* Delete Key Button Operator ------------------------ */
1459
1461{
1462 const bool is_sequencer = CTX_wm_space_seq(C) != nullptr;
1463 Scene *scene = is_sequencer ? CTX_data_sequencer_scene(C) : CTX_data_scene(C);
1464 if (!scene) {
1465 return OPERATOR_CANCELLED;
1466 }
1467 PointerRNA ptr = {};
1468 PropertyRNA *prop = nullptr;
1469 Main *bmain = CTX_data_main(C);
1470 const float cfra = BKE_scene_frame_get(scene);
1471 bool changed = false;
1472 int index;
1473 const bool all = RNA_boolean_get(op->ptr, "all");
1474
1475 if (!UI_context_active_but_prop_get(C, &ptr, &prop, &index)) {
1476 /* pass event on if no active button found */
1478 }
1479
1480 if (ptr.owner_id && ptr.data && prop) {
1482 /* Handle special properties for NLA Strips, whose F-Curves are stored on the
1483 * strips themselves. These are stored separately or else the properties will
1484 * not have any effect.
1485 */
1486 ID *id = ptr.owner_id;
1487 NlaStrip *strip = static_cast<NlaStrip *>(ptr.data);
1488 FCurve *fcu = BKE_fcurve_find(&strip->fcurves, RNA_property_identifier(prop), 0);
1489
1490 if (fcu) {
1491 if (BKE_fcurve_is_protected(fcu)) {
1493 op->reports,
1495 "Not deleting keyframe for locked F-Curve for NLA Strip influence on %s - %s '%s'",
1496 strip->name,
1498 id->name + 2);
1499 }
1500 else {
1501 /* remove the keyframe directly
1502 * NOTE: cannot use delete_keyframe_fcurve(), as that will free the curve,
1503 * and delete_keyframe() expects the FCurve to be part of an action
1504 */
1505 bool found = false;
1506 int i;
1507
1508 /* try to find index of beztriple to get rid of */
1509 i = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, cfra, fcu->totvert, &found);
1510 if (found) {
1511 /* delete the key at the index (will sanity check + do recalc afterwards) */
1514 changed = true;
1515 }
1516 }
1517 }
1518 }
1519 else {
1520 /* standard properties */
1521 if (const std::optional<std::string> path = RNA_path_from_ID_to_property(&ptr, prop)) {
1522 RNAPath rna_path = {*path, std::nullopt, index};
1523 if (all) {
1524 /* nullopt indicates operating on the entire array (or the property itself otherwise). */
1525 rna_path.index = std::nullopt;
1526 }
1527
1529 bmain, op->reports, ptr.owner_id, rna_path, cfra) != 0;
1530 }
1531 else if (G.debug & G_DEBUG) {
1532 printf("Button Delete-Key: no path to property\n");
1533 }
1534 }
1535 }
1536 else if (G.debug & G_DEBUG) {
1537 printf("ptr.data = %p, prop = %p\n", ptr.data, (void *)prop);
1538 }
1539
1540 if (changed) {
1541 /* send updates */
1543
1544 /* send notifiers that keyframes have been changed */
1546 }
1547
1548 return (changed) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1549}
1550
1552{
1553 /* identifiers */
1554 ot->name = "Delete Keyframe (Buttons)";
1555 ot->idname = "ANIM_OT_keyframe_delete_button";
1556 ot->description = "Delete current keyframe of current UI-active property";
1557
1558 /* callbacks */
1559 ot->exec = delete_key_button_exec;
1560 ot->poll = modify_key_op_poll;
1561
1562 /* flags */
1563 ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
1564
1565 /* properties */
1566 RNA_def_boolean(ot->srna, "all", true, "All", "Delete keyframes from all elements of the array");
1567}
1568
1569/* Clear Key Button Operator ------------------------ */
1570
1572{
1573 PointerRNA ptr = {};
1574 PropertyRNA *prop = nullptr;
1575 Main *bmain = CTX_data_main(C);
1576 bool changed = false;
1577 int index;
1578 const bool all = RNA_boolean_get(op->ptr, "all");
1579
1580 if (!UI_context_active_but_prop_get(C, &ptr, &prop, &index)) {
1581 /* pass event on if no active button found */
1583 }
1584
1585 if (ptr.owner_id && ptr.data && prop) {
1586 if (const std::optional<std::string> path = RNA_path_from_ID_to_property(&ptr, prop)) {
1587 RNAPath rna_path = {*path, std::nullopt, index};
1588 if (all) {
1589 /* nullopt indicates operating on the entire array (or the property itself otherwise). */
1590 rna_path.index = std::nullopt;
1591 }
1592
1593 changed |= (blender::animrig::clear_keyframe(bmain, op->reports, ptr.owner_id, rna_path) !=
1594 0);
1595 }
1596 else if (G.debug & G_DEBUG) {
1597 printf("Button Clear-Key: no path to property\n");
1598 }
1599 }
1600 else if (G.debug & G_DEBUG) {
1601 printf("ptr.data = %p, prop = %p\n", ptr.data, (void *)prop);
1602 }
1603
1604 if (changed) {
1605 /* send updates */
1607
1608 /* send notifiers that keyframes have been changed */
1610 }
1611
1612 return (changed) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1613}
1614
1616{
1617 /* identifiers */
1618 ot->name = "Clear Keyframe (Buttons)";
1619 ot->idname = "ANIM_OT_keyframe_clear_button";
1620 ot->description = "Clear all keyframes on the currently active property";
1621
1622 /* callbacks */
1623 ot->exec = clear_key_button_exec;
1624 ot->poll = modify_key_op_poll;
1625
1626 /* flags */
1627 ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
1628
1629 /* properties */
1630 RNA_def_boolean(ot->srna, "all", true, "All", "Clear keyframes from all elements of the array");
1631}
1632
1633/* ******************************************* */
1634/* KEYFRAME DETECTION */
1635
1636/* --------------- API/Per-Datablock Handling ------------------- */
1637
1639 PropertyRNA *prop,
1640 FCurve *fcu,
1641 const AnimationEvalContext *anim_eval_context)
1642{
1643 PathResolvedRNA anim_rna;
1644 anim_rna.ptr = ptr;
1645 anim_rna.prop = prop;
1646 anim_rna.prop_index = fcu->array_index;
1647
1648 int index = fcu->array_index;
1650
1651 float fcurve_val = calculate_fcurve(&anim_rna, fcu, anim_eval_context);
1652 float cur_val = (index >= 0 && index < values.size()) ? values[index] : 0.0f;
1653
1654 return !compare_ff_relative(fcurve_val, cur_val, FLT_EPSILON, 64);
1655}
1656
1657/* -------------------------------------------------------------------- */
1660
1663{
1664 KeyingSet *ks = nullptr;
1665 const int prop_type = RNA_property_type(prop);
1666 if (prop_type == PROP_ENUM) {
1667 int type = RNA_property_enum_get(op->ptr, prop);
1668 ks = ANIM_keyingset_get_from_enum_type(scene, type);
1669 if (ks == nullptr) {
1670 BKE_report(op->reports, RPT_ERROR, "No active Keying Set");
1671 }
1672 }
1673 else if (prop_type == PROP_STRING) {
1674 char type_id[MAX_ID_NAME - 2];
1675 RNA_property_string_get(op->ptr, prop, type_id);
1676
1677 if (STREQ(type_id, "__ACTIVE__")) {
1679 }
1680 else {
1681 ks = ANIM_keyingset_get_from_idname(scene, type_id);
1682 }
1683
1684 if (ks == nullptr) {
1685 BKE_reportf(op->reports, RPT_ERROR, "Keying set '%s' not found", type_id);
1686 }
1687 }
1688 else {
1689 BLI_assert(0);
1690 }
1691 return ks;
1692}
1693
Functions and classes to work with Actions.
Functionality to iterate an Action in various ways.
Functions to work with AnimData.
Functions to deal with Armatures.
C++ functions to deal with Armature collections (i.e. the successor of bone layers).
Functions to work with drivers.
Functions to modify FCurves.
Functions to insert, delete or modify keyframes.
Functionality to interact with keying sets.
Helper functions for animation to interact with the RNA system.
Blender kernel action and pose functionality.
bPoseChannel * BKE_pose_channel_find_name(const bPose *pose, const char *name)
bool id_can_have_animdata(const ID *id)
Definition anim_data.cc:73
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:83
AnimationEvalContext BKE_animsys_eval_context_construct(struct Depsgraph *depsgraph, float eval_time) ATTR_WARN_UNUSED_RESULT
Definition anim_sys.cc:738
#define CTX_DATA_BEGIN(C, Type, instance, member)
eContextObjectMode
@ CTX_MODE_OBJECT
@ CTX_MODE_POSE
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
SpaceSeq * CTX_wm_space_seq(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
bool CTX_data_selected_objects(const bContext *C, blender::Vector< PointerRNA > *list)
Main * CTX_data_main(const bContext *C)
bool CTX_data_selected_pose_bones(const bContext *C, blender::Vector< PointerRNA > *list)
Scene * CTX_data_sequencer_scene(const bContext *C)
#define CTX_DATA_END
enum eContextObjectMode CTX_data_mode_enum(const bContext *C)
int BKE_fcurve_bezt_binarysearch_index(const BezTriple array[], float frame, int arraylen, bool *r_replace)
void BKE_fcurve_handles_recalc(FCurve *fcu)
FCurve * BKE_fcurve_find_by_rna_context_ui(bContext *C, const PointerRNA *ptr, PropertyRNA *prop, int rnaindex, AnimData **r_animdata, bAction **r_action, bool *r_driven, bool *r_special)
FCurve * BKE_fcurve_find(ListBase *list, const char rna_path[], int array_index)
bool BKE_fcurve_is_empty(const FCurve *fcu)
bool BKE_fcurve_is_protected(const FCurve *fcu)
float calculate_fcurve(PathResolvedRNA *anim_rna, FCurve *fcu, const AnimationEvalContext *anim_eval_context)
void BKE_fcurve_delete_key(FCurve *fcu, int index)
@ G_DEBUG
const char * BKE_idtype_idcode_to_name(short idcode)
Definition idtype.cc:164
bool BKE_id_is_editable(const Main *bmain, const ID *id)
Definition lib_id.cc:2523
@ NLATIME_CONVERT_UNMAP
Definition BKE_nla.hh:549
bool BKE_nlastrip_has_curves_for_property(const PointerRNA *ptr, const PropertyRNA *prop)
float BKE_nla_tweakedit_remap(AnimData *adt, float cframe, eNlaTime_ConvertModes mode)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
float BKE_scene_frame_get(const Scene *scene)
Definition scene.cc:2384
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
void BLI_kdtree_nd_ free(KDTree *tree)
MINLINE int compare_ff_relative(float a, float b, float max_diff, int max_ulps)
bool bool BLI_str_quoted_substr(const char *__restrict str, const char *__restrict prefix, char *result, size_t result_maxncpy)
Definition string.cc:521
#define ELEM(...)
#define STREQ(a, b)
#define RPT_(msgid)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
ID and Library types, which are fundamental for SDNA.
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1054
@ ID_RECALC_ANIMATION_NO_FLUSH
Definition DNA_ID.h:1176
#define MAX_ID_NAME
Definition DNA_ID.h:373
eRotationModes
@ ROT_MODE_XZY
@ ROT_MODE_QUAT
@ ROT_MODE_ZXY
@ ROT_MODE_AXISANGLE
@ ROT_MODE_ZYX
@ ROT_MODE_XYZ
@ ROT_MODE_YXZ
@ ROT_MODE_YZX
@ POSE_SELECTED
eInsertKeyFlags
@ INSERTKEY_NOFLAGS
@ HD_AUTO_ANIM
@ BEZT_IPO_BEZ
eBezTriple_KeyframeType
@ OB_MODE_EDIT
@ OB_MODE_POSE
@ OB_MODE_OBJECT
@ SPACE_SEQ
eKeyInsertChannels
@ USER_ANIM_KEY_CHANNEL_ROTATION_MODE
@ USER_ANIM_KEY_CHANNEL_SCALE
@ USER_ANIM_KEY_CHANNEL_ROTATION
@ USER_ANIM_KEY_CHANNEL_LOCATION
@ USER_ANIM_KEY_CHANNEL_CUSTOM_PROPERTIES
@ OPERATOR_CANCELLED
@ OPERATOR_INTERFACE
@ OPERATOR_FINISHED
@ OPERATOR_PASS_THROUGH
bool ED_operator_sequencer_scene_editable(bContext *C)
bool ED_operator_areaactive(bContext *C)
Read Guarded memory(de)allocation.
#define MEM_recallocN(vmemh, len)
@ PROP_STRING_SEARCH_SUGGESTION
Definition RNA_types.hh:792
@ PROP_ENUM
Definition RNA_types.hh:166
@ PROP_STRING
Definition RNA_types.hh:165
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
void UI_context_update_anim_flag(const bContext *C)
@ UI_BUT_DRIVEN
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
uiPopupMenu * UI_popup_menu_begin(bContext *C, const char *title, int icon) ATTR_NONNULL()
uiBut * UI_context_active_but_prop_get(const bContext *C, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
bool UI_but_flag_is_set(uiBut *but, int flag)
@ ALERT_ICON_NONE
#define ND_SEQUENCER
Definition WM_types.hh:437
#define NC_ANIMATION
Definition WM_types.hh:388
#define NC_SCENE
Definition WM_types.hh:378
#define NA_ADDED
Definition WM_types.hh:586
@ OPTYPE_INTERNAL
Definition WM_types.hh:202
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NA_EDITED
Definition WM_types.hh:584
#define NA_REMOVED
Definition WM_types.hh:587
#define ND_KEYS
Definition WM_types.hh:463
#define ND_KEYFRAME
Definition WM_types.hh:494
#define NC_OBJECT
Definition WM_types.hh:379
void ANIM_deselect_keys_in_animation_editors(bContext *C)
Definition anim_deps.cc:478
#define U
BPy_StructRNA * depsgraph
Span< T > as_span() const
bool add(const Key &key)
Definition BLI_set.hh:248
bool add(const Key &key)
int64_t size() const
void append(const T &value)
bool is_empty() const
void extend(Span< T > array)
void merge(const CombinedKeyingResult &other)
int get_count(const SingleKeyingResult result) const
void generate_reports(ReportList *reports, eReportType report_level=RPT_ERROR)
static constexpr slot_handle_t unassigned
#define SELECT
static wmOperatorStatus delete_key_using_keying_set(bContext *C, wmOperator *op, KeyingSet *ks)
void ANIM_OT_keyframe_insert_menu(wmOperatorType *ot)
bool fcurve_is_changed(PointerRNA ptr, PropertyRNA *prop, FCurve *fcu, const AnimationEvalContext *anim_eval_context)
Lesser Keyframe Checking API call.
static wmOperatorStatus insert_key(bContext *C, wmOperator *op)
static wmOperatorStatus delete_key_v3d_invoke(bContext *C, wmOperator *op, const wmEvent *)
static wmOperatorStatus clear_anim_vse_exec(bContext *C, wmOperator *op)
static wmOperatorStatus delete_key_vse_invoke(bContext *C, wmOperator *op, const wmEvent *)
void ANIM_OT_keyframe_delete_vse(wmOperatorType *ot)
static wmOperatorStatus delete_key_button_exec(bContext *C, wmOperator *op)
static wmOperatorStatus insert_key_invoke(bContext *C, wmOperator *op, const wmEvent *)
void ANIM_OT_keyframe_insert(wmOperatorType *ot)
static bool can_delete_scene_key(FCurve *fcu, Scene *scene, wmOperator *op)
static wmOperatorStatus clear_anim_v3d_exec(bContext *C, wmOperator *)
void ANIM_OT_keyframe_clear_button(wmOperatorType *ot)
void ANIM_OT_keyframe_insert_by_name(wmOperatorType *ot)
static wmOperatorStatus delete_key_vse_without_keying_set(bContext *C, wmOperator *op)
static wmOperatorStatus clear_anim_vse_invoke(bContext *C, wmOperator *op, const wmEvent *)
void ANIM_OT_keyframe_delete(wmOperatorType *ot)
void ED_keyframes_add(FCurve *fcu, int num_keys_to_add)
static bool fcurve_belongs_to_strip(const FCurve &fcurve, const std::string &strip_path)
static wmOperatorStatus insert_key_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
void ANIM_OT_keyframe_clear_v3d(wmOperatorType *ot)
static bool can_delete_key(FCurve *fcu, Object *ob, ReportList *reports)
void ANIM_OT_keyframe_insert_button(wmOperatorType *ot)
static wmOperatorStatus clear_key_button_exec(bContext *C, wmOperator *op)
static wmOperatorStatus delete_key_vse_exec(bContext *C, wmOperator *op)
static void invalidate_strip_caches(blender::Vector< PointerRNA > selection, Scene *scene)
static KeyingSet * keyingset_get_from_op_with_error(wmOperator *op, PropertyRNA *prop, Scene *scene)
void update_autoflags_fcurve(FCurve *fcu, bContext *C, ReportList *reports, PointerRNA *ptr)
Lesser Key-framing API call.
static wmOperatorStatus delete_key_v3d_without_keying_set(bContext *C, wmOperator *op)
static bool get_selection(bContext *C, blender::Vector< PointerRNA > *r_selection)
static blender::Vector< std::string > get_selected_strips_rna_paths(blender::Vector< PointerRNA > &selection)
static bool modify_key_op_poll(bContext *C)
static wmOperatorStatus delete_key_v3d_exec(bContext *C, wmOperator *op)
static wmOperatorStatus insert_key_with_keyingset(bContext *C, wmOperator *op, KeyingSet *ks)
void ANIM_OT_keyframe_delete_v3d(wmOperatorType *ot)
static blender::Vector< RNAPath > construct_rna_paths(PointerRNA *ptr)
static wmOperatorStatus insert_key_exec(bContext *C, wmOperator *op)
static wmOperatorStatus clear_anim_v3d_invoke(bContext *C, wmOperator *op, const wmEvent *)
static wmOperatorStatus insert_key_button_exec(bContext *C, wmOperator *op)
static bool can_delete_fcurve(FCurve *fcu, Object *ob)
void ANIM_OT_keyframe_delete_button(wmOperatorType *ot)
static wmOperatorStatus delete_key_exec(bContext *C, wmOperator *op)
void ANIM_OT_keyframe_delete_by_name(wmOperatorType *ot)
void ANIM_OT_keyframe_clear_vse(wmOperatorType *ot)
static wmOperatorStatus keyframe_insert_with_keyingset_exec(bContext *C, wmOperator *op)
void ANIM_keyingset_visit_for_search_no_poll(const bContext *C, PointerRNA *, PropertyRNA *, const char *, blender::FunctionRef< void(StringPropertySearchVisitParams)> visit_fn)
KeyingSet * ANIM_keyingset_get_from_idname(Scene *scene, const char *idname)
KeyingSet * ANIM_keyingset_get_from_enum_type(Scene *scene, int type)
const EnumPropertyItem * ANIM_keying_sets_enum_itemf(bContext *C, PointerRNA *, PropertyRNA *, bool *r_free)
#define GS(x)
#define printf(...)
bool all(VecOp< bool, D >) RET
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define G(x, y, z)
void foreach_fcurve_in_action_slot(Action &action, slot_handle_t handle, FunctionRef< void(FCurve &fcurve)> callback)
bool action_fcurve_remove(Action &action, FCurve &fcu)
std::optional< StringRefNull > default_channel_group_for_path(const PointerRNA *animated_struct, StringRef prop_rna_path)
void animdata_fcurve_delete(AnimData *adt, FCurve *fcu)
Definition animdata.cc:251
void update_autoflags_fcurve_direct(FCurve *fcu, PropertyType prop_type)
KeyingSet * scene_get_active_keyingset(const Scene *scene)
Vector< RNAPath > get_keyable_id_property_paths(const PointerRNA &ptr)
Definition anim_rna.cc:126
Vector< float > get_rna_values(PointerRNA *ptr, PropertyRNA *prop)
Definition anim_rna.cc:25
bool bone_is_selected(const bArmature *armature, const Bone *bone)
int clear_keyframe(Main *bmain, ReportList *reports, ID *id, const RNAPath &rna_path)
int delete_keyframe(Main *bmain, ReportList *reports, ID *id, const RNAPath &rna_path, float cfra)
Main Delete Key-Framing API call.
bool fcurve_delete_keyframe_at_time(FCurve *fcurve, float time)
CombinedKeyingResult insert_keyframes(Main *bmain, PointerRNA *struct_pointer, std::optional< StringRefNull > channel_group, const blender::Span< RNAPath > rna_paths, std::optional< float > scene_frame, const AnimationEvalContext &anim_eval_context, eBezTriple_KeyframeType key_type, eInsertKeyFlags insert_key_flags)
Main key-frame insertion API.
bool keyingset_find_id(KeyingSet *keyingset, ID *id)
bool delete_keyframe_fcurve_legacy(AnimData *adt, FCurve *fcu, float cfra)
bool animdata_remove_empty_action(AnimData *adt)
Definition animdata.cc:310
bool insert_keyframe_direct(ReportList *reports, PointerRNA ptr, PropertyRNA *prop, FCurve *fcu, const AnimationEvalContext *anim_eval_context, eBezTriple_KeyframeType keytype, NlaKeyframingContext *nla_context, eInsertKeyFlags flag)
Secondary Insert Key-framing API call.
int apply_keyingset(bContext *C, blender::Vector< PointerRNA > *sources, KeyingSet *keyingset, ModifyKeyMode mode, float cfra)
eInsertKeyFlags get_keyframing_flags(Scene *scene)
float evaluate_driver_from_rna_pointer(const AnimationEvalContext *anim_eval_context, PointerRNA *ptr, PropertyRNA *prop, const FCurve *fcu)
Definition driver.cc:16
bool mode_set(bContext *C, eObjectMode mode)
blender::VectorSet< Strip * > selected_strips_from_context(bContext *C)
void relations_invalidate_cache(Scene *scene, Strip *strip)
bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
PropertyType RNA_property_type(PropertyRNA *prop)
void RNA_property_enum_items_gettexted(bContext *C, PointerRNA *ptr, PropertyRNA *prop, const EnumPropertyItem **r_item, int *r_totitem, bool *r_free)
bool RNA_property_anim_editable(const PointerRNA *ptr, PropertyRNA *prop_orig)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
std::string RNA_property_string_get(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
int RNA_enum_get(PointerRNA *ptr, const char *name)
const char * RNA_property_identifier(const PropertyRNA *prop)
PointerRNA RNA_id_pointer_create(ID *id)
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
void RNA_def_property_string_search_func_runtime(PropertyRNA *prop, StringPropertySearchFunc search_fn, const eStringPropertySearchFlag search_flag)
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)
void RNA_def_enum_funcs(PropertyRNA *prop, EnumPropertyItemFunc itemfunc)
std::optional< std::string > RNA_path_from_ID_to_struct(const PointerRNA *ptr)
Definition rna_path.cc:1014
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
const EnumPropertyItem rna_enum_dummy_DEFAULT_items[]
Definition rna_rna.cc:32
bAction * action
int32_t slot_handle
const char * identifier
Definition RNA_types.hh:657
const char * name
Definition RNA_types.hh:661
struct FCurve * next
char * rna_path
BezTriple * bezt
int array_index
unsigned int totvert
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
char name[64]
ListBase fcurves
char name[64]
struct bPose * pose
PointerRNA ptr
Definition RNA_types.hh:153
PropertyRNA * prop
Definition RNA_types.hh:154
std::optional< int > index
Definition RNA_path.hh:66
int active_keyingset
struct ToolSettings * toolsettings
struct AnimData * adt
struct Bone * bone
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
wmOperatorStatus(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1049
PropertyRNA * prop
Definition WM_types.hh:1139
struct ReportList * reports
struct wmOperatorType * type
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_operator_properties_confirm_or_exec(wmOperatorType *ot)
std::string WM_operatortype_name(wmOperatorType *ot, PointerRNA *properties)
wmOperatorStatus WM_operator_confirm_ex(bContext *C, wmOperator *op, const char *title, const char *message, const char *confirm_text, int icon, bool cancel_default)
uint8_t flag
Definition wm_window.cc:145