Blender V4.5
fmodifier_ui.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
15
16#include <cstring>
17
18#include "DNA_anim_types.h"
19#include "DNA_scene_types.h"
20#include "DNA_userdef_types.h"
21
22#include "MEM_guardedalloc.h"
23
24#include "BLT_translation.hh"
25
26#include "BLI_listbase.h"
27#include "BLI_string.h"
28#include "BLI_utildefines.h"
29
30#include "BKE_context.hh"
31#include "BKE_fcurve.hh"
32#include "BKE_screen.hh"
33
34#include "WM_api.hh"
35#include "WM_types.hh"
36
37#include "RNA_access.hh"
38#include "RNA_prototypes.hh"
39
40#include "UI_interface.hh"
41#include "UI_resources.hh"
42
43#include "ED_anim_api.hh"
44#include "ED_undo.hh"
45
46#include "DEG_depsgraph.hh"
47
48using PanelDrawFn = void (*)(const bContext *, Panel *);
49static void fmodifier_panel_header(const bContext *C, Panel *panel);
50
51/* -------------------------------------------------------------------- */
54
59{
60 ScrArea *area = CTX_wm_area(C);
61
62 if (area->spacetype == SPACE_GRAPH) {
64 return &fcu->modifiers;
65 }
66
67 if (area->spacetype == SPACE_NLA) {
69 return &strip->modifiers;
70 }
71
72 /* This should not be called in any other space. */
73 BLI_assert(false);
74 return nullptr;
75}
76
81static PointerRNA *fmodifier_get_pointers(const bContext *C, const Panel *panel, ID **r_owner_id)
82{
84
85 if (r_owner_id != nullptr) {
86 *r_owner_id = ptr->owner_id;
87 }
88
89 if (C != nullptr && CTX_wm_space_graph(C)) {
92 }
93
94 return ptr;
95}
96
100static void fmodifier_reorder(bContext *C, Panel *panel, int new_index)
101{
102 ID *owner_id;
103 PointerRNA *ptr = fmodifier_get_pointers(nullptr, panel, &owner_id);
104 FModifier *fcm = static_cast<FModifier *>(ptr->data);
106
107 /* Cycles modifier has to be the first, so make sure it's kept that way. */
109 WM_global_report(RPT_ERROR, "Modifier requires original data");
110 return;
111 }
112
114
115 /* Again, make sure we don't move a modifier before a cycles modifier. */
116 FModifier *fcm_first = static_cast<FModifier *>(modifiers->first);
117 const FModifierTypeInfo *fmi_first = get_fmodifier_typeinfo(fcm_first->type);
118 if (fmi_first->requires_flag & FMI_REQUIRES_ORIGINAL_DATA && new_index == 0) {
119 WM_global_report(RPT_ERROR, "Modifier requires original data");
120 return;
121 }
122
123 int current_index = BLI_findindex(modifiers, fcm);
124 BLI_assert(current_index >= 0);
125 BLI_assert(new_index >= 0);
126
127 /* Don't do anything if the drag didn't change the index. */
128 if (current_index == new_index) {
129 return;
130 }
131
132 /* Move the FModifier in the list. */
133 BLI_listbase_link_move(modifiers, fcm, new_index - current_index);
134
135 ED_undo_push(C, "Reorder F-Curve Modifier");
136
139}
140
141static short get_fmodifier_expand_flag(const bContext * /*C*/, Panel *panel)
142{
143 PointerRNA *ptr = fmodifier_get_pointers(nullptr, panel, nullptr);
144 FModifier *fcm = static_cast<FModifier *>(ptr->data);
145
146 return fcm->ui_expand_flag;
147}
148
149static void set_fmodifier_expand_flag(const bContext * /*C*/, Panel *panel, short expand_flag)
150{
151 PointerRNA *ptr = fmodifier_get_pointers(nullptr, panel, nullptr);
152 FModifier *fcm = static_cast<FModifier *>(ptr->data);
153
154 fcm->ui_expand_flag = expand_flag;
155}
156
158 eFModifier_Types type,
159 PanelDrawFn draw,
160 PanelTypePollFn poll,
161 const char *id_prefix)
162{
163 PanelType *panel_type = MEM_callocN<PanelType>(__func__);
164
165 /* Intentionally leave the label field blank. The header is filled with buttons. */
166 const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(type);
167 SNPRINTF(panel_type->idname, "%s_PT_%s", id_prefix, fmi->name);
168 STRNCPY(panel_type->category, "Modifiers");
170
172 panel_type->draw = draw;
173 panel_type->poll = poll;
174
175 /* Give the panel the special flag that says it was built here and corresponds to a
176 * modifier rather than a #PanelType. */
178 panel_type->reorder = fmodifier_reorder;
181
182 BLI_addtail(&region_type->paneltypes, panel_type);
183
184 return panel_type;
185}
186
194 const char *name,
195 const char *label,
196 PanelDrawFn draw_header,
197 PanelDrawFn draw,
198 PanelTypePollFn poll,
199 PanelType *parent)
200{
201 PanelType *panel_type = MEM_callocN<PanelType>(__func__);
202
203 BLI_assert(parent != nullptr);
204 SNPRINTF(panel_type->idname, "%s_%s", parent->idname, name);
205 STRNCPY(panel_type->label, label);
206 STRNCPY(panel_type->category, "Modifiers");
208
209 panel_type->draw_header = draw_header;
210 panel_type->draw = draw;
211 panel_type->poll = poll;
212 panel_type->flag = PANEL_TYPE_DEFAULT_CLOSED;
213
214 STRNCPY(panel_type->parent_id, parent->idname);
215 panel_type->parent = parent;
216 BLI_addtail(&parent->children, BLI_genericNodeN(panel_type));
217 BLI_addtail(&region_type->paneltypes, panel_type);
218
219 return panel_type;
220}
221
223
224/* -------------------------------------------------------------------- */
227
228#define B_REDR 1
229#define B_FMODIFIER_REDRAW 20
230
231/* Callback to remove the given modifier. */
236
237static void delete_fmodifier_cb(bContext *C, void *ctx_v, void *fcm_v)
238{
239 FModifierDeleteContext *ctx = static_cast<FModifierDeleteContext *>(ctx_v);
240 ListBase *modifiers = ctx->modifiers;
241 FModifier *fcm = static_cast<FModifier *>(fcm_v);
242
243 /* remove the given F-Modifier from the active modifier-stack */
244 remove_fmodifier(modifiers, fcm);
245
246 ED_undo_push(C, "Delete F-Curve Modifier");
247
250}
251
253{
254 FModifier *fcm = static_cast<FModifier *>(ptr->data);
255 layout->separator();
256
257 uiLayout *row = &layout->row(true, IFACE_("Influence"));
258 row->prop(ptr, "use_influence", UI_ITEM_NONE, "", ICON_NONE);
259 uiLayout *sub = &row->row(true);
260
262 sub->prop(ptr, "influence", UI_ITEM_NONE, "", ICON_NONE);
263}
264
266{
267 uiLayout *layout = panel->layout;
268
269 PointerRNA *ptr = fmodifier_get_pointers(C, panel, nullptr);
270
271 layout->prop(ptr, "use_restricted_range", UI_ITEM_NONE, std::nullopt, ICON_NONE);
272}
273
274static void fmodifier_frame_range_draw(const bContext *C, Panel *panel)
275{
276 uiLayout *col;
277 uiLayout *layout = panel->layout;
278
279 PointerRNA *ptr = fmodifier_get_pointers(C, panel, nullptr);
280
281 uiLayoutSetPropSep(layout, true);
282 uiLayoutSetPropDecorate(layout, false);
283
284 FModifier *fcm = static_cast<FModifier *>(ptr->data);
286
287 col = &layout->column(true);
288 col->prop(ptr, "frame_start", UI_ITEM_NONE, IFACE_("Start"), ICON_NONE);
289 col->prop(ptr, "frame_end", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
290
291 col = &layout->column(true);
292 col->prop(ptr, "blend_in", UI_ITEM_NONE, IFACE_("Blend In"), ICON_NONE);
293 col->prop(ptr, "blend_out", UI_ITEM_NONE, IFACE_("Out"), ICON_NONE);
294}
295
296static void fmodifier_panel_header(const bContext *C, Panel *panel)
297{
298 uiLayout *layout = panel->layout;
299
300 ID *owner_id;
301 PointerRNA *ptr = fmodifier_get_pointers(C, panel, &owner_id);
302 FModifier *fcm = static_cast<FModifier *>(ptr->data);
304
305 uiBlock *block = uiLayoutGetBlock(layout);
306
307 uiLayout *sub = &layout->row(true);
308
309 /* Checkbox for 'active' status (for now). */
310 sub->prop(ptr, "active", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
311
312 /* Name. */
313 if (fmi) {
314 sub->prop(ptr, "name", UI_ITEM_NONE, "", ICON_NONE);
315 }
316 else {
317 sub->label(IFACE_("<Unknown Modifier>"), ICON_NONE);
318 }
319 /* Right align. */
320 sub = &layout->row(true);
323
324 /* 'Mute' button. */
325 sub->prop(ptr, "mute", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
326
327 /* Delete button. */
328 uiBut *but = uiDefIconBut(block,
330 B_REDR,
331 ICON_X,
332 0,
333 0,
334 UI_UNIT_X,
335 UI_UNIT_Y,
336 nullptr,
337 0.0,
338 0.0,
339 TIP_("Delete Modifier"));
341 ctx->owner_id = owner_id;
343 BLI_assert(ctx->modifiers != nullptr);
344
346
347 layout->separator();
348}
349
351
352/* -------------------------------------------------------------------- */
355
356static void generator_panel_draw(const bContext *C, Panel *panel)
357{
358 uiLayout *layout = panel->layout;
359
360 ID *owner_id;
361 PointerRNA *ptr = fmodifier_get_pointers(C, panel, &owner_id);
362 FModifier *fcm = static_cast<FModifier *>(ptr->data);
363 FMod_Generator *data = static_cast<FMod_Generator *>(fcm->data);
364
365 layout->prop(ptr, "mode", UI_ITEM_NONE, "", ICON_NONE);
366
367 uiLayoutSetPropSep(layout, true);
368 uiLayoutSetPropDecorate(layout, false);
369
370 layout->prop(ptr, "use_additive", UI_ITEM_NONE, std::nullopt, ICON_NONE);
371
372 layout->prop(ptr, "poly_order", UI_ITEM_NONE, IFACE_("Order"), ICON_NONE);
373
374 PropertyRNA *prop = RNA_struct_find_property(ptr, "coefficients");
375 uiLayout *col = &layout->column(true);
376 switch (data->mode) {
377 case FCM_GENERATOR_POLYNOMIAL: /* Polynomial expression. */
378 {
379
380 char xval[32];
381
382 /* The first value gets a "Coefficient" label. */
383 STRNCPY(xval, N_("Coefficient"));
384
385 for (int i = 0; i < data->arraysize; i++) {
386 col->prop(ptr, prop, i, 0, UI_ITEM_NONE, IFACE_(xval), ICON_NONE);
387 SNPRINTF(xval, "x^%d", i + 1);
388 }
389 break;
390 }
391 case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* Factorized polynomial expression */
392 {
393 {
394 /* Add column labels above the buttons to prevent confusion.
395 * Fake the property split layout, otherwise the labels use the full row. */
396 uiLayout *split = &col->split(0.4f, false);
397 split->column(false);
398 uiLayout *title_col = &split->column(false);
399 uiLayout *title_row = &title_col->row(true);
400 title_row->label(CTX_IFACE_(BLT_I18NCONTEXT_ID_ACTION, "A"), ICON_NONE);
401 title_row->label(CTX_IFACE_(BLT_I18NCONTEXT_ID_ACTION, "B"), ICON_NONE);
402 }
403
404 uiLayout *first_row = &col->row(true);
405 first_row->prop(ptr, prop, 0, 0, UI_ITEM_NONE, IFACE_("y = (Ax + B)"), ICON_NONE);
406 first_row->prop(ptr, prop, 1, 0, UI_ITEM_NONE, "", ICON_NONE);
407 for (int i = 2; i < data->arraysize - 1; i += 2) {
408 /* \u00d7 is the multiplication symbol. */
409 uiLayout *row = &col->row(true);
410 row->prop(ptr, prop, i, 0, UI_ITEM_NONE, IFACE_("\u00d7 (Ax + B)"), ICON_NONE);
411 row->prop(ptr, prop, i + 1, 0, UI_ITEM_NONE, "", ICON_NONE);
412 }
413 break;
414 }
415 }
416
418}
419
420static void panel_register_generator(ARegionType *region_type,
421 const char *id_prefix,
422 PanelTypePollFn poll_fn)
423{
425 region_type, FMODIFIER_TYPE_GENERATOR, generator_panel_draw, poll_fn, id_prefix);
426 fmodifier_subpanel_register(region_type,
427 "frame_range",
428 "",
431 poll_fn,
432 panel_type);
433}
434
436
437/* -------------------------------------------------------------------- */
440
441static void fn_generator_panel_draw(const bContext *C, Panel *panel)
442{
443 uiLayout *col;
444 uiLayout *layout = panel->layout;
445
446 PointerRNA *ptr = fmodifier_get_pointers(C, panel, nullptr);
447
448 layout->prop(ptr, "function_type", UI_ITEM_NONE, "", ICON_NONE);
449
450 uiLayoutSetPropSep(layout, true);
451 uiLayoutSetPropDecorate(layout, false);
452
453 col = &layout->column(false);
454 col->prop(ptr, "use_additive", UI_ITEM_NONE, std::nullopt, ICON_NONE);
455
456 col = &layout->column(false);
457 col->prop(ptr, "amplitude", UI_ITEM_NONE, std::nullopt, ICON_NONE);
458 col->prop(ptr, "phase_multiplier", UI_ITEM_NONE, std::nullopt, ICON_NONE);
459 col->prop(ptr, "phase_offset", UI_ITEM_NONE, std::nullopt, ICON_NONE);
460 col->prop(ptr, "value_offset", UI_ITEM_NONE, std::nullopt, ICON_NONE);
461
463}
464
466 const char *id_prefix,
467 PanelTypePollFn poll_fn)
468{
470 region_type, FMODIFIER_TYPE_FN_GENERATOR, fn_generator_panel_draw, poll_fn, id_prefix);
471 fmodifier_subpanel_register(region_type,
472 "frame_range",
473 "",
476 poll_fn,
477 panel_type);
478}
479
481
482/* -------------------------------------------------------------------- */
485
486static void cycles_panel_draw(const bContext *C, Panel *panel)
487{
488 uiLayout *col;
489 uiLayout *layout = panel->layout;
490
491 PointerRNA *ptr = fmodifier_get_pointers(C, panel, nullptr);
492
493 uiLayoutSetPropSep(layout, true);
494 uiLayoutSetPropDecorate(layout, false);
495
496 /* Before. */
497 col = &layout->column(false);
498 col->prop(ptr, "mode_before", UI_ITEM_NONE, std::nullopt, ICON_NONE);
499 col->prop(ptr, "cycles_before", UI_ITEM_NONE, IFACE_("Count"), ICON_NONE);
500
501 /* After. */
502 col = &layout->column(false);
503 col->prop(ptr, "mode_after", UI_ITEM_NONE, std::nullopt, ICON_NONE);
504 col->prop(ptr, "cycles_after", UI_ITEM_NONE, IFACE_("Count"), ICON_NONE);
505
507}
508
509static void panel_register_cycles(ARegionType *region_type,
510 const char *id_prefix,
511 PanelTypePollFn poll_fn)
512{
514 region_type, FMODIFIER_TYPE_CYCLES, cycles_panel_draw, poll_fn, id_prefix);
515 fmodifier_subpanel_register(region_type,
516 "frame_range",
517 "",
520 poll_fn,
521 panel_type);
522}
523
525
526/* -------------------------------------------------------------------- */
529
530static void noise_panel_draw(const bContext *C, Panel *panel)
531{
532 uiLayout *col;
533 uiLayout *layout = panel->layout;
534
535 PointerRNA *ptr = fmodifier_get_pointers(C, panel, nullptr);
536
537 uiLayoutSetPropSep(layout, true);
538 uiLayoutSetPropDecorate(layout, false);
539
540 layout->prop(ptr, "blend_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
541
542 col = &layout->column(false);
543 col->prop(ptr, "scale", UI_ITEM_NONE, std::nullopt, ICON_NONE);
544 col->prop(ptr, "strength", UI_ITEM_NONE, std::nullopt, ICON_NONE);
545 col->prop(ptr, "offset", UI_ITEM_NONE, std::nullopt, ICON_NONE);
546 col->prop(ptr, "phase", UI_ITEM_NONE, std::nullopt, ICON_NONE);
547 col->prop(ptr, "depth", UI_ITEM_NONE, std::nullopt, ICON_NONE);
548 col->prop(ptr, "use_legacy_noise", UI_ITEM_NONE, std::nullopt, ICON_NONE);
549 PropertyRNA *prop = RNA_struct_find_property(ptr, "use_legacy_noise");
550 const bool use_legacy_noise = RNA_property_boolean_get(ptr, prop);
551 if (!use_legacy_noise) {
552 col->prop(ptr, "lacunarity", UI_ITEM_NONE, std::nullopt, ICON_NONE);
553 col->prop(ptr, "roughness", UI_ITEM_NONE, std::nullopt, ICON_NONE);
554 }
555
557}
558
559static void panel_register_noise(ARegionType *region_type,
560 const char *id_prefix,
561 PanelTypePollFn poll_fn)
562{
564 region_type, FMODIFIER_TYPE_NOISE, noise_panel_draw, poll_fn, id_prefix);
565 fmodifier_subpanel_register(region_type,
566 "frame_range",
567 "",
570 poll_fn,
571 panel_type);
572}
573
575
576/* -------------------------------------------------------------------- */
579
580static void fmod_envelope_addpoint_cb(bContext *C, void *fcm_dv, void * /*arg*/)
581{
582 Scene *scene = CTX_data_scene(C);
583 FMod_Envelope *env = static_cast<FMod_Envelope *>(fcm_dv);
584 FCM_EnvelopeData *fedn;
586
587 /* init template data */
588 fed.min = -1.0f;
589 fed.max = 1.0f;
590 fed.time = float(scene->r.cfra); /* XXX make this int for ease of use? */
591 fed.f1 = fed.f2 = 0;
592
593 /* check that no data exists for the current frame... */
594 if (env->data) {
595 bool exists;
596 int i = BKE_fcm_envelope_find_index(env->data, float(scene->r.cfra), env->totvert, &exists);
597
598 /* binarysearch_...() will set exists by default to 0,
599 * so if it is non-zero, that means that the point exists already */
600 if (exists) {
601 return;
602 }
603
604 /* add new */
605 fedn = MEM_calloc_arrayN<FCM_EnvelopeData>((env->totvert + 1), "FCM_EnvelopeData");
606
607 /* add the points that should occur before the point to be pasted */
608 if (i > 0) {
609 memcpy(fedn, env->data, i * sizeof(FCM_EnvelopeData));
610 }
611
612 /* add point to paste at index i */
613 *(fedn + i) = fed;
614
615 /* add the points that occur after the point to be pasted */
616 if (i < env->totvert) {
617 memcpy(fedn + i + 1, env->data + i, (env->totvert - i) * sizeof(FCM_EnvelopeData));
618 }
619
620 /* replace (+ free) old with new */
621 MEM_freeN(env->data);
622 env->data = fedn;
623
624 env->totvert++;
625 }
626 else {
627 env->data = MEM_callocN<FCM_EnvelopeData>("FCM_EnvelopeData");
628 *(env->data) = fed;
629
630 env->totvert = 1;
631 }
632}
633
634/* callback to remove envelope data point */
635/* TODO: should we have a separate file for things like this? */
636static void fmod_envelope_deletepoint_cb(bContext * /*C*/, void *fcm_dv, void *ind_v)
637{
638 FMod_Envelope *env = static_cast<FMod_Envelope *>(fcm_dv);
639 FCM_EnvelopeData *fedn;
640 int index = POINTER_AS_INT(ind_v);
641
642 /* check that no data exists for the current frame... */
643 if (env->totvert > 1) {
644 /* allocate a new smaller array */
645 fedn = MEM_calloc_arrayN<FCM_EnvelopeData>((env->totvert - 1), "FCM_EnvelopeData");
646
647 memcpy(fedn, env->data, sizeof(FCM_EnvelopeData) * (index));
648 memcpy(fedn + index,
649 env->data + (index + 1),
650 sizeof(FCM_EnvelopeData) * ((env->totvert - index) - 1));
651
652 /* free old array, and set the new */
653 MEM_freeN(env->data);
654 env->data = fedn;
655 env->totvert--;
656 }
657 else {
658 /* just free array, since the only vert was deleted */
659 MEM_SAFE_FREE(env->data);
660 env->totvert = 0;
661 }
662}
663
664/* draw settings for envelope modifier */
665static void envelope_panel_draw(const bContext *C, Panel *panel)
666{
667 uiLayout *row, *col;
668 uiLayout *layout = panel->layout;
669
670 ID *owner_id;
671 PointerRNA *ptr = fmodifier_get_pointers(C, panel, &owner_id);
672 FModifier *fcm = static_cast<FModifier *>(ptr->data);
673 FMod_Envelope *env = static_cast<FMod_Envelope *>(fcm->data);
674
675 uiLayoutSetPropSep(layout, true);
676 uiLayoutSetPropDecorate(layout, false);
677
678 /* General settings. */
679 col = &layout->column(true);
680 col->prop(ptr, "reference_value", UI_ITEM_NONE, IFACE_("Reference"), ICON_NONE);
681 col->prop(ptr, "default_min", UI_ITEM_NONE, IFACE_("Min"), ICON_NONE);
682 col->prop(ptr, "default_max", UI_ITEM_NONE, IFACE_("Max"), ICON_NONE);
683
684 /* Control points list. */
685
686 row = &layout->row(false);
687 uiBlock *block = uiLayoutGetBlock(row);
688
689 uiBut *but = uiDefBut(block,
692 IFACE_("Add Control Point"),
693 0,
694 0,
695 7.5 * UI_UNIT_X,
696 UI_UNIT_Y,
697 nullptr,
698 0,
699 0,
700 TIP_("Add a new control-point to the envelope on the current frame"));
701 UI_but_func_set(but, fmod_envelope_addpoint_cb, env, nullptr);
702
703 col = &layout->column(false);
704 uiLayoutSetPropSep(col, false);
705
706 FCM_EnvelopeData *fed = env->data;
707 for (int i = 0; i < env->totvert; i++, fed++) {
709 owner_id, &RNA_FModifierEnvelopeControlPoint, fed);
710
711 /* get a new row to operate on */
712 row = &col->row(true);
713 block = uiLayoutGetBlock(row);
714
715 row->prop(&ctrl_ptr, "frame", UI_ITEM_NONE, std::nullopt, ICON_NONE);
716 row->prop(&ctrl_ptr, "min", UI_ITEM_NONE, IFACE_("Min"), ICON_NONE);
717 row->prop(&ctrl_ptr, "max", UI_ITEM_NONE, IFACE_("Max"), ICON_NONE);
718
719 but = uiDefIconBut(block,
722 ICON_X,
723 0,
724 0,
725 0.9 * UI_UNIT_X,
726 UI_UNIT_Y,
727 nullptr,
728 0.0,
729 0.0,
730 TIP_("Delete envelope control point"));
733 }
734
736}
737
738static void panel_register_envelope(ARegionType *region_type,
739 const char *id_prefix,
740 PanelTypePollFn poll_fn)
741{
743 region_type, FMODIFIER_TYPE_ENVELOPE, envelope_panel_draw, poll_fn, id_prefix);
744 fmodifier_subpanel_register(region_type,
745 "frame_range",
746 "",
749 poll_fn,
750 panel_type);
751}
752
754
755/* -------------------------------------------------------------------- */
758
759static void limits_panel_draw(const bContext *C, Panel *panel)
760{
761 uiLayout *col, *row, *sub;
762 uiLayout *layout = panel->layout;
763
764 PointerRNA *ptr = fmodifier_get_pointers(C, panel, nullptr);
765
766 uiLayoutSetPropSep(layout, true);
767 uiLayoutSetPropDecorate(layout, false);
768
769 /* Minimums. */
770 col = &layout->column(false);
771 row = &col->row(true, IFACE_("Minimum X"));
772 row->prop(ptr, "use_min_x", UI_ITEM_NONE, "", ICON_NONE);
773 sub = &row->column(true);
774 uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_min_x"));
775 sub->prop(ptr, "min_x", UI_ITEM_NONE, "", ICON_NONE);
776
777 row = &col->row(true, IFACE_("Y"));
778 row->prop(ptr, "use_min_y", UI_ITEM_NONE, "", ICON_NONE);
779 sub = &row->column(true);
780 uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_min_y"));
781 sub->prop(ptr, "min_y", UI_ITEM_NONE, "", ICON_NONE);
782
783 /* Maximums. */
784 col = &layout->column(false);
785 row = &col->row(true, IFACE_("Maximum X"));
786 row->prop(ptr, "use_max_x", UI_ITEM_NONE, "", ICON_NONE);
787 sub = &row->column(true);
788 uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_max_x"));
789 sub->prop(ptr, "max_x", UI_ITEM_NONE, "", ICON_NONE);
790
791 row = &col->row(true, IFACE_("Y"));
792 row->prop(ptr, "use_max_y", UI_ITEM_NONE, "", ICON_NONE);
793 sub = &row->column(true);
794 uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_max_y"));
795 sub->prop(ptr, "max_y", UI_ITEM_NONE, "", ICON_NONE);
796
798}
799
800static void panel_register_limits(ARegionType *region_type,
801 const char *id_prefix,
802 PanelTypePollFn poll_fn)
803{
805 region_type, FMODIFIER_TYPE_LIMITS, limits_panel_draw, poll_fn, id_prefix);
806 fmodifier_subpanel_register(region_type,
807 "frame_range",
808 "",
811 poll_fn,
812 panel_type);
813}
814
816
817/* -------------------------------------------------------------------- */
820
821static void stepped_panel_draw(const bContext *C, Panel *panel)
822{
823 uiLayout *col, *sub, *row;
824 uiLayout *layout = panel->layout;
825
826 PointerRNA *ptr = fmodifier_get_pointers(C, panel, nullptr);
827
828 uiLayoutSetPropSep(layout, true);
829 uiLayoutSetPropDecorate(layout, false);
830
831 /* Stepping Settings. */
832 col = &layout->column(false);
833 col->prop(ptr, "frame_step", UI_ITEM_NONE, std::nullopt, ICON_NONE);
834 col->prop(ptr, "frame_offset", UI_ITEM_NONE, std::nullopt, ICON_NONE);
835
836 /* Start range settings. */
837 row = &layout->row(true, IFACE_("Start Frame"));
838 row->prop(ptr, "use_frame_start", UI_ITEM_NONE, "", ICON_NONE);
839 sub = &row->column(true);
840 uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_frame_start"));
841 sub->prop(ptr, "frame_start", UI_ITEM_NONE, "", ICON_NONE);
842
843 /* End range settings. */
844 row = &layout->row(true, IFACE_("End Frame"));
845 row->prop(ptr, "use_frame_end", UI_ITEM_NONE, "", ICON_NONE);
846 sub = &row->column(true);
847 uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_frame_end"));
848 sub->prop(ptr, "frame_end", UI_ITEM_NONE, "", ICON_NONE);
849
851}
852
853static void panel_register_stepped(ARegionType *region_type,
854 const char *id_prefix,
855 PanelTypePollFn poll_fn)
856{
858 region_type, FMODIFIER_TYPE_STEPPED, stepped_panel_draw, poll_fn, id_prefix);
859 fmodifier_subpanel_register(region_type,
860 "frame_range",
861 "",
864 poll_fn,
865 panel_type);
866}
867
869
870/* -------------------------------------------------------------------- */
873
875 ID *owner_id,
876 ListBase *fmodifiers,
877 uiListPanelIDFromDataFunc panel_id_fn)
878{
879 ARegion *region = CTX_wm_region(C);
880
881 bool panels_match = UI_panel_list_matches_data(region, fmodifiers, panel_id_fn);
882
883 if (!panels_match) {
885 LISTBASE_FOREACH (FModifier *, fcm, fmodifiers) {
886 char panel_idname[MAX_NAME];
887 panel_id_fn(fcm, panel_idname);
888
889 PointerRNA *fcm_ptr = MEM_new<PointerRNA>("panel customdata");
890 *fcm_ptr = RNA_pointer_create_discrete(owner_id, &RNA_FModifier, fcm);
891
892 UI_panel_add_instanced(C, region, &region->panels, panel_idname, fcm_ptr);
893 }
894 }
895 else {
896 /* Assuming there's only one group of instanced panels, update the custom data pointers. */
897 Panel *panel = static_cast<Panel *>(region->panels.first);
898 LISTBASE_FOREACH (FModifier *, fcm, fmodifiers) {
899
900 /* Move to the next instanced panel corresponding to the next modifier. */
901 while ((panel->type == nullptr) || !(panel->type->flag & PANEL_TYPE_INSTANCED)) {
902 panel = panel->next;
903 BLI_assert(panel !=
904 nullptr); /* There shouldn't be fewer panels than modifiers with UIs. */
905 }
906
907 PointerRNA *fcm_ptr = MEM_new<PointerRNA>("panel customdata");
908 *fcm_ptr = RNA_pointer_create_discrete(owner_id, &RNA_FModifier, fcm);
909 UI_panel_custom_data_set(panel, fcm_ptr);
910
911 panel = panel->next;
912 }
913 }
914}
915
917 const char *modifier_panel_prefix,
918 PanelTypePollFn poll_function)
919{
920 panel_register_generator(region_type, modifier_panel_prefix, poll_function);
921 panel_register_fn_generator(region_type, modifier_panel_prefix, poll_function);
922 panel_register_noise(region_type, modifier_panel_prefix, poll_function);
923 panel_register_envelope(region_type, modifier_panel_prefix, poll_function);
924 panel_register_limits(region_type, modifier_panel_prefix, poll_function);
925 panel_register_stepped(region_type, modifier_panel_prefix, poll_function);
926}
927
929 const char *modifier_panel_prefix,
930 PanelTypePollFn poll_function)
931{
932 panel_register_cycles(region_type, modifier_panel_prefix, poll_function);
933}
934
936
937/* -------------------------------------------------------------------- */
943
944/* Copy/Paste Buffer itself (list of FModifier 's) */
945static ListBase fmodifier_copypaste_buf = {nullptr, nullptr};
946
947/* ---------- */
948
950{
951 /* just free the whole buffer */
953}
954
956{
957 bool ok = true;
958
959 /* sanity checks */
960 if (ELEM(nullptr, modifiers, modifiers->first)) {
961 return false;
962 }
963
964 /* copy the whole list, or just the active one? */
965 if (active) {
966 FModifier *fcm = find_active_fmodifier(modifiers);
967
968 if (fcm) {
969 FModifier *fcmN = copy_fmodifier(fcm);
971 }
972 else {
973 ok = false;
974 }
975 }
976 else {
978 }
979
980 /* did we succeed? */
981 return ok;
982}
983
984bool ANIM_fmodifiers_paste_from_buf(ListBase *modifiers, bool replace, FCurve *curve)
985{
986 bool ok = false;
987
988 /* sanity checks */
989 if (modifiers == nullptr) {
990 return false;
991 }
992
993 bool was_cyclic = curve && BKE_fcurve_is_cyclic(curve);
994
995 /* if replacing the list, free the existing modifiers */
996 if (replace) {
997 free_fmodifiers(modifiers);
998 }
999
1000 /* now copy over all the modifiers in the buffer to the end of the list */
1002 /* make a copy of it */
1003 FModifier *fcmN = copy_fmodifier(fcm);
1004
1005 fcmN->curve = curve;
1006
1007 /* make sure the new one isn't active, otherwise the list may get several actives */
1009
1010 /* now add it to the end of the list */
1011 BLI_addtail(modifiers, fcmN);
1012 ok = true;
1013 }
1014
1015 /* adding or removing the Cycles modifier requires an update to handles */
1016 if (curve && BKE_fcurve_is_cyclic(curve) != was_cyclic) {
1018 }
1019
1020 /* did we succeed? */
1021 return ok;
1022}
1023
SpaceGraph * CTX_wm_space_graph(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
int BKE_fcm_envelope_find_index(FCM_EnvelopeData *array, float frame, int arraylen, bool *r_exists)
bool BKE_fcurve_is_cyclic(const FCurve *fcu)
FModifier * copy_fmodifier(const FModifier *src)
const FModifierTypeInfo * get_fmodifier_typeinfo(int type)
void copy_fmodifiers(ListBase *dst, const ListBase *src)
void BKE_fcurve_handles_recalc(FCurve *fcu)
bool remove_fmodifier(ListBase *modifiers, FModifier *fcm)
@ FMI_REQUIRES_ORIGINAL_DATA
FModifier * find_active_fmodifier(ListBase *modifiers)
void free_fmodifiers(ListBase *modifiers)
const FModifierTypeInfo * fmodifier_get_typeinfo(const FModifier *fcm)
@ PANEL_TYPE_INSTANCED
@ PANEL_TYPE_DEFAULT_CLOSED
@ PANEL_TYPE_HEADER_EXPAND
#define BLI_assert(a)
Definition BLI_assert.h:46
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
LinkData * BLI_genericNodeN(void *data)
Definition listbase.cc:922
#define LISTBASE_FOREACH(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void void void bool BLI_listbase_link_move(ListBase *listbase, void *vlink, int step) ATTR_NONNULL()
Definition listbase.cc:436
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define ELEM(...)
#define BLT_I18NCONTEXT_ID_ACTION
#define TIP_(msgid)
#define CTX_IFACE_(context, msgid)
#define IFACE_(msgid)
#define BLT_I18NCONTEXT_DEFAULT_BPYRNA
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_ANIMATION
Definition DNA_ID.h:985
eFModifier_Types
@ FMODIFIER_TYPE_CYCLES
@ FMODIFIER_TYPE_STEPPED
@ FMODIFIER_TYPE_FN_GENERATOR
@ FMODIFIER_TYPE_NOISE
@ FMODIFIER_TYPE_GENERATOR
@ FMODIFIER_TYPE_ENVELOPE
@ FMODIFIER_TYPE_LIMITS
@ FCM_GENERATOR_POLYNOMIAL_FACTORISED
@ FCM_GENERATOR_POLYNOMIAL
@ FMODIFIER_FLAG_USEINFLUENCE
@ FMODIFIER_FLAG_ACTIVE
@ FMODIFIER_FLAG_RANGERESTRICT
@ FCURVE_MOD_OFF
@ SPACE_NLA
@ SPACE_GRAPH
void(*)(void *data_link, char *r_idname) uiListPanelIDFromDataFunc
NlaStrip * ANIM_nla_context_strip(const bContext *C)
FCurve * ANIM_graph_context_fcurve(const bContext *C)
bool(*)(const bContext *C, PanelType *pt) PanelTypePollFn
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:99
static void split(const char *text, const char *seps, char ***str, int *count)
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
#define UI_UNIT_Y
uiBut * uiDefBut(uiBlock *block, int type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_panels_free_instanced(const bContext *C, ARegion *region)
void UI_panel_custom_data_set(Panel *panel, PointerRNA *custom_data)
uiBut * uiDefIconBut(uiBlock *block, int type, int retval, int icon, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_block_align_begin(uiBlock *block)
bool UI_panel_list_matches_data(ARegion *region, ListBase *data, uiListPanelIDFromDataFunc panel_idname_func)
#define UI_UNIT_X
@ UI_BTYPE_BUT
PointerRNA * UI_panel_custom_data_get(const Panel *panel)
void UI_but_funcN_set(uiBut *but, uiButHandleNFunc funcN, void *argN, void *arg2, uiButArgNFree func_argN_free_fn=MEM_freeN, uiButArgNCopy func_argN_copy_fn=MEM_dupallocN)
Panel * UI_panel_add_instanced(const bContext *C, ARegion *region, ListBase *panels, const char *panel_idname, PointerRNA *custom_data)
@ UI_ITEM_R_ICON_ONLY
void uiLayoutSetActive(uiLayout *layout, bool active)
uiBlock * uiLayoutGetBlock(uiLayout *layout)
@ UI_LAYOUT_ALIGN_RIGHT
void uiLayoutSetAlignment(uiLayout *layout, char alignment)
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
void uiLayoutSetEmboss(uiLayout *layout, blender::ui::EmbossType emboss)
#define NC_ANIMATION
Definition WM_types.hh:385
#define NA_EDITED
Definition WM_types.hh:581
#define ND_KEYFRAME
Definition WM_types.hh:491
BMesh const char void * data
#define B_FMODIFIER_REDRAW
static void delete_fmodifier_cb(bContext *C, void *ctx_v, void *fcm_v)
static void panel_register_noise(ARegionType *region_type, const char *id_prefix, PanelTypePollFn poll_fn)
bool ANIM_fmodifiers_paste_from_buf(ListBase *modifiers, bool replace, FCurve *curve)
static void fmodifier_frame_range_draw(const bContext *C, Panel *panel)
static void fmodifier_panel_header(const bContext *C, Panel *panel)
static void fmod_envelope_deletepoint_cb(bContext *, void *fcm_dv, void *ind_v)
static void cycles_panel_draw(const bContext *C, Panel *panel)
static PointerRNA * fmodifier_get_pointers(const bContext *C, const Panel *panel, ID **r_owner_id)
static PanelType * fmodifier_panel_register(ARegionType *region_type, eFModifier_Types type, PanelDrawFn draw, PanelTypePollFn poll, const char *id_prefix)
static void panel_register_stepped(ARegionType *region_type, const char *id_prefix, PanelTypePollFn poll_fn)
static void fmodifier_frame_range_header_draw(const bContext *C, Panel *panel)
static void fmodifier_reorder(bContext *C, Panel *panel, int new_index)
static void generator_panel_draw(const bContext *C, Panel *panel)
void(*)(const bContext *, Panel *) PanelDrawFn
static void fn_generator_panel_draw(const bContext *C, Panel *panel)
#define B_REDR
static void panel_register_cycles(ARegionType *region_type, const char *id_prefix, PanelTypePollFn poll_fn)
static void noise_panel_draw(const bContext *C, Panel *panel)
static ListBase fmodifier_copypaste_buf
void ANIM_modifier_panels_register_graph_only(ARegionType *region_type, const char *modifier_panel_prefix, PanelTypePollFn poll_function)
static void fmodifier_influence_draw(uiLayout *layout, PointerRNA *ptr)
void ANIM_modifier_panels_register_graph_and_NLA(ARegionType *region_type, const char *modifier_panel_prefix, PanelTypePollFn poll_function)
static ListBase * fmodifier_list_space_specific(const bContext *C)
static void panel_register_limits(ARegionType *region_type, const char *id_prefix, PanelTypePollFn poll_fn)
static void panel_register_envelope(ARegionType *region_type, const char *id_prefix, PanelTypePollFn poll_fn)
void ANIM_fmodifiers_copybuf_free()
static void set_fmodifier_expand_flag(const bContext *, Panel *panel, short expand_flag)
static PanelType * fmodifier_subpanel_register(ARegionType *region_type, const char *name, const char *label, PanelDrawFn draw_header, PanelDrawFn draw, PanelTypePollFn poll, PanelType *parent)
static void panel_register_generator(ARegionType *region_type, const char *id_prefix, PanelTypePollFn poll_fn)
static void limits_panel_draw(const bContext *C, Panel *panel)
static void envelope_panel_draw(const bContext *C, Panel *panel)
static void fmod_envelope_addpoint_cb(bContext *C, void *fcm_dv, void *)
bool ANIM_fmodifiers_copy_to_buf(ListBase *modifiers, bool active)
static void stepped_panel_draw(const bContext *C, Panel *panel)
void ANIM_fmodifier_panels(const bContext *C, ID *owner_id, ListBase *fmodifiers, uiListPanelIDFromDataFunc panel_id_fn)
static short get_fmodifier_expand_flag(const bContext *, Panel *panel)
static void panel_register_fn_generator(ARegionType *region_type, const char *id_prefix, PanelTypePollFn poll_fn)
uint col
#define active
#define MEM_SAFE_FREE(v)
#define MAX_NAME
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
ListBase paneltypes
ListBase panels
ListBase modifiers
FCM_EnvelopeData * data
struct FCurve * curve
short ui_expand_flag
Definition DNA_ID.h:404
void * first
ListBase modifiers
void(* reorder)(bContext *C, Panel *pa, int new_index)
void(* set_list_data_expand_flag)(const bContext *C, Panel *pa, short expand_flag)
void(* draw)(const bContext *C, Panel *panel)
char idname[BKE_ST_MAXNAME]
bool(* poll)(const bContext *C, PanelType *pt)
char translation_context[BKE_ST_MAXNAME]
ListBase children
char category[BKE_ST_MAXNAME]
char label[BKE_ST_MAXNAME]
short(* get_list_data_expand_flag)(const bContext *C, Panel *pa)
char parent_id[BKE_ST_MAXNAME]
PanelType * parent
void(* draw_header)(const bContext *C, Panel *panel)
struct PanelType * type
struct uiLayout * layout
struct Panel * next
struct RenderData r
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
uiLayout & row(bool align)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
i
Definition text_draw.cc:230
#define N_(msgid)
void WM_global_report(eReportType type, const char *message)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4227