static void region_select_action_keys(bAnimContext *ac, const rctf *rectf_view, short mode, short selectmode, void *data) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; KeyframeEditData ked; KeyframeEditFunc ok_cb, select_cb; View2D *v2d = &ac->ar->v2d; rctf rectf, scaled_rectf; float ymin = 0, ymax = (float)(-ACHANNEL_HEIGHT_HALF(ac)); /* convert mouse coordinates to frame ranges and channel coordinates corrected for view pan/zoom */ UI_view2d_region_to_view_rctf(v2d, rectf_view, &rectf); /* filter data */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* get beztriple editing/validation funcs */ select_cb = ANIM_editkeyframes_select(selectmode); ok_cb = ANIM_editkeyframes_ok(mode); /* init editing data */ memset(&ked, 0, sizeof(KeyframeEditData)); if (mode == BEZT_OK_CHANNEL_LASSO) { KeyframeEdit_LassoData *data_lasso = data; data_lasso->rectf_scaled = &scaled_rectf; ked.data = data_lasso; } else if (mode == BEZT_OK_CHANNEL_CIRCLE) { KeyframeEdit_CircleData *data_circle = data; data_circle->rectf_scaled = &scaled_rectf; ked.data = data; } else { ked.data = &scaled_rectf; } /* loop over data, doing region select */ for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt = ANIM_nla_mapping_get(ac, ale); /* get new vertical minimum extent of channel */ ymin = ymax - ACHANNEL_STEP(ac); /* compute midpoint of channel (used for testing if the key is in the region or not) */ ked.channel_y = ymin + ACHANNEL_HEIGHT_HALF(ac); /* if channel is mapped in NLA, apply correction * - Apply to the bounds being checked, not all the keyframe points, * to avoid having scaling everything * - Save result to the scaled_rect, which is all that these operators * will read from */ if (adt) { ked.iterflags &= ~(KED_F1_NLA_UNMAP | KED_F2_NLA_UNMAP); ked.f1 = BKE_nla_tweakedit_remap(adt, rectf.xmin, NLATIME_CONVERT_UNMAP); ked.f2 = BKE_nla_tweakedit_remap(adt, rectf.xmax, NLATIME_CONVERT_UNMAP); } else { ked.iterflags |= (KED_F1_NLA_UNMAP | KED_F2_NLA_UNMAP); /* for summary tracks */ ked.f1 = rectf.xmin; ked.f2 = rectf.xmax; } /* Update values for scaled_rectf - which is used to compute the mapping in the callbacks * NOTE: Since summary tracks need late-binding remapping, the callbacks may overwrite these * with the properly remapped ked.f1/f2 values, when needed */ scaled_rectf.xmin = ked.f1; scaled_rectf.xmax = ked.f2; scaled_rectf.ymin = ymin; scaled_rectf.ymax = ymax; /* perform vertical suitability check (if applicable) */ if ((mode == ACTKEYS_BORDERSEL_FRAMERANGE) || !((ymax < rectf.ymin) || (ymin > rectf.ymax))) { /* loop over data selecting */ switch (ale->type) { #if 0 /* XXX: Keyframes are not currently shown here */ case ANIMTYPE_GPDATABLOCK: { bGPdata *gpd = ale->data; bGPDlayer *gpl; for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { ED_gplayer_frames_select_region(&ked, ale->data, mode, selectmode); } break; } #endif case ANIMTYPE_GPLAYER: { ED_gplayer_frames_select_region(&ked, ale->data, mode, selectmode); break; } case ANIMTYPE_MASKDATABLOCK: { Mask *mask = ale->data; MaskLayer *masklay; for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { ED_masklayer_frames_select_region(&ked, masklay, mode, selectmode); } break; } case ANIMTYPE_MASKLAYER: { ED_masklayer_frames_select_region(&ked, ale->data, mode, selectmode); break; } default: ANIM_animchannel_keyframes_loop(&ked, ac->ads, ale, ok_cb, select_cb, NULL); break; } } /* set minimum extent to be the maximum of the next channel */ ymax = ymin; } /* cleanup */ ANIM_animdata_freelist(&anim_data); }
static void borderselect_action(bAnimContext *ac, const rcti rect, short mode, short selectmode) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; KeyframeEditData ked; KeyframeEditFunc ok_cb, select_cb; View2D *v2d = &ac->ar->v2d; rctf rectf; float ymin = 0, ymax = (float)(-ACHANNEL_HEIGHT_HALF(ac)); /* convert mouse coordinates to frame ranges and channel coordinates corrected for view pan/zoom */ UI_view2d_region_to_view(v2d, rect.xmin, rect.ymin + 2, &rectf.xmin, &rectf.ymin); UI_view2d_region_to_view(v2d, rect.xmax, rect.ymax - 2, &rectf.xmax, &rectf.ymax); /* filter data */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* get beztriple editing/validation funcs */ select_cb = ANIM_editkeyframes_select(selectmode); if (ELEM(mode, ACTKEYS_BORDERSEL_FRAMERANGE, ACTKEYS_BORDERSEL_ALLKEYS)) ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE); else ok_cb = NULL; /* init editing data */ memset(&ked, 0, sizeof(KeyframeEditData)); /* loop over data, doing border select */ for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt = ANIM_nla_mapping_get(ac, ale); /* get new vertical minimum extent of channel */ ymin = ymax - ACHANNEL_STEP(ac); /* set horizontal range (if applicable) */ if (ELEM(mode, ACTKEYS_BORDERSEL_FRAMERANGE, ACTKEYS_BORDERSEL_ALLKEYS)) { /* if channel is mapped in NLA, apply correction */ if (adt) { ked.iterflags &= ~(KED_F1_NLA_UNMAP | KED_F2_NLA_UNMAP); ked.f1 = BKE_nla_tweakedit_remap(adt, rectf.xmin, NLATIME_CONVERT_UNMAP); ked.f2 = BKE_nla_tweakedit_remap(adt, rectf.xmax, NLATIME_CONVERT_UNMAP); } else { ked.iterflags |= (KED_F1_NLA_UNMAP | KED_F2_NLA_UNMAP); /* for summary tracks */ ked.f1 = rectf.xmin; ked.f2 = rectf.xmax; } } /* perform vertical suitability check (if applicable) */ if ((mode == ACTKEYS_BORDERSEL_FRAMERANGE) || !((ymax < rectf.ymin) || (ymin > rectf.ymax))) { /* loop over data selecting */ switch (ale->type) { #if 0 /* XXX: Keyframes are not currently shown here */ case ANIMTYPE_GPDATABLOCK: { bGPdata *gpd = ale->data; bGPDlayer *gpl; for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { ED_gplayer_frames_select_border(gpl, rectf.xmin, rectf.xmax, selectmode); } break; } #endif case ANIMTYPE_GPLAYER: ED_gplayer_frames_select_border(ale->data, rectf.xmin, rectf.xmax, selectmode); break; case ANIMTYPE_MASKDATABLOCK: { Mask *mask = ale->data; MaskLayer *masklay; for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { ED_masklayer_frames_select_border(masklay, rectf.xmin, rectf.xmax, selectmode); } break; } case ANIMTYPE_MASKLAYER: ED_masklayer_frames_select_border(ale->data, rectf.xmin, rectf.xmax, selectmode); break; default: ANIM_animchannel_keyframes_loop(&ked, ac->ads, ale, ok_cb, select_cb, NULL); break; } } /* set minimum extent to be the maximum of the next channel */ ymax = ymin; } /* cleanup */ ANIM_animdata_freelist(&anim_data); }
/* left hand part */ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; View2D *v2d = &ar->v2d; float y = 0.0f; size_t items; int height; /* build list of channels to draw */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); height = ((items * ACHANNEL_STEP(ac)) + (ACHANNEL_HEIGHT(ac))); if (height > BLI_rcti_size_y(&v2d->mask)) { /* don't use totrect set, as the width stays the same * (NOTE: this is ok here, the configuration is pretty straightforward) */ v2d->tot.ymin = (float)(-height); } /* need to do a view-sync here, so that the keys area doesn't jump around (it must copy this) */ UI_view2d_sync(NULL, ac->sa, v2d, V2D_LOCK_COPY); /* loop through channels, and set up drawing depending on their type */ { /* first pass: just the standard GL-drawing for backdrop + text */ size_t channel_index = 0; y = (float)ACHANNEL_FIRST(ac); for (ale = anim_data.first; ale; ale = ale->next) { float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac)); float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac)); /* check if visible */ if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) { /* draw all channels using standard channel-drawing API */ ANIM_channel_draw(ac, ale, yminc, ymaxc, channel_index); } /* adjust y-position for next one */ y -= ACHANNEL_STEP(ac); channel_index++; } } { /* second pass: widgets */ uiBlock *block = UI_block_begin(C, ar, __func__, UI_EMBOSS); size_t channel_index = 0; y = (float)ACHANNEL_FIRST(ac); for (ale = anim_data.first; ale; ale = ale->next) { float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac)); float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac)); /* check if visible */ if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) { /* draw all channels using standard channel-drawing API */ ANIM_channel_draw_widgets(C, ac, ale, block, yminc, ymaxc, channel_index); } /* adjust y-position for next one */ y -= ACHANNEL_STEP(ac); channel_index++; } UI_block_end(C, block); UI_block_draw(C, block); } /* free tempolary channels */ ANIM_animdata_freelist(&anim_data); }
static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_mode, bool column, bool same_channel) { ListBase anim_data = {NULL, NULL}; DLRBT_Tree anim_keys; bAnimListElem *ale; int filter; View2D *v2d = &ac->ar->v2d; bDopeSheet *ads = NULL; int channel_index; bool found = false; float frame = 0.0f; /* frame of keyframe under mouse - NLA corrections not applied/included */ float selx = 0.0f; /* frame of keyframe under mouse */ float key_hsize; float x, y; rctf rectf; /* get dopesheet info */ if (ac->datatype == ANIMCONT_DOPESHEET) ads = ac->data; /* use View2D to determine the index of the channel (i.e a row in the list) where keyframe was */ UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y); UI_view2d_listview_view_to_cell(v2d, 0, ACHANNEL_STEP(ac), 0, (float)ACHANNEL_HEIGHT_HALF(ac), x, y, NULL, &channel_index); /* x-range to check is +/- 7px for standard keyframe under standard dpi/y-scale (in screen/region-space), * on either side of mouse click (size of keyframe icon) */ key_hsize = ACHANNEL_HEIGHT(ac) * 0.8f; /* standard channel height (to allow for some slop) */ key_hsize = roundf(key_hsize / 2.0f); /* half-size (for either side), but rounded up to nearest int (for easier targetting) */ UI_view2d_region_to_view(v2d, mval[0] - (int)key_hsize, mval[1], &rectf.xmin, &rectf.ymin); UI_view2d_region_to_view(v2d, mval[0] + (int)key_hsize, mval[1], &rectf.xmax, &rectf.ymax); /* filter data */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* try to get channel */ ale = BLI_findlink(&anim_data, channel_index); if (ale == NULL) { /* channel not found */ printf("Error: animation channel (index = %d) not found in mouse_action_keys()\n", channel_index); ANIM_animdata_freelist(&anim_data); return; } else { /* found match - must return here... */ AnimData *adt = ANIM_nla_mapping_get(ac, ale); ActKeyColumn *ak, *akn = NULL; /* make list of keyframes */ BLI_dlrbTree_init(&anim_keys); 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; ob_to_keylist(ads, ob, &anim_keys, NULL); break; } case ALE_ACT: { bAction *act = (bAction *)ale->key_data; action_to_keylist(adt, act, &anim_keys, NULL); break; } case ALE_FCURVE: { FCurve *fcu = (FCurve *)ale->key_data; fcurve_to_keylist(adt, fcu, &anim_keys, NULL); break; } } } else if (ale->type == ANIMTYPE_SUMMARY) { /* dopesheet summary covers everything */ summary_to_keylist(ac, &anim_keys, NULL); } else if (ale->type == ANIMTYPE_GROUP) { // TODO: why don't we just give groups key_data too? bActionGroup *agrp = (bActionGroup *)ale->data; agroup_to_keylist(adt, agrp, &anim_keys, NULL); } else if (ale->type == ANIMTYPE_GPLAYER) { // TODO: why don't we just give gplayers key_data too? bGPDlayer *gpl = (bGPDlayer *)ale->data; gpl_to_keylist(ads, gpl, &anim_keys); } else if (ale->type == ANIMTYPE_MASKLAYER) { // TODO: why don't we just give masklayers key_data too? MaskLayer *masklay = (MaskLayer *)ale->data; mask_to_keylist(ads, masklay, &anim_keys); } /* start from keyframe at root of BST, traversing until we find one within the range that was clicked on */ for (ak = anim_keys.root; ak; ak = akn) { if (IN_RANGE(ak->cfra, rectf.xmin, rectf.xmax)) { /* set the frame to use, and apply inverse-correction for NLA-mapping * so that the frame will get selected by the selection functions without * requiring to map each frame once again... */ selx = BKE_nla_tweakedit_remap(adt, ak->cfra, NLATIME_CONVERT_UNMAP); frame = ak->cfra; found = true; break; } else if (ak->cfra < rectf.xmin) akn = ak->right; else akn = ak->left; } /* remove active channel from list of channels for separate treatment (since it's needed later on) */ BLI_remlink(&anim_data, ale); /* cleanup temporary lists */ BLI_dlrbTree_free(&anim_keys); /* free list of channels, since it's not used anymore */ ANIM_animdata_freelist(&anim_data); } /* for replacing selection, firstly need to clear existing selection */ if (select_mode == SELECT_REPLACE) { /* reset selection mode for next steps */ select_mode = SELECT_ADD; /* deselect all keyframes */ deselect_action_keys(ac, 0, SELECT_SUBTRACT); /* highlight channel clicked on */ if (ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET)) { /* deselect all other channels first */ ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); /* Highlight Action-Group or F-Curve? */ if (ale && ale->data) { if (ale->type == ANIMTYPE_GROUP) { bActionGroup *agrp = ale->data; agrp->flag |= AGRP_SELECTED; ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP); } else if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) { FCurve *fcu = ale->data; fcu->flag |= FCURVE_SELECTED; ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ale->type); } } } else if (ac->datatype == ANIMCONT_GPENCIL) { /* deselect all other channels first */ ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); /* Highlight GPencil Layer */ if ((ale && ale->data) && (ale->type == ANIMTYPE_GPLAYER)) { bGPDlayer *gpl = ale->data; gpl->flag |= GP_LAYER_SELECT; //gpencil_layer_setactive(gpd, gpl); } } else if (ac->datatype == ANIMCONT_MASK) { /* deselect all other channels first */ ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); /* Highlight GPencil Layer */ if ((ale && ale->data) && (ale->type == ANIMTYPE_MASKLAYER)) { MaskLayer *masklay = ale->data; masklay->flag |= MASK_LAYERFLAG_SELECT; //gpencil_layer_setactive(gpd, gpl); } } } /* only select keyframes if we clicked on a valid channel and hit something */ if (ale) { if (found) { /* apply selection to keyframes */ if (column) { /* select all keyframes in the same frame as the one we hit on the active channel * [T41077]: "frame" not "selx" here (i.e. no NLA corrections yet) as the code here * does that itself again as it needs to work on multiple datablocks */ actkeys_mselect_column(ac, select_mode, frame); } else if (same_channel) { /* select all keyframes in the active channel */ actkeys_mselect_channel_only(ac, ale, select_mode); } else { /* select the nominated keyframe on the given frame */ actkeys_mselect_single(ac, ale, select_mode, selx); } } /* free this channel */ MEM_freeN(ale); } }
/* draw keyframes in each channel */ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; View2D *v2d = &ar->v2d; bDopeSheet *ads = &saction->ads; AnimData *adt = NULL; float act_start, act_end, y; size_t items; int height; unsigned char col1[3], col2[3]; unsigned char col1a[3], col2a[3]; unsigned char col1b[3], col2b[3]; const bool show_group_colors = !(saction->flag & SACTION_NODRAWGCOLORS); /* get theme colors */ UI_GetThemeColor3ubv(TH_BACK, col2); UI_GetThemeColor3ubv(TH_HILITE, col1); UI_GetThemeColor3ubv(TH_GROUP, col2a); UI_GetThemeColor3ubv(TH_GROUP_ACTIVE, col1a); UI_GetThemeColor3ubv(TH_DOPESHEET_CHANNELOB, col1b); UI_GetThemeColor3ubv(TH_DOPESHEET_CHANNELSUBOB, col2b); /* set view-mapping rect (only used for x-axis), for NLA-scaling mapping with less calculation */ /* if in NLA there's a strip active, map the view */ if (ac->datatype == ANIMCONT_ACTION) { /* adt = ANIM_nla_mapping_get(ac, NULL); */ /* UNUSED */ /* start and end of action itself */ calc_action_range(ac->data, &act_start, &act_end, 0); } /* build list of channels to draw */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); height = ((items * ACHANNEL_STEP(ac)) + (ACHANNEL_HEIGHT(ac))); /* don't use totrect set, as the width stays the same * (NOTE: this is ok here, the configuration is pretty straightforward) */ v2d->tot.ymin = (float)(-height); /* first backdrop strips */ y = (float)(-ACHANNEL_HEIGHT(ac)); glEnable(GL_BLEND); for (ale = anim_data.first; ale; ale = ale->next) { const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac)); const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac)); /* check if visible */ if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) { const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); int sel = 0; /* determine if any need to draw channel */ if (ale->datatype != ALE_NONE) { /* determine if channel is selected */ if (acf->has_setting(ac, ale, ACHANNEL_SETTING_SELECT)) sel = ANIM_channel_setting_get(ac, ale, ACHANNEL_SETTING_SELECT); if (ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET, ANIMCONT_SHAPEKEY)) { switch (ale->type) { case ANIMTYPE_SUMMARY: { /* reddish color from NLA */ UI_ThemeColor4(TH_ANIM_ACTIVE); break; } case ANIMTYPE_SCENE: case ANIMTYPE_OBJECT: { if (sel) glColor4ub(col1b[0], col1b[1], col1b[2], 0x45); else glColor4ub(col1b[0], col1b[1], col1b[2], 0x22); break; } case ANIMTYPE_FILLACTD: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: { if (sel) glColor4ub(col2b[0], col2b[1], col2b[2], 0x45); else glColor4ub(col2b[0], col2b[1], col2b[2], 0x22); break; } case ANIMTYPE_GROUP: { bActionGroup *agrp = ale->data; if (show_group_colors && agrp->customCol) { if (sel) { unsigned char *cp = (unsigned char *)agrp->cs.select; glColor4ub(cp[0], cp[1], cp[2], 0x45); } else { unsigned char *cp = (unsigned char *)agrp->cs.solid; glColor4ub(cp[0], cp[1], cp[2], 0x1D); } } else { if (sel) glColor4ub(col1a[0], col1a[1], col1a[2], 0x22); else glColor4ub(col2a[0], col2a[1], col2a[2], 0x22); } break; } case ANIMTYPE_FCURVE: { FCurve *fcu = ale->data; if (show_group_colors && fcu->grp && fcu->grp->customCol) { unsigned char *cp = (unsigned char *)fcu->grp->cs.active; if (sel) glColor4ub(cp[0], cp[1], cp[2], 0x65); else glColor4ub(cp[0], cp[1], cp[2], 0x0B); } else { if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22); else glColor4ub(col2[0], col2[1], col2[2], 0x22); } break; } default: { if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22); else glColor4ub(col2[0], col2[1], col2[2], 0x22); break; } } /* draw region twice: firstly backdrop, then the current range */ glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF(ac), v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF(ac)); if (ac->datatype == ANIMCONT_ACTION) glRectf(act_start, (float)y - ACHANNEL_HEIGHT_HALF(ac), act_end, (float)y + ACHANNEL_HEIGHT_HALF(ac)); } else if (ac->datatype == ANIMCONT_GPENCIL) { /* frames less than one get less saturated background */ if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22); else glColor4ub(col2[0], col2[1], col2[2], 0x22); glRectf(0.0f, (float)y - ACHANNEL_HEIGHT_HALF(ac), v2d->cur.xmin, (float)y + ACHANNEL_HEIGHT_HALF(ac)); /* frames one and higher get a saturated background */ if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x44); else glColor4ub(col2[0], col2[1], col2[2], 0x44); glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF(ac), v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF(ac)); } else if (ac->datatype == ANIMCONT_MASK) { /* TODO --- this is a copy of gpencil */ /* frames less than one get less saturated background */ if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22); else glColor4ub(col2[0], col2[1], col2[2], 0x22); glRectf(0.0f, (float)y - ACHANNEL_HEIGHT_HALF(ac), v2d->cur.xmin, (float)y + ACHANNEL_HEIGHT_HALF(ac)); /* frames one and higher get a saturated background */ if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x44); else glColor4ub(col2[0], col2[1], col2[2], 0x44); glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF(ac), v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF(ac)); } } } /* Increment the step */ y -= ACHANNEL_STEP(ac); } glDisable(GL_BLEND); /* Draw keyframes * 1) Only channels that are visible in the Action Editor get drawn/evaluated. * This is to try to optimize this for heavier data sets * 2) Keyframes which are out of view horizontally are disregarded */ y = (float)(-ACHANNEL_HEIGHT(ac)); for (ale = anim_data.first; ale; ale = ale->next) { const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac)); const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac)); /* check if visible */ if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) { /* check if anything to show for this channel */ if (ale->datatype != ALE_NONE) { adt = ANIM_nla_mapping_get(ac, ale); /* draw 'keyframes' for each specific datatype */ switch (ale->datatype) { case ALE_ALL: draw_summary_channel(v2d, ale->data, y, ac->yscale_fac); break; case ALE_SCE: draw_scene_channel(v2d, ads, ale->key_data, y, ac->yscale_fac); break; case ALE_OB: draw_object_channel(v2d, ads, ale->key_data, y, ac->yscale_fac); break; case ALE_ACT: draw_action_channel(v2d, adt, ale->key_data, y, ac->yscale_fac); break; case ALE_GROUP: draw_agroup_channel(v2d, adt, ale->data, y, ac->yscale_fac); break; case ALE_FCURVE: draw_fcurve_channel(v2d, adt, ale->key_data, y, ac->yscale_fac); break; case ALE_GPFRAME: draw_gpl_channel(v2d, ads, ale->data, y, ac->yscale_fac); break; case ALE_MASKLAY: draw_masklay_channel(v2d, ads, ale->data, y, ac->yscale_fac); break; } } } y -= ACHANNEL_STEP(ac); } /* free tempolary channels used for drawing */ ANIM_animdata_freelist(&anim_data); /* black line marking 'current frame' for Time-Slide transform mode */ if (saction->flag & SACTION_MOVING) { glColor3f(0.0f, 0.0f, 0.0f); glBegin(GL_LINES); glVertex2f(saction->timeslide, v2d->cur.ymin - EXTRA_SCROLL_PAD); glVertex2f(saction->timeslide, v2d->cur.ymax); glEnd(); } }