2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
19 * All rights reserved.
22 * Contributor(s): Joshua Leung (major recode)
24 * ***** END GPL LICENSE BLOCK *****
27 /** \file blender/editors/space_nla/nla_draw.c
38 #include "DNA_anim_types.h"
39 #include "DNA_node_types.h"
40 #include "DNA_screen_types.h"
41 #include "DNA_space_types.h"
42 #include "DNA_windowmanager_types.h"
44 #include "BLI_blenlib.h"
45 #include "BLI_dlrbTree.h"
46 #include "BLI_utildefines.h"
48 #include "BKE_fcurve.h"
50 #include "BKE_context.h"
51 #include "BKE_screen.h"
53 #include "ED_anim_api.h"
54 #include "ED_keyframes_draw.h"
57 #include "BIF_glutil.h"
61 #include "UI_interface.h"
62 #include "UI_resources.h"
63 #include "UI_view2d.h"
66 #include "nla_intern.h" /* own include */
69 /* *********************************************** */
72 /* Action-Line ---------------------- */
74 /* get colors for drawing Action-Line
75 * NOTE: color returned includes fine-tuned alpha!
77 void nla_action_get_color(AnimData *adt, bAction *act, float color[4])
79 if (adt && (adt->flag & ADT_NLA_EDIT_ON)) {
80 /* greenish color (same as tweaking strip) */
81 UI_GetThemeColor4fv(TH_NLA_TWEAK, color);
85 /* reddish color - same as dopesheet summary */
86 UI_GetThemeColor4fv(TH_ANIM_ACTIVE, color);
89 /* grayish-red color */
90 UI_GetThemeColor4fv(TH_ANIM_INACTIVE, color);
94 /* when an NLA track is tagged "solo", action doesn't contribute, so shouldn't be as prominent */
95 if (adt && (adt->flag & ADT_NLA_SOLO_TRACK))
99 /* draw the keyframes in the specified Action */
100 static void nla_action_draw_keyframes(AnimData *adt, bAction *act, View2D *v2d, float y, float ymin, float ymax)
104 float xscale, f1, f2;
107 /* get a list of the keyframes with NLA-scaling applied */
108 BLI_dlrbTree_init(&keys);
109 action_to_keylist(adt, act, &keys, NULL);
110 BLI_dlrbTree_linkedlist_sync(&keys);
112 if (ELEM(NULL, act, keys.first))
115 /* draw a darkened region behind the strips
116 * - get and reset the background color, this time without the alpha to stand out better
117 * (amplified alpha is used instead)
119 nla_action_get_color(adt, act, color);
123 /* - draw a rect from the first to the last frame (no extra overlaps for now)
124 * that is slightly stumpier than the track background (hardcoded 2-units here)
126 f1 = ((ActKeyColumn *)keys.first)->cfra;
127 f2 = ((ActKeyColumn *)keys.last)->cfra;
129 glRectf(f1, ymin + 2, f2, ymax - 2);
132 /* get View2D scaling factor */
133 UI_view2d_scale_get(v2d, &xscale, NULL);
135 /* for now, color is hardcoded to be black */
136 glColor3f(0.0f, 0.0f, 0.0f);
138 /* just draw each keyframe as a simple dot (regardless of the selection status)
139 * - size is 3.0f which is smaller than the editable keyframes, so that there is a distinction
141 for (ak = keys.first; ak; ak = ak->next)
142 draw_keyframe_shape(ak->cfra, y, xscale, 3.0f, 0, ak->key_type, KEYFRAME_SHAPE_FRAME, 1.0f);
145 BLI_dlrbTree_free(&keys);
148 /* Strips (Proper) ---------------------- */
150 /* get colors for drawing NLA-Strips */
151 static void nla_strip_get_color_inside(AnimData *adt, NlaStrip *strip, float color[3])
153 if (strip->type == NLASTRIP_TYPE_TRANSITION) {
154 /* Transition Clip */
155 if (strip->flag & NLASTRIP_FLAG_SELECT) {
156 /* selected - use a bright blue color */
157 UI_GetThemeColor3fv(TH_NLA_TRANSITION_SEL, color);
160 /* normal, unselected strip - use (hardly noticeable) blue tinge */
161 UI_GetThemeColor3fv(TH_NLA_TRANSITION, color);
164 else if (strip->type == NLASTRIP_TYPE_META) {
166 // TODO: should temporary metas get different colors too?
167 if (strip->flag & NLASTRIP_FLAG_SELECT) {
168 /* selected - use a bold purple color */
169 UI_GetThemeColor3fv(TH_NLA_META_SEL, color);
172 /* normal, unselected strip - use (hardly noticeable) dark purple tinge */
173 UI_GetThemeColor3fv(TH_NLA_META, color);
176 else if (strip->type == NLASTRIP_TYPE_SOUND) {
178 if (strip->flag & NLASTRIP_FLAG_SELECT) {
179 /* selected - use a bright teal color */
180 UI_GetThemeColor3fv(TH_NLA_SOUND_SEL, color);
183 /* normal, unselected strip - use (hardly noticeable) teal tinge */
184 UI_GetThemeColor3fv(TH_NLA_SOUND, color);
188 /* Action Clip (default/normal type of strip) */
189 if (adt && (adt->flag & ADT_NLA_EDIT_ON) && (adt->actstrip == strip)) {
190 /* active strip should be drawn green when it is acting as the tweaking strip.
191 * however, this case should be skipped for when not in EditMode...
193 UI_GetThemeColor3fv(TH_NLA_TWEAK, color);
195 else if (strip->flag & NLASTRIP_FLAG_TWEAKUSER) {
196 /* alert user that this strip is also used by the tweaking track (this is set when going into
197 * 'editmode' for that strip), since the edits made here may not be what the user anticipated
199 UI_GetThemeColor3fv(TH_NLA_TWEAK_DUPLI, color);
201 else if (strip->flag & NLASTRIP_FLAG_SELECT) {
202 /* selected strip - use theme color for selected */
203 UI_GetThemeColor3fv(TH_STRIP_SELECT, color);
206 /* normal, unselected strip - use standard strip theme color */
207 UI_GetThemeColor3fv(TH_STRIP, color);
212 /* helper call for drawing influence/time control curves for a given NLA-strip */
213 static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc)
215 const float yheight = ymaxc - yminc;
217 /* drawing color is simply a light-gray */
218 // TODO: is this color suitable?
219 // XXX nasty hacked color for now... which looks quite bad too...
220 glColor3f(0.7f, 0.7f, 0.7f);
222 /* draw with AA'd line */
223 glEnable(GL_LINE_SMOOTH);
226 /* influence -------------------------- */
227 if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) {
228 FCurve *fcu = list_find_fcurve(&strip->fcurves, "influence", 0);
231 /* plot the curve (over the strip's main region) */
232 glBegin(GL_LINE_STRIP);
233 /* sample at 1 frame intervals, and draw
234 * - min y-val is yminc, max is y-maxc, so clamp in those regions
236 for (cfra = strip->start; cfra <= strip->end; cfra += 1.0f) {
237 float y = evaluate_fcurve(fcu, cfra); // assume this to be in 0-1 range
238 glVertex2f(cfra, ((y * yheight) + yminc));
240 glEnd(); // GL_LINE_STRIP
243 /* use blend in/out values only if both aren't zero */
244 if ((IS_EQF(strip->blendin, 0.0f) && IS_EQF(strip->blendout, 0.0f)) == 0) {
245 glBegin(GL_LINE_STRIP);
246 /* start of strip - if no blendin, start straight at 1, otherwise from 0 to 1 over blendin frames */
247 if (IS_EQF(strip->blendin, 0.0f) == 0) {
248 glVertex2f(strip->start, yminc);
249 glVertex2f(strip->start + strip->blendin, ymaxc);
252 glVertex2f(strip->start, ymaxc);
255 if (IS_EQF(strip->blendout, 0.0f) == 0) {
256 glVertex2f(strip->end - strip->blendout, ymaxc);
257 glVertex2f(strip->end, yminc);
260 glVertex2f(strip->end, ymaxc);
261 glEnd(); // GL_LINE_STRIP
265 /* time -------------------------- */
266 // XXX do we want to draw this curve? in a different color too?
268 /* turn off AA'd lines */
269 glDisable(GL_LINE_SMOOTH);
273 /* main call for drawing a single NLA-strip */
274 static void nla_draw_strip(SpaceNla *snla, AnimData *adt, NlaTrack *nlt, NlaStrip *strip, View2D *v2d, float yminc, float ymaxc)
276 short nonSolo = ((adt && (adt->flag & ADT_NLA_SOLO_TRACK)) && (nlt->flag & NLATRACK_SOLO) == 0);
279 /* get color of strip */
280 nla_strip_get_color_inside(adt, strip, color);
282 /* draw extrapolation info first (as backdrop)
283 * - but this should only be drawn if track has some contribution
285 if ((strip->extendmode != NLASTRIP_EXTEND_NOTHING) && (nonSolo == 0)) {
286 /* enable transparency... */
287 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
290 switch (strip->extendmode) {
291 /* since this does both sides, only do the 'before' side, and leave the rest to the next case */
292 case NLASTRIP_EXTEND_HOLD:
293 /* only need to draw here if there's no strip before since
294 * it only applies in such a situation
296 if (strip->prev == NULL) {
297 /* set the drawing color to the color of the strip, but with very faint alpha */
298 glColor4f(color[0], color[1], color[2], 0.15f);
300 /* draw the rect to the edge of the screen */
302 glVertex2f(v2d->cur.xmin, yminc);
303 glVertex2f(v2d->cur.xmin, ymaxc);
304 glVertex2f(strip->start, ymaxc);
305 glVertex2f(strip->start, yminc);
310 /* this only draws after the strip */
311 case NLASTRIP_EXTEND_HOLD_FORWARD:
312 /* only need to try and draw if the next strip doesn't occur immediately after */
313 if ((strip->next == NULL) || (IS_EQF(strip->next->start, strip->end) == 0)) {
314 /* set the drawing color to the color of the strip, but this time less faint */
315 glColor4f(color[0], color[1], color[2], 0.3f);
317 /* draw the rect to the next strip or the edge of the screen */
319 glVertex2f(strip->end, yminc);
320 glVertex2f(strip->end, ymaxc);
323 glVertex2f(strip->next->start, ymaxc);
324 glVertex2f(strip->next->start, yminc);
327 glVertex2f(v2d->cur.xmax, ymaxc);
328 glVertex2f(v2d->cur.xmax, yminc);
339 /* draw 'inside' of strip itself */
341 /* strip is in normal track */
343 UI_draw_roundbox_corner_set(UI_CNR_ALL); /* all corners rounded */
345 UI_draw_roundbox_shade_x(GL_POLYGON, strip->start, yminc, strip->end, ymaxc, 0.0, 0.5, 0.1);
348 /* strip is in disabled track - make less visible */
349 glColor4f(color[0], color[1], color[2], 0.1f);
352 glRectf(strip->start, yminc, strip->end, ymaxc);
357 /* draw strip's control 'curves'
358 * - only if user hasn't hidden them...
360 if ((snla->flag & SNLA_NOSTRIPCURVES) == 0)
361 nla_draw_strip_curves(strip, yminc, ymaxc);
364 /* draw strip outline
365 * - color used here is to indicate active vs non-active
367 if (strip->flag & NLASTRIP_FLAG_ACTIVE) {
368 /* strip should appear 'sunken', so draw a light border around it */
369 glColor3f(0.9f, 1.0f, 0.9f); // FIXME: hardcoded temp-hack colors
372 /* strip should appear to stand out, so draw a dark border around it */
373 glColor3f(0.0f, 0.0f, 0.0f);
376 /* - line style: dotted for muted */
377 if ((nlt->flag & NLATRACK_MUTED) || (strip->flag & NLASTRIP_FLAG_MUTED))
381 UI_draw_roundbox_shade_x(GL_LINE_LOOP, strip->start, yminc, strip->end, ymaxc, 0.0, 0.0, 0.1);
383 /* if action-clip strip, draw lines delimiting repeats too (in the same color as outline) */
384 if ((strip->type == NLASTRIP_TYPE_CLIP) && IS_EQF(strip->repeat, 1.0f) == 0) {
385 float repeatLen = (strip->actend - strip->actstart) * strip->scale;
388 /* only draw lines for whole-numbered repeats, starting from the first full-repeat
389 * up to the last full repeat (but not if it lies on the end of the strip)
391 for (i = 1; i < strip->repeat; i++) {
392 float repeatPos = strip->start + (repeatLen * i);
394 /* don't draw if line would end up on or after the end of the strip */
395 if (repeatPos < strip->end)
396 fdrawline(repeatPos, yminc + 4, repeatPos, ymaxc - 4);
399 /* or if meta-strip, draw lines delimiting extents of sub-strips (in same color as outline, if more than 1 exists) */
400 else if ((strip->type == NLASTRIP_TYPE_META) && (strip->strips.first != strip->strips.last)) {
402 float y = (ymaxc - yminc) / 2.0f + yminc;
404 /* only draw first-level of child-strips, but don't draw any lines on the endpoints */
405 for (cs = strip->strips.first; cs; cs = cs->next) {
406 /* draw start-line if not same as end of previous (and only if not the first strip)
407 * - on upper half of strip
409 if ((cs->prev) && IS_EQF(cs->prev->end, cs->start) == 0)
410 fdrawline(cs->start, y, cs->start, ymaxc);
412 /* draw end-line if not the last strip
413 * - on lower half of strip
416 fdrawline(cs->end, yminc, cs->end, y);
420 /* reset linestyle */
424 /* add the relevant text to the cache of text-strings to draw in pixelspace */
425 static void nla_draw_strip_text(AnimData *adt, NlaTrack *nlt, NlaStrip *strip, int index, View2D *v2d, float yminc, float ymaxc)
427 short notSolo = ((adt && (adt->flag & ADT_NLA_SOLO_TRACK)) && (nlt->flag & NLATRACK_SOLO) == 0);
434 /* just print the name and the range */
435 if (strip->flag & NLASTRIP_FLAG_TEMP_META) {
436 str_len = BLI_snprintf(str, sizeof(str), "%d) Temp-Meta", index);
439 str_len = BLI_strncpy_rlen(str, strip->name, sizeof(str));
442 /* set text color - if colors (see above) are light, draw black text, otherwise draw white */
443 if (strip->flag & (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_SELECT | NLASTRIP_FLAG_TWEAKUSER)) {
444 col[0] = col[1] = col[2] = 0;
447 col[0] = col[1] = col[2] = 255;
450 /* text opacity depends on whether if there's a solo'd track, this isn't it */
456 /* determine the amount of padding required - cannot be constant otherwise looks weird in some cases */
457 if ((strip->end - strip->start) <= 5.0f)
462 /* set bounding-box for text
463 * - padding of 2 'units' on either side
465 // TODO: make this centered?
466 rect.xmin = strip->start + xofs;
468 rect.xmax = strip->end - xofs;
471 /* add this string to the cache of texts to draw */
472 UI_view2d_text_cache_add_rectf(v2d, &rect, str, str_len, col);
475 /* add frame extents to cache of text-strings to draw in pixelspace
476 * for now, only used when transforming strips
478 static void nla_draw_strip_frames_text(NlaTrack *UNUSED(nlt), NlaStrip *strip, View2D *v2d, float UNUSED(yminc), float ymaxc)
480 const float ytol = 1.0f; /* small offset to vertical positioning of text, for legibility */
481 const char col[4] = {220, 220, 220, 255}; /* light gray */
486 /* Always draw times above the strip, whereas sequencer drew below + above.
487 * However, we should be fine having everything on top, since these tend to be
489 * - 1 dp is compromise between lack of precision (ints only, as per sequencer)
490 * while also preserving some accuracy, since we do use floats
493 numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%.1f", strip->start);
494 UI_view2d_text_cache_add(v2d, strip->start - 1.0f, ymaxc + ytol, numstr, numstr_len, col);
497 numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%.1f", strip->end);
498 UI_view2d_text_cache_add(v2d, strip->end, ymaxc + ytol, numstr, numstr_len, col);
501 /* ---------------------- */
503 void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *ar)
505 ListBase anim_data = {NULL, NULL};
509 View2D *v2d = &ar->v2d;
514 /* build list of channels to draw */
515 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
516 items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
518 /* Update max-extent of channels here (taking into account scrollers):
519 * - this is done to allow the channel list to be scrollable, but must be done here
520 * to avoid regenerating the list again and/or also because channels list is drawn first
521 * - offset of NLACHANNEL_HEIGHT*2 is added to the height of the channels, as first is for
522 * start of list offset, and the second is as a correction for the scrollers.
524 height = ((items * NLACHANNEL_STEP(snla)) + (NLACHANNEL_HEIGHT(snla) * 2));
525 /* don't use totrect set, as the width stays the same
526 * (NOTE: this is ok here, the configuration is pretty straightforward)
528 v2d->tot.ymin = (float)(-height);
530 /* loop through channels, and set up drawing depending on their type */
531 y = (float)(-NLACHANNEL_HEIGHT(snla));
533 for (ale = anim_data.first; ale; ale = ale->next) {
534 const float yminc = (float)(y - NLACHANNEL_HEIGHT_HALF(snla));
535 const float ymaxc = (float)(y + NLACHANNEL_HEIGHT_HALF(snla));
537 /* check if visible */
538 if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) ||
539 IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) )
541 /* data to draw depends on the type of channel */
543 case ANIMTYPE_NLATRACK:
545 AnimData *adt = ale->adt;
546 NlaTrack *nlt = (NlaTrack *)ale->data;
550 /* draw each strip in the track (if visible) */
551 for (strip = nlt->strips.first, index = 1; strip; strip = strip->next, index++) {
552 if (BKE_nlastrip_within_bounds(strip, v2d->cur.xmin, v2d->cur.xmax)) {
553 /* draw the visualization of the strip */
554 nla_draw_strip(snla, adt, nlt, strip, v2d, yminc, ymaxc);
556 /* add the text for this strip to the cache */
557 nla_draw_strip_text(adt, nlt, strip, index, v2d, yminc, ymaxc);
559 /* if transforming strips (only real reason for temp-metas currently),
560 * add to the cache the frame numbers of the strip's extents
562 if (strip->flag & NLASTRIP_FLAG_TEMP_META)
563 nla_draw_strip_frames_text(nlt, strip, v2d, yminc, ymaxc);
568 case ANIMTYPE_NLAACTION:
570 AnimData *adt = ale->adt;
573 /* just draw a semi-shaded rect spanning the width of the viewable area if there's data,
574 * and a second darker rect within which we draw keyframe indicator dots if there's data
576 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
579 /* get colors for drawing */
580 nla_action_get_color(adt, ale->data, color);
583 /* draw slightly shifted up for greater separation from standard channels,
584 * but also slightly shorter for some more contrast when viewing the strips
586 glRectf(v2d->cur.xmin, yminc + NLACHANNEL_SKIP, v2d->cur.xmax, ymaxc - NLACHANNEL_SKIP);
588 /* draw keyframes in the action */
589 nla_action_draw_keyframes(adt, ale->data, v2d, y, yminc + NLACHANNEL_SKIP, ymaxc - NLACHANNEL_SKIP);
591 /* draw 'embossed' lines above and below the strip for effect */
592 /* white base-lines */
594 glColor4f(1.0f, 1.0f, 1.0f, 0.3);
595 fdrawline(v2d->cur.xmin, yminc + NLACHANNEL_SKIP, v2d->cur.xmax, yminc + NLACHANNEL_SKIP);
596 fdrawline(v2d->cur.xmin, ymaxc - NLACHANNEL_SKIP, v2d->cur.xmax, ymaxc - NLACHANNEL_SKIP);
598 /* black top-lines */
600 glColor3f(0.0f, 0.0f, 0.0f);
601 fdrawline(v2d->cur.xmin, yminc + NLACHANNEL_SKIP, v2d->cur.xmax, yminc + NLACHANNEL_SKIP);
602 fdrawline(v2d->cur.xmin, ymaxc - NLACHANNEL_SKIP, v2d->cur.xmax, ymaxc - NLACHANNEL_SKIP);
610 /* adjust y-position for next one */
611 y -= NLACHANNEL_STEP(snla);
614 /* free tempolary channels */
615 ANIM_animdata_freelist(&anim_data);
618 /* *********************************************** */
621 void draw_nla_channel_list(const bContext *C, bAnimContext *ac, ARegion *ar)
623 ListBase anim_data = {NULL, NULL};
627 SpaceNla *snla = (SpaceNla *)ac->sl;
628 View2D *v2d = &ar->v2d;
633 /* build list of channels to draw */
634 filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
635 items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
637 /* Update max-extent of channels here (taking into account scrollers):
638 * - this is done to allow the channel list to be scrollable, but must be done here
639 * to avoid regenerating the list again and/or also because channels list is drawn first
640 * - offset of NLACHANNEL_HEIGHT*2 is added to the height of the channels, as first is for
641 * start of list offset, and the second is as a correction for the scrollers.
643 height = ((items * NLACHANNEL_STEP(snla)) + (NLACHANNEL_HEIGHT(snla) * 2));
644 /* don't use totrect set, as the width stays the same
645 * (NOTE: this is ok here, the configuration is pretty straightforward)
647 v2d->tot.ymin = (float)(-height);
648 /* need to do a view-sync here, so that the keys area doesn't jump around (it must copy this) */
649 UI_view2d_sync(NULL, ac->sa, v2d, V2D_LOCK_COPY);
652 { /* first pass: just the standard GL-drawing for backdrop + text */
653 size_t channel_index = 0;
655 y = (float)(-NLACHANNEL_HEIGHT(snla));
657 for (ale = anim_data.first; ale; ale = ale->next) {
658 float yminc = (float)(y - NLACHANNEL_HEIGHT_HALF(snla));
659 float ymaxc = (float)(y + NLACHANNEL_HEIGHT_HALF(snla));
661 /* check if visible */
662 if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) ||
663 IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) )
665 /* draw all channels using standard channel-drawing API */
666 ANIM_channel_draw(ac, ale, yminc, ymaxc, channel_index);
669 /* adjust y-position for next one */
670 y -= NLACHANNEL_STEP(snla);
674 { /* second pass: UI widgets */
675 uiBlock *block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
676 size_t channel_index = 0;
678 y = (float)(-NLACHANNEL_HEIGHT(snla));
680 /* set blending again, as may not be set in previous step */
681 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
684 /* loop through channels, and set up drawing depending on their type */
685 for (ale = anim_data.first; ale; ale = ale->next) {
686 const float yminc = (float)(y - NLACHANNEL_HEIGHT_HALF(snla));
687 const float ymaxc = (float)(y + NLACHANNEL_HEIGHT_HALF(snla));
689 /* check if visible */
690 if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) ||
691 IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) )
693 /* draw all channels using standard channel-drawing API */
694 ANIM_channel_draw_widgets(C, ac, ale, block, yminc, ymaxc, channel_index);
697 /* adjust y-position for next one */
698 y -= NLACHANNEL_STEP(snla);
702 UI_block_end(C, block);
703 UI_block_draw(C, block);
708 /* free temporary channels */
709 ANIM_animdata_freelist(&anim_data);
712 /* *********************************************** */