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