DopeSheet and Graph Editors: Select More/Less Operators
authorJoshua Leung <aligorith@gmail.com>
Sun, 7 Feb 2010 11:50:03 +0000 (11:50 +0000)
committerJoshua Leung <aligorith@gmail.com>
Sun, 7 Feb 2010 11:50:03 +0000 (11:50 +0000)
This commit introduces the Select More/Less Operators (Ctrl +/-) for keyframes. This works like the ones for curves, by only selecting/deselecting keyframes lying in the same F-Curve. Inter F-Curve selection is not done by this operator. That is the job for another one.

This is especially useful for F-Curves set in the 0-1-0 pattern (i.e. 3 keyframes forming localised peaks), where the peaks can be selected by clicking on them individually, and immediately surrounding '0' values are selected too using "Select More".

release/scripts/ui/space_dopesheet.py
release/scripts/ui/space_graph.py
source/blender/editors/animation/keyframes_edit.c
source/blender/editors/include/ED_keyframes_edit.h
source/blender/editors/space_action/action_intern.h
source/blender/editors/space_action/action_ops.c
source/blender/editors/space_action/action_select.c
source/blender/editors/space_graph/graph_intern.h
source/blender/editors/space_graph/graph_ops.c
source/blender/editors/space_graph/graph_select.c

index 4416f3128a87530a06f6e5401828243fd991f0c9..2bd0300c465949678659fbdabc00f3c1ebf27271 100644 (file)
@@ -119,6 +119,10 @@ class DOPESHEET_MT_select(bpy.types.Menu):
         layout.operator("action.select_column", text="Columns on Selected Markers").mode = 'MARKERS_COLUMN'
         layout.operator("action.select_column", text="Between Selected Markers").mode = 'MARKERS_BETWEEN'
 
+        layout.separator()
+        layout.operator("action.select_more")
+        layout.operator("action.select_less")
+
 
 class DOPESHEET_MT_channel(bpy.types.Menu):
     bl_label = "Channel"
index e4752877c2f082d2efc5335184f5ed96e2746c5f..511ab06d0dad56191d9ece3a5223e07bb7a619aa 100644 (file)
@@ -122,6 +122,10 @@ class GRAPH_MT_select(bpy.types.Menu):
         layout.operator("graph.select_column", text="Columns on Selected Markers").mode = 'MARKERS_COLUMN'
         layout.operator("graph.select_column", text="Between Selected Markers").mode = 'MARKERS_BETWEEN'
 
+        layout.separator()
+        layout.operator("graph.select_more")
+        layout.operator("graph.select_less")
+
 
 class GRAPH_MT_channel(bpy.types.Menu):
     bl_label = "Channel"
index 912b51a0b4dca5e21c81aee15f3bde6a0dfdb192..c1b2e474d522d42ce04833c34f7ec4840bba24f5 100644 (file)
 short ANIM_fcurve_keys_bezier_loop(BeztEditData *bed, FCurve *fcu, BeztEditFunc bezt_ok, BeztEditFunc bezt_cb, FcuEditFunc fcu_cb) 
 {
     BezTriple *bezt;
-       int b;
        
        /* sanity check */
        if (ELEM(NULL, fcu, fcu->bezt))
                return 0;
        
+       /* set the F-Curve into the editdata so that it can be accessed */
+       bed->fcu= fcu;
+       bed->curIndex= 0;
+       
        /* if function to apply to bezier curves is set, then loop through executing it on beztriples */
     if (bezt_cb) {
                /* if there's a validation func, include that check in the loop 
                 * (this is should be more efficient than checking for it in every loop)
                 */
                if (bezt_ok) {
-                       for (b=0, bezt=fcu->bezt; b < fcu->totvert; b++, bezt++) {
+                       for (bed->curIndex=0, bezt=fcu->bezt; bed->curIndex < fcu->totvert; bed->curIndex++, bezt++) {
                                /* Only operate on this BezTriple if it fullfills the criteria of the validation func */
                                if (bezt_ok(bed, bezt)) {
                                        /* Exit with return-code '1' if function returns positive
@@ -113,7 +116,7 @@ short ANIM_fcurve_keys_bezier_loop(BeztEditData *bed, FCurve *fcu, BeztEditFunc
                        }
                }
                else {
-                       for (b=0, bezt=fcu->bezt; b < fcu->totvert; b++, bezt++) {
+                       for (bed->curIndex=0, bezt=fcu->bezt; bed->curIndex < fcu->totvert; bed->curIndex++, bezt++) {
                                /* Exit with return-code '1' if function returns positive
                                 * This is useful if finding if some BezTriple satisfies a condition.
                                 */
@@ -121,6 +124,10 @@ short ANIM_fcurve_keys_bezier_loop(BeztEditData *bed, FCurve *fcu, BeztEditFunc
                        }
                }
     }
+       
+       /* unset the F-Curve from the editdata now that it's done */
+       bed->fcu= NULL;
+       bed->curIndex= 0;
 
     /* if fcu_cb (F-Curve post-editing callback) has been specified then execute it */
     if (fcu_cb)
@@ -786,6 +793,8 @@ static short set_bezier_vector(BeztEditData *bed, BezTriple *bezt)
 }
 
 /* Queries if the handle should be set to 'free' or 'align' */
+// NOTE: this was used for the 'toggle free/align' option
+//             currently this isn't used, but may be restored later
 static short bezier_isfree(BeztEditData *bed, BezTriple *bezt) 
 {
        if ((bezt->f1 & SELECT) && (bezt->h1)) return 1;
@@ -951,3 +960,123 @@ BeztEditFunc ANIM_editkeyframes_select(short selectmode)
                        return select_bezier_add;
        }
 }
+
+/* ******************************************* */
+/* Selection Maps */
+
+/* Selection maps are simply fancy names for char arrays that store on/off
+ * info for whether the selection status. The main purpose for these is to
+ * allow extra info to be tagged to the keyframes without influencing their
+ * values or having to be removed later.
+ */
+
+/* ----------- */
+
+static short selmap_build_bezier_more(BeztEditData *bed, BezTriple *bezt)
+{
+       FCurve *fcu= bed->fcu;
+       char *map= bed->data;
+       int i= bed->curIndex;
+       
+       /* if current is selected, just make sure it stays this way */
+       if (BEZSELECTED(bezt)) {
+               map[i]= 1;
+               return 0;
+       }
+       
+       /* if previous is selected, that means that selection should extend across */
+       if (i > 0) {
+               BezTriple *prev= bezt - 1;
+               
+               if (BEZSELECTED(prev)) {
+                       map[i]= 1;
+                       return 0;
+               }
+       }
+       
+       /* if next is selected, that means that selection should extend across */
+       if (i < (fcu->totvert-1)) {
+               BezTriple *next= bezt + 1;
+               
+               if (BEZSELECTED(next)) {
+                       map[i]= 1;
+                       return 0;
+               }
+       }
+       
+       return 0;
+}
+
+static short selmap_build_bezier_less(BeztEditData *bed, BezTriple *bezt)
+{
+       FCurve *fcu= bed->fcu;
+       char *map= bed->data;
+       int i= bed->curIndex;
+       
+       /* if current is selected, check the left/right keyframes
+        * since it might need to be deselected (but otherwise no)
+        */
+       if (BEZSELECTED(bezt)) {
+               /* if previous is not selected, we're on the tip of an iceberg */
+               if (i > 0) {
+                       BezTriple *prev= bezt - 1;
+                       
+                       if (BEZSELECTED(prev) == 0)
+                               return 0;
+               }
+               else if (i == 0) {
+                       /* current keyframe is selected at an endpoint, so should get deselected */
+                       return 0;
+               }
+               
+               /* if next is not selected, we're on the tip of an iceberg */
+               if (i < (fcu->totvert-1)) {
+                       BezTriple *next= bezt + 1;
+                       
+                       if (BEZSELECTED(next) == 0)
+                               return 0;
+               }
+               else if (i == (fcu->totvert-1)) {
+                       /* current keyframe is selected at an endpoint, so should get deselected */
+                       return 0;
+               }
+               
+               /* if we're still here, that means that keyframe should remain untouched */
+               map[i]= 1;
+       }
+       
+       return 0;
+}
+
+/* Get callback for building selection map */
+BeztEditFunc ANIM_editkeyframes_buildselmap(short mode)
+{
+       switch (mode) {
+               case SELMAP_LESS: /* less */
+                       return selmap_build_bezier_less;
+               
+               case SELMAP_MORE: /* more */
+               default:
+                       return selmap_build_bezier_more;
+       }
+}
+
+/* ----------- */
+
+/* flush selection map values to the given beztriple */
+short bezt_selmap_flush(BeztEditData *bed, BezTriple *bezt)
+{
+       char *map= bed->data;
+       short on= map[bed->curIndex];
+       
+       /* select or deselect based on whether the map allows it or not */
+       if (on) {
+               BEZ_SEL(bezt);
+       }
+       else {
+               BEZ_DESEL(bezt);
+       }
+       
+       return 0;
+}
+
index 9e29d747d4a767cfeb98f58497cf9e40f1f52305..675dde0ae0d16f28ff279196c808536a6b102fc5 100644 (file)
@@ -58,7 +58,7 @@ typedef enum eEditKeyframes_Validate {
 
 /* ------------ */
 
-/* select tools */
+/* select modes */
 typedef enum eEditKeyframes_Select {
        SELECT_REPLACE  =       (1<<0),
        SELECT_ADD              =       (1<<1),
@@ -66,6 +66,12 @@ typedef enum eEditKeyframes_Select {
        SELECT_INVERT   =       (1<<4),
 } eEditKeyframes_Select;
 
+/* "selection map" building modes */
+typedef enum eEditKeyframes_SelMap {
+       SELMAP_MORE     = 0,
+       SELMAP_LESS,
+} eEditKeyframes_SelMap;
+
 /* snapping tools */
 typedef enum eEditKeyframes_Snap {
        SNAP_KEYS_CURFRAME = 1,
@@ -91,11 +97,16 @@ typedef enum eEditKeyframes_Mirror {
 /* --- Generic Properties for Bezier Edit Tools ----- */
 
 typedef struct BeztEditData {
+               /* generic properties/data access */
        ListBase list;                          /* temp list for storing custom list of data to check */
        struct Scene *scene;            /* pointer to current scene - many tools need access to cfra/etc.  */
        void *data;                                     /* pointer to custom data - usually 'Object' but also 'rectf', but could be other types too */
        float f1, f2;                           /* storage of times/values as 'decimals' */
        int i1, i2;                                     /* storage of times/values/flags as 'whole' numbers */
+       
+               /* current iteration data */
+       struct FCurve *fcu;                     /* F-Curve that is being iterated over */
+       int curIndex;                           /* index of current keyframe being iterated over */
 } BeztEditData;
 
 /* ------- Function Pointer Typedefs ---------------- */
@@ -143,6 +154,18 @@ BeztEditFunc ANIM_editkeyframes_handles(short mode);
 BeztEditFunc ANIM_editkeyframes_ipo(short mode);
 BeztEditFunc ANIM_editkeyframes_keytype(short mode);
 
+/* -------- BezTriple Callbacks (Selection Map) ---------- */
+
+/* Get a callback to populate the selection settings map  
+ * requires: bed->custom = char[] of length fcurve->totvert 
+ */
+BeztEditFunc ANIM_editkeyframes_buildselmap(short mode);
+
+/* Change the selection status of the keyframe based on the map entry for this vert
+ * requires: bed->custom = char[] of length fcurve->totvert
+ */
+short bezt_selmap_flush(BeztEditData *bed, struct BezTriple *bezt);
+
 /* ----------- BezTriple Callback (Assorted Utilities) ---------- */
 
 /* used to calculate the the average location of all relevant BezTriples by summing their locations */
index 166004a5742017141daaa5261acd0fe5710bc00c..cf708bb7140c40f19978bf6122c09c83568ba094 100644 (file)
@@ -56,6 +56,8 @@ void action_header_buttons(const struct bContext *C, struct ARegion *ar);
 void ACTION_OT_select_all_toggle(struct wmOperatorType *ot);
 void ACTION_OT_select_border(struct wmOperatorType *ot);
 void ACTION_OT_select_column(struct wmOperatorType *ot);
+void ACTION_OT_select_more(struct wmOperatorType *ot);
+void ACTION_OT_select_less(struct wmOperatorType *ot);
 void ACTION_OT_clickselect(struct wmOperatorType *ot);
 
 /* defines for left-right select tool */
index 7792247fe815ae0ba6dca53a343295d758662e46..a36021553c86063a4406134fbfe83e7f0412d5e6 100644 (file)
@@ -67,6 +67,8 @@ void action_operatortypes(void)
        WM_operatortype_append(ACTION_OT_select_all_toggle);
        WM_operatortype_append(ACTION_OT_select_border);
        WM_operatortype_append(ACTION_OT_select_column);
+       WM_operatortype_append(ACTION_OT_select_more);
+       WM_operatortype_append(ACTION_OT_select_less);
        
                /* editing */
        WM_operatortype_append(ACTION_OT_snap);
@@ -122,6 +124,11 @@ static void action_keymap_keyframes (wmKeyConfig *keyconf, wmKeyMap *keymap)
        RNA_enum_set(WM_keymap_add_item(keymap, "ACTION_OT_select_column", KKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", ACTKEYS_COLUMNSEL_MARKERS_COLUMN);
        RNA_enum_set(WM_keymap_add_item(keymap, "ACTION_OT_select_column", KKEY, KM_PRESS, KM_ALT, 0)->ptr, "mode", ACTKEYS_COLUMNSEL_MARKERS_BETWEEN);
        
+               /* select more/less */
+       WM_keymap_add_item(keymap, "ACTION_OT_select_more", PADPLUSKEY, KM_PRESS, KM_CTRL, 0);
+       WM_keymap_add_item(keymap, "ACTION_OT_select_less", PADMINUS, KM_PRESS, KM_CTRL, 0);
+       
+       
        /* action_edit.c */
                /* snap - current frame to selected keys */
                // TODO: maybe since this is called jump, we're better to have it on <something>-J?
index be74da0718e87479f14c36f05f7aa55d39446554..1939057b654f501471e5fc04e84b8fa3f6cc0ce0 100644 (file)
@@ -574,6 +574,118 @@ void ACTION_OT_select_column (wmOperatorType *ot)
        RNA_def_enum(ot->srna, "mode", prop_column_select_types, 0, "Mode", "");
 }
 
+/* ******************** Select More/Less Operators *********************** */
+
+/* Common code to perform selection */
+static void select_moreless_action_keys (bAnimContext *ac, short mode)
+{
+       ListBase anim_data= {NULL, NULL};
+       bAnimListElem *ale;
+       int filter;
+       
+       BeztEditData bed;
+       BeztEditFunc build_cb;
+       
+       
+       /* init selmap building data */
+       build_cb= ANIM_editkeyframes_buildselmap(mode);
+       memset(&bed, 0, sizeof(BeztEditData)); 
+       
+       /* loop through all of the keys and select additional keyframes based on these */
+       filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVESONLY);
+       ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+       
+       for (ale= anim_data.first; ale; ale= ale->next) {
+               FCurve *fcu= (FCurve *)ale->key_data;
+               
+               /* only continue if F-Curve has keyframes */
+               if (fcu->bezt == NULL)
+                       continue;
+               
+               /* build up map of whether F-Curve's keyframes should be selected or not */
+               bed.data= MEM_callocN(fcu->totvert, "selmap actEdit more");
+               ANIM_fcurve_keys_bezier_loop(&bed, fcu, NULL, build_cb, NULL);
+               
+               /* based on this map, adjust the selection status of the keyframes */
+               ANIM_fcurve_keys_bezier_loop(&bed, fcu, NULL, bezt_selmap_flush, NULL);
+               
+               /* free the selmap used here */
+               MEM_freeN(bed.data);
+               bed.data= NULL;
+       }
+       
+       /* Cleanup */
+       BLI_freelistN(&anim_data);
+}
+
+/* ----------------- */
+
+static int actkeys_select_more_exec (bContext *C, wmOperator *op)
+{
+       bAnimContext ac;
+       
+       /* get editor data */
+       if (ANIM_animdata_get_context(C, &ac) == 0)
+               return OPERATOR_CANCELLED;
+       
+       /* perform select changes */
+       select_moreless_action_keys(&ac, SELMAP_MORE);
+       
+       /* set notifier that keyframe selection has changed */
+       WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL);
+       
+       return OPERATOR_FINISHED;
+}
+
+void ACTION_OT_select_more (wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Select More";
+       ot->idname= "ACTION_OT_select_more";
+       ot->description = "Select keyframes beside already selected ones.";
+       
+       /* api callbacks */
+       ot->exec= actkeys_select_more_exec;
+       ot->poll= ED_operator_action_active;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
+}
+
+/* ----------------- */
+
+static int actkeys_select_less_exec (bContext *C, wmOperator *op)
+{
+       bAnimContext ac;
+       
+       /* get editor data */
+       if (ANIM_animdata_get_context(C, &ac) == 0)
+               return OPERATOR_CANCELLED;
+       
+       /* perform select changes */
+       select_moreless_action_keys(&ac, SELMAP_LESS);
+       
+       /* set notifier that keyframe selection has changed */
+       WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL);
+       
+       return OPERATOR_FINISHED;
+}
+
+void ACTION_OT_select_less (wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Select Less";
+       ot->idname= "ACTION_OT_select_less";
+       ot->description = "Deselect keyframes on ends of selection islands.";
+       
+       /* api callbacks */
+       ot->exec= actkeys_select_less_exec;
+       ot->poll= ED_operator_action_active;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
+}
+
 /* ******************** Mouse-Click Select Operator *********************** */
 /* This operator works in one of three ways:
  *     - 1) keyframe under mouse - no special modifiers
index ec6f38c93b709e4f4145aaf00a4345a806d57bb1..a42944f0b35ba143796f9f800cd8b8ad6dc634d9 100644 (file)
@@ -63,6 +63,8 @@ void graph_header_buttons(const bContext *C, struct ARegion *ar);
 void GRAPH_OT_select_all_toggle(struct wmOperatorType *ot);
 void GRAPH_OT_select_border(struct wmOperatorType *ot);
 void GRAPH_OT_select_column(struct wmOperatorType *ot);
+void GRAPH_OT_select_more(struct wmOperatorType *ot);
+void GRAPH_OT_select_less(struct wmOperatorType *ot);
 void GRAPH_OT_clickselect(struct wmOperatorType *ot);
 
 /* defines for left-right select tool */
index 7be2d7b3e4b661a36832a818abecd727acdcfa73..648a162890d57ebc66a5c068626c3d4f21744bc6 100644 (file)
@@ -237,6 +237,8 @@ void graphedit_operatortypes(void)
        WM_operatortype_append(GRAPH_OT_select_all_toggle);
        WM_operatortype_append(GRAPH_OT_select_border);
        WM_operatortype_append(GRAPH_OT_select_column);
+       WM_operatortype_append(GRAPH_OT_select_more);
+       WM_operatortype_append(GRAPH_OT_select_less);
        
                /* editing */
        WM_operatortype_append(GRAPH_OT_snap);
@@ -310,6 +312,10 @@ static void graphedit_keymap_keyframes (wmKeyConfig *keyconf, wmKeyMap *keymap)
        RNA_enum_set(WM_keymap_add_item(keymap, "GRAPH_OT_select_column", KKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "mode", GRAPHKEYS_COLUMNSEL_MARKERS_COLUMN);
        RNA_enum_set(WM_keymap_add_item(keymap, "GRAPH_OT_select_column", KKEY, KM_PRESS, KM_ALT, 0)->ptr, "mode", GRAPHKEYS_COLUMNSEL_MARKERS_BETWEEN);
        
+               /* select more/less */
+       WM_keymap_add_item(keymap, "GRAPH_OT_select_more", PADPLUSKEY, KM_PRESS, KM_CTRL, 0);
+       WM_keymap_add_item(keymap, "GRAPH_OT_select_less", PADMINUS, KM_PRESS, KM_CTRL, 0);
+       
        
        /* graph_edit.c */
                /* snap - current frame to selected keys */
index b90d5a48d9a6d4f568b46936cda34a628def13d6..872b688bea14e863ed66575c41774c7412b16727 100644 (file)
@@ -549,6 +549,118 @@ void GRAPH_OT_select_column (wmOperatorType *ot)
        RNA_def_enum(ot->srna, "mode", prop_column_select_types, 0, "Mode", "");
 }
 
+/* ******************** Select More/Less Operators *********************** */
+
+/* Common code to perform selection */
+static void select_moreless_graph_keys (bAnimContext *ac, short mode)
+{
+       ListBase anim_data= {NULL, NULL};
+       bAnimListElem *ale;
+       int filter;
+       
+       BeztEditData bed;
+       BeztEditFunc build_cb;
+       
+       
+       /* init selmap building data */
+       build_cb= ANIM_editkeyframes_buildselmap(mode);
+       memset(&bed, 0, sizeof(BeztEditData)); 
+       
+       /* loop through all of the keys and select additional keyframes based on these */
+       filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVEVISIBLE | ANIMFILTER_CURVESONLY);
+       ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+       
+       for (ale= anim_data.first; ale; ale= ale->next) {
+               FCurve *fcu= (FCurve *)ale->key_data;
+               
+               /* only continue if F-Curve has keyframes */
+               if (fcu->bezt == NULL)
+                       continue;
+               
+               /* build up map of whether F-Curve's keyframes should be selected or not */
+               bed.data= MEM_callocN(fcu->totvert, "selmap graphEdit");
+               ANIM_fcurve_keys_bezier_loop(&bed, fcu, NULL, build_cb, NULL);
+               
+               /* based on this map, adjust the selection status of the keyframes */
+               ANIM_fcurve_keys_bezier_loop(&bed, fcu, NULL, bezt_selmap_flush, NULL);
+               
+               /* free the selmap used here */
+               MEM_freeN(bed.data);
+               bed.data= NULL;
+       }
+       
+       /* Cleanup */
+       BLI_freelistN(&anim_data);
+}
+
+/* ----------------- */
+
+static int graphkeys_select_more_exec (bContext *C, wmOperator *op)
+{
+       bAnimContext ac;
+       
+       /* get editor data */
+       if (ANIM_animdata_get_context(C, &ac) == 0)
+               return OPERATOR_CANCELLED;
+       
+       /* perform select changes */
+       select_moreless_graph_keys(&ac, SELMAP_MORE);
+       
+       /* set notifier that keyframe selection has changed */
+       WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL);
+       
+       return OPERATOR_FINISHED;
+}
+
+void GRAPH_OT_select_more (wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Select More";
+       ot->idname= "GRAPH_OT_select_more";
+       ot->description = "Select keyframes beside already selected ones.";
+       
+       /* api callbacks */
+       ot->exec= graphkeys_select_more_exec;
+       ot->poll= graphop_visible_keyframes_poll;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
+}
+
+/* ----------------- */
+
+static int graphkeys_select_less_exec (bContext *C, wmOperator *op)
+{
+       bAnimContext ac;
+       
+       /* get editor data */
+       if (ANIM_animdata_get_context(C, &ac) == 0)
+               return OPERATOR_CANCELLED;
+       
+       /* perform select changes */
+       select_moreless_graph_keys(&ac, SELMAP_LESS);
+       
+       /* set notifier that keyframe selection has changed */
+       WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_SELECT, NULL);
+       
+       return OPERATOR_FINISHED;
+}
+
+void GRAPH_OT_select_less (wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name = "Select Less";
+       ot->idname= "GRAPH_OT_select_less";
+       ot->description = "Deselect keyframes on ends of selection islands.";
+       
+       /* api callbacks */
+       ot->exec= graphkeys_select_less_exec;
+       ot->poll= graphop_visible_keyframes_poll;
+       
+       /* flags */
+       ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/;
+}
+
 /* ******************** Mouse-Click Select Operator *********************** */
 /* This operator works in one of three ways:
  *     - 1) keyframe under mouse - no special modifiers