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