NLA SoC: Bugfixes for previous commit
[blender.git] / source / blender / editors / space_nla / nla_draw.c
1 /**
2  * $Id:
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Joshua Leung (major recode)
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <string.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <math.h>
33
34 #include "DNA_listBase.h"
35 #include "DNA_anim_types.h"
36 #include "DNA_action_types.h"
37 #include "DNA_armature_types.h"
38 #include "DNA_camera_types.h"
39 #include "DNA_curve_types.h"
40 #include "DNA_object_types.h"
41 #include "DNA_screen_types.h"
42 #include "DNA_scene_types.h"
43 #include "DNA_space_types.h"
44 #include "DNA_constraint_types.h"
45 #include "DNA_key_types.h"
46 #include "DNA_lamp_types.h"
47 #include "DNA_material_types.h"
48 #include "DNA_userdef_types.h"
49 #include "DNA_windowmanager_types.h"
50 #include "DNA_world_types.h"
51
52 #include "MEM_guardedalloc.h"
53
54 #include "BLI_blenlib.h"
55 #include "BLI_arithb.h"
56 #include "BLI_rand.h"
57
58 #include "BKE_animsys.h"
59 #include "BKE_nla.h"
60 #include "BKE_context.h"
61 #include "BKE_screen.h"
62 #include "BKE_utildefines.h"
63
64 #include "ED_anim_api.h"
65 #include "ED_space_api.h"
66 #include "ED_screen.h"
67
68 #include "BIF_gl.h"
69
70 #include "WM_api.h"
71 #include "WM_types.h"
72
73 #include "UI_interface.h"
74 #include "UI_interface_icons.h"
75 #include "UI_resources.h"
76 #include "UI_view2d.h"
77
78 #include "ED_markers.h"
79
80 #include "nla_intern.h" // own include
81
82 /* XXX */
83 extern void gl_round_box(int mode, float minx, float miny, float maxx, float maxy, float rad);
84
85 /* *********************************************** */
86 /* Strips */
87
88 static void nla_draw_strip (NlaTrack *nlt, NlaStrip *strip, int index, View2D *v2d, float yminc, float ymaxc)
89 {
90         char name[128];
91         
92         /* draw extrapolation info first (as backdrop) */
93         // TODO...
94         
95         /* draw 'inside' of strip itself */
96                 /* set color of strip - color is used to indicate status here */
97         if (strip->flag & NLASTRIP_FLAG_ACTIVE) {
98                 /* tweaking strip should be drawn green when it is acting as the tweaking strip */
99                 // FIXME: hardcoded temp-hack colors
100                 glColor3f(0.3f, 0.95f, 0.1f);
101         }
102         else if (strip->flag & NLASTRIP_FLAG_TWEAKUSER) {
103                 /* alert user that this strip is also used by the tweaking track (this is set when going into
104                  * 'editmode' for that strip), since the edits made here may not be what the user anticipated
105                  */
106                 // FIXME: hardcoded temp-hack colors
107                 glColor3f(0.85f, 0.0f, 0.0f);
108         }
109         else if (strip->flag & NLASTRIP_FLAG_SELECT) {
110                 /* selected strip - use theme color for selected */
111                 UI_ThemeColor(TH_STRIP_SELECT);
112         }
113         else {
114                 /* normal, unselected strip - use standard strip theme color */
115                 UI_ThemeColor(TH_STRIP);
116         }
117         uiSetRoundBox(15); /* all corners rounded */
118         gl_round_box(GL_POLYGON, strip->start, yminc, strip->end, ymaxc, 9);
119         
120         /* draw strip outline */
121         if (strip->flag & NLASTRIP_FLAG_ACTIVE) {
122                 /* strip should appear 'sunken', so draw a light border around it */
123                 glColor3f(0.9f, 1.0f, 0.9f); // FIXME: hardcoded temp-hack colors
124         }
125         else {
126                 /* strip should appear to stand out, so draw a dark border around it */
127                 glColor3f(0.0f, 0.0f, 0.0f);
128         }
129         gl_round_box(GL_LINES, strip->start, yminc, strip->end, ymaxc, 9);
130         
131         /* draw some identifying info on the strip (index and name of action if there's room) */
132         // XXX for now, just the index
133         if (strip->flag & NLASTRIP_FLAG_SELECT)
134                 UI_ThemeColor(TH_TEXT_HI);
135         else
136                 UI_ThemeColor(TH_TEXT);
137         sprintf(name, "%d |", index);
138         UI_DrawString(strip->start, yminc+8, name);
139
140
141 /* ---------------------- */
142
143 void draw_nla_main_data (bAnimContext *ac, SpaceNla *snla, ARegion *ar)
144 {
145         ListBase anim_data = {NULL, NULL};
146         bAnimListElem *ale;
147         int filter;
148         
149         View2D *v2d= &ar->v2d;
150         float y= 0.0f;
151         int items, height;
152         
153         /* build list of channels to draw */
154         filter= (ANIMFILTER_VISIBLE|ANIMFILTER_CHANNELS);
155         items= ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
156         
157         /* Update max-extent of channels here (taking into account scrollers):
158          *      - this is done to allow the channel list to be scrollable, but must be done here
159          *        to avoid regenerating the list again and/or also because channels list is drawn first
160          *      - offset of NLACHANNEL_HEIGHT*2 is added to the height of the channels, as first is for 
161          *        start of list offset, and the second is as a correction for the scrollers.
162          */
163         height= ((items*NLACHANNEL_STEP) + (NLACHANNEL_HEIGHT*2));
164         if (height > (v2d->mask.ymax - v2d->mask.ymin)) {
165                 /* don't use totrect set, as the width stays the same 
166                  * (NOTE: this is ok here, the configuration is pretty straightforward) 
167                  */
168                 v2d->tot.ymax= (float)(height);
169         }
170         
171         /* loop through channels, and set up drawing depending on their type  */        
172         y= (float)(-NLACHANNEL_FIRST);
173         
174         for (ale= anim_data.first; ale; ale= ale->next) {
175                 const float yminc= (float)(y - NLACHANNEL_HEIGHT_HALF);
176                 const float ymaxc= (float)(y + NLACHANNEL_HEIGHT_HALF);
177                 
178                 /* check if visible */
179                 if ( IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) ||
180                          IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) 
181                 {
182                         /* data to draw depends on the type of channel */
183                         switch (ale->type) {
184                                 case ANIMTYPE_NLATRACK:
185                                 {
186                                         NlaTrack *nlt= (NlaTrack *)ale->data;
187                                         NlaStrip *strip;
188                                         int index;
189                                         
190                                         /* draw backdrop? */
191                                         // TODO...
192                                         
193                                         /* draw each strip in the track */
194                                         for (strip=nlt->strips.first, index=1; strip; strip= strip->next, index++) {
195                                                 /* only draw if at least part of the strip is within view */
196                                                 if ( IN_RANGE(v2d->cur.xmin, strip->start, strip->end) ||
197                                                          IN_RANGE(v2d->cur.xmax, strip->start, strip->end) )
198                                                 {
199                                                         nla_draw_strip(nlt, strip, index, v2d, yminc, ymaxc);
200                                                 }
201                                         }
202                                 }
203                                         break;
204                                         
205                                 case ANIMTYPE_NLAACTION:
206                                 {
207                                         /* just draw a semi-shaded rect spanning the width of the viewable area if there's data */
208                                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
209                                         glEnable(GL_BLEND);
210                                         
211                                         if (ale->data)
212                                                 glColor4f(0.8f, 0.2f, 0.0f, 0.4f);      // reddish color - hardcoded for now 
213                                         else
214                                                 glColor4f(0.6f, 0.5f, 0.5f, 0.3f);      // greyish-red color - hardcoded for now
215                                                 
216                                         glBegin(GL_QUADS);
217                                                 glVertex2f(v2d->cur.xmin, yminc);
218                                                 glVertex2f(v2d->cur.xmin, ymaxc);
219                                                 glVertex2f(v2d->cur.xmax, ymaxc);
220                                                 glVertex2f(v2d->cur.xmax, yminc);
221                                         glEnd();
222                                         
223                                         glDisable(GL_BLEND);
224                                 }
225                                         break;
226                         }
227                 }
228                 
229                 /* adjust y-position for next one */
230                 y += NLACHANNEL_STEP;
231         }
232         
233         /* free tempolary channels */
234         BLI_freelistN(&anim_data);
235 }
236
237 /* *********************************************** */
238 /* Channel List */
239
240 void draw_nla_channel_list (bAnimContext *ac, SpaceNla *snla, ARegion *ar)
241 {
242         ListBase anim_data = {NULL, NULL};
243         bAnimListElem *ale;
244         int filter;
245         
246         View2D *v2d= &ar->v2d;
247         float x= 0.0f, y= 0.0f;
248         int items, height;
249         
250         /* build list of channels to draw */
251         filter= (ANIMFILTER_VISIBLE|ANIMFILTER_CHANNELS);
252         items= ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
253         
254         /* Update max-extent of channels here (taking into account scrollers):
255          *      - this is done to allow the channel list to be scrollable, but must be done here
256          *        to avoid regenerating the list again and/or also because channels list is drawn first
257          *      - offset of NLACHANNEL_HEIGHT*2 is added to the height of the channels, as first is for 
258          *        start of list offset, and the second is as a correction for the scrollers.
259          */
260         height= ((items*NLACHANNEL_STEP) + (NLACHANNEL_HEIGHT*2));
261         if (height > (v2d->mask.ymax - v2d->mask.ymin)) {
262                 /* don't use totrect set, as the width stays the same 
263                  * (NOTE: this is ok here, the configuration is pretty straightforward) 
264                  */
265                 v2d->tot.ymax= (float)(height);
266         }
267         
268         /* loop through channels, and set up drawing depending on their type  */        
269         y= (float)(-NLACHANNEL_FIRST);
270         
271         for (ale= anim_data.first; ale; ale= ale->next) {
272                 const float yminc= (float)(y - NLACHANNEL_HEIGHT_HALF);
273                 const float ymaxc= (float)(y + NLACHANNEL_HEIGHT_HALF);
274                 const float ydatac= (float)(y - 7);
275                 
276                 /* check if visible */
277                 if ( IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) ||
278                          IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) 
279                 {
280                         short indent= 0, offset= 0, sel= 0, group= 0;
281                         int expand= -1, protect = -1, special= -1, mute = -1;
282                         char name[128];
283                         
284                         /* determine what needs to be drawn */
285                         switch (ale->type) {
286                                 case ANIMTYPE_SCENE: /* scene */
287                                 {
288                                         Scene *sce= (Scene *)ale->data;
289                                         
290                                         group= 4;
291                                         indent= 0;
292                                         
293                                         special= ICON_SCENE_DATA;
294                                                 
295                                         /* only show expand if there are any channels */
296                                         if (EXPANDED_SCEC(sce))
297                                                 expand= ICON_TRIA_UP;
298                                         else
299                                                 expand= ICON_TRIA_RIGHT;
300                                         
301                                         sel = SEL_SCEC(sce);
302                                         strcpy(name, sce->id.name+2);
303                                 }
304                                         break;
305                                 case ANIMTYPE_OBJECT: /* object */
306                                 {
307                                         Base *base= (Base *)ale->data;
308                                         Object *ob= base->object;
309                                         
310                                         group= 4;
311                                         indent= 0;
312                                         
313                                         /* icon depends on object-type */
314                                         if (ob->type == OB_ARMATURE)
315                                                 special= ICON_ARMATURE_DATA;
316                                         else    
317                                                 special= ICON_OBJECT_DATA;
318                                                 
319                                         /* only show expand if there are any channels */
320                                         if (EXPANDED_OBJC(ob))
321                                                 expand= ICON_TRIA_UP;
322                                         else
323                                                 expand= ICON_TRIA_RIGHT;
324                                         
325                                         sel = SEL_OBJC(base);
326                                         strcpy(name, ob->id.name+2);
327                                 }
328                                         break;
329                                 case ANIMTYPE_FILLMATD: /* object materials (dopesheet) expand widget */
330                                 {
331                                         Object *ob = (Object *)ale->data;
332                                         
333                                         group = 4;
334                                         indent = 1;
335                                         special = ICON_MATERIAL_DATA;
336                                         
337                                         if (FILTER_MAT_OBJC(ob))
338                                                 expand = ICON_TRIA_UP;
339                                         else
340                                                 expand = ICON_TRIA_RIGHT;
341                                                 
342                                         strcpy(name, "Materials");
343                                 }
344                                         break;
345                                 
346                                 
347                                 case ANIMTYPE_DSMAT: /* single material (dopesheet) expand widget */
348                                 {
349                                         Material *ma = (Material *)ale->data;
350                                         
351                                         group = 0;
352                                         indent = 0;
353                                         special = ICON_MATERIAL_DATA;
354                                         offset = 21;
355                                         
356                                         if (FILTER_MAT_OBJD(ma))
357                                                 expand = ICON_TRIA_UP;
358                                         else
359                                                 expand = ICON_TRIA_RIGHT;
360                                         
361                                         strcpy(name, ma->id.name+2);
362                                 }
363                                         break;
364                                 case ANIMTYPE_DSLAM: /* lamp (dopesheet) expand widget */
365                                 {
366                                         Lamp *la = (Lamp *)ale->data;
367                                         
368                                         group = 4;
369                                         indent = 1;
370                                         special = ICON_LAMP_DATA;
371                                         
372                                         if (FILTER_LAM_OBJD(la))
373                                                 expand = ICON_TRIA_UP;
374                                         else
375                                                 expand = ICON_TRIA_RIGHT;
376                                         
377                                         strcpy(name, la->id.name+2);
378                                 }
379                                         break;
380                                 case ANIMTYPE_DSCAM: /* camera (dopesheet) expand widget */
381                                 {
382                                         Camera *ca = (Camera *)ale->data;
383                                         
384                                         group = 4;
385                                         indent = 1;
386                                         special = ICON_CAMERA_DATA;
387                                         
388                                         if (FILTER_CAM_OBJD(ca))
389                                                 expand = ICON_TRIA_UP;
390                                         else
391                                                 expand = ICON_TRIA_RIGHT;
392                                         
393                                         strcpy(name, ca->id.name+2);
394                                 }
395                                         break;
396                                 case ANIMTYPE_DSCUR: /* curve (dopesheet) expand widget */
397                                 {
398                                         Curve *cu = (Curve *)ale->data;
399                                         
400                                         group = 4;
401                                         indent = 1;
402                                         special = ICON_CURVE_DATA;
403                                         
404                                         if (FILTER_CUR_OBJD(cu))
405                                                 expand = ICON_TRIA_UP;
406                                         else
407                                                 expand = ICON_TRIA_RIGHT;
408                                         
409                                         strcpy(name, cu->id.name+2);
410                                 }
411                                         break;
412                                 case ANIMTYPE_DSSKEY: /* shapekeys (dopesheet) expand widget */
413                                 {
414                                         Key *key= (Key *)ale->data;
415                                         
416                                         group = 4;
417                                         indent = 1;
418                                         special = ICON_SHAPEKEY_DATA; // XXX 
419                                         
420                                         if (FILTER_SKE_OBJD(key))       
421                                                 expand = ICON_TRIA_UP;
422                                         else
423                                                 expand = ICON_TRIA_RIGHT;
424                                                 
425                                         //sel = SEL_OBJC(base);
426                                         strcpy(name, "Shape Keys");
427                                 }
428                                         break;
429                                 case ANIMTYPE_DSWOR: /* world (dopesheet) expand widget */
430                                 {
431                                         World *wo= (World *)ale->data;
432                                         
433                                         group = 4;
434                                         indent = 1;
435                                         special = ICON_WORLD_DATA;
436                                         
437                                         if (FILTER_WOR_SCED(wo))        
438                                                 expand = ICON_TRIA_DOWN;
439                                         else
440                                                 expand = ICON_TRIA_RIGHT;
441                                         
442                                         strcpy(name, wo->id.name+2);
443                                 }
444                                         break;
445                                 
446                                 case ANIMTYPE_NLATRACK: /* NLA Track */
447                                 {
448                                         NlaTrack *nlt= (NlaTrack *)ale->data;
449                                         
450                                         indent= 0;
451                                         
452                                         if (ale->id) {
453                                                 /* special exception for materials */
454                                                 if (GS(ale->id->name) == ID_MA) {
455                                                         offset= 21;
456                                                         indent= 1;
457                                                 }
458                                                 else
459                                                         offset= 14;
460                                         }
461                                         else
462                                                 offset= 0;
463                                         
464                                         /* FIXME: 'solo' as the 'special' button?
465                                          *      - need special icons for these
466                                          */
467                                         if (nlt->flag & NLATRACK_SOLO)
468                                                 special= ICON_LAYER_ACTIVE;
469                                         else
470                                                 special= ICON_LAYER_USED;
471                                                 
472                                         if (nlt->flag & NLATRACK_MUTED)
473                                                 mute = ICON_MUTE_IPO_ON;
474                                         else    
475                                                 mute = ICON_MUTE_IPO_OFF;
476                                                 
477                                         if (EDITABLE_NLT(nlt))
478                                                 protect = ICON_UNLOCKED;
479                                         else
480                                                 protect = ICON_LOCKED;
481                                                 
482                                         strcpy(name, nlt->name);
483                                 }
484                                         break;
485                                 case ANIMTYPE_NLAACTION: /* NLA Action-Line */
486                                 {
487                                         bAction *act= (bAction *)ale->data;
488                                         
489                                         group = 5;
490                                         
491                                         if (ale->id) {
492                                                 /* special exception for materials */
493                                                 if (GS(ale->id->name) == ID_MA) {
494                                                         offset= 21;
495                                                         indent= 1;
496                                                 }
497                                                 else
498                                                         offset= 14;
499                                         }
500                                         else
501                                                 offset= 0;
502                                         
503                                         special = ICON_ACTION;
504                                         
505                                         if (act)
506                                                 sprintf(name, "ActAction: <%s>", act->id.name+2);
507                                         else
508                                                 sprintf(name, "<No Action>");
509                                 }
510                                         break;
511                         }       
512                         
513                         /* now, start drawing based on this information */
514                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
515                         glEnable(GL_BLEND);
516                         
517                         /* draw backing strip behind channel name */
518                         if (group == 4) {
519                                 /* only used in dopesheet... */
520                                 if (ELEM(ale->type, ANIMTYPE_SCENE, ANIMTYPE_OBJECT)) {
521                                         /* object channel - darker */
522                                         UI_ThemeColor(TH_DOPESHEET_CHANNELOB);
523                                         uiSetRoundBox((expand == ICON_TRIA_UP)? (8):(1|8));
524                                         gl_round_box(GL_POLYGON, x+offset,  yminc, (float)NLACHANNEL_NAMEWIDTH, ymaxc, 8);
525                                 }
526                                 else {
527                                         /* sub-object folders - lighter */
528                                         UI_ThemeColor(TH_DOPESHEET_CHANNELSUBOB);
529                                         
530                                         offset += 7 * indent;
531                                         glBegin(GL_QUADS);
532                                                 glVertex2f(x+offset, yminc);
533                                                 glVertex2f(x+offset, ymaxc);
534                                                 glVertex2f((float)ACHANNEL_NAMEWIDTH, ymaxc);
535                                                 glVertex2f((float)ACHANNEL_NAMEWIDTH, yminc);
536                                         glEnd();
537                                         
538                                         /* clear group value, otherwise we cause errors... */
539                                         group = 0;
540                                 }
541                         }
542                         else if (group == 5) {
543                                 /* Action Line */
544                                 if (ale->data)
545                                         glColor3f(0.8f, 0.2f, 0.0f);    // reddish color - hardcoded for now 
546                                 else
547                                         glColor3f(0.6f, 0.5f, 0.5f);    // greyish-red color - hardcoded for now
548                                         
549                                 offset += 7 * indent;
550                                 uiSetRoundBox(15);
551                                 gl_round_box(GL_POLYGON, x+offset,  yminc, (float)NLACHANNEL_NAMEWIDTH, ymaxc, 8);
552                                 
553                                 /* clear group value, otherwise we cause errors... */
554                                 group = 0;
555                         }
556                         else {
557                                 /* for normal channels 
558                                  *      - use 3 shades of color group/standard color for 3 indention level
559                                  */
560                                 UI_ThemeColorShade(TH_HEADER, ((indent==0)?20: (indent==1)?-20: -40));
561                                 
562                                 indent += group;
563                                 offset += 7 * indent;
564                                 glBegin(GL_QUADS);
565                                         glVertex2f(x+offset, yminc);
566                                         glVertex2f(x+offset, ymaxc);
567                                         glVertex2f((float)NLACHANNEL_NAMEWIDTH, ymaxc);
568                                         glVertex2f((float)NLACHANNEL_NAMEWIDTH, yminc);
569                                 glEnd();
570                         }
571                         
572                         /* draw expand/collapse triangle */
573                         if (expand > 0) {
574                                 UI_icon_draw(x+offset, ydatac, expand);
575                                 offset += 17;
576                         }
577                         
578                         /* draw special icon indicating certain data-types */
579                         if (special > -1) {
580                                 /* for normal channels */
581                                 UI_icon_draw(x+offset, ydatac, special);
582                                 offset += 17;
583                         }
584                         glDisable(GL_BLEND);
585                         
586                         /* draw name */
587                         if (sel)
588                                 UI_ThemeColor(TH_TEXT_HI);
589                         else
590                                 UI_ThemeColor(TH_TEXT);
591                         offset += 3;
592                         UI_DrawString(x+offset, y-4, name);
593                         
594                         /* reset offset - for RHS of panel */
595                         offset = 0;
596                         
597                         /* set blending again, as text drawing may clear it */
598                         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
599                         glEnable(GL_BLEND);
600                         
601                         /* draw protect 'lock' */
602                         if (protect > -1) {
603                                 offset = 16;
604                                 UI_icon_draw((float)NLACHANNEL_NAMEWIDTH-offset, ydatac, protect);
605                         }
606                         
607                         /* draw mute 'eye' */
608                         if (mute > -1) {
609                                 offset += 16;
610                                 UI_icon_draw((float)(NLACHANNEL_NAMEWIDTH-offset), ydatac, mute);
611                         }
612                         
613                         /* draw action 'push-down' */
614                         if (ale->type == ANIMTYPE_NLAACTION) {
615                                 offset += 16;
616                                 
617                                 /* XXX firstly draw a little rect to help identify that it's different from the toggles */
618                                 glBegin(GL_LINES);
619                                         glVertex2f((float)NLACHANNEL_NAMEWIDTH-offset-1, y-7);
620                                         glVertex2f((float)NLACHANNEL_NAMEWIDTH-offset-1, y+7);
621                                         glVertex2f((float)NLACHANNEL_NAMEWIDTH-1, y-7);
622                                         glVertex2f((float)NLACHANNEL_NAMEWIDTH-1, y+7);
623                                 glEnd(); // GL_LINES
624                                 
625                                 /* now draw the icon */
626                                 UI_icon_draw((float)NLACHANNEL_NAMEWIDTH-offset, ydatac, ICON_FREEZE);
627                         }
628                         
629                         glDisable(GL_BLEND);
630                 }
631                 
632                 /* adjust y-position for next one */
633                 y += NLACHANNEL_STEP;
634         }
635         
636         /* free tempolary channels */
637         BLI_freelistN(&anim_data);
638 }
639
640 /* *********************************************** */