Fix #34040: Moving Normal Node with enabled Cycles Material Preview crashes
[blender.git] / source / blender / editors / space_action / action_edit.c
index 73c36fd8086bd68260d6ef2aa8c5cd249cbeee3e..7e99e6c065dfed7ce87c4f3bf8ce46c0427fed19 100644 (file)
@@ -44,6 +44,7 @@
 #include "DNA_gpencil_types.h"
 #include "DNA_object_types.h"
 #include "DNA_scene_types.h"
+#include "DNA_mask_types.h"
 
 #include "RNA_access.h"
 #include "RNA_define.h"
@@ -51,6 +52,8 @@
 
 #include "BKE_action.h"
 #include "BKE_fcurve.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
 #include "BKE_nla.h"
 #include "BKE_context.h"
 #include "BKE_report.h"
@@ -64,6 +67,7 @@
 #include "ED_screen.h"
 #include "ED_transform.h"
 #include "ED_markers.h"
+#include "ED_mask.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
@@ -101,8 +105,10 @@ static int act_new_exec(bContext *C, wmOperator *UNUSED(op))
                        action = BKE_action_copy(oldact);
                }
                else {
+                       Main *bmain = CTX_data_main(C);
+
                        /* just make a new (empty) action */
-                       action = add_empty_action("Action");
+                       action = add_empty_action(bmain, "Action");
                }
                
                /* when creating new ID blocks, use is already 1 (fake user), 
@@ -130,7 +136,7 @@ void ACTION_OT_new(wmOperatorType *ot)
        
        /* api callbacks */
        ot->exec = act_new_exec;
-       // NOTE: this is used in the NLA too...
+       /* NOTE: this is used in the NLA too... */
        //ot->poll = ED_operator_action_active;
        
        /* flags */
@@ -232,8 +238,8 @@ static void get_keyframe_extents(bAnimContext *ac, float *min, float *max, const
        int filter;
        
        /* get data to filter, from Action or Dopesheet */
-       // XXX: what is sel doing here?!
-       //      Commented it, was breaking things (eg. the "auto preview range" tool).
+       /* XXX: what is sel doing here?!
+        *      Commented it, was breaking things (eg. the "auto preview range" tool). */
        filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_SEL *//*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
        ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
        
@@ -252,8 +258,23 @@ static void get_keyframe_extents(bAnimContext *ac, float *min, float *max, const
 
                                /* find gp-frame which is less than or equal to cframe */
                                for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
-                                       *min = MIN2(*min, gpf->framenum);
-                                       *max = MAX2(*max, gpf->framenum);
+                                       const float framenum = (float)gpf->framenum;
+                                       *min = min_ff(*min, framenum);
+                                       *max = max_ff(*max, framenum);
+                               }
+                       }
+                       else if (ale->datatype == ALE_MASKLAY) {
+                               MaskLayer *masklay = ale->data;
+                               MaskLayerShape *masklay_shape;
+
+                               /* find mask layer which is less than or equal to cframe */
+                               for (masklay_shape = masklay->splines_shapes.first;
+                                    masklay_shape;
+                                    masklay_shape = masklay_shape->next)
+                               {
+                                       const float framenum = (float)masklay_shape->frame;
+                                       *min = min_ff(*min, framenum);
+                                       *max = max_ff(*max, framenum);
                                }
                        }
                        else {
@@ -269,8 +290,8 @@ static void get_keyframe_extents(bAnimContext *ac, float *min, float *max, const
                                }
 
                                /* try to set cur using these values, if they're more extreme than previously set values */
-                               *min = MIN2(*min, tmin);
-                               *max = MAX2(*max, tmax);
+                               *min = min_ff(*min, tmin);
+                               *max = max_ff(*max, tmax);
                        }
                }
                
@@ -350,13 +371,13 @@ static int actkeys_viewall(bContext *C, const short onlySel)
        /* set the horizontal range, with an extra offset so that the extreme keys will be in view */
        get_keyframe_extents(&ac, &v2d->cur.xmin, &v2d->cur.xmax, onlySel);
        
-       extra = 0.1f * (v2d->cur.xmax - v2d->cur.xmin);
+       extra = 0.1f * BLI_rctf_size_x(&v2d->cur);
        v2d->cur.xmin -= extra;
        v2d->cur.xmax += extra;
        
        /* set vertical range */
        v2d->cur.ymax = 0.0f;
-       v2d->cur.ymin = (float)-(v2d->mask.ymax - v2d->mask.ymin);
+       v2d->cur.ymin = (float)-BLI_rcti_size_y(&v2d->mask);
        
        /* do View2D syncing */
        UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY);
@@ -445,9 +466,16 @@ static short paste_action_keys(bAnimContext *ac,
        ListBase anim_data = {NULL, NULL};
        int filter, ok = 0;
        
-       /* filter data */
-       filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
-       ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+       /* filter data 
+        * - First time we try to filter more strictly, allowing only selected channels 
+        *   to allow copying animation between channels
+        * - Second time, we loosen things up if nothing was found the first time, allowing
+        *   users to just paste keyframes back into the original curve again [#31670]
+        */
+       filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
+       
+       if (ANIM_animdata_filter(ac, &anim_data, filter | ANIMFILTER_SEL, ac->data, ac->datatype) == 0)
+               ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
        
        /* paste keyframes */
        ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode);
@@ -467,15 +495,20 @@ static int actkeys_copy_exec(bContext *C, wmOperator *op)
        /* get editor data */
        if (ANIM_animdata_get_context(C, &ac) == 0)
                return OPERATOR_CANCELLED;
-       
+
        /* copy keyframes */
        if (ac.datatype == ANIMCONT_GPENCIL) {
-               // FIXME...
-               BKE_report(op->reports, RPT_ERROR, "Keyframe pasting is not available for Grease Pencil mode");
+               /* FIXME... */
+               BKE_report(op->reports, RPT_ERROR, "Keyframe pasting is not available for grease pencil mode");
+               return OPERATOR_CANCELLED;
+       }
+       else if (ac.datatype == ANIMCONT_MASK) {
+               /* FIXME... */
+               BKE_report(op->reports, RPT_ERROR, "Keyframe pasting is not available for mask mode");
                return OPERATOR_CANCELLED;
        }
        else {
-               if (copy_action_keys(&ac)) {    
+               if (copy_action_keys(&ac)) {
                        BKE_report(op->reports, RPT_ERROR, "No keyframes copied to keyframes copy/paste buffer");
                        return OPERATOR_CANCELLED;
                }
@@ -514,9 +547,9 @@ static int actkeys_paste_exec(bContext *C, wmOperator *op)
        ac.reports = op->reports;
        
        /* paste keyframes */
-       if (ac.datatype == ANIMCONT_GPENCIL) {
-               // FIXME...
-               BKE_report(op->reports, RPT_ERROR, "Keyframe pasting is not available for Grease Pencil mode");
+       if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) {
+               /* FIXME... */
+               BKE_report(op->reports, RPT_ERROR, "Keyframe pasting is not available for grease pencil or mask mode");
                return OPERATOR_CANCELLED;
        }
        else {
@@ -561,7 +594,7 @@ void ACTION_OT_paste(wmOperatorType *ot)
 static EnumPropertyItem prop_actkeys_insertkey_types[] = {
        {1, "ALL", 0, "All Channels", ""},
        {2, "SEL", 0, "Only Selected Channels", ""},
-       {3, "GROUP", 0, "In Active Group", ""}, // xxx not in all cases
+       {3, "GROUP", 0, "In Active Group", ""},  /* XXX not in all cases */
        {0, NULL, 0, NULL, NULL}
 };
 
@@ -618,7 +651,7 @@ static int actkeys_insertkey_exec(bContext *C, wmOperator *op)
        /* get editor data */
        if (ANIM_animdata_get_context(C, &ac) == 0)
                return OPERATOR_CANCELLED;
-       if (ac.datatype == ANIMCONT_GPENCIL)
+       if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
                return OPERATOR_CANCELLED;
                
        /* what channels to affect? */
@@ -664,7 +697,7 @@ static void duplicate_action_keys(bAnimContext *ac)
        int filter;
        
        /* filter data */
-       if (ac->datatype == ANIMCONT_GPENCIL)
+       if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
                filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
        else
                filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
@@ -674,8 +707,12 @@ static void duplicate_action_keys(bAnimContext *ac)
        for (ale = anim_data.first; ale; ale = ale->next) {
                if (ale->type == ANIMTYPE_FCURVE)
                        duplicate_fcurve_keys((FCurve *)ale->key_data);
+               else if (ale->type == ANIMTYPE_GPLAYER)
+                       ED_gplayer_frames_duplicate((bGPDlayer *)ale->data);
+               else if (ale->type == ANIMTYPE_MASKLAYER)
+                       ED_masklayer_frames_duplicate((MaskLayer *)ale->data);
                else
-                       duplicate_gplayer_frames((bGPDlayer *)ale->data);
+                       BLI_assert(0);
        }
        
        /* free filtered list */
@@ -696,7 +733,7 @@ static int actkeys_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
        duplicate_action_keys(&ac);
        
        /* validate keyframes after editing */
-       if (ac.datatype != ANIMCONT_GPENCIL)
+       if (!ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
                ANIM_editkeyframes_refresh(&ac);
        
        /* set notifier that keyframes have changed */
@@ -737,7 +774,7 @@ static void delete_action_keys(bAnimContext *ac)
        int filter;
        
        /* filter data */
-       if (ac->datatype == ANIMCONT_GPENCIL)
+       if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
                filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
        else
                filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
@@ -745,7 +782,13 @@ static void delete_action_keys(bAnimContext *ac)
        
        /* loop through filtered data and delete selected keys */
        for (ale = anim_data.first; ale; ale = ale->next) {
-               if (ale->type != ANIMTYPE_GPLAYER) {
+               if (ale->type == ANIMTYPE_GPLAYER) {
+                       ED_gplayer_frames_delete((bGPDlayer *)ale->data);
+               }
+               else if (ale->type == ANIMTYPE_MASKLAYER) {
+                       ED_masklayer_frames_delete((MaskLayer *)ale->data);
+               }
+               else {
                        FCurve *fcu = (FCurve *)ale->key_data;
                        AnimData *adt = ale->adt;
                        
@@ -756,8 +799,6 @@ static void delete_action_keys(bAnimContext *ac)
                        if ((fcu->totvert == 0) && (list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE) == 0))
                                ANIM_fcurve_delete_from_animdata(ac, adt, fcu);
                }
-               else
-                       delete_gplayer_frames((bGPDlayer *)ale->data);
        }
        
        /* free filtered list */
@@ -778,7 +819,7 @@ static int actkeys_delete_exec(bContext *C, wmOperator *UNUSED(op))
        delete_action_keys(&ac);
        
        /* validate keyframes after editing */
-       if (ac.datatype != ANIMCONT_GPENCIL)
+       if (!ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
                ANIM_editkeyframes_refresh(&ac);
        
        /* set notifier that keyframes have changed */
@@ -833,7 +874,7 @@ static int actkeys_clean_exec(bContext *C, wmOperator *op)
        /* get editor data */
        if (ANIM_animdata_get_context(C, &ac) == 0)
                return OPERATOR_CANCELLED;
-       if (ac.datatype == ANIMCONT_GPENCIL)
+       if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
                return OPERATOR_PASS_THROUGH;
                
        /* get cleaning threshold */
@@ -900,7 +941,7 @@ static int actkeys_sample_exec(bContext *C, wmOperator *UNUSED(op))
        /* get editor data */
        if (ANIM_animdata_get_context(C, &ac) == 0)
                return OPERATOR_CANCELLED;
-       if (ac.datatype == ANIMCONT_GPENCIL)
+       if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
                return OPERATOR_PASS_THROUGH;
        
        /* sample keyframes */
@@ -941,8 +982,8 @@ void ACTION_OT_sample(wmOperatorType *ot)
 
 /* defines for set extrapolation-type for selected keyframes tool */
 static EnumPropertyItem prop_actkeys_expo_types[] = {
-       {FCURVE_EXTRAPOLATE_CONSTANT, "CONSTANT", 0, "Constant Extrapolation", ""},
-       {FCURVE_EXTRAPOLATE_LINEAR, "LINEAR", 0, "Linear Extrapolation", ""},
+       {FCURVE_EXTRAPOLATE_CONSTANT, "CONSTANT", 0, "Constant Extrapolation", "Values on endpoint keyframes are held"},
+       {FCURVE_EXTRAPOLATE_LINEAR, "LINEAR", 0, "Linear Extrapolation", "Straight-line slope of end segments are extended past the endpoint keyframes"},
        
        {MAKE_CYCLIC_EXPO, "MAKE_CYCLIC", 0, "Make Cyclic (F-Modifier)", "Add Cycles F-Modifier if one doesn't exist already"},
        {CLEAR_CYCLIC_EXPO, "CLEAR_CYCLIC", 0, "Clear Cyclic (F-Modifier)", "Remove Cycles F-Modifier if not needed anymore"},
@@ -975,7 +1016,7 @@ static void setexpo_action_keys(bAnimContext *ac, short mode)
                        if (mode == MAKE_CYCLIC_EXPO) {
                                /* only add if one doesn't exist */
                                if (list_has_suitable_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES, -1) == 0) {
-                                       // TODO: add some more preset versions which set different extrapolation options?
+                                       /* TODO: add some more preset versions which set different extrapolation options? */
                                        add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_CYCLES);
                                }
                        }
@@ -1007,7 +1048,7 @@ static int actkeys_expo_exec(bContext *C, wmOperator *op)
        /* get editor data */
        if (ANIM_animdata_get_context(C, &ac) == 0)
                return OPERATOR_CANCELLED;
-       if (ac.datatype == ANIMCONT_GPENCIL) 
+       if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
                return OPERATOR_PASS_THROUGH;
                
        /* get handle setting mode */
@@ -1078,7 +1119,7 @@ static int actkeys_ipo_exec(bContext *C, wmOperator *op)
        /* get editor data */
        if (ANIM_animdata_get_context(C, &ac) == 0)
                return OPERATOR_CANCELLED;
-       if (ac.datatype == ANIMCONT_GPENCIL) 
+       if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
                return OPERATOR_PASS_THROUGH;
                
        /* get handle setting mode */
@@ -1158,7 +1199,7 @@ static int actkeys_handletype_exec(bContext *C, wmOperator *op)
        /* get editor data */
        if (ANIM_animdata_get_context(C, &ac) == 0)
                return OPERATOR_CANCELLED;
-       if (ac.datatype == ANIMCONT_GPENCIL) 
+       if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
                return OPERATOR_PASS_THROUGH;
                
        /* get handle setting mode */
@@ -1229,7 +1270,7 @@ static int actkeys_keytype_exec(bContext *C, wmOperator *op)
        /* get editor data */
        if (ANIM_animdata_get_context(C, &ac) == 0)
                return OPERATOR_CANCELLED;
-       if (ac.datatype == ANIMCONT_GPENCIL) 
+       if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
                return OPERATOR_PASS_THROUGH;
                
        /* get handle setting mode */
@@ -1271,6 +1312,15 @@ void ACTION_OT_keyframe_type(wmOperatorType *ot)
 
 /* ***************** Jump to Selected Frames Operator *********************** */
 
+static int actkeys_framejump_poll(bContext *C)
+{
+       /* prevent changes during render */
+       if (G.is_rendering)
+               return 0;
+
+       return ED_operator_action_active(C);
+}
+
 /* snap current-frame indicator to 'average time' of selected keyframe */
 static int actkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op))
 {
@@ -1284,7 +1334,7 @@ static int actkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op))
        if (ANIM_animdata_get_context(C, &ac) == 0)
                return OPERATOR_CANCELLED;
        
-       /* init edit data */    
+       /* init edit data */
        /* loop over action data, averaging values */
        filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE /*| ANIMFILTER_CURVESONLY */ | ANIMFILTER_NODUPLIS);
        ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
@@ -1318,13 +1368,13 @@ static int actkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op))
 void ACTION_OT_frame_jump(wmOperatorType *ot)
 {
        /* identifiers */
-       ot->name = "Jump to Frame";
+       ot->name = "Jump to Keyframes";
        ot->idname = "ACTION_OT_frame_jump";
-       ot->description = "Set the current frame to the average frame of the selected keyframes";
+       ot->description = "Set the current frame to the average frame value of selected keyframes";
        
        /* api callbacks */
        ot->exec = actkeys_framejump_exec;
-       ot->poll = ED_operator_action_active;
+       ot->poll = actkeys_framejump_poll;
        
        /* flags */
        ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1334,10 +1384,14 @@ void ACTION_OT_frame_jump(wmOperatorType *ot)
 
 /* defines for snap keyframes tool */
 static EnumPropertyItem prop_actkeys_snap_types[] = {
-       {ACTKEYS_SNAP_CFRA, "CFRA", 0, "Current frame", ""},
-       {ACTKEYS_SNAP_NEAREST_FRAME, "NEAREST_FRAME", 0, "Nearest Frame", ""}, // XXX as single entry?
-       {ACTKEYS_SNAP_NEAREST_SECOND, "NEAREST_SECOND", 0, "Nearest Second", ""}, // XXX as single entry?
-       {ACTKEYS_SNAP_NEAREST_MARKER, "NEAREST_MARKER", 0, "Nearest Marker", ""},
+       {ACTKEYS_SNAP_CFRA, "CFRA", 0, "Current frame",
+        "Snap selected keyframes to the current frame"},
+       {ACTKEYS_SNAP_NEAREST_FRAME, "NEAREST_FRAME", 0, "Nearest Frame",
+        "Snap selected keyframes to the nearest (whole) frame (use to fix accidental sub-frame offsets)"},
+       {ACTKEYS_SNAP_NEAREST_SECOND, "NEAREST_SECOND", 0, "Nearest Second",
+        "Snap selected keyframes to the nearest second"},
+       {ACTKEYS_SNAP_NEAREST_MARKER, "NEAREST_MARKER", 0, "Nearest Marker",
+        "Snap selected keyframes to the nearest marker"},
        {0, NULL, 0, NULL, NULL}
 };
 
@@ -1352,7 +1406,7 @@ static void snap_action_keys(bAnimContext *ac, short mode)
        KeyframeEditFunc edit_cb;
        
        /* filter data */
-       if (ac->datatype == ANIMCONT_GPENCIL)
+       if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
                filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT);
        else
                filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
@@ -1371,15 +1425,20 @@ static void snap_action_keys(bAnimContext *ac, short mode)
        for (ale = anim_data.first; ale; ale = ale->next) {
                AnimData *adt = ANIM_nla_mapping_get(ac, ale);
                
-               if (adt) {
+               if (ale->type == ANIMTYPE_GPLAYER) {
+                       ED_gplayer_snap_frames(ale->data, ac->scene, mode);
+               }
+               else if (ale->type == ANIMTYPE_MASKLAYER) {
+                       ED_masklayer_snap_frames(ale->data, ac->scene, mode);
+               }
+               else if (adt) {
                        ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 1); 
                        ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
                        ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1);
                }
-               //else if (ale->type == ACTTYPE_GPLAYER)
-               //      snap_gplayer_frames(ale->data, mode);
-               else 
+               else {
                        ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+               }
        }
        
        BLI_freelistN(&anim_data);
@@ -1395,11 +1454,7 @@ static int actkeys_snap_exec(bContext *C, wmOperator *op)
        /* get editor data */
        if (ANIM_animdata_get_context(C, &ac) == 0)
                return OPERATOR_CANCELLED;
-               
-       // XXX...
-       if (ac.datatype == ANIMCONT_GPENCIL)
-               return OPERATOR_PASS_THROUGH;
-               
+       
        /* get snapping mode */
        mode = RNA_enum_get(op->ptr, "type");
        
@@ -1407,7 +1462,8 @@ static int actkeys_snap_exec(bContext *C, wmOperator *op)
        snap_action_keys(&ac, mode);
        
        /* validate keyframes after editing */
-       ANIM_editkeyframes_refresh(&ac);
+       if (!ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
+               ANIM_editkeyframes_refresh(&ac);
        
        /* set notifier that keyframes have changed */
        WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
@@ -1438,9 +1494,12 @@ void ACTION_OT_snap(wmOperatorType *ot)
 
 /* defines for mirror keyframes tool */
 static EnumPropertyItem prop_actkeys_mirror_types[] = {
-       {ACTKEYS_MIRROR_CFRA, "CFRA", 0, "By Times over Current frame", ""},
-       {ACTKEYS_MIRROR_XAXIS, "XAXIS", 0, "By Values over Value=0", ""},
-       {ACTKEYS_MIRROR_MARKER, "MARKER", 0, "By Times over First Selected Marker", ""},
+       {ACTKEYS_MIRROR_CFRA, "CFRA", 0, "By Times over Current frame",
+        "Flip times of selected keyframes using the current frame as the mirror line"},
+       {ACTKEYS_MIRROR_XAXIS, "XAXIS", 0, "By Values over Value=0",
+        "Flip values of selected keyframes (i.e. negative values become positive, and vice versa)"},
+       {ACTKEYS_MIRROR_MARKER, "MARKER", 0, "By Times over First Selected Marker",
+        "Flip times of selected keyframes using the first selected marker as the reference point"},
        {0, NULL, 0, NULL, NULL}
 };
 
@@ -1460,14 +1519,10 @@ static void mirror_action_keys(bAnimContext *ac, short mode)
        ked.scene = ac->scene;
        
        /* for 'first selected marker' mode, need to find first selected marker first! */
-       // XXX should this be made into a helper func in the API?
+       /* XXX should this be made into a helper func in the API? */
        if (mode == ACTKEYS_MIRROR_MARKER) {
-               TimeMarker *marker = NULL;
-               
-               /* find first selected marker */
-               marker = ED_markers_get_first_selected(ac->markers);
+               TimeMarker *marker = ED_markers_get_first_selected(ac->markers);
                
-               /* store marker's time (if available) */
                if (marker)
                        ked.f1 = (float)marker->frame;
                else
@@ -1475,7 +1530,7 @@ static void mirror_action_keys(bAnimContext *ac, short mode)
        }
        
        /* filter data */
-       if (ac->datatype == ANIMCONT_GPENCIL)
+       if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
                filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS);
        else
                filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS);
@@ -1510,8 +1565,8 @@ static int actkeys_mirror_exec(bContext *C, wmOperator *op)
        if (ANIM_animdata_get_context(C, &ac) == 0)
                return OPERATOR_CANCELLED;
                
-       // XXX...
-       if (ac.datatype == ANIMCONT_GPENCIL)
+       /* XXX... */
+       if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK))
                return OPERATOR_PASS_THROUGH;
                
        /* get mirroring mode */