eeb297b7f5774e9f0b38ca3b8bf1065b407c56e0
[blender.git] / source / blender / editors / space_action / action_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_action/action_edit.c
29  *  \ingroup spaction
30  */
31
32
33 #include <math.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <float.h>
37
38
39 #include "BLI_blenlib.h"
40 #include "BLI_math.h"
41 #include "BLI_utildefines.h"
42
43 #include "DNA_anim_types.h"
44 #include "DNA_gpencil_types.h"
45 #include "DNA_object_types.h"
46 #include "DNA_scene_types.h"
47 #include "DNA_mask_types.h"
48
49 #include "RNA_access.h"
50 #include "RNA_define.h"
51 #include "RNA_enum_types.h"
52
53 #include "BKE_action.h"
54 #include "BKE_fcurve.h"
55 #include "BKE_nla.h"
56 #include "BKE_context.h"
57 #include "BKE_report.h"
58
59 #include "UI_view2d.h"
60
61 #include "ED_anim_api.h"
62 #include "ED_gpencil.h"
63 #include "ED_keyframing.h"
64 #include "ED_keyframes_edit.h"
65 #include "ED_screen.h"
66 #include "ED_transform.h"
67 #include "ED_markers.h"
68 #include "ED_mask.h"
69
70 #include "WM_api.h"
71 #include "WM_types.h"
72
73 #include "UI_interface.h"
74
75 #include "action_intern.h"
76
77 /* ************************************************************************** */
78 /* ACTION MANAGEMENT */
79
80 /* ******************** New Action Operator *********************** */
81
82 static int act_new_exec(bContext *C, wmOperator *UNUSED(op))
83 {
84         PointerRNA ptr, idptr;
85         PropertyRNA *prop;
86
87         /* hook into UI */
88         uiIDContextProperty(C, &ptr, &prop);
89         
90         if (prop) {
91                 bAction *action = NULL, *oldact = NULL;
92                 PointerRNA oldptr;
93                 
94                 /* create action - the way to do this depends on whether we've got an
95                  * existing one there already, in which case we make a copy of it
96                  * (which is useful for "versioning" actions within the same file)
97                  */
98                 oldptr = RNA_property_pointer_get(&ptr, prop);
99                 oldact = (bAction *)oldptr.id.data;
100                 
101                 if (oldact && GS(oldact->id.name) == ID_AC) {
102                         /* make a copy of the existing action */
103                         action = BKE_action_copy(oldact);
104                 }
105                 else {
106                         /* just make a new (empty) action */
107                         action = add_empty_action("Action");
108                 }
109                 
110                 /* when creating new ID blocks, use is already 1 (fake user), 
111                  * but RNA pointer use also increases user, so this compensates it 
112                  */
113                 action->id.us--;
114                 
115                 RNA_id_pointer_create(&action->id, &idptr);
116                 RNA_property_pointer_set(&ptr, prop, idptr);
117                 RNA_property_update(C, &ptr, prop);
118         }
119         
120         /* set notifier that keyframes have changed */
121         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
122         
123         return OPERATOR_FINISHED;
124 }
125  
126 void ACTION_OT_new(wmOperatorType *ot)
127 {
128         /* identifiers */
129         ot->name = "New Action";
130         ot->idname = "ACTION_OT_new";
131         ot->description = "Create new action";
132         
133         /* api callbacks */
134         ot->exec = act_new_exec;
135         // NOTE: this is used in the NLA too...
136         //ot->poll = ED_operator_action_active;
137         
138         /* flags */
139         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
140 }
141
142 /* ************************************************************************** */
143 /* POSE MARKERS STUFF */
144
145 /* *************************** Localise Markers ***************************** */
146
147 /* ensure that there is:
148  *  1) an active action editor
149  *  2) that the mode will have an active action available
150  *  3) that the set of markers being shown are the scene markers, not the list we're merging
151  *      4) that there are some selected markers
152  */
153 static int act_markers_make_local_poll(bContext *C)
154 {
155         SpaceAction *sact = CTX_wm_space_action(C);
156         
157         /* 1) */
158         if (sact == NULL)
159                 return 0;
160         
161         /* 2) */
162         if (ELEM(sact->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY) == 0)
163                 return 0;
164         if (sact->action == NULL)
165                 return 0;
166                 
167         /* 3) */
168         if (sact->flag & SACTION_POSEMARKERS_SHOW)
169                 return 0;
170                 
171         /* 4) */
172         return ED_markers_get_first_selected(ED_context_get_markers(C)) != NULL;
173 }
174
175 static int act_markers_make_local_exec(bContext *C, wmOperator *UNUSED(op))
176 {       
177         ListBase *markers = ED_context_get_markers(C);
178         
179         SpaceAction *sact = CTX_wm_space_action(C);
180         bAction *act = (sact) ? sact->action : NULL;
181         
182         TimeMarker *marker, *markern = NULL;
183         
184         /* sanity checks */
185         if (ELEM(NULL, markers, act))
186                 return OPERATOR_CANCELLED;
187                 
188         /* migrate markers */
189         for (marker = markers->first; marker; marker = markern) {
190                 markern = marker->next;
191                 
192                 /* move if marker is selected */
193                 if (marker->flag & SELECT) {
194                         BLI_remlink(markers, marker);
195                         BLI_addtail(&act->markers, marker);
196                 }
197         }
198         
199         /* now enable the "show posemarkers only" setting, so that we can see that something did happen */
200         sact->flag |= SACTION_POSEMARKERS_SHOW;
201         
202         /* notifiers - both sets, as this change affects both */
203         WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
204         WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
205         
206         return OPERATOR_FINISHED;
207 }
208
209 void ACTION_OT_markers_make_local(wmOperatorType *ot)
210 {
211         /* identifiers */
212         ot->name = "Make Markers Local";
213         ot->idname = "ACTION_OT_markers_make_local";
214         ot->description = "Move selected scene markers to the active Action as local 'pose' markers";
215         
216         /* callbacks */
217         ot->exec = act_markers_make_local_exec;
218         ot->poll = act_markers_make_local_poll;
219         
220         /* flags */
221         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
222 }
223
224 /* ************************************************************************** */
225 /* KEYFRAME-RANGE STUFF */
226
227 /* *************************** Calculate Range ************************** */
228
229 /* Get the min/max keyframes*/
230 static void get_keyframe_extents(bAnimContext *ac, float *min, float *max, const short onlySel)
231 {
232         ListBase anim_data = {NULL, NULL};
233         bAnimListElem *ale;
234         int filter;
235         
236         /* get data to filter, from Action or Dopesheet */
237         // XXX: what is sel doing here?!
238         //      Commented it, was breaking things (eg. the "auto preview range" tool).
239         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_SEL *//*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
240         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
241         
242         /* set large values to try to override */
243         *min = 999999999.0f;
244         *max = -999999999.0f;
245         
246         /* check if any channels to set range with */
247         if (anim_data.first) {
248                 /* go through channels, finding max extents */
249                 for (ale = anim_data.first; ale; ale = ale->next) {
250                         AnimData *adt = ANIM_nla_mapping_get(ac, ale);
251                         if (ale->datatype == ALE_GPFRAME) {
252                                 bGPDlayer *gpl = ale->data;
253                                 bGPDframe *gpf;
254
255                                 /* find gp-frame which is less than or equal to cframe */
256                                 for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
257                                         *min = MIN2(*min, gpf->framenum);
258                                         *max = MAX2(*max, gpf->framenum);
259                                 }
260                         }
261                         else if (ale->datatype == ALE_MASKLAY) {
262                                 MaskLayer *masklay = ale->data;
263                                 MaskLayerShape *masklay_shape;
264
265                                 /* find mask layer which is less than or equal to cframe */
266                                 for (masklay_shape = masklay->splines_shapes.first;
267                                      masklay_shape;
268                                      masklay_shape = masklay_shape->next)
269                                 {
270                                         *min = MIN2(*min, masklay_shape->frame);
271                                         *max = MAX2(*max, masklay_shape->frame);
272                                 }
273                         }
274                         else {
275                                 FCurve *fcu = (FCurve *)ale->key_data;
276                                 float tmin, tmax;
277
278                                 /* get range and apply necessary scaling before processing */
279                                 calc_fcurve_range(fcu, &tmin, &tmax, onlySel, TRUE);
280
281                                 if (adt) {
282                                         tmin = BKE_nla_tweakedit_remap(adt, tmin, NLATIME_CONVERT_MAP);
283                                         tmax = BKE_nla_tweakedit_remap(adt, tmax, NLATIME_CONVERT_MAP);
284                                 }
285
286                                 /* try to set cur using these values, if they're more extreme than previously set values */
287                                 *min = MIN2(*min, tmin);
288                                 *max = MAX2(*max, tmax);
289                         }
290                 }
291                 
292                 /* free memory */
293                 BLI_freelistN(&anim_data);
294         }
295         else {
296                 /* set default range */
297                 if (ac->scene) {
298                         *min = (float)ac->scene->r.sfra;
299                         *max = (float)ac->scene->r.efra;
300                 }
301                 else {
302                         *min = -5;
303                         *max = 100;
304                 }
305         }
306 }
307
308 /* ****************** Automatic Preview-Range Operator ****************** */
309
310 static int actkeys_previewrange_exec(bContext *C, wmOperator *UNUSED(op))
311 {
312         bAnimContext ac;
313         Scene *scene;
314         float min, max;
315         
316         /* get editor data */
317         if (ANIM_animdata_get_context(C, &ac) == 0)
318                 return OPERATOR_CANCELLED;
319         if (ac.scene == NULL)
320                 return OPERATOR_CANCELLED;
321         else
322                 scene = ac.scene;
323         
324         /* set the range directly */
325         get_keyframe_extents(&ac, &min, &max, FALSE);
326         scene->r.flag |= SCER_PRV_RANGE;
327         scene->r.psfra = (int)floor(min + 0.5f);
328         scene->r.pefra = (int)floor(max + 0.5f);
329         
330         /* set notifier that things have changed */
331         // XXX err... there's nothing for frame ranges yet, but this should do fine too
332         WM_event_add_notifier(C, NC_SCENE | ND_FRAME, ac.scene);
333         
334         return OPERATOR_FINISHED;
335 }
336  
337 void ACTION_OT_previewrange_set(wmOperatorType *ot)
338 {
339         /* identifiers */
340         ot->name = "Auto-Set Preview Range";
341         ot->idname = "ACTION_OT_previewrange_set";
342         ot->description = "Set Preview Range based on extents of selected Keyframes";
343         
344         /* api callbacks */
345         ot->exec = actkeys_previewrange_exec;
346         ot->poll = ED_operator_action_active;
347         
348         /* flags */
349         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
350 }
351
352 /* ****************** View-All Operator ****************** */
353
354 static int actkeys_viewall(bContext *C, const short onlySel)
355 {
356         bAnimContext ac;
357         View2D *v2d;
358         float extra;
359         
360         /* get editor data */
361         if (ANIM_animdata_get_context(C, &ac) == 0)
362                 return OPERATOR_CANCELLED;
363         v2d = &ac.ar->v2d;
364         
365         /* set the horizontal range, with an extra offset so that the extreme keys will be in view */
366         get_keyframe_extents(&ac, &v2d->cur.xmin, &v2d->cur.xmax, onlySel);
367         
368         extra = 0.1f * BLI_rctf_size_x(&v2d->cur);
369         v2d->cur.xmin -= extra;
370         v2d->cur.xmax += extra;
371         
372         /* set vertical range */
373         v2d->cur.ymax = 0.0f;
374         v2d->cur.ymin = (float)-BLI_rcti_size_y(&v2d->mask);
375         
376         /* do View2D syncing */
377         UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
378         
379         /* just redraw this view */
380         ED_area_tag_redraw(CTX_wm_area(C));
381         
382         return OPERATOR_FINISHED;
383 }
384
385 /* ......... */
386
387 static int actkeys_viewall_exec(bContext *C, wmOperator *UNUSED(op))
388 {       
389         /* whole range */
390         return actkeys_viewall(C, FALSE);
391 }
392
393 static int actkeys_viewsel_exec(bContext *C, wmOperator *UNUSED(op))
394 {
395         /* only selected */
396         return actkeys_viewall(C, TRUE);
397 }
398  
399 void ACTION_OT_view_all(wmOperatorType *ot)
400 {
401         /* identifiers */
402         ot->name = "View All";
403         ot->idname = "ACTION_OT_view_all";
404         ot->description = "Reset viewable area to show full keyframe range";
405         
406         /* api callbacks */
407         ot->exec = actkeys_viewall_exec;
408         ot->poll = ED_operator_action_active;
409         
410         /* flags */
411         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
412 }
413
414 void ACTION_OT_view_selected(wmOperatorType *ot)
415 {
416         /* identifiers */
417         ot->name = "View Selected";
418         ot->idname = "ACTION_OT_view_selected";
419         ot->description = "Reset viewable area to show selected keyframes range";
420         
421         /* api callbacks */
422         ot->exec = actkeys_viewsel_exec;
423         ot->poll = ED_operator_action_active;
424         
425         /* flags */
426         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
427 }
428
429 /* ************************************************************************** */
430 /* GENERAL STUFF */
431
432 /* ******************** Copy/Paste Keyframes Operator ************************* */
433 /* NOTE: the backend code for this is shared with the graph editor */
434
435 static short copy_action_keys(bAnimContext *ac)
436 {       
437         ListBase anim_data = {NULL, NULL};
438         int filter, ok = 0;
439         
440         /* clear buffer first */
441         free_anim_copybuf();
442         
443         /* filter data */
444         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
445         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
446         
447         /* copy keyframes */
448         ok = copy_animedit_keys(ac, &anim_data);
449         
450         /* clean up */
451         BLI_freelistN(&anim_data);
452
453         return ok;
454 }
455
456
457 static short paste_action_keys(bAnimContext *ac,
458                                const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode)
459 {       
460         ListBase anim_data = {NULL, NULL};
461         int filter, ok = 0;
462         
463         /* filter data 
464          * - First time we try to filter more strictly, allowing only selected channels 
465          *   to allow copying animation between channels
466          * - Second time, we loosen things up if nothing was found the first time, allowing
467          *   users to just paste keyframes back into the original curve again [#31670]
468          */
469         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
470         
471         if (ANIM_animdata_filter(ac, &anim_data, filter | ANIMFILTER_SEL, ac->data, ac->datatype) == 0)
472                 ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
473         
474         /* paste keyframes */
475         ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode);
476         
477         /* clean up */
478         BLI_freelistN(&anim_data);
479
480         return ok;
481 }
482
483 /* ------------------- */
484
485 static int actkeys_copy_exec(bContext *C, wmOperator *op)
486 {
487         bAnimContext ac;
488         
489         /* get editor data */
490         if (ANIM_animdata_get_context(C, &ac) == 0)
491                 return OPERATOR_CANCELLED;
492
493         /* copy keyframes */
494         if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
495                 /* FIXME... */
496                 BKE_report(op->reports, RPT_ERROR, "Keyframe pasting is not available for Grease Pencil mode");
497                 return OPERATOR_CANCELLED;
498         }
499         else if (ac.datatype == ANIMCONT_MASK) {
500                 /* FIXME... */
501                 BKE_report(op->reports, RPT_ERROR, "Keyframe pasting is not available for mask mode");
502                 return OPERATOR_CANCELLED;
503         }
504         else {
505                 if (copy_action_keys(&ac)) {    
506                         BKE_report(op->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer");
507                         return OPERATOR_CANCELLED;
508                 }
509         }
510         
511         return OPERATOR_FINISHED;
512 }
513  
514 void ACTION_OT_copy(wmOperatorType *ot)
515 {
516         /* identifiers */
517         ot->name = "Copy Keyframes";
518         ot->idname = "ACTION_OT_copy";
519         ot->description = "Copy selected keyframes to the copy/paste buffer";
520         
521         /* api callbacks */
522         ot->exec = actkeys_copy_exec;
523         ot->poll = ED_operator_action_active;
524
525         /* flags */
526         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
527 }
528
529 static int actkeys_paste_exec(bContext *C, wmOperator *op)
530 {
531         bAnimContext ac;
532
533         const eKeyPasteOffset offset_mode = RNA_enum_get(op->ptr, "offset");
534         const eKeyMergeMode merge_mode = RNA_enum_get(op->ptr, "merge");
535         
536         /* get editor data */
537         if (ANIM_animdata_get_context(C, &ac) == 0)
538                 return OPERATOR_CANCELLED;
539                 
540         /* ac.reports by default will be the global reports list, which won't show warnings */
541         ac.reports = op->reports;
542         
543         /* paste keyframes */
544         if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
545                 // FIXME...
546                 BKE_report(op->reports, RPT_ERROR, "Keyframe pasting is not available for Grease Pencil or Mask mode");
547                 return OPERATOR_CANCELLED;
548         }
549         else {
550                 /* non-zero return means an error occurred while trying to paste */
551                 if (paste_action_keys(&ac, offset_mode, merge_mode)) {
552                         return OPERATOR_CANCELLED;
553                 }
554         }
555         
556         /* validate keyframes after editing */
557         ANIM_editkeyframes_refresh(&ac);
558         
559         /* set notifier that keyframes have changed */
560         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
561         
562         return OPERATOR_FINISHED;
563 }
564  
565 void ACTION_OT_paste(wmOperatorType *ot)
566 {
567         /* identifiers */
568         ot->name = "Paste Keyframes";
569         ot->idname = "ACTION_OT_paste";
570         ot->description = "Paste keyframes from copy/paste buffer for the selected channels, starting on the current frame";
571         
572         /* api callbacks */
573 //      ot->invoke = WM_operator_props_popup; // better wait for action redo panel
574         ot->exec = actkeys_paste_exec;
575         ot->poll = ED_operator_action_active;
576         
577         /* flags */
578         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
579         
580         /* props */
581         RNA_def_enum(ot->srna, "offset", keyframe_paste_offset_items, KEYFRAME_PASTE_OFFSET_CFRA_START, "Offset", "Paste time offset of keys");
582         RNA_def_enum(ot->srna, "merge", keyframe_paste_merge_items, KEYFRAME_PASTE_MERGE_MIX, "Type", "Method of merging pasted keys and existing");
583 }
584
585 /* ******************** Insert Keyframes Operator ************************* */
586
587 /* defines for insert keyframes tool */
588 static EnumPropertyItem prop_actkeys_insertkey_types[] = {
589         {1, "ALL", 0, "All Channels", ""},
590         {2, "SEL", 0, "Only Selected Channels", ""},
591         {3, "GROUP", 0, "In Active Group", ""}, // xxx not in all cases
592         {0, NULL, 0, NULL, NULL}
593 };
594
595 /* this function is responsible for snapping keyframes to frame-times */
596 static void insert_action_keys(bAnimContext *ac, short mode) 
597 {
598         ListBase anim_data = {NULL, NULL};
599         bAnimListElem *ale;
600         int filter;
601         
602         ReportList *reports = ac->reports;
603         Scene *scene = ac->scene;
604         short flag = 0;
605         
606         /* filter data */
607         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
608         if (mode == 2) filter |= ANIMFILTER_SEL;
609         else if (mode == 3) filter |= ANIMFILTER_ACTGROUPED;
610         
611         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
612         
613         /* init keyframing flag */
614         flag = ANIM_get_keyframing_flags(scene, 1);
615         
616         /* insert keyframes */
617         for (ale = anim_data.first; ale; ale = ale->next) {
618                 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
619                 FCurve *fcu = (FCurve *)ale->key_data;
620                 float cfra;
621                 
622                 /* adjust current frame for NLA-scaling */
623                 if (adt)
624                         cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
625                 else 
626                         cfra = (float)CFRA;
627                         
628                 /* if there's an id */
629                 if (ale->id)
630                         insert_keyframe(reports, ale->id, NULL, ((fcu->grp) ? (fcu->grp->name) : (NULL)), fcu->rna_path, fcu->array_index, cfra, flag);
631                 else
632                         insert_vert_fcurve(fcu, cfra, fcu->curval, 0);
633         }
634         
635         BLI_freelistN(&anim_data);
636 }
637
638 /* ------------------- */
639
640 static int actkeys_insertkey_exec(bContext *C, wmOperator *op)
641 {
642         bAnimContext ac;
643         short mode;
644         
645         /* get editor data */
646         if (ANIM_animdata_get_context(C, &ac) == 0)
647                 return OPERATOR_CANCELLED;
648         if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
649                 return OPERATOR_CANCELLED;
650                 
651         /* what channels to affect? */
652         mode = RNA_enum_get(op->ptr, "type");
653         
654         /* insert keyframes */
655         insert_action_keys(&ac, mode);
656         
657         /* validate keyframes after editing */
658         ANIM_editkeyframes_refresh(&ac);
659         
660         /* set notifier that keyframes have changed */
661         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
662         
663         return OPERATOR_FINISHED;
664 }
665
666 void ACTION_OT_keyframe_insert(wmOperatorType *ot)
667 {
668         /* identifiers */
669         ot->name = "Insert Keyframes";
670         ot->idname = "ACTION_OT_keyframe_insert";
671         ot->description = "Insert keyframes for the specified channels";
672         
673         /* api callbacks */
674         ot->invoke = WM_menu_invoke;
675         ot->exec = actkeys_insertkey_exec;
676         ot->poll = ED_operator_action_active;
677         
678         /* flags */
679         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
680         
681         /* id-props */
682         ot->prop = RNA_def_enum(ot->srna, "type", prop_actkeys_insertkey_types, 0, "Type", "");
683 }
684
685 /* ******************** Duplicate Keyframes Operator ************************* */
686
687 static void duplicate_action_keys(bAnimContext *ac)
688 {
689         ListBase anim_data = {NULL, NULL};
690         bAnimListElem *ale;
691         int filter;
692         
693         /* filter data */
694         if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
695                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
696         else
697                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
698         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
699         
700         /* loop through filtered data and delete selected keys */
701         for (ale = anim_data.first; ale; ale = ale->next) {
702                 if (ale->type == ANIMTYPE_FCURVE)
703                         duplicate_fcurve_keys((FCurve *)ale->key_data);
704                 else if (ale->type == ANIMTYPE_GPLAYER)
705                         ED_gplayer_frames_duplicate((bGPDlayer *)ale->data);
706                 else if (ale->type == ANIMTYPE_MASKLAYER)
707                         ED_masklayer_frames_duplicate((MaskLayer *)ale->data);
708                 else
709                         BLI_assert(0);
710         }
711         
712         /* free filtered list */
713         BLI_freelistN(&anim_data);
714 }
715
716 /* ------------------- */
717
718 static int actkeys_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
719 {
720         bAnimContext ac;
721         
722         /* get editor data */
723         if (ANIM_animdata_get_context(C, &ac) == 0)
724                 return OPERATOR_CANCELLED;
725                 
726         /* duplicate keyframes */
727         duplicate_action_keys(&ac);
728         
729         /* validate keyframes after editing */
730         if (!ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
731                 ANIM_editkeyframes_refresh(&ac);
732         
733         /* set notifier that keyframes have changed */
734         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
735         
736         return OPERATOR_FINISHED;
737 }
738
739 static int actkeys_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
740 {
741         actkeys_duplicate_exec(C, op);
742         
743         return OPERATOR_FINISHED;
744 }
745  
746 void ACTION_OT_duplicate(wmOperatorType *ot)
747 {
748         /* identifiers */
749         ot->name = "Duplicate Keyframes";
750         ot->idname = "ACTION_OT_duplicate";
751         ot->description = "Make a copy of all selected keyframes";
752         
753         /* api callbacks */
754         ot->invoke = actkeys_duplicate_invoke;
755         ot->exec = actkeys_duplicate_exec;
756         ot->poll = ED_operator_action_active;
757         
758         /* flags */
759         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
760 }
761
762 /* ******************** Delete Keyframes Operator ************************* */
763
764 static void delete_action_keys(bAnimContext *ac)
765 {
766         ListBase anim_data = {NULL, NULL};
767         bAnimListElem *ale;
768         int filter;
769         
770         /* filter data */
771         if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
772                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
773         else
774                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
775         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
776         
777         /* loop through filtered data and delete selected keys */
778         for (ale = anim_data.first; ale; ale = ale->next) {
779                 if (ale->type == ANIMTYPE_GPLAYER) {
780                         ED_gplayer_frames_delete((bGPDlayer *)ale->data);
781                 }
782                 else if (ale->type == ANIMTYPE_MASKLAYER) {
783                         ED_masklayer_frames_delete((MaskLayer *)ale->data);
784                 }
785                 else {
786                         FCurve *fcu = (FCurve *)ale->key_data;
787                         AnimData *adt = ale->adt;
788                         
789                         /* delete selected keyframes only */
790                         delete_fcurve_keys(fcu); 
791                         
792                         /* Only delete curve too if it won't be doing anything anymore */
793                         if ((fcu->totvert == 0) && (list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE) == 0))
794                                 ANIM_fcurve_delete_from_animdata(ac, adt, fcu);
795                 }
796         }
797         
798         /* free filtered list */
799         BLI_freelistN(&anim_data);
800 }
801
802 /* ------------------- */
803
804 static int actkeys_delete_exec(bContext *C, wmOperator *UNUSED(op))
805 {
806         bAnimContext ac;
807         
808         /* get editor data */
809         if (ANIM_animdata_get_context(C, &ac) == 0)
810                 return OPERATOR_CANCELLED;
811                 
812         /* delete keyframes */
813         delete_action_keys(&ac);
814         
815         /* validate keyframes after editing */
816         if (!ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
817                 ANIM_editkeyframes_refresh(&ac);
818         
819         /* set notifier that keyframes have changed */
820         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
821         
822         return OPERATOR_FINISHED;
823 }
824  
825 void ACTION_OT_delete(wmOperatorType *ot)
826 {
827         /* identifiers */
828         ot->name = "Delete Keyframes";
829         ot->idname = "ACTION_OT_delete";
830         ot->description = "Remove all selected keyframes";
831         
832         /* api callbacks */
833         ot->invoke = WM_operator_confirm;
834         ot->exec = actkeys_delete_exec;
835         ot->poll = ED_operator_action_active;
836         
837         /* flags */
838         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
839 }
840
841 /* ******************** Clean Keyframes Operator ************************* */
842
843 static void clean_action_keys(bAnimContext *ac, float thresh)
844 {       
845         ListBase anim_data = {NULL, NULL};
846         bAnimListElem *ale;
847         int filter;
848         
849         /* filter data */
850         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_SEL /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
851         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
852         
853         /* loop through filtered data and clean curves */
854         for (ale = anim_data.first; ale; ale = ale->next)
855                 clean_fcurve((FCurve *)ale->key_data, thresh);
856         
857         /* free temp data */
858         BLI_freelistN(&anim_data);
859 }
860
861 /* ------------------- */
862
863 static int actkeys_clean_exec(bContext *C, wmOperator *op)
864 {
865         bAnimContext ac;
866         float thresh;
867         
868         /* get editor data */
869         if (ANIM_animdata_get_context(C, &ac) == 0)
870                 return OPERATOR_CANCELLED;
871         if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
872                 return OPERATOR_PASS_THROUGH;
873                 
874         /* get cleaning threshold */
875         thresh = RNA_float_get(op->ptr, "threshold");
876         
877         /* clean keyframes */
878         clean_action_keys(&ac, thresh);
879         
880         /* validate keyframes after editing */
881         ANIM_editkeyframes_refresh(&ac);
882         
883         /* set notifier that keyframes have changed */
884         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
885         
886         return OPERATOR_FINISHED;
887 }
888  
889 void ACTION_OT_clean(wmOperatorType *ot)
890 {
891         /* identifiers */
892         ot->name = "Clean Keyframes";
893         ot->idname = "ACTION_OT_clean";
894         ot->description = "Simplify F-Curves by removing closely spaced keyframes";
895         
896         /* api callbacks */
897         //ot->invoke =  // XXX we need that number popup for this! 
898         ot->exec = actkeys_clean_exec;
899         ot->poll = ED_operator_action_active;
900         
901         /* flags */
902         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
903         
904         /* properties */
905         ot->prop = RNA_def_float(ot->srna, "threshold", 0.001f, 0.0f, FLT_MAX, "Threshold", "", 0.0f, 1000.0f);
906 }
907
908 /* ******************** Sample Keyframes Operator *********************** */
909
910 /* Evaluates the curves between each selected keyframe on each frame, and keys the value  */
911 static void sample_action_keys(bAnimContext *ac)
912 {       
913         ListBase anim_data = {NULL, NULL};
914         bAnimListElem *ale;
915         int filter;
916         
917         /* filter data */
918         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
919         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
920         
921         /* loop through filtered data and add keys between selected keyframes on every frame  */
922         for (ale = anim_data.first; ale; ale = ale->next)
923                 sample_fcurve((FCurve *)ale->key_data);
924         
925         /* admin and redraws */
926         BLI_freelistN(&anim_data);
927 }
928
929 /* ------------------- */
930
931 static int actkeys_sample_exec(bContext *C, wmOperator *UNUSED(op))
932 {
933         bAnimContext ac;
934         
935         /* get editor data */
936         if (ANIM_animdata_get_context(C, &ac) == 0)
937                 return OPERATOR_CANCELLED;
938         if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
939                 return OPERATOR_PASS_THROUGH;
940         
941         /* sample keyframes */
942         sample_action_keys(&ac);
943         
944         /* validate keyframes after editing */
945         ANIM_editkeyframes_refresh(&ac);
946         
947         /* set notifier that keyframes have changed */
948         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
949         
950         return OPERATOR_FINISHED;
951 }
952  
953 void ACTION_OT_sample(wmOperatorType *ot)
954 {
955         /* identifiers */
956         ot->name = "Sample Keyframes";
957         ot->idname = "ACTION_OT_sample";
958         ot->description = "Add keyframes on every frame between the selected keyframes";
959         
960         /* api callbacks */
961         ot->exec = actkeys_sample_exec;
962         ot->poll = ED_operator_action_active;
963         
964         /* flags */
965         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
966 }
967
968 /* ************************************************************************** */
969 /* SETTINGS STUFF */
970
971 /* ******************** Set Extrapolation-Type Operator *********************** */
972
973 /* defines for make/clear cyclic extrapolation tools */
974 #define MAKE_CYCLIC_EXPO    -1
975 #define CLEAR_CYCLIC_EXPO   -2
976
977 /* defines for set extrapolation-type for selected keyframes tool */
978 static EnumPropertyItem prop_actkeys_expo_types[] = {
979         {FCURVE_EXTRAPOLATE_CONSTANT, "CONSTANT", 0, "Constant Extrapolation", "Values on endpoint keyframes are held"},
980         {FCURVE_EXTRAPOLATE_LINEAR, "LINEAR", 0, "Linear Extrapolation", "Straight-line slope of end segments are extended past the endpoint keyframes"},
981         
982         {MAKE_CYCLIC_EXPO, "MAKE_CYCLIC", 0, "Make Cyclic (F-Modifier)", "Add Cycles F-Modifier if one doesn't exist already"},
983         {CLEAR_CYCLIC_EXPO, "CLEAR_CYCLIC", 0, "Clear Cyclic (F-Modifier)", "Remove Cycles F-Modifier if not needed anymore"},
984         {0, NULL, 0, NULL, NULL}
985 };
986
987 /* this function is responsible for setting extrapolation mode for keyframes */
988 static void setexpo_action_keys(bAnimContext *ac, short mode) 
989 {
990         ListBase anim_data = {NULL, NULL};
991         bAnimListElem *ale;
992         int filter;
993         
994         /* filter data */
995         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_SEL /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
996         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
997         
998         /* loop through setting mode per F-Curve */
999         for (ale = anim_data.first; ale; ale = ale->next) {
1000                 FCurve *fcu = (FCurve *)ale->data;
1001                 
1002                 if (mode >= 0) {
1003                         /* just set mode setting */
1004                         fcu->extend = mode;
1005                 }
1006                 else {
1007                         /* shortcuts for managing Cycles F-Modifiers to make it easier to toggle cyclic animation 
1008                          * without having to go through FModifier UI in Graph Editor to do so
1009                          */
1010                         if (mode == MAKE_CYCLIC_EXPO) {
1011                                 /* only add if one doesn't exist */
1012                                 if (list_has_suitable_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES, -1) == 0) {
1013                                         // TODO: add some more preset versions which set different extrapolation options?
1014                                         add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES);
1015                                 }
1016                         }
1017                         else if (mode == CLEAR_CYCLIC_EXPO) {
1018                                 /* remove all the modifiers fitting this description */
1019                                 FModifier *fcm, *fcn = NULL;
1020                                 
1021                                 for (fcm = fcu->modifiers.first; fcm; fcm = fcn) {
1022                                         fcn = fcm->next;
1023                                         
1024                                         if (fcm->type == FMODIFIER_TYPE_CYCLES)
1025                                                 remove_fmodifier(&fcu->modifiers, fcm);
1026                                 }
1027                         }
1028                 }
1029         }
1030         
1031         /* cleanup */
1032         BLI_freelistN(&anim_data);
1033 }
1034
1035 /* ------------------- */
1036
1037 static int actkeys_expo_exec(bContext *C, wmOperator *op)
1038 {
1039         bAnimContext ac;
1040         short mode;
1041         
1042         /* get editor data */
1043         if (ANIM_animdata_get_context(C, &ac) == 0)
1044                 return OPERATOR_CANCELLED;
1045         if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
1046                 return OPERATOR_PASS_THROUGH;
1047                 
1048         /* get handle setting mode */
1049         mode = RNA_enum_get(op->ptr, "type");
1050         
1051         /* set handle type */
1052         setexpo_action_keys(&ac, mode);
1053         
1054         /* validate keyframes after editing */
1055         ANIM_editkeyframes_refresh(&ac);
1056         
1057         /* set notifier that keyframe properties have changed */
1058         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL);
1059         
1060         return OPERATOR_FINISHED;
1061 }
1062  
1063 void ACTION_OT_extrapolation_type(wmOperatorType *ot)
1064 {
1065         /* identifiers */
1066         ot->name = "Set Keyframe Extrapolation";
1067         ot->idname = "ACTION_OT_extrapolation_type";
1068         ot->description = "Set extrapolation mode for selected F-Curves";
1069         
1070         /* api callbacks */
1071         ot->invoke = WM_menu_invoke;
1072         ot->exec = actkeys_expo_exec;
1073         ot->poll = ED_operator_action_active;
1074         
1075         /* flags */
1076         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1077         
1078         /* id-props */
1079         ot->prop = RNA_def_enum(ot->srna, "type", prop_actkeys_expo_types, 0, "Type", "");
1080 }
1081
1082 /* ******************** Set Interpolation-Type Operator *********************** */
1083
1084 /* this function is responsible for setting interpolation mode for keyframes */
1085 static void setipo_action_keys(bAnimContext *ac, short mode) 
1086 {
1087         ListBase anim_data = {NULL, NULL};
1088         bAnimListElem *ale;
1089         int filter;
1090         KeyframeEditFunc set_cb = ANIM_editkeyframes_ipo(mode);
1091         
1092         /* filter data */
1093         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
1094         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1095         
1096         /* loop through setting BezTriple interpolation
1097          * Note: we do not supply KeyframeEditData to the looper yet. Currently that's not necessary here...
1098          */
1099         for (ale = anim_data.first; ale; ale = ale->next)
1100                 ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve);
1101         
1102         /* cleanup */
1103         BLI_freelistN(&anim_data);
1104 }
1105
1106 /* ------------------- */
1107
1108 static int actkeys_ipo_exec(bContext *C, wmOperator *op)
1109 {
1110         bAnimContext ac;
1111         short mode;
1112         
1113         /* get editor data */
1114         if (ANIM_animdata_get_context(C, &ac) == 0)
1115                 return OPERATOR_CANCELLED;
1116         if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
1117                 return OPERATOR_PASS_THROUGH;
1118                 
1119         /* get handle setting mode */
1120         mode = RNA_enum_get(op->ptr, "type");
1121         
1122         /* set handle type */
1123         setipo_action_keys(&ac, mode);
1124         
1125         /* validate keyframes after editing */
1126         ANIM_editkeyframes_refresh(&ac);
1127         
1128         /* set notifier that keyframe properties have changed */
1129         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL);
1130         
1131         return OPERATOR_FINISHED;
1132 }
1133  
1134 void ACTION_OT_interpolation_type(wmOperatorType *ot)
1135 {
1136         /* identifiers */
1137         ot->name = "Set Keyframe Interpolation";
1138         ot->idname = "ACTION_OT_interpolation_type";
1139         ot->description = "Set interpolation mode for the F-Curve segments starting from the selected keyframes";
1140         
1141         /* api callbacks */
1142         ot->invoke = WM_menu_invoke;
1143         ot->exec = actkeys_ipo_exec;
1144         ot->poll = ED_operator_action_active;
1145         
1146         /* flags */
1147         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1148         
1149         /* id-props */
1150         ot->prop = RNA_def_enum(ot->srna, "type", beztriple_interpolation_mode_items, 0, "Type", "");
1151 }
1152
1153 /* ******************** Set Handle-Type Operator *********************** */
1154
1155 /* this function is responsible for setting handle-type of selected keyframes */
1156 static void sethandles_action_keys(bAnimContext *ac, short mode) 
1157 {
1158         ListBase anim_data = {NULL, NULL};
1159         bAnimListElem *ale;
1160         int filter;
1161         
1162         KeyframeEditFunc edit_cb = ANIM_editkeyframes_handles(mode);
1163         KeyframeEditFunc sel_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED);
1164         
1165         /* filter data */
1166         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
1167         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1168         
1169         /* loop through setting flags for handles 
1170          * Note: we do not supply KeyframeEditData to the looper yet. Currently that's not necessary here...
1171          */
1172         for (ale = anim_data.first; ale; ale = ale->next) {
1173                 FCurve *fcu = (FCurve *)ale->key_data;
1174                 
1175                 /* any selected keyframes for editing? */
1176                 if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL)) {
1177                         /* change type of selected handles */
1178                         ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, calchandles_fcurve);
1179                 }
1180         }
1181         
1182         /* cleanup */
1183         BLI_freelistN(&anim_data);
1184 }
1185
1186 /* ------------------- */
1187
1188 static int actkeys_handletype_exec(bContext *C, wmOperator *op)
1189 {
1190         bAnimContext ac;
1191         short mode;
1192         
1193         /* get editor data */
1194         if (ANIM_animdata_get_context(C, &ac) == 0)
1195                 return OPERATOR_CANCELLED;
1196         if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
1197                 return OPERATOR_PASS_THROUGH;
1198                 
1199         /* get handle setting mode */
1200         mode = RNA_enum_get(op->ptr, "type");
1201         
1202         /* set handle type */
1203         sethandles_action_keys(&ac, mode);
1204         
1205         /* validate keyframes after editing */
1206         ANIM_editkeyframes_refresh(&ac);
1207         
1208         /* set notifier that keyframe properties have changed */
1209         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL);
1210         
1211         return OPERATOR_FINISHED;
1212 }
1213  
1214 void ACTION_OT_handle_type(wmOperatorType *ot)
1215 {
1216         /* identifiers */
1217         ot->name = "Set Keyframe Handle Type";
1218         ot->idname = "ACTION_OT_handle_type";
1219         ot->description = "Set type of handle for selected keyframes";
1220         
1221         /* api callbacks */
1222         ot->invoke = WM_menu_invoke;
1223         ot->exec = actkeys_handletype_exec;
1224         ot->poll = ED_operator_action_active;
1225         
1226         /* flags */
1227         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1228         
1229         /* id-props */
1230         ot->prop = RNA_def_enum(ot->srna, "type", keyframe_handle_type_items, 0, "Type", "");
1231 }
1232
1233 /* ******************** Set Keyframe-Type Operator *********************** */
1234
1235 /* this function is responsible for setting interpolation mode for keyframes */
1236 static void setkeytype_action_keys(bAnimContext *ac, short mode) 
1237 {
1238         ListBase anim_data = {NULL, NULL};
1239         bAnimListElem *ale;
1240         int filter;
1241         KeyframeEditFunc set_cb = ANIM_editkeyframes_keytype(mode);
1242         
1243         /* filter data */
1244         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
1245         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1246         
1247         /* loop through setting BezTriple interpolation
1248          * Note: we do not supply KeyframeEditData to the looper yet. Currently that's not necessary here...
1249          */
1250         for (ale = anim_data.first; ale; ale = ale->next)
1251                 ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, NULL);
1252         
1253         /* cleanup */
1254         BLI_freelistN(&anim_data);
1255 }
1256
1257 /* ------------------- */
1258
1259 static int actkeys_keytype_exec(bContext *C, wmOperator *op)
1260 {
1261         bAnimContext ac;
1262         short mode;
1263         
1264         /* get editor data */
1265         if (ANIM_animdata_get_context(C, &ac) == 0)
1266                 return OPERATOR_CANCELLED;
1267         if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
1268                 return OPERATOR_PASS_THROUGH;
1269                 
1270         /* get handle setting mode */
1271         mode = RNA_enum_get(op->ptr, "type");
1272         
1273         /* set handle type */
1274         setkeytype_action_keys(&ac, mode);
1275         
1276         /* validate keyframes after editing */
1277         ANIM_editkeyframes_refresh(&ac);
1278         
1279         /* set notifier that keyframe properties have changed */
1280         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL);
1281         
1282         return OPERATOR_FINISHED;
1283 }
1284  
1285 void ACTION_OT_keyframe_type(wmOperatorType *ot)
1286 {
1287         /* identifiers */
1288         ot->name = "Set Keyframe Type";
1289         ot->idname = "ACTION_OT_keyframe_type";
1290         ot->description = "Set type of keyframe for the selected keyframes";
1291         
1292         /* api callbacks */
1293         ot->invoke = WM_menu_invoke;
1294         ot->exec = actkeys_keytype_exec;
1295         ot->poll = ED_operator_action_active;
1296         
1297         /* flags */
1298         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1299         
1300         /* id-props */
1301         ot->prop = RNA_def_enum(ot->srna, "type", beztriple_keyframe_type_items, 0, "Type", "");
1302 }
1303
1304 /* ************************************************************************** */
1305 /* TRANSFORM STUFF */
1306
1307 /* ***************** Jump to Selected Frames Operator *********************** */
1308
1309 /* snap current-frame indicator to 'average time' of selected keyframe */
1310 static int actkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op))
1311 {
1312         bAnimContext ac;
1313         ListBase anim_data = {NULL, NULL};
1314         bAnimListElem *ale;
1315         int filter;
1316         KeyframeEditData ked = {{NULL}};
1317         
1318         /* get editor data */
1319         if (ANIM_animdata_get_context(C, &ac) == 0)
1320                 return OPERATOR_CANCELLED;
1321         
1322         /* init edit data */    
1323         /* loop over action data, averaging values */
1324         filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ | ANIMFILTER_NODUPLIS);
1325         ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
1326         
1327         for (ale = anim_data.first; ale; ale = ale->next) {
1328                 AnimData *adt = ANIM_nla_mapping_get(&ac, ale);
1329                 if (adt) {
1330                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); 
1331                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_calc_average, NULL);
1332                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
1333                 }
1334                 else
1335                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_calc_average, NULL);
1336         }
1337         
1338         BLI_freelistN(&anim_data);
1339         
1340         /* set the new current frame value, based on the average time */
1341         if (ked.i1) {
1342                 Scene *scene = ac.scene;
1343                 CFRA = (int)floor((ked.f1 / ked.i1) + 0.5f);
1344                 SUBFRA = 0.f;
1345         }
1346         
1347         /* set notifier that things have changed */
1348         WM_event_add_notifier(C, NC_SCENE | ND_FRAME, ac.scene);
1349         
1350         return OPERATOR_FINISHED;
1351 }
1352
1353 void ACTION_OT_frame_jump(wmOperatorType *ot)
1354 {
1355         /* identifiers */
1356         ot->name = "Jump to Keyframes";
1357         ot->idname = "ACTION_OT_frame_jump";
1358         ot->description = "Set the current frame to the average frame value of selected keyframes";
1359         
1360         /* api callbacks */
1361         ot->exec = actkeys_framejump_exec;
1362         ot->poll = ED_operator_action_active;
1363         
1364         /* flags */
1365         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1366 }
1367
1368 /* ******************** Snap Keyframes Operator *********************** */
1369
1370 /* defines for snap keyframes tool */
1371 static EnumPropertyItem prop_actkeys_snap_types[] = {
1372         {ACTKEYS_SNAP_CFRA, "CFRA", 0, "Current frame", 
1373          "Snap selected keyframes to the current frame"},
1374         {ACTKEYS_SNAP_NEAREST_FRAME, "NEAREST_FRAME", 0, "Nearest Frame", 
1375          "Snap selected keyframes to the nearest (whole) frame (use to fix accidental sub-frame offsets)"},
1376         {ACTKEYS_SNAP_NEAREST_SECOND, "NEAREST_SECOND", 0, "Nearest Second", 
1377          "Snap selected keyframes to the nearest second"},
1378         {ACTKEYS_SNAP_NEAREST_MARKER, "NEAREST_MARKER", 0, "Nearest Marker", 
1379          "Snap selected keyframes to the nearest marker"},
1380         {0, NULL, 0, NULL, NULL}
1381 };
1382
1383 /* this function is responsible for snapping keyframes to frame-times */
1384 static void snap_action_keys(bAnimContext *ac, short mode) 
1385 {
1386         ListBase anim_data = {NULL, NULL};
1387         bAnimListElem *ale;
1388         int filter;
1389         
1390         KeyframeEditData ked = {{NULL}};
1391         KeyframeEditFunc edit_cb;
1392         
1393         /* filter data */
1394         if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
1395                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
1396         else
1397                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
1398         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1399         
1400         /* get beztriple editing callbacks */
1401         edit_cb = ANIM_editkeyframes_snap(mode);
1402
1403         ked.scene = ac->scene;
1404         if (mode == ACTKEYS_SNAP_NEAREST_MARKER) {
1405                 ked.list.first = (ac->markers) ? ac->markers->first : NULL;
1406                 ked.list.last = (ac->markers) ? ac->markers->last : NULL;
1407         }
1408         
1409         /* snap keyframes */
1410         for (ale = anim_data.first; ale; ale = ale->next) {
1411                 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1412                 
1413                 if (adt) {
1414                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); 
1415                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
1416                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
1417                 }
1418                 //else if (ale->type == ACTTYPE_GPLAYER)
1419                 //      snap_gplayer_frames(ale->data, mode);
1420                 else 
1421                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
1422         }
1423         
1424         BLI_freelistN(&anim_data);
1425 }
1426
1427 /* ------------------- */
1428
1429 static int actkeys_snap_exec(bContext *C, wmOperator *op)
1430 {
1431         bAnimContext ac;
1432         short mode;
1433         
1434         /* get editor data */
1435         if (ANIM_animdata_get_context(C, &ac) == 0)
1436                 return OPERATOR_CANCELLED;
1437                 
1438         // XXX...
1439         if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
1440                 return OPERATOR_PASS_THROUGH;
1441                 
1442         /* get snapping mode */
1443         mode = RNA_enum_get(op->ptr, "type");
1444         
1445         /* snap keyframes */
1446         snap_action_keys(&ac, mode);
1447         
1448         /* validate keyframes after editing */
1449         ANIM_editkeyframes_refresh(&ac);
1450         
1451         /* set notifier that keyframes have changed */
1452         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1453         
1454         return OPERATOR_FINISHED;
1455 }
1456  
1457 void ACTION_OT_snap(wmOperatorType *ot)
1458 {
1459         /* identifiers */
1460         ot->name = "Snap Keys";
1461         ot->idname = "ACTION_OT_snap";
1462         ot->description = "Snap selected keyframes to the times specified";
1463         
1464         /* api callbacks */
1465         ot->invoke = WM_menu_invoke;
1466         ot->exec = actkeys_snap_exec;
1467         ot->poll = ED_operator_action_active;
1468         
1469         /* flags */
1470         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1471         
1472         /* id-props */
1473         ot->prop = RNA_def_enum(ot->srna, "type", prop_actkeys_snap_types, 0, "Type", "");
1474 }
1475
1476 /* ******************** Mirror Keyframes Operator *********************** */
1477
1478 /* defines for mirror keyframes tool */
1479 static EnumPropertyItem prop_actkeys_mirror_types[] = {
1480         {ACTKEYS_MIRROR_CFRA, "CFRA", 0, "By Times over Current frame", 
1481          "Flip times of selected keyframes using the current frame as the mirror line"},
1482         {ACTKEYS_MIRROR_XAXIS, "XAXIS", 0, "By Values over Value=0", 
1483          "Flip values of selected keyframes (i.e. negative values become positive, and vice versa)"},
1484         {ACTKEYS_MIRROR_MARKER, "MARKER", 0, "By Times over First Selected Marker", 
1485          "Flip times of selected keyframes using the first selected marker as the reference point"},
1486         {0, NULL, 0, NULL, NULL}
1487 };
1488
1489 /* this function is responsible for mirroring keyframes */
1490 static void mirror_action_keys(bAnimContext *ac, short mode) 
1491 {
1492         ListBase anim_data = {NULL, NULL};
1493         bAnimListElem *ale;
1494         int filter;
1495         
1496         KeyframeEditData ked = {{NULL}};
1497         KeyframeEditFunc edit_cb;
1498         
1499         /* get beztriple editing callbacks */
1500         edit_cb = ANIM_editkeyframes_mirror(mode);
1501
1502         ked.scene = ac->scene;
1503         
1504         /* for 'first selected marker' mode, need to find first selected marker first! */
1505         // XXX should this be made into a helper func in the API?
1506         if (mode == ACTKEYS_MIRROR_MARKER) {
1507                 TimeMarker *marker = ED_markers_get_first_selected(ac->markers);
1508                 
1509                 if (marker)
1510                         ked.f1 = (float)marker->frame;
1511                 else
1512                         return;
1513         }
1514         
1515         /* filter data */
1516         if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
1517                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
1518         else
1519                 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
1520         ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
1521         
1522         /* mirror keyframes */
1523         for (ale = anim_data.first; ale; ale = ale->next) {
1524                 AnimData *adt = ANIM_nla_mapping_get(ac, ale);
1525                 
1526                 if (adt) {
1527                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); 
1528                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
1529                         ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
1530                 }
1531                 //else if (ale->type == ACTTYPE_GPLAYER)
1532                 //      snap_gplayer_frames(ale->data, mode);
1533                 else 
1534                         ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
1535         }
1536         
1537         BLI_freelistN(&anim_data);
1538 }
1539
1540 /* ------------------- */
1541
1542 static int actkeys_mirror_exec(bContext *C, wmOperator *op)
1543 {
1544         bAnimContext ac;
1545         short mode;
1546         
1547         /* get editor data */
1548         if (ANIM_animdata_get_context(C, &ac) == 0)
1549                 return OPERATOR_CANCELLED;
1550                 
1551         // XXX...
1552         if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
1553                 return OPERATOR_PASS_THROUGH;
1554                 
1555         /* get mirroring mode */
1556         mode = RNA_enum_get(op->ptr, "type");
1557         
1558         /* mirror keyframes */
1559         mirror_action_keys(&ac, mode);
1560         
1561         /* validate keyframes after editing */
1562         ANIM_editkeyframes_refresh(&ac);
1563         
1564         /* set notifier that keyframes have changed */
1565         WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
1566         
1567         return OPERATOR_FINISHED;
1568 }
1569  
1570 void ACTION_OT_mirror(wmOperatorType *ot)
1571 {
1572         /* identifiers */
1573         ot->name = "Mirror Keys";
1574         ot->idname = "ACTION_OT_mirror";
1575         ot->description = "Flip selected keyframes over the selected mirror line";
1576         
1577         /* api callbacks */
1578         ot->invoke = WM_menu_invoke;
1579         ot->exec = actkeys_mirror_exec;
1580         ot->poll = ED_operator_action_active;
1581         
1582         /* flags */
1583         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1584         
1585         /* id-props */
1586         ot->prop = RNA_def_enum(ot->srna, "type", prop_actkeys_mirror_types, 0, "Type", "");
1587 }
1588
1589 /* ************************************************************************** */