Blender V4.5
interface_anim.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstdio>
10#include <cstdlib>
11#include <cstring>
12
13#include "DNA_anim_types.h"
14#include "DNA_screen_types.h"
15
16#include "BLI_listbase.h"
17#include "BLI_string.h"
18#include "BLI_string_utf8.h"
19#include "BLI_utildefines.h"
20
21#include "BKE_animsys.h"
22#include "BKE_context.hh"
23#include "BKE_fcurve.hh"
24#include "BKE_fcurve_driver.h"
25#include "BKE_global.hh"
26#include "BKE_nla.hh"
27
29
30#include "ED_keyframing.hh"
31
32#include "ANIM_fcurve.hh"
33#include "ANIM_keyframing.hh"
34
35#include "UI_interface.hh"
36
37#include "RNA_access.hh"
38#include "RNA_path.hh"
39
40#include "WM_api.hh"
41#include "WM_types.hh"
42
43#include "interface_intern.hh"
44
46 uiBut *but, AnimData **adt, bAction **action, bool *r_driven, bool *r_special)
47{
48 /* for entire array buttons we check the first component, it's not perfect
49 * but works well enough in typical cases */
50 const int rnaindex = (but->rnaindex == -1) ? 0 : but->rnaindex;
51
52 return BKE_fcurve_find_by_rna_context_ui(static_cast<bContext *>(but->block->evil_C),
53 &but->rnapoin,
54 but->rnaprop,
55 rnaindex,
56 adt,
57 action,
58 r_driven,
59 r_special);
60}
61
62void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context)
63{
64 /* Clear the flags that this function might set. */
67
68 /* NOTE: "special" is reserved for special F-Curves stored on the animation data
69 * itself (which are used to animate properties of the animation data).
70 * We count those as "animated" too for now
71 */
72 AnimData *adt;
73 bAction *act;
74 bool driven;
75 bool special;
76 FCurve *fcu = ui_but_get_fcurve(but, &adt, &act, &driven, &special);
77
78 if (!fcu) {
79 return;
80 }
81 if (driven) {
82 but->flag |= UI_BUT_DRIVEN;
83 return;
84 }
85
86 /* Empty curves are ignored by the animation evaluation system. */
87 if (BKE_fcurve_is_empty(fcu)) {
88 return;
89 }
90
91 but->flag |= UI_BUT_ANIMATED;
92
93 /* #41525 - When the active action is a NLA strip being edited,
94 * we need to correct the frame number to "look inside" the
95 * remapped action
96 */
97 float cfra = anim_eval_context->eval_time;
98 if (adt) {
100 }
101
104 }
105
106 /* This feature is not implemented at all for the NLA. However, if the NLA just consists of
107 * stashed (i.e. deactivated) Actions, it doesn't do anything, and we can treat it as
108 * non-existent here. Note that this is mostly to play nice with stashed Actions, and doesn't
109 * fully look at all the track & strip flags. */
110 if (adt) {
111 LISTBASE_FOREACH (NlaTrack *, nla_track, &adt->nla_tracks) {
112 if (!(nla_track->flag & NLATRACK_MUTED)) {
113 /* Found a non-muted track, so this NLA is not purely for stashing Actions. */
114 return;
115 }
116 }
117 }
118
120 anim_eval_context, cfra);
121 if (fcurve_is_changed(but->rnapoin, but->rnaprop, fcu, &remapped_context)) {
123 }
124}
125
127{
128 uiBut *but_iter = nullptr;
129
132 if (but->block->buttons.is_empty()) {
133 return nullptr;
134 }
135 int i = but->block->but_index(but);
136 i = i > 0 ? i - 1 : but->block->buttons.size() - 1;
137 const int start = i;
138 do {
139 but_iter = but->block->buttons[i].get();
140 if (but_iter != but &&
142 but_iter, &but->decorated_rnapoin, but->decorated_rnaprop, but->decorated_rnaindex))
143 {
144 return but_iter;
145 }
146 i = i > 0 ? i - 1 : but->block->buttons.size() - 1;
147 } while (i != start);
148
149 return nullptr;
150}
151
153{
154 if (!but->decorated_rnapoin.data || !but->decorated_rnaprop) {
155 /* Nothing to do. */
156 return;
157 }
158
160
161 if (!but_anim) {
162 printf("Could not find button with matching property to decorate (%s.%s)\n",
165 return;
166 }
167
168 const int flag = but_anim->flag;
169
170 if (flag & UI_BUT_DRIVEN) {
171 but->icon = ICON_DECORATE_DRIVER;
172 }
173 else if (flag & UI_BUT_ANIMATED_KEY) {
174 but->icon = ICON_DECORATE_KEYFRAME;
175 }
176 else if (flag & UI_BUT_ANIMATED) {
177 but->icon = ICON_DECORATE_ANIMATE;
178 }
179 else if (flag & UI_BUT_OVERRIDDEN) {
180 but->icon = ICON_DECORATE_OVERRIDE;
181 }
182 else {
183 but->icon = ICON_DECORATE;
184 }
185
186 const int flag_copy = (UI_BUT_DISABLED | UI_BUT_INACTIVE);
187 but->flag = (but->flag & ~flag_copy) | (flag & flag_copy);
188}
189
190bool ui_but_anim_expression_get(uiBut *but, char *str, size_t str_maxncpy)
191{
192 FCurve *fcu;
193 ChannelDriver *driver;
194 bool driven, special;
195
196 fcu = ui_but_get_fcurve(but, nullptr, nullptr, &driven, &special);
197
198 if (fcu && driven) {
199 driver = fcu->driver;
200
201 if (driver && driver->type == DRIVER_TYPE_PYTHON) {
202 if (str) {
203 BLI_strncpy(str, driver->expression, str_maxncpy);
204 }
205 return true;
206 }
207 }
208
209 return false;
210}
211
212bool ui_but_anim_expression_set(uiBut *but, const char *str)
213{
214 FCurve *fcu;
215 ChannelDriver *driver;
216 bool driven, special;
217
218 fcu = ui_but_get_fcurve(but, nullptr, nullptr, &driven, &special);
219
220 if (fcu && driven) {
221 driver = fcu->driver;
222
223 if (driver && (driver->type == DRIVER_TYPE_PYTHON)) {
224 bContext *C = static_cast<bContext *>(but->block->evil_C);
225
226 STRNCPY_UTF8(driver->expression, str);
227
228 /* tag driver as needing to be recompiled */
229 BKE_driver_invalidate_expression(driver, true, false);
230
231 /* clear invalid flags which may prevent this from working */
232 driver->flag &= ~DRIVER_FLAG_INVALID;
233 fcu->flag &= ~FCURVE_DISABLED;
234
235 /* this notifier should update the Graph Editor and trigger depsgraph refresh? */
237
239
240 return true;
241 }
242 }
243
244 return false;
245}
246
248{
249 bContext *C = static_cast<bContext *>(but->block->evil_C);
250 ID *id;
251 FCurve *fcu;
252 bool ok = false;
253
254 /* button must have RNA-pointer to a numeric-capable property */
255 if (ELEM(nullptr, but->rnapoin.data, but->rnaprop)) {
256 if (G.debug & G_DEBUG) {
257 printf("ERROR: create expression failed - button has no RNA info attached\n");
258 }
259 return false;
260 }
261
262 if (RNA_property_array_check(but->rnaprop) != 0) {
263 if (but->rnaindex == -1) {
264 if (G.debug & G_DEBUG) {
265 printf("ERROR: create expression failed - can't create expression for entire array\n");
266 }
267 return false;
268 }
269 }
270
271 /* make sure we have animdata for this */
272 /* FIXME: until materials can be handled by depsgraph,
273 * don't allow drivers to be created for them */
274 id = but->rnapoin.owner_id;
275 if ((id == nullptr) || (GS(id->name) == ID_MA) || (GS(id->name) == ID_TE)) {
276 if (G.debug & G_DEBUG) {
277 printf("ERROR: create expression failed - invalid data-block for adding drivers (%p)\n", id);
278 }
279 return false;
280 }
281
282 /* get path */
283 const std::optional<std::string> path = RNA_path_from_ID_to_property(&but->rnapoin,
284 but->rnaprop);
285 if (!path) {
286 return false;
287 }
288
289 /* create driver */
290 fcu = verify_driver_fcurve(id, path->c_str(), but->rnaindex, DRIVER_FCURVE_KEYFRAMES);
291 if (fcu) {
292 ChannelDriver *driver = fcu->driver;
293
294 if (driver) {
295 /* set type of driver */
296 driver->type = DRIVER_TYPE_PYTHON;
297
298 /* set the expression */
299 /* TODO: need some way of identifying variables used */
300 STRNCPY_UTF8(driver->expression, str);
301
302 /* updates */
303 BKE_driver_invalidate_expression(driver, true, false);
306 ok = true;
307 }
308 }
309
310 return ok;
311}
312
313void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra)
314{
316 C, scene, &but->rnapoin, but->rnaprop, but->rnaindex, cfra, true);
317}
318
320{
321 /* this operator calls UI_context_active_but_prop_get */
322 WM_operator_name_call(C, "ANIM_OT_copy_driver_button", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
323}
324
326{
327 /* this operator calls UI_context_active_but_prop_get */
328 WM_operator_name_call(C, "ANIM_OT_paste_driver_button", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
329}
330
331void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void * /*arg_dummy*/)
332{
334 uiButDecorator *but_decorate = static_cast<uiButDecorator *>(arg_but);
335 uiBut *but_anim = ui_but_anim_decorate_find_attached_button(but_decorate);
336
337 if (!but_anim) {
338 return;
339 }
340 /* While click drag the active button may not be `but_decorate`, instead is the but where the
341 * drag started, temporarily override `but_anim` as active. */
342 but_anim->flag |= UI_BUT_ACTIVE_OVERRIDE;
343 wm->op_undo_depth++;
344
345 if (but_anim->flag & UI_BUT_DRIVEN) {
346 /* pass */
347 /* TODO: report? */
348 }
349 else if (but_anim->flag & UI_BUT_ANIMATED_KEY) {
350 PointerRNA props_ptr;
351 wmOperatorType *ot = WM_operatortype_find("ANIM_OT_keyframe_delete_button", false);
353 RNA_boolean_set(&props_ptr, "all", but_anim->rnaindex == -1);
354 WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, nullptr);
355 WM_operator_properties_free(&props_ptr);
356 }
357 else {
358 PointerRNA props_ptr;
359 wmOperatorType *ot = WM_operatortype_find("ANIM_OT_keyframe_insert_button", false);
361 RNA_boolean_set(&props_ptr, "all", but_anim->rnaindex == -1);
362 WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, nullptr);
363 WM_operator_properties_free(&props_ptr);
364 }
365
366 but_anim->flag &= ~UI_BUT_ACTIVE_OVERRIDE;
367 wm->op_undo_depth--;
368}
Functions to modify FCurves.
Functions to insert, delete or modify keyframes.
AnimationEvalContext BKE_animsys_eval_context_construct_at(const AnimationEvalContext *anim_eval_context, float eval_time) ATTR_WARN_UNUSED_RESULT
Definition anim_sys.cc:743
Main * CTX_data_main(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
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)
bool BKE_fcurve_is_empty(const FCurve *fcu)
void BKE_driver_invalidate_expression(struct ChannelDriver *driver, bool expr_changed, bool varname_changed)
@ G_DEBUG
@ NLATIME_CONVERT_UNMAP
Definition BKE_nla.hh:540
float BKE_nla_tweakedit_remap(AnimData *adt, float cframe, eNlaTime_ConvertModes mode)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
#define ELEM(...)
void DEG_relations_tag_update(Main *bmain)
@ ID_TE
@ ID_MA
@ DRIVER_TYPE_PYTHON
@ DRIVER_FLAG_INVALID
@ FCURVE_DISABLED
@ NLATRACK_MUTED
@ DRIVER_FCURVE_KEYFRAMES
#define C
Definition RandGen.cpp:29
@ UI_BUT_ANIMATED_CHANGED
@ UI_BUT_ANIMATED
@ UI_BUT_DISABLED
@ UI_BUT_INACTIVE
@ UI_BUT_OVERRIDDEN
@ UI_BUT_DRIVEN
@ UI_BUT_ANIMATED_KEY
#define UI_but_is_decorator(but)
#define NC_ANIMATION
Definition WM_types.hh:385
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:238
#define ND_KEYFRAME
Definition WM_types.hh:491
int64_t size() const
bool is_empty() const
FCurve * verify_driver_fcurve(ID *id, const char rna_path[], const int array_index, eDriverFCurveCreationMode creation_mode)
Definition drivers.cc:50
bool fcurve_is_changed(PointerRNA ptr, PropertyRNA *prop, FCurve *fcu, const AnimationEvalContext *anim_eval_context)
Lesser Keyframe Checking API call.
#define str(s)
#define printf(...)
#define GS(a)
bool ui_but_rna_equals_ex(const uiBut *but, const PointerRNA *ptr, const PropertyRNA *prop, int index)
Definition interface.cc:759
static FCurve * ui_but_get_fcurve(uiBut *but, AnimData **adt, bAction **action, bool *r_driven, bool *r_special)
void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *)
bool ui_but_anim_expression_create(uiBut *but, const char *str)
void ui_but_anim_decorate_update_from_flag(uiButDecorator *but)
void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context)
void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra)
bool ui_but_anim_expression_get(uiBut *but, char *str, size_t str_maxncpy)
static uiBut * ui_but_anim_decorate_find_attached_button(uiButDecorator *but)
void ui_but_anim_copy_driver(bContext *C)
void ui_but_anim_paste_driver(bContext *C)
bool ui_but_anim_expression_set(uiBut *but, const char *str)
@ UI_BUT_ACTIVE_OVERRIDE
#define G(x, y, z)
bool fcurve_frame_has_keyframe(const FCurve *fcu, float frame)
bool autokeyframe_property(bContext *C, Scene *scene, PointerRNA *ptr, PropertyRNA *prop, int rnaindex, float cfra, bool only_if_property_keyed)
bool RNA_property_array_check(PropertyRNA *prop)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
const char * RNA_struct_identifier(const StructRNA *type)
const char * RNA_property_identifier(const PropertyRNA *prop)
std::optional< std::string > RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop)
Definition rna_path.cc:1173
ListBase nla_tracks
char expression[256]
ChannelDriver * driver
Definition DNA_ID.h:404
char name[66]
Definition DNA_ID.h:415
ID * owner_id
Definition RNA_types.hh:51
StructRNA * type
Definition RNA_types.hh:52
void * data
Definition RNA_types.hh:53
blender::Vector< std::unique_ptr< uiBut > > buttons
int but_index(const uiBut *but) const
Definition interface.cc:262
PointerRNA decorated_rnapoin
PropertyRNA * decorated_rnaprop
PropertyRNA * rnaprop
uiBlock * block
BIFIconID icon
PointerRNA rnapoin
i
Definition text_draw.cc:230
wmOperatorStatus WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
wmOperatorStatus WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4225
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
void WM_operator_properties_free(PointerRNA *ptr)
uint8_t flag
Definition wm_window.cc:139