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