DopeSheet and Graph Editors: Select More/Less Operators

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".
This commit is contained in:
Joshua Leung 2010-02-07 11:50:03 +00:00
parent 7d2c4384e2
commit 20fb4e3367
10 changed files with 405 additions and 4 deletions

@ -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"

@ -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"

@ -90,19 +90,22 @@
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;
}

@ -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 */

@ -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 */

@ -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?

@ -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

@ -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 */

@ -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 */

@ -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