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