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