Blender V5.0
graph_buttons.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
10
11#include <cfloat>
12#include <cstring>
13
14#include "DNA_anim_types.h"
15#include "DNA_object_types.h"
16#include "DNA_scene_types.h"
17
18#include "MEM_guardedalloc.h"
19
20#include "BLI_listbase.h"
21#include "BLI_math_rotation.h"
22#include "BLI_string.h"
23#include "BLI_string_utf8.h"
24#include "BLI_utildefines.h"
25
26#include "BLT_translation.hh"
27
28#include "BKE_anim_data.hh"
29#include "BKE_context.hh"
30#include "BKE_curve.hh"
31#include "BKE_fcurve.hh"
32#include "BKE_fcurve_driver.h"
33#include "BKE_global.hh"
34#include "BKE_screen.hh"
35#include "BKE_unit.hh"
36
37#include "DEG_depsgraph.hh"
39
40#include "WM_api.hh"
41#include "WM_types.hh"
42
43#include "RNA_access.hh"
44#include "RNA_path.hh"
45#include "RNA_prototypes.hh"
46
47#include "ED_anim_api.hh"
48#include "ED_screen.hh"
49#include "ED_undo.hh"
50
51#include "UI_interface.hh"
53#include "UI_resources.hh"
54
55#include "graph_intern.hh" /* own include */
56
57#define B_REDR 1
58
59/* -------------------------------------------------------------------- */
62
63static bool graph_panel_context(const bContext *C, bAnimListElem **ale, FCurve **fcu)
64{
65 bAnimContext ac;
66 bAnimListElem *elem = nullptr;
67
68 /* For now, only draw if we could init the anim-context info
69 * (necessary for all animation-related tools)
70 * to work correctly is able to be correctly retrieved.
71 * There's no point showing empty panels?
72 */
73 if (ANIM_animdata_get_context(C, &ac) == 0) {
74 return false;
75 }
76
77 /* try to find 'active' F-Curve */
78 elem = get_active_fcurve_channel(&ac);
79 if (elem == nullptr) {
80 return false;
81 }
82
83 if (fcu) {
84 *fcu = (FCurve *)elem->data;
85 }
86 if (ale) {
87 *ale = elem;
88 }
89 else {
90 MEM_freeN(elem);
91 }
92
93 return true;
94}
95
97{
98 FCurve *fcu;
99 if (!graph_panel_context(C, nullptr, &fcu)) {
100 return nullptr;
101 }
102
103 return fcu;
104}
105
106static bool graph_panel_poll(const bContext *C, PanelType * /*pt*/)
107{
108 return graph_panel_context(C, nullptr, nullptr);
109}
110
112
113/* -------------------------------------------------------------------- */
116
117static void graph_panel_cursor_header(const bContext *C, Panel *panel)
118{
119 bScreen *screen = CTX_wm_screen(C);
121 uiLayout *col;
122
123 /* get RNA pointers for use when creating the UI elements */
124 PointerRNA spaceptr = RNA_pointer_create_discrete(&screen->id, &RNA_SpaceGraphEditor, sipo);
125
126 /* 2D-Cursor */
127 col = &panel->layout->column(false);
128 col->prop(&spaceptr, "show_cursor", UI_ITEM_NONE, "", ICON_NONE);
129}
130
131static void graph_panel_cursor(const bContext *C, Panel *panel)
132{
133 bScreen *screen = CTX_wm_screen(C);
135 Scene *scene = CTX_data_scene(C);
136 uiLayout *layout = panel->layout;
137 uiLayout *col, *sub;
138
139 /* get RNA pointers for use when creating the UI elements */
140 PointerRNA sceneptr = RNA_id_pointer_create(&scene->id);
141 PointerRNA spaceptr = RNA_pointer_create_discrete(&screen->id, &RNA_SpaceGraphEditor, sipo);
142
143 layout->use_property_split_set(true);
144 layout->use_property_decorate_set(false);
145
146 /* 2D-Cursor */
147 col = &layout->column(false);
148 col->active_set(RNA_boolean_get(&spaceptr, "show_cursor"));
149
150 sub = &col->column(true);
151 if (sipo->mode == SIPO_MODE_DRIVERS) {
152 sub->prop(&spaceptr, "cursor_position_x", UI_ITEM_NONE, IFACE_("Cursor X"), ICON_NONE);
153 }
154 else {
155 sub->prop(&sceneptr, "frame_current", UI_ITEM_NONE, IFACE_("Cursor X"), ICON_NONE);
156 }
157
158 sub->prop(&spaceptr, "cursor_position_y", UI_ITEM_NONE, IFACE_("Y"), ICON_NONE);
159
160 sub = &col->column(true);
161 sub->op("GRAPH_OT_frame_jump", IFACE_("Cursor to Selection"), ICON_NONE);
162 sub->op("GRAPH_OT_snap_cursor_value", IFACE_("Cursor Value to Selection"), ICON_NONE);
163}
164
166
167/* -------------------------------------------------------------------- */
170
171static void graph_panel_properties(const bContext *C, Panel *panel)
172{
173 bAnimListElem *ale;
174 FCurve *fcu;
175 uiLayout *layout = panel->layout;
176 uiLayout *col;
177 char name[256];
178 int icon = 0;
179
180 if (!graph_panel_context(C, &ale, &fcu)) {
181 return;
182 }
183
184 /* F-Curve pointer */
185 PointerRNA fcu_ptr = RNA_pointer_create_discrete(ale->fcurve_owner_id, &RNA_FCurve, fcu);
186
187 /* user-friendly 'name' for F-Curve */
188 col = &layout->column(false);
189 if (ale->type == ANIMTYPE_FCURVE) {
190 /* get user-friendly name for F-Curve */
191 const std::optional<int> optional_icon = getname_anim_fcurve(name, ale->id, fcu);
192 if (optional_icon) {
193 icon = *optional_icon;
194 }
195 else if (ale->id) {
197 }
198 else {
199 icon = ICON_NONE;
200 }
201 }
202 else {
203 /* NLA Control Curve, etc. */
205
206 /* get name */
207 if (acf && acf->name) {
208 acf->name(ale, name);
209 }
210 else {
211 STRNCPY_UTF8(name, IFACE_("<invalid>"));
212 icon = ICON_ERROR;
213 }
214
215 /* icon */
216 if (ale->type == ANIMTYPE_NLACURVE) {
217 icon = ICON_NLA;
218 }
219 }
220 col->label(name, icon);
221
222 layout->use_property_split_set(true);
223 layout->use_property_decorate_set(false);
224
225 /* RNA-Path Editing - only really should be enabled when things aren't working */
226 col = &layout->column(false);
227 col->enabled_set((fcu->flag & FCURVE_DISABLED) != 0);
228 col->prop(&fcu_ptr, "data_path", UI_ITEM_NONE, "", ICON_RNA);
229 col->prop(&fcu_ptr, "array_index", UI_ITEM_NONE, std::nullopt, ICON_NONE);
230
231 /* color settings */
232 col = &layout->column(true);
233 col->prop(&fcu_ptr, "color_mode", UI_ITEM_NONE, IFACE_("Display Color"), ICON_NONE);
234
235 if (fcu->color_mode == FCURVE_COLOR_CUSTOM) {
236 col->prop(&fcu_ptr, "color", UI_ITEM_NONE, IFACE_("Color"), ICON_NONE);
237 }
238
239 /* smoothing setting */
240 col = &layout->column(true);
241 col->prop(&fcu_ptr, "auto_smoothing", UI_ITEM_NONE, IFACE_("Handle Smoothing"), ICON_NONE);
242
243 MEM_freeN(ale);
244}
245
247
248/* -------------------------------------------------------------------- */
251
252/* get 'active' keyframe for panel editing */
254 BezTriple **r_bezt,
255 BezTriple **r_prevbezt)
256{
257 /* zero the pointers */
258 *r_bezt = *r_prevbezt = nullptr;
259
260 const int active_keyframe_index = BKE_fcurve_active_keyframe_index(fcu);
261 if (active_keyframe_index == FCURVE_ACTIVE_KEYFRAME_NONE) {
262 return false;
263 }
264
265 /* The active keyframe should be selected. */
266 BLI_assert(BEZT_ISSEL_ANY(&fcu->bezt[active_keyframe_index]));
267
268 *r_bezt = &fcu->bezt[active_keyframe_index];
269 /* Previous is either one before the active, or the point itself if it's the first. */
270 const int prev_index = max_ii(active_keyframe_index - 1, 0);
271 *r_prevbezt = &fcu->bezt[prev_index];
272
273 return true;
274}
275
276/* update callback for active keyframe properties - base updates stuff */
277static void graphedit_activekey_update_cb(bContext * /*C*/, void *fcu_ptr, void * /*bezt_ptr*/)
278{
279 FCurve *fcu = (FCurve *)fcu_ptr;
280
281 /* make sure F-Curve and its handles are still valid after this editing */
282 sort_time_fcurve(fcu);
284}
285
286/* update callback for active keyframe properties - handle-editing wrapper */
287static void graphedit_activekey_handles_cb(bContext *C, void *fcu_ptr, void *bezt_ptr)
288{
289 BezTriple *bezt = (BezTriple *)bezt_ptr;
290
291 /* since editing the handles, make sure they're set to types which are receptive to editing
292 * see transform_conversions.c :: createTransGraphEditData(), last step in second loop
293 */
294 if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) && ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM)) {
295 /* by changing to aligned handles, these can now be moved... */
296 bezt->h1 = HD_ALIGN;
297 bezt->h2 = HD_ALIGN;
298 }
299 else {
301 }
302
303 /* now call standard updates */
304 graphedit_activekey_update_cb(C, fcu_ptr, bezt_ptr);
305}
306
307/* update callback for editing coordinates of right handle in active keyframe properties
308 * NOTE: we cannot just do graphedit_activekey_handles_cb() due to "order of computation"
309 * weirdness (see calchandleNurb_intern() and #39911)
310 */
311static void graphedit_activekey_left_handle_coord_cb(bContext *C, void *fcu_ptr, void *bezt_ptr)
312{
313 BezTriple *bezt = (BezTriple *)bezt_ptr;
314
315 const char f1 = bezt->f1;
316 const char f3 = bezt->f3;
317
318 bezt->f1 |= SELECT;
319 bezt->f3 &= ~SELECT;
320
321 /* perform normal updates NOW */
322 graphedit_activekey_handles_cb(C, fcu_ptr, bezt_ptr);
323
324 /* restore selection state so that no one notices this hack */
325 bezt->f1 = f1;
326 bezt->f3 = f3;
327}
328
329static void graphedit_activekey_right_handle_coord_cb(bContext *C, void *fcu_ptr, void *bezt_ptr)
330{
331 BezTriple *bezt = (BezTriple *)bezt_ptr;
332
333 /* original state of handle selection - to be restored after performing the recalculation */
334 const char f1 = bezt->f1;
335 const char f3 = bezt->f3;
336
337 /* temporarily make it so that only the right handle is selected, so that updates go correctly
338 * (i.e. it now acts as if we've just transforming the vert when it is selected by itself)
339 */
340 bezt->f1 &= ~SELECT;
341 bezt->f3 |= SELECT;
342
343 /* perform normal updates NOW */
344 graphedit_activekey_handles_cb(C, fcu_ptr, bezt_ptr);
345
346 /* restore selection state so that no one notices this hack */
347 bezt->f1 = f1;
348 bezt->f3 = f3;
349}
350
351static void graph_panel_key_properties(const bContext *C, Panel *panel)
352{
353 bAnimListElem *ale;
354 FCurve *fcu;
355 BezTriple *bezt, *prevbezt;
356
357 uiLayout *layout = panel->layout;
358 const ARegion *region = CTX_wm_region(C);
359 /* Just a width big enough so buttons use entire layout width (will be clamped by it then). */
360 const int but_max_width = region->winx;
361 uiLayout *col;
362 uiBlock *block;
363
364 if (!graph_panel_context(C, &ale, &fcu)) {
365 return;
366 }
367
368 block = layout->block();
369 // UI_block_func_handle_set(block, do_graph_region_buttons, nullptr);
370 layout->use_property_split_set(true);
371 layout->use_property_decorate_set(false);
372
373 /* only show this info if there are keyframes to edit */
374 if (get_active_fcurve_keyframe_edit(fcu, &bezt, &prevbezt)) {
375 PointerRNA fcu_prop_ptr;
376 PropertyRNA *fcu_prop = nullptr;
377 uiBut *but;
378 int unit = B_UNIT_NONE;
379
380 /* RNA pointer to keyframe, to allow editing */
381 PointerRNA bezt_ptr = RNA_pointer_create_discrete(ale->fcurve_owner_id, &RNA_Keyframe, bezt);
382
383 /* get property that F-Curve affects, for some unit-conversion magic */
384 PointerRNA id_ptr = RNA_id_pointer_create(ale->id);
385 if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &fcu_prop_ptr, &fcu_prop)) {
386 /* determine the unit for this property */
387 unit = RNA_SUBTYPE_UNIT(RNA_property_subtype(fcu_prop));
388 }
389
390 /* interpolation */
391 col = &layout->column(false);
392 if (fcu->flag & FCURVE_DISCRETE_VALUES) {
393 uiLayout *split = &col->split(0.33f, true);
394 split->label(IFACE_("Interpolation:"), ICON_NONE);
395 split->label(IFACE_("None for Enum/Boolean"), ICON_IPO_CONSTANT);
396 }
397 else {
398 col->prop(&bezt_ptr, "interpolation", UI_ITEM_NONE, std::nullopt, ICON_NONE);
399 }
400
401 /* easing type */
402 if (bezt->ipo > BEZT_IPO_BEZ) {
403 col->prop(&bezt_ptr, "easing", UI_ITEM_NONE, std::nullopt, ICON_NONE);
404 }
405
406 /* easing extra */
407 switch (bezt->ipo) {
408 case BEZT_IPO_BACK:
409 col = &layout->column(true);
410 col->prop(&bezt_ptr, "back", UI_ITEM_NONE, std::nullopt, ICON_NONE);
411 break;
412 case BEZT_IPO_ELASTIC:
413 col = &layout->column(true);
414 col->prop(&bezt_ptr, "amplitude", UI_ITEM_NONE, std::nullopt, ICON_NONE);
415 col->prop(&bezt_ptr, "period", UI_ITEM_NONE, std::nullopt, ICON_NONE);
416 break;
417 default:
418 break;
419 }
420
421 /* numerical coordinate editing
422 * - we use the button-versions of the calls so that we can attach special update handlers
423 * and unit conversion magic that cannot be achieved using a purely RNA-approach
424 */
425 col = &layout->column(true);
426 /* keyframe itself */
427 {
428 uiItemL_respect_property_split(col, IFACE_("Key Frame"), ICON_NONE);
429 but = uiDefButR(block,
431 B_REDR,
432 "",
433 0,
434 0,
435 but_max_width,
436 UI_UNIT_Y,
437 &bezt_ptr,
438 "co_ui",
439 0,
440 0,
441 0,
442 std::nullopt);
444
445 uiItemL_respect_property_split(col, IFACE_("Value"), ICON_NONE);
446 but = uiDefButR(block,
448 B_REDR,
449 "",
450 0,
451 0,
452 but_max_width,
453 UI_UNIT_Y,
454 &bezt_ptr,
455 "co_ui",
456 1,
457 0,
458 0,
459 std::nullopt);
461 UI_but_unit_type_set(but, unit);
462 }
463
464 /* previous handle - only if previous was Bezier interpolation */
465 if ((prevbezt) && (prevbezt->ipo == BEZT_IPO_BEZ)) {
466
467 col = &layout->column(true);
468 uiItemL_respect_property_split(col, IFACE_("Left Handle Type"), ICON_NONE);
469 but = uiDefButR(block,
471 B_REDR,
472 std::nullopt,
473 0,
474 0,
475 but_max_width,
476 UI_UNIT_Y,
477 &bezt_ptr,
478 "handle_left_type",
479 0,
480 0,
481 0,
482 "Type of left handle");
484
485 uiItemL_respect_property_split(col, IFACE_("Frame"), ICON_NONE);
486 but = uiDefButR(block,
488 B_REDR,
489 "",
490 0,
491 0,
492 but_max_width,
493 UI_UNIT_Y,
494 &bezt_ptr,
495 "handle_left",
496 0,
497 0,
498 0,
499 std::nullopt);
501
502 uiItemL_respect_property_split(col, IFACE_("Value"), ICON_NONE);
503 but = uiDefButR(block,
505 B_REDR,
506 "",
507 0,
508 0,
509 but_max_width,
510 UI_UNIT_Y,
511 &bezt_ptr,
512 "handle_left",
513 1,
514 0,
515 0,
516 std::nullopt);
518 UI_but_unit_type_set(but, unit);
519 }
520
521 /* next handle - only if current is Bezier interpolation */
522 if (bezt->ipo == BEZT_IPO_BEZ) {
523 /* NOTE: special update callbacks are needed on the coords here due to #39911 */
524
525 col = &layout->column(true);
526 uiItemL_respect_property_split(col, IFACE_("Right Handle Type"), ICON_NONE);
527 but = uiDefButR(block,
529 B_REDR,
530 std::nullopt,
531 0,
532 0,
533 but_max_width,
534 UI_UNIT_Y,
535 &bezt_ptr,
536 "handle_right_type",
537 0,
538 0,
539 0,
540 "Type of right handle");
542
543 uiItemL_respect_property_split(col, IFACE_("Frame"), ICON_NONE);
544 but = uiDefButR(block,
546 B_REDR,
547 "",
548 0,
549 0,
550 but_max_width,
551 UI_UNIT_Y,
552 &bezt_ptr,
553 "handle_right",
554 0,
555 0,
556 0,
557 std::nullopt);
559
560 uiItemL_respect_property_split(col, IFACE_("Value"), ICON_NONE);
561 but = uiDefButR(block,
563 B_REDR,
564 "",
565 0,
566 0,
567 but_max_width,
568 UI_UNIT_Y,
569 &bezt_ptr,
570 "handle_right",
571 1,
572 0,
573 0,
574 std::nullopt);
576 UI_but_unit_type_set(but, unit);
577 }
578 }
579 else {
580 if ((fcu->bezt == nullptr) && (fcu->modifiers.first)) {
581 /* modifiers only - so no keyframes to be active */
582 layout->label(RPT_("F-Curve only has F-Modifiers"), ICON_NONE);
583 layout->label(RPT_("See Modifiers panel below"), ICON_INFO);
584 }
585 else if (fcu->fpt) {
586 /* samples only */
587 layout->label(RPT_("F-Curve does not have any keyframes as it only contains sampled points"),
588 ICON_NONE);
589 }
590 else {
591 layout->label(RPT_("No active keyframe on F-Curve"), ICON_NONE);
592 }
593 }
594
595 MEM_freeN(ale);
596}
597
599
600/* -------------------------------------------------------------------- */
603
604#define B_IPO_DEPCHANGE 10
605
606static void do_graph_region_driver_buttons(bContext *C, void *id_v, int event)
607{
608 Main *bmain = CTX_data_main(C);
609 Scene *scene = CTX_data_scene(C);
610
611 switch (event) {
612 case B_IPO_DEPCHANGE: {
613/* Was not actually run ever (nullptr always passed as arg to this callback).
614 * If needed again, will need to check how to pass both fcurve and ID... :/ */
615#if 0
616 /* force F-Curve & Driver to get re-evaluated (same as the old Update Dependencies) */
617 FCurve *fcu = (FCurve *)fcu_v;
618 ChannelDriver *driver = (fcu) ? fcu->driver : nullptr;
619
620 /* clear invalid flags */
621 if (fcu) {
622 fcu->flag &= ~FCURVE_DISABLED;
623 driver->flag &= ~DRIVER_FLAG_INVALID;
624 }
625#endif
626 ID *id = static_cast<ID *>(id_v);
628
629 /* Rebuild depsgraph for the new dependencies, and ensure evaluated copies get flushed. */
632 if (adt != nullptr) {
633 if (adt->action != nullptr) {
635 }
636 if (adt->tmpact != nullptr) {
638 }
639 }
640
641 break;
642 }
643 }
644
645 /* default for now */
646 WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); /* XXX could use better notifier */
647}
648
649/* callback to add a target variable to the active driver */
650static void driver_add_var_cb(bContext *C, void *driver_v, void * /*arg*/)
651{
652 ChannelDriver *driver = (ChannelDriver *)driver_v;
653
654 /* add a new variable */
656 ED_undo_push(C, "Add Driver Variable");
657}
658
659/* callback to remove target variable from active driver */
660static void driver_delete_var_cb(bContext *C, void *driver_v, void *dvar_v)
661{
662 ChannelDriver *driver = (ChannelDriver *)driver_v;
663 DriverVar *dvar = (DriverVar *)dvar_v;
664
665 /* remove the active variable */
666 driver_free_variable_ex(driver, dvar);
667 ED_undo_push(C, "Delete Driver Variable");
668}
669
670/* callback to report why a driver variable is invalid */
671static void driver_dvar_invalid_name_query_cb(bContext *C, void *dvar_v, void * /*arg*/)
672{
674 C, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Invalid Variable Name"), ICON_NONE);
675 uiLayout *layout = UI_popup_menu_layout(pup);
676
677 DriverVar *dvar = (DriverVar *)dvar_v;
678
679 if (dvar->flag & DVAR_FLAG_INVALID_EMPTY) {
680 layout->label(RPT_("It cannot be left blank"), ICON_ERROR);
681 }
682 if (dvar->flag & DVAR_FLAG_INVALID_START_NUM) {
683 layout->label(RPT_("It cannot start with a number"), ICON_ERROR);
684 }
686 layout->label(RPT_("It cannot start with a special character,"
687 " including '$', '@', '!', '~', '+', '-', '_', '.', or ' '"),
688 ICON_NONE);
689 }
690 if (dvar->flag & DVAR_FLAG_INVALID_HAS_SPACE) {
691 layout->label(RPT_("It cannot contain spaces (e.g. 'a space')"), ICON_ERROR);
692 }
693 if (dvar->flag & DVAR_FLAG_INVALID_HAS_DOT) {
694 layout->label(RPT_("It cannot contain dots (e.g. 'a.dot')"), ICON_ERROR);
695 }
697 layout->label(RPT_("It cannot contain special (non-alphabetical/numeric) characters"),
698 ICON_ERROR);
699 }
701 layout->label(RPT_("It cannot be a reserved keyword in Python"), ICON_INFO);
702 }
703
704 UI_popup_menu_end(C, pup);
705}
706
707/* callback to reset the driver's flags */
708static void driver_update_flags_cb(bContext * /*C*/, void *fcu_v, void * /*arg*/)
709{
710 FCurve *fcu = (FCurve *)fcu_v;
711 ChannelDriver *driver = fcu->driver;
712
713 /* clear invalid flags */
714 fcu->flag &= ~FCURVE_DISABLED;
715 driver->flag &= ~DRIVER_FLAG_INVALID;
716}
717
718/* drivers panel poll */
719static bool graph_panel_drivers_poll(const bContext *C, PanelType * /*pt*/)
720{
722
723 if (sipo->mode != SIPO_MODE_DRIVERS) {
724 return false;
725 }
726
727 return graph_panel_context(C, nullptr, nullptr);
728}
729
731 const DriverTarget *dtar,
732 PointerRNA *dtar_ptr)
733{
734 if (dtar->options & DTAR_OPTION_USE_FALLBACK) {
735 uiLayout *row = &layout->row(true);
736 row->prop(dtar_ptr, "use_fallback_value", UI_ITEM_NONE, "", ICON_NONE);
737 row->prop(dtar_ptr, "fallback_value", UI_ITEM_NONE, std::nullopt, ICON_NONE);
738 }
739 else {
740 layout->prop(dtar_ptr, "use_fallback_value", UI_ITEM_NONE, std::nullopt, ICON_NONE);
741 }
742}
743
744/* settings for 'single property' driver variable type */
746{
747 DriverTarget *dtar = &dvar->targets[0];
748 uiLayout *row, *col;
749
750 /* initialize RNA pointer to the target */
751 PointerRNA dtar_ptr = RNA_pointer_create_discrete(id, &RNA_DriverTarget, dtar);
752
753 /* Target ID */
754 row = &layout->row(false);
755 row->red_alert_set((dtar->flag & DTAR_FLAG_INVALID) && !dtar->id);
756 uiTemplateAnyID(row, &dtar_ptr, "id", "id_type", IFACE_("Prop:"));
757
758 /* Target Property */
759 if (dtar->id) {
760 /* get pointer for resolving the property selected */
761 PointerRNA root_ptr = RNA_id_pointer_create(dtar->id);
762
763 /* rna path */
764 col = &layout->column(true);
765 col->red_alert_set(dtar->flag & (DTAR_FLAG_INVALID | DTAR_FLAG_FALLBACK_USED));
767 &dtar_ptr,
768 "data_path",
769 &root_ptr,
771
772 /* Default value. */
773 graph_panel_driverVar_fallback(layout, dtar, &dtar_ptr);
774 }
775}
776
777/* settings for 'rotation difference' driver variable type */
778/* FIXME: 1) Must be same armature for both dtars, 2) Alignment issues... */
779static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar *dvar)
780{
781 DriverTarget *dtar = &dvar->targets[0];
782 DriverTarget *dtar2 = &dvar->targets[1];
783 Object *ob1 = (Object *)dtar->id;
784 Object *ob2 = (Object *)dtar2->id;
785 uiLayout *col;
786
787 /* initialize RNA pointer to the target */
788 PointerRNA dtar_ptr = RNA_pointer_create_discrete(id, &RNA_DriverTarget, dtar);
789 PointerRNA dtar2_ptr = RNA_pointer_create_discrete(id, &RNA_DriverTarget, dtar2);
790
791 /* Object 1 */
792 col = &layout->column(true);
793 col->red_alert_set(dtar->flag & DTAR_FLAG_INVALID); /* XXX: per field... */
794 col->prop(&dtar_ptr, "id", UI_ITEM_NONE, IFACE_("Object 1"), ICON_NONE);
795
796 if (dtar->id && GS(dtar->id->name) == ID_OB && ob1->pose) {
797 PointerRNA tar_ptr = RNA_pointer_create_discrete(dtar->id, &RNA_Pose, ob1->pose);
798 col->prop_search(&dtar_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA);
799 }
800
801 /* Object 2 */
802 col = &layout->column(true);
803 col->red_alert_set(dtar2->flag & DTAR_FLAG_INVALID); /* XXX: per field... */
804 col->prop(&dtar2_ptr, "id", UI_ITEM_NONE, IFACE_("Object 2"), ICON_NONE);
805
806 if (dtar2->id && GS(dtar2->id->name) == ID_OB && ob2->pose) {
807 PointerRNA tar_ptr = RNA_pointer_create_discrete(dtar2->id, &RNA_Pose, ob2->pose);
808 col->prop_search(&dtar2_ptr, "bone_target", &tar_ptr, "bones", "", ICON_BONE_DATA);
809 }
810}
811
812/* settings for 'location difference' driver variable type */
813static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar *dvar)
814{
815 DriverTarget *dtar = &dvar->targets[0];
816 DriverTarget *dtar2 = &dvar->targets[1];
817 Object *ob1 = (Object *)dtar->id;
818 Object *ob2 = (Object *)dtar2->id;
819 uiLayout *col;
820
821 /* initialize RNA pointer to the target */
822 PointerRNA dtar_ptr = RNA_pointer_create_discrete(id, &RNA_DriverTarget, dtar);
823 PointerRNA dtar2_ptr = RNA_pointer_create_discrete(id, &RNA_DriverTarget, dtar2);
824
825 /* Object 1 */
826 col = &layout->column(true);
827 col->red_alert_set(dtar->flag & DTAR_FLAG_INVALID); /* XXX: per field... */
828 col->prop(&dtar_ptr, "id", UI_ITEM_NONE, IFACE_("Object 1"), ICON_NONE);
829
830 if (dtar->id && GS(dtar->id->name) == ID_OB && ob1->pose) {
831 PointerRNA tar_ptr = RNA_pointer_create_discrete(dtar->id, &RNA_Pose, ob1->pose);
832 col->prop_search(&dtar_ptr, "bone_target", &tar_ptr, "bones", IFACE_("Bone"), ICON_BONE_DATA);
833 }
834
835 /* we can clear it again now - it's only needed when creating the ID/Bone fields */
836 col->red_alert_set(false);
837
838 col->prop(&dtar_ptr, "transform_space", UI_ITEM_NONE, std::nullopt, ICON_NONE);
839
840 /* Object 2 */
841 col = &layout->column(true);
842 col->red_alert_set(dtar2->flag & DTAR_FLAG_INVALID); /* XXX: per field... */
843 col->prop(&dtar2_ptr, "id", UI_ITEM_NONE, IFACE_("Object 2"), ICON_NONE);
844
845 if (dtar2->id && GS(dtar2->id->name) == ID_OB && ob2->pose) {
846 PointerRNA tar_ptr = RNA_pointer_create_discrete(dtar2->id, &RNA_Pose, ob2->pose);
847 col->prop_search(&dtar2_ptr, "bone_target", &tar_ptr, "bones", IFACE_("Bone"), ICON_BONE_DATA);
848 }
849
850 /* we can clear it again now - it's only needed when creating the ID/Bone fields */
851 col->red_alert_set(false);
852
853 col->prop(&dtar2_ptr, "transform_space", UI_ITEM_NONE, std::nullopt, ICON_NONE);
854}
855
856/* settings for 'transform channel' driver variable type */
858{
859 DriverTarget *dtar = &dvar->targets[0];
860 Object *ob = (Object *)dtar->id;
861 uiLayout *col, *sub;
862
863 /* initialize RNA pointer to the target */
864 PointerRNA dtar_ptr = RNA_pointer_create_discrete(id, &RNA_DriverTarget, dtar);
865
866 /* properties */
867 col = &layout->column(true);
868 col->red_alert_set(dtar->flag & DTAR_FLAG_INVALID); /* XXX: per field... */
869 col->prop(&dtar_ptr, "id", UI_ITEM_NONE, IFACE_("Object"), ICON_NONE);
870
871 if (dtar->id && GS(dtar->id->name) == ID_OB && ob->pose) {
872 PointerRNA tar_ptr = RNA_pointer_create_discrete(dtar->id, &RNA_Pose, ob->pose);
873 col->prop_search(&dtar_ptr, "bone_target", &tar_ptr, "bones", IFACE_("Bone"), ICON_BONE_DATA);
874 }
875
876 sub = &layout->column(true);
877 sub->prop(&dtar_ptr, "transform_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
878
879 if (ELEM(dtar->transChan,
884 {
885 sub->prop(&dtar_ptr, "rotation_mode", UI_ITEM_NONE, IFACE_("Mode"), ICON_NONE);
886 }
887
888 sub->prop(&dtar_ptr, "transform_space", UI_ITEM_NONE, IFACE_("Space"), ICON_NONE);
889}
890
891/* Settings for 'Context Property' driver variable type. */
893{
894 DriverTarget *dtar = &dvar->targets[0];
895
896 /* Initialize RNA pointer to the target. */
897 PointerRNA dtar_ptr = RNA_pointer_create_discrete(id, &RNA_DriverTarget, dtar);
898
899 /* Target Property. */
900 {
901 uiLayout *row = &layout->row(false);
902 row->prop(&dtar_ptr, "context_property", UI_ITEM_NONE, std::nullopt, ICON_NONE);
903 }
904
905 /* Target Path */
906 {
907 uiLayout *col = &layout->column(true);
908 col->red_alert_set(dtar->flag & (DTAR_FLAG_INVALID | DTAR_FLAG_FALLBACK_USED));
910 &dtar_ptr,
911 "data_path",
912 nullptr,
914 }
915
916 /* Default value. */
917 graph_panel_driverVar_fallback(layout, dtar, &dtar_ptr);
918}
919
920/* ----------------------------------------------------------------- */
921
922/* property driven by the driver - duplicates Active FCurve, but useful for clarity */
923
925 ID *id,
926 FCurve *fcu,
927 const char *label)
928{
929 PointerRNA fcurve_ptr = RNA_pointer_create_discrete(id, &RNA_FCurve, fcu);
930
931 uiBlock *block = layout->block();
932 uiDefButR(block,
934 0,
935 label,
936 0,
937 0,
938 UI_UNIT_X,
939 UI_UNIT_Y,
940 &fcurve_ptr,
941 "mute",
942 0,
943 0,
944 0,
945 TIP_("Let the driver determine this property's value"));
946}
947
948static void graph_panel_drivers_header(const bContext *C, Panel *panel)
949{
950 bAnimListElem *ale;
951 FCurve *fcu;
952 if (!graph_panel_context(C, &ale, &fcu)) {
953 return;
954 }
955
956 graph_draw_driven_property_enabled_btn(panel->layout, ale->id, fcu, IFACE_("Driver"));
957 MEM_freeN(ale);
958}
959
960static void graph_draw_driven_property_panel(uiLayout *layout, ID *id, FCurve *fcu)
961{
962 uiLayout *row;
963 char name[256];
964 int icon = 0;
965
966 /* get user-friendly 'name' for F-Curve */
967 const std::optional<int> optional_icon = getname_anim_fcurve(name, id, fcu);
968 if (optional_icon) {
969 icon = *optional_icon;
970 }
971 else {
973 }
974
975 /* panel layout... */
976 row = &layout->row(true);
978
979 /* -> user friendly 'name' for datablock that owns F-Curve */
980 /* XXX: Actually, we may need the datablock icons only...
981 * (e.g. right now will show bone for bone props). */
982 row->label(id->name + 2, icon);
983
984 /* -> user friendly 'name' for F-Curve/driver target */
985 row->label("", ICON_RIGHTARROW);
986 row->label(name, ICON_RNA);
987}
988
989/* UI properties panel layout for driver settings - shared for Drivers Editor and for */
991 ID *id,
992 FCurve *fcu,
993 const bool is_popover)
994{
995 ChannelDriver *driver = fcu->driver;
996
997 uiLayout *col, *row, *row_outer;
998 uiBlock *block;
999 uiBut *but;
1000
1001 /* set event handler for panel */
1002 block = layout->block();
1004
1005 /* driver-level settings - type, expressions, and errors */
1006 PointerRNA driver_ptr = RNA_pointer_create_discrete(id, &RNA_Driver, driver);
1007
1008 col = &layout->column(true);
1009 block = col->block();
1010 col->prop(&driver_ptr, "type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1011
1012 {
1013 char valBuf[32];
1014
1015 /* value of driver */
1016 row = &col->row(true);
1017 row->label(IFACE_("Driver Value:"), ICON_NONE);
1018 SNPRINTF_UTF8(valBuf, "%.3f", driver->curval);
1019 row->label(valBuf, ICON_NONE);
1020 }
1021
1022 layout->separator();
1023 layout->separator();
1024
1025 /* show expression box if doing scripted drivers,
1026 * and/or error messages when invalid drivers exist */
1027 if (driver->type == DRIVER_TYPE_PYTHON) {
1028 bool bpy_data_expr_error = (strstr(driver->expression, "bpy.data.") != nullptr);
1029 bool bpy_ctx_expr_error = (strstr(driver->expression, "bpy.context.") != nullptr);
1030
1031 /* expression */
1032 /* TODO: "Show syntax hints" button */
1033 col = &layout->column(true);
1034 block = col->block();
1035
1036 col->label(IFACE_("Expression:"), ICON_NONE);
1037 col->prop(&driver_ptr, "expression", UI_ITEM_NONE, "", ICON_NONE);
1038 col->prop(&driver_ptr, "use_self", UI_ITEM_NONE, std::nullopt, ICON_NONE);
1039
1040 /* errors? */
1041 col = &layout->column(true);
1042 block = col->block();
1043
1044 if (driver->flag & DRIVER_FLAG_PYTHON_BLOCKED) {
1045 /* TODO: Add button to enable? */
1046 col->label(RPT_("Python restricted for security"), ICON_ERROR);
1047 col->label(RPT_("Slow Python expression"), ICON_INFO);
1048 }
1049 else if (driver->flag & DRIVER_FLAG_INVALID) {
1050 col->label(RPT_("ERROR: Invalid Python expression"), ICON_CANCEL);
1051 }
1052 else if (!BKE_driver_has_simple_expression(driver)) {
1053 col->label(RPT_("Slow Python expression"), ICON_INFO);
1054 }
1055
1056 /* Explicit bpy-references are evil. Warn about these to prevent errors */
1057 /* TODO: put these in a box? */
1058 if (bpy_data_expr_error || bpy_ctx_expr_error) {
1059 col->label(RPT_("WARNING: Driver expression may not work correctly"), ICON_HELP);
1060
1061 if (bpy_data_expr_error) {
1062 col->label(RPT_("TIP: Use variables instead of bpy.data paths (see below)"), ICON_ERROR);
1063 }
1064 if (bpy_ctx_expr_error) {
1065 col->label(RPT_("TIP: bpy.context is not safe for renderfarm usage"), ICON_ERROR);
1066 }
1067 }
1068 }
1069 else {
1070 /* errors? */
1071 col = &layout->column(true);
1072 block = col->block();
1073
1074 if (driver->flag & DRIVER_FLAG_INVALID) {
1075 col->label(RPT_("ERROR: Invalid target channel(s)"), ICON_ERROR);
1076 }
1077
1078 /* Warnings about a lack of variables
1079 * NOTE: The lack of variables is generally a bad thing, since it indicates
1080 * that the driver doesn't work at all. This particular scenario arises
1081 * primarily when users mistakenly try to use drivers for procedural
1082 * property animation
1083 */
1084 if (BLI_listbase_is_empty(&driver->variables)) {
1085 col->label(RPT_("ERROR: Driver is useless without any inputs"), ICON_ERROR);
1086
1087 if (!BLI_listbase_is_empty(&fcu->modifiers)) {
1088 col->label(RPT_("TIP: Use F-Curves for procedural animation instead"), ICON_INFO);
1089 col->label(RPT_("F-Modifiers can generate curves for those too"), ICON_INFO);
1090 }
1091 }
1092 }
1093
1094 layout->separator();
1095
1096 /* add/copy/paste driver variables */
1097 row_outer = &layout->row(false);
1098
1099 /* add driver variable - add blank */
1100 row = &row_outer->row(true);
1101 block = row->block();
1102 but = uiDefIconTextBut(
1103 block,
1106 ICON_ADD,
1107 IFACE_("Add Input Variable"),
1108 0,
1109 0,
1110 10 * UI_UNIT_X,
1111 UI_UNIT_Y,
1112 nullptr,
1113 TIP_("Add a Driver Variable to keep track of an input used by the driver"));
1114 UI_but_func_set(but, driver_add_var_cb, driver, nullptr);
1115
1116 if (is_popover) {
1117 /* add driver variable - add using eyedropper */
1118 /* XXX: will this operator work like this? */
1119 row->op("UI_OT_eyedropper_driver", "", ICON_EYEDROPPER);
1120 }
1121
1122 /* copy/paste (as sub-row) */
1123 row = &row_outer->row(true);
1124 block = row->block();
1125
1126 row->op("GRAPH_OT_driver_variables_copy", "", ICON_COPYDOWN);
1127 row->op("GRAPH_OT_driver_variables_paste", "", ICON_PASTEDOWN);
1128
1129 /* loop over targets, drawing them */
1130 LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) {
1131 uiLayout *box;
1132 uiLayout *subrow, *sub;
1133
1134 /* sub-layout column for this variable's settings */
1135 col = &layout->column(true);
1136
1137 /* 1) header panel */
1138 box = &col->box();
1139 PointerRNA dvar_ptr = RNA_pointer_create_discrete(id, &RNA_DriverVariable, dvar);
1140
1141 row = &box->row(false);
1142 block = row->block();
1143
1144 /* 1.1) variable type and name */
1145 subrow = &row->row(true);
1146
1147 /* 1.1.1) variable type */
1148
1149 /* HACK: special group just for the enum,
1150 * otherwise we get ugly layout with text included too... */
1151 sub = &subrow->row(true);
1152
1154
1155 sub->prop(&dvar_ptr, "type", UI_ITEM_R_ICON_ONLY, "", ICON_NONE);
1156
1157 /* 1.1.2) variable name */
1158
1159 /* HACK: special group to counteract the effects of the previous enum,
1160 * which now pushes everything too far right */
1161 sub = &subrow->row(true);
1162
1164
1165 sub->prop(&dvar_ptr, "name", UI_ITEM_NONE, "", ICON_NONE);
1166
1167 /* 1.2) invalid name? */
1169
1170 if (dvar->flag & DVAR_FLAG_INVALID_NAME) {
1171 but = uiDefIconBut(block,
1174 ICON_ERROR,
1175 290,
1176 0,
1177 UI_UNIT_X,
1178 UI_UNIT_Y,
1179 nullptr,
1180 0.0,
1181 0.0,
1182 TIP_("Invalid variable name, click here for details"));
1183 UI_but_func_set(but, driver_dvar_invalid_name_query_cb, dvar, nullptr); /* XXX: reports? */
1184 }
1185
1186 /* 1.3) remove button */
1187 but = uiDefIconBut(block,
1190 ICON_X,
1191 290,
1192 0,
1193 UI_UNIT_X,
1194 UI_UNIT_Y,
1195 nullptr,
1196 0.0,
1197 0.0,
1198 TIP_("Delete target variable"));
1199 UI_but_func_set(but, driver_delete_var_cb, driver, dvar);
1201
1202 /* 2) variable type settings */
1203 box = &col->box();
1204 /* controls to draw depends on the type of variable */
1205 switch (dvar->type) {
1206 case DVAR_TYPE_SINGLE_PROP: /* single property */
1207 graph_panel_driverVar__singleProp(box, id, dvar);
1208 break;
1209 case DVAR_TYPE_ROT_DIFF: /* rotational difference */
1210 graph_panel_driverVar__rotDiff(box, id, dvar);
1211 break;
1212 case DVAR_TYPE_LOC_DIFF: /* location difference */
1213 graph_panel_driverVar__locDiff(box, id, dvar);
1214 break;
1215 case DVAR_TYPE_TRANSFORM_CHAN: /* transform channel */
1216 graph_panel_driverVar__transChan(box, id, dvar);
1217 break;
1218 case DVAR_TYPE_CONTEXT_PROP: /* context property */
1220 break;
1221 }
1222
1223 /* 3) value of variable */
1224 {
1225 char valBuf[32];
1226
1227 box = &col->box();
1228 row = &box->row(true);
1229 row->label(IFACE_("Value:"), ICON_NONE);
1230
1231 if ((dvar->type == DVAR_TYPE_ROT_DIFF) ||
1232 (dvar->type == DVAR_TYPE_TRANSFORM_CHAN &&
1233 ELEM(dvar->targets[0].transChan,
1238 dvar->targets[0].rotation_mode != DTAR_ROTMODE_QUATERNION))
1239 {
1240 SNPRINTF_UTF8(valBuf,
1241 "%.3f (%4.1f" BLI_STR_UTF8_DEGREE_SIGN ")",
1242 dvar->curval,
1243 RAD2DEGF(dvar->curval));
1244 }
1245 else {
1246 SNPRINTF_UTF8(valBuf, "%.3f", dvar->curval);
1247 }
1248
1249 row->label(valBuf, ICON_NONE);
1250 }
1251 }
1252 /* Quiet warning about old value being unused before re-assigned. */
1253 UNUSED_VARS(block);
1254
1255 layout->separator();
1256 layout->separator();
1257
1258 /* XXX: This should become redundant. But sometimes the flushing fails,
1259 * so keep this around for a while longer as a "last resort" */
1260 row = &layout->row(true);
1261 block = row->block();
1262 but = uiDefIconTextBut(
1263 block,
1266 ICON_FILE_REFRESH,
1267 IFACE_("Update Dependencies"),
1268 0,
1269 0,
1270 10 * UI_UNIT_X,
1271 UI_UNIT_Y,
1272 nullptr,
1273 TIP_("Force updates of dependencies - Only use this if drivers are not updating correctly"));
1274 UI_but_func_set(but, driver_update_flags_cb, fcu, nullptr);
1275}
1276
1277/* ----------------------------------------------------------------- */
1278
1279/* Panel to show property driven by the driver (in Drivers Editor) - duplicates Active FCurve,
1280 * but useful for clarity. */
1281static void graph_panel_driven_property(const bContext *C, Panel *panel)
1282{
1283 bAnimListElem *ale;
1284 FCurve *fcu;
1285
1286 if (!graph_panel_context(C, &ale, &fcu)) {
1287 return;
1288 }
1289
1290 graph_draw_driven_property_panel(panel->layout, ale->id, fcu);
1291
1292 MEM_freeN(ale);
1293}
1294
1295/* driver settings for active F-Curve
1296 * (only for 'Drivers' mode in Graph Editor, i.e. the full "Drivers Editor") */
1297static void graph_panel_drivers(const bContext *C, Panel *panel)
1298{
1299 bAnimListElem *ale;
1300 FCurve *fcu;
1301
1302 /* Get settings from context */
1303 if (!graph_panel_context(C, &ale, &fcu)) {
1304 return;
1305 }
1306
1307 graph_draw_driver_settings_panel(panel->layout, ale->id, fcu, false);
1308
1309 /* cleanup */
1310 MEM_freeN(ale);
1311}
1312
1313/* ----------------------------------------------------------------- */
1314
1315/* Poll to make this not show up in the graph editor,
1316 * as this is only to be used as a popup elsewhere. */
1318{
1319 return ED_operator_graphedit_active((bContext *)C) == false;
1320}
1321
1322/* popover panel for driver editing anywhere in ui */
1323static void graph_panel_drivers_popover(const bContext *C, Panel *panel)
1324{
1325 uiLayout *layout = panel->layout;
1326
1327 PointerRNA ptr = {};
1328 PropertyRNA *prop = nullptr;
1329 int index = -1;
1330 uiBut *but = nullptr;
1331
1332 /* Get active property to show driver properties for */
1333 but = UI_region_active_but_prop_get(CTX_wm_region(C), &ptr, &prop, &index);
1334 if (but) {
1335 FCurve *fcu;
1336 bool driven, special;
1337
1339 (bContext *)C, &ptr, prop, index, nullptr, nullptr, &driven, &special);
1340
1341 /* Hack: Force all buttons in this panel to be able to know the driver button
1342 * this panel is getting spawned from, so that things like the "Open Drivers Editor"
1343 * button will work.
1344 */
1345 layout->context_set_from_but(but);
1346
1347 /* Populate Panel - With a combination of the contents of the Driven and Driver panels */
1348 if (fcu && fcu->driver) {
1349 ID *id = ptr.owner_id;
1350
1351 PointerRNA ptr_fcurve = RNA_pointer_create_discrete(id, &RNA_FCurve, fcu);
1352 layout->context_ptr_set("active_editable_fcurve", &ptr_fcurve);
1353
1354 /* Driven Property Settings */
1355 layout->label(IFACE_("Driven Property:"), ICON_NONE);
1357 /* TODO: All vs Single */
1358
1359 layout->separator();
1360 layout->separator();
1361
1362 /* Drivers Settings */
1363 graph_draw_driven_property_enabled_btn(panel->layout, id, fcu, IFACE_("Driver:"));
1364 graph_draw_driver_settings_panel(panel->layout, id, fcu, true);
1365 }
1366 }
1367
1368 /* Show drivers editor is always visible */
1369 layout->op("SCREEN_OT_drivers_editor_show", IFACE_("Show in Drivers Editor"), ICON_DRIVER);
1370}
1371
1373
1374/* -------------------------------------------------------------------- */
1379
1380#define B_FMODIFIER_REDRAW 20
1382#define GRAPH_FMODIFIER_PANEL_PREFIX "GRAPH"
1383
1384static void graph_fmodifier_panel_id(void *fcm_link, char *r_name)
1385{
1386 FModifier *fcm = (FModifier *)fcm_link;
1388 const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(type);
1389
1390 if (!fmi) {
1391 /* This can happen when the blend file has data for a modifier that doesn't exist in this
1392 * Blender version (when the blend file is newer). */
1393 r_name[0] = '\0';
1394 return;
1395 }
1396
1398}
1399
1400static void do_graph_region_modifier_buttons(bContext *C, void * /*arg*/, int event)
1401{
1402 switch (event) {
1403 case B_FMODIFIER_REDRAW: /* XXX this should send depsgraph updates too */
1404 /* XXX: need a notifier specially for F-Modifiers */
1406 break;
1407 }
1408}
1409
1410static void graph_panel_modifiers(const bContext *C, Panel *panel)
1411{
1412 bAnimListElem *ale;
1413 FCurve *fcu;
1414 uiLayout *row;
1415 uiBlock *block;
1416
1417 if (!graph_panel_context(C, &ale, &fcu)) {
1418 return;
1419 }
1420
1421 block = panel->layout->block();
1423
1424 /* 'add modifier' button at top of panel */
1425 {
1426 row = &panel->layout->row(false);
1427
1428 /* this is an operator button which calls a 'add modifier' operator...
1429 * a menu might be nicer but would be tricky as we need some custom filtering
1430 */
1431 row->op_menu_enum(C, "GRAPH_OT_fmodifier_add", "type", IFACE_("Add Modifier"), ICON_NONE);
1432
1433 /* copy/paste (as sub-row) */
1434 row = &row->row(true);
1435 row->op("GRAPH_OT_fmodifier_copy", "", ICON_COPYDOWN);
1436 row->op("GRAPH_OT_fmodifier_paste", "", ICON_PASTEDOWN);
1437 }
1438
1440
1441 MEM_freeN(ale);
1442}
1443
1445
1446/* -------------------------------------------------------------------- */
1449
1451{
1452 PanelType *pt;
1453
1454 pt = MEM_callocN<PanelType>("spacetype graph panel properties");
1455 STRNCPY_UTF8(pt->idname, "GRAPH_PT_properties");
1456 STRNCPY_UTF8(pt->label, N_("Active F-Curve"));
1457 STRNCPY_UTF8(pt->category, "F-Curve");
1460 pt->poll = graph_panel_poll;
1461 BLI_addtail(&art->paneltypes, pt);
1462
1463 pt = MEM_callocN<PanelType>("spacetype graph panel properties");
1464 STRNCPY_UTF8(pt->idname, "GRAPH_PT_key_properties");
1465 STRNCPY_UTF8(pt->label, N_("Active Keyframe"));
1466 STRNCPY_UTF8(pt->category, "F-Curve");
1469 pt->poll = graph_panel_poll;
1470 BLI_addtail(&art->paneltypes, pt);
1471
1472 pt = MEM_callocN<PanelType>("spacetype graph panel drivers driven");
1473 STRNCPY_UTF8(pt->idname, "GRAPH_PT_driven_property");
1474 STRNCPY_UTF8(pt->label, N_("Driven Property"));
1475 STRNCPY_UTF8(pt->category, "Drivers");
1479 BLI_addtail(&art->paneltypes, pt);
1480
1481 pt = MEM_callocN<PanelType>("spacetype graph panel drivers");
1482 STRNCPY_UTF8(pt->idname, "GRAPH_PT_drivers");
1483 STRNCPY_UTF8(pt->label, N_("Driver"));
1484 STRNCPY_UTF8(pt->category, "Drivers");
1489 BLI_addtail(&art->paneltypes, pt);
1490
1491 pt = MEM_callocN<PanelType>("spacetype graph panel drivers popover");
1492 STRNCPY_UTF8(pt->idname, "GRAPH_PT_drivers_popover");
1493 STRNCPY_UTF8(pt->label, N_("Add/Edit Driver"));
1494 STRNCPY_UTF8(pt->category, "Drivers");
1498 BLI_addtail(&art->paneltypes, pt);
1499 /* This panel isn't used in this region.
1500 * Add explicitly to global list (so popovers work). */
1501 WM_paneltype_add(pt);
1502
1503 pt = MEM_callocN<PanelType>("spacetype graph panel modifiers");
1504 STRNCPY_UTF8(pt->idname, "GRAPH_PT_modifiers");
1505 STRNCPY_UTF8(pt->label, N_("Modifiers"));
1506 STRNCPY_UTF8(pt->category, "Modifiers");
1510 pt->poll = graph_panel_poll;
1511 BLI_addtail(&art->paneltypes, pt);
1512
1515
1516 pt = MEM_callocN<PanelType>("spacetype graph panel view");
1517 STRNCPY_UTF8(pt->idname, "GRAPH_PT_view");
1518 STRNCPY_UTF8(pt->label, N_("Show Cursor"));
1519 STRNCPY_UTF8(pt->category, "View");
1523 BLI_addtail(&art->paneltypes, pt);
1524}
1525
AnimData * BKE_animdata_from_id(const ID *id)
Definition anim_data.cc:83
SpaceGraph * CTX_wm_space_graph(const bContext *C)
bScreen * CTX_wm_screen(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
@ NURB_HANDLE_TEST_EACH
Definition BKE_curve.hh:61
void BKE_nurb_bezt_handle_test(BezTriple *bezt, eBezTriple_Flag__Alias sel_flag, const eNurbHandleTest_Mode handle_mode, bool use_around_local)
Definition curve.cc:4048
const FModifierTypeInfo * get_fmodifier_typeinfo(int type)
int BKE_fcurve_active_keyframe_index(const FCurve *fcu)
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)
void sort_time_fcurve(FCurve *fcu)
struct DriverVar * driver_add_new_variable(struct ChannelDriver *driver)
bool BKE_driver_has_simple_expression(struct ChannelDriver *driver)
void driver_free_variable_ex(struct ChannelDriver *driver, struct DriverVar *dvar)
#define BKE_ST_MAXNAME
Definition BKE_screen.hh:72
@ PANEL_TYPE_NO_HEADER
@ B_UNIT_NONE
Definition BKE_unit.hh:136
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
MINLINE int max_ii(int a, int b)
#define RAD2DEGF(_rad)
size_t size_t size_t BLI_snprintf_utf8(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
#define SNPRINTF_UTF8(dst, format,...)
#define STRNCPY_UTF8(dst, src)
#define BLI_STR_UTF8_DEGREE_SIGN
#define UNUSED_VARS(...)
#define ELEM(...)
#define RPT_(msgid)
#define TIP_(msgid)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define IFACE_(msgid)
#define BLT_I18NCONTEXT_DEFAULT_BPYRNA
#define BLT_I18NCONTEXT_EDITOR_FILEBROWSER
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ ID_OB
@ DTAR_TRANSCHAN_ROTZ
@ DTAR_TRANSCHAN_ROTW
@ DTAR_TRANSCHAN_ROTX
@ DTAR_TRANSCHAN_ROTY
@ DTAR_OPTION_USE_FALLBACK
@ DRIVER_TYPE_PYTHON
@ FCURVE_COLOR_CUSTOM
@ DVAR_TYPE_LOC_DIFF
@ DVAR_TYPE_TRANSFORM_CHAN
@ DVAR_TYPE_ROT_DIFF
@ DVAR_TYPE_SINGLE_PROP
@ DVAR_TYPE_CONTEXT_PROP
eFModifier_Types
@ DTAR_ROTMODE_QUATERNION
@ DTAR_FLAG_FALLBACK_USED
@ DTAR_FLAG_INVALID
@ DRIVER_FLAG_INVALID
@ DRIVER_FLAG_PYTHON_BLOCKED
#define FCURVE_ACTIVE_KEYFRAME_NONE
@ FCURVE_DISABLED
@ FCURVE_DISCRETE_VALUES
@ DVAR_FLAG_INVALID_START_CHAR
@ DVAR_FLAG_INVALID_NAME
@ DVAR_FLAG_INVALID_EMPTY
@ DVAR_FLAG_INVALID_START_NUM
@ DVAR_FLAG_INVALID_HAS_SPACE
@ DVAR_FLAG_INVALID_HAS_DOT
@ DVAR_FLAG_INVALID_HAS_SPECIAL
@ DVAR_FLAG_INVALID_PY_KEYWORD
@ HD_AUTO_ANIM
@ HD_AUTO
@ HD_ALIGN
@ BEZT_IPO_ELASTIC
@ BEZT_IPO_BACK
@ BEZT_IPO_BEZ
#define BEZT_ISSEL_ANY(bezt)
Object is a sort of wrapper for general info.
@ SIPO_MODE_DRIVERS
@ ANIMTYPE_NLACURVE
@ ANIMTYPE_FCURVE
bool ED_operator_graphedit_active(bContext *C)
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.
StructRNA * ID_code_to_RNA_type(short idcode)
#define RNA_SUBTYPE_UNIT(subtype)
Definition RNA_types.hh:218
#define C
Definition RandGen.cpp:29
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
uiBut * uiDefIconTextBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, int icon, blender::StringRef str, int x, int y, short width, short height, void *poin, std::optional< blender::StringRef > tip)
#define UI_UNIT_Y
uiBut * uiDefButR(uiBlock *block, ButType type, int retval, std::optional< blender::StringRef > str, int x, int y, short width, short height, PointerRNA *ptr, blender::StringRefNull propname, int index, float min, float max, std::optional< blender::StringRef > tip)
void UI_block_emboss_set(uiBlock *block, blender::ui::EmbossType emboss)
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_popup_menu_end(bContext *C, uiPopupMenu *pup)
void uiTemplateAnyID(uiLayout *layout, PointerRNA *ptr, blender::StringRefNull propname, blender::StringRefNull proptypename, std::optional< blender::StringRef > text)
void UI_block_func_handle_set(uiBlock *block, uiBlockHandleFunc func, void *arg)
uiPopupMenu * UI_popup_menu_begin(bContext *C, const char *title, int icon) ATTR_NONNULL()
uiLayout * UI_popup_menu_layout(uiPopupMenu *pup)
void uiTemplatePathBuilder(uiLayout *layout, PointerRNA *ptr, blender::StringRefNull propname, PointerRNA *root_ptr, std::optional< blender::StringRefNull > text)
uiBut * UI_region_active_but_prop_get(const ARegion *region, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
#define UI_UNIT_X
void UI_but_unit_type_set(uiBut *but, int unit_type)
@ UI_ITEM_R_ICON_ONLY
uiLayout * uiItemL_respect_property_split(uiLayout *layout, blender::StringRef text, int icon)
#define UI_ITEM_NONE
#define NC_ANIMATION
Definition WM_types.hh:388
#define NC_SCENE
Definition WM_types.hh:378
#define ND_FRAME
Definition WM_types.hh:434
const bAnimChannelType * ANIM_channel_get_typeinfo(const bAnimListElem *ale)
bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac)
std::optional< int > getname_anim_fcurve(char *name, ID *id, FCurve *fcu)
#define SELECT
#define B_FMODIFIER_REDRAW
#define B_REDR
void ANIM_modifier_panels_register_graph_only(ARegionType *region_type, const char *modifier_panel_prefix, PanelTypePollFn poll_function)
void ANIM_modifier_panels_register_graph_and_NLA(ARegionType *region_type, const char *modifier_panel_prefix, PanelTypePollFn poll_function)
void ANIM_fmodifier_panels(const bContext *C, ID *owner_id, ListBase *fmodifiers, uiListPanelIDFromDataFunc panel_id_fn)
#define GS(x)
uint col
static bool graph_panel_context(const bContext *C, bAnimListElem **ale, FCurve **fcu)
static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar *dvar)
static void graph_panel_properties(const bContext *C, Panel *panel)
static void graph_panel_key_properties(const bContext *C, Panel *panel)
static void graph_draw_driver_settings_panel(uiLayout *layout, ID *id, FCurve *fcu, const bool is_popover)
static void driver_add_var_cb(bContext *C, void *driver_v, void *)
static void graph_draw_driven_property_panel(uiLayout *layout, ID *id, FCurve *fcu)
static void graph_panel_driverVar__transChan(uiLayout *layout, ID *id, DriverVar *dvar)
static void graphedit_activekey_left_handle_coord_cb(bContext *C, void *fcu_ptr, void *bezt_ptr)
static void graphedit_activekey_handles_cb(bContext *C, void *fcu_ptr, void *bezt_ptr)
static void graph_panel_driverVar__contextProp(uiLayout *layout, ID *id, DriverVar *dvar)
#define B_IPO_DEPCHANGE
static void do_graph_region_driver_buttons(bContext *C, void *id_v, int event)
static void graph_panel_cursor(const bContext *C, Panel *panel)
void graph_buttons_register(ARegionType *art)
static bool graph_panel_drivers_popover_poll(const bContext *C, PanelType *)
static void graph_panel_modifiers(const bContext *C, Panel *panel)
static void driver_delete_var_cb(bContext *C, void *driver_v, void *dvar_v)
static void graph_panel_driverVar__singleProp(uiLayout *layout, ID *id, DriverVar *dvar)
static void graph_panel_driven_property(const bContext *C, Panel *panel)
static void graph_panel_driverVar_fallback(uiLayout *layout, const DriverTarget *dtar, PointerRNA *dtar_ptr)
#define GRAPH_FMODIFIER_PANEL_PREFIX
static void graph_panel_cursor_header(const bContext *C, Panel *panel)
static bool get_active_fcurve_keyframe_edit(const FCurve *fcu, BezTriple **r_bezt, BezTriple **r_prevbezt)
static void driver_dvar_invalid_name_query_cb(bContext *C, void *dvar_v, void *)
FCurve * ANIM_graph_context_fcurve(const bContext *C)
static void do_graph_region_modifier_buttons(bContext *C, void *, int event)
static void driver_update_flags_cb(bContext *, void *fcu_v, void *)
static void graphedit_activekey_update_cb(bContext *, void *fcu_ptr, void *)
static void graph_draw_driven_property_enabled_btn(uiLayout *layout, ID *id, FCurve *fcu, const char *label)
static void graph_fmodifier_panel_id(void *fcm_link, char *r_name)
static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar *dvar)
static void graph_panel_drivers(const bContext *C, Panel *panel)
static bool graph_panel_poll(const bContext *C, PanelType *)
static void graph_panel_drivers_popover(const bContext *C, Panel *panel)
static void graphedit_activekey_right_handle_coord_cb(bContext *C, void *fcu_ptr, void *bezt_ptr)
static bool graph_panel_drivers_poll(const bContext *C, PanelType *)
static void graph_panel_drivers_header(const bContext *C, Panel *panel)
bAnimListElem * get_active_fcurve_channel(bAnimContext *ac)
DEG_id_tag_update_ex(cb_data->bmain, cb_data->owner_id, ID_RECALC_TAG_FOR_UNDO|ID_RECALC_SYNC_TO_EVAL)
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
int RNA_struct_ui_icon(const StructRNA *type)
PropertySubType RNA_property_subtype(PropertyRNA *prop)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
PointerRNA RNA_id_pointer_create(ID *id)
bool RNA_path_resolve_property(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop)
Definition rna_path.cc:560
ListBase paneltypes
bAction * action
bAction * tmpact
char expression[256]
DriverTarget targets[8]
char * rna_path
FPoint * fpt
ChannelDriver * driver
BezTriple * bezt
ListBase modifiers
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
void * first
struct bPose * pose
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]
char category[BKE_ST_MAXNAME]
char label[BKE_ST_MAXNAME]
void(* draw_header)(const bContext *C, Panel *panel)
struct uiLayout * layout
void(* name)(bAnimListElem *ale, char *name)
eAnim_ChannelType type
PointerRNA op_menu_enum(const bContext *C, wmOperatorType *ot, blender::StringRefNull propname, std::optional< blender::StringRefNull > name, int icon)
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 red_alert_set(bool red_alert)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
void context_ptr_set(blender::StringRef name, const PointerRNA *ptr)
uiLayout & row(bool align)
void context_set_from_but(const uiBut *but)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
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)
#define N_(msgid)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4238
bool WM_paneltype_add(PanelType *pt)