Added a summary channel that appears as the first channel in the DopeSheet. For now, this is disabled by default, but can be enabled using the 'Summary' toggle in the header between the mode selector and the standard filtering options. This has been done, since there is a possibility that it will make the DopeSheet run a bit slower.
In this channel you can do everything that you can normally do with DopeSheet channels (i.e. select, transform, edit, etc). It might be worth noting though that care probably needs to be taken when trying to use Copy/Paste, since that is still a bit fidgety...
In the process, I've fixed a few bugs, mostly with selection:
- Selecting keyframes in scene summaries wouldn't work
- Border select only worked in F-Curve and Group channels
/* Settings ------------------------------------------- */
+/* channel type has no settings */
+static short acf_generic_none_setting_valid(bAnimContext *ac, bAnimListElem *ale, int setting)
+{
+ return 0;
+}
+
/* check if some setting exists for this object-based data-expander (category only) */
static short acf_generic_dsexpand_setting_valid(bAnimContext *ac, bAnimListElem *ale, int setting)
{
/* *********************************************** */
/* Type Specific Functions + Defines */
+/* Animation Summary ----------------------------------- */
+
+/* backdrop for summary widget */
+static void acf_summary_backdrop(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc)
+{
+ View2D *v2d= &ac->ar->v2d;
+
+ // FIXME: hardcoded color - same as the 'action' line in NLA
+ glColor3f(0.8f, 0.2f, 0.0f); // reddish color
+
+ /* rounded corners on LHS only
+ * - top and bottom
+ * - special hack: make the top a bit higher, since we are first...
+ */
+ uiSetRoundBox((1|8));
+ gl_round_box(GL_POLYGON, 0, yminc-2, v2d->cur.xmax+EXTRA_SCROLL_PAD, ymaxc, 8);
+}
+
+/* name for summary entries */
+static void acf_summary_name(bAnimListElem *ale, char *name)
+{
+ if (name)
+ strcpy(name, "DopeSheet Summary");
+}
+
+// TODO: this is really a temp icon I think
+static int acf_summary_icon(bAnimListElem *ale)
+{
+ return ICON_BORDERMOVE;
+}
+
+/* all animation summary (DopeSheet only) type define */
+static bAnimChannelType ACF_SUMMARY =
+{
+ acf_summary_backdrop, /* backdrop */
+ acf_generic_indention_0, /* indent level */
+ NULL, /* offset */
+
+ acf_summary_name, /* name */
+ acf_summary_icon, /* icon */
+
+ acf_generic_none_setting_valid, /* has setting */
+ NULL, /* flag for setting */
+ NULL /* pointer for setting */
+};
+
/* Scene ------------------------------------------- */
// TODO: just get this from RNA?
animchannelTypeInfo[type++]= NULL; /* AnimData */
animchannelTypeInfo[type++]= NULL; /* Special */
+ animchannelTypeInfo[type++]= &ACF_SUMMARY; /* Motion Summary */
+
animchannelTypeInfo[type++]= &ACF_SCENE; /* Scene */
animchannelTypeInfo[type++]= &ACF_OBJECT; /* Object */
animchannelTypeInfo[type++]= &ACF_GROUP; /* Group */
/* do specifics */
switch (datatype) {
+ case ANIMTYPE_SUMMARY:
+ {
+ /* nothing to include for now... this is just a dummy wrappy around all the other channels
+ * in the DopeSheet, and gets included at the start of the list
+ */
+ ale->key_data= NULL;
+ ale->datatype= ALE_ALL;
+ }
+ break;
+
case ANIMTYPE_SCENE:
{
Scene *sce= (Scene *)data;
}
// TODO: implement pinning... (if and when pinning is done, what we need to do is to provide freeing mechanisms - to protect against data that was deleted)
-static int animdata_filter_dopesheet (ListBase *anim_data, bDopeSheet *ads, int filter_mode)
+static int animdata_filter_dopesheet (ListBase *anim_data, bAnimContext *ac, bDopeSheet *ads, int filter_mode)
{
Scene *sce= (Scene *)ads->source;
Base *base;
return 0;
}
+ /* dopesheet summary
+ * - only for drawing and/or selecting keyframes in channels, but not for real editing
+ * - only useful for DopeSheet Editor, where the summary is useful
+ */
+ // TODO: we should really check if some other prohibited filters are also active, but that can be for later
+ if ((filter_mode & ANIMFILTER_CHANNELS) && (ads->filterflag & ADS_FILTER_SUMMARY)) {
+ ale= make_new_animlistelem(ac, ANIMTYPE_SUMMARY, NULL, ANIMTYPE_NONE, NULL);
+ if (ale) {
+ BLI_addtail(anim_data, ale);
+ items++;
+ }
+
+ // TODO: if the summary gets a collapse widget, then we could make the other stuff not get shown...
+ }
+
/* scene-linked animation */
// TODO: sequencer, composite nodes - are we to include those here too?
{
case ANIMCONT_FCURVES:
case ANIMCONT_DRIVERS:
case ANIMCONT_NLA:
- items= animdata_filter_dopesheet(anim_data, data, filter_mode);
+ items= animdata_filter_dopesheet(anim_data, ac, data, filter_mode);
break;
}
/* *************************** Channel Drawing Funcs *************************** */
+void draw_summary_channel(View2D *v2d, bAnimContext *ac, float ypos)
+{
+ DLRBT_Tree keys, blocks;
+
+ BLI_dlrbTree_init(&keys);
+ BLI_dlrbTree_init(&blocks);
+
+ summary_to_keylist(ac, &keys, &blocks);
+
+ BLI_dlrbTree_linkedlist_sync(&keys);
+ BLI_dlrbTree_linkedlist_sync(&blocks);
+
+ draw_keylist(v2d, &keys, &blocks, ypos);
+
+ BLI_dlrbTree_free(&keys);
+ BLI_dlrbTree_free(&blocks);
+}
+
void draw_scene_channel(View2D *v2d, bDopeSheet *ads, Scene *sce, float ypos)
{
DLRBT_Tree keys, blocks;
/* *************************** Keyframe List Conversions *************************** */
+void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys, DLRBT_Tree *blocks)
+{
+ if (ac) {
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale;
+ int filter;
+
+ /* get F-Curves to take keyframes from */
+ filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVESONLY);
+ ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+
+ /* loop through each F-Curve, grabbing the keyframes */
+ for (ale= anim_data.first; ale; ale= ale->next)
+ fcurve_to_keylist(ale->adt, ale->data, keys, blocks);
+
+ BLI_freelistN(&anim_data);
+ }
+}
+
void scene_to_keylist(bDopeSheet *ads, Scene *sce, DLRBT_Tree *keys, DLRBT_Tree *blocks)
{
if (sce) {
return 0;
/* Scene's own animation */
- if (sce->adt)
- adt_keys_bezier_loop(bed, sce->adt, bezt_ok, bezt_cb, fcu_cb, filterflag);
+ if (sce->adt) {
+ if (adt_keys_bezier_loop(bed, sce->adt, bezt_ok, bezt_cb, fcu_cb, filterflag))
+ return 1;
+ }
/* World */
- if (wo && wo->adt)
- adt_keys_bezier_loop(bed, wo->adt, bezt_ok, bezt_cb, fcu_cb, filterflag);
+ if (wo && wo->adt) {
+ if (adt_keys_bezier_loop(bed, wo->adt, bezt_ok, bezt_cb, fcu_cb, filterflag))
+ return 1;
+ }
return 0;
}
+/* This function is used to loop over the keyframe data in a DopeSheet summary */
+static short summary_keys_bezier_loop(BeztEditData *bed, bAnimContext *ac, BeztEditFunc bezt_ok, BeztEditFunc bezt_cb, FcuEditFunc fcu_cb, int filterflag)
+{
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale;
+ int filter, ret_code=0;
+
+ /* sanity check */
+ if (ac == NULL)
+ return 0;
+
+ /* get F-Curves to take keyframes from */
+ filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CURVESONLY);
+ ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+
+ /* loop through each F-Curve, working on the keyframes until the first curve aborts */
+ for (ale= anim_data.first; ale; ale= ale->next) {
+ ret_code= ANIM_fcurve_keys_bezier_loop(bed, ale->data, bezt_ok, bezt_cb, fcu_cb);
+
+ if (ret_code)
+ break;
+ }
+
+ BLI_freelistN(&anim_data);
+
+ return ret_code;
+}
+
/* --- */
/* This function is used to apply operation to all keyframes, regardless of the type */
return ob_keys_bezier_loop(bed, (Object *)ale->key_data, bezt_ok, bezt_cb, fcu_cb, filterflag);
case ALE_SCE: /* scene */
return scene_keys_bezier_loop(bed, (Scene *)ale->data, bezt_ok, bezt_cb, fcu_cb, filterflag);
+ case ALE_ALL: /* 'all' (DopeSheet summary) */
+ return summary_keys_bezier_loop(bed, (bAnimContext *)ale->data, bezt_ok, bezt_cb, fcu_cb, filterflag);
}
return 0;
return ob_keys_bezier_loop(bed, (Object *)data, bezt_ok, bezt_cb, fcu_cb, filterflag);
case ALE_SCE: /* scene */
return scene_keys_bezier_loop(bed, (Scene *)data, bezt_ok, bezt_cb, fcu_cb, filterflag);
+ case ALE_ALL: /* 'all' (DopeSheet summary) */
+ return summary_keys_bezier_loop(bed, (bAnimContext *)data, bezt_ok, bezt_cb, fcu_cb, filterflag);
}
return 0;
ANIMTYPE_ANIMDATA,
ANIMTYPE_SPECIALDATA,
+ ANIMTYPE_SUMMARY,
+
ANIMTYPE_SCENE,
ANIMTYPE_OBJECT,
ANIMTYPE_GROUP,
ALE_GPFRAME, /* Grease Pencil Frames */
ALE_NLASTRIP, /* NLA Strips */
+ ALE_ALL, /* All channels summary */
ALE_SCE, /* Scene summary */
ALE_OB, /* Object summary */
ALE_ACT, /* Action summary */
ANIMFILTER_ANIMDATA = (1<<9), /* only return the underlying AnimData blocks (not the tracks, etc.) data comes from */
ANIMFILTER_NLATRACKS = (1<<10), /* only include NLA-tracks */
ANIMFILTER_SELEDIT = (1<<11), /* link editability with selected status */
+
+ /* all filters - the power inside the bracket must be the last power for left-shifts + 1 */
+ ANIMFILTER_ALLFILTERS = ((1<<12) - 1)
} eAnimFilter_Flags;
#ifndef ED_KEYFRAMES_DRAW_H
#define ED_KEYFRAMES_DRAW_H
+struct bAnimContext;
struct AnimData;
struct BezTriple;
struct FCurve;
void draw_action_channel(struct View2D *v2d, struct AnimData *adt, struct bAction *act, float ypos);
void draw_object_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Object *ob, float ypos);
void draw_scene_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Scene *sce, float ypos);
+void draw_summary_channel(struct View2D *v2d, struct bAnimContext *ac, float ypos);
void draw_gpl_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPDlayer *gpl, float ypos);
/* Keydata Generation */
void action_to_keylist(struct AnimData *adt, struct bAction *act, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks);
void ob_to_keylist(struct bDopeSheet *ads, struct Object *ob, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks);
void scene_to_keylist(struct bDopeSheet *ads, struct Scene *sce, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks);
+void summary_to_keylist(struct bAnimContext *ac, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks);
void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks);
/* Keyframe Finding */
if (ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET)) {
switch (ale->type) {
+ case ANIMTYPE_SUMMARY:
+ {
+ // FIXME: hardcoded colours - reddish color from NLA
+ glColor4f(0.8f, 0.2f, 0.0f, 0.4f);
+ }
+ break;
+
case ANIMTYPE_SCENE:
case ANIMTYPE_OBJECT:
{
/* draw 'keyframes' for each specific datatype */
switch (ale->datatype) {
+ case ALE_ALL:
+ draw_summary_channel(v2d, ale->data, y);
+ break;
case ALE_SCE:
draw_scene_channel(v2d, ads, ale->key_data, y);
break;
/* MODE-DEPENDENT DRAWING */
if (saction->mode == SACTCONT_DOPESHEET) {
/* FILTERING OPTIONS */
- xco -= 10;
+ /* DopeSheet summary... */
+ uiDefIconTextButBitI(block, TOG, ADS_FILTER_SUMMARY, B_REDR, ICON_BORDERMOVE, "Summary", xco,yco,XIC*4,YIC, &(saction->ads.filterflag), 0, 0, 0, 0, "Include DopeSheet summary row"); // TODO: needs a better icon
+ xco += (XIC*3.5);
+ /* Standard filtering... */
xco= ANIM_headerUI_standard_buttons(C, &saction->ads, block, xco, yco);
}
else if (saction->mode == SACTCONT_ACTION) {
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
- int filter;
+ int filter, filterflag;
BeztEditData bed;
BeztEditFunc ok_cb, select_cb;
filter= (ANIMFILTER_VISIBLE | ANIMFILTER_CHANNELS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
+ /* get filtering flag for dopesheet data (if applicable) */
+ if (ac->datatype == ANIMCONT_DOPESHEET) {
+ bDopeSheet *ads= (bDopeSheet *)ac->data;
+ filterflag= ads->filterflag;
+ }
+ else
+ filterflag= 0;
+
/* get beztriple editing/validation funcs */
select_cb= ANIM_editkeyframes_select(selectmode);
!((ymax < rectf.ymin) || (ymin > rectf.ymax)) )
{
/* loop over data selecting */
- if (ale->key_data) {
- if (ale->datatype == ALE_FCURVE)
- ANIM_fcurve_keys_bezier_loop(&bed, ale->key_data, ok_cb, select_cb, NULL);
- }
- else if (ale->type == ANIMTYPE_GROUP) {
- bActionGroup *agrp= ale->data;
- FCurve *fcu;
-
- for (fcu= agrp->channels.first; fcu && fcu->grp==agrp; fcu= fcu->next)
- ANIM_fcurve_keys_bezier_loop(&bed, fcu, ok_cb, select_cb, NULL);
- }
- //else if (ale->type == ANIMTYPE_GPLAYER) {
+ //if (ale->type == ANIMTYPE_GPLAYER)
// borderselect_gplayer_frames(ale->data, rectf.xmin, rectf.xmax, selectmode);
- //}
+ //else
+ ANIM_animchannel_keys_bezier_loop(&bed, ale, ok_cb, select_cb, NULL, filterflag);
}
/* set minimum extent to be the maximum of the next channel */
if (ale->key_data) {
switch (ale->datatype) {
+ case ALE_SCE:
+ {
+ Scene *scene= (Scene *)ale->key_data;
+ scene_to_keylist(ads, scene, &anim_keys, NULL);
+ }
+ break;
case ALE_OB:
{
Object *ob= (Object *)ale->key_data;
break;
}
}
+ else if (ale->type == ANIMTYPE_SUMMARY) {
+ /* dopesheet summary covers everything */
+ summary_to_keylist(ac, &anim_keys, NULL);
+ }
else if (ale->type == ANIMTYPE_GROUP) {
bActionGroup *agrp= (bActionGroup *)ale->data;
agroup_to_keylist(adt, agrp, &anim_keys, NULL);
ADS_FILTER_ONLYDRIVERS = (1<<1), /* for 'Drivers' editor - only include Driver data from AnimData */
ADS_FILTER_ONLYNLA = (1<<2), /* for 'NLA' editor - only include NLA data from AnimData */
ADS_FILTER_SELEDIT = (1<<3), /* for Graph Editor - used to indicate whether to include a filtering flag or not */
+ ADS_FILTER_SUMMARY = (1<<4), /* for 'DopeSheet' Editor - include 'summary' line */
/* datatype-based filtering */
ADS_FILTER_NOSHAPEKEYS = (1<<6),