Пример #1
0
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);
}
Пример #2
0
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);
}
Пример #3
0
/* 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);
}
Пример #4
0
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);
	}
}
Пример #5
0
/* 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();
	}
}