added mousewheel support part 3
[blender.git] / source / blender / src / editaction.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): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  */
32
33 #include <string.h>
34 #include <math.h>
35
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif
39
40 #ifdef WIN32
41 #include "BLI_winstuff.h"
42 #endif
43
44 #include "MEM_guardedalloc.h"
45
46 #include "PIL_time.h"
47
48 #include "BLI_blenlib.h"
49 #include "BLI_arithb.h"
50
51 #include "DNA_action_types.h"
52 #include "DNA_armature_types.h"
53 #include "DNA_curve_types.h"
54 #include "DNA_ipo_types.h"
55 #include "DNA_object_types.h"
56 #include "DNA_scene_types.h"
57 #include "DNA_screen_types.h"
58 #include "DNA_userdef_types.h"
59 #include "DNA_constraint_types.h"
60
61 #include "BKE_armature.h"
62 #include "BKE_constraint.h"
63 #include "BKE_displist.h"
64 #include "BKE_curve.h"
65 #include "BKE_ipo.h"
66 #include "BKE_global.h"
67 #include "BKE_library.h"
68 #include "BKE_main.h"
69 #include "BKE_action.h"
70
71 #include "BIF_gl.h"
72 #include "BIF_mywindow.h"
73 #include "BIF_toolbox.h"
74 #include "BIF_screen.h"
75 #include "BIF_space.h"
76 #include "BIF_buttons.h"
77 #include "BIF_interface.h"
78 #include "BIF_editview.h"
79 #include "BIF_poseobject.h"
80 #include "BIF_editarmature.h"
81
82 #include "BSE_edit.h"
83 #include "BSE_drawipo.h"
84 #include "BSE_headerbuttons.h"
85 #include "BSE_editipo.h"
86 #include "BSE_editaction.h"
87 #include "BSE_trans_types.h"
88 #include "BSE_editaction_types.h"
89
90 #include "BDR_editobject.h"
91
92 #include "blendertimer.h"
93
94 #include "interface.h"
95 #include "mydevice.h"
96 #include "blendef.h"
97 #include "nla.h"
98
99 static bPose    *g_posebuf=NULL;
100 extern int count_action_levels (bAction *act);
101
102 #define BEZSELECTED(bezt)   (((bezt)->f1 & 1) || ((bezt)->f2 & 1) || ((bezt)->f3 & 1))
103
104 /* Local Function prototypes */
105
106 static void insertactionkey(bAction *act, bActionChannel *achan, bPoseChannel *chan, int adrcode, short makecurve, float time);
107 static void flip_name (char *name);
108 static void mouse_actionchannels(bAction *act, short *mval);
109 static void borderselect_action(void);
110 static void mouse_action(void);
111 static bActionChannel *get_nearest_actionchannel_key (float *index, short *sel, bConstraintChannel **conchan);
112 static void delete_actionchannels(void);
113 static void delete_actionchannel_keys(void);
114 static void duplicate_actionchannel_keys(void);
115 static void transform_actionchannel_keys(char mode);
116 static void select_poseelement_by_name (char *name, int select);
117 static void hilight_channel (bAction *act, bActionChannel *chan, short hilight);
118 static void set_action_key_time (bAction *act, bPoseChannel *chan, int adrcode, short makecurve, float time);
119
120 /* Implementation */
121
122 static void select_poseelement_by_name (char *name, int select)
123 {
124         /* Synchs selection of channels with selection of object elements in posemode */
125
126         Object *ob;
127
128         ob = G.obpose;
129         if (!ob)
130                 return;
131
132         switch (ob->type){
133         case OB_ARMATURE:
134                 select_bone_by_name ((bArmature*)ob->data, name, select);
135                 break;
136         default:
137                 break;
138         }
139 }
140 #ifdef __NLA_BAKE
141 bAction* bake_action_with_client (bAction *act, Object *armob, float tolerance)
142 {
143         bAction                 *result=NULL;
144         bActionChannel *achan;
145         float                   actlen;
146         int                             curframe;
147         char                    newname[64];
148         bArmature               *arm;
149         Bone                    *bone;
150         float                   oldframe;
151         bAction                 *temp;
152         bPoseChannel *pchan;
153
154         if (!act)
155                 return NULL;
156
157         arm = get_armature(armob);
158
159         
160         if (G.obedit){
161                 error ("Not in editmode");
162                 return NULL;
163         }
164
165         if (!arm){
166                 error ("Must have an armature selected");
167                 return NULL;
168         }
169         /* Get a new action */
170         result = add_empty_action();
171
172         /* Assign the new action a unique name */
173         sprintf (newname, "%s.BAKED", act->id.name+2);
174         rename_id(&result->id, newname);
175
176         actlen = calc_action_end(act);
177
178         oldframe = G.scene->r.cfra;
179
180         temp = armob->action;
181         armob->action = act;
182         
183         for (curframe=1; curframe<ceil(actlen+1); curframe++){
184
185                 /* Apply the old action */
186                 
187                 G.scene->r.cfra = curframe;
188
189                 /* Apply the object ipo */
190                 get_pose_from_action(&armob->pose, act, curframe);
191                 apply_pose_armature(arm, armob->pose, 1);
192                 clear_object_constraint_status(armob);
193                 where_is_armature_time(armob, curframe);
194                 
195                 /* For each channel: set avail keys with current values */
196                 for (pchan=armob->pose->chanbase.first; pchan; pchan=pchan->next){      
197
198                         /* Copy the constraints from the armature (if any) */
199
200                         bone = get_named_bone(arm, pchan->name);
201                         if (bone){
202
203                                 Mat4ToQuat(pchan->obmat, pchan->quat);
204                                 Mat4ToSize(pchan->obmat, pchan->size);
205                                 VECCOPY(pchan->loc, pchan->obmat[3]);
206                         
207                                 /* Apply to keys */      
208                                 set_action_key_time (result, pchan, AC_QUAT_X, 1, curframe);
209                                 set_action_key_time (result, pchan, AC_QUAT_Y, 1, curframe);
210                                 set_action_key_time (result, pchan, AC_QUAT_Z, 1, curframe);
211                                 set_action_key_time (result, pchan, AC_QUAT_W, 1, curframe);
212                                 set_action_key_time (result, pchan, AC_LOC_X, 1, curframe);
213                                 set_action_key_time (result, pchan, AC_LOC_Y, 1, curframe);
214                                 set_action_key_time (result, pchan, AC_LOC_Z, 1, curframe);
215                         }
216                 }
217         }
218
219         /* Make another pass to ensure all keyframes are set to linear interpolation mode */
220         for (achan = result->chanbase.first; achan; achan=achan->next){
221                 IpoCurve* icu;
222                 for (icu = achan->ipo->curve.first; icu; icu=icu->next){
223                         icu->ipo= IPO_LIN;
224                 }
225         }
226
227         notice ("Made new action \"%s\"", newname);
228         G.scene->r.cfra = oldframe;
229         armob->action = temp;
230         return result;
231 }
232 #endif
233
234 void select_actionchannel_by_name (bAction *act, char *name, int select)
235 {
236         bActionChannel *chan;
237
238         if (!act)
239                 return;
240
241         for (chan = act->chanbase.first; chan; chan=chan->next){
242                 if (!strcmp (chan->name, name)){
243                         act->achan = chan;
244                         if (select){
245                                 chan->flag |= ACHAN_SELECTED;
246                                 hilight_channel (act, chan, 1);
247                         }
248                         else{
249                                 chan->flag &= ~ACHAN_SELECTED;
250                                 hilight_channel (act, chan, 0);
251                         }
252                         return;
253                 }
254         }
255 }
256
257 void remake_action_ipos(bAction *act)
258 {
259         bActionChannel *chan;
260         bConstraintChannel *conchan;
261         IpoCurve                *icu;
262
263         for (chan= act->chanbase.first; chan; chan=chan->next){
264                 if (chan->ipo){
265                         for (icu = chan->ipo->curve.first; icu; icu=icu->next){
266                                 sort_time_ipocurve(icu);
267                                 testhandles_ipocurve(icu);
268                         }
269                 }
270                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
271                         if (conchan->ipo){
272                                 for (icu = conchan->ipo->curve.first; icu; icu=icu->next){
273                                         sort_time_ipocurve(icu);
274                                         testhandles_ipocurve(icu);
275                                 }
276                         }
277                 }
278         }
279 }
280
281 static void duplicate_actionchannel_keys(void)
282 {
283         bAction *act;
284         bActionChannel *chan;
285         bConstraintChannel *conchan;
286
287         act=G.saction->action;
288         if (!act)
289                 return;
290
291         /* Find selected items */
292         for (chan = act->chanbase.first; chan; chan=chan->next){
293                 duplicate_ipo_keys(chan->ipo);
294                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
295                         duplicate_ipo_keys(conchan->ipo);
296         }
297
298         transform_actionchannel_keys ('g');
299 }
300
301 static bActionChannel *get_nearest_actionchannel_key (float *index, short *sel, bConstraintChannel **rchan){
302         bAction *act;
303         bActionChannel *chan;
304         IpoCurve *icu;
305         bActionChannel *firstchan=NULL;
306         bConstraintChannel *conchan, *firstconchan=NULL;
307         int     foundsel=0;
308         float firstvert=-1, foundx=-1;
309                 int i;
310         short mval[2];
311         float ymin, ymax;
312         rctf    rectf;
313         *index=0;
314
315         *rchan=NULL;
316         act=G.saction->action; /* We presume that we are only called during a valid action */
317         
318         getmouseco_areawin (mval);
319
320         mval[0]-=7;
321         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
322
323         mval[0]+=14;
324         areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
325
326         ymax = count_action_levels (act) * (CHANNELHEIGHT + CHANNELSKIP);
327
328         *sel=0;
329
330         for (chan=act->chanbase.first; chan; chan=chan->next){
331
332                 /* Check action channel */
333                 ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
334                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
335                         for (icu=chan->ipo->curve.first; icu; icu=icu->next){
336                                 for (i=0; i<icu->totvert; i++){
337                                         if (icu->bezt[i].vec[1][0] > rectf.xmin && icu->bezt[i].vec[1][0] <= rectf.xmax ){
338                                                 if (!firstchan){
339                                                         firstchan=chan;
340                                                         firstvert=icu->bezt[i].vec[1][0];
341                                                         *sel = icu->bezt[i].f2 & 1;     
342                                                 }
343                                                 
344                                                 if (icu->bezt[i].f2 & 1){ 
345                                                         if (!foundsel){
346                                                                 foundsel=1;
347                                                                 foundx = icu->bezt[i].vec[1][0];
348                                                         }
349                                                 }
350                                                 else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
351                                                         *index=icu->bezt[i].vec[1][0];
352                                                         *sel = 0;
353                                                         return chan;
354                                                 }
355                                         }
356                                 }
357                         }
358                 }
359                 ymax=ymin;
360                 
361                 /* Check constraint channels */
362                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
363                         ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
364                         if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))){
365                                 for (icu=conchan->ipo->curve.first; icu; icu=icu->next){
366                                         for (i=0; i<icu->totvert; i++){
367                                                 if (icu->bezt[i].vec[1][0] > rectf.xmin && icu->bezt[i].vec[1][0] <= rectf.xmax ){
368                                                         if (!firstchan){
369                                                                 firstchan=chan;
370                                                                 firstconchan=conchan;
371                                                                 firstvert=icu->bezt[i].vec[1][0];
372                                                                 *sel = icu->bezt[i].f2 & 1;     
373                                                         }
374                                                         
375                                                         if (icu->bezt[i].f2 & 1){ 
376                                                                 if (!foundsel){
377                                                                         foundsel=1;
378                                                                         foundx = icu->bezt[i].vec[1][0];
379                                                                 }
380                                                         }
381                                                         else if (foundsel && icu->bezt[i].vec[1][0] != foundx){
382                                                                 *index=icu->bezt[i].vec[1][0];
383                                                                 *sel = 0;
384                                                                 *rchan = conchan;
385                                                                 return chan;
386                                                         }
387                                                 }
388                                         }
389                                 }
390                         }
391                         ymax=ymin;
392                 }
393         }       
394         
395         *rchan = firstconchan;
396         *index=firstvert;
397         return firstchan;
398 }
399
400 static void mouse_action(void)
401 {
402         bAction *act;
403         short sel;
404         float   selx;
405         bActionChannel *chan;
406         bConstraintChannel *conchan;
407         short   mval[2];
408
409         act=G.saction->action;
410         if (!act)
411                 return;
412
413         getmouseco_areawin (mval);
414
415         chan=get_nearest_actionchannel_key(&selx, &sel, &conchan);
416
417         if (chan){
418                 if (!(G.qual & LR_SHIFTKEY)){
419                         deselect_actionchannel_keys(act, 0);
420                         deselect_actionchannels(act, 0);
421                         act->achan = chan;
422                         chan->flag |= ACHAN_SELECTED;
423                         hilight_channel (act, chan, 1);
424                         sel = 0;
425                 }
426                 
427                 if (conchan)
428                         select_ipo_key(conchan->ipo, selx, sel);
429                 else
430                         select_ipo_key(chan->ipo, selx, sel);
431
432                 allqueue(REDRAWIPO, 0);
433                 allqueue(REDRAWVIEW3D, 0);
434                 allqueue(REDRAWACTION, 0);
435                 allqueue(REDRAWNLA, 0);
436
437         }
438 }
439
440 static void borderselect_action(void)
441
442         rcti rect;
443         rctf rectf;
444         int val;                
445         short   mval[2];
446         bActionChannel *chan;
447         bConstraintChannel *conchan;
448         bAction *act;
449         float   ymin, ymax;
450
451         act=G.saction->action;
452         val= get_border (&rect, 3);
453
454         if (!act)
455                 return;
456
457         if (val){
458                 mval[0]= rect.xmin;
459                 mval[1]= rect.ymin+2;
460                 areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin);
461                 mval[0]= rect.xmax;
462                 mval[1]= rect.ymax-2;
463                 areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax);
464                 
465                 ymax=count_action_levels(act) * (CHANNELHEIGHT+CHANNELSKIP);
466                 for (chan=act->chanbase.first; chan; chan=chan->next){
467                         
468                         /* Check action */
469                         ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
470                         if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
471                                 borderselect_ipo_key(chan->ipo, rectf.xmin, rectf.xmax, val);
472                         
473                         ymax=ymin;
474
475                         /* Check constraints */
476                         for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
477                                 ymin=ymax-(CHANNELHEIGHT+CHANNELSKIP);
478                                 if (!((ymax < rectf.ymin) || (ymin > rectf.ymax)))
479                                         borderselect_ipo_key(conchan->ipo, rectf.xmin, rectf.xmax, val);
480                                 
481                                 ymax=ymin;
482                         }
483                 }       
484                 allqueue(REDRAWNLA, 0);
485                 allqueue(REDRAWACTION, 0);
486                 allqueue(REDRAWIPO, 0);
487         }
488 }
489
490 bActionChannel* get_hilighted_action_channel(bAction* action)
491 {
492         bActionChannel *chan;
493
494         if (!action)
495                 return NULL;
496
497         for (chan=action->chanbase.first; chan; chan=chan->next){
498                 if (chan->flag & ACHAN_SELECTED && chan->flag & ACHAN_HILIGHTED)
499                         return chan;
500         }
501
502         return NULL;
503
504 }
505
506 void set_exprap_action(int mode)
507 {
508         if(G.saction->action && G.saction->action->id.lib) return;
509
510         error ("Not yet implemented!");
511 }
512
513 void free_posebuf(void) 
514 {
515         if (g_posebuf){
516                 clear_pose(g_posebuf);
517                 MEM_freeN (g_posebuf);
518         }
519         g_posebuf=NULL;
520 }
521
522 void copy_posebuf (void)
523 {
524         Object *ob;
525
526         free_posebuf();
527
528         ob=G.obpose;
529         if (!ob){
530                 error ("Copybuf is empty");
531                 return;
532         }
533
534         filter_pose_keys();
535         copy_pose(&g_posebuf, ob->pose, 0);
536
537 }
538
539 static void flip_name (char *name)
540 {
541
542         char    prefix[128]={""};       /* The part before the facing */
543         char    suffix[128]={""};       /* The part after the facing */
544         char    replace[128]={""};      /* The replacement string */
545
546         char    *index=NULL;
547         /* Find the last period */
548
549         strcpy (prefix, name);
550
551         /* Check for an instance of .Right */
552         if (!index){
553                 index = strstr (prefix, "Right");
554                 if (index){
555                         *index=0;
556                         strcpy (replace, "Left");
557                         strcpy (suffix, index+6);
558                 }
559         }
560
561         /* Che ck for an instance of .RIGHT */
562         if (!index){
563                 index = strstr (prefix, "RIGHT");
564                 if (index){
565                         *index=0;
566                         strcpy (replace, "LEFT");
567                         strcpy (suffix, index+6);
568                 }
569         }
570
571
572         /* Check for an instance of .right */
573         if (!index){
574                 index = strstr (prefix, "right");
575                 if (index){
576                         *index=0;
577                         strcpy (replace, "left");
578                         strcpy (suffix, index+6);
579                 }
580         }
581
582         /* Check for an instance of .left */
583         if (!index){
584                 index = strstr (prefix, "left");
585                 if (index){
586                         *index=0;
587                         strcpy (replace, "right");
588                         strcpy (suffix, index+5);
589                 }
590         }
591
592         /* Check for an instance of .LEFT */
593         if (!index){
594                 index = strstr (prefix, "LEFT");
595                 if (index){
596                         *index=0;
597                         strcpy (replace, "RIGHT");
598                         strcpy (suffix, index+5);
599                 }
600         }
601
602         /* Check for an instance of .Left */
603         if (!index){
604                 index = strstr (prefix, "Left");
605                 if (index){
606                         *index=0;
607                         strcpy (replace, "Right");
608                         strcpy (suffix, index+5);
609                 }
610         }
611
612         /* check for an instance of .L */
613         if (!index){
614                 index = strstr (prefix, ".L");
615                 if (index){
616                         *index=0;
617                         strcpy (replace, ".R");
618                         strcpy (suffix, index+2);
619                 }
620         }
621
622         /* check for an instance of .l */
623         if (!index){
624                 index = strstr (prefix, ".l");
625                 if (index){
626                         *index=0;
627                         strcpy (replace, ".r");
628                         strcpy (suffix, index+2);
629                 }
630         }
631
632         /* Checl for an instance of .R */
633         if (!index){
634                 index = strstr (prefix, ".R");
635                 if (index){
636                         *index=0;
637                         strcpy (replace, ".L");
638                         strcpy (suffix, index+2);
639                 }
640         }
641
642         /* Checl for an instance of .r */
643         if (!index){
644                 index = strstr (prefix, ".r");
645                 if (index){
646                         *index=0;
647                         strcpy (replace, ".l");
648                         strcpy (suffix, index+2);
649                 }
650         }
651
652         sprintf (name, "%s%s%s", prefix, replace, suffix);
653 }
654
655 void paste_posebuf (int flip){
656         Object *ob;
657         bPoseChannel *temp, *chan;
658         float eul[4];
659         Base    *base;
660         int             newchan = 0;
661
662         ob=G.obpose;
663         if (!ob)
664                 return;
665
666         if (!g_posebuf){
667                 error ("Copybuf is empty");
668                 return;
669         };
670         
671         collect_pose_garbage(ob);
672
673         /* Safely merge all of the channels in this pose into
674         any existing pose */
675         if (ob->pose){
676                 if (U.uiflag & KEYINSERTACT){
677                         /* Display "Avail, all" dialog */
678                 }
679                 for (chan=g_posebuf->chanbase.first; chan; chan=chan->next){
680                         if (chan->flag & POSE_KEY){
681                                 temp = copy_pose_channel (chan);
682                                 if (flip){
683                                         flip_name (temp->name);
684                                         temp->loc[0]*=-1;
685
686                                         QuatToEul(temp->quat, eul);
687                                         eul[1]*=-1;
688                                         eul[2]*=-1;
689                                         EulToQuat(eul, temp->quat);
690                                 }
691
692                                 temp = set_pose_channel (ob->pose, temp);
693
694                                 if (U.uiflag & KEYINSERTACT){
695                                         /* Set keys on pose */
696                                         if (chan->flag & POSE_ROT){
697                                                 set_action_key(ob->action, temp, AC_QUAT_X, newchan);
698                                                 set_action_key(ob->action, temp, AC_QUAT_Y, newchan);
699                                                 set_action_key(ob->action, temp, AC_QUAT_Z, newchan);
700                                                 set_action_key(ob->action, temp, AC_QUAT_W, newchan);
701                                         };
702                                         if (chan->flag & POSE_SIZE){
703                                                 set_action_key(ob->action, temp, AC_SIZE_X, newchan);
704                                                 set_action_key(ob->action, temp, AC_SIZE_Y, newchan);
705                                                 set_action_key(ob->action, temp, AC_SIZE_Z, newchan);
706                                         };
707                                         if (chan->flag & POSE_LOC){
708                                                 set_action_key(ob->action, temp, AC_LOC_X, newchan);
709                                                 set_action_key(ob->action, temp, AC_LOC_Y, newchan);
710                                                 set_action_key(ob->action, temp, AC_LOC_Z, newchan);
711                                         };                                      
712                                 }
713                         }
714                 }
715
716                 if (U.uiflag & KEYINSERTACT){
717                         remake_action_ipos(ob->action);
718                         allqueue (REDRAWIPO, 0);
719                         allqueue (REDRAWVIEW3D, 0);
720                         allqueue (REDRAWACTION, 0);             
721                         allqueue(REDRAWNLA, 0);
722                 }
723
724                 /* Update deformation children */
725                 if (G.obpose->type == OB_ARMATURE){
726                         for (base= FIRSTBASE; base; base= base->next){
727                                 if (G.obpose==base->object->parent){
728                                         if (base->object->partype==PARSKEL)
729                                                 makeDispList(base->object);
730                                 }
731                         }
732                 }
733         }
734 }
735
736 void set_action_key (struct bAction *act, struct bPoseChannel *chan, int adrcode, short makecurve)
737 {
738         set_action_key_time (act, chan, adrcode, makecurve, frame_to_float(CFRA));
739 }
740
741 static void set_action_key_time (bAction *act, bPoseChannel *chan, int adrcode, short makecurve, float time)
742 {
743         bActionChannel  *achan;
744         char    ipstr[256];
745
746         if (!act)
747                 return;
748
749         if (!chan)
750                 return;
751         /* See if this action channel exists already */ 
752         for (achan=act->chanbase.first; achan; achan=achan->next){
753                 if (!strcmp (chan->name, achan->name))
754                         break;
755         }
756
757         if (!achan){
758                 if (!makecurve)
759                         return;
760                 achan = MEM_callocN (sizeof(bActionChannel), "actionChannel");
761                 strcpy (achan->name, chan->name);
762                 BLI_addtail (&act->chanbase, achan);
763         }
764
765         /* Ensure the channel appears selected in the action window */
766         achan->flag |= ACHAN_SELECTED;
767
768         /* Ensure this action channel has a valid Ipo */
769         if (!achan->ipo){
770                 sprintf (ipstr, "%s.%s", act->id.name+2, chan->name);
771                 ipstr[23]=0;
772                 achan->ipo=     add_ipo(ipstr, ID_AC);  
773         }
774
775         insertactionkey(act, achan, chan, adrcode, makecurve, time);
776
777 }
778
779 static void insertactionkey(bAction *act, bActionChannel *achan, bPoseChannel *chan, int adrcode, short makecurve, float cfra)
780 {
781         IpoCurve *icu;
782         void *poin;
783         float curval;
784         int type;
785         ID *id;
786         
787         if (!act){
788                 return;
789         }
790         if (act->id.lib){
791                 error ("Can't pose libactions");
792                 return;
793         }
794         act->achan=achan;
795         act->pchan=chan;
796
797         id=(ID*) act;
798
799         /* First see if this curve exists */
800         if (!makecurve){
801                 if (!achan->ipo)
802                         return;
803
804                 for (icu = achan->ipo->curve.first; icu; icu=icu->next){
805                         if (icu->adrcode == adrcode)
806                                 break;
807                 }
808                 if (!icu)
809                         return;
810         }
811
812         
813         icu = get_ipocurve (id, GS(id->name), adrcode, achan->ipo);
814
815         if(icu) {
816                 poin= get_ipo_poin(id, icu, &type);
817                 if(poin) {
818                         curval= read_ipo_poin(poin, type);
819         //              cfra= frame_to_float(CFRA);
820                         insert_vert_ipo(icu, cfra, curval);
821                 }
822         }
823         
824 }
825
826 bAction *add_empty_action(void)
827 {
828         bAction *act;
829
830         act= alloc_libblock(&G.main->action, ID_AC, "Action");
831         act->id.flag |= LIB_FAKEUSER;
832         act->id.us++;
833         return act;
834 }
835
836 static void transform_actionchannel_keys(char mode)
837 {
838         bAction *act;
839         TransVert *tv;
840         int /*sel=0,*/  i;
841         bActionChannel  *chan;
842         short   mvals[2], mvalc[2], cent[2];
843         float   sval[2], cval[2], lastcval[2];
844         short   cancel=0;
845         float   fac=0.0F;
846         int             loop=1;
847         int             tvtot=0;
848         float   deltax, startx;
849         float   cenf[2];
850         int             invert=0, firsttime=1;
851         char    str[256];
852         bConstraintChannel *conchan;
853
854         act=G.saction->action;
855
856         /* Ensure that partial selections result in beztriple selections */
857         for (chan=act->chanbase.first; chan; chan=chan->next){
858                 tvtot+=fullselect_ipo_keys(chan->ipo);
859
860                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
861                         tvtot+=fullselect_ipo_keys(conchan->ipo);
862         }
863         
864         /* If nothing is selected, bail out */
865         if (!tvtot)
866                 return;
867         
868         
869         /* Build the transvert structure */
870         tv = MEM_callocN (sizeof(TransVert) * tvtot, "transVert");
871         tvtot=0;
872         for (chan=act->chanbase.first; chan; chan=chan->next){
873                 /* Add the actionchannel */
874                 tvtot = add_trans_ipo_keys(chan->ipo, tv, tvtot);
875                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
876                         tvtot = add_trans_ipo_keys(conchan->ipo, tv, tvtot);
877         }
878
879         /* Do the event loop */
880         cent[0] = curarea->winx + (G.saction->v2d.hor.xmax)/2;
881         cent[1] = curarea->winy + (G.saction->v2d.hor.ymax)/2;
882         areamouseco_to_ipoco(G.v2d, cent, &cenf[0], &cenf[1]);
883
884         getmouseco_areawin (mvals);
885         areamouseco_to_ipoco(G.v2d, mvals, &sval[0], &sval[1]);
886
887         startx=sval[0];
888         while (loop) {
889                 /*              Get the input */
890                 /*              If we're cancelling, reset transformations */
891                 /*                      Else calc new transformation */
892                 /*              Perform the transformations */
893                 while (qtest()) {
894                         short val;
895                         unsigned short event= extern_qread(&val);
896
897                         if (val) {
898                                 switch (event) {
899                                 case LEFTMOUSE:
900                                 case SPACEKEY:
901                                 case RETKEY:
902                                         loop=0;
903                                         break;
904                                 case XKEY:
905                                         break;
906                                 case ESCKEY:
907                                 case RIGHTMOUSE:
908                                         cancel=1;
909                                         loop=0;
910                                         break;
911                                 default:
912                                         arrows_move_cursor(event);
913                                         break;
914                                 };
915                         }
916                 }
917
918                 if (cancel) {
919                         for (i=0; i<tvtot; i++) {
920                                 tv[i].loc[0]=tv[i].oldloc[0];
921                                 tv[i].loc[1]=tv[i].oldloc[1];
922                         }
923                 } else {
924                         getmouseco_areawin (mvalc);
925                         areamouseco_to_ipoco(G.v2d, mvalc, &cval[0], &cval[1]);
926
927                         if (!firsttime && lastcval[0]==cval[0] && lastcval[1]==cval[1]) {
928                                 PIL_sleep_ms(1);
929                         } else {
930                                 for (i=0; i<tvtot; i++){
931                                         tv[i].loc[0]=tv[i].oldloc[0];
932
933                                         switch (mode){
934                                         case 'g':
935                                                 deltax = cval[0]-sval[0];
936                                                 fac= deltax;
937                                                 
938                                                 apply_keyb_grid(&fac, 0.0, 1.0, 0.1, U.flag & AUTOGRABGRID);
939
940                                                 tv[i].loc[0]+=fac;
941                                                 break;
942                                         case 's':
943                                                 startx=mvals[0]-(ACTWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2);
944                                                 deltax=mvalc[0]-(ACTWIDTH/2+(curarea->winrct.xmax-curarea->winrct.xmin)/2);
945                                                 fac= fabs(deltax/startx);
946                                                 
947                                                 apply_keyb_grid(&fac, 0.0, 0.2, 0.1, U.flag & AUTOSIZEGRID);
948                 
949                                                 if (invert){
950                                                         if (i % 03 == 0){
951                                                                 memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i+2].oldloc));
952                                                         }
953                                                         if (i % 03 == 2){
954                                                                 memcpy (tv[i].loc, tv[i].oldloc, sizeof(tv[i-2].oldloc));
955                                                         }
956         
957                                                         fac*=-1;
958                                                 }
959                                                 startx= (G.scene->r.cfra);
960                                         
961                                                 tv[i].loc[0]-= startx;
962                                                 tv[i].loc[0]*=fac;
963                                                 tv[i].loc[0]+= startx;
964                 
965                                                 break;
966                                         }
967                                 }
968                         }
969         
970                         if (mode=='s'){
971                                 sprintf(str, "sizeX: %.3f", fac);
972                                 headerprint(str);
973                         }
974                         else if (mode=='g'){
975                                 sprintf(str, "deltaX: %.3f", fac);
976                                 headerprint(str);
977                         }
978         
979                         if (G.saction->lock){
980                                 do_all_actions();
981                                 allqueue (REDRAWVIEW3D, 0);
982                                 allqueue (REDRAWACTION, 0);
983                                 allqueue (REDRAWIPO, 0);
984                                 allqueue(REDRAWNLA, 0);
985                                 force_draw_all();
986                         }
987                         else {
988                                 addqueue (curarea->win, REDRAWALL, 0);
989                                 force_draw ();
990                         }
991                 }
992                 
993                 lastcval[0]= cval[0];
994                 lastcval[1]= cval[1];
995                 firsttime= 0;
996         }
997         
998         /*              Update the curve */
999         /*              Depending on the lock status, draw necessary views */
1000
1001         do_all_actions();
1002         remake_action_ipos(act);
1003         allqueue (REDRAWVIEW3D, 0);
1004         allqueue (REDRAWACTION, 0);
1005         allqueue(REDRAWNLA, 0);
1006         allqueue (REDRAWIPO, 0);
1007         MEM_freeN (tv);
1008 }
1009
1010 void deselect_actionchannel_keys (bAction *act, int test)
1011 {
1012         bActionChannel  *chan;
1013         bConstraintChannel *conchan;
1014         int             sel=1;;
1015
1016         if (!act)
1017                 return;
1018
1019         /* Determine if this is selection or deselection */
1020         
1021         if (test){
1022                 for (chan=act->chanbase.first; chan; chan=chan->next){
1023                         /* Test the channel ipos */
1024                         if (is_ipo_key_selected(chan->ipo)){
1025                                 sel = 0;
1026                                 break;
1027                         }
1028
1029                         /* Test the constraint ipos */
1030                         for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
1031                                 if (is_ipo_key_selected(conchan->ipo)){
1032                                         sel = 0;
1033                                         break;
1034                                 }
1035                         }
1036
1037                         if (sel == 0)
1038                                 break;
1039                 }
1040         }
1041         else
1042                 sel=0;
1043         
1044         /* Set the flags */
1045         for (chan=act->chanbase.first; chan; chan=chan->next){
1046                 set_ipo_key_selection(chan->ipo, sel);
1047                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1048                         set_ipo_key_selection(conchan->ipo, sel);
1049         }
1050 }
1051
1052 void deselect_actionchannels (bAction *act, int test)
1053 {
1054         bActionChannel *chan;
1055         bConstraintChannel *conchan;
1056         int                     sel=1;  
1057
1058         if (!act)
1059                 return;
1060
1061         /* See if we should be selecting or deselecting */
1062         if (test){
1063                 for (chan=act->chanbase.first; chan; chan=chan->next){
1064                         if (!sel)
1065                                 break;
1066
1067                         if (chan->flag & ACHAN_SELECTED){
1068                                 sel=0;
1069                                 break;
1070                         }
1071                         if (sel){
1072                                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
1073                                         if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){
1074                                                 sel=0;
1075                                                 break;
1076                                         }
1077                                 }
1078                         }
1079                 }
1080         }
1081         else
1082                 sel=0;
1083
1084         /* Now set the flags */
1085         for (chan=act->chanbase.first; chan; chan=chan->next){
1086                 select_poseelement_by_name(chan->name, sel);
1087
1088                 if (sel)
1089                         chan->flag |= ACHAN_SELECTED;
1090                 else
1091                         chan->flag &= ~ACHAN_SELECTED;
1092
1093                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
1094                         if (sel)
1095                                 conchan->flag |= CONSTRAINT_CHANNEL_SELECT;
1096                         else
1097                                 conchan->flag &= ~CONSTRAINT_CHANNEL_SELECT;
1098                 }
1099         }
1100
1101 }
1102
1103 static void hilight_channel (bAction *act, bActionChannel *chan, short select)
1104 {
1105         bActionChannel *curchan;
1106
1107         if (!act)
1108                 return;
1109
1110         for (curchan=act->chanbase.first; curchan; curchan=curchan->next){
1111                 if (curchan==chan && select)
1112                         curchan->flag |= ACHAN_HILIGHTED;
1113                 else
1114                         curchan->flag &= ~ACHAN_HILIGHTED;
1115         }
1116 }
1117
1118 static void mouse_actionchannels(bAction *act, short *mval)
1119
1120         bActionChannel *chan;
1121         bConstraintChannel *clickconchan=NULL;
1122         float   click;
1123         int             wsize;
1124         int             sel;
1125         bConstraintChannel *conchan;
1126         
1127         if (!act)
1128                 return;
1129
1130         wsize = (count_action_levels (act)*(CHANNELHEIGHT+CHANNELSKIP));
1131
1132
1133         click = (wsize-(mval[1]+G.v2d->cur.ymin));
1134         click += CHANNELHEIGHT/2;
1135         click /= (CHANNELHEIGHT+CHANNELSKIP);
1136
1137         if (click<0)
1138                 return;
1139
1140         for (chan = act->chanbase.first; chan; chan=chan->next){
1141                 if ((int)click==0)
1142                         break;
1143
1144                 click--;
1145
1146                 /* Check for click in a constraint */
1147                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next){
1148                         if ((int)click==0){
1149                                 clickconchan=conchan;
1150                                 chan=act->chanbase.last;
1151                                 break;
1152                         }
1153                         click--;
1154                 }
1155         }
1156
1157         if (!chan){
1158                 if (clickconchan){
1159                         if (clickconchan->flag & CONSTRAINT_CHANNEL_SELECT)
1160                                 sel = 0;
1161                         else
1162                                 sel =1;
1163                         
1164                         /* Channel names clicking */
1165                         if (G.qual & LR_SHIFTKEY){
1166                 //              select_poseelement_by_name(chan->name, !(chan->flag & ACHAN_SELECTED));
1167                                 if (clickconchan->flag & CONSTRAINT_CHANNEL_SELECT){
1168                                         clickconchan->flag &= ~CONSTRAINT_CHANNEL_SELECT;
1169                                 //      hilight_channel(act, chan, 0);
1170                                 }
1171                                 else{
1172                                         clickconchan->flag |= CONSTRAINT_CHANNEL_SELECT;
1173                                 //      hilight_channel(act, chan, 1);
1174                                 }
1175                         }
1176                         else{
1177                                 deselect_actionchannels (act, 0);       // Auto clear
1178                                 clickconchan->flag |= CONSTRAINT_CHANNEL_SELECT;
1179                         //      hilight_channel(act, chan, 1);
1180                         //      act->achan = chan;
1181                         //      select_poseelement_by_name(chan->name, 1);
1182                         }
1183
1184                 }
1185                 else
1186                         return;
1187         }
1188         else{
1189                 /* Choose the mode */
1190                 if (chan->flag & ACHAN_SELECTED)
1191                         sel = 0;
1192                 else
1193                         sel =1;
1194                 
1195                 /* Channel names clicking */
1196                         if (G.qual & LR_SHIFTKEY){
1197                                 select_poseelement_by_name(chan->name, !(chan->flag & ACHAN_SELECTED));
1198                                 if (chan->flag & ACHAN_SELECTED){
1199                                         chan->flag &= ~ACHAN_SELECTED;
1200                                         hilight_channel(act, chan, 0);
1201                                 }
1202                                 else{
1203                                         chan->flag |= ACHAN_SELECTED;
1204                                         hilight_channel(act, chan, 1);
1205                                 }
1206                         }
1207                         else{
1208                                 deselect_actionchannels (act, 0);       // Auto clear
1209                                 chan->flag |= ACHAN_SELECTED;
1210                                 hilight_channel(act, chan, 1);
1211                                 act->achan = chan;
1212                                 select_poseelement_by_name(chan->name, 1);
1213                         }
1214
1215         }
1216         allqueue (REDRAWIPO, 0);
1217         allqueue (REDRAWVIEW3D, 0);
1218         allqueue (REDRAWACTION, 0);
1219         allqueue(REDRAWNLA, 0);
1220
1221 }
1222
1223 static void delete_actionchannel_keys(void)
1224 {
1225         bAction *act;
1226         bActionChannel *chan;
1227         bConstraintChannel *conchan;
1228
1229         act = G.saction->action;
1230         if (!act)
1231                 return;
1232
1233         if (!okee("Erase selected keys"))
1234                 return;
1235
1236         for (chan = act->chanbase.first; chan; chan=chan->next){
1237
1238                 /* Check action channel keys*/
1239                 delete_ipo_keys(chan->ipo);
1240
1241                 /* Delete constraint channel keys */
1242                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1243                         delete_ipo_keys(conchan->ipo);
1244         }
1245
1246         remake_action_ipos (act);
1247         allspace(REMAKEIPO, 0);
1248         allqueue(REDRAWACTION, 0);
1249         allqueue(REDRAWIPO, 0);
1250         allqueue(REDRAWNLA, 0);
1251
1252 }
1253 static void delete_actionchannels (void)
1254 {
1255         bConstraintChannel *conchan, *nextconchan;
1256         bActionChannel *chan, *next;
1257         bAction *act;
1258         int freechan;
1259
1260         act=G.saction->action;
1261
1262         if (!act)
1263                 return;
1264
1265         for (chan=act->chanbase.first; chan; chan=chan->next){
1266                 if (chan->flag & ACHAN_SELECTED)
1267                         break;
1268                 for (conchan=chan->constraintChannels.first; conchan; conchan=conchan->next)
1269                 {
1270                         if (conchan->flag & CONSTRAINT_CHANNEL_SELECT){
1271                                 chan=act->chanbase.last;
1272                                 break;
1273                         }
1274                 }
1275         }
1276
1277         if (!chan && !conchan)
1278                 return;
1279
1280         if (!okee("Erase selected channels"))
1281                 return;
1282
1283         for (chan=act->chanbase.first; chan; chan=next){
1284                 freechan = 0;
1285                 next=chan->next;
1286                 
1287                 /* Remove action channels */
1288                 if (chan->flag & ACHAN_SELECTED){
1289                         if (chan->ipo)
1290                                 chan->ipo->id.us--;     /* Release the ipo */
1291                         freechan = 1;
1292                 }
1293                 
1294                 /* Remove constraint channels */
1295                 for (conchan=chan->constraintChannels.first; conchan; conchan=nextconchan){
1296                         nextconchan=conchan->next;
1297                         if (freechan || conchan->flag & CONSTRAINT_CHANNEL_SELECT){
1298                                 if (conchan->ipo)
1299                                         conchan->ipo->id.us--;
1300                                 BLI_freelinkN(&chan->constraintChannels, conchan);
1301                         }
1302                 }
1303                 
1304                 if (freechan)
1305                         BLI_freelinkN (&act->chanbase, chan);
1306
1307         }
1308
1309         allqueue (REDRAWACTION, 0);
1310         allqueue(REDRAWNLA, 0);
1311
1312 }
1313
1314 static void sethandles_actionchannel_keys(int code)
1315 {
1316         bAction *act;
1317         bActionChannel *chan;
1318
1319         /* Get the selected action, exit if none are selected 
1320          */
1321         act = G.saction->action;
1322         if (!act)
1323                 return;
1324
1325         /* Loop through the channels and set the beziers
1326          * of the selected keys based on the integer code
1327          */
1328         for (chan = act->chanbase.first; chan; chan=chan->next){
1329                 sethandles_ipo_keys(chan->ipo, code);
1330         }
1331
1332         /* Clean up and redraw stuff
1333          */
1334         remake_action_ipos (act);
1335         allspace(REMAKEIPO, 0);
1336         allqueue(REDRAWACTION, 0);
1337         allqueue(REDRAWIPO, 0);
1338         allqueue(REDRAWNLA, 0);
1339 }
1340
1341 static void set_ipotype_actionchannels(void) {
1342
1343         bAction *act; 
1344         bActionChannel *chan;
1345         short event;
1346
1347         /* Get the selected action, exit if none are selected 
1348          */
1349         act = G.saction->action;
1350         if (!act)
1351                 return;
1352
1353         /* Present a popup menu asking the user what type
1354          * of IPO curve he/she/GreenBTH wants. ;)
1355          */
1356         event= pupmenu("Channel Ipo Type %t|Constant %x1|Linear %x2|Bezier %x3");
1357         if(event < 1) return;
1358         
1359         /* Loop through the channels and for the selected ones set
1360          * the type for each Ipo curve in the channel Ipo (based on
1361          * the value from the popup).
1362          */
1363         for (chan = act->chanbase.first; chan; chan=chan->next){
1364                 if (chan->flag & ACHAN_SELECTED){
1365                         if (chan->ipo)
1366                                 setipotype_ipo(chan->ipo, event);
1367                 }
1368         }
1369
1370         /* Clean up and redraw stuff
1371          */
1372         remake_action_ipos (act);
1373         allspace(REMAKEIPO, 0);
1374         allqueue(REDRAWACTION, 0);
1375         allqueue(REDRAWIPO, 0);
1376         allqueue(REDRAWNLA, 0);
1377 }
1378
1379 void winqreadactionspace(unsigned short event, short val, char ascii)
1380 {
1381         SpaceAction *saction;
1382         bAction *act;
1383         int doredraw= 0;
1384         short   mval[2];
1385         float dx,dy;
1386         int     cfra;
1387         
1388         if(curarea->win==0) return;
1389
1390         saction= curarea->spacedata.first;
1391         if (!saction)
1392                 return;
1393
1394         act=saction->action;
1395         if(val) {
1396                 
1397                 if( uiDoBlocks(&curarea->uiblocks, event)!=UI_NOTHING ) event= 0;
1398                 
1399                 getmouseco_areawin(mval);
1400
1401                 switch(event) {
1402                 case UI_BUT_EVENT:
1403                         do_blenderbuttons(val);
1404                         break;
1405                 case HOMEKEY:
1406                         do_action_buttons(B_ACTHOME);
1407                         break;
1408                 case DKEY:
1409                         if (G.qual & LR_SHIFTKEY && mval[0]>ACTWIDTH){
1410                                 duplicate_actionchannel_keys();
1411                                 remake_action_ipos(act);
1412                         }
1413                         break;
1414                 case DELKEY:
1415                 case XKEY:
1416                         if (mval[0]<ACTWIDTH)
1417                                 delete_actionchannels ();
1418                         else
1419                                 delete_actionchannel_keys ();
1420                         break;
1421                 case GKEY:
1422                         if (mval[0]>=ACTWIDTH)
1423                                 transform_actionchannel_keys ('g');
1424                         break;
1425                 case SKEY:
1426                         if (mval[0]>=ACTWIDTH)
1427                                 transform_actionchannel_keys ('s');
1428                         break;
1429                 case AKEY:
1430                         if (mval[0]<ACTWIDTH){
1431                                 deselect_actionchannels (act, 1);
1432                                 allqueue (REDRAWVIEW3D, 0);
1433                                 allqueue (REDRAWACTION, 0);
1434                                 allqueue(REDRAWNLA, 0);
1435                                 allqueue (REDRAWIPO, 0);
1436                         }
1437                         else{
1438                                 deselect_actionchannel_keys (act, 1);
1439                                 allqueue (REDRAWACTION, 0);
1440                                 allqueue(REDRAWNLA, 0);
1441                                 allqueue (REDRAWIPO, 0);
1442                         }
1443                         break;
1444
1445                         /*** set the Ipo handles ***/
1446                 case VKEY:
1447                         sethandles_actionchannel_keys(HD_VECT);
1448                         break;
1449                 case HKEY:
1450                         if(G.qual & LR_SHIFTKEY) sethandles_actionchannel_keys(HD_AUTO);
1451                         else sethandles_actionchannel_keys(HD_ALIGN);
1452                         break;
1453  
1454                         /*** set the Ipo type  ***/
1455                 case TKEY:
1456                         set_ipotype_actionchannels();
1457                         break;
1458
1459                 case BKEY:
1460                         borderselect_action();
1461                         break;
1462                 case RIGHTMOUSE:
1463                         if (mval[0]<ACTWIDTH)
1464                                 mouse_actionchannels(act, mval);
1465                         else
1466                                 mouse_action();
1467                         break;
1468                 case LEFTMOUSE:
1469                         if (mval[0]>ACTWIDTH){
1470                                 do {
1471                                         getmouseco_areawin(mval);
1472                                         
1473                                         areamouseco_to_ipoco(G.v2d, mval, &dx, &dy);
1474                                         
1475                                         cfra= (int)dx;
1476                                         if(cfra< 1) cfra= 1;
1477                                         
1478                                         if( cfra!=CFRA ) {
1479                                                 CFRA= cfra;
1480                                                 update_for_newframe();
1481                                                 force_draw_plus(SPACE_VIEW3D);
1482                                                 force_draw_plus(SPACE_IPO);
1483                                                 force_draw_plus(SPACE_BUTS);
1484                                         }
1485                                         
1486                                 } while(get_mbut()&L_MOUSE);
1487                         }
1488                         
1489                         break;
1490                 case MIDDLEMOUSE:
1491                 case WHEELUPMOUSE:
1492                 case WHEELDOWNMOUSE:
1493                         view2dmove(event);      /* in drawipo.c */
1494                         break;
1495                 }
1496         }
1497
1498         if(doredraw) addqueue(curarea->win, REDRAW, 1);
1499         
1500 }
1501