Blender V5.0
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_utf8.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"
42#include "UI_resources.hh"
43
44#include "ED_anim_api.hh"
45#include "ED_undo.hh"
46
47#include "DEG_depsgraph.hh"
48
49using PanelDrawFn = void (*)(const bContext *, Panel *);
50static void fmodifier_panel_header(const bContext *C, Panel *panel);
51
52/* -------------------------------------------------------------------- */
55
60{
61 ScrArea *area = CTX_wm_area(C);
62
63 if (area->spacetype == SPACE_GRAPH) {
65 return &fcu->modifiers;
66 }
67
68 if (area->spacetype == SPACE_NLA) {
70 return &strip->modifiers;
71 }
72
73 /* This should not be called in any other space. */
74 BLI_assert(false);
75 return nullptr;
76}
77
82static PointerRNA *fmodifier_get_pointers(const bContext *C, const Panel *panel, ID **r_owner_id)
83{
85
86 if (r_owner_id != nullptr) {
87 *r_owner_id = ptr->owner_id;
88 }
89
90 if (C != nullptr && CTX_wm_space_graph(C)) {
92 panel->layout->active_set(!(fcu->flag & FCURVE_MOD_OFF));
93 }
94
95 return ptr;
96}
97
101static void fmodifier_reorder(bContext *C, Panel *panel, int new_index)
102{
103 ID *owner_id;
104 PointerRNA *ptr = fmodifier_get_pointers(nullptr, panel, &owner_id);
105 FModifier *fcm = static_cast<FModifier *>(ptr->data);
107
108 /* Cycles modifier has to be the first, so make sure it's kept that way. */
110 WM_global_report(RPT_ERROR, "Modifier requires original data");
111 return;
112 }
113
115
116 /* Again, make sure we don't move a modifier before a cycles modifier. */
117 FModifier *fcm_first = static_cast<FModifier *>(modifiers->first);
118 const FModifierTypeInfo *fmi_first = get_fmodifier_typeinfo(fcm_first->type);
119 if (fmi_first->requires_flag & FMI_REQUIRES_ORIGINAL_DATA && new_index == 0) {
120 WM_global_report(RPT_ERROR, "Modifier requires original data");
121 return;
122 }
123
124 int current_index = BLI_findindex(modifiers, fcm);
125 BLI_assert(current_index >= 0);
126 BLI_assert(new_index >= 0);
127
128 /* Don't do anything if the drag didn't change the index. */
129 if (current_index == new_index) {
130 return;
131 }
132
133 /* Move the FModifier in the list. */
134 BLI_listbase_link_move(modifiers, fcm, new_index - current_index);
135
136 ED_undo_push(C, "Reorder F-Curve Modifier");
137
140}
141
142static short get_fmodifier_expand_flag(const bContext * /*C*/, Panel *panel)
143{
144 PointerRNA *ptr = fmodifier_get_pointers(nullptr, panel, nullptr);
145 FModifier *fcm = static_cast<FModifier *>(ptr->data);
146
147 return fcm->ui_expand_flag;
148}
149
150static void set_fmodifier_expand_flag(const bContext * /*C*/, Panel *panel, short expand_flag)
151{
152 PointerRNA *ptr = fmodifier_get_pointers(nullptr, panel, nullptr);
153 FModifier *fcm = static_cast<FModifier *>(ptr->data);
154
155 fcm->ui_expand_flag = expand_flag;
156}
157
159 eFModifier_Types type,
160 PanelDrawFn draw,
161 PanelTypePollFn poll,
162 const char *id_prefix)
163{
164 PanelType *panel_type = MEM_callocN<PanelType>(__func__);
165
166 /* Intentionally leave the label field blank. The header is filled with buttons. */
167 const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(type);
168 SNPRINTF_UTF8(panel_type->idname, "%s_PT_%s", id_prefix, fmi->name);
169 STRNCPY_UTF8(panel_type->category, "Modifiers");
171
173 panel_type->draw = draw;
174 panel_type->poll = poll;
175
176 /* Give the panel the special flag that says it was built here and corresponds to a
177 * modifier rather than a #PanelType. */
179 panel_type->reorder = fmodifier_reorder;
182
183 BLI_addtail(&region_type->paneltypes, panel_type);
184
185 return panel_type;
186}
187
195 const char *name,
196 const char *label,
197 PanelDrawFn draw_header,
198 PanelDrawFn draw,
199 PanelTypePollFn poll,
200 PanelType *parent)
201{
202 PanelType *panel_type = MEM_callocN<PanelType>(__func__);
203
204 BLI_assert(parent != nullptr);
205 SNPRINTF_UTF8(panel_type->idname, "%s_%s", parent->idname, name);
206 STRNCPY_UTF8(panel_type->label, label);
207 STRNCPY_UTF8(panel_type->category, "Modifiers");
209
210 panel_type->draw_header = draw_header;
211 panel_type->draw = draw;
212 panel_type->poll = poll;
213 panel_type->flag = PANEL_TYPE_DEFAULT_CLOSED;
214
215 STRNCPY_UTF8(panel_type->parent_id, parent->idname);
216 panel_type->parent = parent;
217 BLI_addtail(&parent->children, BLI_genericNodeN(panel_type));
218 BLI_addtail(&region_type->paneltypes, panel_type);
219
220 return panel_type;
221}
222
224
225/* -------------------------------------------------------------------- */
228
229#define B_REDR 1
230#define B_FMODIFIER_REDRAW 20
231
232/* Callback to remove the given modifier. */
237
238static void delete_fmodifier_cb(bContext *C, void *ctx_v, void *fcm_v)
239{
240 FModifierDeleteContext *ctx = static_cast<FModifierDeleteContext *>(ctx_v);
241 ListBase *modifiers = ctx->modifiers;
242 FModifier *fcm = static_cast<FModifier *>(fcm_v);
243
244 /* remove the given F-Modifier from the active modifier-stack */
245 remove_fmodifier(modifiers, fcm);
246
247 ED_undo_push(C, "Delete F-Curve Modifier");
248
251}
252
254{
255 FModifier *fcm = static_cast<FModifier *>(ptr->data);
256 layout->separator();
257
258 uiLayout *row = &layout->row(true, IFACE_("Influence"));
259 row->prop(ptr, "use_influence", UI_ITEM_NONE, "", ICON_NONE);
260 uiLayout *sub = &row->row(true);
261
263 sub->prop(ptr, "influence", UI_ITEM_NONE, "", ICON_NONE);
264}
265
267{
268 uiLayout *layout = panel->layout;
269
270 PointerRNA *ptr = fmodifier_get_pointers(C, panel, nullptr);
271
272 layout->prop(ptr, "use_restricted_range", UI_ITEM_NONE, std::nullopt, ICON_NONE);
273}
274
275static void fmodifier_frame_range_draw(const bContext *C, Panel *panel)
276{
277 uiLayout *col;
278 uiLayout *layout = panel->layout;
279
280 PointerRNA *ptr = fmodifier_get_pointers(C, panel, nullptr);
281
282 layout->use_property_split_set(true);
283 layout->use_property_decorate_set(false);
284
285 FModifier *fcm = static_cast<FModifier *>(ptr->data);
287
288 col = &layout->column(true);
289 col->prop(ptr, "frame_start", UI_ITEM_NONE, IFACE_("Start"), ICON_NONE);
290 col->prop(ptr, "frame_end", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
291
292 col = &layout->column(true);
293 col->prop(ptr, "blend_in", UI_ITEM_NONE, IFACE_("Blend In"), ICON_NONE);
294 col->prop(ptr, "blend_out", UI_ITEM_NONE, IFACE_("Out"), ICON_NONE);
295}
296
297static void fmodifier_panel_header(const bContext *C, Panel *panel)
298{
299 uiLayout *layout = panel->layout;
300
301 ID *owner_id;
302 PointerRNA *ptr = fmodifier_get_pointers(C, panel, &owner_id);
303 FModifier *fcm = static_cast<FModifier *>(ptr->data);
305
306 uiBlock *block = layout->block();
307
308 uiLayout *sub = &layout->row(true);
309
310 /* Checkbox for 'active' status (for now). */
311 sub->prop(ptr, "active", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
312
313 /* Name. */
314 if (fmi) {
315 sub->prop(ptr, "name", UI_ITEM_NONE, "", ICON_NONE);
316 }
317 else {
318 sub->label(IFACE_("<Unknown Modifier>"), ICON_NONE);
319 }
320 /* Right align. */
321 sub = &layout->row(true);
324
325 /* 'Mute' button. */
326 sub->prop(ptr, "mute", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
327
328 /* Delete button. */
329 uiBut *but = uiDefIconBut(block,
331 B_REDR,
332 ICON_X,
333 0,
334 0,
335 UI_UNIT_X,
336 UI_UNIT_Y,
337 nullptr,
338 0.0,
339 0.0,
340 TIP_("Delete Modifier"));
342 ctx->owner_id = owner_id;
344 BLI_assert(ctx->modifiers != nullptr);
345
347
348 layout->separator();
349}
350
352
353/* -------------------------------------------------------------------- */
356
357static void generator_panel_draw(const bContext *C, Panel *panel)
358{
359 uiLayout *layout = panel->layout;
360
361 ID *owner_id;
362 PointerRNA *ptr = fmodifier_get_pointers(C, panel, &owner_id);
363 FModifier *fcm = static_cast<FModifier *>(ptr->data);
364 FMod_Generator *data = static_cast<FMod_Generator *>(fcm->data);
365
366 layout->prop(ptr, "mode", UI_ITEM_NONE, "", ICON_NONE);
367
368 layout->use_property_split_set(true);
369 layout->use_property_decorate_set(false);
370
371 layout->prop(ptr, "use_additive", UI_ITEM_NONE, std::nullopt, ICON_NONE);
372
373 layout->prop(ptr, "poly_order", UI_ITEM_NONE, IFACE_("Order"), ICON_NONE);
374
375 PropertyRNA *prop = RNA_struct_find_property(ptr, "coefficients");
376 uiLayout *col = &layout->column(true);
377 switch (data->mode) {
378 case FCM_GENERATOR_POLYNOMIAL: /* Polynomial expression. */
379 {
380
381 char xval[32];
382
383 /* The first value gets a "Coefficient" label. */
384 STRNCPY_UTF8(xval, N_("Coefficient"));
385
386 for (int i = 0; i < data->arraysize; i++) {
387 col->prop(ptr, prop, i, 0, UI_ITEM_NONE, IFACE_(xval), ICON_NONE);
388 SNPRINTF_UTF8(xval, "x^%d", i + 1);
389 }
390 break;
391 }
392 case FCM_GENERATOR_POLYNOMIAL_FACTORISED: /* Factorized polynomial expression */
393 {
394 {
395 /* Add column labels above the buttons to prevent confusion.
396 * Fake the property split layout, otherwise the labels use the full row. */
397 uiLayout *split = &col->split(0.4f, false);
398 split->column(false);
399 uiLayout *title_col = &split->column(false);
400 uiLayout *title_row = &title_col->row(true);
401 title_row->label(CTX_IFACE_(BLT_I18NCONTEXT_ID_ACTION, "A"), ICON_NONE);
402 title_row->label(CTX_IFACE_(BLT_I18NCONTEXT_ID_ACTION, "B"), ICON_NONE);
403 }
404
405 uiLayout *first_row = &col->row(true);
406 first_row->prop(ptr, prop, 0, 0, UI_ITEM_NONE, IFACE_("y = (Ax + B)"), ICON_NONE);
407 first_row->prop(ptr, prop, 1, 0, UI_ITEM_NONE, "", ICON_NONE);
408 for (int i = 2; i < data->arraysize - 1; i += 2) {
409 /* \u00d7 is the multiplication symbol. */
410 uiLayout *row = &col->row(true);
411 row->prop(ptr, prop, i, 0, UI_ITEM_NONE, IFACE_("\u00d7 (Ax + B)"), ICON_NONE);
412 row->prop(ptr, prop, i + 1, 0, UI_ITEM_NONE, "", ICON_NONE);
413 }
414 break;
415 }
416 }
417
419}
420
421static void panel_register_generator(ARegionType *region_type,
422 const char *id_prefix,
423 PanelTypePollFn poll_fn)
424{
426 region_type, FMODIFIER_TYPE_GENERATOR, generator_panel_draw, poll_fn, id_prefix);
427 fmodifier_subpanel_register(region_type,
428 "frame_range",
429 "",
432 poll_fn,
433 panel_type);
434}
435
437
438/* -------------------------------------------------------------------- */
441
442static void fn_generator_panel_draw(const bContext *C, Panel *panel)
443{
444 uiLayout *col;
445 uiLayout *layout = panel->layout;
446
447 PointerRNA *ptr = fmodifier_get_pointers(C, panel, nullptr);
448
449 layout->prop(ptr, "function_type", UI_ITEM_NONE, "", ICON_NONE);
450
451 layout->use_property_split_set(true);
452 layout->use_property_decorate_set(false);
453
454 col = &layout->column(false);
455 col->prop(ptr, "use_additive", UI_ITEM_NONE, std::nullopt, ICON_NONE);
456
457 col = &layout->column(false);
458 col->prop(ptr, "amplitude", UI_ITEM_NONE, std::nullopt, ICON_NONE);
459 col->prop(ptr, "phase_multiplier", UI_ITEM_NONE, std::nullopt, ICON_NONE);
460 col->prop(ptr, "phase_offset", UI_ITEM_NONE, std::nullopt, ICON_NONE);
461 col->prop(ptr, "value_offset", UI_ITEM_NONE, std::nullopt, ICON_NONE);
462
464}
465
467 const char *id_prefix,
468 PanelTypePollFn poll_fn)
469{
471 region_type, FMODIFIER_TYPE_FN_GENERATOR, fn_generator_panel_draw, poll_fn, id_prefix);
472 fmodifier_subpanel_register(region_type,
473 "frame_range",
474 "",
477 poll_fn,
478 panel_type);
479}
480
482
483/* -------------------------------------------------------------------- */
486
487static void cycles_panel_draw(const bContext *C, Panel *panel)
488{
489 uiLayout *col;
490 uiLayout *layout = panel->layout;
491
492 PointerRNA *ptr = fmodifier_get_pointers(C, panel, nullptr);
493
494 layout->use_property_split_set(true);
495 layout->use_property_decorate_set(false);
496
497 /* Before. */
498 col = &layout->column(false);
499 col->prop(ptr, "mode_before", UI_ITEM_NONE, std::nullopt, ICON_NONE);
500 col->prop(ptr, "cycles_before", UI_ITEM_NONE, IFACE_("Count"), ICON_NONE);
501
502 /* After. */
503 col = &layout->column(false);
504 col->prop(ptr, "mode_after", UI_ITEM_NONE, std::nullopt, ICON_NONE);
505 col->prop(ptr, "cycles_after", UI_ITEM_NONE, IFACE_("Count"), ICON_NONE);
506
508}
509
510static void panel_register_cycles(ARegionType *region_type,
511 const char *id_prefix,
512 PanelTypePollFn poll_fn)
513{
515 region_type, FMODIFIER_TYPE_CYCLES, cycles_panel_draw, poll_fn, id_prefix);
516 fmodifier_subpanel_register(region_type,
517 "frame_range",
518 "",
521 poll_fn,
522 panel_type);
523}
524
526
527/* -------------------------------------------------------------------- */
530
531static void noise_panel_draw(const bContext *C, Panel *panel)
532{
533 uiLayout *col;
534 uiLayout *layout = panel->layout;
535
536 PointerRNA *ptr = fmodifier_get_pointers(C, panel, nullptr);
537
538 layout->use_property_split_set(true);
539 layout->use_property_decorate_set(false);
540
541 layout->prop(ptr, "blend_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
542
543 col = &layout->column(false);
544 col->prop(ptr, "scale", UI_ITEM_NONE, std::nullopt, ICON_NONE);
545 col->prop(ptr, "strength", UI_ITEM_NONE, std::nullopt, ICON_NONE);
546 col->prop(ptr, "offset", UI_ITEM_NONE, std::nullopt, ICON_NONE);
547 col->prop(ptr, "phase", UI_ITEM_NONE, std::nullopt, ICON_NONE);
548 col->prop(ptr, "depth", UI_ITEM_NONE, std::nullopt, ICON_NONE);
549 col->prop(ptr, "use_legacy_noise", UI_ITEM_NONE, std::nullopt, ICON_NONE);
550 PropertyRNA *prop = RNA_struct_find_property(ptr, "use_legacy_noise");
551 const bool use_legacy_noise = RNA_property_boolean_get(ptr, prop);
552 if (!use_legacy_noise) {
553 col->prop(ptr, "lacunarity", UI_ITEM_NONE, std::nullopt, ICON_NONE);
554 col->prop(ptr, "roughness", UI_ITEM_NONE, std::nullopt, ICON_NONE);
555 }
556
558}
559
560static void panel_register_noise(ARegionType *region_type,
561 const char *id_prefix,
562 PanelTypePollFn poll_fn)
563{
565 region_type, FMODIFIER_TYPE_NOISE, noise_panel_draw, poll_fn, id_prefix);
566 fmodifier_subpanel_register(region_type,
567 "frame_range",
568 "",
571 poll_fn,
572 panel_type);
573}
574
576
577/* -------------------------------------------------------------------- */
580
581static void fmod_envelope_addpoint_cb(bContext *C, void *fcm_dv, void * /*arg*/)
582{
583 Scene *scene = CTX_data_scene(C);
584 FMod_Envelope *env = static_cast<FMod_Envelope *>(fcm_dv);
585 FCM_EnvelopeData *fedn;
587
588 /* init template data */
589 fed.min = -1.0f;
590 fed.max = 1.0f;
591 fed.time = float(scene->r.cfra); /* XXX make this int for ease of use? */
592 fed.f1 = fed.f2 = 0;
593
594 /* check that no data exists for the current frame... */
595 if (env->data) {
596 bool exists;
597 int i = BKE_fcm_envelope_find_index(env->data, float(scene->r.cfra), env->totvert, &exists);
598
599 /* binarysearch_...() will set exists by default to 0,
600 * so if it is non-zero, that means that the point exists already */
601 if (exists) {
602 return;
603 }
604
605 /* add new */
606 fedn = MEM_calloc_arrayN<FCM_EnvelopeData>((env->totvert + 1), "FCM_EnvelopeData");
607
608 /* add the points that should occur before the point to be pasted */
609 if (i > 0) {
610 memcpy(fedn, env->data, i * sizeof(FCM_EnvelopeData));
611 }
612
613 /* add point to paste at index i */
614 *(fedn + i) = fed;
615
616 /* add the points that occur after the point to be pasted */
617 if (i < env->totvert) {
618 memcpy(fedn + i + 1, env->data + i, (env->totvert - i) * sizeof(FCM_EnvelopeData));
619 }
620
621 /* replace (+ free) old with new */
622 MEM_freeN(env->data);
623 env->data = fedn;
624
625 env->totvert++;
626 }
627 else {
628 env->data = MEM_callocN<FCM_EnvelopeData>("FCM_EnvelopeData");
629 *(env->data) = fed;
630
631 env->totvert = 1;
632 }
633}
634
635/* callback to remove envelope data point */
636/* TODO: should we have a separate file for things like this? */
637static void fmod_envelope_deletepoint_cb(bContext * /*C*/, void *fcm_dv, void *ind_v)
638{
639 FMod_Envelope *env = static_cast<FMod_Envelope *>(fcm_dv);
640 FCM_EnvelopeData *fedn;
641 int index = POINTER_AS_INT(ind_v);
642
643 /* check that no data exists for the current frame... */
644 if (env->totvert > 1) {
645 /* allocate a new smaller array */
646 fedn = MEM_calloc_arrayN<FCM_EnvelopeData>((env->totvert - 1), "FCM_EnvelopeData");
647
648 memcpy(fedn, env->data, sizeof(FCM_EnvelopeData) * (index));
649 memcpy(fedn + index,
650 env->data + (index + 1),
651 sizeof(FCM_EnvelopeData) * ((env->totvert - index) - 1));
652
653 /* free old array, and set the new */
654 MEM_freeN(env->data);
655 env->data = fedn;
656 env->totvert--;
657 }
658 else {
659 /* just free array, since the only vert was deleted */
660 MEM_SAFE_FREE(env->data);
661 env->totvert = 0;
662 }
663}
664
665/* draw settings for envelope modifier */
666static void envelope_panel_draw(const bContext *C, Panel *panel)
667{
668 uiLayout *row, *col;
669 uiLayout *layout = panel->layout;
670
671 ID *owner_id;
672 PointerRNA *ptr = fmodifier_get_pointers(C, panel, &owner_id);
673 FModifier *fcm = static_cast<FModifier *>(ptr->data);
674 FMod_Envelope *env = static_cast<FMod_Envelope *>(fcm->data);
675
676 layout->use_property_split_set(true);
677 layout->use_property_decorate_set(false);
678
679 /* General settings. */
680 col = &layout->column(true);
681 col->prop(ptr, "reference_value", UI_ITEM_NONE, IFACE_("Reference"), ICON_NONE);
682 col->prop(ptr, "default_min", UI_ITEM_NONE, IFACE_("Min"), ICON_NONE);
683 col->prop(ptr, "default_max", UI_ITEM_NONE, IFACE_("Max"), ICON_NONE);
684
685 /* Control points list. */
686
687 row = &layout->row(false);
688 uiBlock *block = row->block();
689
690 uiBut *but = uiDefBut(block,
693 IFACE_("Add Control Point"),
694 0,
695 0,
696 7.5 * UI_UNIT_X,
697 UI_UNIT_Y,
698 nullptr,
699 0,
700 0,
701 TIP_("Add a new control-point to the envelope on the current frame"));
702 UI_but_func_set(but, fmod_envelope_addpoint_cb, env, nullptr);
703
704 col = &layout->column(false);
705 col->use_property_split_set(false);
706
707 FCM_EnvelopeData *fed = env->data;
708 for (int i = 0; i < env->totvert; i++, fed++) {
710 owner_id, &RNA_FModifierEnvelopeControlPoint, fed);
711
712 /* get a new row to operate on */
713 row = &col->row(true);
714 block = row->block();
715
716 row->prop(&ctrl_ptr, "frame", UI_ITEM_NONE, std::nullopt, ICON_NONE);
717 row->prop(&ctrl_ptr, "min", UI_ITEM_NONE, IFACE_("Min"), ICON_NONE);
718 row->prop(&ctrl_ptr, "max", UI_ITEM_NONE, IFACE_("Max"), ICON_NONE);
719
720 but = uiDefIconBut(block,
723 ICON_X,
724 0,
725 0,
726 0.9 * UI_UNIT_X,
727 UI_UNIT_Y,
728 nullptr,
729 0.0,
730 0.0,
731 TIP_("Delete envelope control point"));
734 }
735
737}
738
739static void panel_register_envelope(ARegionType *region_type,
740 const char *id_prefix,
741 PanelTypePollFn poll_fn)
742{
744 region_type, FMODIFIER_TYPE_ENVELOPE, envelope_panel_draw, poll_fn, id_prefix);
745 fmodifier_subpanel_register(region_type,
746 "frame_range",
747 "",
750 poll_fn,
751 panel_type);
752}
753
755
756/* -------------------------------------------------------------------- */
759
760static void limits_panel_draw(const bContext *C, Panel *panel)
761{
762 uiLayout *col, *row, *sub;
763 uiLayout *layout = panel->layout;
764
765 PointerRNA *ptr = fmodifier_get_pointers(C, panel, nullptr);
766
767 layout->use_property_split_set(true);
768 layout->use_property_decorate_set(false);
769
770 /* Minimums. */
771 col = &layout->column(false);
772 row = &col->row(true, IFACE_("Minimum X"));
773 row->prop(ptr, "use_min_x", UI_ITEM_NONE, "", ICON_NONE);
774 sub = &row->column(true);
775 sub->active_set(RNA_boolean_get(ptr, "use_min_x"));
776 sub->prop(ptr, "min_x", UI_ITEM_NONE, "", ICON_NONE);
777
778 row = &col->row(true, IFACE_("Y"));
779 row->prop(ptr, "use_min_y", UI_ITEM_NONE, "", ICON_NONE);
780 sub = &row->column(true);
781 sub->active_set(RNA_boolean_get(ptr, "use_min_y"));
782 sub->prop(ptr, "min_y", UI_ITEM_NONE, "", ICON_NONE);
783
784 /* Maximums. */
785 col = &layout->column(false);
786 row = &col->row(true, IFACE_("Maximum X"));
787 row->prop(ptr, "use_max_x", UI_ITEM_NONE, "", ICON_NONE);
788 sub = &row->column(true);
789 sub->active_set(RNA_boolean_get(ptr, "use_max_x"));
790 sub->prop(ptr, "max_x", UI_ITEM_NONE, "", ICON_NONE);
791
792 row = &col->row(true, IFACE_("Y"));
793 row->prop(ptr, "use_max_y", UI_ITEM_NONE, "", ICON_NONE);
794 sub = &row->column(true);
795 sub->active_set(RNA_boolean_get(ptr, "use_max_y"));
796 sub->prop(ptr, "max_y", UI_ITEM_NONE, "", ICON_NONE);
797
799}
800
801static void panel_register_limits(ARegionType *region_type,
802 const char *id_prefix,
803 PanelTypePollFn poll_fn)
804{
806 region_type, FMODIFIER_TYPE_LIMITS, limits_panel_draw, poll_fn, id_prefix);
807 fmodifier_subpanel_register(region_type,
808 "frame_range",
809 "",
812 poll_fn,
813 panel_type);
814}
815
817
818/* -------------------------------------------------------------------- */
821
822static void stepped_panel_draw(const bContext *C, Panel *panel)
823{
824 uiLayout *col, *sub, *row;
825 uiLayout *layout = panel->layout;
826
827 PointerRNA *ptr = fmodifier_get_pointers(C, panel, nullptr);
828
829 layout->use_property_split_set(true);
830 layout->use_property_decorate_set(false);
831
832 /* Stepping Settings. */
833 col = &layout->column(false);
834 col->prop(ptr, "frame_step", UI_ITEM_NONE, std::nullopt, ICON_NONE);
835 col->prop(ptr, "frame_offset", UI_ITEM_NONE, std::nullopt, ICON_NONE);
836
837 /* Start range settings. */
838 row = &layout->row(true, IFACE_("Start Frame"));
839 row->prop(ptr, "use_frame_start", UI_ITEM_NONE, "", ICON_NONE);
840 sub = &row->column(true);
841 sub->active_set(RNA_boolean_get(ptr, "use_frame_start"));
842 sub->prop(ptr, "frame_start", UI_ITEM_NONE, "", ICON_NONE);
843
844 /* End range settings. */
845 row = &layout->row(true, IFACE_("End Frame"));
846 row->prop(ptr, "use_frame_end", UI_ITEM_NONE, "", ICON_NONE);
847 sub = &row->column(true);
848 sub->active_set(RNA_boolean_get(ptr, "use_frame_end"));
849 sub->prop(ptr, "frame_end", UI_ITEM_NONE, "", ICON_NONE);
850
852}
853
854static void panel_register_stepped(ARegionType *region_type,
855 const char *id_prefix,
856 PanelTypePollFn poll_fn)
857{
859 region_type, FMODIFIER_TYPE_STEPPED, stepped_panel_draw, poll_fn, id_prefix);
860 fmodifier_subpanel_register(region_type,
861 "frame_range",
862 "",
865 poll_fn,
866 panel_type);
867}
868
870
871/* -------------------------------------------------------------------- */
874
876 ID *owner_id,
877 ListBase *fmodifiers,
878 uiListPanelIDFromDataFunc panel_id_fn)
879{
880 ARegion *region = CTX_wm_region(C);
881
882 bool panels_match = UI_panel_list_matches_data(region, fmodifiers, panel_id_fn);
883
884 if (!panels_match) {
886 LISTBASE_FOREACH (FModifier *, fcm, fmodifiers) {
887 char panel_idname[MAX_NAME];
888 panel_id_fn(fcm, panel_idname);
889
890 PointerRNA *fcm_ptr = MEM_new<PointerRNA>("panel customdata");
891 *fcm_ptr = RNA_pointer_create_discrete(owner_id, &RNA_FModifier, fcm);
892
893 UI_panel_add_instanced(C, region, &region->panels, panel_idname, fcm_ptr);
894 }
895 }
896 else {
897 /* Assuming there's only one group of instanced panels, update the custom data pointers. */
898 Panel *panel = static_cast<Panel *>(region->panels.first);
899 LISTBASE_FOREACH (FModifier *, fcm, fmodifiers) {
900
901 /* Move to the next instanced panel corresponding to the next modifier. */
902 while ((panel->type == nullptr) || !(panel->type->flag & PANEL_TYPE_INSTANCED)) {
903 panel = panel->next;
904 BLI_assert(panel !=
905 nullptr); /* There shouldn't be fewer panels than modifiers with UIs. */
906 }
907
908 PointerRNA *fcm_ptr = MEM_new<PointerRNA>("panel customdata");
909 *fcm_ptr = RNA_pointer_create_discrete(owner_id, &RNA_FModifier, fcm);
910 UI_panel_custom_data_set(panel, fcm_ptr);
911
912 panel = panel->next;
913 }
914 }
915}
916
918 const char *modifier_panel_prefix,
919 PanelTypePollFn poll_function)
920{
921 panel_register_generator(region_type, modifier_panel_prefix, poll_function);
922 panel_register_fn_generator(region_type, modifier_panel_prefix, poll_function);
923 panel_register_noise(region_type, modifier_panel_prefix, poll_function);
924 panel_register_envelope(region_type, modifier_panel_prefix, poll_function);
925 panel_register_limits(region_type, modifier_panel_prefix, poll_function);
926 panel_register_stepped(region_type, modifier_panel_prefix, poll_function);
927}
928
930 const char *modifier_panel_prefix,
931 PanelTypePollFn poll_function)
932{
933 panel_register_cycles(region_type, modifier_panel_prefix, poll_function);
934}
935
937
938/* -------------------------------------------------------------------- */
944
945/* Copy/Paste Buffer itself (list of FModifier 's) */
946static ListBase fmodifier_copypaste_buf = {nullptr, nullptr};
947
948/* ---------- */
949
951{
952 /* just free the whole buffer */
954}
955
957{
958 bool ok = true;
959
960 /* sanity checks */
961 if (ELEM(nullptr, modifiers, modifiers->first)) {
962 return false;
963 }
964
965 /* copy the whole list, or just the active one? */
966 if (active) {
967 FModifier *fcm = find_active_fmodifier(modifiers);
968
969 if (fcm) {
970 FModifier *fcmN = copy_fmodifier(fcm);
972 }
973 else {
974 ok = false;
975 }
976 }
977 else {
979 }
980
981 /* did we succeed? */
982 return ok;
983}
984
985bool ANIM_fmodifiers_paste_from_buf(ListBase *modifiers, bool replace, FCurve *curve)
986{
987 bool ok = false;
988
989 /* sanity checks */
990 if (modifiers == nullptr) {
991 return false;
992 }
993
994 bool was_cyclic = curve && BKE_fcurve_is_cyclic(curve);
995
996 /* if replacing the list, free the existing modifiers */
997 if (replace) {
998 free_fmodifiers(modifiers);
999 }
1000
1001 /* now copy over all the modifiers in the buffer to the end of the list */
1003 /* make a copy of it */
1004 FModifier *fcmN = copy_fmodifier(fcm);
1005
1006 fcmN->curve = curve;
1007
1008 /* make sure the new one isn't active, otherwise the list may get several actives */
1010
1011 /* now add it to the end of the list */
1012 BLI_addtail(modifiers, fcmN);
1013 ok = true;
1014 }
1015
1016 /* adding or removing the Cycles modifier requires an update to handles */
1017 if (curve && BKE_fcurve_is_cyclic(curve) != was_cyclic) {
1019 }
1020
1021 /* did we succeed? */
1022 return ok;
1023}
1024
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)
@ RPT_ERROR
Definition BKE_report.hh:39
@ 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_UTF8(dst, format,...)
#define STRNCPY_UTF8(dst, src)
#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:1077
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
#define MAX_NAME
Definition DNA_defs.h:50
@ 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:98
static void split(const char *text, const char *seps, char ***str, int *count)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define C
Definition RandGen.cpp:29
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
#define UI_UNIT_Y
uiBut * uiDefIconBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_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_panels_free_instanced(const bContext *C, ARegion *region)
void UI_panel_custom_data_set(Panel *panel, PointerRNA *custom_data)
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
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)
uiBut * uiDefBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_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)
@ UI_ITEM_R_ICON_ONLY
#define UI_ITEM_NONE
#define NC_ANIMATION
Definition WM_types.hh:388
#define NA_EDITED
Definition WM_types.hh:584
#define ND_KEYFRAME
Definition WM_types.hh:494
BMesh const char void * data
nullptr float
#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
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
const char * name
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:414
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 use_property_decorate_set(bool is_sep)
void alignment_set(blender::ui::LayoutAlign alignment)
uiBlock * block() const
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
void active_set(bool active)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
uiLayout & row(bool align)
void emboss_set(blender::ui::EmbossType emboss)
void use_property_split_set(bool value)
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:4238