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