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