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