WIP: Animation: Select Grouped operator #104758
|
@ -1768,6 +1768,7 @@ def km_graph_editor(params):
|
||||||
("graph.select_leftright", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
|
("graph.select_leftright", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
|
||||||
{"properties": [("mode", 'RIGHT')]}),
|
{"properties": [("mode", 'RIGHT')]}),
|
||||||
*_template_items_select_actions(params, "graph.select_all"),
|
*_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'}, None),
|
||||||
("graph.select_box", {"type": 'B', "value": 'PRESS', "alt": True},
|
("graph.select_box", {"type": 'B', "value": 'PRESS', "alt": True},
|
||||||
{"properties": [("axis_range", True)]}),
|
{"properties": [("axis_range", True)]}),
|
||||||
|
@ -2430,6 +2431,7 @@ def km_dopesheet(params):
|
||||||
("action.select_more", {"type": 'NUMPAD_PLUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
|
("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_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
|
||||||
("action.select_linked", {"type": 'L', "value": 'PRESS'}, 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),
|
("action.frame_jump", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
|
||||||
(
|
(
|
||||||
op_menu_pie("DOPESHEET_MT_snap_pie", {"type": 'S', "value": 'PRESS', "shift": True})
|
op_menu_pie("DOPESHEET_MT_snap_pie", {"type": 'S', "value": 'PRESS', "shift": True})
|
||||||
|
@ -2556,6 +2558,7 @@ def km_nla_editor(params):
|
||||||
("nla.select_leftright", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
|
("nla.select_leftright", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
|
||||||
{"properties": [("mode", 'RIGHT')]}),
|
{"properties": [("mode", 'RIGHT')]}),
|
||||||
*_template_items_select_actions(params, "nla.select_all"),
|
*_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'},
|
("nla.select_box", {"type": 'B', "value": 'PRESS'},
|
||||||
{"properties": [("axis_range", False)]}),
|
{"properties": [("axis_range", False)]}),
|
||||||
("nla.select_box", {"type": 'B', "value": 'PRESS', "alt": True},
|
("nla.select_box", {"type": 'B', "value": 'PRESS', "alt": True},
|
||||||
|
|
|
@ -903,6 +903,7 @@ def km_graph_editor(params):
|
||||||
{"properties": [("mode", 'LEFT'), ("extend", False)]}),
|
{"properties": [("mode", 'LEFT'), ("extend", False)]}),
|
||||||
("graph.select_leftright", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
|
("graph.select_leftright", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
|
||||||
{"properties": [("mode", 'RIGHT'), ("extend", False)]}),
|
{"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}, {"properties": [("action", 'SELECT')]}),
|
||||||
("graph.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True,
|
("graph.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True,
|
||||||
"shift": True}, {"properties": [("action", 'DESELECT')]}),
|
"shift": True}, {"properties": [("action", 'DESELECT')]}),
|
||||||
|
@ -1393,6 +1394,7 @@ def km_dopesheet(params):
|
||||||
("action.select_more", {"type": 'UP_ARROW', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
|
("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_less", {"type": 'DOWN_ARROW', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
|
||||||
("action.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, 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),
|
("action.frame_jump", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
|
||||||
("wm.context_menu_enum", {"type": 'X', "value": 'PRESS'},
|
("wm.context_menu_enum", {"type": 'X', "value": 'PRESS'},
|
||||||
{"properties": [("data_path", 'space_data.auto_snap')]}),
|
{"properties": [("data_path", 'space_data.auto_snap')]}),
|
||||||
|
@ -1495,6 +1497,7 @@ def km_nla_editor(params):
|
||||||
{"properties": [("mode", 'LEFT'), ("extend", False)]}),
|
{"properties": [("mode", 'LEFT'), ("extend", False)]}),
|
||||||
("nla.select_leftright", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
|
("nla.select_leftright", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
|
||||||
{"properties": [("mode", 'RIGHT'), ("extend", False)]}),
|
{"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}, {"properties": [("action", 'SELECT')]}),
|
||||||
("nla.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True,
|
("nla.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True,
|
||||||
"shift": True}, {"properties": [("action", 'DESELECT')]}),
|
"shift": True}, {"properties": [("action", 'DESELECT')]}),
|
||||||
|
|
|
@ -415,6 +415,7 @@ class DOPESHEET_MT_select(Menu):
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.operator("action.select_linked")
|
layout.operator("action.select_linked")
|
||||||
|
layout.operator_menu_enum("action.select_grouped", "type", text="Select Grouped")
|
||||||
|
|
||||||
|
|
||||||
class DOPESHEET_MT_marker(Menu):
|
class DOPESHEET_MT_marker(Menu):
|
||||||
|
|
|
@ -178,6 +178,9 @@ class GRAPH_MT_select(Menu):
|
||||||
props.extend = False
|
props.extend = False
|
||||||
props.mode = 'RIGHT'
|
props.mode = 'RIGHT'
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.operator_menu_enum("graph.select_grouped", "type", text="Select Grouped")
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
layout.operator("graph.select_more")
|
layout.operator("graph.select_more")
|
||||||
layout.operator("graph.select_less")
|
layout.operator("graph.select_less")
|
||||||
|
|
|
@ -138,6 +138,9 @@ class NLA_MT_select(Menu):
|
||||||
props.extend = False
|
props.extend = False
|
||||||
props.mode = 'RIGHT'
|
props.mode = 'RIGHT'
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
layout.operator_menu_enum("nla.select_grouped", "type", text="Select Grouped")
|
||||||
|
|
||||||
|
|
||||||
class NLA_MT_marker(Menu):
|
class NLA_MT_marker(Menu):
|
||||||
bl_label = "Marker"
|
bl_label = "Marker"
|
||||||
|
|
|
@ -51,6 +51,7 @@ void ACTION_OT_select_linked(struct wmOperatorType *ot);
|
||||||
void ACTION_OT_select_more(struct wmOperatorType *ot);
|
void ACTION_OT_select_more(struct wmOperatorType *ot);
|
||||||
void ACTION_OT_select_less(struct wmOperatorType *ot);
|
void ACTION_OT_select_less(struct wmOperatorType *ot);
|
||||||
void ACTION_OT_select_leftright(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);
|
void ACTION_OT_clickselect(struct wmOperatorType *ot);
|
||||||
|
|
||||||
/* defines for left-right select tool */
|
/* defines for left-right select tool */
|
||||||
|
|
|
@ -36,6 +36,7 @@ void action_operatortypes(void)
|
||||||
WM_operatortype_append(ACTION_OT_select_more);
|
WM_operatortype_append(ACTION_OT_select_more);
|
||||||
WM_operatortype_append(ACTION_OT_select_less);
|
WM_operatortype_append(ACTION_OT_select_less);
|
||||||
WM_operatortype_append(ACTION_OT_select_leftright);
|
WM_operatortype_append(ACTION_OT_select_leftright);
|
||||||
|
WM_operatortype_append(ACTION_OT_select_grouped);
|
||||||
|
|
||||||
/* editing */
|
/* editing */
|
||||||
WM_operatortype_append(ACTION_OT_snap);
|
WM_operatortype_append(ACTION_OT_snap);
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include "BLI_blenlib.h"
|
#include "BLI_blenlib.h"
|
||||||
#include "BLI_dlrbTree.h"
|
#include "BLI_dlrbTree.h"
|
||||||
|
#include "BLI_ghash.h"
|
||||||
#include "BLI_lasso_2d.h"
|
#include "BLI_lasso_2d.h"
|
||||||
#include "BLI_utildefines.h"
|
#include "BLI_utildefines.h"
|
||||||
|
|
||||||
|
@ -1906,3 +1907,403 @@ void ACTION_OT_clickselect(wmOperatorType *ot)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
|
|
||||||
|
/* Select Grouped Operator */
|
||||||
|
|
||||||
|
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,
|
||||||
|
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 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,
|
||||||
|
"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 have all of the same types of 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_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_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)
|
||||||
|
{
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ACTKEYS_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ACTKEYS_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ACTKEYS_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 == 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__);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ACTKEYS_SELECT_GROUP_CHANNEL_TYPE: {
|
||||||
|
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: {
|
||||||
|
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: {
|
||||||
|
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: {
|
||||||
|
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: {
|
||||||
|
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: {
|
||||||
|
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 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. */
|
||||||
|
if (type == ACTKEYS_SELECT_GROUP_DATABLOCK && !BLI_gset_haskey(ked.data, ale->id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ANIM_fcurve_keyframes_loop(&ked, fcu, ok_cb, select_cb, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
ANIM_animdata_freelist(&anim_data);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 channels as the selected ones");
|
||||||
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ void GRAPH_OT_select_linked(struct wmOperatorType *ot);
|
||||||
void GRAPH_OT_select_more(struct wmOperatorType *ot);
|
void GRAPH_OT_select_more(struct wmOperatorType *ot);
|
||||||
void GRAPH_OT_select_less(struct wmOperatorType *ot);
|
void GRAPH_OT_select_less(struct wmOperatorType *ot);
|
||||||
void GRAPH_OT_select_leftright(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);
|
void GRAPH_OT_clickselect(struct wmOperatorType *ot);
|
||||||
|
|
||||||
/* defines for left-right select tool */
|
/* defines for left-right select tool */
|
||||||
|
|
|
@ -442,6 +442,7 @@ void graphedit_operatortypes(void)
|
||||||
WM_operatortype_append(GRAPH_OT_select_more);
|
WM_operatortype_append(GRAPH_OT_select_more);
|
||||||
WM_operatortype_append(GRAPH_OT_select_less);
|
WM_operatortype_append(GRAPH_OT_select_less);
|
||||||
WM_operatortype_append(GRAPH_OT_select_leftright);
|
WM_operatortype_append(GRAPH_OT_select_leftright);
|
||||||
|
WM_operatortype_append(GRAPH_OT_select_grouped);
|
||||||
|
|
||||||
/* editing */
|
/* editing */
|
||||||
WM_operatortype_append(GRAPH_OT_snap);
|
WM_operatortype_append(GRAPH_OT_snap);
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "MEM_guardedalloc.h"
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
#include "BLI_blenlib.h"
|
#include "BLI_blenlib.h"
|
||||||
|
#include "BLI_ghash.h"
|
||||||
#include "BLI_lasso_2d.h"
|
#include "BLI_lasso_2d.h"
|
||||||
#include "BLI_math.h"
|
#include "BLI_math.h"
|
||||||
#include "BLI_utildefines.h"
|
#include "BLI_utildefines.h"
|
||||||
|
@ -2020,3 +2021,462 @@ 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_CHANNEL_TYPE_STRICT,
|
||||||
|
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,
|
||||||
|
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 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,
|
||||||
|
"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,
|
||||||
|
"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 have all of the same types of 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_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_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)
|
||||||
|
{
|
||||||
|
/* 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_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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 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__);
|
||||||
|
}
|
||||||
|
|
||||||
|
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: {
|
||||||
|
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: {
|
||||||
|
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_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
ANIM_fcurve_keyframes_loop(ked, fcu, ok_cb, select_grouped_active_key_type, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GRAPHKEYS_SELECT_GROUP_MODIFIERS: {
|
||||||
|
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: {
|
||||||
|
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;
|
||||||
|
ked.iterflags |= KEYFRAME_ITER_INCL_HANDLES;
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ enum eNlaEdit_LeftRightSelect_Mode {
|
||||||
void NLA_OT_select_all(wmOperatorType *ot);
|
void NLA_OT_select_all(wmOperatorType *ot);
|
||||||
void NLA_OT_select_box(wmOperatorType *ot);
|
void NLA_OT_select_box(wmOperatorType *ot);
|
||||||
void NLA_OT_select_leftright(wmOperatorType *ot);
|
void NLA_OT_select_leftright(wmOperatorType *ot);
|
||||||
|
void NLA_OT_select_grouped(wmOperatorType *ot);
|
||||||
void NLA_OT_click_select(wmOperatorType *ot);
|
void NLA_OT_click_select(wmOperatorType *ot);
|
||||||
|
|
||||||
/* **************************************** */
|
/* **************************************** */
|
||||||
|
|
|
@ -97,6 +97,7 @@ void nla_operatortypes(void)
|
||||||
WM_operatortype_append(NLA_OT_select_box);
|
WM_operatortype_append(NLA_OT_select_box);
|
||||||
WM_operatortype_append(NLA_OT_select_all);
|
WM_operatortype_append(NLA_OT_select_all);
|
||||||
WM_operatortype_append(NLA_OT_select_leftright);
|
WM_operatortype_append(NLA_OT_select_leftright);
|
||||||
|
WM_operatortype_append(NLA_OT_select_grouped);
|
||||||
|
|
||||||
/* view */
|
/* view */
|
||||||
WM_operatortype_append(NLA_OT_view_all);
|
WM_operatortype_append(NLA_OT_view_all);
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "MEM_guardedalloc.h"
|
#include "MEM_guardedalloc.h"
|
||||||
|
|
||||||
#include "BLI_blenlib.h"
|
#include "BLI_blenlib.h"
|
||||||
|
#include "BLI_ghash.h"
|
||||||
#include "BLI_math_base.h"
|
#include "BLI_math_base.h"
|
||||||
|
|
||||||
#include "BKE_context.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) == 0) {
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue