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