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