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