Animation Editors: Name-based filtering
I'm finally yielding to months of feature requesting, and adding support for filtering F-Curves by name, where the "name" here is the text which is displayed for each F-Curve in the Animation Editor channel lists. To use, just enable the magnifying-glass toggle on the DopeSheet filtering settings, and enter a snippet of text to find within the names of channels you wish to filter. This is case insensitive, and currently doesn't support any wildcard/regrex fanciness. Some examples: loc <--- location curves only x loc <--- x location curves only x eul <--- x rotation curves only rot <--- rotation curves only etc.
This commit is contained in:
@@ -33,48 +33,52 @@ def dopesheet_filter(layout, context, genericFiltersOnly=False):
|
|||||||
row.prop(dopesheet, "show_only_selected", text="")
|
row.prop(dopesheet, "show_only_selected", text="")
|
||||||
row.prop(dopesheet, "show_hidden", text="")
|
row.prop(dopesheet, "show_hidden", text="")
|
||||||
|
|
||||||
if genericFiltersOnly:
|
if not genericFiltersOnly:
|
||||||
return
|
|
||||||
|
|
||||||
row = layout.row(align=True)
|
|
||||||
row.prop(dopesheet, "show_transforms", text="")
|
|
||||||
|
|
||||||
if is_nla:
|
|
||||||
row.prop(dopesheet, "show_missing_nla", text="")
|
|
||||||
|
|
||||||
row = layout.row(align=True)
|
|
||||||
row.prop(dopesheet, "show_scenes", text="")
|
|
||||||
row.prop(dopesheet, "show_worlds", text="")
|
|
||||||
row.prop(dopesheet, "show_nodes", text="")
|
|
||||||
|
|
||||||
if bpy.data.meshes:
|
|
||||||
row.prop(dopesheet, "show_meshes", text="")
|
|
||||||
if bpy.data.shape_keys:
|
|
||||||
row.prop(dopesheet, "show_shapekeys", text="")
|
|
||||||
if bpy.data.materials:
|
|
||||||
row.prop(dopesheet, "show_materials", text="")
|
|
||||||
if bpy.data.lamps:
|
|
||||||
row.prop(dopesheet, "show_lamps", text="")
|
|
||||||
if bpy.data.textures:
|
|
||||||
row.prop(dopesheet, "show_textures", text="")
|
|
||||||
if bpy.data.cameras:
|
|
||||||
row.prop(dopesheet, "show_cameras", text="")
|
|
||||||
if bpy.data.curves:
|
|
||||||
row.prop(dopesheet, "show_curves", text="")
|
|
||||||
if bpy.data.metaballs:
|
|
||||||
row.prop(dopesheet, "show_metaballs", text="")
|
|
||||||
if bpy.data.lattices:
|
|
||||||
row.prop(dopesheet, "show_lattices", text="")
|
|
||||||
if bpy.data.armatures:
|
|
||||||
row.prop(dopesheet, "show_armatures", text="")
|
|
||||||
if bpy.data.particles:
|
|
||||||
row.prop(dopesheet, "show_particles", text="")
|
|
||||||
|
|
||||||
if bpy.data.groups:
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.prop(dopesheet, "show_only_group_objects", text="")
|
row.prop(dopesheet, "show_transforms", text="")
|
||||||
if dopesheet.show_only_group_objects:
|
|
||||||
row.prop(dopesheet, "filter_group", text="")
|
if is_nla:
|
||||||
|
row.prop(dopesheet, "show_missing_nla", text="")
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(dopesheet, "show_scenes", text="")
|
||||||
|
row.prop(dopesheet, "show_worlds", text="")
|
||||||
|
row.prop(dopesheet, "show_nodes", text="")
|
||||||
|
|
||||||
|
if bpy.data.meshes:
|
||||||
|
row.prop(dopesheet, "show_meshes", text="")
|
||||||
|
if bpy.data.shape_keys:
|
||||||
|
row.prop(dopesheet, "show_shapekeys", text="")
|
||||||
|
if bpy.data.materials:
|
||||||
|
row.prop(dopesheet, "show_materials", text="")
|
||||||
|
if bpy.data.lamps:
|
||||||
|
row.prop(dopesheet, "show_lamps", text="")
|
||||||
|
if bpy.data.textures:
|
||||||
|
row.prop(dopesheet, "show_textures", text="")
|
||||||
|
if bpy.data.cameras:
|
||||||
|
row.prop(dopesheet, "show_cameras", text="")
|
||||||
|
if bpy.data.curves:
|
||||||
|
row.prop(dopesheet, "show_curves", text="")
|
||||||
|
if bpy.data.metaballs:
|
||||||
|
row.prop(dopesheet, "show_metaballs", text="")
|
||||||
|
if bpy.data.lattices:
|
||||||
|
row.prop(dopesheet, "show_lattices", text="")
|
||||||
|
if bpy.data.armatures:
|
||||||
|
row.prop(dopesheet, "show_armatures", text="")
|
||||||
|
if bpy.data.particles:
|
||||||
|
row.prop(dopesheet, "show_particles", text="")
|
||||||
|
|
||||||
|
if bpy.data.groups:
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(dopesheet, "show_only_group_objects", text="")
|
||||||
|
if dopesheet.show_only_group_objects:
|
||||||
|
row.prop(dopesheet, "filter_group", text="")
|
||||||
|
|
||||||
|
if not is_nla:
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.prop(dopesheet, "show_only_matching_fcurves", text="")
|
||||||
|
if dopesheet.show_only_matching_fcurves:
|
||||||
|
row.prop(dopesheet, "filter_fcurve_name", text="")
|
||||||
|
|
||||||
|
|
||||||
#######################################
|
#######################################
|
||||||
|
@@ -804,7 +804,9 @@ static bAnimListElem *make_new_animlistelem (void *data, short datatype, void *o
|
|||||||
|
|
||||||
/* ----------------------------------------- */
|
/* ----------------------------------------- */
|
||||||
|
|
||||||
/* NOTE: when this function returns true, the F-Curve is to be skipped */
|
/* 'Only Selected' selected data filtering
|
||||||
|
* NOTE: when this function returns true, the F-Curve is to be skipped
|
||||||
|
*/
|
||||||
static int skip_fcurve_selected_data (bDopeSheet *ads, FCurve *fcu, ID *owner_id, int filter_mode)
|
static int skip_fcurve_selected_data (bDopeSheet *ads, FCurve *fcu, ID *owner_id, int filter_mode)
|
||||||
{
|
{
|
||||||
if (GS(owner_id->name) == ID_OB) {
|
if (GS(owner_id->name) == ID_OB) {
|
||||||
@@ -876,6 +878,37 @@ static int skip_fcurve_selected_data (bDopeSheet *ads, FCurve *fcu, ID *owner_id
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* (Display-)Name-based F-Curve filtering
|
||||||
|
* NOTE: when this function returns true, the F-Curve is to be skipped
|
||||||
|
*/
|
||||||
|
static short skip_fcurve_with_name (bDopeSheet *ads, FCurve *fcu, ID *owner_id)
|
||||||
|
{
|
||||||
|
bAnimListElem ale_dummy = {0};
|
||||||
|
bAnimChannelType *acf;
|
||||||
|
|
||||||
|
/* create a dummy wrapper for the F-Curve */
|
||||||
|
ale_dummy.type = ANIMTYPE_FCURVE;
|
||||||
|
ale_dummy.id = owner_id;
|
||||||
|
ale_dummy.data = fcu;
|
||||||
|
|
||||||
|
/* get type info for channel */
|
||||||
|
acf = ANIM_channel_get_typeinfo(&ale_dummy);
|
||||||
|
if (acf && acf->name) {
|
||||||
|
char name[256]; /* hopefully this will be enough! */
|
||||||
|
|
||||||
|
/* get name */
|
||||||
|
acf->name(&ale_dummy, name);
|
||||||
|
|
||||||
|
/* check for partial match with the match string, assuming case insensitive filtering
|
||||||
|
* if match, this channel shouldn't be ignored!
|
||||||
|
*/
|
||||||
|
return BLI_strcasestr(name, ads->searchstr) == NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* just let this go... */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* find the next F-Curve that is usable for inclusion */
|
/* find the next F-Curve that is usable for inclusion */
|
||||||
static FCurve *animdata_filter_fcurve_next (bDopeSheet *ads, FCurve *first, bActionGroup *grp, int filter_mode, ID *owner_id)
|
static FCurve *animdata_filter_fcurve_next (bDopeSheet *ads, FCurve *first, bActionGroup *grp, int filter_mode, ID *owner_id)
|
||||||
{
|
{
|
||||||
@@ -885,7 +918,7 @@ static FCurve *animdata_filter_fcurve_next (bDopeSheet *ads, FCurve *first, bAct
|
|||||||
* NOTE: we need to check if the F-Curves belong to the same group, as this gets called for groups too...
|
* NOTE: we need to check if the F-Curves belong to the same group, as this gets called for groups too...
|
||||||
*/
|
*/
|
||||||
for (fcu= first; ((fcu) && (fcu->grp==grp)); fcu= fcu->next) {
|
for (fcu= first; ((fcu) && (fcu->grp==grp)); fcu= fcu->next) {
|
||||||
/* special exception for Pose-Channel Based F-Curves:
|
/* special exception for Pose-Channel/Sequence-Strip/Node Based F-Curves:
|
||||||
* - the 'Only Selected' data filter should be applied to Pose-Channel data too, but those are
|
* - the 'Only Selected' data filter should be applied to Pose-Channel data too, but those are
|
||||||
* represented as F-Curves. The way the filter for objects worked was to be the first check
|
* represented as F-Curves. The way the filter for objects worked was to be the first check
|
||||||
* after 'normal' visibility, so this is done first here too...
|
* after 'normal' visibility, so this is done first here too...
|
||||||
@@ -897,7 +930,7 @@ static FCurve *animdata_filter_fcurve_next (bDopeSheet *ads, FCurve *first, bAct
|
|||||||
if (skip_fcurve_selected_data(ads, fcu, owner_id, filter_mode))
|
if (skip_fcurve_selected_data(ads, fcu, owner_id, filter_mode))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* only include if visible (Graph Editor check, not channels check) */
|
/* only include if visible (Graph Editor check, not channels check) */
|
||||||
if (!(filter_mode & ANIMFILTER_CURVEVISIBLE) || (fcu->flag & FCURVE_VISIBLE)) {
|
if (!(filter_mode & ANIMFILTER_CURVEVISIBLE) || (fcu->flag & FCURVE_VISIBLE)) {
|
||||||
/* only work with this channel and its subchannels if it is editable */
|
/* only work with this channel and its subchannels if it is editable */
|
||||||
@@ -906,6 +939,12 @@ static FCurve *animdata_filter_fcurve_next (bDopeSheet *ads, FCurve *first, bAct
|
|||||||
if ( ANIMCHANNEL_SELOK(SEL_FCU(fcu)) && ANIMCHANNEL_SELEDITOK(SEL_FCU(fcu)) ) {
|
if ( ANIMCHANNEL_SELOK(SEL_FCU(fcu)) && ANIMCHANNEL_SELEDITOK(SEL_FCU(fcu)) ) {
|
||||||
/* only include if this curve is active */
|
/* only include if this curve is active */
|
||||||
if (!(filter_mode & ANIMFILTER_ACTIVE) || (fcu->flag & FCURVE_ACTIVE)) {
|
if (!(filter_mode & ANIMFILTER_ACTIVE) || (fcu->flag & FCURVE_ACTIVE)) {
|
||||||
|
/* name based filtering... */
|
||||||
|
if ( ((ads) && (ads->filterflag & ADS_FILTER_BY_FCU_NAME)) && (owner_id) ) {
|
||||||
|
if (skip_fcurve_with_name(ads, fcu, owner_id))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* this F-Curve can be used, so return it */
|
/* this F-Curve can be used, so return it */
|
||||||
return fcu;
|
return fcu;
|
||||||
}
|
}
|
||||||
|
@@ -511,7 +511,8 @@ typedef struct bDopeSheet {
|
|||||||
ID *source; /* currently ID_SCE (for Dopesheet), and ID_SC (for Grease Pencil) */
|
ID *source; /* currently ID_SCE (for Dopesheet), and ID_SC (for Grease Pencil) */
|
||||||
ListBase chanbase; /* cache for channels (only initialised when pinned) */ // XXX not used!
|
ListBase chanbase; /* cache for channels (only initialised when pinned) */ // XXX not used!
|
||||||
|
|
||||||
struct Group *filter_grp; /* object group for ADS_FILTER_ONLYOBGROUP filtering option */
|
struct Group *filter_grp; /* object group for ADS_FILTER_ONLYOBGROUP filtering option */
|
||||||
|
char searchstr[64]; /* string to search for in displayed names of F-Curves for ADS_FILTER_BY_FCU_NAME filtering option */
|
||||||
|
|
||||||
int filterflag; /* flags to use for filtering data */
|
int filterflag; /* flags to use for filtering data */
|
||||||
int flag; /* standard flags */
|
int flag; /* standard flags */
|
||||||
@@ -554,6 +555,7 @@ typedef enum eDopeSheet_FilterFlag {
|
|||||||
|
|
||||||
/* general filtering 3 */
|
/* general filtering 3 */
|
||||||
ADS_FILTER_INCL_HIDDEN = (1<<26), /* include 'hidden' channels too (i.e. those from hidden Objects/Bones) */
|
ADS_FILTER_INCL_HIDDEN = (1<<26), /* include 'hidden' channels too (i.e. those from hidden Objects/Bones) */
|
||||||
|
ADS_FILTER_BY_FCU_NAME = (1<<27), /* for F-Curves, filter by the displayed name (i.e. to isolate all Location curves only) */
|
||||||
|
|
||||||
/* combination filters (some only used at runtime) */
|
/* combination filters (some only used at runtime) */
|
||||||
ADS_FILTER_NOOBDATA = (ADS_FILTER_NOCAM|ADS_FILTER_NOMAT|ADS_FILTER_NOLAM|ADS_FILTER_NOCUR|ADS_FILTER_NOPART|ADS_FILTER_NOARM)
|
ADS_FILTER_NOOBDATA = (ADS_FILTER_NOCAM|ADS_FILTER_NOMAT|ADS_FILTER_NOLAM|ADS_FILTER_NOCUR|ADS_FILTER_NOPART|ADS_FILTER_NOARM)
|
||||||
|
@@ -236,6 +236,18 @@ static void rna_def_dopesheet(BlenderRNA *brna)
|
|||||||
RNA_def_property_ui_text(prop, "Filtering Group", "Group that included Object should be a member of");
|
RNA_def_property_ui_text(prop, "Filtering Group", "Group that included Object should be a member of");
|
||||||
RNA_def_property_update(prop, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
|
RNA_def_property_update(prop, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
|
||||||
|
|
||||||
|
/* FCurve Display Name Search Settings */
|
||||||
|
prop= RNA_def_property(srna, "show_only_matching_fcurves", PROP_BOOLEAN, PROP_NONE);
|
||||||
|
RNA_def_property_boolean_sdna(prop, NULL, "filterflag", ADS_FILTER_BY_FCU_NAME);
|
||||||
|
RNA_def_property_ui_text(prop, "Only Matching F-Curves", "Only include F-Curves with names containing search text");
|
||||||
|
RNA_def_property_ui_icon(prop, ICON_VIEWZOOM, 0);
|
||||||
|
RNA_def_property_update(prop, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
|
||||||
|
|
||||||
|
prop= RNA_def_property(srna, "filter_fcurve_name", PROP_STRING, PROP_NONE);
|
||||||
|
RNA_def_property_string_sdna(prop, NULL, "searchstr");
|
||||||
|
RNA_def_property_ui_text(prop, "F-Curve Name Filter", "F-Curve live filtering string");
|
||||||
|
RNA_def_property_update(prop, NC_ANIMATION|ND_ANIMCHAN|NA_EDITED, NULL);
|
||||||
|
|
||||||
/* NLA Specific Settings */
|
/* NLA Specific Settings */
|
||||||
prop= RNA_def_property(srna, "show_missing_nla", PROP_BOOLEAN, PROP_NONE);
|
prop= RNA_def_property(srna, "show_missing_nla", PROP_BOOLEAN, PROP_NONE);
|
||||||
RNA_def_property_boolean_negative_sdna(prop, NULL, "filterflag", ADS_FILTER_NLA_NOACT);
|
RNA_def_property_boolean_negative_sdna(prop, NULL, "filterflag", ADS_FILTER_NLA_NOACT);
|
||||||
|
Reference in New Issue
Block a user