From 4de1c0ff3eb557872376223e4ebb642c66f7c1be Mon Sep 17 00:00:00 2001 From: RedMser Date: Sat, 30 Apr 2022 16:10:46 +0200 Subject: [PATCH 1/9] Animation: initial select grouped implementation --- .../keyconfig/keymap_data/blender_default.py | 1 + .../keymap_data/industry_compatible_data.py | 1 + scripts/startup/bl_ui/space_dopesheet.py | 1 + .../editors/space_action/action_intern.h | 1 + .../blender/editors/space_action/action_ops.c | 1 + .../editors/space_action/action_select.c | 297 ++++++++++++++++++ 6 files changed, 302 insertions(+) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index 60f8ea6d9e1..85b8b08cdf8 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2430,6 +2430,7 @@ def km_dopesheet(params): ("action.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), ("action.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), ("action.select_linked", {"type": 'L', "value": 'PRESS'}, None), + ("action.select_grouped", {"type": 'G', "value": 'PRESS', "shift": True}, None), ("action.frame_jump", {"type": 'G', "value": 'PRESS', "ctrl": True}, None), ( op_menu_pie("DOPESHEET_MT_snap_pie", {"type": 'S', "value": 'PRESS', "shift": True}) diff --git a/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 98cee34519f..af738e398d6 100644 --- a/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -1393,6 +1393,7 @@ def km_dopesheet(params): ("action.select_more", {"type": 'UP_ARROW', "value": 'PRESS', "ctrl": True, "repeat": True}, None), ("action.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS', "ctrl": True, "repeat": True}, None), ("action.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, None), + ("action.select_grouped", {"type": 'G', "value": 'PRESS', "shift": True}, None), ("action.frame_jump", {"type": 'G', "value": 'PRESS', "ctrl": True}, None), ("wm.context_menu_enum", {"type": 'X', "value": 'PRESS'}, {"properties": [("data_path", 'space_data.auto_snap')]}), diff --git a/scripts/startup/bl_ui/space_dopesheet.py b/scripts/startup/bl_ui/space_dopesheet.py index eed77d93de9..d879471a079 100644 --- a/scripts/startup/bl_ui/space_dopesheet.py +++ b/scripts/startup/bl_ui/space_dopesheet.py @@ -415,6 +415,7 @@ class DOPESHEET_MT_select(Menu): layout.separator() layout.operator("action.select_linked") + layout.operator_menu_enum("action.select_grouped", "type", text="Select Grouped") class DOPESHEET_MT_marker(Menu): diff --git a/source/blender/editors/space_action/action_intern.h b/source/blender/editors/space_action/action_intern.h index c227651d1d7..ddb7a5c2b70 100644 --- a/source/blender/editors/space_action/action_intern.h +++ b/source/blender/editors/space_action/action_intern.h @@ -51,6 +51,7 @@ void ACTION_OT_select_linked(struct wmOperatorType *ot); void ACTION_OT_select_more(struct wmOperatorType *ot); void ACTION_OT_select_less(struct wmOperatorType *ot); void ACTION_OT_select_leftright(struct wmOperatorType *ot); +void ACTION_OT_select_grouped(struct wmOperatorType *ot); void ACTION_OT_clickselect(struct wmOperatorType *ot); /* defines for left-right select tool */ diff --git a/source/blender/editors/space_action/action_ops.c b/source/blender/editors/space_action/action_ops.c index b2af86959c8..79e36258f9b 100644 --- a/source/blender/editors/space_action/action_ops.c +++ b/source/blender/editors/space_action/action_ops.c @@ -36,6 +36,7 @@ void action_operatortypes(void) WM_operatortype_append(ACTION_OT_select_more); WM_operatortype_append(ACTION_OT_select_less); WM_operatortype_append(ACTION_OT_select_leftright); + WM_operatortype_append(ACTION_OT_select_grouped); /* editing */ WM_operatortype_append(ACTION_OT_snap); diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 0c416712a0e..03234c1f66a 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -1906,3 +1906,300 @@ void ACTION_OT_clickselect(wmOperatorType *ot) } /* ************************************************************************** */ + +/* Select Grouped Operator */ + +enum { + ACTKEYS_SELECT_GROUP_CHANNEL_TYPE, + ACTKEYS_SELECT_GROUP_KEY_TYPE, + ACTKEYS_SELECT_GROUP_INTERPOLATION_TYPE, + ACTKEYS_SELECT_GROUP_HANDLE_TYPE, + ACTKEYS_SELECT_GROUP_INTERPOLATION_HANDLE_TYPE, + ACTKEYS_SELECT_GROUP_MODIFIERS, + ACTKEYS_SELECT_GROUP_DATABLOCK, +}; + +static const EnumPropertyItem actkeys_prop_select_grouped_types[] = { + {ACTKEYS_SELECT_GROUP_CHANNEL_TYPE, + "CHANNEL_TYPE", + 0, + "Channel Type", + "All keyframes of same type of channel (X Location, etc.)"}, + {ACTKEYS_SELECT_GROUP_KEY_TYPE, + "KEY_TYPE", + 0, + "Key Type", + "All keyframes of the same type (breakdown, extreme, etc.)"}, + {ACTKEYS_SELECT_GROUP_INTERPOLATION_TYPE, + "INTERPOLATION_TYPE", + 0, + "Interpolation Mode", + "All keyframes of the same interpolation mode (constant, bezier, etc.)"}, + {ACTKEYS_SELECT_GROUP_HANDLE_TYPE, + "HANDLE_TYPE", + 0, + "Handle Type", + "All keyframes of the same handle type (vector, automatic, etc.)"}, + {ACTKEYS_SELECT_GROUP_MODIFIERS, + "MODIFIERS", + 0, + "Modifiers", + "All keyframes on each channel that has the same modifiers"}, + {ACTKEYS_SELECT_GROUP_DATABLOCK, + "DATABLOCK", + 0, + "Datablock", + "All keyframes that are part of the same datablock"}, + {0, NULL, 0, NULL, NULL}, +}; + +/* ACTKEYS_SELECT_GROUP_DATABLOCK */ + +static short select_grouped_datablock(KeyframeEditData *ked, struct BezTriple *bezt) +{ + /* Actual filtering is done via the anim data list. */ + return KEYFRAME_OK_ALL; +} + +static short select_grouped_active_datablock(KeyframeEditData *ked, struct BezTriple *bezt) +{ + /* Stop after the first selected bezt was found. */ + return 1; +} + +/* ACTKEYS_SELECT_GROUP_HANDLE_TYPE */ + +static short select_grouped_handle_type(KeyframeEditData *ked, struct BezTriple *bezt) +{ + /* TODO(redmser): Should this compare left and right handle individually? In dopesheet, you can't + * select individual handles to clarify the intent. */ + char handle = *(char *)ked->data; + if (bezt->h1 == handle && bezt->h2 == handle) { + return KEYFRAME_OK_ALL; + } + return 0; +} + +static short select_grouped_active_handle_type(KeyframeEditData *ked, struct BezTriple *bezt) +{ + if (bezt->h1 == bezt->h2) { + *((uint8_t *)ked->data) = bezt->h1; + } + return 1; +} + +/* ACTKEYS_SELECT_GROUP_INTERPOLATION_TYPE */ + +static short select_grouped_interpolation_type(KeyframeEditData *ked, struct BezTriple *bezt) +{ + char ipo = *(char *)ked->data; + if (bezt->ipo == ipo) { + return KEYFRAME_OK_ALL; + } + return 0; +} + +static short select_grouped_active_interpolation_type(KeyframeEditData *ked, + struct BezTriple *bezt) +{ + *((char *)ked->data) = bezt->ipo; + return 1; +} + +/* ACTKEYS_SELECT_GROUP_KEY_TYPE */ + +static short select_grouped_key_type(KeyframeEditData *ked, struct BezTriple *bezt) +{ + char key_type = *(char *)ked->data; + if (BEZKEYTYPE(bezt) == key_type) { + return KEYFRAME_OK_ALL; + } + return 0; +} + +static short select_grouped_active_key_type(KeyframeEditData *ked, struct BezTriple *bezt) +{ + *((char *)ked->data) = BEZKEYTYPE(bezt); + return 1; +} + +/* TODO(redmser): These filters could all allow multi-selection. Instead of setting ked->data, add + * the filter (if unique) to ked->list and do an "includes" check instead. */ + +static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, + KeyframeEditData *ked, + int type) +{ + KeyframeEditFunc ok_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED); + + bAnimListElem *ale; + for (ale = anim_data->first; ale; ale = ale->next) { + /* TODO(redmser): Other types (GP, mask, etc.). */ + if (ale->datatype != ALE_FCURVE) { + continue; + } + + /* Only continue if F-Curve has keyframes. */ + FCurve *fcu = (FCurve *)ale->key_data; + if (fcu->bezt == NULL) { + continue; + } + + /* Find first selected keyframe for context info. */ + switch (type) { + case ACTKEYS_SELECT_GROUP_CHANNEL_TYPE: { + /* TODO(redmser): Channel type (by name?). */ + break; + } + case ACTKEYS_SELECT_GROUP_HANDLE_TYPE: { + char handle = 0; + ked->data = &handle; + short result = ANIM_fcurve_keyframes_loop( + ked, fcu, ok_cb, select_grouped_active_handle_type, NULL); + if (result == 1) { + return select_grouped_handle_type; + } + break; + } + case ACTKEYS_SELECT_GROUP_INTERPOLATION_TYPE: { + char ipo = 0; + ked->data = &ipo; + short result = ANIM_fcurve_keyframes_loop( + ked, fcu, ok_cb, select_grouped_active_interpolation_type, NULL); + if (result == 1) { + return select_grouped_interpolation_type; + } + break; + } + case ACTKEYS_SELECT_GROUP_KEY_TYPE: { + char key_type = 0; + ked->data = &key_type; + short result = ANIM_fcurve_keyframes_loop( + ked, fcu, ok_cb, select_grouped_active_key_type, NULL); + if (result == 1) { + return select_grouped_key_type; + } + break; + } + case ACTKEYS_SELECT_GROUP_MODIFIERS: { + /* TODO(redmser): Modifier types. */ + break; + } + case ACTKEYS_SELECT_GROUP_DATABLOCK: { + /* Same datablock, identified by name. */ + short result = ANIM_fcurve_keyframes_loop( + ked, fcu, ok_cb, select_grouped_active_datablock, NULL); + if (result == 1) { + ked->data = ale->id->name; + return select_grouped_datablock; + } + break; + } + default: { + BLI_assert(0); + break; + } + } + } + + if (type == ACTKEYS_SELECT_GROUP_DATABLOCK) { + char empty[1] = {'\0'}; + ked->data = empty; + } + return NULL; +} + +static int actkeys_select_grouped_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + + if (ANIM_animdata_get_context(C, &ac) == 0) { + return OPERATOR_CANCELLED; + } + + const int type = RNA_enum_get(op->ptr, "type"); + const bool use_selected_channels = RNA_boolean_get(op->ptr, "use_selected_channels"); + + /* Filter data. */ + int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); + if (use_selected_channels) { + /* TODO(redmser): Doing it this way means that the keyframe that we're getting the reference + * value from must also be in a selected channel. But not sure if filtering twice is a better + * solution here either. */ + filter |= ANIMFILTER_SEL; + } + + ListBase anim_data = {NULL, NULL}; + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + KeyframeEditData ked = {{NULL}}; + KeyframeEditFunc ok_cb = select_grouped_get_filter_callback(&anim_data, &ked, type); + KeyframeEditFunc select_cb = ANIM_editkeyframes_select(SELECT_ADD); + + if (!RNA_boolean_get(op->ptr, "extend")) { + /* If not extending, deselect all first. */ + deselect_action_keys(&ac, 0, SELECT_SUBTRACT); + } + + bAnimListElem *ale; + for (ale = anim_data.first; ale; ale = ale->next) { + /* TODO(redmser): Other types (GP, mask, etc.). */ + if (ale->datatype != ALE_FCURVE) { + continue; + } + + /* Only continue if F-Curve has keyframes. */ + FCurve *fcu = (FCurve *)ale->key_data; + if (fcu->bezt == NULL) { + continue; + } + + /* Filtering by datablock has to be done on anim data level. */ + /* TODO(redmser): This will not work for datablocks from different libraries, since they have + * the same name. */ + if (type == ACTKEYS_SELECT_GROUP_DATABLOCK && !STREQ(ale->id->name, (char *)ked.data)) { + continue; + } + + ANIM_fcurve_keyframes_loop(&ked, fcu, ok_cb, select_cb, NULL); + } + + ANIM_animdata_freelist(&anim_data); + + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); + return OPERATOR_FINISHED; +} + +void ACTION_OT_select_grouped(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Select Grouped"; + ot->idname = "ACTION_OT_select_grouped"; + ot->description = "Select all keyframes grouped by various properties"; + + /* Api callbacks. */ + ot->invoke = WM_menu_invoke; + ot->exec = actkeys_select_grouped_exec; + ot->poll = ED_operator_action_active; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties. */ + ot->prop = RNA_def_enum(ot->srna, + "type", + actkeys_prop_select_grouped_types, + 0, + "Type", + "Which criterion to filter selection by"); + RNA_def_boolean(ot->srna, + "extend", + false, + "Extend", + "Extend selection instead of deselecting everything first"); + RNA_def_boolean(ot->srna, + "use_selected_channels", + false, + "Use Selected Channels", + "Only consider keyframes on the same channel as the selected ones"); +} -- 2.30.2 From 73e55990e050b0c3a6293766a2ddf5661f682919 Mon Sep 17 00:00:00 2001 From: RedMser Date: Sat, 30 Apr 2022 16:44:27 +0200 Subject: [PATCH 2/9] Animation: Allow multi select for select grouped --- .../editors/space_action/action_select.c | 119 ++++++++++-------- 1 file changed, 68 insertions(+), 51 deletions(-) diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 03234c1f66a..e2af2c7df61 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -14,6 +14,7 @@ #include "BLI_blenlib.h" #include "BLI_dlrbTree.h" +#include "BLI_ghash.h" #include "BLI_lasso_2d.h" #include "BLI_utildefines.h" @@ -1963,7 +1964,8 @@ static short select_grouped_datablock(KeyframeEditData *ked, struct BezTriple *b static short select_grouped_active_datablock(KeyframeEditData *ked, struct BezTriple *bezt) { - /* Stop after the first selected bezt was found. */ + /* Cancel iteration if we found any selected key. The key will be added to the set in the loop, + * since the info comes from the anim data list. */ return 1; } @@ -1971,29 +1973,32 @@ static short select_grouped_active_datablock(KeyframeEditData *ked, struct BezTr static short select_grouped_handle_type(KeyframeEditData *ked, struct BezTriple *bezt) { - /* TODO(redmser): Should this compare left and right handle individually? In dopesheet, you can't - * select individual handles to clarify the intent. */ - char handle = *(char *)ked->data; - if (bezt->h1 == handle && bezt->h2 == handle) { - return KEYFRAME_OK_ALL; + short ok = 0; + if (BLI_gset_haskey(ked->data, POINTER_FROM_INT(bezt->h1))) { + ok |= KEYFRAME_OK_H1; } - return 0; + if (BLI_gset_haskey(ked->data, POINTER_FROM_INT(bezt->h2))) { + ok |= KEYFRAME_OK_H2; + } + if (ok == (KEYFRAME_OK_H1 | KEYFRAME_OK_H2)) { + ok |= KEYFRAME_OK_KEY; + } + return ok; } static short select_grouped_active_handle_type(KeyframeEditData *ked, struct BezTriple *bezt) { if (bezt->h1 == bezt->h2) { - *((uint8_t *)ked->data) = bezt->h1; + BLI_gset_add(ked->data, POINTER_FROM_INT(bezt->h1)); } - return 1; + return 0; } /* ACTKEYS_SELECT_GROUP_INTERPOLATION_TYPE */ static short select_grouped_interpolation_type(KeyframeEditData *ked, struct BezTriple *bezt) { - char ipo = *(char *)ked->data; - if (bezt->ipo == ipo) { + if (BLI_gset_haskey(ked->data, POINTER_FROM_INT(bezt->ipo))) { return KEYFRAME_OK_ALL; } return 0; @@ -2002,16 +2007,15 @@ static short select_grouped_interpolation_type(KeyframeEditData *ked, struct Bez static short select_grouped_active_interpolation_type(KeyframeEditData *ked, struct BezTriple *bezt) { - *((char *)ked->data) = bezt->ipo; - return 1; + BLI_gset_add(ked->data, POINTER_FROM_INT(bezt->ipo)); + return 0; } /* ACTKEYS_SELECT_GROUP_KEY_TYPE */ static short select_grouped_key_type(KeyframeEditData *ked, struct BezTriple *bezt) { - char key_type = *(char *)ked->data; - if (BEZKEYTYPE(bezt) == key_type) { + if (BLI_gset_haskey(ked->data, POINTER_FROM_INT(BEZKEYTYPE(bezt)))) { return KEYFRAME_OK_ALL; } return 0; @@ -2019,30 +2023,41 @@ static short select_grouped_key_type(KeyframeEditData *ked, struct BezTriple *be static short select_grouped_active_key_type(KeyframeEditData *ked, struct BezTriple *bezt) { - *((char *)ked->data) = BEZKEYTYPE(bezt); - return 1; + BLI_gset_add(ked->data, POINTER_FROM_INT(BEZKEYTYPE(bezt))); + return 0; } -/* TODO(redmser): These filters could all allow multi-selection. Instead of setting ked->data, add - * the filter (if unique) to ked->list and do an "includes" check instead. */ - static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, KeyframeEditData *ked, int type) { KeyframeEditFunc ok_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED); + GSet *comp_set; + if (type == ACTKEYS_SELECT_GROUP_DATABLOCK) { + comp_set = BLI_gset_str_new(__func__); + } + else { + comp_set = BLI_gset_int_new(__func__); + } + + ked->data = comp_set; + + /* Last iteration of this loop will be used to return a callback based on type. */ bAnimListElem *ale; - for (ale = anim_data->first; ale; ale = ale->next) { - /* TODO(redmser): Other types (GP, mask, etc.). */ - if (ale->datatype != ALE_FCURVE) { - continue; - } + for (ale = anim_data->first;; ale = ale->next) { + FCurve *fcu; + if (ale != NULL) { + /* TODO(redmser): Other types (GP, mask, etc.). */ + if (ale->datatype != ALE_FCURVE) { + continue; + } - /* Only continue if F-Curve has keyframes. */ - FCurve *fcu = (FCurve *)ale->key_data; - if (fcu->bezt == NULL) { - continue; + /* Only continue if F-Curve has keyframes. */ + fcu = (FCurve *)ale->key_data; + if (fcu->bezt == NULL) { + continue; + } } /* Find first selected keyframe for context info. */ @@ -2052,33 +2067,28 @@ static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, break; } case ACTKEYS_SELECT_GROUP_HANDLE_TYPE: { - char handle = 0; - ked->data = &handle; - short result = ANIM_fcurve_keyframes_loop( - ked, fcu, ok_cb, select_grouped_active_handle_type, NULL); - if (result == 1) { + if (ale == NULL) { return select_grouped_handle_type; } + + ANIM_fcurve_keyframes_loop(ked, fcu, ok_cb, select_grouped_active_handle_type, NULL); break; } case ACTKEYS_SELECT_GROUP_INTERPOLATION_TYPE: { - char ipo = 0; - ked->data = &ipo; - short result = ANIM_fcurve_keyframes_loop( - ked, fcu, ok_cb, select_grouped_active_interpolation_type, NULL); - if (result == 1) { + if (ale == NULL) { return select_grouped_interpolation_type; } + + ANIM_fcurve_keyframes_loop( + ked, fcu, ok_cb, select_grouped_active_interpolation_type, NULL); break; } case ACTKEYS_SELECT_GROUP_KEY_TYPE: { - char key_type = 0; - ked->data = &key_type; - short result = ANIM_fcurve_keyframes_loop( - ked, fcu, ok_cb, select_grouped_active_key_type, NULL); - if (result == 1) { + if (ale == NULL) { return select_grouped_key_type; } + + ANIM_fcurve_keyframes_loop(ked, fcu, ok_cb, select_grouped_active_key_type, NULL); break; } case ACTKEYS_SELECT_GROUP_MODIFIERS: { @@ -2087,11 +2097,15 @@ static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, } case ACTKEYS_SELECT_GROUP_DATABLOCK: { /* Same datablock, identified by name. */ + if (ale == NULL) { + return select_grouped_datablock; + } + short result = ANIM_fcurve_keyframes_loop( ked, fcu, ok_cb, select_grouped_active_datablock, NULL); if (result == 1) { - ked->data = ale->id->name; - return select_grouped_datablock; + /* Found a selected key in this datablock, add its id. */ + BLI_gset_add(ked->data, BLI_strdup(ale->id->name)); } break; } @@ -2102,10 +2116,6 @@ static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, } } - if (type == ACTKEYS_SELECT_GROUP_DATABLOCK) { - char empty[1] = {'\0'}; - ked->data = empty; - } return NULL; } @@ -2157,7 +2167,7 @@ static int actkeys_select_grouped_exec(bContext *C, wmOperator *op) /* Filtering by datablock has to be done on anim data level. */ /* TODO(redmser): This will not work for datablocks from different libraries, since they have * the same name. */ - if (type == ACTKEYS_SELECT_GROUP_DATABLOCK && !STREQ(ale->id->name, (char *)ked.data)) { + if (type == ACTKEYS_SELECT_GROUP_DATABLOCK && !BLI_gset_haskey(ked.data, ale->id->name)) { continue; } @@ -2166,6 +2176,13 @@ static int actkeys_select_grouped_exec(bContext *C, wmOperator *op) ANIM_animdata_freelist(&anim_data); + if (type == ACTKEYS_SELECT_GROUP_DATABLOCK) { + BLI_gset_free(ked.data, MEM_freeN); + } + else { + BLI_gset_free(ked.data, NULL); + } + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); return OPERATOR_FINISHED; } -- 2.30.2 From 9949161edf3f397981513eccc3657cc9cebd3977 Mon Sep 17 00:00:00 2001 From: RedMser Date: Sat, 30 Apr 2022 16:49:02 +0200 Subject: [PATCH 3/9] Animation: use pointer comparison for datablocks --- .../editors/space_action/action_select.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index e2af2c7df61..4eb01692b31 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -2035,7 +2035,7 @@ static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, GSet *comp_set; if (type == ACTKEYS_SELECT_GROUP_DATABLOCK) { - comp_set = BLI_gset_str_new(__func__); + comp_set = BLI_gset_ptr_new(__func__); } else { comp_set = BLI_gset_int_new(__func__); @@ -2096,7 +2096,6 @@ static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, break; } case ACTKEYS_SELECT_GROUP_DATABLOCK: { - /* Same datablock, identified by name. */ if (ale == NULL) { return select_grouped_datablock; } @@ -2105,7 +2104,7 @@ static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, ked, fcu, ok_cb, select_grouped_active_datablock, NULL); if (result == 1) { /* Found a selected key in this datablock, add its id. */ - BLI_gset_add(ked->data, BLI_strdup(ale->id->name)); + BLI_gset_add(ked->data, ale->id); } break; } @@ -2165,9 +2164,7 @@ static int actkeys_select_grouped_exec(bContext *C, wmOperator *op) } /* Filtering by datablock has to be done on anim data level. */ - /* TODO(redmser): This will not work for datablocks from different libraries, since they have - * the same name. */ - if (type == ACTKEYS_SELECT_GROUP_DATABLOCK && !BLI_gset_haskey(ked.data, ale->id->name)) { + if (type == ACTKEYS_SELECT_GROUP_DATABLOCK && !BLI_gset_haskey(ked.data, ale->id)) { continue; } @@ -2175,13 +2172,7 @@ static int actkeys_select_grouped_exec(bContext *C, wmOperator *op) } ANIM_animdata_freelist(&anim_data); - - if (type == ACTKEYS_SELECT_GROUP_DATABLOCK) { - BLI_gset_free(ked.data, MEM_freeN); - } - else { - BLI_gset_free(ked.data, NULL); - } + BLI_gset_free(ked.data, NULL); WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); return OPERATOR_FINISHED; -- 2.30.2 From 2f43c2dc38c26e812e941080d38d5457b91866f4 Mon Sep 17 00:00:00 2001 From: RedMser Date: Mon, 16 May 2022 18:56:03 +0200 Subject: [PATCH 4/9] Fix typo --- source/blender/editors/space_action/action_select.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 4eb01692b31..4e6ba03b8ff 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -2209,5 +2209,5 @@ void ACTION_OT_select_grouped(wmOperatorType *ot) "use_selected_channels", false, "Use Selected Channels", - "Only consider keyframes on the same channel as the selected ones"); + "Only consider keyframes on the same channels as the selected ones"); } -- 2.30.2 From 131f8ace4576fd76ddeaa8dfde972e2af10682b1 Mon Sep 17 00:00:00 2001 From: RedMser Date: Mon, 16 May 2022 20:04:42 +0200 Subject: [PATCH 5/9] Animation: Add NLA select grouped operator --- .../keyconfig/keymap_data/blender_default.py | 1 + .../keymap_data/industry_compatible_data.py | 1 + scripts/startup/bl_ui/space_nla.py | 3 + .../editors/space_action/action_select.c | 2 + source/blender/editors/space_nla/nla_intern.h | 1 + source/blender/editors/space_nla/nla_ops.c | 1 + source/blender/editors/space_nla/nla_select.c | 238 ++++++++++++++++++ 7 files changed, 247 insertions(+) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index 85b8b08cdf8..159c60c9489 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2557,6 +2557,7 @@ def km_nla_editor(params): ("nla.select_leftright", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, {"properties": [("mode", 'RIGHT')]}), *_template_items_select_actions(params, "nla.select_all"), + ("nla.select_grouped", {"type": 'G', "value": 'PRESS', "shift": True}, None), ("nla.select_box", {"type": 'B', "value": 'PRESS'}, {"properties": [("axis_range", False)]}), ("nla.select_box", {"type": 'B', "value": 'PRESS', "alt": True}, diff --git a/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index af738e398d6..6ac27b000ac 100644 --- a/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -1496,6 +1496,7 @@ def km_nla_editor(params): {"properties": [("mode", 'LEFT'), ("extend", False)]}), ("nla.select_leftright", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, {"properties": [("mode", 'RIGHT'), ("extend", False)]}), + ("nla.select_grouped", {"type": 'G', "value": 'PRESS', "shift": True}, None), ("nla.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}), ("nla.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}), diff --git a/scripts/startup/bl_ui/space_nla.py b/scripts/startup/bl_ui/space_nla.py index b43434d9988..8e705e4e5b4 100644 --- a/scripts/startup/bl_ui/space_nla.py +++ b/scripts/startup/bl_ui/space_nla.py @@ -138,6 +138,9 @@ class NLA_MT_select(Menu): props.extend = False props.mode = 'RIGHT' + layout.separator() + layout.operator_menu_enum("nla.select_grouped", "type", text="Select Grouped") + class NLA_MT_marker(Menu): bl_label = "Marker" diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 4e6ba03b8ff..d6c6594e8b7 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -2064,6 +2064,7 @@ static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, switch (type) { case ACTKEYS_SELECT_GROUP_CHANNEL_TYPE: { /* TODO(redmser): Channel type (by name?). */ + BLI_assert(0); break; } case ACTKEYS_SELECT_GROUP_HANDLE_TYPE: { @@ -2093,6 +2094,7 @@ static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, } case ACTKEYS_SELECT_GROUP_MODIFIERS: { /* TODO(redmser): Modifier types. */ + BLI_assert(0); break; } case ACTKEYS_SELECT_GROUP_DATABLOCK: { diff --git a/source/blender/editors/space_nla/nla_intern.h b/source/blender/editors/space_nla/nla_intern.h index 33aa30f32da..e4fe281b0f1 100644 --- a/source/blender/editors/space_nla/nla_intern.h +++ b/source/blender/editors/space_nla/nla_intern.h @@ -44,6 +44,7 @@ enum eNlaEdit_LeftRightSelect_Mode { void NLA_OT_select_all(wmOperatorType *ot); void NLA_OT_select_box(wmOperatorType *ot); void NLA_OT_select_leftright(wmOperatorType *ot); +void NLA_OT_select_grouped(wmOperatorType *ot); void NLA_OT_click_select(wmOperatorType *ot); /* **************************************** */ diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c index 3ae73282230..373103b39c6 100644 --- a/source/blender/editors/space_nla/nla_ops.c +++ b/source/blender/editors/space_nla/nla_ops.c @@ -97,6 +97,7 @@ void nla_operatortypes(void) WM_operatortype_append(NLA_OT_select_box); WM_operatortype_append(NLA_OT_select_all); WM_operatortype_append(NLA_OT_select_leftright); + WM_operatortype_append(NLA_OT_select_grouped); /* view */ WM_operatortype_append(NLA_OT_view_all); diff --git a/source/blender/editors/space_nla/nla_select.c b/source/blender/editors/space_nla/nla_select.c index 281782123d1..d98d3a154a7 100644 --- a/source/blender/editors/space_nla/nla_select.c +++ b/source/blender/editors/space_nla/nla_select.c @@ -14,6 +14,7 @@ #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_ghash.h" #include "BLI_math_base.h" #include "BKE_context.h" @@ -742,3 +743,240 @@ void NLA_OT_click_select(wmOperatorType *ot) } /* *********************************************** */ + +/* Select Grouped Operator */ + +enum { + NLAEDIT_SELECT_GROUP_BLENDING, + NLAEDIT_SELECT_GROUP_ACTION, + NLAEDIT_SELECT_GROUP_EXTRAPOLATION, + NLAEDIT_SELECT_GROUP_REVERSED, + NLAEDIT_SELECT_GROUP_DATABLOCK, + NLAEDIT_SELECT_GROUP_MUTED, +}; + +static const EnumPropertyItem nlaedit_prop_select_grouped_types[] = { + {NLAEDIT_SELECT_GROUP_BLENDING, + "BLENDING", + 0, + "Blending Mode", + "All strips with the same blending mode (replace, combine, add, etc.)"}, + {NLAEDIT_SELECT_GROUP_ACTION, + "ACTION", + 0, + "Action", + "All strips that use the same action datablock"}, + {NLAEDIT_SELECT_GROUP_EXTRAPOLATION, + "EXTRAPOLATION", + 0, + "Extrapolation Mode", + "All strips the use the same extrapolation mode (hold, hold forward, etc.)"}, + {NLAEDIT_SELECT_GROUP_REVERSED, + "REVERSED", + 0, + "Reversed", + "All strips that have the same value for the reversed option"}, + {NLAEDIT_SELECT_GROUP_DATABLOCK, + "DATABLOCK", + 0, + "Datablock", + "All strips that are part of the same datablock"}, + {NLAEDIT_SELECT_GROUP_MUTED, + "MUTED", + 0, + "Muted", + "All strips that have the same value for the muted option"}, + {0, NULL, 0, NULL, NULL}, +}; + +static void select_grouped_tag_strips(int type, bAnimListElem *ale, GSet *comp_set) +{ + NlaTrack *track = (NlaTrack *)ale->data; + + for (NlaStrip *strip = track->strips.first; strip; strip = strip->next) { + if (!(strip->flag & NLASTRIP_FLAG_SELECT)) { + continue; + } + + switch (type) { + case NLAEDIT_SELECT_GROUP_ACTION: { + BLI_gset_add(comp_set, strip->act); + break; + } + case NLAEDIT_SELECT_GROUP_BLENDING: { + BLI_gset_add(comp_set, POINTER_FROM_INT(strip->blendmode)); + break; + } + case NLAEDIT_SELECT_GROUP_DATABLOCK: { + BLI_gset_add(comp_set, ale->id); + + /* No need to check all strips in the track. */ + return; + } + case NLAEDIT_SELECT_GROUP_EXTRAPOLATION: { + BLI_gset_add(comp_set, POINTER_FROM_INT(strip->extendmode)); + break; + } + case NLAEDIT_SELECT_GROUP_REVERSED: { + BLI_gset_add(comp_set, POINTER_FROM_INT(strip->flag & NLASTRIP_FLAG_REVERSE)); + break; + } + case NLAEDIT_SELECT_GROUP_MUTED: { + BLI_gset_add(comp_set, POINTER_FROM_INT(strip->flag & NLASTRIP_FLAG_MUTED)); + break; + } + default: { + BLI_assert(0); + break; + } + } + } +} + +static void select_grouped_select_matching_strips(int type, bAnimListElem *ale, GSet *comp_set) +{ + NlaTrack *track = (NlaTrack *)ale->data; + + for (NlaStrip *strip = track->strips.first; strip; strip = strip->next) { + switch (type) { + case NLAEDIT_SELECT_GROUP_ACTION: { + if (!BLI_gset_haskey(comp_set, strip->act)) { + continue; + } + break; + } + case NLAEDIT_SELECT_GROUP_BLENDING: { + if (!BLI_gset_haskey(comp_set, POINTER_FROM_INT(strip->blendmode))) { + continue; + } + break; + } + case NLAEDIT_SELECT_GROUP_DATABLOCK: { + if (!BLI_gset_haskey(comp_set, ale->id)) { + /* No need to check all strips in the track. */ + return; + } + break; + } + case NLAEDIT_SELECT_GROUP_EXTRAPOLATION: { + if (!BLI_gset_haskey(comp_set, POINTER_FROM_INT(strip->extendmode))) { + continue; + } + break; + } + case NLAEDIT_SELECT_GROUP_REVERSED: { + if (!BLI_gset_haskey(comp_set, POINTER_FROM_INT(strip->flag & NLASTRIP_FLAG_REVERSE))) { + continue; + } + break; + } + case NLAEDIT_SELECT_GROUP_MUTED: { + if (!BLI_gset_haskey(comp_set, POINTER_FROM_INT(strip->flag & NLASTRIP_FLAG_MUTED))) { + continue; + } + break; + } + default: { + BLI_assert(0); + break; + } + } + + ACHANNEL_SET_FLAG(strip, SELECT_ADD, NLASTRIP_FLAG_SELECT); + } +} + +static int nlaedit_select_grouped_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + + if (ANIM_animdata_get_context(C, &ac) == 0) { + return OPERATOR_CANCELLED; + } + + Scene *scene = ac.scene; + /* If currently in tweak-mode, exit tweak-mode first. */ + if (scene->flag & SCE_NLA_EDIT_ON) { + WM_operator_name_call(C, "NLA_OT_tweakmode_exit", WM_OP_EXEC_DEFAULT, NULL, NULL); + } + + const int type = RNA_enum_get(op->ptr, "type"); + const bool use_selected_tracks = RNA_boolean_get(op->ptr, "use_selected_tracks"); + + /* Filter data. */ + int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE); + if (use_selected_tracks) { + /* TODO(redmser): Doing it this way means that the strip that we're getting the reference + * value from must also be in a selected track. But not sure if filtering twice is a better + * solution here either. */ + filter |= ANIMFILTER_SEL; + } + + ListBase anim_data = {NULL, NULL}; + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* Collect list of selected strips' properties. */ + GSet *comp_set; + if (type == NLAEDIT_SELECT_GROUP_DATABLOCK || type == NLAEDIT_SELECT_GROUP_ACTION) { + comp_set = BLI_gset_ptr_new(__func__); + } + else { + comp_set = BLI_gset_int_new(__func__); + } + + for (bAnimListElem *ale = anim_data.first; ale; ale = ale->next) { + select_grouped_tag_strips(type, ale, comp_set); + } + + if (!RNA_boolean_get(op->ptr, "extend")) { + /* If not extending, deselect all first. */ + /* TODO(redmser): This is acting a bit weirdly... */ + deselect_nla_strips(&ac, 0, SELECT_SUBTRACT); + } + + /* Select all strips that match. */ + for (bAnimListElem *ale = anim_data.first; ale; ale = ale->next) { + select_grouped_select_matching_strips(type, ale, comp_set); + } + + ANIM_animdata_freelist(&anim_data); + BLI_gset_free(comp_set, NULL); + + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL); + return OPERATOR_FINISHED; +} + +void NLA_OT_select_grouped(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Select Grouped"; + ot->idname = "NLA_OT_select_grouped"; + ot->description = "Select all strips grouped by various properties"; + + /* Api callbacks. */ + ot->invoke = WM_menu_invoke; + ot->exec = nlaedit_select_grouped_exec; + ot->poll = ED_operator_nla_active; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties. */ + ot->prop = RNA_def_enum(ot->srna, + "type", + nlaedit_prop_select_grouped_types, + 0, + "Type", + "Which criterion to filter selection by"); + RNA_def_boolean(ot->srna, + "extend", + false, + "Extend", + "Extend selection instead of deselecting everything first"); + RNA_def_boolean(ot->srna, + "use_selected_tracks", + false, + "Use Selected Tracks", + "Only consider strips on the same track as the selected ones"); +} -- 2.30.2 From b5e6ec09c98dbb11dbb548c872e5497e51407cbf Mon Sep 17 00:00:00 2001 From: RedMser Date: Mon, 16 May 2022 20:21:14 +0200 Subject: [PATCH 6/9] Animation: Add Graph Editor select grouped operator --- .../keyconfig/keymap_data/blender_default.py | 1 + .../keymap_data/industry_compatible_data.py | 1 + scripts/startup/bl_ui/space_graph.py | 3 + .../editors/space_action/action_select.c | 5 +- .../editors/space_graph/graph_intern.h | 1 + .../blender/editors/space_graph/graph_ops.c | 1 + .../editors/space_graph/graph_select.c | 317 ++++++++++++++++++ source/blender/editors/space_nla/nla_select.c | 2 +- 8 files changed, 327 insertions(+), 4 deletions(-) diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index 159c60c9489..bc52f96e9c0 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1768,6 +1768,7 @@ def km_graph_editor(params): ("graph.select_leftright", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, {"properties": [("mode", 'RIGHT')]}), *_template_items_select_actions(params, "graph.select_all"), + ("graph.select_grouped", {"type": 'G', "value": 'PRESS', "shift": True}, None), ("graph.select_box", {"type": 'B', "value": 'PRESS'}, None), ("graph.select_box", {"type": 'B', "value": 'PRESS', "alt": True}, {"properties": [("axis_range", True)]}), diff --git a/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 6ac27b000ac..68c517d2f7e 100644 --- a/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -903,6 +903,7 @@ def km_graph_editor(params): {"properties": [("mode", 'LEFT'), ("extend", False)]}), ("graph.select_leftright", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, {"properties": [("mode", 'RIGHT'), ("extend", False)]}), + ("graph.select_grouped", {"type": 'G', "value": 'PRESS', "shift": True}, None), ("graph.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}), ("graph.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True, "shift": True}, {"properties": [("action", 'DESELECT')]}), diff --git a/scripts/startup/bl_ui/space_graph.py b/scripts/startup/bl_ui/space_graph.py index 5a84bd5140d..cf2c296d43f 100644 --- a/scripts/startup/bl_ui/space_graph.py +++ b/scripts/startup/bl_ui/space_graph.py @@ -178,6 +178,9 @@ class GRAPH_MT_select(Menu): props.extend = False props.mode = 'RIGHT' + layout.separator() + layout.operator_menu_enum("graph.select_grouped", "type", text="Select Grouped") + layout.separator() layout.operator("graph.select_more") layout.operator("graph.select_less") diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index d6c6594e8b7..edb187d50e6 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -1988,9 +1988,8 @@ static short select_grouped_handle_type(KeyframeEditData *ked, struct BezTriple static short select_grouped_active_handle_type(KeyframeEditData *ked, struct BezTriple *bezt) { - if (bezt->h1 == bezt->h2) { - BLI_gset_add(ked->data, POINTER_FROM_INT(bezt->h1)); - } + BLI_gset_add(ked->data, POINTER_FROM_INT(bezt->h1)); + BLI_gset_add(ked->data, POINTER_FROM_INT(bezt->h2)); return 0; } diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h index a685216db31..fe7a9ea0b9e 100644 --- a/source/blender/editors/space_graph/graph_intern.h +++ b/source/blender/editors/space_graph/graph_intern.h @@ -65,6 +65,7 @@ void GRAPH_OT_select_linked(struct wmOperatorType *ot); void GRAPH_OT_select_more(struct wmOperatorType *ot); void GRAPH_OT_select_less(struct wmOperatorType *ot); void GRAPH_OT_select_leftright(struct wmOperatorType *ot); +void GRAPH_OT_select_grouped(struct wmOperatorType *ot); void GRAPH_OT_clickselect(struct wmOperatorType *ot); /* defines for left-right select tool */ diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c index b178a3d3430..2661965dfb0 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -442,6 +442,7 @@ void graphedit_operatortypes(void) WM_operatortype_append(GRAPH_OT_select_more); WM_operatortype_append(GRAPH_OT_select_less); WM_operatortype_append(GRAPH_OT_select_leftright); + WM_operatortype_append(GRAPH_OT_select_grouped); /* editing */ WM_operatortype_append(GRAPH_OT_snap); diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index f92fe71455b..2383e90e80a 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -13,6 +13,7 @@ #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_ghash.h" #include "BLI_lasso_2d.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -2020,3 +2021,319 @@ void GRAPH_OT_clickselect(wmOperatorType *ot) } /** \} */ + +/* Select Grouped Operator */ +/* TODO(redmser): This can be de-duplicated almost completely from the action variant of this + * operator. */ + +enum { + GRAPHKEYS_SELECT_GROUP_CHANNEL_TYPE, + GRAPHKEYS_SELECT_GROUP_KEY_TYPE, + GRAPHKEYS_SELECT_GROUP_INTERPOLATION_TYPE, + GRAPHKEYS_SELECT_GROUP_HANDLE_TYPE, + GRAPHKEYS_SELECT_GROUP_INTERPOLATION_HANDLE_TYPE, + GRAPHKEYS_SELECT_GROUP_MODIFIERS, + GRAPHKEYS_SELECT_GROUP_DATABLOCK, +}; + +static const EnumPropertyItem graphkeys_prop_select_grouped_types[] = { + {GRAPHKEYS_SELECT_GROUP_CHANNEL_TYPE, + "CHANNEL_TYPE", + 0, + "Channel Type", + "All keyframes of same type of channel (X Location, etc.)"}, + {GRAPHKEYS_SELECT_GROUP_KEY_TYPE, + "KEY_TYPE", + 0, + "Key Type", + "All keyframes of the same type (breakdown, extreme, etc.)"}, + {GRAPHKEYS_SELECT_GROUP_INTERPOLATION_TYPE, + "INTERPOLATION_TYPE", + 0, + "Interpolation Mode", + "All keyframes of the same interpolation mode (constant, bezier, etc.)"}, + {GRAPHKEYS_SELECT_GROUP_HANDLE_TYPE, + "HANDLE_TYPE", + 0, + "Handle Type", + "All keyframes of the same handle type (vector, automatic, etc.)"}, + {GRAPHKEYS_SELECT_GROUP_MODIFIERS, + "MODIFIERS", + 0, + "Modifiers", + "All keyframes on each channel that has the same modifiers"}, + {GRAPHKEYS_SELECT_GROUP_DATABLOCK, + "DATABLOCK", + 0, + "Datablock", + "All keyframes that are part of the same datablock"}, + {0, NULL, 0, NULL, NULL}, +}; + +/* GRAPHKEYS_SELECT_GROUP_DATABLOCK */ + +static short select_grouped_datablock(KeyframeEditData *ked, struct BezTriple *bezt) +{ + /* Actual filtering is done via the anim data list. */ + return KEYFRAME_OK_ALL; +} + +static short select_grouped_active_datablock(KeyframeEditData *ked, struct BezTriple *bezt) +{ + /* Cancel iteration if we found any selected key. The key will be added to the set in the loop, + * since the info comes from the anim data list. */ + return 1; +} + +/* GRAPHKEYS_SELECT_GROUP_HANDLE_TYPE */ + +static short select_grouped_handle_type(KeyframeEditData *ked, struct BezTriple *bezt) +{ + short ok = 0; + if (BLI_gset_haskey(ked->data, POINTER_FROM_INT(bezt->h1))) { + ok |= KEYFRAME_OK_H1; + } + if (BLI_gset_haskey(ked->data, POINTER_FROM_INT(bezt->h2))) { + ok |= KEYFRAME_OK_H2; + } + if (ok == (KEYFRAME_OK_H1 | KEYFRAME_OK_H2)) { + ok |= KEYFRAME_OK_KEY; + } + return ok; +} + +static short select_grouped_active_handle_type(KeyframeEditData *ked, struct BezTriple *bezt) +{ + BLI_gset_add(ked->data, POINTER_FROM_INT(bezt->h1)); + BLI_gset_add(ked->data, POINTER_FROM_INT(bezt->h2)); + return 0; +} + +/* GRAPHKEYS_SELECT_GROUP_INTERPOLATION_TYPE */ + +static short select_grouped_interpolation_type(KeyframeEditData *ked, struct BezTriple *bezt) +{ + if (BLI_gset_haskey(ked->data, POINTER_FROM_INT(bezt->ipo))) { + return KEYFRAME_OK_ALL; + } + return 0; +} + +static short select_grouped_active_interpolation_type(KeyframeEditData *ked, + struct BezTriple *bezt) +{ + BLI_gset_add(ked->data, POINTER_FROM_INT(bezt->ipo)); + return 0; +} + +/* GRAPHKEYS_SELECT_GROUP_KEY_TYPE */ + +static short select_grouped_key_type(KeyframeEditData *ked, struct BezTriple *bezt) +{ + if (BLI_gset_haskey(ked->data, POINTER_FROM_INT(BEZKEYTYPE(bezt)))) { + return KEYFRAME_OK_ALL; + } + return 0; +} + +static short select_grouped_active_key_type(KeyframeEditData *ked, struct BezTriple *bezt) +{ + BLI_gset_add(ked->data, POINTER_FROM_INT(BEZKEYTYPE(bezt))); + return 0; +} + +static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, + KeyframeEditData *ked, + int type) +{ + KeyframeEditFunc ok_cb = ANIM_editkeyframes_ok(BEZT_OK_SELECTED); + GSet *comp_set; + + if (type == GRAPHKEYS_SELECT_GROUP_DATABLOCK) { + comp_set = BLI_gset_ptr_new(__func__); + } + else { + comp_set = BLI_gset_int_new(__func__); + } + + ked->data = comp_set; + + /* Last iteration of this loop will be used to return a callback based on type. */ + bAnimListElem *ale; + for (ale = anim_data->first;; ale = ale->next) { + FCurve *fcu; + if (ale != NULL) { + /* TODO(redmser): Other types (GP, mask, etc.). */ + if (ale->datatype != ALE_FCURVE) { + continue; + } + + /* Only continue if F-Curve has keyframes. */ + fcu = (FCurve *)ale->key_data; + if (fcu->bezt == NULL) { + continue; + } + } + + /* Find first selected keyframe for context info. */ + switch (type) { + case GRAPHKEYS_SELECT_GROUP_CHANNEL_TYPE: { + /* TODO(redmser): Channel type (by name?). */ + BLI_assert(0); + break; + } + case GRAPHKEYS_SELECT_GROUP_HANDLE_TYPE: { + if (ale == NULL) { + return select_grouped_handle_type; + } + + ANIM_fcurve_keyframes_loop(ked, fcu, ok_cb, select_grouped_active_handle_type, NULL); + break; + } + case GRAPHKEYS_SELECT_GROUP_INTERPOLATION_TYPE: { + if (ale == NULL) { + return select_grouped_interpolation_type; + } + + ANIM_fcurve_keyframes_loop( + ked, fcu, ok_cb, select_grouped_active_interpolation_type, NULL); + break; + } + case GRAPHKEYS_SELECT_GROUP_KEY_TYPE: { + if (ale == NULL) { + return select_grouped_key_type; + } + + ANIM_fcurve_keyframes_loop(ked, fcu, ok_cb, select_grouped_active_key_type, NULL); + break; + } + case GRAPHKEYS_SELECT_GROUP_MODIFIERS: { + /* TODO(redmser): Modifier types. */ + BLI_assert(0); + break; + } + case GRAPHKEYS_SELECT_GROUP_DATABLOCK: { + if (ale == NULL) { + return select_grouped_datablock; + } + + short result = ANIM_fcurve_keyframes_loop( + ked, fcu, ok_cb, select_grouped_active_datablock, NULL); + if (result == 1) { + /* Found a selected key in this datablock, add its id. */ + BLI_gset_add(ked->data, ale->id); + } + break; + } + default: { + BLI_assert(0); + break; + } + } + } + + return NULL; +} + +static int graphkeys_select_grouped_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + + if (ANIM_animdata_get_context(C, &ac) == 0) { + return OPERATOR_CANCELLED; + } + + const int type = RNA_enum_get(op->ptr, "type"); + const bool use_selected_channels = RNA_boolean_get(op->ptr, "use_selected_channels"); + + /* Filter data. */ + int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS); + if (use_selected_channels) { + /* TODO(redmser): Doing it this way means that the keyframe that we're getting the reference + * value from must also be in a selected channel. But not sure if filtering twice is a better + * solution here either. */ + filter |= ANIMFILTER_SEL; + } + + ListBase anim_data = {NULL, NULL}; + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + KeyframeEditData ked = {{NULL}}; + KeyframeEditFunc ok_cb = select_grouped_get_filter_callback(&anim_data, &ked, type); + KeyframeEditFunc select_cb = ANIM_editkeyframes_select(SELECT_ADD); + + if (!RNA_boolean_get(op->ptr, "extend")) { + /* If not extending, deselect all first. */ + deselect_graph_keys(&ac, 0, SELECT_SUBTRACT, true); + } + + bAnimListElem *ale; + SpaceGraph *sipo = (SpaceGraph *)ac.sl; + for (ale = anim_data.first; ale; ale = ale->next) { + if (ale->datatype != ALE_FCURVE) { + continue; + } + + /* Only continue if F-Curve has keyframes. */ + FCurve *fcu = (FCurve *)ale->key_data; + if (fcu->bezt == NULL) { + continue; + } + + /* Filtering by datablock has to be done on anim data level. */ + if (type == GRAPHKEYS_SELECT_GROUP_DATABLOCK && !BLI_gset_haskey(ked.data, ale->id)) { + continue; + } + + /* Check if any keyframe will be selected. */ + if (ANIM_fcurve_keyframes_loop(&ked, fcu, NULL, ok_cb, NULL)) { + ANIM_fcurve_keyframes_loop(&ked, fcu, ok_cb, select_cb, NULL); + + /* Only change selection of channel when the visibility of keyframes + * doesn't depend on this. */ + if ((sipo->flag & SIPO_SELCUVERTSONLY) == 0) { + fcu->flag |= FCURVE_SELECTED; + } + } + } + + ANIM_animdata_freelist(&anim_data); + BLI_gset_free(ked.data, NULL); + + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); + return OPERATOR_FINISHED; +} + +void GRAPH_OT_select_grouped(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Select Grouped"; + ot->idname = "GRAPH_OT_select_grouped"; + ot->description = "Select all keyframes grouped by various properties"; + + /* Api callbacks. */ + ot->invoke = WM_menu_invoke; + ot->exec = graphkeys_select_grouped_exec; + ot->poll = graphop_visible_keyframes_poll; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* Properties. */ + ot->prop = RNA_def_enum(ot->srna, + "type", + graphkeys_prop_select_grouped_types, + 0, + "Type", + "Which criterion to filter selection by"); + RNA_def_boolean(ot->srna, + "extend", + false, + "Extend", + "Extend selection instead of deselecting everything first"); + RNA_def_boolean(ot->srna, + "use_selected_channels", + false, + "Use Selected Channels", + "Only consider keyframes on the same channels as the selected ones"); +} diff --git a/source/blender/editors/space_nla/nla_select.c b/source/blender/editors/space_nla/nla_select.c index d98d3a154a7..e72be7f14f5 100644 --- a/source/blender/editors/space_nla/nla_select.c +++ b/source/blender/editors/space_nla/nla_select.c @@ -794,7 +794,7 @@ static void select_grouped_tag_strips(int type, bAnimListElem *ale, GSet *comp_s NlaTrack *track = (NlaTrack *)ale->data; for (NlaStrip *strip = track->strips.first; strip; strip = strip->next) { - if (!(strip->flag & NLASTRIP_FLAG_SELECT)) { + if ((strip->flag & NLASTRIP_FLAG_SELECT) == 0) { continue; } -- 2.30.2 From 10a0890402386fd9b956901dadb4d3e14f5406e1 Mon Sep 17 00:00:00 2001 From: RedMser Date: Tue, 17 May 2022 13:39:10 +0200 Subject: [PATCH 7/9] add handle side + fix for graph select grouped --- .../editors/space_graph/graph_select.c | 52 ++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 2383e90e80a..80c4fcae694 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -2029,6 +2029,7 @@ void GRAPH_OT_clickselect(wmOperatorType *ot) enum { GRAPHKEYS_SELECT_GROUP_CHANNEL_TYPE, GRAPHKEYS_SELECT_GROUP_KEY_TYPE, + GRAPHKEYS_SELECT_GROUP_HANDLE_SIDE, GRAPHKEYS_SELECT_GROUP_INTERPOLATION_TYPE, GRAPHKEYS_SELECT_GROUP_HANDLE_TYPE, GRAPHKEYS_SELECT_GROUP_INTERPOLATION_HANDLE_TYPE, @@ -2047,6 +2048,11 @@ static const EnumPropertyItem graphkeys_prop_select_grouped_types[] = { 0, "Key Type", "All keyframes of the same type (breakdown, extreme, etc.)"}, + {GRAPHKEYS_SELECT_GROUP_HANDLE_SIDE, + "HANDLE_SIDE", + 0, + "Handle Side", + "All handles that are on the same side as the selected (left or right)"}, {GRAPHKEYS_SELECT_GROUP_INTERPOLATION_TYPE, "INTERPOLATION_TYPE", 0, @@ -2085,6 +2091,33 @@ static short select_grouped_active_datablock(KeyframeEditData *ked, struct BezTr return 1; } +/* GRAPHKEYS_SELECT_GROUP_HANDLE_SIDE */ + +static short select_grouped_handle_side(KeyframeEditData *ked, struct BezTriple *bezt) +{ + short ok = 0; + if (BLI_gset_haskey(ked->data, POINTER_FROM_INT(1))) { + ok |= KEYFRAME_OK_H1; + } + if (BLI_gset_haskey(ked->data, POINTER_FROM_INT(2))) { + ok |= KEYFRAME_OK_H2; + } + return ok; +} + +static short select_grouped_active_handle_side(KeyframeEditData *ked, struct BezTriple *bezt) +{ + /* Left handle. */ + if ((bezt->f1 & SELECT) != 0) { + BLI_gset_add(ked->data, POINTER_FROM_INT(1)); + } + /* Right handle. */ + if ((bezt->f3 & SELECT) != 0) { + BLI_gset_add(ked->data, POINTER_FROM_INT(2)); + } + return 0; +} + /* GRAPHKEYS_SELECT_GROUP_HANDLE_TYPE */ static short select_grouped_handle_type(KeyframeEditData *ked, struct BezTriple *bezt) @@ -2104,8 +2137,14 @@ static short select_grouped_handle_type(KeyframeEditData *ked, struct BezTriple static short select_grouped_active_handle_type(KeyframeEditData *ked, struct BezTriple *bezt) { - BLI_gset_add(ked->data, POINTER_FROM_INT(bezt->h1)); - BLI_gset_add(ked->data, POINTER_FROM_INT(bezt->h2)); + /* Left handle. */ + if ((bezt->f1 & SELECT) != 0) { + BLI_gset_add(ked->data, POINTER_FROM_INT(bezt->h1)); + } + /* Right handle. */ + if ((bezt->f3 & SELECT) != 0) { + BLI_gset_add(ked->data, POINTER_FROM_INT(bezt->h2)); + } return 0; } @@ -2199,6 +2238,14 @@ static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, ked, fcu, ok_cb, select_grouped_active_interpolation_type, NULL); break; } + case GRAPHKEYS_SELECT_GROUP_HANDLE_SIDE: { + if (ale == NULL) { + return select_grouped_handle_side; + } + + ANIM_fcurve_keyframes_loop(ked, fcu, ok_cb, select_grouped_active_handle_side, NULL); + break; + } case GRAPHKEYS_SELECT_GROUP_KEY_TYPE: { if (ale == NULL) { return select_grouped_key_type; @@ -2269,6 +2316,7 @@ static int graphkeys_select_grouped_exec(bContext *C, wmOperator *op) bAnimListElem *ale; SpaceGraph *sipo = (SpaceGraph *)ac.sl; + ked.iterflags |= KEYFRAME_ITER_INCL_HANDLES; for (ale = anim_data.first; ale; ale = ale->next) { if (ale->datatype != ALE_FCURVE) { continue; -- 2.30.2 From edc88dac311e89851aec256cf055bdb168f41f03 Mon Sep 17 00:00:00 2001 From: RedMser Date: Tue, 14 Feb 2023 18:27:34 +0100 Subject: [PATCH 8/9] Animation: add channel type + strict mode --- .../editors/space_action/action_select.c | 73 ++++++++++++++++++- .../editors/space_graph/graph_select.c | 73 ++++++++++++++++++- 2 files changed, 138 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index edb187d50e6..8f61e5e4b30 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -1912,6 +1912,7 @@ void ACTION_OT_clickselect(wmOperatorType *ot) enum { ACTKEYS_SELECT_GROUP_CHANNEL_TYPE, + ACTKEYS_SELECT_GROUP_CHANNEL_TYPE_STRICT, ACTKEYS_SELECT_GROUP_KEY_TYPE, ACTKEYS_SELECT_GROUP_INTERPOLATION_TYPE, ACTKEYS_SELECT_GROUP_HANDLE_TYPE, @@ -1925,7 +1926,12 @@ static const EnumPropertyItem actkeys_prop_select_grouped_types[] = { "CHANNEL_TYPE", 0, "Channel Type", - "All keyframes of same type of channel (X Location, etc.)"}, + "All keyframes with the same RNA path (Location, etc.)"}, + {ACTKEYS_SELECT_GROUP_CHANNEL_TYPE_STRICT, + "CHANNEL_TYPE_STRICT", + 0, + "Channel Type (Strict)", + "All keyframes with the same RNA path, including array index (X Location, etc.)"}, {ACTKEYS_SELECT_GROUP_KEY_TYPE, "KEY_TYPE", 0, @@ -1954,6 +1960,43 @@ static const EnumPropertyItem actkeys_prop_select_grouped_types[] = { {0, NULL, 0, NULL, NULL}, }; +/* ACTKEYS_SELECT_GROUP_CHANNEL_TYPE */ + +static short select_grouped_channel_type(KeyframeEditData *ked, struct BezTriple *bezt) +{ + if (BLI_gset_haskey(ked->data, ked->fcu->rna_path)) { + return KEYFRAME_OK_ALL; + } + return 0; +} + +static short select_grouped_active_channel_type(KeyframeEditData *ked, struct BezTriple *bezt) +{ + BLI_gset_add(ked->data, BLI_strdup(ked->fcu->rna_path)); + return 0; +} + +/* ACTKEYS_SELECT_GROUP_CHANNEL_TYPE_STRICT */ + +static short select_grouped_channel_type_strict(KeyframeEditData *ked, struct BezTriple *bezt) +{ + char name[256]; + BLI_snprintf(name, 256, "%s[%d]", ked->fcu->rna_path, ked->fcu->array_index); + if (BLI_gset_haskey(ked->data, name)) { + return KEYFRAME_OK_ALL; + } + return 0; +} + +static short select_grouped_active_channel_type_strict(KeyframeEditData *ked, + struct BezTriple *bezt) +{ + char *name = MEM_mallocN(256, __func__); + BLI_snprintf(name, 256, "%s[%d]", ked->fcu->rna_path, ked->fcu->array_index); + BLI_gset_add(ked->data, name); + return 0; +} + /* ACTKEYS_SELECT_GROUP_DATABLOCK */ static short select_grouped_datablock(KeyframeEditData *ked, struct BezTriple *bezt) @@ -2036,6 +2079,10 @@ static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, if (type == ACTKEYS_SELECT_GROUP_DATABLOCK) { comp_set = BLI_gset_ptr_new(__func__); } + else if (type == ACTKEYS_SELECT_GROUP_CHANNEL_TYPE || + type == ACTKEYS_SELECT_GROUP_CHANNEL_TYPE_STRICT) { + comp_set = BLI_gset_str_new(__func__); + } else { comp_set = BLI_gset_int_new(__func__); } @@ -2062,8 +2109,20 @@ static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, /* Find first selected keyframe for context info. */ switch (type) { case ACTKEYS_SELECT_GROUP_CHANNEL_TYPE: { - /* TODO(redmser): Channel type (by name?). */ - BLI_assert(0); + if (ale == NULL) { + return select_grouped_channel_type; + } + + ANIM_fcurve_keyframes_loop(ked, fcu, ok_cb, select_grouped_active_channel_type, NULL); + break; + } + case ACTKEYS_SELECT_GROUP_CHANNEL_TYPE_STRICT: { + if (ale == NULL) { + return select_grouped_channel_type_strict; + } + + ANIM_fcurve_keyframes_loop( + ked, fcu, ok_cb, select_grouped_active_channel_type_strict, NULL); break; } case ACTKEYS_SELECT_GROUP_HANDLE_TYPE: { @@ -2173,7 +2232,13 @@ static int actkeys_select_grouped_exec(bContext *C, wmOperator *op) } ANIM_animdata_freelist(&anim_data); - BLI_gset_free(ked.data, NULL); + if (type == ACTKEYS_SELECT_GROUP_CHANNEL_TYPE || + type == ACTKEYS_SELECT_GROUP_CHANNEL_TYPE_STRICT) { + BLI_gset_free(ked.data, MEM_freeN); + } + else { + BLI_gset_free(ked.data, NULL); + } WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 80c4fcae694..3cde48858c7 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -2028,6 +2028,7 @@ void GRAPH_OT_clickselect(wmOperatorType *ot) enum { GRAPHKEYS_SELECT_GROUP_CHANNEL_TYPE, + GRAPHKEYS_SELECT_GROUP_CHANNEL_TYPE_STRICT, GRAPHKEYS_SELECT_GROUP_KEY_TYPE, GRAPHKEYS_SELECT_GROUP_HANDLE_SIDE, GRAPHKEYS_SELECT_GROUP_INTERPOLATION_TYPE, @@ -2042,7 +2043,12 @@ static const EnumPropertyItem graphkeys_prop_select_grouped_types[] = { "CHANNEL_TYPE", 0, "Channel Type", - "All keyframes of same type of channel (X Location, etc.)"}, + "All keyframes with the same RNA path (Location, etc.)"}, + {GRAPHKEYS_SELECT_GROUP_CHANNEL_TYPE_STRICT, + "CHANNEL_TYPE_STRICT", + 0, + "Channel Type (Strict)", + "All keyframes with the same RNA path, including array index (X Location, etc.)"}, {GRAPHKEYS_SELECT_GROUP_KEY_TYPE, "KEY_TYPE", 0, @@ -2076,6 +2082,43 @@ static const EnumPropertyItem graphkeys_prop_select_grouped_types[] = { {0, NULL, 0, NULL, NULL}, }; +/* GRAPHKEYS_SELECT_GROUP_CHANNEL_TYPE */ + +static short select_grouped_channel_type(KeyframeEditData *ked, struct BezTriple *bezt) +{ + if (BLI_gset_haskey(ked->data, ked->fcu->rna_path)) { + return KEYFRAME_OK_ALL; + } + return 0; +} + +static short select_grouped_active_channel_type(KeyframeEditData *ked, struct BezTriple *bezt) +{ + BLI_gset_add(ked->data, BLI_strdup(ked->fcu->rna_path)); + return 0; +} + +/* GRAPHKEYS_SELECT_GROUP_CHANNEL_TYPE_STRICT */ + +static short select_grouped_channel_type_strict(KeyframeEditData *ked, struct BezTriple *bezt) +{ + char name[256]; + BLI_snprintf(name, 256, "%s[%d]", ked->fcu->rna_path, ked->fcu->array_index); + if (BLI_gset_haskey(ked->data, name)) { + return KEYFRAME_OK_ALL; + } + return 0; +} + +static short select_grouped_active_channel_type_strict(KeyframeEditData *ked, + struct BezTriple *bezt) +{ + char *name = MEM_mallocN(256, __func__); + BLI_snprintf(name, 256, "%s[%d]", ked->fcu->rna_path, ked->fcu->array_index); + BLI_gset_add(ked->data, name); + return 0; +} + /* GRAPHKEYS_SELECT_GROUP_DATABLOCK */ static short select_grouped_datablock(KeyframeEditData *ked, struct BezTriple *bezt) @@ -2191,6 +2234,10 @@ static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, if (type == GRAPHKEYS_SELECT_GROUP_DATABLOCK) { comp_set = BLI_gset_ptr_new(__func__); } + else if (type == GRAPHKEYS_SELECT_GROUP_CHANNEL_TYPE || + type == GRAPHKEYS_SELECT_GROUP_CHANNEL_TYPE_STRICT) { + comp_set = BLI_gset_str_new(__func__); + } else { comp_set = BLI_gset_int_new(__func__); } @@ -2217,8 +2264,20 @@ static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, /* Find first selected keyframe for context info. */ switch (type) { case GRAPHKEYS_SELECT_GROUP_CHANNEL_TYPE: { - /* TODO(redmser): Channel type (by name?). */ - BLI_assert(0); + if (ale == NULL) { + return select_grouped_channel_type; + } + + ANIM_fcurve_keyframes_loop(ked, fcu, ok_cb, select_grouped_active_channel_type, NULL); + break; + } + case GRAPHKEYS_SELECT_GROUP_CHANNEL_TYPE_STRICT: { + if (ale == NULL) { + return select_grouped_channel_type_strict; + } + + ANIM_fcurve_keyframes_loop( + ked, fcu, ok_cb, select_grouped_active_channel_type_strict, NULL); break; } case GRAPHKEYS_SELECT_GROUP_HANDLE_TYPE: { @@ -2346,7 +2405,13 @@ static int graphkeys_select_grouped_exec(bContext *C, wmOperator *op) } ANIM_animdata_freelist(&anim_data); - BLI_gset_free(ked.data, NULL); + if (type == GRAPHKEYS_SELECT_GROUP_CHANNEL_TYPE || + type == GRAPHKEYS_SELECT_GROUP_CHANNEL_TYPE_STRICT) { + BLI_gset_free(ked.data, MEM_freeN); + } + else { + BLI_gset_free(ked.data, NULL); + } WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); return OPERATOR_FINISHED; -- 2.30.2 From ead2243a63de47dc18e6e58fd805a2f15e926ffd Mon Sep 17 00:00:00 2001 From: RedMser Date: Tue, 14 Feb 2023 18:39:50 +0100 Subject: [PATCH 9/9] Animation: add fmodifier grouping --- .../editors/space_action/action_select.c | 36 +++++++++++++++++-- .../editors/space_graph/graph_select.c | 36 +++++++++++++++++-- 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 8f61e5e4b30..fb868790aff 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -1951,7 +1951,7 @@ static const EnumPropertyItem actkeys_prop_select_grouped_types[] = { "MODIFIERS", 0, "Modifiers", - "All keyframes on each channel that has the same modifiers"}, + "All keyframes on each channel that have all of the same types of modifiers"}, {ACTKEYS_SELECT_GROUP_DATABLOCK, "DATABLOCK", 0, @@ -1997,6 +1997,33 @@ static short select_grouped_active_channel_type_strict(KeyframeEditData *ked, return 0; } +/* ACTKEYS_SELECT_GROUP_MODIFIERS */ + +static int convert_modifiers_to_bitmask(ListBase modifiers) +{ + int bitmask = 0; + FModifier *modifier; + for (modifier = modifiers.first; modifier; modifier = modifier->next) { + bitmask |= 1 << modifier->type; + } + return bitmask; +} + +static short select_grouped_modifiers(KeyframeEditData *ked, struct BezTriple *bezt) +{ + if (BLI_gset_haskey(ked->data, + POINTER_FROM_INT(convert_modifiers_to_bitmask(ked->fcu->modifiers)))) { + return KEYFRAME_OK_ALL; + } + return 0; +} + +static short select_grouped_active_modifiers(KeyframeEditData *ked, struct BezTriple *bezt) +{ + BLI_gset_add(ked->data, POINTER_FROM_INT(convert_modifiers_to_bitmask(ked->fcu->modifiers))); + return 0; +} + /* ACTKEYS_SELECT_GROUP_DATABLOCK */ static short select_grouped_datablock(KeyframeEditData *ked, struct BezTriple *bezt) @@ -2151,8 +2178,11 @@ static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, break; } case ACTKEYS_SELECT_GROUP_MODIFIERS: { - /* TODO(redmser): Modifier types. */ - BLI_assert(0); + if (ale == NULL) { + return select_grouped_modifiers; + } + + ANIM_fcurve_keyframes_loop(ked, fcu, ok_cb, select_grouped_active_modifiers, NULL); break; } case ACTKEYS_SELECT_GROUP_DATABLOCK: { diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 3cde48858c7..9a4d494f0f5 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -2073,7 +2073,7 @@ static const EnumPropertyItem graphkeys_prop_select_grouped_types[] = { "MODIFIERS", 0, "Modifiers", - "All keyframes on each channel that has the same modifiers"}, + "All keyframes on each channel that have all of the same types of modifiers"}, {GRAPHKEYS_SELECT_GROUP_DATABLOCK, "DATABLOCK", 0, @@ -2119,6 +2119,33 @@ static short select_grouped_active_channel_type_strict(KeyframeEditData *ked, return 0; } +/* GRAPHKEYS_SELECT_GROUP_MODIFIERS */ + +static int convert_modifiers_to_bitmask(ListBase modifiers) +{ + int bitmask = 0; + FModifier *modifier; + for (modifier = modifiers.first; modifier; modifier = modifier->next) { + bitmask |= 1 << modifier->type; + } + return bitmask; +} + +static short select_grouped_modifiers(KeyframeEditData *ked, struct BezTriple *bezt) +{ + if (BLI_gset_haskey(ked->data, + POINTER_FROM_INT(convert_modifiers_to_bitmask(ked->fcu->modifiers)))) { + return KEYFRAME_OK_ALL; + } + return 0; +} + +static short select_grouped_active_modifiers(KeyframeEditData *ked, struct BezTriple *bezt) +{ + BLI_gset_add(ked->data, POINTER_FROM_INT(convert_modifiers_to_bitmask(ked->fcu->modifiers))); + return 0; +} + /* GRAPHKEYS_SELECT_GROUP_DATABLOCK */ static short select_grouped_datablock(KeyframeEditData *ked, struct BezTriple *bezt) @@ -2314,8 +2341,11 @@ static KeyframeEditFunc select_grouped_get_filter_callback(ListBase *anim_data, break; } case GRAPHKEYS_SELECT_GROUP_MODIFIERS: { - /* TODO(redmser): Modifier types. */ - BLI_assert(0); + if (ale == NULL) { + return select_grouped_modifiers; + } + + ANIM_fcurve_keyframes_loop(ked, fcu, ok_cb, select_grouped_active_modifiers, NULL); break; } case GRAPHKEYS_SELECT_GROUP_DATABLOCK: { -- 2.30.2