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