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