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