== Action Editor - Copy and Paste Tools ==
[blender.git] / source / blender / src / drawaction.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL 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. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): Joshua Leung
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  * Drawing routines for the Action window type
32  */
33
34 /* System includes ----------------------------------------------------- */
35
36 #include <math.h>
37 #include <stdlib.h>
38
39 #ifdef HAVE_CONFIG_H
40 #include <config.h>
41 #endif
42
43 #include "MEM_guardedalloc.h"
44
45 #include "BMF_Api.h"
46
47 #include "BLI_blenlib.h"
48 #include "BLI_arithb.h"
49
50 /* Types --------------------------------------------------------------- */
51 #include "DNA_listBase.h"
52 #include "DNA_action_types.h"
53 #include "DNA_armature_types.h"
54 #include "DNA_curve_types.h"
55 #include "DNA_ipo_types.h"
56 #include "DNA_object_types.h"
57 #include "DNA_screen_types.h"
58 #include "DNA_scene_types.h"
59 #include "DNA_space_types.h"
60 #include "DNA_constraint_types.h"
61 #include "DNA_key_types.h"
62
63 #include "BKE_action.h"
64 #include "BKE_ipo.h"
65 #include "BKE_global.h"
66 #include "BKE_utildefines.h"
67
68 /* Everything from source (BIF, BDR, BSE) ------------------------------ */ 
69
70 #include "BIF_editaction.h"
71 #include "BIF_editkey.h"
72 #include "BIF_interface.h"
73 #include "BIF_interface_icons.h"
74 #include "BIF_gl.h"
75 #include "BIF_glutil.h"
76 #include "BIF_resources.h"
77 #include "BIF_screen.h"
78 #include "BIF_mywindow.h"
79 #include "BIF_space.h"
80
81 #include "BDR_drawaction.h"
82 #include "BDR_editcurve.h"
83
84 #include "BSE_drawnla.h"
85 #include "BSE_drawipo.h"
86 #include "BSE_editaction_types.h"
87 #include "BSE_editipo.h"
88 #include "BSE_time.h"
89 #include "BSE_view.h"
90
91 /* 'old' stuff": defines and types, and own include -------------------- */
92
93 #include "blendef.h"
94 #include "mydevice.h"
95
96 /********************************** Slider Stuff **************************** */
97
98 /* sliders for shapekeys */
99 static void meshactionbuts(SpaceAction *saction, Object *ob, Key *key)
100 {
101         int           i;
102         char          str[64];
103         float         x, y;
104         uiBlock       *block;
105         uiBut             *but;
106
107 #define XIC 20
108 #define YIC 20
109
110         /* lets make the rvk sliders */
111
112         /* reset the damn myortho2 or the sliders won't draw/redraw
113          * correctly *grumble*
114          */
115         mywinset(curarea->win);
116         myortho2(-0.375, curarea->winx-0.375, G.v2d->cur.ymin, G.v2d->cur.ymax);
117
118     sprintf(str, "actionbuttonswin %d", curarea->win);
119     block= uiNewBlock (&curarea->uiblocks, str, UI_EMBOSS, UI_HELV, curarea->win);
120
121         x = NAMEWIDTH + 1;
122     y = 0.0f;
123
124         /* make the little 'open the sliders' widget */
125         // should eventually be removed
126     BIF_ThemeColor(TH_FACE); // this slot was open... (???... Aligorith)
127         glRects(2,            y + 2*CHANNELHEIGHT - 2, ACTWIDTH - 2, y + CHANNELHEIGHT + 2);
128         glColor3ub(0, 0, 0);
129         glRasterPos2f(4, y + CHANNELHEIGHT + 6);
130         BMF_DrawString(G.font, "Sliders");
131
132         uiBlockSetEmboss(block, UI_EMBOSSN);
133
134         if (!(G.saction->flag & SACTION_SLIDERS)) {
135                 ACTWIDTH = NAMEWIDTH;
136                 but=uiDefIconButBitS(block, TOG, SACTION_SLIDERS, B_REDR, 
137                                           ICON_DISCLOSURE_TRI_RIGHT,
138                                           NAMEWIDTH - XIC - 5, y + CHANNELHEIGHT,
139                                           XIC,YIC-2,
140                                           &(G.saction->flag), 0, 0, 0, 0, 
141                                           "Show action window sliders");
142                 /* no hilite, the winmatrix is not correct later on... */
143                 uiButSetFlag(but, UI_NO_HILITE);
144
145         }
146         else {
147                 but= uiDefIconButBitS(block, TOG, SACTION_SLIDERS, B_REDR, 
148                                           ICON_DISCLOSURE_TRI_DOWN,
149                                           NAMEWIDTH - XIC - 5, y + CHANNELHEIGHT,
150                                           XIC,YIC-2,
151                                           &(G.saction->flag), 0, 0, 0, 0, 
152                                           "Hide action window sliders");
153                 /* no hilite, the winmatrix is not correct later on... */
154                 uiButSetFlag(but, UI_NO_HILITE);
155                                           
156                 ACTWIDTH = NAMEWIDTH + SLIDERWIDTH;
157
158                 /* sliders are open so draw them */
159                 BIF_ThemeColor(TH_FACE); 
160
161                 glRects(NAMEWIDTH,  0,  NAMEWIDTH+SLIDERWIDTH,  curarea->winy);
162                 uiBlockSetEmboss(block, UI_EMBOSS);
163                 for (i=1; i < key->totkey; i++) {
164                         make_rvk_slider(block, ob, i, 
165                                                         x, y, SLIDERWIDTH-2, CHANNELHEIGHT-1, "Slider to control Shape Keys");
166
167                         y-=CHANNELHEIGHT+CHANNELSKIP;
168                         
169                         /* see sliderval array in editkey.c */
170                         if(i >= 255) break;
171                 }
172         }
173         uiDrawBlock(block);
174
175 }
176
177 static void icu_slider_func(void *voidicu, void *voidignore) 
178 {
179         /* the callback for the icu sliders ... copies the
180          * value from the icu->curval into a bezier at the
181          * right frame on the right ipo curve (creating both the
182          * ipo curve and the bezier if needed).
183          */
184         IpoCurve  *icu= voidicu;
185         BezTriple *bezt=NULL;
186         float cfra, icuval;
187
188         cfra = frame_to_float(CFRA);
189         if (G.saction->pin==0 && OBACT)
190                 cfra= get_action_frame(OBACT, cfra);
191         
192         /* if the ipocurve exists, try to get a bezier
193          * for this frame
194          */
195         bezt = get_bezt_icu_time(icu, &cfra, &icuval);
196
197         /* create the bezier triple if one doesn't exist,
198          * otherwise modify it's value
199          */
200         if (!bezt) {
201                 insert_vert_icu(icu, cfra, icu->curval);
202         }
203         else {
204                 bezt->vec[1][1] = icu->curval;
205         }
206
207         /* make sure the Ipo's are properly process and
208          * redraw as necessary
209          */
210         sort_time_ipocurve(icu);
211         testhandles_ipocurve(icu);
212         
213         allqueue (REDRAWVIEW3D, 0);
214         allqueue (REDRAWACTION, 0);
215         allqueue (REDRAWNLA, 0);
216         allqueue (REDRAWIPO, 0);
217         allspace(REMAKEIPO, 0);
218         allqueue(REDRAWBUTSALL, 0);
219 }
220
221 static void make_icu_slider(uiBlock *block, IpoCurve *icu,
222                                          int x, int y, int w, int h, char *tip)
223 {
224         /* create a slider for the ipo-curve*/
225         uiBut *but;
226         
227         if(icu==NULL) return;
228         
229         if (IS_EQ(icu->slide_max, icu->slide_min)) {
230                 if (IS_EQ(icu->ymax, icu->ymin)) {
231                         if (ELEM(icu->blocktype, ID_CO, ID_KE)) {
232                                 /* hack for constraints and shapekeys (and maybe a few others) */
233                                 icu->slide_min= 0.0;
234                                 icu->slide_max= 1.0;
235                         }
236                         else {
237                                 icu->slide_min= -100;
238                                 icu->slide_max= 100;
239                         }
240                 }
241                 else {
242                         icu->slide_min= icu->ymin;
243                         icu->slide_max= icu->ymax;
244                 }
245         }
246         if (icu->slide_min >= icu->slide_max) {
247                 SWAP(float, icu->slide_min, icu->slide_max);
248         }
249
250         but=uiDefButF(block, NUMSLI, REDRAWVIEW3D, "",
251                                   x, y , w, h,
252                                   &(icu->curval), icu->slide_min, icu->slide_max, 
253                                   10, 2, tip);
254         
255         uiButSetFunc(but, icu_slider_func, icu, NULL);
256         
257         // no hilite, the winmatrix is not correct later on...
258         uiButSetFlag(but, UI_NO_HILITE);
259 }
260
261 /* sliders for ipo-curves of active action-channel */
262 static void action_icu_buts(SpaceAction *saction)
263 {
264         bAction *act= saction->action;
265         bActionChannel *achan;
266         bConstraintChannel *conchan;
267         IpoCurve *icu;
268         char          str[64];
269         float           x, y;
270         uiBlock       *block;
271
272         /* lets make the action sliders */
273
274         /* reset the damn myortho2 or the sliders won't draw/redraw
275          * correctly *grumble*
276          */
277         mywinset(curarea->win);
278         myortho2(-0.375, curarea->winx-0.375, G.v2d->cur.ymin, G.v2d->cur.ymax);
279         
280     sprintf(str, "actionbuttonswin %d", curarea->win);
281     block= uiNewBlock (&curarea->uiblocks, str, 
282                        UI_EMBOSS, UI_HELV, curarea->win);
283
284         x = NAMEWIDTH + 1;
285     y = 0.0f;
286         
287         uiBlockSetEmboss(block, UI_EMBOSSN);
288
289         if (G.saction->flag & SACTION_SLIDERS) {
290                 /* sliders are open so draw them */
291                 
292                 /* draw backdrop first */
293                 BIF_ThemeColor(TH_FACE); // change this color... it's ugly
294                 glRects(NAMEWIDTH,  G.v2d->cur.ymin,  NAMEWIDTH+SLIDERWIDTH,  G.v2d->cur.ymax);
295                 
296                 uiBlockSetEmboss(block, UI_EMBOSS);
297                 for (achan=act->chanbase.first; achan; achan= achan->next) {
298                         if(VISIBLE_ACHAN(achan)) {
299                                 y-=CHANNELHEIGHT+CHANNELSKIP;
300                                 
301                                 if (EXPANDED_ACHAN(achan)) {                                    
302                                         if (achan->ipo) {
303                                                 y-=CHANNELHEIGHT+CHANNELSKIP;
304                                                 
305                                                 if (FILTER_IPO_ACHAN(achan)) {
306                                                         for (icu= achan->ipo->curve.first; icu; icu=icu->next) {
307                                                                 if (achan->flag & ACHAN_HILIGHTED) {
308                                                                         make_icu_slider(block, icu,
309                                                                                                         x, y, SLIDERWIDTH-2, CHANNELHEIGHT-2, 
310                                                                                                         "Slider to control current value of IPO-Curve");
311                                                                 }
312                                                                 
313                                                                 y-=CHANNELHEIGHT+CHANNELSKIP;
314                                                         }
315                                                 }
316                                         }
317                                         
318                                         if (achan->constraintChannels.first) {
319                                                 y-=CHANNELHEIGHT+CHANNELSKIP;
320                                                 
321                                                 if (FILTER_CON_ACHAN(achan)) {
322                                                         for (conchan= achan->constraintChannels.first; conchan; conchan=conchan->next) {
323                                                                 if ((achan->flag & ACHAN_HILIGHTED) && EDITABLE_CONCHAN(conchan)) {
324                                                                         icu= (IpoCurve *)conchan->ipo->curve.first;
325                                                                         make_icu_slider(block, icu,
326                                                                                                         x, y, SLIDERWIDTH-2, CHANNELHEIGHT-2, 
327                                                                                                         "Slider to control current value of Constraint Channel");
328                                                                 }
329                                                                 
330                                                                 y-=CHANNELHEIGHT+CHANNELSKIP;
331                                                         }
332                                                 }
333                                         }
334                                 }
335                         }
336                 }
337         }
338         uiDrawBlock(block);
339 }
340
341 /********************************** Current Frame **************************** */
342
343 void draw_cfra_action (void)
344 {
345         Object *ob;
346         float vec[2];
347         
348         /* Draw a light green line to indicate current frame */
349         vec[0]= (G.scene->r.cfra);
350         vec[0]*= G.scene->r.framelen;
351         
352         vec[1]= G.v2d->cur.ymin;
353         glColor3ub(0x60, 0xc0, 0x40);
354         glLineWidth(2.0);
355         
356         glBegin(GL_LINE_STRIP);
357         glVertex2fv(vec);
358         vec[1]= G.v2d->cur.ymax;
359         glVertex2fv(vec);
360         glEnd();
361         
362         /* Draw dark green line if slow-parenting/time-offset is enabled */
363         ob= (G.scene->basact) ? (G.scene->basact->object) : 0;
364         if(ob && ob->sf!=0.0 && (ob->ipoflag & OB_OFFS_OB) ) {
365                 vec[0]-= ob->sf;
366                 
367                 glColor3ub(0x10, 0x60, 0);
368                 
369                 glBegin(GL_LINE_STRIP);
370                 glVertex2fv(vec);
371                 vec[1]= G.v2d->cur.ymin;
372                 glVertex2fv(vec);
373                 glEnd();
374         }
375         
376         glLineWidth(1.0);
377 }
378
379 /********************************** Left-Hand Panel + Generics **************************** */
380
381 /* left hand part */
382 static void draw_channel_names(void) 
383 {
384         ListBase act_data = {NULL, NULL};
385         bActListElem *ale;
386         int filter;
387         void *data;
388         short datatype;
389         short ofsx = 0, ofsy = 0; 
390         float x= 0.0f, y= 0.0f;
391         
392         /* determine what type of data we are operating on */
393         data = get_action_context(&datatype);
394         if (data == NULL) return;
395         
396         /* Clip to the scrollable area */
397         if(curarea->winx>SCROLLB+10 && curarea->winy>SCROLLH+10) {
398                 if(G.v2d->scroll) {     
399                         ofsx= curarea->winrct.xmin;     
400                         ofsy= curarea->winrct.ymin;
401                         glViewport(ofsx,  ofsy+G.v2d->mask.ymin, NAMEWIDTH, 
402                                            (ofsy+G.v2d->mask.ymax) -
403                                            (ofsy+G.v2d->mask.ymin)); 
404                         glScissor(ofsx,  ofsy+G.v2d->mask.ymin, NAMEWIDTH, 
405                                           (ofsy+G.v2d->mask.ymax) -
406                                           (ofsy+G.v2d->mask.ymin));
407                 }
408         }
409         
410         /* prepare scaling for LHS panel */
411         myortho2(0,     NAMEWIDTH, G.v2d->cur.ymin, G.v2d->cur.ymax);
412         
413         /* set default color back to black */
414         glColor3ub(0x00, 0x00, 0x00);
415         
416         /* build list of channels to draw */
417         filter= (ACTFILTER_FORDRAWING|ACTFILTER_VISIBLE|ACTFILTER_CHANNELS);
418         actdata_filter(&act_data, filter, data, datatype);
419                 
420         /* loop through channels, and set up drawing depending on their type  */
421         glEnable(GL_BLEND);
422         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
423         
424         for (ale= act_data.first; ale; ale= ale->next) {
425                 short indent= 0, offset= 0, sel= 0;
426                 int expand= -1, protect = -1, special= -1, mute = -1;
427                 char name[32];
428                 
429                 /* determine what needs to be drawn */
430                 switch (ale->type) {
431                         case ACTTYPE_ACHAN: /* action channel */
432                         {
433                                 bActionChannel *achan= (bActionChannel *)ale->data;
434                                 
435                                 indent = 0;
436                                 special = -1;
437                                 
438                                 if (EXPANDED_ACHAN(achan))
439                                         expand = ICON_TRIA_DOWN;
440                                 else
441                                         expand = ICON_TRIA_RIGHT;
442                                         
443                                 if (EDITABLE_ACHAN(achan))
444                                         protect = ICON_UNLOCKED;
445                                 else
446                                         protect = ICON_LOCKED;
447                                         
448                                 if (achan->ipo) {
449                                         if (achan->ipo->muteipo)
450                                                 mute = ICON_MUTE_IPO_ON;
451                                         else
452                                                 mute = ICON_MUTE_IPO_OFF;
453                                 }
454                                 
455                                 sel = SEL_ACHAN(achan);
456                                 sprintf(name, achan->name);
457                         }
458                                 break;
459                         case ACTTYPE_CONCHAN: /* constraint channel */
460                         {
461                                 bConstraintChannel *conchan = (bConstraintChannel *)ale->data;
462                                 
463                                 indent = 2;
464                                 
465                                 if (EDITABLE_CONCHAN(conchan))
466                                         protect = ICON_UNLOCKED;
467                                 else
468                                         protect = ICON_LOCKED;
469                                         
470                                 if (conchan->ipo) {
471                                         if (conchan->ipo->muteipo)
472                                                 mute = ICON_MUTE_IPO_ON;
473                                         else
474                                                 mute = ICON_MUTE_IPO_OFF;
475                                 }
476                                 
477                                 sel = SEL_CONCHAN(conchan);
478                                 sprintf(name, conchan->name);
479                         }
480                                 break;
481                         case ACTTYPE_ICU: /* ipo-curve channel */
482                         {
483                                 IpoCurve *icu = (IpoCurve *)ale->data;
484                                 
485                                 indent = 2;
486                                 protect = -1; // for now, until this can be supported by others
487                                 
488                                 if (icu->flag & IPO_MUTE)
489                                         mute = ICON_MUTE_IPO_ON;
490                                 else    
491                                         mute = ICON_MUTE_IPO_OFF;
492                                 
493                                 sel = SEL_ICU(icu);
494                                 if (G.saction->pin)
495                                         sprintf(name, getname_ipocurve(icu, NULL));
496                                 else
497                                         sprintf(name, getname_ipocurve(icu, OBACT));
498                         }
499                                 break;
500                         case ACTTYPE_SHAPEKEY: /* shapekey channel */
501                         {
502                                 KeyBlock *kb = (KeyBlock *)ale->data;
503                                 
504                                 indent = 0;
505                                 special = -1;
506                                 
507                                 if (kb->name[0] == '\0')
508                                         sprintf(name, "Key %d", ale->index);
509                                 else
510                                         sprintf(name, kb->name);
511                         }
512                                 break;
513                         case ACTTYPE_FILLIPO: /* ipo expand widget */
514                         {
515                                 bActionChannel *achan = (bActionChannel *)ale->data;
516                                 
517                                 indent = 1;
518                                 special = geticon_ipo_blocktype(achan->ipo->blocktype);
519                                 
520                                 if (FILTER_IPO_ACHAN(achan))    
521                                         expand = ICON_TRIA_DOWN;
522                                 else
523                                         expand = ICON_TRIA_RIGHT;
524                                 
525                                 sel = SEL_ACHAN(achan);
526                                 sprintf(name, "IPO Curves");
527                         }
528                                 break;
529                         case ACTTYPE_FILLCON: /* constraint expand widget */
530                         {
531                                 bActionChannel *achan = (bActionChannel *)ale->data;
532                                 
533                                 indent = 1;
534                                 special = ICON_CONSTRAINT;
535                                 
536                                 if (FILTER_CON_ACHAN(achan))    
537                                         expand = ICON_TRIA_DOWN;
538                                 else
539                                         expand = ICON_TRIA_RIGHT;
540                                         
541                                 sel = SEL_ACHAN(achan);
542                                 sprintf(name, "Constraint");
543                         }
544                                 break;
545                 }       
546
547                 /* now, start drawing based on this information */
548                 /* draw backing strip behind channel name */
549                 BIF_ThemeColorShade(TH_HEADER, ((indent==0)?20: (indent==1)?-20: -40));
550                 offset = 7 * indent;
551                 glRectf(x+offset,  y-CHANNELHEIGHT/2,  (float)NAMEWIDTH,  y+CHANNELHEIGHT/2);
552                 
553                 /* draw expand/collapse triangle */
554                 if (expand > 0) {
555                         BIF_icon_draw(x+offset, y-CHANNELHEIGHT/2, expand);
556                         offset += 17;
557                 }
558                 
559                 /* draw special icon indicating type of ipo-blocktype? 
560                  *      only for expand widgets for Ipo and Constraint Channels 
561                  */
562                 if (special > 0) {
563                         offset = 24;
564                         BIF_icon_draw(x+offset, y-CHANNELHEIGHT/2, special);
565                         offset += 17;
566                 }
567                         
568                 /* draw name */
569                 if (sel)
570                         BIF_ThemeColor(TH_TEXT_HI);
571                 else
572                         BIF_ThemeColor(TH_TEXT);
573                 offset += 3;
574                 glRasterPos2f(x+offset, y-4);
575                 BMF_DrawString(G.font, name);
576                 
577                 /* reset offset - for RHS of panel */
578                 offset = 0;
579                 
580                 /* draw protect 'lock' */
581                 if (protect > 0) {
582                         offset = 16;
583                         BIF_icon_draw(NAMEWIDTH-offset, y-CHANNELHEIGHT/2, protect);
584                 }
585                 
586                 /* draw mute 'eye' */
587                 if (mute > 0) {
588                         offset += 16;
589                         BIF_icon_draw(NAMEWIDTH-offset, y-CHANNELHEIGHT/2, mute);
590                 }
591                 
592                 /* adjust y-position for next one */
593                 y-=CHANNELHEIGHT+CHANNELSKIP;
594         }
595         
596         /* free tempolary channels */
597         BLI_freelistN(&act_data);
598         
599         /* re-adjust view matrices for correct scaling */
600     myortho2(0, NAMEWIDTH, 0, (ofsy+G.v2d->mask.ymax) - (ofsy+G.v2d->mask.ymin));       //      Scaling
601 }
602
603 /* sets or clears hidden flags */
604 void check_action_context(SpaceAction *saction)
605 {
606         bActionChannel *achan;
607         
608         if(saction->action==NULL) return;
609         
610         for (achan=saction->action->chanbase.first; achan; achan=achan->next)
611                 achan->flag &= ~ACHAN_HIDDEN;
612         
613         if (G.saction->pin==0 && OBACT) {
614                 Object *ob= OBACT;
615                 bPoseChannel *pchan;
616                 bArmature *arm= ob->data;
617                 
618                 for (achan=saction->action->chanbase.first; achan; achan=achan->next) {
619                         pchan= get_pose_channel(ob->pose, achan->name);
620                         if (pchan) {
621                                 if ((pchan->bone->layer & arm->layer)==0)
622                                         achan->flag |= ACHAN_HIDDEN;
623                                 else if (pchan->bone->flag & BONE_HIDDEN_P)
624                                         achan->flag |= ACHAN_HIDDEN;
625                         }
626                 }
627         }
628 }
629
630 static void draw_channel_strips(void)
631 {
632         ListBase act_data = {NULL, NULL};
633         bActListElem *ale;
634         int filter;
635         void *data;
636         short datatype;
637         
638         rcti scr_rct;
639         gla2DDrawInfo *di;
640         float y, sta, end;
641         int act_start, act_end, dummy;
642         char col1[3], col2[3];
643         
644         BIF_GetThemeColor3ubv(TH_SHADE2, col2);
645         BIF_GetThemeColor3ubv(TH_HILITE, col1);
646
647         /* get editor data */
648         data= get_action_context(&datatype);
649         if (data == NULL) return;
650
651         scr_rct.xmin= G.saction->area->winrct.xmin + G.saction->v2d.mask.xmin;
652         scr_rct.ymin= G.saction->area->winrct.ymin + G.saction->v2d.mask.ymin;
653         scr_rct.xmax= G.saction->area->winrct.xmin + G.saction->v2d.hor.xmax;
654         scr_rct.ymax= G.saction->area->winrct.ymin + G.saction->v2d.mask.ymax; 
655         di= glaBegin2DDraw(&scr_rct, &G.v2d->cur);
656
657         /* if in NLA there's a strip active, map the view */
658         if (datatype == ACTCONT_ACTION) {
659                 if (NLA_ACTION_SCALED)
660                         map_active_strip(di, OBACT, 0);
661                 
662                 /* start and end of action itself */
663                 calc_action_range(data, &sta, &end, 0);
664                 gla2DDrawTranslatePt(di, sta, 0.0f, &act_start, &dummy);
665                 gla2DDrawTranslatePt(di, end, 0.0f, &act_end, &dummy);
666                 
667                 if (NLA_ACTION_SCALED)
668                         map_active_strip(di, OBACT, 1);
669         }
670         
671         /* build list of channels to draw */
672         filter= (ACTFILTER_FORDRAWING|ACTFILTER_VISIBLE|ACTFILTER_CHANNELS);
673         actdata_filter(&act_data, filter, data, datatype);
674         
675         /* first backdrop strips */
676         y = 0.0;
677         glEnable(GL_BLEND);
678         for (ale= act_data.first; ale; ale= ale->next) {
679                 int frame1_x, channel_y, sel=0;
680                 
681                 /* determine if any need to draw channel */
682                 if (ale->datatype != ALE_NONE) {
683                         /* determine if channel is selected */
684                         switch (ale->type) {
685                                 case ACTTYPE_ACHAN:
686                                 {
687                                         bActionChannel *achan = (bActionChannel *)ale->data;
688                                         sel = SEL_ACHAN(achan);
689                                 }
690                                         break;
691                                 case ACTTYPE_CONCHAN:
692                                 {
693                                         bConstraintChannel *conchan = (bConstraintChannel *)ale->data;
694                                         sel = SEL_CONCHAN(conchan);
695                                 }
696                                         break;
697                                 case ACTTYPE_ICU:
698                                 {
699                                         IpoCurve *icu = (IpoCurve *)ale->data;
700                                         sel = SEL_ICU(icu);
701                                 }
702                                         break;
703                         }
704                         
705                         if (datatype == ACTCONT_ACTION) {
706                                 gla2DDrawTranslatePt(di, G.v2d->cur.xmin, y, &frame1_x, &channel_y);
707                                 
708                                 if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22);
709                                 else glColor4ub(col2[0], col2[1], col2[2], 0x22);
710                                 glRectf(frame1_x,  channel_y-CHANNELHEIGHT/2,  G.v2d->hor.xmax,  channel_y+CHANNELHEIGHT/2);
711                                 
712                                 if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22);
713                                 else glColor4ub(col2[0], col2[1], col2[2], 0x22);
714                                 glRectf(act_start,  channel_y-CHANNELHEIGHT/2,  act_end,  channel_y+CHANNELHEIGHT/2);
715                         }
716                         else if (datatype == ACTCONT_SHAPEKEY) {
717                                 gla2DDrawTranslatePt(di, 1, y, &frame1_x, &channel_y);
718                                 
719                                 /* all frames that have a frame number less than one
720                                  * get a desaturated orange background
721                                  */
722                                 glColor4ub(col2[0], col2[1], col2[2], 0x22);
723                                 glRectf(0, channel_y-CHANNELHEIGHT/2, frame1_x, channel_y+CHANNELHEIGHT/2);
724                                 
725                                 /* frames one and higher get a saturated orange background */
726                                 glColor4ub(col2[0], col2[1], col2[2], 0x44);
727                                 glRectf(frame1_x, channel_y-CHANNELHEIGHT/2, G.v2d->hor.xmax,  channel_y+CHANNELHEIGHT/2);
728                         }
729                 }
730                 
731                 /*      Increment the step */
732                 y-=CHANNELHEIGHT+CHANNELSKIP;
733         }               
734         glDisable(GL_BLEND);
735         
736         if (NLA_ACTION_SCALED)
737                 map_active_strip(di, OBACT, 0);
738         
739         /* draw keyframes */
740         y = 0.0;
741         for (ale= act_data.first; ale; ale= ale->next) {
742                 switch (ale->datatype) {
743                         case ALE_IPO:
744                                 draw_ipo_channel(di, ale->key_data, y);
745                                 break;
746                         case ALE_ICU:
747                                 draw_icu_channel(di, ale->key_data, y);
748                                 break;
749                 }
750                 
751                 y-=CHANNELHEIGHT+CHANNELSKIP;
752         }
753         
754         /* free tempolary channels used for drawing */
755         BLI_freelistN(&act_data);
756
757         /* black line marking 'current frame' for Time-Slide transform mode */
758         if (G.saction->flag & SACTION_MOVING) {
759                 int frame1_x, channel_y;
760                 
761                 gla2DDrawTranslatePt(di, G.saction->timeslide, 0, &frame1_x, &channel_y);
762                 cpack(0x0);
763                 
764                 glBegin(GL_LINES);
765                 glVertex2f(frame1_x, G.v2d->mask.ymin - 100);
766                 glVertex2f(frame1_x, G.v2d->mask.ymax);
767                 glEnd();
768         }
769         
770         glaEnd2DDraw(di);
771 }
772
773 /* ********* action panel *********** */
774
775
776 void do_actionbuts(unsigned short event)
777 {
778         switch(event) {
779         case REDRAWVIEW3D:
780                 allqueue(REDRAWVIEW3D, 0);
781                 break;
782         case B_REDR:
783                 allqueue(REDRAWACTION, 0);
784                 break;
785         }
786 }
787
788
789 static void action_panel_properties(short cntrl)        // ACTION_HANDLER_PROPERTIES
790 {
791         uiBlock *block;
792
793         block= uiNewBlock(&curarea->uiblocks, "action_panel_properties", UI_EMBOSS, UI_HELV, curarea->win);
794         uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl);
795         uiSetPanelHandler(ACTION_HANDLER_PROPERTIES);  // for close and esc
796         if(uiNewPanel(curarea, block, "Transform Properties", "Action", 10, 230, 318, 204)==0) return;
797
798         uiDefBut(block, LABEL, 0, "test text",          10,180,300,19, 0, 0, 0, 0, 0, "");
799
800 }
801
802 static void action_blockhandlers(ScrArea *sa)
803 {
804         SpaceAction *sact= sa->spacedata.first;
805         short a;
806         
807         for(a=0; a<SPACE_MAXHANDLER; a+=2) {
808                 switch(sact->blockhandler[a]) {
809
810                 case ACTION_HANDLER_PROPERTIES:
811                         action_panel_properties(sact->blockhandler[a+1]);
812                         break;
813                 
814                 }
815                 /* clear action value for event */
816                 sact->blockhandler[a+1]= 0;
817         }
818         uiDrawBlocksPanels(sa, 0);
819 }
820
821 /* ************************* Action Editor Space ***************************** */
822
823 void drawactionspace(ScrArea *sa, void *spacedata)
824 {
825         bAction *act = NULL;
826         Key *key = NULL;
827         void *data;
828         short datatype;
829         
830         short ofsx = 0, ofsy = 0;
831         float col[3];
832
833         /* this is unlikely to occur, but it may */
834         if (G.saction == NULL)
835                 return;
836
837         /* warning: blocks need to be freed each time, handlers dont remove  */
838         uiFreeBlocksWin(&sa->uiblocks, sa->win);
839
840         /* only try to refresh action that's displayed if not pinned */
841         if (G.saction->pin==0) {
842                 /* TODO: allow more than one active action sometime? */
843                 if (OBACT)
844                         G.saction->action = OBACT->action;
845                 else
846                         G.saction->action= NULL;
847         }
848         
849         /* get data */
850         data = get_action_context(&datatype);
851         if (datatype == ACTCONT_ACTION)
852                 act = data;
853         else if (datatype == ACTCONT_SHAPEKEY)
854                 key = data;
855         
856         /* Lets make sure the width of the left hand of the screen
857          * is set to an appropriate value based on whether sliders
858          * are showing of not
859          */
860         if ((data) && (G.saction->flag & SACTION_SLIDERS)) 
861                 ACTWIDTH = NAMEWIDTH + SLIDERWIDTH;
862         else 
863                 ACTWIDTH = NAMEWIDTH;
864
865         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
866
867         calc_scrollrcts(sa, G.v2d, curarea->winx, curarea->winy);
868
869         /* background color for entire window (used in lefthand part though) */
870         BIF_GetThemeColor3fv(TH_HEADER, col);
871         glClearColor(col[0], col[1], col[2], 0.0); 
872         glClear(GL_COLOR_BUFFER_BIT);
873         
874         if(curarea->winx>SCROLLB+10 && curarea->winy>SCROLLH+10) {
875                 if(G.v2d->scroll) {     
876                         ofsx= curarea->winrct.xmin;     
877                         ofsy= curarea->winrct.ymin;
878                         glViewport(ofsx+G.v2d->mask.xmin,  ofsy+G.v2d->mask.ymin, 
879                                            ( ofsx+G.v2d->mask.xmax-1)-(ofsx+G.v2d->mask.xmin)+1, 
880                                            ( ofsy+G.v2d->mask.ymax-1)-( ofsy+G.v2d->mask.ymin)+1); 
881                         glScissor(ofsx+G.v2d->mask.xmin,  ofsy+G.v2d->mask.ymin, 
882                                           ( ofsx+G.v2d->mask.xmax-1)-(ofsx+G.v2d->mask.xmin)+1, 
883                                           ( ofsy+G.v2d->mask.ymax-1)-( ofsy+G.v2d->mask.ymin)+1);
884                 }
885         }
886
887         BIF_GetThemeColor3fv(TH_BACK, col);
888         glClearColor(col[0], col[1], col[2], 0.0);
889         glClear(GL_COLOR_BUFFER_BIT);
890
891         myortho2(G.v2d->cur.xmin, G.v2d->cur.xmax, G.v2d->cur.ymin, G.v2d->cur.ymax);
892         bwin_clear_viewmat(sa->win);    /* clear buttons view */
893         glLoadIdentity();
894
895         /*      Draw backdrop */
896         calc_ipogrid(); 
897         draw_ipogrid();
898
899         check_action_context(G.saction);
900         
901         /* Draw channel strips */
902         draw_channel_strips();
903         
904         /* reset matrices for stuff to be drawn on top of keys*/
905         glViewport(ofsx+G.v2d->mask.xmin,  
906              ofsy+G.v2d->mask.ymin, 
907              ( ofsx+G.v2d->mask.xmax-1)-(ofsx+G.v2d->mask.xmin)+1, 
908              ( ofsy+G.v2d->mask.ymax-1)-( ofsy+G.v2d->mask.ymin)+1); 
909         glScissor(ofsx+G.v2d->mask.xmin,  
910             ofsy+G.v2d->mask.ymin, 
911             ( ofsx+G.v2d->mask.xmax-1)-(ofsx+G.v2d->mask.xmin)+1, 
912             ( ofsy+G.v2d->mask.ymax-1)-( ofsy+G.v2d->mask.ymin)+1);
913         myortho2(G.v2d->cur.xmin, G.v2d->cur.xmax,  G.v2d->cur.ymin, G.v2d->cur.ymax);
914         
915         /* Draw current frame */
916         draw_cfra_action();
917         
918         /* Draw markers */
919         draw_markers_timespace();
920         
921         /* Draw 'curtains' for preview */
922         draw_anim_preview_timespace();
923
924         /* Draw scroll */
925         mywinset(curarea->win); // reset scissor too
926         if (curarea->winx>SCROLLB+10 && curarea->winy>SCROLLH+10) {
927                 myortho2(-0.375, curarea->winx-0.375, -0.375, curarea->winy-0.375);
928                 if (G.v2d->scroll) drawscroll(0);
929         }
930
931         /* Draw Left-Hand Panel if enough space in window */
932         if (G.v2d->mask.xmin!=0) {
933                 /* Draw channel names */
934                 draw_channel_names();
935                 
936                 if(sa->winx > 50 + NAMEWIDTH + SLIDERWIDTH) {
937                         if (act) {
938                                 /* if there is an action, draw sliders for its
939                                  * ipo-curve channels in the action window
940                                  */
941                                 action_icu_buts(G.saction);
942                         }
943                         else if (key) {
944                                 /* if there is a mesh with rvk's selected,
945                                  * then draw the key frames in the action window
946                                  */
947                                 meshactionbuts(G.saction, OBACT, key);
948                         }
949                 }
950         }
951         
952         mywinset(curarea->win); // reset scissor too
953         myortho2(-0.375, curarea->winx-0.375, -0.375, curarea->winy-0.375);
954         draw_area_emboss(sa);
955
956         /* it is important to end a view in a transform compatible with buttons */
957         bwin_scalematrix(sa->win, G.saction->blockscale, G.saction->blockscale, G.saction->blockscale);
958         action_blockhandlers(sa);
959
960         curarea->win_swap= WIN_BACK_OK;
961 }
962
963 /* *************************** Keyframe Drawing *************************** */
964
965 static void add_bezt_to_keycolumnslist(ListBase *keys, BezTriple *bezt)
966 {
967         /* The equivilant of add_to_cfra_elem except this version 
968          * makes ActKeyColumns - one of the two datatypes required
969          * for action editor drawing.
970          */
971         ActKeyColumn *ak, *akn;
972         
973         if (!(keys) || !(bezt)) return;
974         
975         /* try to find a keyblock that starts on the previous beztriple */
976         for (ak= keys->first; ak; ak= ak->next) {
977                 /* do because of double keys */
978                 if (ak->cfra == bezt->vec[1][0]) {                      
979                         /* set selection status and 'touched' status */
980                         if (BEZSELECTED(bezt)) ak->sel = SELECT;
981                         ak->modified += 1;
982                         
983                         return;
984                 }
985                 else if (ak->cfra > bezt->vec[1][0]) break;
986         }
987         
988         /* add new block */
989         akn= MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn");
990         if (ak) BLI_insertlinkbefore(keys, ak, akn);
991         else BLI_addtail(keys, akn);
992         
993         akn->cfra= bezt->vec[1][0];
994         akn->modified += 1;
995         
996         // TODO: handle type = bezt->h1 or bezt->h2
997         akn->handle_type= 0; 
998         
999         if (BEZSELECTED(bezt))
1000                 akn->sel = SELECT;
1001         else
1002                 akn->sel = 0;
1003 }
1004
1005 static void add_bezt_to_keyblockslist(ListBase *blocks, IpoCurve *icu, int index)
1006 {
1007         /* The equivilant of add_to_cfra_elem except this version 
1008          * makes ActKeyBlocks - one of the two datatypes required
1009          * for action editor drawing.
1010          */
1011         ActKeyBlock *ab, *abn;
1012         BezTriple *beztn=NULL, *prev=NULL;
1013         BezTriple *bezt;
1014         int v;
1015         
1016         /* get beztriples */
1017         beztn= (icu->bezt + index);
1018         
1019         for (v=0, bezt=icu->bezt; v<icu->totvert; v++, bezt++) {
1020                 /* skip if beztriple is current */
1021                 if (v != index) {
1022                         /* check if beztriple is immediately before */
1023                         if (beztn->vec[1][0] > bezt->vec[1][0]) {
1024                                 /* check if closer than previous was */
1025                                 if (prev) {
1026                                         if (prev->vec[1][0] < bezt->vec[1][0])
1027                                                 prev= bezt;
1028                                 }
1029                                 else {
1030                                         prev= bezt;
1031                                 }
1032                         }
1033                 }
1034         }
1035         
1036         /* check if block needed - same value? */
1037         if ((!prev) || (!beztn))
1038                 return;
1039         if (beztn->vec[1][1] != prev->vec[1][1])
1040                 return;
1041         
1042         /* try to find a keyblock that starts on the previous beztriple */
1043         for (ab= blocks->first; ab; ab= ab->next) {
1044                 /* check if alter existing block or add new block */
1045                 if (ab->start == prev->vec[1][0]) {                     
1046                         /* set selection status and 'touched' status */
1047                         if (BEZSELECTED(beztn)) ab->sel = SELECT;
1048                         ab->modified += 1;
1049                         
1050                         return;
1051                 }
1052                 else if (ab->start > prev->vec[1][0]) break;
1053         }
1054         
1055         /* add new block */
1056         abn= MEM_callocN(sizeof(ActKeyBlock), "ActKeyBlock");
1057         if (ab) BLI_insertlinkbefore(blocks, ab, abn);
1058         else BLI_addtail(blocks, abn);
1059         
1060         abn->start= prev->vec[1][0];
1061         abn->end= beztn->vec[1][0];
1062         abn->val= beztn->vec[1][1];
1063         
1064         if (BEZSELECTED(prev) || BEZSELECTED(beztn))
1065                 abn->sel = SELECT;
1066         else
1067                 abn->sel = 0;
1068         abn->modified = 1;
1069 }
1070
1071 /* helper function - find actkeycolumn that occurs on cframe */
1072 static ActKeyColumn *cfra_find_actkeycolumn (ListBase *keys, float cframe)
1073 {
1074         ActKeyColumn *ak;
1075         
1076         if (keys==NULL) 
1077                 return NULL;
1078          
1079         for (ak= keys->first; ak; ak= ak->next) {
1080                 if (ak->cfra == cframe)
1081                         return ak;
1082         }
1083         
1084         return NULL;
1085 }
1086
1087 static void draw_keylist(gla2DDrawInfo *di, ListBase *keys, ListBase *blocks, float ypos)
1088 {
1089         ActKeyColumn *ak;
1090         ActKeyBlock *ab;
1091         
1092         glEnable(GL_BLEND);
1093         
1094         /* draw keyblocks */
1095         if (blocks) {
1096                 for (ab= blocks->first; ab; ab= ab->next) {
1097                         short startCurves, endCurves, totCurves;
1098                         
1099                         /* find out how many curves occur at each keyframe */
1100                         ak= cfra_find_actkeycolumn(keys, ab->start);
1101                         startCurves = (ak)? ak->totcurve: 0;
1102                         
1103                         ak= cfra_find_actkeycolumn(keys, ab->end);
1104                         endCurves = (ak)? ak->totcurve: 0;
1105                         
1106                         /* only draw keyblock if it appears in at all of the keyframes at lowest end */
1107                         if (!startCurves && !endCurves) 
1108                                 continue;
1109                         else
1110                                 totCurves = (startCurves>endCurves)? endCurves: startCurves;
1111                                 
1112                         if (ab->totcurve >= totCurves) {
1113                                 int sc_xa, sc_ya;
1114                                 int sc_xb, sc_yb;
1115                                 
1116                                 /* get co-ordinates of block */
1117                                 gla2DDrawTranslatePt(di, ab->start, ypos, &sc_xa, &sc_ya);
1118                                 gla2DDrawTranslatePt(di, ab->end, ypos, &sc_xb, &sc_yb);
1119                                 
1120                                 /* draw block */
1121                                 if (ab->sel)
1122                                         BIF_ThemeColor4(TH_STRIP_SELECT);
1123                                 else
1124                                         BIF_ThemeColor4(TH_STRIP);
1125                                 glRectf(sc_xa,  sc_ya-3,  sc_xb,  sc_yb+5);
1126                         }
1127                 }
1128         }
1129         
1130         /* draw keys */
1131         if (keys) {
1132                 for (ak= keys->first; ak; ak= ak->next) {
1133                         int sc_x, sc_y;
1134                         
1135                         /* get co-ordinate to draw at */
1136                         gla2DDrawTranslatePt(di, ak->cfra, ypos, &sc_x, &sc_y);
1137                         
1138                         if(ak->sel & 1) BIF_icon_draw_aspect(sc_x-7, sc_y-6, ICON_SPACE2, 1.0f);
1139                         else BIF_icon_draw_aspect(sc_x-7, sc_y-6, ICON_SPACE3, 1.0f);
1140                 }       
1141         }
1142         
1143         glDisable(GL_BLEND);
1144 }
1145
1146 void draw_object_channel(gla2DDrawInfo *di, Object *ob, float ypos)
1147 {
1148         ListBase keys = {0, 0};
1149         ListBase blocks = {0, 0};
1150
1151         ob_to_keylist(ob, &keys, &blocks);
1152         draw_keylist(di, &keys, &blocks, ypos);
1153         
1154         BLI_freelistN(&keys);
1155         BLI_freelistN(&blocks);
1156 }
1157
1158 void draw_ipo_channel(gla2DDrawInfo *di, Ipo *ipo, float ypos)
1159 {
1160         ListBase keys = {0, 0};
1161         ListBase blocks = {0, 0};
1162
1163         ipo_to_keylist(ipo, &keys, &blocks);
1164         draw_keylist(di, &keys, &blocks, ypos);
1165         
1166         BLI_freelistN(&keys);
1167         BLI_freelistN(&blocks);
1168 }
1169
1170 void draw_icu_channel(gla2DDrawInfo *di, IpoCurve *icu, float ypos)
1171 {
1172         ListBase keys = {0, 0};
1173         ListBase blocks = {0, 0};
1174
1175         icu_to_keylist(icu, &keys, &blocks);
1176         draw_keylist(di, &keys, &blocks, ypos);
1177         
1178         BLI_freelistN(&keys);
1179         BLI_freelistN(&blocks);
1180 }
1181
1182 void draw_action_channel(gla2DDrawInfo *di, bAction *act, float ypos)
1183 {
1184         ListBase keys = {0, 0};
1185
1186         action_to_keylist(act, &keys, NULL);
1187         draw_keylist(di, &keys, NULL, ypos);
1188         BLI_freelistN(&keys);
1189 }
1190
1191 /* --------------- Conversion: data -> keyframe list ------------------ */
1192
1193 void ob_to_keylist(Object *ob, ListBase *keys, ListBase *blocks)
1194 {
1195         bConstraintChannel *conchan;
1196
1197         if (ob) {
1198                 /* Add object keyframes */
1199                 if (ob->ipo)
1200                         ipo_to_keylist(ob->ipo, keys, blocks);
1201                 
1202                 /* Add constraint keyframes */
1203                 for (conchan=ob->constraintChannels.first; conchan; conchan=conchan->next){
1204                         if(conchan->ipo)
1205                                 ipo_to_keylist(conchan->ipo, keys, blocks);             
1206                 }
1207                         
1208                 /* Add object data keyframes */
1209                 //              TODO??
1210         }
1211 }
1212
1213 void icu_to_keylist(IpoCurve *icu, ListBase *keys, ListBase *blocks)
1214 {
1215         BezTriple *bezt;
1216         ActKeyColumn *ak;
1217         ActKeyBlock *ab;
1218         int v;
1219         
1220         if (icu && icu->totvert) {
1221                 /* loop through beztriples, making ActKeys and ActKeyBlocks */
1222                 bezt= icu->bezt;
1223                 
1224                 for (v=0; v<icu->totvert; v++, bezt++) {
1225                         add_bezt_to_keycolumnslist(keys, bezt);
1226                         if (blocks) add_bezt_to_keyblockslist(blocks, icu, v);
1227                 }
1228                 
1229                 /* update the number of curves that elements have appeared in  */
1230                 if (keys) {
1231                         for (ak= keys->first; ak; ak= ak->next) {
1232                                 if (ak->modified) {
1233                                         ak->modified = 0;
1234                                         ak->totcurve += 1;
1235                                 }
1236                         }
1237                 }
1238                 if (blocks) {
1239                         for (ab= blocks->first; ab; ab= ab->next) {
1240                                 if (ab->modified) {
1241                                         ab->modified = 0;
1242                                         ab->totcurve += 1;
1243                                 }
1244                         }
1245                 }
1246         }
1247 }
1248
1249 void ipo_to_keylist(Ipo *ipo, ListBase *keys, ListBase *blocks)
1250 {
1251         IpoCurve *icu;
1252         
1253         if (ipo) {
1254                 for (icu= ipo->curve.first; icu; icu= icu->next)
1255                         icu_to_keylist(icu, keys, blocks);
1256         }
1257 }
1258
1259 void action_to_keylist(bAction *act, ListBase *keys, ListBase *blocks)
1260 {
1261         bActionChannel *achan;
1262         bConstraintChannel *conchan;
1263
1264         if (act) {
1265                 /* loop through action channels */
1266                 for (achan= act->chanbase.first; achan; achan= achan->next) {
1267                         /* firstly, add keys from action channel's ipo block */
1268                         if (achan->ipo)
1269                                 ipo_to_keylist(achan->ipo, keys, blocks);
1270                         
1271                         /* then, add keys from constraint channels */
1272                         for (conchan= achan->constraintChannels.first; conchan; conchan= conchan->next) {
1273                                 if (conchan->ipo)
1274                                         ipo_to_keylist(achan->ipo, keys, blocks);
1275                         }
1276                 }
1277         }
1278 }
1279