c8e07dbda681bc28dd0cb2048da65ee1817ad83f
[blender.git] / source / blender / editors / space_graph / graph_edit.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) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Joshua Leung
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/space_graph/graph_edit.c
29  *  \ingroup spgraph
30  */
31
32
33 #include <math.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <float.h>
37
38 #ifdef WITH_AUDASPACE
39 #  include "AUD_C-API.h"
40 #endif
41
42 #include "MEM_guardedalloc.h"
43
44 #include "BLI_blenlib.h"
45 #include "BLI_math.h"
46 #include "BLI_utildefines.h"
47
48 #include "DNA_anim_types.h"
49 #include "DNA_object_types.h"
50 #include "DNA_scene_types.h"
51
52 #include "RNA_access.h"
53 #include "RNA_define.h"
54 #include "RNA_enum_types.h"
55
56 #include "BLF_translation.h"
57
58 #include "BKE_fcurve.h"
59 #include "BKE_global.h"
60 #include "BKE_nla.h"
61 #include "BKE_context.h"
62 #include "BKE_report.h"
63
64 #include "UI_interface.h"
65 #include "UI_resources.h"
66 #include "UI_view2d.h"
67
68 #include "ED_anim_api.h"
69 #include "ED_keyframing.h"
70 #include "ED_keyframes_edit.h"
71 #include "ED_screen.h"
72 #include "ED_transform.h"
73 #include "ED_markers.h"
74
75 #include "WM_api.h"
76 #include "WM_types.h"
77
78 #include "graph_intern.h"
79
80 /* ************************************************************************** */
81 /* KEYFRAME-RANGE STUFF */
82
83 /* *************************** Calculate Range ************************** */
84
85 /* Get the min/max keyframes*/
86 /* note: it should return total boundbox, filter for selection only can be argument... */
87 void get_graph_keyframe_extents(bAnimContext *ac, float *xmin, float *xmax, float *ymin, float *ymax, 
88                                 const short do_sel_only, const short include_handles)
89 {
90         Scene *scene = ac->scene;
91         
92         ListBase anim_data = {NULL, NULL};
93         bAnimListElem *ale;
94         int filter;
95         
96         /* get data to filter, from Dopesheet */
97         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
98         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
99         
100         /* set large values initial values that will be easy to override */
101         if (xmin) *xmin = 999999999.0f;
102         if (xmax) *xmax = -999999999.0f;
103         if (ymin) *ymin = 999999999.0f;
104         if (ymax) *ymax = -999999999.0f;
105         
106         /* check if any channels to set range with */
107         if (anim_data.first) {
108                 short foundBounds = FALSE;
109                 
110                 /* go through channels, finding max extents */
111                 for (ale = anim_data.first; ale; ale = ale->next) {
112                         AnimData *adt = ANIM_nla_mapping_get(ac, ale);
113                         FCurve *fcu = (FCurve *)ale->key_data;
114                         float txmin, txmax, tymin, tymax;
115                         float unitFac;
116                         
117                         /* get range */
118                         if (calc_fcurve_bounds(fcu, &txmin, &txmax, &tymin, &tymax, do_sel_only, include_handles)) {
119                                 /* apply NLA scaling */
120                                 if (adt) {
121                                         txmin = BKE_nla_tweakedit_remap(adt, txmin, NLATIME_CONVERT_MAP);
122                                         txmax = BKE_nla_tweakedit_remap(adt, txmax, NLATIME_CONVERT_MAP);
123                                 }
124                                 
125                                 /* apply unit corrections */
126                                 unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, 0);
127                                 tymin *= unitFac;
128                                 tymax *= unitFac;
129                                 
130                                 /* try to set cur using these values, if they're more extreme than previously set values */
131                                 if ((xmin) && (txmin < *xmin)) *xmin = txmin;
132                                 if ((xmax) && (txmax > *xmax)) *xmax = txmax;
133                                 if ((ymin) && (tymin < *ymin)) *ymin = tymin;
134                                 if ((ymax) && (tymax > *ymax)) *ymax = tymax;
135                                 
136                                 foundBounds = TRUE;
137                         }
138                 }
139                 
140                 /* ensure that the extents are not too extreme that view implodes...*/
141                 if (foundBounds) {
142                         if ((xmin && xmax) && (fabsf(*xmax - *xmin) < 0.1f)) *xmax += 0.1f;
143                         if ((ymin && ymax) && (fabsf(*ymax - *ymin) < 0.1f)) *ymax += 0.1f;
144                 }
145                 else {
146                         if (xmin) *xmin = (float)PSFRA;
147                         if (xmax) *xmax = (float)PEFRA;
148                         if (ymin) *ymin = -5;
149                         if (ymax) *ymax = 5;
150                 }
151                 
152                 /* free memory */
153                 BLI_freelistN(&anim_data);
154         }
155         else {
156                 /* set default range */
157                 if (ac->scene) {
158                         if (xmin) *xmin = (float)PSFRA;
159                         if (xmax) *xmax = (float)PEFRA;
160                 }
161                 else {
162                         if (xmin) *xmin = -5;
163                         if (xmax) *xmax = 100;
164                 }
165                 
166                 if (ymin) *ymin = -5;
167                 if (ymax) *ymax = 5;
168         }
169 }
170
171 /* ****************** Automatic Preview-Range Operator ****************** */
172
173 static int graphkeys_previewrange_exec(bContext *C, wmOperator *UNUSED(op))
174 {
175         bAnimContext ac;
176         Scene *scene;
177         float min, max;
178         
179         /* get editor data */
180         if (ANIM_animdata_get_context(C, &ac) == 0)
181                 return OPERATOR_CANCELLED;
182         if (ac.scene == NULL)
183                 return OPERATOR_CANCELLED;
184         else
185                 scene = ac.scene;
186         
187         /* set the range directly */
188         get_graph_keyframe_extents(&ac, &min, &max, NULL, NULL, FALSE, FALSE);
189         scene->r.flag |= SCER_PRV_RANGE;
190         scene->r.psfra = (int)floor(min + 0.5f);
191         scene->r.pefra = (int)floor(max + 0.5f);
192         
193         /* set notifier that things have changed */
194         // XXX err... there's nothing for frame ranges yet, but this should do fine too
195         WM_event_add_notifier(C, NC_SCENE | ND_FRAME, ac.scene);
196         
197         return OPERATOR_FINISHED;
198 }
199  
200 void GRAPH_OT_previewrange_set(wmOperatorType *ot)
201 {
202         /* identifiers */
203         ot->name = "Auto-Set Preview Range";
204         ot->idname = "GRAPH_OT_previewrange_set";
205         ot->description = "Automatically set Preview Range based on range of keyframes";
206         
207         /* api callbacks */
208         ot->exec = graphkeys_previewrange_exec;
209         ot->poll = ED_operator_graphedit_active; // XXX: unchecked poll to get fsamples working too, but makes modifier damage trickier...
210         
211         /* flags */
212         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
213 }
214
215 /* ****************** View-All Operator ****************** */
216
217 static int graphkeys_viewall(bContext *C, const short do_sel_only, const short include_handles,
218                              const int smooth_viewtx)
219 {
220         bAnimContext ac;
221         rctf cur_new;
222
223         /* get editor data */
224         if (ANIM_animdata_get_context(C, &ac) == 0)
225                 return OPERATOR_CANCELLED;
226
227         /* set the horizontal range, with an extra offset so that the extreme keys will be in view */
228         get_graph_keyframe_extents(&ac,
229                                    &cur_new.xmin, &cur_new.xmax,
230                                    &cur_new.ymin, &cur_new.ymax,
231                                    do_sel_only, include_handles);
232
233         BLI_rctf_scale(&cur_new, 1.1f);
234
235         UI_view2d_smooth_view(C, ac.ar, &cur_new, smooth_viewtx);
236
237         return OPERATOR_FINISHED;
238 }
239
240 /* ......... */
241
242 static int graphkeys_viewall_exec(bContext *C, wmOperator *op)
243 {
244         const short include_handles = RNA_boolean_get(op->ptr, "include_handles");
245         const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
246         
247         /* whole range */
248         return graphkeys_viewall(C, false, include_handles, smooth_viewtx);
249 }
250  
251 static int graphkeys_view_selected_exec(bContext *C, wmOperator *op)
252 {
253         const short include_handles = RNA_boolean_get(op->ptr, "include_handles");
254         const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
255         
256         /* only selected */
257         return graphkeys_viewall(C, true, include_handles, smooth_viewtx);
258 }
259
260 void GRAPH_OT_view_all(wmOperatorType *ot)
261 {
262         /* identifiers */
263         ot->name = "View All";
264         ot->idname = "GRAPH_OT_view_all";
265         ot->description = "Reset viewable area to show full keyframe range";
266         
267         /* api callbacks */
268         ot->exec = graphkeys_viewall_exec;
269         ot->poll = ED_operator_graphedit_active; /* XXX: unchecked poll to get fsamples working too, but makes modifier damage trickier... */
270
271         /* flags */
272         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
273         
274         /* props */
275         ot->prop = RNA_def_boolean(ot->srna, "include_handles", TRUE, "Include Handles", 
276                                    "Include handles of keyframes when calculating extents");
277 }
278
279 void GRAPH_OT_view_selected(wmOperatorType *ot)
280 {
281         /* identifiers */
282         ot->name = "View Selected";
283         ot->idname = "GRAPH_OT_view_selected";
284         ot->description = "Reset viewable area to show selected keyframe range";
285
286         /* api callbacks */
287         ot->exec = graphkeys_view_selected_exec;
288         ot->poll = ED_operator_graphedit_active; /* XXX: unchecked poll to get fsamples working too, but makes modifier damage trickier... */
289
290         /* flags */
291         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
292         
293         /* props */
294         ot->prop = RNA_def_boolean(ot->srna, "include_handles", TRUE, "Include Handles", 
295                                    "Include handles of keyframes when calculating extents");
296 }
297
298 /* ******************** Create Ghost-Curves Operator *********************** */
299 /* This operator samples the data of the selected F-Curves to F-Points, storing them
300  * as 'ghost curves' in the active Graph Editor
301  */
302
303 /* Bake each F-Curve into a set of samples, and store as a ghost curve */
304 static void create_ghost_curves(bAnimContext *ac, int start, int end)
305 {       
306         SpaceIpo *sipo = (SpaceIpo *)ac->sl;
307         ListBase anim_data = {NULL, NULL};
308         bAnimListElem *ale;
309         int filter;
310         
311         /* free existing ghost curves */
312         free_fcurves(&sipo->ghostCurves);
313         
314         /* sanity check */
315         if (start >= end) {
316                 printf("Error: Frame range for Ghost F-Curve creation is inappropriate\n");
317                 return;
318         }
319         
320         /* filter data */
321         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
322         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
323         
324         /* loop through filtered data and add keys between selected keyframes on every frame  */
325         for (ale = anim_data.first; ale; ale = ale->next) {
326                 FCurve *fcu = (FCurve *)ale->key_data;
327                 FCurve *gcu = MEM_callocN(sizeof(FCurve), "Ghost FCurve");
328                 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
329                 ChannelDriver *driver = fcu->driver;
330                 FPoint *fpt;
331                 float unitFac;
332                 int cfra;
333                 
334                 /* disable driver so that it don't muck up the sampling process */
335                 fcu->driver = NULL;
336                 
337                 /* calculate unit-mapping factor */
338                 unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, 0);
339                 
340                 /* create samples, but store them in a new curve 
341                  *      - we cannot use fcurve_store_samples() as that will only overwrite the original curve 
342                  */
343                 gcu->fpt = fpt = MEM_callocN(sizeof(FPoint) * (end - start + 1), "Ghost FPoint Samples");
344                 gcu->totvert = end - start + 1;
345                 
346                 /* use the sampling callback at 1-frame intervals from start to end frames */
347                 for (cfra = start; cfra <= end; cfra++, fpt++) {
348                         float cfrae = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP);
349                         
350                         fpt->vec[0] = cfrae;
351                         fpt->vec[1] = fcurve_samplingcb_evalcurve(fcu, NULL, cfrae) * unitFac;
352                 }
353                 
354                 /* set color of ghost curve 
355                  *      - make the color slightly darker
356                  */
357                 gcu->color[0] = fcu->color[0] - 0.07f;
358                 gcu->color[1] = fcu->color[1] - 0.07f;
359                 gcu->color[2] = fcu->color[2] - 0.07f;
360                 
361                 /* store new ghost curve */
362                 BLI_addtail(&sipo->ghostCurves, gcu);
363                 
364                 /* restore driver */
365                 fcu->driver = driver;
366         }
367         
368         /* admin and redraws */
369         BLI_freelistN(&anim_data);
370 }
371
372 /* ------------------- */
373
374 static int graphkeys_create_ghostcurves_exec(bContext *C, wmOperator *UNUSED(op))
375 {
376         bAnimContext ac;
377         View2D *v2d;
378         int start, end;
379         
380         /* get editor data */
381         if (ANIM_animdata_get_context(C, &ac) == 0)
382                 return OPERATOR_CANCELLED;
383                 
384         /* ghost curves are snapshots of the visible portions of the curves, so set range to be the visible range */
385         v2d = &ac.ar->v2d;
386         start = (int)v2d->cur.xmin;
387         end = (int)v2d->cur.xmax;
388         
389         /* bake selected curves into a ghost curve */
390         create_ghost_curves(&ac, start, end);
391         
392         /* update this editor only */
393         ED_area_tag_redraw(CTX_wm_area(C));
394         
395         return OPERATOR_FINISHED;
396 }
397  
398 void GRAPH_OT_ghost_curves_create(wmOperatorType *ot)
399 {
400         /* identifiers */
401         ot->name = "Create Ghost Curves";
402         ot->idname = "GRAPH_OT_ghost_curves_create";
403         ot->description = "Create snapshot (Ghosts) of selected F-Curves as background aid for active Graph Editor";
404         
405         /* api callbacks */
406         ot->exec = graphkeys_create_ghostcurves_exec;
407         ot->poll = graphop_visible_keyframes_poll;
408         
409         /* flags */
410         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
411         
412         // todo: add props for start/end frames
413 }
414
415 /* ******************** Clear Ghost-Curves Operator *********************** */
416 /* This operator clears the 'ghost curves' for the active Graph Editor */
417
418 static int graphkeys_clear_ghostcurves_exec(bContext *C, wmOperator *UNUSED(op))
419 {
420         bAnimContext ac;
421         SpaceIpo *sipo;
422         
423         /* get editor data */
424         if (ANIM_animdata_get_context(C, &ac) == 0)
425                 return OPERATOR_CANCELLED;
426         sipo = (SpaceIpo *)ac.sl;
427                 
428         /* if no ghost curves, don't do anything */
429         if (sipo->ghostCurves.first == NULL)
430                 return OPERATOR_CANCELLED;
431         
432         /* free ghost curves */
433         free_fcurves(&sipo->ghostCurves);
434         
435         /* update this editor only */
436         ED_area_tag_redraw(CTX_wm_area(C));
437         
438         return OPERATOR_FINISHED;
439 }
440  
441 void GRAPH_OT_ghost_curves_clear(wmOperatorType *ot)
442 {
443         /* identifiers */
444         ot->name = "Clear Ghost Curves";
445         ot->idname = "GRAPH_OT_ghost_curves_clear";
446         ot->description = "Clear F-Curve snapshots (Ghosts) for active Graph Editor";
447         
448         /* api callbacks */
449         ot->exec = graphkeys_clear_ghostcurves_exec;
450         ot->poll = ED_operator_graphedit_active;
451         
452         /* flags */
453         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
454 }
455
456 /* ************************************************************************** */
457 /* GENERAL STUFF */
458
459 /* ******************** Insert Keyframes Operator ************************* */
460
461 /* defines for insert keyframes tool */
462 static EnumPropertyItem prop_graphkeys_insertkey_types[] = {
463         {1, "ALL", 0, "All Channels", ""},
464         {2, "SEL", 0, "Only Selected Channels", ""},
465         {0, NULL, 0, NULL, NULL}
466 };
467
468 /* this function is responsible for snapping keyframes to frame-times */
469 static void insert_graph_keys(bAnimContext *ac, short mode) 
470 {
471         ListBase anim_data = {NULL, NULL};
472         bAnimListElem *ale;
473         int filter;
474         
475         ReportList *reports = ac->reports;
476         Scene *scene = ac->scene;
477         short flag = 0;
478         
479         /* filter data */
480         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
481         if (mode == 2) filter |= ANIMFILTER_SEL;
482         
483         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
484         
485         /* init keyframing flag */
486         flag = ANIM_get_keyframing_flags(scene, 1);
487         
488         /* insert keyframes */
489         for (ale = anim_data.first; ale; ale = ale->next) {
490                 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
491                 FCurve *fcu = (FCurve *)ale->key_data;
492                 float cfra;
493                 
494                 /* adjust current frame for NLA-mapping */
495                 if (adt)
496                         cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
497                 else 
498                         cfra = (float)CFRA;
499                         
500                 /* if there's an id */
501                 if (ale->id)
502                         insert_keyframe(reports, ale->id, NULL, ((fcu->grp) ? (fcu->grp->name) : (NULL)), fcu->rna_path, fcu->array_index, cfra, flag);
503                 else
504                         insert_vert_fcurve(fcu, cfra, fcu->curval, 0);
505         }
506         
507         BLI_freelistN(&anim_data);
508 }
509
510 /* ------------------- */
511
512 static int graphkeys_insertkey_exec(bContext *C, wmOperator *op)
513 {
514         bAnimContext ac;
515         short mode;
516         
517         /* get editor data */
518         if (ANIM_animdata_get_context(C, &ac) == 0)
519                 return OPERATOR_CANCELLED;
520                 
521         /* which channels to affect? */
522         mode = RNA_enum_get(op->ptr, "type");
523         
524         /* insert keyframes */
525         insert_graph_keys(&ac, mode);
526         
527         /* validate keyframes after editing */
528         ANIM_editkeyframes_refresh(&ac);
529         
530         /* set notifier that keyframes have changed */
531         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
532         
533         return OPERATOR_FINISHED;
534 }
535
536 void GRAPH_OT_keyframe_insert(wmOperatorType *ot)
537 {
538         /* identifiers */
539         ot->name = "Insert Keyframes";
540         ot->idname = "GRAPH_OT_keyframe_insert";
541         ot->description = "Insert keyframes for the specified channels";
542         
543         /* api callbacks */
544         ot->invoke = WM_menu_invoke;
545         ot->exec = graphkeys_insertkey_exec;
546         ot->poll = graphop_editable_keyframes_poll;
547         
548         /* flags */
549         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
550         
551         /* id-props */
552         ot->prop = RNA_def_enum(ot->srna, "type", prop_graphkeys_insertkey_types, 0, "Type", "");
553 }
554
555 /* ******************** Click-Insert Keyframes Operator ************************* */
556
557 static int graphkeys_click_insert_exec(bContext *C, wmOperator *op)
558 {
559         bAnimContext ac;
560         bAnimListElem *ale;
561         AnimData *adt;
562         FCurve *fcu;
563         float frame, val;
564         
565         /* get animation context */
566         if (ANIM_animdata_get_context(C, &ac) == 0)
567                 return OPERATOR_CANCELLED;
568         
569         /* get active F-Curve 'anim-list-element' */
570         ale = get_active_fcurve_channel(&ac);
571         if (ELEM(NULL, ale, ale->data)) {
572                 if (ale) MEM_freeN(ale);
573                 return OPERATOR_CANCELLED;
574         }
575         fcu = ale->data;
576         
577         /* when there are F-Modifiers on the curve, only allow adding
578          * keyframes if these will be visible after doing so...
579          */
580         if (fcurve_is_keyframable(fcu)) {
581                 /* get frame and value from props */
582                 frame = RNA_float_get(op->ptr, "frame");
583                 val = RNA_float_get(op->ptr, "value");
584                 
585                 /* apply inverse NLA-mapping to frame to get correct time in un-scaled action */
586                 adt = ANIM_nla_mapping_get(&ac, ale);
587                 frame = BKE_nla_tweakedit_remap(adt, frame, NLATIME_CONVERT_UNMAP);
588                 
589                 /* apply inverse unit-mapping to value to get correct value for F-Curves */
590                 val *= ANIM_unit_mapping_get_factor(ac.scene, ale->id, fcu, 1);
591                 
592                 /* insert keyframe on the specified frame + value */
593                 insert_vert_fcurve(fcu, frame, val, 0);
594         }
595         else {
596                 /* warn about why this can't happen */
597                 if (fcu->fpt)
598                         BKE_report(op->reports, RPT_ERROR, "Keyframes cannot be added to sampled F-Curves");
599                 else if (fcu->flag & FCURVE_PROTECTED)
600                         BKE_report(op->reports, RPT_ERROR, "Active F-Curve is not editable");
601                 else
602                         BKE_report(op->reports, RPT_ERROR, "Remove F-Modifiers from F-Curve to add keyframes");
603         }
604         
605         /* free temp data */
606         MEM_freeN(ale);
607         
608         /* set notifier that keyframes have changed */
609         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
610         
611         /* done */
612         return OPERATOR_FINISHED;
613 }
614
615 static int graphkeys_click_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event)
616 {
617         bAnimContext ac;
618         ARegion *ar;
619         View2D *v2d;
620         int mval[2];
621         float x, y;
622         
623         /* get animation context */
624         if (ANIM_animdata_get_context(C, &ac) == 0)
625                 return OPERATOR_CANCELLED;
626         
627         /* store mouse coordinates in View2D space, into the operator's properties */
628         ar = ac.ar;
629         v2d = &ar->v2d;
630         
631         mval[0] = (event->x - ar->winrct.xmin);
632         mval[1] = (event->y - ar->winrct.ymin);
633         
634         UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
635         
636         RNA_float_set(op->ptr, "frame", x);
637         RNA_float_set(op->ptr, "value", y);
638         
639         /* run exec now */
640         return graphkeys_click_insert_exec(C, op);
641 }
642
643 void GRAPH_OT_click_insert(wmOperatorType *ot)
644 {
645         /* identifiers */
646         ot->name = "Click-Insert Keyframes";
647         ot->idname = "GRAPH_OT_click_insert";
648         ot->description = "Insert new keyframe at the cursor position for the active F-Curve";
649         
650         /* api callbacks */
651         ot->invoke = graphkeys_click_insert_invoke;
652         ot->exec = graphkeys_click_insert_exec;
653         ot->poll = graphop_active_fcurve_poll;
654         
655         /* flags */
656         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
657         
658         /* properties */
659         RNA_def_float(ot->srna, "frame", 1.0f, -FLT_MAX, FLT_MAX, "Frame Number", "Frame to insert keyframe on", 0, 100);
660         RNA_def_float(ot->srna, "value", 1.0f, -FLT_MAX, FLT_MAX, "Value", "Value for keyframe on", 0, 100);
661 }
662
663 /* ******************** Copy/Paste Keyframes Operator ************************* */
664 /* NOTE: the backend code for this is shared with the dopesheet editor */
665
666 static short copy_graph_keys(bAnimContext *ac)
667 {       
668         ListBase anim_data = {NULL, NULL};
669         int filter, ok = 0;
670         
671         /* clear buffer first */
672         free_anim_copybuf();
673         
674         /* filter data */
675         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
676         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
677         
678         /* copy keyframes */
679         ok = copy_animedit_keys(ac, &anim_data);
680         
681         /* clean up */
682         BLI_freelistN(&anim_data);
683
684         return ok;
685 }
686
687 static short paste_graph_keys(bAnimContext *ac,
688                               const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode)
689 {       
690         ListBase anim_data = {NULL, NULL};
691         int filter, ok = 0;
692         
693         /* filter data 
694          * - First time we try to filter more strictly, allowing only selected channels 
695          *   to allow copying animation between channels
696          * - Second time, we loosen things up if nothing was found the first time, allowing
697          *   users to just paste keyframes back into the original curve again [#31670]
698          */
699         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
700         
701         if (ANIM_animdata_filter(ac, &anim_data, filter | ANIMFILTER_SEL, ac->data, ac->datatype) == 0)
702                 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
703         
704         /* paste keyframes */
705         ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode);
706         
707         /* clean up */
708         BLI_freelistN(&anim_data);
709
710         return ok;
711 }
712
713 /* ------------------- */
714
715 static int graphkeys_copy_exec(bContext *C, wmOperator *op)
716 {
717         bAnimContext ac;
718         
719         /* get editor data */
720         if (ANIM_animdata_get_context(C, &ac) == 0)
721                 return OPERATOR_CANCELLED;
722         
723         /* copy keyframes */
724         if (copy_graph_keys(&ac)) {
725                 BKE_report(op->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer");
726                 return OPERATOR_CANCELLED;
727         }
728         
729         /* just return - no operator needed here (no changes) */
730         return OPERATOR_FINISHED;
731 }
732  
733 void GRAPH_OT_copy(wmOperatorType *ot)
734 {
735         /* identifiers */
736         ot->name = "Copy Keyframes";
737         ot->idname = "GRAPH_OT_copy";
738         ot->description = "Copy selected keyframes to the copy/paste buffer";
739         
740         /* api callbacks */
741         ot->exec = graphkeys_copy_exec;
742         ot->poll = graphop_editable_keyframes_poll;
743         
744         /* flags */
745         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
746 }
747
748
749
750 static int graphkeys_paste_exec(bContext *C, wmOperator *op)
751 {
752         bAnimContext ac;
753
754         const eKeyPasteOffset offset_mode = RNA_enum_get(op->ptr, "offset");
755         const eKeyMergeMode merge_mode = RNA_enum_get(op->ptr, "merge");
756         
757         /* get editor data */
758         if (ANIM_animdata_get_context(C, &ac) == 0)
759                 return OPERATOR_CANCELLED;
760         
761         /* ac.reports by default will be the global reports list, which won't show warnings */
762         ac.reports = op->reports;
763
764         /* paste keyframes - non-zero return means an error occurred while trying to paste */
765         if (paste_graph_keys(&ac, offset_mode, merge_mode)) {
766                 return OPERATOR_CANCELLED;
767         }
768         
769         /* validate keyframes after editing */
770         ANIM_editkeyframes_refresh(&ac);
771         
772         /* set notifier that keyframes have changed */
773         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
774         
775         return OPERATOR_FINISHED;
776 }
777  
778 void GRAPH_OT_paste(wmOperatorType *ot)
779 {
780         /* identifiers */
781         ot->name = "Paste Keyframes";
782         ot->idname = "GRAPH_OT_paste";
783         ot->description = "Paste keyframes from copy/paste buffer for the selected channels, starting on the current frame";
784         
785         /* api callbacks */
786 //      ot->invoke = WM_operator_props_popup; // better wait for graph redo panel
787         ot->exec = graphkeys_paste_exec;
788         ot->poll = graphop_editable_keyframes_poll;
789         
790         /* flags */
791         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
792         
793         /* props */
794         RNA_def_enum(ot->srna, "offset", keyframe_paste_offset_items, KEYFRAME_PASTE_OFFSET_CFRA_START, "Offset", "Paste time offset of keys");
795         RNA_def_enum(ot->srna, "merge", keyframe_paste_merge_items, KEYFRAME_PASTE_MERGE_MIX, "Type", "Method of merging pasted keys and existing");
796 }
797
798 /* ******************** Duplicate Keyframes Operator ************************* */
799
800 static void duplicate_graph_keys(bAnimContext *ac)
801 {
802         ListBase anim_data = {NULL, NULL};
803         bAnimListElem *ale;
804         int filter;
805         
806         /* filter data */
807         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
808         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
809         
810         /* loop through filtered data and delete selected keys */
811         for (ale = anim_data.first; ale; ale = ale->next) {
812                 duplicate_fcurve_keys((FCurve *)ale->key_data);
813         }
814         
815         /* free filtered list */
816         BLI_freelistN(&anim_data);
817 }
818
819 /* ------------------- */
820
821 static int graphkeys_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
822 {
823         bAnimContext ac;
824         
825         /* get editor data */
826         if (ANIM_animdata_get_context(C, &ac) == 0)
827                 return OPERATOR_CANCELLED;
828                 
829         /* duplicate keyframes */
830         duplicate_graph_keys(&ac);
831         
832         /* validate keyframes after editing */
833         ANIM_editkeyframes_refresh(&ac);
834         
835         /* set notifier that keyframes have changed */
836         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
837         
838         return OPERATOR_FINISHED;
839 }
840
841 static int graphkeys_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
842 {
843         graphkeys_duplicate_exec(C, op);
844
845         return OPERATOR_FINISHED;
846 }
847  
848 void GRAPH_OT_duplicate(wmOperatorType *ot)
849 {
850         /* identifiers */
851         ot->name = "Duplicate Keyframes";
852         ot->idname = "GRAPH_OT_duplicate";
853         ot->description = "Make a copy of all selected keyframes";
854         
855         /* api callbacks */
856         ot->invoke = graphkeys_duplicate_invoke;
857         ot->exec = graphkeys_duplicate_exec;
858         ot->poll = graphop_editable_keyframes_poll;
859         
860         /* flags */
861         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
862         
863         /* to give to transform */
864         RNA_def_enum(ot->srna, "mode", transform_mode_types, TFM_TRANSLATION, "Mode", "");
865 }
866
867 /* ******************** Delete Keyframes Operator ************************* */
868
869 static void delete_graph_keys(bAnimContext *ac)
870 {
871         ListBase anim_data = {NULL, NULL};
872         bAnimListElem *ale;
873         int filter;
874         
875         /* filter data */
876         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
877         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
878         
879         /* loop through filtered data and delete selected keys */
880         for (ale = anim_data.first; ale; ale = ale->next) {
881                 FCurve *fcu = (FCurve *)ale->key_data;
882                 AnimData *adt = ale->adt;
883                 
884                 /* delete selected keyframes only */
885                 delete_fcurve_keys(fcu); 
886                 
887                 /* Only delete curve too if it won't be doing anything anymore */
888                 if ((fcu->totvert == 0) &&
889                     (list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE) == 0) &&
890                     (fcu->driver == NULL))
891                 {
892                         ANIM_fcurve_delete_from_animdata(ac, adt, fcu);
893                 }
894         }
895         
896         /* free filtered list */
897         BLI_freelistN(&anim_data);
898 }
899
900 /* ------------------- */
901
902 static int graphkeys_delete_exec(bContext *C, wmOperator *UNUSED(op))
903 {
904         bAnimContext ac;
905         
906         /* get editor data */
907         if (ANIM_animdata_get_context(C, &ac) == 0)
908                 return OPERATOR_CANCELLED;
909                 
910         /* delete keyframes */
911         delete_graph_keys(&ac);
912         
913         /* validate keyframes after editing */
914         ANIM_editkeyframes_refresh(&ac);
915         
916         /* set notifier that keyframes have changed */
917         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
918         
919         return OPERATOR_FINISHED;
920 }
921  
922 void GRAPH_OT_delete(wmOperatorType *ot)
923 {
924         /* identifiers */
925         ot->name = "Delete Keyframes";
926         ot->idname = "GRAPH_OT_delete";
927         ot->description = "Remove all selected keyframes";
928         
929         /* api callbacks */
930         ot->invoke = WM_operator_confirm;
931         ot->exec = graphkeys_delete_exec;
932         ot->poll = graphop_editable_keyframes_poll;
933         
934         /* flags */
935         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
936 }
937
938 /* ******************** Clean Keyframes Operator ************************* */
939
940 static void clean_graph_keys(bAnimContext *ac, float thresh)
941 {       
942         ListBase anim_data = {NULL, NULL};
943         bAnimListElem *ale;
944         int filter;
945         
946         /* filter data */
947         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_SEL | ANIMFILTER_NODUPLIS);
948         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
949         
950         /* loop through filtered data and clean curves */
951         for (ale = anim_data.first; ale; ale = ale->next)
952                 clean_fcurve((FCurve *)ale->key_data, thresh);
953         
954         /* free temp data */
955         BLI_freelistN(&anim_data);
956 }
957
958 /* ------------------- */
959
960 static int graphkeys_clean_exec(bContext *C, wmOperator *op)
961 {
962         bAnimContext ac;
963         float thresh;
964         
965         /* get editor data */
966         if (ANIM_animdata_get_context(C, &ac) == 0)
967                 return OPERATOR_CANCELLED;
968                 
969         /* get cleaning threshold */
970         thresh = RNA_float_get(op->ptr, "threshold");
971         
972         /* clean keyframes */
973         clean_graph_keys(&ac, thresh);
974         
975         /* validate keyframes after editing */
976         ANIM_editkeyframes_refresh(&ac);
977         
978         /* set notifier that keyframes have changed */
979         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
980         
981         return OPERATOR_FINISHED;
982 }
983  
984 void GRAPH_OT_clean(wmOperatorType *ot)
985 {
986         /* identifiers */
987         ot->name = "Clean Keyframes";
988         ot->idname = "GRAPH_OT_clean";
989         ot->description = "Simplify F-Curves by removing closely spaced keyframes";
990         
991         /* api callbacks */
992         //ot->invoke =  // XXX we need that number popup for this! 
993         ot->exec = graphkeys_clean_exec;
994         ot->poll = graphop_editable_keyframes_poll;
995         
996         /* flags */
997         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
998         
999         /* properties */
1000         ot->prop = RNA_def_float(ot->srna, "threshold", 0.001f, 0.0f, FLT_MAX, "Threshold", "", 0.0f, 1000.0f);
1001 }
1002
1003 /* ******************** Bake F-Curve Operator *********************** */
1004 /* This operator bakes the data of the selected F-Curves to F-Points */
1005
1006 /* Bake each F-Curve into a set of samples */
1007 static void bake_graph_curves(bAnimContext *ac, int start, int end)
1008 {       
1009         ListBase anim_data = {NULL, NULL};
1010         bAnimListElem *ale;
1011         int filter;
1012         
1013         /* filter data */
1014         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1015         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1016         
1017         /* loop through filtered data and add keys between selected keyframes on every frame  */
1018         for (ale = anim_data.first; ale; ale = ale->next) {
1019                 FCurve *fcu = (FCurve *)ale->key_data;
1020                 ChannelDriver *driver = fcu->driver;
1021                 
1022                 /* disable driver so that it don't muck up the sampling process */
1023                 fcu->driver = NULL;
1024                 
1025                 /* create samples */
1026                 fcurve_store_samples(fcu, NULL, start, end, fcurve_samplingcb_evalcurve);
1027                 
1028                 /* restore driver */
1029                 fcu->driver = driver;
1030         }
1031         
1032         /* admin and redraws */
1033         BLI_freelistN(&anim_data);
1034 }
1035
1036 /* ------------------- */
1037
1038 static int graphkeys_bake_exec(bContext *C, wmOperator *UNUSED(op))
1039 {
1040         bAnimContext ac;
1041         Scene *scene = NULL;
1042         int start, end;
1043         
1044         /* get editor data */
1045         if (ANIM_animdata_get_context(C, &ac) == 0)
1046                 return OPERATOR_CANCELLED;
1047                 
1048         /* for now, init start/end from preview-range extents */
1049         // TODO: add properties for this 
1050         scene = ac.scene;
1051         start = PSFRA;
1052         end = PEFRA;
1053         
1054         /* bake keyframes */
1055         bake_graph_curves(&ac, start, end);
1056         
1057         /* validate keyframes after editing */
1058         ANIM_editkeyframes_refresh(&ac);
1059         
1060         /* set notifier that keyframes have changed */
1061         // NOTE: some distinction between order/number of keyframes and type should be made?
1062         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1063         
1064         return OPERATOR_FINISHED;
1065 }
1066  
1067 void GRAPH_OT_bake(wmOperatorType *ot)
1068 {
1069         /* identifiers */
1070         ot->name = "Bake Curve";
1071         ot->idname = "GRAPH_OT_bake";
1072         ot->description = "Bake selected F-Curves to a set of sampled points defining a similar curve";
1073         
1074         /* api callbacks */
1075         ot->invoke = WM_operator_confirm; // FIXME...
1076         ot->exec = graphkeys_bake_exec;
1077         ot->poll = graphop_selected_fcurve_poll; 
1078         
1079         /* flags */
1080         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1081         
1082         // todo: add props for start/end frames
1083 }
1084
1085 #ifdef WITH_AUDASPACE
1086
1087 /* ******************** Sound Bake F-Curve Operator *********************** */
1088 /* This operator bakes the given sound to the selected F-Curves */
1089
1090 /* ------------------- */
1091
1092 /* Custom data storage passed to the F-Sample-ing function,
1093  * which provides the necessary info for baking the sound
1094  */
1095 typedef struct tSoundBakeInfo {
1096         float *samples;
1097         int length;
1098         int cfra;
1099 } tSoundBakeInfo;
1100
1101 /* ------------------- */
1102
1103 /* Sampling callback used to determine the value from the sound to
1104  * save in the F-Curve at the specified frame
1105  */
1106 static float fcurve_samplingcb_sound(FCurve *UNUSED(fcu), void *data, float evaltime)
1107 {
1108         tSoundBakeInfo *sbi = (tSoundBakeInfo *)data;
1109
1110         int position = evaltime - sbi->cfra;
1111         if ((position < 0) || (position >= sbi->length))
1112                 return 0.0f;
1113
1114         return sbi->samples[position];
1115 }
1116
1117 /* ------------------- */
1118
1119 static int graphkeys_sound_bake_exec(bContext *C, wmOperator *op)
1120 {
1121         bAnimContext ac;
1122         ListBase anim_data = {NULL, NULL};
1123         bAnimListElem *ale;
1124         int filter;
1125
1126         tSoundBakeInfo sbi;
1127         Scene *scene = NULL;
1128         int start, end;
1129
1130         char path[FILE_MAX];
1131
1132         /* get editor data */
1133         if (ANIM_animdata_get_context(C, &ac) == 0)
1134                 return OPERATOR_CANCELLED;
1135
1136         RNA_string_get(op->ptr, "filepath", path);
1137
1138         scene = ac.scene;    /* current scene */
1139
1140         /* store necessary data for the baking steps */
1141         sbi.samples = AUD_readSoundBuffer(path,
1142                                           RNA_float_get(op->ptr, "low"),
1143                                           RNA_float_get(op->ptr, "high"),
1144                                           RNA_float_get(op->ptr, "attack"),
1145                                           RNA_float_get(op->ptr, "release"),
1146                                           RNA_float_get(op->ptr, "threshold"),
1147                                           RNA_boolean_get(op->ptr, "use_accumulate"),
1148                                           RNA_boolean_get(op->ptr, "use_additive"),
1149                                           RNA_boolean_get(op->ptr, "use_square"),
1150                                           RNA_float_get(op->ptr, "sthreshold"),
1151                                           FPS, &sbi.length);
1152
1153         if (sbi.samples == NULL) {
1154                 BKE_report(op->reports, RPT_ERROR, "Unsupported audio format");
1155                 return OPERATOR_CANCELLED;
1156         }
1157
1158         /* determine extents of the baking */
1159         sbi.cfra = start = CFRA;
1160         end = CFRA + sbi.length - 1;
1161
1162         /* filter anim channels */
1163         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1164         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1165
1166         /* loop through all selected F-Curves, replacing its data with the sound samples */
1167         for (ale = anim_data.first; ale; ale = ale->next) {
1168                 FCurve *fcu = (FCurve *)ale->key_data;
1169                 
1170                 /* sample the sound */
1171                 fcurve_store_samples(fcu, &sbi, start, end, fcurve_samplingcb_sound);
1172         }
1173
1174         /* free sample data */
1175         free(sbi.samples);
1176
1177         /* admin and redraws */
1178         BLI_freelistN(&anim_data);
1179
1180         /* validate keyframes after editing */
1181         ANIM_editkeyframes_refresh(&ac);
1182
1183         /* set notifier that 'keyframes' have changed */
1184         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1185
1186         return OPERATOR_FINISHED;
1187 }
1188
1189 #else //WITH_AUDASPACE
1190
1191 static int graphkeys_sound_bake_exec(bContext *UNUSED(C), wmOperator *op)
1192 {
1193         BKE_report(op->reports, RPT_ERROR, "Compiled without sound support");
1194
1195         return OPERATOR_CANCELLED;
1196 }
1197
1198 #endif //WITH_AUDASPACE
1199
1200 static int graphkeys_sound_bake_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1201 {
1202         bAnimContext ac;
1203
1204         /* verify editor data */
1205         if (ANIM_animdata_get_context(C, &ac) == 0)
1206                 return OPERATOR_CANCELLED;
1207
1208         return WM_operator_filesel(C, op, event);
1209 }
1210
1211 void GRAPH_OT_sound_bake(wmOperatorType *ot)
1212 {
1213         /* identifiers */
1214         ot->name = "Bake Sound to F-Curves";
1215         ot->idname = "GRAPH_OT_sound_bake";
1216         ot->description = "Bakes a sound wave to selected F-Curves";
1217
1218         /* api callbacks */
1219         ot->invoke = graphkeys_sound_bake_invoke;
1220         ot->exec = graphkeys_sound_bake_exec;
1221         ot->poll = graphop_selected_fcurve_poll;
1222
1223         /* flags */
1224         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1225
1226         /* properties */
1227         WM_operator_properties_filesel(ot, FOLDERFILE | SOUNDFILE | MOVIEFILE, FILE_SPECIAL, FILE_OPENFILE,
1228                                        WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY);
1229         RNA_def_float(ot->srna, "low", 0.0f, 0.0, 100000.0, "Lowest frequency",
1230                       "Cutoff frequency of a high-pass filter that is applied to the audio data", 0.1, 1000.00);
1231         RNA_def_float(ot->srna, "high", 100000.0, 0.0, 100000.0, "Highest frequency",
1232                       "Cutoff frequency of a low-pass filter that is applied to the audio data", 0.1, 1000.00);
1233         RNA_def_float(ot->srna, "attack", 0.005, 0.0, 2.0, "Attack time",
1234                       "Value for the hull curve calculation that tells how fast the hull curve can rise "
1235                       "(the lower the value the steeper it can rise)", 0.01, 0.1);
1236         RNA_def_float(ot->srna, "release", 0.2, 0.0, 5.0, "Release time",
1237                       "Value for the hull curve calculation that tells how fast the hull curve can fall "
1238                       "(the lower the value the steeper it can fall)", 0.01, 0.2);
1239         RNA_def_float(ot->srna, "threshold", 0.0, 0.0, 1.0, "Threshold",
1240                       "Minimum amplitude value needed to influence the hull curve", 0.01, 0.1);
1241         RNA_def_boolean(ot->srna, "use_accumulate", 0, "Accumulate",
1242                         "Only the positive differences of the hull curve amplitudes are summarized to produce the output");
1243         RNA_def_boolean(ot->srna, "use_additive", 0, "Additive",
1244                         "The amplitudes of the hull curve are summarized (or, when Accumulate is enabled, "
1245                         "both positive and negative differences are accumulated)");
1246         RNA_def_boolean(ot->srna, "use_square", 0, "Square",
1247                         "The output is a square curve (negative values always result in -1, and positive ones in 1)");
1248         RNA_def_float(ot->srna, "sthreshold", 0.1, 0.0, 1.0, "Square Threshold",
1249                       "Square only: all values with an absolute amplitude lower than that result in 0", 0.01, 0.1);
1250 }
1251
1252 /* ******************** Sample Keyframes Operator *********************** */
1253 /* This operator 'bakes' the values of the curve into new keyframes between pairs
1254  * of selected keyframes. It is useful for creating keyframes for tweaking overlap.
1255  */
1256
1257 /* Evaluates the curves between each selected keyframe on each frame, and keys the value  */
1258 static void sample_graph_keys(bAnimContext *ac)
1259 {       
1260         ListBase anim_data = {NULL, NULL};
1261         bAnimListElem *ale;
1262         int filter;
1263         
1264         /* filter data */
1265         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1266         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1267         
1268         /* loop through filtered data and add keys between selected keyframes on every frame  */
1269         for (ale = anim_data.first; ale; ale = ale->next)
1270                 sample_fcurve((FCurve *)ale->key_data);
1271         
1272         /* admin and redraws */
1273         BLI_freelistN(&anim_data);
1274 }
1275
1276 /* ------------------- */
1277
1278 static int graphkeys_sample_exec(bContext *C, wmOperator *UNUSED(op))
1279 {
1280         bAnimContext ac;
1281         
1282         /* get editor data */
1283         if (ANIM_animdata_get_context(C, &ac) == 0)
1284                 return OPERATOR_CANCELLED;
1285         
1286         /* sample keyframes */
1287         sample_graph_keys(&ac);
1288         
1289         /* validate keyframes after editing */
1290         ANIM_editkeyframes_refresh(&ac);
1291         
1292         /* set notifier that keyframes have changed */
1293         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1294         
1295         return OPERATOR_FINISHED;
1296 }
1297  
1298 void GRAPH_OT_sample(wmOperatorType *ot)
1299 {
1300         /* identifiers */
1301         ot->name = "Sample Keyframes";
1302         ot->idname = "GRAPH_OT_sample";
1303         ot->description = "Add keyframes on every frame between the selected keyframes";
1304         
1305         /* api callbacks */
1306         ot->exec = graphkeys_sample_exec;
1307         ot->poll = graphop_editable_keyframes_poll;
1308         
1309         /* flags */
1310         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1311 }
1312
1313
1314 /* ************************************************************************** */
1315 /* SETTINGS STUFF */
1316
1317 /* ******************** Set Extrapolation-Type Operator *********************** */
1318
1319 /* defines for make/clear cyclic extrapolation tools */
1320 #define MAKE_CYCLIC_EXPO    -1
1321 #define CLEAR_CYCLIC_EXPO   -2
1322
1323 /* defines for set extrapolation-type for selected keyframes tool */
1324 static EnumPropertyItem prop_graphkeys_expo_types[] = {
1325         {FCURVE_EXTRAPOLATE_CONSTANT, "CONSTANT", 0, "Constant Extrapolation", "Values on endpoint keyframes are held"},
1326         {FCURVE_EXTRAPOLATE_LINEAR, "LINEAR", 0, "Linear Extrapolation", "Straight-line slope of end segments are extended past the endpoint keyframes"},
1327         
1328         {MAKE_CYCLIC_EXPO, "MAKE_CYCLIC", 0, "Make Cyclic (F-Modifier)", "Add Cycles F-Modifier if one doesn't exist already"},
1329         {CLEAR_CYCLIC_EXPO, "CLEAR_CYCLIC", 0, "Clear Cyclic (F-Modifier)", "Remove Cycles F-Modifier if not needed anymore"},
1330         {0, NULL, 0, NULL, NULL}
1331 };
1332
1333 /* this function is responsible for setting extrapolation mode for keyframes */
1334 static void setexpo_graph_keys(bAnimContext *ac, short mode) 
1335 {
1336         ListBase anim_data = {NULL, NULL};
1337         bAnimListElem *ale;
1338         int filter;
1339         
1340         /* filter data */
1341         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1342         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1343         
1344         /* loop through setting mode per F-Curve */
1345         for (ale = anim_data.first; ale; ale = ale->next) {
1346                 FCurve *fcu = (FCurve *)ale->data;
1347                 
1348                 if (mode >= 0) {
1349                         /* just set mode setting */
1350                         fcu->extend = mode;
1351                 }
1352                 else {
1353                         /* shortcuts for managing Cycles F-Modifiers to make it easier to toggle cyclic animation 
1354                          * without having to go through FModifier UI in Graph Editor to do so
1355                          */
1356                         if (mode == MAKE_CYCLIC_EXPO) {
1357                                 /* only add if one doesn't exist */
1358                                 if (list_has_suitable_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES, -1) == 0) {
1359                                         // TODO: add some more preset versions which set different extrapolation options?
1360                                         add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES);
1361                                 }
1362                         }
1363                         else if (mode == CLEAR_CYCLIC_EXPO) {
1364                                 /* remove all the modifiers fitting this description */
1365                                 FModifier *fcm, *fcn = NULL;
1366                                 
1367                                 for (fcm = fcu->modifiers.first; fcm; fcm = fcn) {
1368                                         fcn = fcm->next;
1369                                         
1370                                         if (fcm->type == FMODIFIER_TYPE_CYCLES)
1371                                                 remove_fmodifier(&fcu->modifiers, fcm);
1372                                 }
1373                         }
1374                 }
1375         }
1376         
1377         /* cleanup */
1378         BLI_freelistN(&anim_data);
1379 }
1380
1381 /* ------------------- */
1382
1383 static int graphkeys_expo_exec(bContext *C, wmOperator *op)
1384 {
1385         bAnimContext ac;
1386         short mode;
1387         
1388         /* get editor data */
1389         if (ANIM_animdata_get_context(C, &ac) == 0)
1390                 return OPERATOR_CANCELLED;
1391                 
1392         /* get handle setting mode */
1393         mode = RNA_enum_get(op->ptr, "type");
1394         
1395         /* set handle type */
1396         setexpo_graph_keys(&ac, mode);
1397         
1398         /* validate keyframes after editing */
1399         ANIM_editkeyframes_refresh(&ac);
1400         
1401         /* set notifier that keyframe properties have changed */
1402         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL);
1403         
1404         return OPERATOR_FINISHED;
1405 }
1406  
1407 void GRAPH_OT_extrapolation_type(wmOperatorType *ot)
1408 {
1409         /* identifiers */
1410         ot->name = "Set Keyframe Extrapolation";
1411         ot->idname = "GRAPH_OT_extrapolation_type";
1412         ot->description = "Set extrapolation mode for selected F-Curves";
1413         
1414         /* api callbacks */
1415         ot->invoke = WM_menu_invoke;
1416         ot->exec = graphkeys_expo_exec;
1417         ot->poll = graphop_editable_keyframes_poll;
1418         
1419         /* flags */
1420         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1421         
1422         /* id-props */
1423         ot->prop = RNA_def_enum(ot->srna, "type", prop_graphkeys_expo_types, 0, "Type", "");
1424 }
1425
1426 /* ******************** Set Interpolation-Type Operator *********************** */
1427
1428 /* this function is responsible for setting interpolation mode for keyframes */
1429 static void setipo_graph_keys(bAnimContext *ac, short mode) 
1430 {
1431         ListBase anim_data = {NULL, NULL};
1432         bAnimListElem *ale;
1433         int filter;
1434         KeyframeEditFunc set_cb = ANIM_editkeyframes_ipo(mode);
1435         
1436         /* filter data */
1437         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1438         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1439         
1440         /* loop through setting BezTriple interpolation
1441          * Note: we do not supply KeyframeEditData to the looper yet. Currently that's not necessary here...
1442          */
1443         for (ale = anim_data.first; ale; ale = ale->next)
1444                 ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve);
1445         
1446         /* cleanup */
1447         BLI_freelistN(&anim_data);
1448 }
1449
1450 /* ------------------- */
1451
1452 static int graphkeys_ipo_exec(bContext *C, wmOperator *op)
1453 {
1454         bAnimContext ac;
1455         short mode;
1456         
1457         /* get editor data */
1458         if (ANIM_animdata_get_context(C, &ac) == 0)
1459                 return OPERATOR_CANCELLED;
1460                 
1461         /* get handle setting mode */
1462         mode = RNA_enum_get(op->ptr, "type");
1463         
1464         /* set handle type */
1465         setipo_graph_keys(&ac, mode);
1466         
1467         /* validate keyframes after editing */
1468         ANIM_editkeyframes_refresh(&ac);
1469         
1470         /* set notifier that keyframe properties have changed */
1471         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL);
1472         
1473         return OPERATOR_FINISHED;
1474 }
1475  
1476 void GRAPH_OT_interpolation_type(wmOperatorType *ot)
1477 {
1478         /* identifiers */
1479         ot->name = "Set Keyframe Interpolation";
1480         ot->idname = "GRAPH_OT_interpolation_type";
1481         ot->description = "Set interpolation mode for the F-Curve segments starting from the selected keyframes";
1482         
1483         /* api callbacks */
1484         ot->invoke = WM_menu_invoke;
1485         ot->exec = graphkeys_ipo_exec;
1486         ot->poll = graphop_editable_keyframes_poll;
1487         
1488         /* flags */
1489         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1490         
1491         /* id-props */
1492         ot->prop = RNA_def_enum(ot->srna, "type", beztriple_interpolation_mode_items, 0, "Type", "");
1493 }
1494
1495 /* ******************** Set Handle-Type Operator *********************** */
1496
1497 /* this function is responsible for setting handle-type of selected keyframes */
1498 static void sethandles_graph_keys(bAnimContext *ac, short mode) 
1499 {
1500         ListBase anim_data = {NULL, NULL};
1501         bAnimListElem *ale;
1502         int filter;
1503         
1504         KeyframeEditFunc edit_cb = ANIM_editkeyframes_handles(mode);
1505         KeyframeEditFunc sel_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
1506         
1507         /* filter data */
1508         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1509         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1510         
1511         /* loop through setting flags for handles 
1512          * Note: we do not supply KeyframeEditData to the looper yet. Currently that's not necessary here...
1513          */
1514         for (ale = anim_data.first; ale; ale = ale->next) {
1515                 FCurve *fcu = (FCurve *)ale->key_data;
1516                 
1517                 /* any selected keyframes for editing? */
1518                 if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL)) {
1519                         /* change type of selected handles */
1520                         ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, calchandles_fcurve);
1521                 }
1522         }
1523         
1524         /* cleanup */
1525         BLI_freelistN(&anim_data);
1526 }
1527 /* ------------------- */
1528
1529 static int graphkeys_handletype_exec(bContext *C, wmOperator *op)
1530 {
1531         bAnimContext ac;
1532         short mode;
1533         
1534         /* get editor data */
1535         if (ANIM_animdata_get_context(C, &ac) == 0)
1536                 return OPERATOR_CANCELLED;
1537                 
1538         /* get handle setting mode */
1539         mode = RNA_enum_get(op->ptr, "type");
1540         
1541         /* set handle type */
1542         sethandles_graph_keys(&ac, mode);
1543         
1544         /* validate keyframes after editing */
1545         ANIM_editkeyframes_refresh(&ac);
1546         
1547         /* set notifier that keyframe properties have changed */
1548         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL);
1549         
1550         return OPERATOR_FINISHED;
1551 }
1552  
1553 void GRAPH_OT_handle_type(wmOperatorType *ot)
1554 {
1555         /* identifiers */
1556         ot->name = "Set Keyframe Handle Type";
1557         ot->idname = "GRAPH_OT_handle_type";
1558         ot->description = "Set type of handle for selected keyframes";
1559         
1560         /* api callbacks */
1561         ot->invoke = WM_menu_invoke;
1562         ot->exec = graphkeys_handletype_exec;
1563         ot->poll = graphop_editable_keyframes_poll;
1564         
1565         /* flags */
1566         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1567         
1568         /* id-props */
1569         ot->prop = RNA_def_enum(ot->srna, "type", keyframe_handle_type_items, 0, "Type", "");
1570 }
1571
1572 /* ************************************************************************** */
1573 /* TRANSFORM STUFF */
1574
1575 /* ***************** 'Euler Filter' Operator **************************** */
1576 /* Euler filter tools (as seen in Maya), are necessary for working with 'baked'
1577  * rotation curves (with Euler rotations). The main purpose of such tools is to
1578  * resolve any discontinuities that may arise in the curves due to the clamping
1579  * of values to -180 degrees to 180 degrees.
1580  */
1581
1582 /* set of three euler-rotation F-Curves */
1583 typedef struct tEulerFilter {
1584         struct tEulerFilter *next, *prev;
1585         
1586         ID *id;                         /* ID-block which owns the channels */
1587         FCurve *(fcurves[3]);           /* 3 Pointers to F-Curves */
1588         char *rna_path;                 /* Pointer to one of the RNA Path's used by one of the F-Curves */
1589 } tEulerFilter;
1590  
1591 static int graphkeys_euler_filter_exec(bContext *C, wmOperator *op)
1592 {
1593         bAnimContext ac;
1594         
1595         ListBase anim_data = {NULL, NULL};
1596         bAnimListElem *ale;
1597         int filter;
1598         
1599         ListBase eulers = {NULL, NULL};
1600         tEulerFilter *euf = NULL;
1601         int groups = 0, failed = 0;
1602         
1603         /* get editor data */
1604         if (ANIM_animdata_get_context(C, &ac) == 0)
1605                 return OPERATOR_CANCELLED;
1606                 
1607         /* The process is done in two passes:
1608          *   1) Sets of three related rotation curves are identified from the selected channels,
1609          *              and are stored as a single 'operation unit' for the next step
1610          *       2) Each set of three F-Curves is processed for each keyframe, with the values being
1611          *      processed as necessary
1612          */
1613          
1614         /* step 1: extract only the rotation f-curves */
1615         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1616         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1617         
1618         for (ale = anim_data.first; ale; ale = ale->next) {
1619                 FCurve *fcu = (FCurve *)ale->data;
1620                 
1621                 /* check if this is an appropriate F-Curve 
1622                  *      - only rotation curves
1623                  *      - for pchan curves, make sure we're only using the euler curves
1624                  */
1625                 if (strstr(fcu->rna_path, "rotation_euler") == NULL)
1626                         continue;
1627                 else if (ELEM3(fcu->array_index, 0, 1, 2) == 0) {
1628                         BKE_reportf(op->reports, RPT_WARNING,
1629                                     "Euler Rotation F-Curve has invalid index (ID='%s', Path='%s', Index=%d)",
1630                                     (ale->id) ? ale->id->name : TIP_("<No ID>"), fcu->rna_path, fcu->array_index);
1631                         continue;
1632                 }
1633                 
1634                 /* optimization: assume that xyz curves will always be stored consecutively,
1635                  * so if the paths or the ID's don't match up, then a curve needs to be added 
1636                  * to a new group
1637                  */
1638                 if ((euf) && (euf->id == ale->id) && (strcmp(euf->rna_path, fcu->rna_path) == 0)) {
1639                         /* this should be fine to add to the existing group then */
1640                         euf->fcurves[fcu->array_index] = fcu;
1641                 }
1642                 else {
1643                         /* just add to a new block */
1644                         euf = MEM_callocN(sizeof(tEulerFilter), "tEulerFilter");
1645                         BLI_addtail(&eulers, euf);
1646                         groups++;
1647                         
1648                         euf->id = ale->id;
1649                         euf->rna_path = fcu->rna_path; /* this should be safe, since we're only using it for a short time */
1650                         euf->fcurves[fcu->array_index] = fcu;
1651                 }
1652         }
1653         BLI_freelistN(&anim_data);
1654         
1655         if (groups == 0) {
1656                 BKE_report(op->reports, RPT_WARNING, "No Euler Rotation F-Curves to fix up");
1657                 return OPERATOR_CANCELLED;
1658         }
1659         
1660         /* step 2: go through each set of curves, processing the values at each keyframe 
1661          *      - it is assumed that there must be a full set of keyframes at each keyframe position
1662          */
1663         for (euf = eulers.first; euf; euf = euf->next) {
1664                 int f;
1665                 
1666                 /* sanity check: ensure that there are enough F-Curves to work on in this group */
1667                 /* TODO: also enforce assumption that there be a full set of keyframes at each position by ensuring that totvert counts are same? */
1668                 if (ELEM3(NULL, euf->fcurves[0], euf->fcurves[1], euf->fcurves[2])) {
1669                         /* report which components are missing */
1670                         BKE_reportf(op->reports, RPT_WARNING,
1671                                     "Missing %s%s%s component(s) of euler rotation for ID='%s' and RNA-Path='%s'",
1672                                     (euf->fcurves[0] == NULL) ? "X" : "",
1673                                     (euf->fcurves[1] == NULL) ? "Y" : "",
1674                                     (euf->fcurves[2] == NULL) ? "Z" : "",
1675                                     euf->id->name, euf->rna_path);
1676                                 
1677                         /* keep track of number of failed sets, and carry on to next group */
1678                         failed++;
1679                         continue;
1680                 }
1681
1682                 /* simple method: just treat any difference between keys of greater than 180 degrees as being a flip */
1683                 /* FIXME: there are more complicated methods that will be needed to fix more cases than just some */
1684                 for (f = 0; f < 3; f++) {
1685                         FCurve *fcu = euf->fcurves[f];
1686                         BezTriple *bezt, *prev;
1687                         unsigned int i;
1688                         
1689                         /* skip if not enough vets to do a decent analysis of... */
1690                         if (fcu->totvert <= 2)
1691                                 continue;
1692                         
1693                         /* prev follows bezt, bezt = "current" point to be fixed */
1694                         /* our method depends on determining a "difference" from the previous vert */
1695                         for (i = 1, prev = fcu->bezt, bezt = fcu->bezt + 1; i < fcu->totvert; i++, prev = bezt++) {
1696                                 const float sign = (prev->vec[1][1] > bezt->vec[1][1]) ? 1.0f : -1.0f;
1697                                 
1698                                 /* > 180 degree flip? */
1699                                 if ((sign * (prev->vec[1][1] - bezt->vec[1][1])) >= (float)M_PI) {
1700                                         /* 360 degrees to add/subtract frame value until difference is acceptably small that there's no more flip */
1701                                         const float fac = sign * 2.0f * (float)M_PI;
1702                                         
1703                                         while ((sign * (prev->vec[1][1] - bezt->vec[1][1])) >= (float)M_PI) {
1704                                                 bezt->vec[0][1] += fac;
1705                                                 bezt->vec[1][1] += fac;
1706                                                 bezt->vec[2][1] += fac;
1707                                         }
1708                                 }
1709                         }
1710                 }
1711         }
1712         BLI_freelistN(&eulers);
1713         
1714         /* updates + finishing warnings */
1715         if (failed == groups) {
1716                 BKE_report(op->reports, RPT_ERROR, 
1717                            "No Euler Rotations could be corrected, ensure each rotation has keys for all components, "
1718                            "and that F-Curves for these are in consecutive XYZ order and selected");
1719                 return OPERATOR_CANCELLED;
1720         }
1721         else {
1722                 if (failed) {
1723                         BKE_report(op->reports, RPT_ERROR,
1724                                    "Some Euler Rotations could not be corrected due to missing/unselected/out-of-order F-Curves, "
1725                                    "ensure each rotation has keys for all components, and that F-Curves for these are in "
1726                                    "consecutive XYZ order and selected");
1727                 }
1728                 
1729                 /* validate keyframes after editing */
1730                 ANIM_editkeyframes_refresh(&ac);
1731                 
1732                 /* set notifier that keyframes have changed */
1733                 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1734                 
1735                 /* done at last */
1736                 return OPERATOR_FINISHED;
1737         }
1738 }
1739  
1740 void GRAPH_OT_euler_filter(wmOperatorType *ot)
1741 {
1742         /* identifiers */
1743         ot->name = "Euler Discontinuity Filter";
1744         ot->idname = "GRAPH_OT_euler_filter";
1745         ot->description = "Fix large jumps and flips in the selected "
1746                           "Euler Rotation F-Curves arising from rotation "
1747                           "values being clipped when baking physics";
1748         
1749         /* api callbacks */
1750         ot->exec = graphkeys_euler_filter_exec;
1751         ot->poll = graphop_editable_keyframes_poll;
1752         
1753         /* flags */
1754         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1755 }
1756
1757 /* ***************** Jump to Selected Frames Operator *********************** */
1758
1759 static int graphkeys_framejump_poll(bContext *C)
1760 {
1761         /* prevent changes during render */
1762         if (G.is_rendering)
1763                 return 0;
1764
1765         return graphop_visible_keyframes_poll(C);
1766 }
1767
1768 /* snap current-frame indicator to 'average time' of selected keyframe */
1769 static int graphkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op))
1770 {
1771         bAnimContext ac;
1772         ListBase anim_data = {NULL, NULL};
1773         bAnimListElem *ale;
1774         int filter;
1775         KeyframeEditData ked;
1776         
1777         /* get editor data */
1778         if (ANIM_animdata_get_context(C, &ac) == 0)
1779                 return OPERATOR_CANCELLED;
1780         
1781         /* init edit data */
1782         memset(&ked, 0, sizeof(KeyframeEditData));
1783         
1784         /* loop over action data, averaging values */
1785         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS);
1786         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1787         
1788         for (ale = anim_data.first; ale; ale = ale->next) {
1789                 AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
1790                 KeyframeEditData current_ked;
1791                 float unit_scale = ANIM_unit_mapping_get_factor(ac.scene, ale->id, ale->key_data, ANIM_UNITCONV_ONLYKEYS);
1792
1793                 memset(&current_ked, 0, sizeof(current_ked));
1794
1795                 if (adt) {
1796                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); 
1797                         ANIM_fcurve_keyframes_loop(&current_ked, ale->key_data, NULL, bezt_calc_average, NULL);
1798                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); 
1799                 }
1800                 else
1801                         ANIM_fcurve_keyframes_loop(&current_ked, ale->key_data, NULL, bezt_calc_average, NULL);
1802
1803                 ked.f1 += current_ked.f1;
1804                 ked.i1 += current_ked.i1;
1805                 ked.f2 += current_ked.f2 / unit_scale;
1806                 ked.i2 += current_ked.i2;
1807         }
1808         
1809         BLI_freelistN(&anim_data);
1810         
1811         /* set the new current frame and cursor values, based on the average time and value */
1812         if (ked.i1) {
1813                 SpaceIpo *sipo = (SpaceIpo *)ac.sl;
1814                 Scene *scene = ac.scene;
1815                 
1816                 /* take the average values, rounding to the nearest int for the current frame */
1817                 CFRA = (int)floor((ked.f1 / ked.i1) + 0.5f);
1818                 SUBFRA = 0.f;
1819                 sipo->cursorVal = ked.f2 / (float)ked.i1;
1820         }
1821         
1822         /* set notifier that things have changed */
1823         WM_event_add_notifier(C, NC_SCENE | ND_FRAME, ac.scene);
1824         
1825         return OPERATOR_FINISHED;
1826 }
1827
1828 void GRAPH_OT_frame_jump(wmOperatorType *ot)
1829 {
1830         /* identifiers */
1831         ot->name = "Jump to Keyframes";
1832         ot->idname = "GRAPH_OT_frame_jump";
1833         ot->description = "Place the cursor on the midpoint of selected keyframes";
1834         
1835         /* api callbacks */
1836         ot->exec = graphkeys_framejump_exec;
1837         ot->poll = graphkeys_framejump_poll;
1838         
1839         /* flags */
1840         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1841 }
1842
1843 /* ******************** Snap Keyframes Operator *********************** */
1844
1845 /* defines for snap keyframes tool */
1846 static EnumPropertyItem prop_graphkeys_snap_types[] = {
1847         {GRAPHKEYS_SNAP_CFRA, "CFRA", 0, "Current Frame",
1848          "Snap selected keyframes to the current frame"},
1849         {GRAPHKEYS_SNAP_VALUE, "VALUE", 0, "Cursor Value",
1850          "Set values of selected keyframes to the cursor value (Y/Horizontal component)"},
1851         {GRAPHKEYS_SNAP_NEAREST_FRAME, "NEAREST_FRAME", 0, "Nearest Frame",
1852          "Snap selected keyframes to the nearest (whole) frame (use to fix accidental sub-frame offsets)"},
1853         {GRAPHKEYS_SNAP_NEAREST_SECOND, "NEAREST_SECOND", 0, "Nearest Second",
1854          "Snap selected keyframes to the nearest second"},
1855         {GRAPHKEYS_SNAP_NEAREST_MARKER, "NEAREST_MARKER", 0, "Nearest Marker",
1856          "Snap selected keyframes to the nearest marker"},
1857         {GRAPHKEYS_SNAP_HORIZONTAL, "HORIZONTAL", 0, "Flatten Handles",
1858          "Flatten handles for a smoother transition"},
1859         {0, NULL, 0, NULL, NULL}
1860 };
1861
1862 /* this function is responsible for snapping keyframes to frame-times */
1863 static void snap_graph_keys(bAnimContext *ac, short mode) 
1864 {
1865         ListBase anim_data = {NULL, NULL};
1866         bAnimListElem *ale;
1867         int filter;
1868         
1869         KeyframeEditData ked;
1870         KeyframeEditFunc edit_cb;
1871         float cursor_value = 0.0f;
1872         
1873         /* filter data */
1874         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1875         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1876         
1877         /* get beztriple editing callbacks */
1878         edit_cb = ANIM_editkeyframes_snap(mode);
1879         
1880         memset(&ked, 0, sizeof(KeyframeEditData)); 
1881         ked.scene = ac->scene;
1882         if (mode == GRAPHKEYS_SNAP_NEAREST_MARKER) {
1883                 ked.list.first = (ac->markers) ? ac->markers->first : NULL;
1884                 ked.list.last = (ac->markers) ? ac->markers->last : NULL;
1885         }
1886         else if (mode == GRAPHKEYS_SNAP_VALUE) {
1887                 SpaceIpo *sipo = (SpaceIpo *)ac->sl;
1888                 cursor_value = (sipo) ? sipo->cursorVal : 0.0f;
1889         }
1890         
1891         /* snap keyframes */
1892         for (ale = anim_data.first; ale; ale = ale->next) {
1893                 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1894                 float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, ale->key_data, 0);
1895
1896                 ked.f1 = cursor_value / unit_scale;
1897
1898                 if (adt) {
1899                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); 
1900                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
1901                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
1902                 }
1903                 else 
1904                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
1905         }
1906         
1907         BLI_freelistN(&anim_data);
1908 }
1909
1910 /* ------------------- */
1911
1912 static int graphkeys_snap_exec(bContext *C, wmOperator *op)
1913 {
1914         bAnimContext ac;
1915         short mode;
1916         
1917         /* get editor data */
1918         if (ANIM_animdata_get_context(C, &ac) == 0)
1919                 return OPERATOR_CANCELLED;
1920                 
1921         /* get snapping mode */
1922         mode = RNA_enum_get(op->ptr, "type");
1923         
1924         /* snap keyframes */
1925         snap_graph_keys(&ac, mode);
1926         
1927         /* validate keyframes after editing */
1928         ANIM_editkeyframes_refresh(&ac);
1929         
1930         /* set notifier that keyframes have changed */
1931         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1932         
1933         return OPERATOR_FINISHED;
1934 }
1935  
1936 void GRAPH_OT_snap(wmOperatorType *ot)
1937 {
1938         /* identifiers */
1939         ot->name = "Snap Keys";
1940         ot->idname = "GRAPH_OT_snap";
1941         ot->description = "Snap selected keyframes to the chosen times/values";
1942         
1943         /* api callbacks */
1944         ot->invoke = WM_menu_invoke;
1945         ot->exec = graphkeys_snap_exec;
1946         ot->poll = graphop_editable_keyframes_poll;
1947         
1948         /* flags */
1949         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1950         
1951         /* id-props */
1952         ot->prop = RNA_def_enum(ot->srna, "type", prop_graphkeys_snap_types, 0, "Type", "");
1953 }
1954
1955 /* ******************** Mirror Keyframes Operator *********************** */
1956
1957 /* defines for mirror keyframes tool */
1958 static EnumPropertyItem prop_graphkeys_mirror_types[] = {
1959         {GRAPHKEYS_MIRROR_CFRA, "CFRA", 0, "By Times over Current Frame",
1960          "Flip times of selected keyframes using the current frame as the mirror line"},
1961         {GRAPHKEYS_MIRROR_VALUE, "VALUE", 0, "By Values over Cursor Value",
1962          "Flip values of selected keyframes using the cursor value (Y/Horizontal component) as the mirror line"},
1963         {GRAPHKEYS_MIRROR_YAXIS, "YAXIS", 0, "By Times over Time=0",
1964          "Flip times of selected keyframes, effectively reversing the order they appear in"},
1965         {GRAPHKEYS_MIRROR_XAXIS, "XAXIS", 0, "By Values over Value=0",
1966          "Flip values of selected keyframes (i.e. negative values become positive, and vice versa)"},
1967         {GRAPHKEYS_MIRROR_MARKER, "MARKER", 0, "By Times over First Selected Marker",
1968          "Flip times of selected keyframes using the first selected marker as the reference point"},
1969         {0, NULL, 0, NULL, NULL}
1970 };
1971
1972 /* this function is responsible for mirroring keyframes */
1973 static void mirror_graph_keys(bAnimContext *ac, short mode) 
1974 {
1975         ListBase anim_data = {NULL, NULL};
1976         bAnimListElem *ale;
1977         int filter;
1978         
1979         KeyframeEditData ked;
1980         KeyframeEditFunc edit_cb;
1981         float cursor_value;
1982
1983         /* get beztriple editing callbacks */
1984         edit_cb = ANIM_editkeyframes_mirror(mode);
1985         
1986         memset(&ked, 0, sizeof(KeyframeEditData)); 
1987         ked.scene = ac->scene;
1988         
1989         /* for 'first selected marker' mode, need to find first selected marker first! */
1990         // XXX should this be made into a helper func in the API?
1991         if (mode == GRAPHKEYS_MIRROR_MARKER) {
1992                 TimeMarker *marker = NULL;
1993                 
1994                 /* find first selected marker */
1995                 marker = ED_markers_get_first_selected(ac->markers);
1996                 
1997                 /* store marker's time (if available) */
1998                 if (marker)
1999                         ked.f1 = (float)marker->frame;
2000                 else
2001                         return;
2002         }
2003         else if (mode == GRAPHKEYS_MIRROR_VALUE) {
2004                 SpaceIpo *sipo = (SpaceIpo *)ac->sl;
2005                 cursor_value = (sipo) ? sipo->cursorVal : 0.0f;
2006         }
2007         
2008         /* filter data */
2009         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
2010         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
2011         
2012         /* mirror keyframes */
2013         for (ale = anim_data.first; ale; ale = ale->next) {
2014                 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
2015                 float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, ale->key_data, ANIM_UNITCONV_ONLYKEYS);
2016
2017                 /* apply unit corrections */
2018                 ked.f1 = cursor_value * unit_scale;
2019
2020                 if (adt) {
2021                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); 
2022                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
2023                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
2024                 }
2025                 else 
2026                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
2027         }
2028         
2029         BLI_freelistN(&anim_data);
2030 }
2031
2032 /* ------------------- */
2033
2034 static int graphkeys_mirror_exec(bContext *C, wmOperator *op)
2035 {
2036         bAnimContext ac;
2037         short mode;
2038         
2039         /* get editor data */
2040         if (ANIM_animdata_get_context(C, &ac) == 0)
2041                 return OPERATOR_CANCELLED;
2042                 
2043         /* get mirroring mode */
2044         mode = RNA_enum_get(op->ptr, "type");
2045         
2046         /* mirror keyframes */
2047         mirror_graph_keys(&ac, mode);
2048         
2049         /* validate keyframes after editing */
2050         ANIM_editkeyframes_refresh(&ac);
2051         
2052         /* set notifier that keyframes have changed */
2053         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
2054         
2055         return OPERATOR_FINISHED;
2056 }
2057  
2058 void GRAPH_OT_mirror(wmOperatorType *ot)
2059 {
2060         /* identifiers */
2061         ot->name = "Mirror Keys";
2062         ot->idname = "GRAPH_OT_mirror";
2063         ot->description = "Flip selected keyframes over the selected mirror line";
2064         
2065         /* api callbacks */
2066         ot->invoke = WM_menu_invoke;
2067         ot->exec = graphkeys_mirror_exec;
2068         ot->poll = graphop_editable_keyframes_poll;
2069         
2070         /* flags */
2071         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2072         
2073         /* id-props */
2074         ot->prop = RNA_def_enum(ot->srna, "type", prop_graphkeys_mirror_types, 0, "Type", "");
2075 }
2076
2077 /* ******************** Smooth Keyframes Operator *********************** */
2078
2079 static int graphkeys_smooth_exec(bContext *C, wmOperator *UNUSED(op))
2080 {
2081         bAnimContext ac;
2082         ListBase anim_data = {NULL, NULL};
2083         bAnimListElem *ale;
2084         int filter;
2085         
2086         /* get editor data */
2087         if (ANIM_animdata_get_context(C, &ac) == 0)
2088                 return OPERATOR_CANCELLED;
2089         
2090         /* filter data */
2091         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
2092         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
2093         
2094         /* smooth keyframes */
2095         for (ale = anim_data.first; ale; ale = ale->next) {
2096                 /* For now, we can only smooth by flattening handles AND smoothing curve values.
2097                  * Perhaps the mode argument could be removed, as that functionality is offered through
2098                  * Snap->Flatten Handles anyway.
2099                  */
2100                 smooth_fcurve(ale->key_data);
2101         }
2102         BLI_freelistN(&anim_data);
2103         
2104         /* validate keyframes after editing */
2105         ANIM_editkeyframes_refresh(&ac);
2106         
2107         /* set notifier that keyframes have changed */
2108         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
2109         
2110         return OPERATOR_FINISHED;
2111 }
2112  
2113 void GRAPH_OT_smooth(wmOperatorType *ot)
2114 {
2115         /* identifiers */
2116         ot->name = "Smooth Keys";
2117         ot->idname = "GRAPH_OT_smooth";
2118         ot->description = "Apply weighted moving means to make selected F-Curves less bumpy";
2119         
2120         /* api callbacks */
2121         ot->exec = graphkeys_smooth_exec;
2122         ot->poll = graphop_editable_keyframes_poll;
2123         
2124         /* flags */
2125         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2126 }
2127
2128 /* ************************************************************************** */
2129 /* F-CURVE MODIFIERS */
2130
2131 /* ******************** Add F-Modifier Operator *********************** */
2132
2133 /* present a special customised popup menu for this, with some filtering */
2134 static int graph_fmodifier_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
2135 {
2136         wmOperatorType *ot = WM_operatortype_find("GRAPH_OT_fmodifier_add", 1);
2137         uiPopupMenu *pup;
2138         uiLayout *layout;
2139         int i;
2140         
2141         pup = uiPupMenuBegin(C, IFACE_("Add F-Curve Modifier"), ICON_NONE);
2142         layout = uiPupMenuLayout(pup);
2143         
2144         /* start from 1 to skip the 'Invalid' modifier type */
2145         for (i = 1; i < FMODIFIER_NUM_TYPES; i++) {
2146                 FModifierTypeInfo *fmi = get_fmodifier_typeinfo(i);
2147                 PointerRNA props_ptr;
2148                 
2149                 /* check if modifier is valid for this context */
2150                 if (fmi == NULL)
2151                         continue;
2152                 
2153                 /* create operator menu item with relevant properties filled in */
2154                 props_ptr = uiItemFullO_ptr(layout, ot, IFACE_(fmi->name), ICON_NONE,
2155                                             NULL, WM_OP_EXEC_REGION_WIN, UI_ITEM_O_RETURN_PROPS);
2156                 /* the only thing that gets set from the menu is the type of F-Modifier to add */
2157                 RNA_enum_set(&props_ptr, "type", i);
2158                 /* the following properties are just repeats of existing ones... */
2159                 RNA_boolean_set(&props_ptr, "only_active", RNA_boolean_get(op->ptr, "only_active"));
2160         }
2161         uiItemS(layout);
2162         
2163         uiPupMenuEnd(C, pup);
2164         
2165         return OPERATOR_CANCELLED;
2166 }
2167
2168 static int graph_fmodifier_add_exec(bContext *C, wmOperator *op)
2169 {
2170         bAnimContext ac;
2171         ListBase anim_data = {NULL, NULL};
2172         bAnimListElem *ale;
2173         int filter;
2174         short type;
2175         
2176         /* get editor data */
2177         if (ANIM_animdata_get_context(C, &ac) == 0)
2178                 return OPERATOR_CANCELLED;
2179         
2180         /* get type of modifier to add */
2181         type = RNA_enum_get(op->ptr, "type");
2182         
2183         /* filter data */
2184         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
2185         if (RNA_boolean_get(op->ptr, "only_active"))
2186                 filter |= ANIMFILTER_ACTIVE;  // FIXME: enforce in this case only a single channel to get handled?
2187         else
2188                 filter |= (ANIMFILTER_SEL | ANIMFILTER_CURVE_VISIBLE);
2189         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
2190         
2191         /* add f-modifier to each curve */
2192         for (ale = anim_data.first; ale; ale = ale->next) {
2193                 FCurve *fcu = (FCurve *)ale->data;
2194                 FModifier *fcm;
2195                 
2196                 /* add F-Modifier of specified type to active F-Curve, and make it the active one */
2197                 fcm = add_fmodifier(&fcu->modifiers, type);
2198                 if (fcm) {
2199                         set_active_fmodifier(&fcu->modifiers, fcm);
2200                 }
2201                 else {
2202                         BKE_report(op->reports, RPT_ERROR, "Modifier could not be added (see console for details)");
2203                         break;
2204                 }
2205         }
2206         BLI_freelistN(&anim_data);
2207         
2208         /* validate keyframes after editing */
2209         ANIM_editkeyframes_refresh(&ac);
2210         
2211         /* set notifier that things have changed */
2212         // FIXME: this really isn't the best description for it...
2213         WM_event_add_notifier(C, NC_ANIMATION, NULL);
2214         
2215         return OPERATOR_FINISHED;
2216 }
2217  
2218 void GRAPH_OT_fmodifier_add(wmOperatorType *ot)
2219 {
2220         /* identifiers */
2221         ot->name = "Add F-Curve Modifier";
2222         ot->idname = "GRAPH_OT_fmodifier_add";
2223         ot->description = "Add F-Modifiers to the selected F-Curves";
2224         
2225         /* api callbacks */
2226         ot->invoke = graph_fmodifier_add_invoke;
2227         ot->exec = graph_fmodifier_add_exec;
2228         ot->poll = graphop_selected_fcurve_poll; 
2229         
2230         /* flags */
2231         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2232         
2233         /* id-props */
2234         ot->prop = RNA_def_enum(ot->srna, "type", fmodifier_type_items, 0, "Type", "");
2235         RNA_def_boolean(ot->srna, "only_active", 1, "Only Active", "Only add F-Modifier to active F-Curve");
2236 }
2237
2238 /* ******************** Copy F-Modifiers Operator *********************** */
2239
2240 static int graph_fmodifier_copy_exec(bContext *C, wmOperator *op)
2241 {
2242         bAnimContext ac;
2243         bAnimListElem *ale;
2244         short ok = 0;
2245         
2246         /* get editor data */
2247         if (ANIM_animdata_get_context(C, &ac) == 0)
2248                 return OPERATOR_CANCELLED;
2249         
2250         /* clear buffer first */
2251         free_fmodifiers_copybuf();
2252         
2253         /* get the active F-Curve */
2254         ale = get_active_fcurve_channel(&ac);
2255         
2256         /* if this exists, call the copy F-Modifiers API function */
2257         if (ale && ale->data) {
2258                 FCurve *fcu = (FCurve *)ale->data;
2259
2260                 /* TODO: when 'active' vs 'all' boolean is added, change last param! */
2261                 ok = ANIM_fmodifiers_copy_to_buf(&fcu->modifiers, 0);
2262
2263                 /* free temp data now */
2264                 MEM_freeN(ale);
2265         }
2266         
2267         /* successful or not? */
2268         if (ok == 0) {
2269                 BKE_report(op->reports, RPT_ERROR, "No F-Modifiers available to be copied");
2270                 return OPERATOR_CANCELLED;
2271         }
2272         else
2273                 return OPERATOR_FINISHED;
2274 }
2275  
2276 void GRAPH_OT_fmodifier_copy(wmOperatorType *ot)
2277 {
2278         /* identifiers */
2279         ot->name = "Copy F-Modifiers";
2280         ot->idname = "GRAPH_OT_fmodifier_copy";
2281         ot->description = "Copy the F-Modifier(s) of the active F-Curve";
2282         
2283         /* api callbacks */
2284         ot->exec = graph_fmodifier_copy_exec;
2285         ot->poll = graphop_active_fcurve_poll; 
2286         
2287         /* flags */
2288         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2289         
2290         /* id-props */
2291         //ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All F-Modifiers", "Copy all the F-Modifiers, instead of just the active one");
2292 }
2293
2294 /* ******************** Paste F-Modifiers Operator *********************** */
2295
2296 static int graph_fmodifier_paste_exec(bContext *C, wmOperator *op)
2297 {
2298         bAnimContext ac;
2299         ListBase anim_data = {NULL, NULL};
2300         bAnimListElem *ale;
2301         int filter, ok = 0;
2302         
2303         /* get editor data */
2304         if (ANIM_animdata_get_context(C, &ac) == 0)
2305                 return OPERATOR_CANCELLED;
2306         
2307         /* filter data */
2308         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT);
2309         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
2310         
2311         /* paste modifiers */
2312         for (ale = anim_data.first; ale; ale = ale->next) {
2313                 FCurve *fcu = (FCurve *)ale->data;
2314
2315                 /* TODO: do we want to replace existing modifiers? add user pref for that! */
2316                 ok += ANIM_fmodifiers_paste_from_buf(&fcu->modifiers, 0);
2317         }
2318
2319         /* clean up */
2320         BLI_freelistN(&anim_data);
2321         
2322         /* successful or not? */
2323         if (ok) {
2324                 /* validate keyframes after editing */
2325                 ANIM_editkeyframes_refresh(&ac);
2326                 
2327                 /* set notifier that keyframes have changed */
2328                 WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
2329                 
2330                 return OPERATOR_FINISHED;
2331         }
2332         else {
2333                 BKE_report(op->reports, RPT_ERROR, "No F-Modifiers to paste");
2334                 return OPERATOR_CANCELLED;
2335         }
2336 }
2337  
2338 void GRAPH_OT_fmodifier_paste(wmOperatorType *ot)
2339 {
2340         /* identifiers */
2341         ot->name = "Paste F-Modifiers";
2342         ot->idname = "GRAPH_OT_fmodifier_paste";
2343         ot->description = "Add copied F-Modifiers to the selected F-Curves";
2344         
2345         /* api callbacks */
2346         ot->exec = graph_fmodifier_paste_exec;
2347         ot->poll = graphop_active_fcurve_poll;
2348         
2349         /* flags */
2350         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2351 }
2352
2353 /* ************************************************************************** */