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